mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2607 lines
67 KiB
2607 lines
67 KiB
/*
|
|
* @doc INTERNAL
|
|
*
|
|
* @module OLS.CPP -- COls LineServices object class
|
|
*
|
|
* Authors:
|
|
* Murray Sargent: initial coding up to nonLS RichEdit functionality
|
|
* (with lots of help from RickSa's ols code)
|
|
* Keith Curtis and Worachai Chaoweeraprasit: complex script support,
|
|
* etc.
|
|
*
|
|
* @todo
|
|
* 1) Fix table selection
|
|
* 2) What are we to do with RTL tables? Word has a very different model
|
|
* 3) What we should give for LSCHP.dcpMaxContext
|
|
*
|
|
* Copyright (c) 1997-1999 Microsoft Corporation. All rights reserved.
|
|
*/
|
|
|
|
#include "_common.h"
|
|
#include "_edit.h"
|
|
#include "_font.h"
|
|
#include "_render.h"
|
|
#include "_osdc.h"
|
|
#include "_dfreeze.h"
|
|
#include "_tomfmt.h"
|
|
#include "_ols.h"
|
|
#include "_clasfyc.h"
|
|
#include "_uspi.h"
|
|
#include "_txtbrk.h"
|
|
#include "lskysr.h"
|
|
|
|
#ifdef LINESERVICES
|
|
|
|
ASSERTDATA
|
|
|
|
// Guess at the number of characters on the line
|
|
const int cchLineHint = 66;
|
|
|
|
#define OBJID_OLE 0
|
|
#define OBJID_REVERSE 1
|
|
#define OBJID_COUNT 2
|
|
|
|
const WCHAR wchObjectEnd = 0x9F;
|
|
const WCHAR rgchObjectEnd[] = {wchObjectEnd};
|
|
|
|
#define MAX_OBJ_DEPTH 3
|
|
|
|
extern const LSCBK lscbk;
|
|
|
|
// Kinsoku break pair information
|
|
extern const INT g_cKinsokuCategories;
|
|
|
|
CLineServices *g_plsc = NULL; // LineServices Context
|
|
COls* g_pols = NULL; // COls ptr
|
|
|
|
const LSBRK rglsbrkDefault[] =
|
|
{
|
|
0,0, // Always prohibited
|
|
0,1, // OK across blanks
|
|
1,1 // Always allowed
|
|
};
|
|
|
|
// prototypes
|
|
void EmitBrace(COls* pols, PLSCHP pchp, BOOL* pfHid, DWORD* pcch, PLSRUN* pprun, LPCWSTR* plpwch, int id, LPCWSTR str);
|
|
void DupShapeState(PLSRUN prun, LONG cch);
|
|
|
|
|
|
// public inline functions
|
|
//
|
|
|
|
// Emitting fake brace to LS
|
|
inline void EmitBrace(
|
|
COls* pols,
|
|
PLSCHP pchp,
|
|
BOOL* pfHid,
|
|
DWORD* pcch,
|
|
PLSRUN* pprun,
|
|
LPCWSTR* plpwch,
|
|
int id,
|
|
LPCWSTR str)
|
|
{
|
|
ZeroMemory(pchp, sizeof(*pchp));
|
|
pchp->idObj = (WORD)id;
|
|
*pfHid = 0;
|
|
*pcch = 1;
|
|
*pprun = pols->GetPlsrun(0, pols->_pme->GetCF(), FALSE);
|
|
*plpwch = str;
|
|
}
|
|
|
|
// Duplicate shaping state to each runs in the chain
|
|
// note: this macro used only by GetGlyph and GetGlyphPosition
|
|
inline void DupShapeState(
|
|
PLSRUN prun,
|
|
LONG cch)
|
|
{
|
|
PLSRUN pnext = prun->_pNext;
|
|
LONG cpEnd = prun->_cp + cch;
|
|
while (pnext && pnext->_cp < cpEnd)
|
|
{
|
|
CopyMemory(&pnext->_a, &prun->_a, sizeof(SCRIPT_ANALYSIS));
|
|
pnext->SetFallback(prun->IsFallback());
|
|
prun = pnext;
|
|
pnext = prun->_pNext;
|
|
}
|
|
Assert(!pnext && prun->_cp < cpEnd);
|
|
}
|
|
|
|
|
|
|
|
|
|
LONG COls::GetCpLsFromCpRe(
|
|
LONG cpRe)
|
|
{
|
|
if (_rgcp.Count() == 0)
|
|
return cpRe;
|
|
|
|
LONG *pcp = _rgcp.Elem(0);
|
|
|
|
for(LONG cpLs = cpRe; cpLs >= *pcp; pcp++)
|
|
cpLs++;
|
|
|
|
return cpLs;
|
|
}
|
|
|
|
LONG COls::GetCpReFromCpLs(
|
|
LONG cpLs
|
|
)
|
|
{
|
|
if (_rgcp.Count() == 0)
|
|
return cpLs;
|
|
|
|
LONG *pcp = _rgcp.Elem(0);
|
|
|
|
for(int dcp = 0; cpLs > *pcp; pcp++)
|
|
dcp--;
|
|
|
|
return cpLs + dcp;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
//#define DEBUG_BRACE
|
|
#endif
|
|
// return TRUE if braces added
|
|
BOOL COls::AddBraceCp(long cpLs)
|
|
{
|
|
if (_rgcp.Count() == 0)
|
|
{
|
|
long *pcp = _rgcp.Insert(0, 1);
|
|
*pcp = tomForward;
|
|
}
|
|
|
|
long *pcp = _rgcp.Elem(0);
|
|
long iel = 0;
|
|
|
|
while (cpLs > pcp[iel])
|
|
iel++;
|
|
|
|
if (cpLs < pcp[iel])
|
|
{
|
|
pcp = _rgcp.Insert(iel, 1);
|
|
*pcp = cpLs;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
// return number of braces before cp
|
|
//
|
|
LONG COls::BracesBeforeCp(LONG cpLs)
|
|
{
|
|
LONG iel, cbr = 0;
|
|
LONG* pcp;
|
|
|
|
if (!cpLs || (iel = _rgcp.Count()) < 2)
|
|
return 0;
|
|
|
|
iel -= 2; // exclude the last tomForward one and make a count an index
|
|
cpLs--; // start with the cp preceding given cp
|
|
|
|
pcp = _rgcp.Elem(0);
|
|
|
|
while (iel > -1 && pcp[iel] > cpLs) // search the first one
|
|
iel--;
|
|
|
|
while (iel > -1 && pcp[iel] == cpLs) // continue counting
|
|
{
|
|
iel--;
|
|
cpLs--;
|
|
cbr++;
|
|
}
|
|
return cbr;
|
|
}
|
|
|
|
/*
|
|
* SetRun(plsrun)
|
|
*
|
|
* @func
|
|
* Do whatever is needed to initialize the measurer (pme) to the lsrun
|
|
* givin by plsrun and return whether the run is for autonumbering.
|
|
*
|
|
* @rdesc
|
|
* TRUE if plsrun refers to an autonumbering run
|
|
*/
|
|
BOOL COls::SetRun(PLSRUN plsrun)
|
|
{
|
|
LONG cp = plsrun->_cp;
|
|
_pme->SetCp(cp & 0x7FFFFFFF);
|
|
return plsrun->IsBullet();
|
|
}
|
|
|
|
/*
|
|
* IsSelected()
|
|
*
|
|
* @mfunc
|
|
* return whether or not the run should be drawn as selected.
|
|
*
|
|
*/
|
|
CLsrun::IsSelected(void)
|
|
{
|
|
if (!_fSelected)
|
|
return FALSE;
|
|
CRenderer *pre = g_pols->GetRenderer();
|
|
Assert(pre->IsRenderer());
|
|
return pre->_fRenderSelection ? TRUE : FALSE;
|
|
}
|
|
|
|
/*
|
|
* CreatePlsrun (void)
|
|
*
|
|
* @func
|
|
* Creates a PLSRUN. Is a little tricky because we allocate them in
|
|
* chunks.
|
|
*
|
|
* @rdesc
|
|
*/
|
|
const int cplsrunAlloc = 8;
|
|
PLSRUN COls::CreatePlsrun()
|
|
{
|
|
CLsrunChunk *plsrunChunk = 0;
|
|
|
|
//First, find a chunk to use
|
|
int cchunk = _rglsrunChunk.Count();
|
|
for (int ichunk = 0; cchunk && ichunk < cchunk; ichunk++)
|
|
{
|
|
plsrunChunk = _rglsrunChunk.Elem(ichunk);
|
|
if (plsrunChunk->_cel < cplsrunAlloc)
|
|
break;
|
|
}
|
|
|
|
if (!cchunk || ichunk == cchunk || plsrunChunk->_cel == cplsrunAlloc)
|
|
{
|
|
CLsrun *rglsrun = new CLsrun[cplsrunAlloc];
|
|
if (rglsrun)
|
|
{
|
|
plsrunChunk = _rglsrunChunk.Add(1, 0);
|
|
if (!plsrunChunk)
|
|
{
|
|
delete[] rglsrun;
|
|
return 0;
|
|
}
|
|
plsrunChunk->_prglsrun = rglsrun;
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
return &plsrunChunk->_prglsrun[plsrunChunk->_cel++];
|
|
}
|
|
|
|
/*
|
|
* GetPlsrun(cp, pCF, fAutoNumber)
|
|
*
|
|
* @func
|
|
* Return plsrun for info in run. The structure contains the starting cp
|
|
* of the run and the script analysis if Uniscribe is activated. The
|
|
* analysis information is needed by subsequent callbacks - GetGlyphs and
|
|
* GetGlyphPositions to be passed to Uniscribe in order to shape and
|
|
* position glyphs correctly for complex scripts.
|
|
*
|
|
* @rdesc
|
|
* plsrun corresponding to info in arguments
|
|
*/
|
|
PLSRUN COls::GetPlsrun(
|
|
LONG cp,
|
|
const CCharFormat *pCF,
|
|
BOOL fAutoNumber)
|
|
{
|
|
if(fAutoNumber)
|
|
cp |= CP_BULLET;
|
|
|
|
CLsrun *plsrun = CreatePlsrun();
|
|
|
|
if (plsrun)
|
|
{
|
|
ZeroMemory(plsrun, sizeof(CLsrun));
|
|
|
|
plsrun->_pCF = pCF;
|
|
plsrun->_cp = fAutoNumber ? _cp | CP_BULLET : cp;
|
|
|
|
LONG cpSelMin, cpSelMost;
|
|
_pme->GetPed()->GetSelRangeForRender(&cpSelMin, &cpSelMost);
|
|
|
|
plsrun->SetSelected(!plsrun->IsBullet() && cp >= cpSelMin && cp < cpSelMost);
|
|
|
|
if (pCF->_wScript && !_pme->GetPasswordChar())
|
|
{
|
|
CUniscribe* pusp = _pme->Getusp();
|
|
Assert(pusp);
|
|
const SCRIPT_PROPERTIES* psp = pusp->GeteProp(pCF->_wScript);
|
|
|
|
plsrun->_a.eScript = (pCF->_wScript < SCRIPT_MAX_COUNT) ? pCF->_wScript : 0;
|
|
plsrun->_a.fRTL = !psp->fNumeric && (IsBiDiCharSet(pCF->_bCharSet) || IsBiDiCharSet(psp->bCharSet));
|
|
plsrun->_a.fLogicalOrder = TRUE;
|
|
}
|
|
}
|
|
return plsrun;
|
|
}
|
|
|
|
/*
|
|
* COls::~COls()
|
|
*
|
|
* @mfunc
|
|
* Destructor
|
|
*/
|
|
COls::~COls()
|
|
{
|
|
for (int ichunk = 0, cchunk = _rglsrunChunk.Count(); ichunk < cchunk; ichunk++)
|
|
delete []_rglsrunChunk.Elem(ichunk)->_prglsrun;
|
|
DestroyLine(NULL);
|
|
if (g_plsc)
|
|
LsDestroyContext(g_plsc);
|
|
}
|
|
|
|
|
|
/*
|
|
* COls::Init(pme)
|
|
*
|
|
* @mfunc
|
|
* Initialize this LineServices object
|
|
*
|
|
* @rdesc
|
|
* HRESULT = (success) ? NOERROR : E_FAIL
|
|
*/
|
|
HRESULT COls::Init(
|
|
CMeasurer *pme)
|
|
{
|
|
_pme = pme;
|
|
|
|
if(g_plsc)
|
|
return NOERROR;
|
|
|
|
// Build LS context to create
|
|
LSCONTEXTINFO lsctxinf;
|
|
|
|
// Setup object handlers
|
|
LSIMETHODS vlsctxinf[OBJID_COUNT];
|
|
vlsctxinf[OBJID_OLE] = vlsimethodsOle;
|
|
if(LsGetReverseLsimethods(&vlsctxinf[OBJID_REVERSE]) != lserrNone)
|
|
return E_FAIL;
|
|
|
|
lsctxinf.cInstalledHandlers = OBJID_COUNT;
|
|
lsctxinf.pInstalledHandlers = &vlsctxinf[0];
|
|
|
|
// Set default and all other characters to 0xFFFF
|
|
memset(&lsctxinf.lstxtcfg, 0xFF, sizeof(lsctxinf.lstxtcfg));
|
|
|
|
lsctxinf.fDontReleaseRuns = TRUE;
|
|
lsctxinf.lstxtcfg.cEstimatedCharsPerLine = cchLineHint;
|
|
|
|
// Set the characters we handle
|
|
lsctxinf.lstxtcfg.wchNull = 0;
|
|
lsctxinf.lstxtcfg.wchSpace = ' ';
|
|
lsctxinf.lstxtcfg.wchHyphen = '-';
|
|
lsctxinf.lstxtcfg.wchTab = '\t';
|
|
lsctxinf.lstxtcfg.wchEndLineInPara = '\v';
|
|
lsctxinf.lstxtcfg.wchEndPara1 = '\r';
|
|
lsctxinf.lstxtcfg.wchEndPara2 = '\n';
|
|
|
|
lsctxinf.lstxtcfg.wchVisiAltEndPara =
|
|
lsctxinf.lstxtcfg.wchVisiEndPara =
|
|
lsctxinf.lstxtcfg.wchVisiEndLineInPara = ' ';
|
|
|
|
lsctxinf.lstxtcfg.wchNonReqHyphen = SOFTHYPHEN;
|
|
|
|
// Auto number escape character
|
|
lsctxinf.lstxtcfg.wchEscAnmRun = wchObjectEnd;
|
|
|
|
lsctxinf.pols = this;
|
|
lsctxinf.lscbk = lscbk;
|
|
|
|
if(LsCreateContext(&lsctxinf, &g_plsc) != lserrNone)
|
|
return E_FAIL;
|
|
|
|
//REVIEW (keithcu) Quill seems to have a more mature kinsoku
|
|
//table. For example, we don't allow breaking across space between
|
|
//a word and the ending punctuation. French people want this behavior.
|
|
BYTE rgbrkpairsKinsoku[cKinsokuCategories][cKinsokuCategories];
|
|
BYTE *prgbrkpairsKinsoku = &rgbrkpairsKinsoku[0][0];
|
|
for(LONG i = 0; i < cKinsokuCategories; i++)
|
|
{
|
|
for(LONG j = 0; j < cKinsokuCategories; j++)
|
|
{
|
|
LONG iBreak = 2*CanBreak(i, j);
|
|
// If don't break, allow break across blanks unless first
|
|
// char is open brace or second char is close brace
|
|
if (!iBreak &&
|
|
GetKinsokuClass(i) != brkclsOpen &&
|
|
GetKinsokuClass(j) != brkclsOpen)
|
|
{
|
|
iBreak = 1;
|
|
}
|
|
*prgbrkpairsKinsoku++ = iBreak;
|
|
}
|
|
}
|
|
if(g_plsc->SetBreaking(ARRAY_SIZE(rglsbrkDefault), rglsbrkDefault,
|
|
cKinsokuCategories, &rgbrkpairsKinsoku[0][0]) != lserrNone)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
/*
|
|
* COls::MeasureLine(xWidth, pliTarget)
|
|
*
|
|
* @mfunc
|
|
* Wrapper for LsCreateLine
|
|
*
|
|
* @rdesc
|
|
* TRUE if success, FALSE if failed
|
|
*/
|
|
const int dxpMaxWidth = 0x000FFFFF;
|
|
BOOL COls::MeasureLine(
|
|
LONG xWidth, //@parm Width of line in device units
|
|
CLine * pliTarget) //@parm Returns target-device line metrics (optional)
|
|
{
|
|
LONG xWidthActual = xWidth;
|
|
CMeasurer *pme = _pme;
|
|
const CParaFormat *pPF = pme->Get_pPF();
|
|
const CDisplay *pdp = pme->_pdp;
|
|
|
|
LONG cp = pme->GetCp();
|
|
#ifdef DEBUG
|
|
LONG cchText = pme->GetTextLength(); // For DEBUG...
|
|
AssertSz(cp < cchText || !pme->IsRich() && cp == cchText, "COls::Measure: trying to measure past EOD");
|
|
#endif
|
|
DestroyLine(NULL);
|
|
|
|
_cp = cp;
|
|
_pdp = pdp;
|
|
_fCheckFit = FALSE;
|
|
pme->SetUseTargetDevice(FALSE);
|
|
|
|
LSDEVRES lsdevres;
|
|
lsdevres.dyrInch = pme->_dyrInch;
|
|
lsdevres.dxrInch = pme->_dxrInch;
|
|
lsdevres.dypInch = pme->_dypInch;
|
|
lsdevres.dxpInch = pme->_dxpInch;
|
|
|
|
g_plsc->SetDoc(TRUE, lsdevres.dyrInch == lsdevres.dypInch &&
|
|
lsdevres.dxrInch == lsdevres.dxpInch, &lsdevres);
|
|
|
|
if(xWidth == -1)
|
|
{
|
|
if (pdp->GetMaxWidth())
|
|
xWidth = xWidthActual = pme->LXtoDX(pdp->GetMaxWidth());
|
|
else
|
|
xWidth = xWidthActual = max(0, pdp->GetMaxPixelWidth() - dxCaret);
|
|
}
|
|
|
|
if(!pdp->GetWordWrap())
|
|
{
|
|
xWidth = dxpMaxWidth;
|
|
BOOL fNearJust = pPF->_bAlignment == PFA_LEFT && !pPF->IsRtlPara() ||
|
|
pPF->_bAlignment == PFA_RIGHT && pPF->IsRtlPara();
|
|
if (!fNearJust)
|
|
_fCheckFit = TRUE;
|
|
}
|
|
|
|
|
|
DWORD cBreakRecOut;
|
|
LSLINFO lslinfo;
|
|
BREAKREC rgBreak[MAX_OBJ_DEPTH];
|
|
_xWidth = xWidth;
|
|
|
|
LSERR lserr = g_plsc->CreateLine(cp, pme->DXtoLX(xWidth), NULL, 0, MAX_OBJ_DEPTH, rgBreak,
|
|
&cBreakRecOut, &lslinfo, &_plsline);
|
|
|
|
if (_fCheckFit)
|
|
{
|
|
long upJunk, upStartTrailing;
|
|
LsQueryLineDup(_plsline, &upJunk, &upJunk, &upJunk, &upStartTrailing, &upJunk);
|
|
|
|
if (pme->_pPF->InTable())
|
|
{
|
|
// We play games in case of tables, so we should change width to get proper alignment.
|
|
//
|
|
// LS formats from negative left indent of -_dxOffset, but we'll actually display
|
|
// from the +_dxOffset. We will also lie to LS (or is is truth) about position of
|
|
// the last tab. As a result of this, LS thinks the line by _dxOffset shorter than
|
|
// it really is.
|
|
xWidthActual -= pme->LXtoDX(pme->_pPF->_dxOffset);
|
|
}
|
|
|
|
if (upStartTrailing < xWidthActual)
|
|
{
|
|
_xWidth = xWidth = xWidthActual;
|
|
_fCheckFit = FALSE;
|
|
DestroyLine(NULL);
|
|
lserr = g_plsc->CreateLine(cp, pme->DXtoLX(xWidth), NULL, 0, MAX_OBJ_DEPTH, rgBreak,
|
|
&cBreakRecOut, &lslinfo, &_plsline);
|
|
}
|
|
}
|
|
|
|
//Line Services doesn't put the autonumbering dimensions into the line,
|
|
//so we have to do it ourselves.
|
|
lslinfo.dvpAscent = max(lslinfo.dvpAscent, lslinfo.dvpAscentAutoNumber);
|
|
lslinfo.dvpDescent = max(lslinfo.dvpDescent, lslinfo.dvpDescentAutoNumber);
|
|
|
|
pme->SetUseTargetDevice(FALSE);
|
|
|
|
lslinfo.cpLim = GetCpReFromCpLs(lslinfo.cpLim);
|
|
|
|
if (lserr != lserrNone)
|
|
{
|
|
AssertSz(lserr == lserrOutOfMemory, "Line format failed for invalid reason");
|
|
pme->GetPed()->GetCallMgr()->SetOutOfMemory();
|
|
return FALSE;
|
|
}
|
|
|
|
//REVIEW (keithcu) Doing this hit-testing during measurement is slow--is it
|
|
//worth it to cache this data?
|
|
if(!pme->IsRenderer())
|
|
{
|
|
long upJunk, upStart, upStartTrailing, upLimLine;
|
|
|
|
// Save some LineServices results in the measurer's CLine
|
|
pme->_li._cch = lslinfo.cpLim - cp;
|
|
AssertSz(pme->_li._cch > 0, "no cps on line");
|
|
|
|
// Query line services for line width and indent.
|
|
LsQueryLineDup(_plsline, &upJunk, &upJunk, &upStart, &upStartTrailing, &upLimLine);
|
|
long dupWidth = upStartTrailing - upStart;
|
|
|
|
pme->_li._xLeft = upStart;
|
|
pme->_li._xWidth = dupWidth;
|
|
|
|
if(pme->IsRich())
|
|
{
|
|
pme->_li._yHeight = lslinfo.dvpAscent + lslinfo.dvpDescent;
|
|
pme->_li._yDescent = lslinfo.dvpDescent;
|
|
}
|
|
else
|
|
pme->CheckLineHeight(); // Use default heights
|
|
|
|
pme->_li._cchEOP = 0;
|
|
|
|
pme->SetCp(lslinfo.cpLim);
|
|
if(pme->_rpTX.IsAfterEOP()) // Line ends with an EOP
|
|
{ // Store cch of EOP (1 or 2)
|
|
pme->_rpTX.BackupCpCRLF(FALSE);
|
|
UINT ch = pme->GetChar();
|
|
if(ch == CR || pme->GetPed()->fUseCRLF() && ch == LF)
|
|
pme->_li._bFlags |= fliHasEOP;
|
|
pme->_li._cchEOP = pme->_rpTX.AdvanceCpCRLF(FALSE);
|
|
}
|
|
if (lslinfo.cpLim > pme->GetTextLength() &&
|
|
(!pme->IsRich() || pme->IsHidden()))
|
|
{
|
|
Assert(lslinfo.cpLim == pme->GetTextLength() + 1);
|
|
pme->_li._cch--;
|
|
}
|
|
else
|
|
pme->AdjustLineHeight();
|
|
}
|
|
|
|
//Setup pliTarget if caller requests it
|
|
//FUTURE (KeithCu) If people want target information, then the display
|
|
//information is the same, except that OnFormatRange has a bug.
|
|
if (pliTarget)
|
|
{
|
|
CLine liSave = pme->_li;
|
|
pme->_li._yHeight = max(lslinfo.dvrAscent, lslinfo.dvrAscentAutoNumber) +
|
|
max(lslinfo.dvrDescent, lslinfo.dvrDescentAutoNumber);
|
|
pme->_li._yDescent = lslinfo.dvrDescent;
|
|
pme->SetUseTargetDevice(TRUE);
|
|
pme->AdjustLineHeight();
|
|
pme->SetUseTargetDevice(FALSE);
|
|
*pliTarget = pme->_li;
|
|
pme->_li = liSave;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* COls::RenderLine()
|
|
*
|
|
* @mfunc
|
|
* Wrapper for LsDisplayLine
|
|
*
|
|
* @rdesc
|
|
* TRUE if success, FALSE if failed
|
|
*/
|
|
BOOL COls::RenderLine(
|
|
CLine & li) //@parm Line to render
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "COls::RenderLine");
|
|
|
|
LONG cp = _pme->GetCp();
|
|
CRenderer *pre = GetRenderer();
|
|
LONG xAdj = 0, yAdj = 0;
|
|
Assert(pre->_fRenderer);
|
|
|
|
pre->NewLine(li);
|
|
if(li._fCollapsed) // Line is collapsed in Outline mode
|
|
{
|
|
pre->Advance(li._cch); // Bypass line
|
|
return TRUE; // Let dispml continue with next line
|
|
}
|
|
pre->SetNumber(li._bNumber);
|
|
|
|
CreateOrGetLine();
|
|
if(!_plsline)
|
|
return FALSE;
|
|
|
|
pre->SetCp(cp); // Back to beginning of line
|
|
Assert(pre->_fTarget == FALSE);
|
|
pre->Check_pccs(FALSE);
|
|
pre->SetClipRect();
|
|
|
|
HDC hdcSave = NULL;
|
|
if(li._cch > 0 && pre->fUseOffScreenDC() && (li._bFlags & fliUseOffScreenDC))
|
|
{
|
|
// Set up an off-screen DC if we can. Note that if this fails,
|
|
// we just use the regular DC which won't look as nice but
|
|
// will at least display something readable.
|
|
hdcSave = pre->SetUpOffScreenDC(xAdj, yAdj);
|
|
|
|
// Is this a uniform text being rendered off screen?
|
|
if(li._bFlags & fliOffScreenOnce)
|
|
{
|
|
// Yes - turn off special rendering since line has been rendered
|
|
li._bFlags &= ~(fliOffScreenOnce | fliUseOffScreenDC);
|
|
}
|
|
}
|
|
POINT pt = pre->GetCurPoint(); // Must follow offscreen setup
|
|
RECT rc = pre->GetClipRect(); // since _ptCur, _rc change
|
|
LONG x = 0;
|
|
if(pre->_pPF->InTable())
|
|
{
|
|
pt.x += 2*pre->LXtoDX(pre->_pPF->_dxOffset);
|
|
x = pt.x + pre->_li._xLeft;
|
|
}
|
|
|
|
pre->_li._xLeft = 0;
|
|
pre->RenderStartLine();
|
|
|
|
pt.x += pre->XFromU(0);
|
|
|
|
pt.y += li._yHeight - li._yDescent; // Move to baseline for LS
|
|
LSERR lserr = LsDisplayLine(_plsline, &pt, pre->GetPdp()->IsMain() ? ETO_CLIPPED : 0, &rc);
|
|
|
|
AssertSz(lserr == lserrNone, "COls::RenderLine: error in rendering line");
|
|
|
|
pre->EndRenderLine(hdcSave, xAdj, yAdj, x);
|
|
pre->SetCp(cp + li._cch);
|
|
|
|
return lserr == lserrNone;
|
|
}
|
|
|
|
/*
|
|
* COls::CreateOrGetLine()
|
|
*
|
|
* @mfunc
|
|
* If _plsline is nonNull and _cp equals _pme->GetCp(), return. Else
|
|
* create line with caching so that _plsline and _cp are correct for
|
|
* current line
|
|
*/
|
|
void COls::CreateOrGetLine()
|
|
{
|
|
if(_plsline && _pme->GetCp() == _cp && _pme->_pdp == _pdp)
|
|
return;
|
|
|
|
MeasureLine(-1, NULL); // Define new _plsline
|
|
}
|
|
|
|
/*
|
|
* COls::MeasureText(cch, taMode, pdx, pdy)
|
|
*
|
|
* @mfunc
|
|
* Gets x offset to cp given by CMeasurer _pme + cch chars along with
|
|
* display dimensions.
|
|
*
|
|
* @rdesc
|
|
* xwidth measured
|
|
*/
|
|
LONG COls::MeasureText(
|
|
LONG cch, //(IN): Max cch to measure
|
|
UINT taMode, //(IN): requested coordinate
|
|
CDispDim *pdispdim) //(OUT): display dimensions
|
|
{
|
|
CMeasurer * pme = _pme;
|
|
LONG cp = pme->GetCp() + cch; // Enter with me at start of line
|
|
POINT pt; // Point at cp in client coords
|
|
BOOL fAtLogicalRightEdge = FALSE;
|
|
|
|
CreateOrGetLine();
|
|
if(!_plsline)
|
|
return 0;
|
|
Assert(pme->_fTarget == FALSE);
|
|
|
|
// Query point from cp
|
|
DWORD cActualDepth;
|
|
LSQSUBINFO lsqSubInfo[MAX_OBJ_DEPTH];
|
|
LSTEXTCELL lsTextCell;
|
|
|
|
memset(&lsTextCell, 0, sizeof(lsTextCell));
|
|
|
|
LsQueryLineCpPpoint(_plsline, GetCpLsFromCpRe(cp), MAX_OBJ_DEPTH, &lsqSubInfo[0],
|
|
&cActualDepth, &lsTextCell);
|
|
|
|
pdispdim->lstflow = lsqSubInfo[cActualDepth - 1].lstflowSubline;
|
|
pdispdim->dx = lsTextCell.dupCell;
|
|
|
|
LSTFLOW lstflowLine = lsqSubInfo[0].lstflowSubline;
|
|
|
|
POINT ptStart = {pme->XFromU(0), pme->_li._yHeight - pme->_li._yDescent};
|
|
POINTUV ptuv = lsTextCell.pointUvStartCell;
|
|
|
|
if(taMode & (TA_STARTOFLINE | TA_ENDOFLINE) && cActualDepth > 1)
|
|
{
|
|
ptuv = lsqSubInfo[0].pointUvStartRun;
|
|
if(taMode & TA_ENDOFLINE)
|
|
ptuv.u += lsqSubInfo[0].dupRun;
|
|
}
|
|
|
|
//If they ask for position inside ligature or at lim of line, give right edge of cell
|
|
else if (cp > GetCpReFromCpLs(lsTextCell.cpStartCell))
|
|
{
|
|
fAtLogicalRightEdge = TRUE;
|
|
if (lstflowLine != pdispdim->lstflow)
|
|
ptuv.u -= lsTextCell.dupCell;
|
|
else
|
|
ptuv.u += lsTextCell.dupCell;
|
|
}
|
|
|
|
LsPointXYFromPointUV(&ptStart, lstflowLine, &ptuv, &pt);
|
|
|
|
if(pme->_pPF->InTable())
|
|
pt.x += 2*pme->LXtoDX(pme->_pPF->_dxOffset);
|
|
|
|
if (pdispdim->lstflow == lstflowWS && !(taMode & (TA_LOGICAL | TA_STARTOFLINE)))
|
|
{
|
|
if (fAtLogicalRightEdge)
|
|
{
|
|
if ((taMode & TA_RIGHT) == TA_RIGHT)
|
|
pt.x += pdispdim->dx;
|
|
else if (taMode & TA_CENTER)
|
|
pt.x += pdispdim->dx / 2;
|
|
return pt.x;
|
|
}
|
|
else
|
|
pt.x -= pdispdim->dx;
|
|
}
|
|
|
|
LONG dx = 0;
|
|
|
|
if(taMode & TA_CENTER && !fAtLogicalRightEdge)
|
|
dx = pdispdim->dx;
|
|
if((taMode & TA_CENTER) == TA_CENTER)
|
|
dx >>= 1;
|
|
|
|
if (pdispdim->lstflow == lstflowWS && (taMode & TA_LOGICAL))
|
|
dx = -dx;
|
|
return max(0, pt.x + dx);
|
|
}
|
|
|
|
/*
|
|
* COls::CchFromXpos(pt, &dx)
|
|
*
|
|
* @mfunc
|
|
* Moves _pme to pt.x. Calls LsQueryLinePointPcp()
|
|
*/
|
|
void COls::CchFromXpos(
|
|
POINT pt, //@parm Point to find cch for in line
|
|
CDispDim *pdispdim, //@parm dimensions of object
|
|
LONG *pcpActual) //@parm CP point
|
|
{
|
|
TRACEBEGIN(TRCSUBSYSDISP, TRCSCOPEINTERN, "COls::CchFromXpos");
|
|
|
|
if(_pme->_pPF->InTable())
|
|
pt.x -= 2*_pme->LXtoDX(_pme->_pPF->_dxOffset);
|
|
|
|
// Make point relative to LS coordinate system - (0,0) in LS is at the
|
|
// baseline of the line.
|
|
POINTUV ptuv = {_pme->UFromX(pt.x), -pt.y + _pme->_li._yHeight - _pme->_li._yDescent};
|
|
|
|
LONG cpStart = _pme->GetCp();
|
|
|
|
CreateOrGetLine();
|
|
if(!_plsline)
|
|
return;
|
|
Assert(_pme->_fTarget == FALSE);
|
|
|
|
DWORD cActualDepth;
|
|
LSQSUBINFO lsqSubInfo[MAX_OBJ_DEPTH];
|
|
LSTEXTCELL lsTextCell;
|
|
|
|
memset(&lsTextCell, 0, sizeof(lsTextCell));
|
|
|
|
LsQueryLinePointPcp(_plsline, &ptuv, MAX_OBJ_DEPTH, &lsqSubInfo[0], &cActualDepth, &lsTextCell);
|
|
|
|
if (cActualDepth == 0) //If we got back empty textcell, let's just query cp explicitly to get information
|
|
{
|
|
LsQueryLineCpPpoint(_plsline, cpStart, MAX_OBJ_DEPTH, &lsqSubInfo[0], &cActualDepth, &lsTextCell);
|
|
Assert(cActualDepth != 0);
|
|
}
|
|
|
|
pdispdim->dx = lsTextCell.dupCell;
|
|
pdispdim->lstflow = lsqSubInfo[cActualDepth - 1].lstflowSubline;
|
|
|
|
LONG cp = *pcpActual = GetCpReFromCpLs(lsTextCell.cpStartCell);
|
|
|
|
POINTUV ptuvCell;
|
|
//Convert the hit-test point from u,v of line to u,v of cell
|
|
LsPointUV2FromPointUV1(lsqSubInfo[0].lstflowSubline, &lsTextCell.pointUvStartCell, &ptuv,
|
|
lsqSubInfo[cActualDepth - 1].lstflowSubline, &ptuvCell);
|
|
|
|
if(ptuvCell.u > lsTextCell.dupCell/2)
|
|
cp += lsTextCell.cpEndCell - lsTextCell.cpStartCell + 1;
|
|
|
|
if (_pme->GetPed()->_pbrk)
|
|
{
|
|
// If text breaker is up, verify cluster before placing the caret
|
|
CTxtBreaker* pbrk = _pme->GetPed()->_pbrk;
|
|
LONG cpEnd = _pme->GetPed()->GetTextLength();
|
|
while (cp < cpEnd && !pbrk->CanBreakCp(BRK_CLUSTER, cp))
|
|
cp++;
|
|
}
|
|
|
|
_pme->_li._cch = cp - _cp;
|
|
_pme->SetCp(cp);
|
|
}
|
|
|
|
/*
|
|
* COls::DestroyLine(pdp)
|
|
*
|
|
* @mfunc
|
|
* Destroys any line data structures.
|
|
*/
|
|
void COls::DestroyLine(CDisplay *pdp)
|
|
{
|
|
CLock lock;
|
|
if (pdp && pdp != _pdp)
|
|
return;
|
|
|
|
if(_plsline)
|
|
{
|
|
g_plsc->DestroyLine(_plsline);
|
|
_plsline = NULL;
|
|
}
|
|
if (_rgcp.Count())
|
|
_rgcp.Clear(AF_KEEPMEM);
|
|
|
|
int cchunk = _rglsrunChunk.Count();
|
|
for (int ichunk = 0; ichunk < cchunk; ichunk++)
|
|
_rglsrunChunk.Elem(ichunk)->_cel = 0;
|
|
}
|
|
|
|
/*
|
|
* LimitChunk(pch, &cchChunk, f10Mode)
|
|
*
|
|
* @func
|
|
* Return object ID at *pch and shorten cchChunk to 1 if object isn't
|
|
* text and to the count of text chars up to a nontext object if one
|
|
* occurs within cchChunk and within the current paragraph.
|
|
*
|
|
* @rdesc
|
|
* Object ID at *pch
|
|
*/
|
|
DWORD LimitChunk(const WCHAR *pch, LONG &cchChunk, BOOL f10Mode)
|
|
{
|
|
for(LONG i = 0; i < cchChunk && *pch != CR; i++, pch++)
|
|
{
|
|
switch(*pch)
|
|
{
|
|
case WCH_EMBEDDING:
|
|
if(i == 0)
|
|
{
|
|
cchChunk = 1;
|
|
return OBJID_OLE; // Entered at an OLE object
|
|
}
|
|
cchChunk = i; // Will break before
|
|
break;
|
|
|
|
case EURO:
|
|
if (i == 0)
|
|
{
|
|
for(; i < cchChunk && *pch == EURO; i++)
|
|
pch++;
|
|
}
|
|
cchChunk = i;
|
|
break;
|
|
|
|
case FF:
|
|
if(f10Mode) // RE 1.0 treats FFs as
|
|
continue; // ordinary characters
|
|
|
|
cchChunk = i; // Will break before
|
|
break;
|
|
}
|
|
}
|
|
|
|
return idObjTextChp;
|
|
}
|
|
|
|
/*
|
|
* SetLsChp(dwObjId, pme, plsChp)
|
|
*
|
|
* @func
|
|
* Helper function that initializes an LS chp from RE CCharFormat
|
|
*
|
|
* @rdesc
|
|
* TRUE iff IsHidden()
|
|
*/
|
|
BOOL COls::SetLsChp(
|
|
DWORD dwObjId, //(IN): Object id
|
|
PLSRUN plsrun, //(IN): Current Run
|
|
PLSCHP plsChp) //(OUT): LS chp
|
|
{
|
|
ZeroMemory(plsChp, sizeof(*plsChp));
|
|
plsChp->idObj = (WORD)dwObjId;
|
|
|
|
//If we do FE or Latin kerning, we need to set dcpMaxContext to 2
|
|
|
|
if (_pme->GetPed()->IsComplexScript() && plsrun->_a.eScript && !plsrun->IsBullet())
|
|
{
|
|
CUniscribe* pusp = _pme->Getusp();
|
|
Assert (pusp);
|
|
const SCRIPT_PROPERTIES *psp = pusp->GeteProp(plsrun->_a.eScript);
|
|
|
|
if (psp->fComplex || plsrun->_a.fRTL ||
|
|
psp->fNumeric && W32->GetDigitSubstitutionMode() != DIGITS_NOTIMPL)
|
|
{
|
|
// 1. Complex script
|
|
// 2. RTL (internal direction) run (handle mirror glyph i.e.'?')
|
|
// 3. Numeric run and substitution mode is either Native or Context
|
|
|
|
plsChp->fGlyphBased = TRUE;
|
|
}
|
|
}
|
|
|
|
DWORD dwEffects = plsrun->_pCF->_dwEffects;
|
|
|
|
if(dwEffects & (CFE_UNDERLINE | CFE_LINK | CFE_REVISED))
|
|
plsChp->fUnderline = TRUE;
|
|
|
|
if(dwEffects & CFE_STRIKEOUT && !plsrun->IsBullet())
|
|
plsChp->fStrike = TRUE;
|
|
|
|
if (plsrun->_pCF->_yOffset || dwEffects & (CFE_SUPERSCRIPT | CFE_SUBSCRIPT))
|
|
{
|
|
_pme->SetUseTargetDevice(FALSE);
|
|
CCcs *pccs = _pme->Check_pccs(plsrun->IsBullet());
|
|
LONG yOffset, yAdjust;
|
|
pccs->GetOffset(plsrun->_pCF, _pme->_dypInch, &yOffset, &yAdjust);
|
|
|
|
plsChp->dvpPos += yOffset + yAdjust;
|
|
}
|
|
|
|
//If its an OLE object, but the Object doesn't exist yet, then hide it
|
|
if (dwObjId == OBJID_OLE)
|
|
{
|
|
COleObject * pobj = _pme->GetPed()->GetObjectMgr()->GetObjectFromCp(_pme->GetCp());
|
|
if (!pobj)
|
|
return TRUE;
|
|
}
|
|
return dwEffects & CFE_HIDDEN;
|
|
}
|
|
|
|
/*
|
|
* FetchAnmRun(pols, cp, plpwchRun, pcchRun, pfHidden, plsChp, pplsrun)
|
|
*
|
|
* @func
|
|
* LineServices fetch bullets/numbering callback
|
|
*
|
|
* @rdesc
|
|
* LSERR
|
|
*/
|
|
LSERR WINAPI COls::FetchAnmRun(
|
|
LSCP cp, //@parm [IN]: RE cp
|
|
LPCWSTR *plpwchRun, //@parm [OUT]: Run of characters
|
|
DWORD * pcchRun, //@parm [OUT]: Count of characters in run
|
|
BOOL * pfHidden, //@parm [OUT]: fHidden run?
|
|
PLSCHP plsChp, //@parm [OUT]: Char properties of run
|
|
PLSRUN * pplsrun) //@parm [OUT]: Abstract representation of run properties
|
|
{
|
|
if (cp == cpFirstAnm && _pme->Get_pPF()->IsRtlPara())
|
|
{
|
|
ZeroMemory(plsChp, sizeof(*plsChp));
|
|
plsChp->idObj = OBJID_REVERSE;
|
|
*pfHidden = 0; *pcchRun = 1;
|
|
*pplsrun = GetPlsrun(_pme->GetCp(), &_CFBullet, TRUE);
|
|
*plpwchRun = &_szAnm[0];
|
|
return lserrNone;
|
|
}
|
|
|
|
*plpwchRun = &_szAnm[cp - cpFirstAnm];
|
|
*pcchRun = _cchAnm - (cp - cpFirstAnm);
|
|
*pplsrun = GetPlsrun(_pme->GetCp(), &_CFBullet, TRUE);
|
|
SetLsChp(idObjTextChp, *pplsrun, plsChp);
|
|
*pfHidden = FALSE;
|
|
|
|
if (!_pme->GetNumber())
|
|
plsChp->fUnderline = FALSE;
|
|
|
|
return lserrNone;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* OlsFetchRun(pols, cp, plpwchRun, pcchRun, pfHidden, plsChp, pplsrun)
|
|
*
|
|
* @func
|
|
* LineServices fetch-run callback
|
|
*
|
|
* @rdesc
|
|
* LSERR
|
|
*/
|
|
LSERR WINAPI OlsFetchRun(
|
|
POLS pols, //@parm [IN]: COls *
|
|
LSCP cpLs, //@parm [IN]: LS cp
|
|
LPCWSTR *plpwchRun, //@parm [OUT]: Run of characters
|
|
DWORD * pcchRun, //@parm [OUT]: Count of characters in run
|
|
BOOL * pfHidden, //@parm [OUT]: Hidden run?
|
|
PLSCHP plsChp, //@parm [OUT]: Char properties of run
|
|
PLSRUN * pplsrun) //@parm [OUT]: Abstract representation of run properties
|
|
{
|
|
if(cpLs < 0)
|
|
return pols->FetchAnmRun(cpLs, plpwchRun, pcchRun, pfHidden, plsChp, pplsrun);
|
|
|
|
CMeasurer *pme = pols->GetMeasurer();
|
|
CTxtEdit *ped = pme->GetPed();
|
|
BOOL fStart = FALSE;
|
|
BOOL fFetchBraces = ped->IsBiDi() && g_pusp && g_pusp->IsValid() &&
|
|
!ped->_fItemizePending && ped->GetAdjustedTextLength();
|
|
WCHAR chPassword = pme->GetPasswordChar();
|
|
LONG cpAccelerator = ped->GetCpAccelerator();
|
|
BOOL fAccelerator = FALSE;
|
|
BOOL f10Mode = ped->Get10Mode();
|
|
|
|
if (cpLs == pols->_cp)
|
|
{
|
|
//If we are formatting (or re-formatting) the line, cleanup
|
|
if (pols->_rgcp.Count())
|
|
pols->_rgcp.Clear(AF_KEEPMEM);
|
|
pols->_cEmit = 0;
|
|
}
|
|
long cpRe = pols->GetCpReFromCpLs(cpLs);
|
|
|
|
|
|
pme->SetCp(cpRe); // start fetching at given cp
|
|
|
|
|
|
if (fFetchBraces && pme->_rpCF.IsValid())
|
|
{
|
|
// consider emitting braces only at the run boundary or start of a fetched line
|
|
//
|
|
if (cpRe == pols->_cp || !pme->GetIchRunCF() || !pme->GetCchLeftRunCF())
|
|
{
|
|
SHORT cBrClose, cBrOpen;
|
|
BYTE bBaseLevel = pme->IsParaRTL() ? 1 : 0;
|
|
BYTE bLevel, bLevelPrev;
|
|
|
|
bLevelPrev = bLevel = bBaseLevel; // assume base level
|
|
|
|
if (cpRe < ped->GetTextLength())
|
|
{
|
|
CBiDiLevel level;
|
|
|
|
bLevel = pme->_rpCF.GetLevel(&level); // got level of current run
|
|
fStart = level._fStart;
|
|
}
|
|
|
|
if (cpRe > pols->_cp && pme->Advance(-1))
|
|
{
|
|
if (pme->_rpPF.SameLevel(bBaseLevel)) // preceding run may be hidden
|
|
bLevelPrev = pme->_rpCF.GetLevel(); // got level of preceding run
|
|
pme->Advance(1); // resume position
|
|
}
|
|
|
|
cBrOpen = cBrClose = bLevel - bLevelPrev;
|
|
|
|
if (fStart) // start embedding at the current run
|
|
cBrClose = bBaseLevel - bLevelPrev; // this means we must close all braces of preceding run
|
|
|
|
cBrClose = max(0, -cBrClose);
|
|
|
|
if (cBrClose > 0 && pols->BracesBeforeCp(cpLs) < cBrClose)
|
|
{
|
|
// emit close braces
|
|
|
|
if (pols->_cEmit > 0)
|
|
{
|
|
EmitBrace(pols, plsChp, pfHidden, pcchRun, pplsrun, plpwchRun, idObjTextChp, rgchObjectEnd);
|
|
if (pols->AddBraceCp(cpLs))
|
|
pols->_cEmit--;
|
|
#ifdef DEBUG_BRACE
|
|
Tracef(TRCSEVNONE, "CLOSE(%d) cpLs %d: emitted %d", cBrClose, cpLs, pols->_cEmit);
|
|
#endif
|
|
return lserrNone;
|
|
}
|
|
else
|
|
{
|
|
// We assert. You can click "Ignore All" with no hang.
|
|
AssertSz(FALSE, "Prevent emitting close brace (no open counterpart)");
|
|
}
|
|
}
|
|
|
|
if (fStart) // start embedding at the current run
|
|
cBrOpen = bLevel - bBaseLevel; // we begin openning braces
|
|
|
|
if (cBrOpen > 0 && pols->BracesBeforeCp(cpLs) < cBrOpen + cBrClose)
|
|
{
|
|
// emit open braces
|
|
|
|
EmitBrace(pols, plsChp, pfHidden, pcchRun, pplsrun, plpwchRun, OBJID_REVERSE, L" ");
|
|
if (pols->AddBraceCp(cpLs))
|
|
pols->_cEmit++;
|
|
#ifdef DEBUG_BRACE
|
|
Tracef(TRCSEVNONE, "OPEN(%d) cpLs %d: emitted %d", cBrOpen, cpLs, pols->_cEmit);
|
|
#endif
|
|
return lserrNone;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Done fetching braces.
|
|
// Begin getting real data...
|
|
|
|
|
|
#ifdef DEBUG_BRACE
|
|
Tracef(TRCSEVNONE, "cpLs %d: emitted %d", cpLs, pols->_cEmit);
|
|
#endif
|
|
|
|
// Initialized chunk to count of characters in format run
|
|
LONG cchChunk = pme->GetCchLeftRunCF();
|
|
DWORD dwObjId = idObjTextChp;
|
|
|
|
if(!pme->IsHidden()) // Run isn't hidden
|
|
{
|
|
LONG cch;
|
|
|
|
*plpwchRun = pme->GetPch(cch); // Get text in run
|
|
cchChunk = min(cchChunk, cch); // Maybe less than cchChunk
|
|
if (!pme->GetPdp()->IsMetafile())
|
|
cchChunk = min(cchChunk, cchLineHint);
|
|
|
|
if (chPassword)
|
|
{
|
|
cchChunk = min(cchChunk, int(sizeof(pols->_rgchTemp) / sizeof(WCHAR)));
|
|
memcpy(pols->_rgchTemp, *plpwchRun, cchChunk * sizeof(WCHAR));
|
|
|
|
for (int i = 0; i < cchChunk; i++)
|
|
{
|
|
if (!IsASCIIEOP((*plpwchRun)[i]))
|
|
pols->_rgchTemp[i] = chPassword;
|
|
else
|
|
pols->_rgchTemp[i] = (*plpwchRun)[i];
|
|
}
|
|
*plpwchRun = pols->_rgchTemp;
|
|
}
|
|
|
|
if(cpAccelerator != -1)
|
|
{
|
|
LONG cpCur = pme->GetCp(); // Get current cp
|
|
|
|
// Does accelerator character fall in this chunk?
|
|
if (cpCur < cpAccelerator &&
|
|
cpCur + cchChunk > cpAccelerator)
|
|
{
|
|
// Yes. Reduce chunk to char just before accelerator
|
|
cchChunk = cpAccelerator - cpCur;
|
|
}
|
|
// Is this character the accelerator?
|
|
else if(cpCur == cpAccelerator)
|
|
{ // Set chunk size to 1 since only
|
|
cchChunk = 1; // want to output underlined char
|
|
fAccelerator = TRUE; // Tell downstream routines that
|
|
// we're handling accelerator
|
|
}
|
|
}
|
|
if(pme->GetCF()->_dwEffects & CFE_ALLCAPS)
|
|
{
|
|
cchChunk = min(cchChunk, int(sizeof(pols->_rgchTemp) / sizeof(WCHAR)));
|
|
memcpy(pols->_rgchTemp, *plpwchRun, cchChunk * sizeof(WCHAR));
|
|
CharUpperBuff(pols->_rgchTemp, cchChunk);
|
|
*plpwchRun = pols->_rgchTemp;
|
|
}
|
|
|
|
//Line Services handles page breaks in a weird way, so lets just convert to a CR.
|
|
if (*plpwchRun && *(*plpwchRun) == FF && !f10Mode)
|
|
{
|
|
pols->_szAnm[0] = CR;
|
|
*plpwchRun = pols->_szAnm;
|
|
cchChunk = 1;
|
|
}
|
|
|
|
AssertSz(cpRe < ped->GetTextLength() || !ped->IsRich(), "0-length run at end of control");
|
|
AssertSz(cch || !ped->IsRich(), "0-length run at end of control");
|
|
|
|
// Set run size appropriately for any objects that are in run
|
|
dwObjId = LimitChunk(*plpwchRun, cchChunk, f10Mode);
|
|
|
|
// Get regular highlighted positions
|
|
LONG cpSelMin, cpSelMost;
|
|
ped->GetSelRangeForRender(&cpSelMin, &cpSelMost);
|
|
|
|
if(cpSelMin != cpSelMost)
|
|
{
|
|
if(cpRe >= cpSelMin)
|
|
{
|
|
if(cpRe < cpSelMost)
|
|
{
|
|
// Current text falls inside selection
|
|
cch = cpSelMost - cpRe;
|
|
cchChunk = min(cchChunk, cch);
|
|
}
|
|
}
|
|
else if(cpRe + cchChunk >= cpSelMin)
|
|
{
|
|
// cp < cpSelMin - run starts outside of selection.
|
|
// Limit text to start of selection.
|
|
cchChunk = cpSelMin - cpRe;
|
|
}
|
|
}
|
|
}
|
|
|
|
*pplsrun = pols->GetPlsrun(cpRe, pme->GetCF(), FALSE);
|
|
*pfHidden = pols->SetLsChp(dwObjId, *pplsrun, plsChp);
|
|
|
|
if (fAccelerator)
|
|
plsChp->fUnderline = TRUE;
|
|
|
|
if(!cchChunk) // Happens in plain-text controls
|
|
{ // and if hidden text to end of story
|
|
if (!ped->IsRich() && pols->_cEmit > 0)
|
|
{
|
|
EmitBrace(pols, plsChp, pfHidden, pcchRun, pplsrun, plpwchRun, idObjTextChp, rgchObjectEnd);
|
|
TRACEWARNSZ("(plain)Auto-emit a close brace to make balance");
|
|
if (pols->AddBraceCp(cpLs))
|
|
pols->_cEmit--;
|
|
return lserrNone;
|
|
}
|
|
cchChunk = 1;
|
|
*plpwchRun = szCR;
|
|
*pfHidden = FALSE;
|
|
//Paragraph marks should not have any script state associated with them,
|
|
//even if the pCF that point to does.
|
|
ZeroMemory(&(*pplsrun)->_a, sizeof((*pplsrun)->_a));
|
|
}
|
|
*pcchRun = cchChunk;
|
|
|
|
return lserrNone;
|
|
}
|
|
|
|
/*
|
|
* OlsGetAutoNumberInfo (pols, plskalAnm, pwchAdd, plschp, pplsrun,
|
|
* pfWord95Model, pduaSpaceAnm, pduaWidthAnm)
|
|
* @func
|
|
* LineServices fetch autonumbering info callback. Return info needed
|
|
* by LS for auto numbering. Get the chp/run for last char from auto
|
|
* number run. Always say we are Word95 model Anm and get rest of info
|
|
* from paragraph properties.
|
|
*
|
|
* @rdesc
|
|
* LSERR
|
|
*/
|
|
LSERR WINAPI OlsGetAutoNumberInfo(
|
|
POLS pols, //(IN): Client context
|
|
LSKALIGN *plskalAnm, //(OUT):Justification
|
|
PLSCHP plschpAnm,
|
|
PLSRUN *pplsrunAnm,
|
|
WCHAR * pwchAdd, //(OUT):char to add (Nil is treated as none)
|
|
PLSCHP plsChp, //(OUT):chp for bridge character
|
|
PLSRUN * pplsrun, //(OUT):Run for bridge character
|
|
BOOL * pfWord95Model, //(OUT):Type of autonumber run
|
|
long * pduaSpaceAnm, //(OUT):Relevant iff fWord95Model
|
|
long * pduaWidthAnm) //(OUT):Relevant iff fWord95Model
|
|
{
|
|
CMeasurer *pme = pols->GetMeasurer();
|
|
const CParaFormat *pPF = pme->Get_pPF();
|
|
|
|
*pplsrunAnm = *pplsrun = pols->GetPlsrun(pme->GetCp(), &pols->_CFBullet, TRUE);
|
|
pols->SetLsChp(idObjTextChp, *pplsrun, plsChp);
|
|
|
|
if (!pme->GetNumber())
|
|
plsChp->fUnderline = FALSE;
|
|
|
|
*plschpAnm = *plsChp;
|
|
*pwchAdd = '\t';
|
|
*pfWord95Model = TRUE;
|
|
*pduaSpaceAnm = 0;
|
|
*pduaWidthAnm = max(pPF->_dxOffset, pPF->_wNumberingTab);
|
|
*plskalAnm = (LSKALIGN)(lskalLeft + (pPF->_wNumberingStyle & 3));
|
|
|
|
return lserrNone;
|
|
}
|
|
|
|
/*
|
|
* OlsGetNumericSeparators (pols, cp, plspap)
|
|
*
|
|
* @func
|
|
* Get numeric separators needed, e.g., for decimal tabs
|
|
*
|
|
* @rdesc
|
|
* LSERR
|
|
*/
|
|
LSERR WINAPI OlsGetNumericSeparators(
|
|
POLS pols, //(IN): pols
|
|
PLSRUN plsrun, //(IN): Run (cp here)
|
|
WCHAR * pwchDecimal, //(OUT): Decimal separator for this run
|
|
WCHAR * pwchThousands) //(OUT): Thousands separator for this run
|
|
{
|
|
LCID lcid = plsrun->_pCF->_lcid;
|
|
WCHAR ch = TEXT('.');
|
|
|
|
// This may need to be virtualized for Win95/CE...
|
|
::GetLocaleInfo(lcid, LOCALE_SDECIMAL, &ch, 1);
|
|
*pwchDecimal = ch;
|
|
ch = TEXT(',');
|
|
::GetLocaleInfo(lcid, LOCALE_STHOUSAND, &ch, 1);
|
|
*pwchThousands = ch;
|
|
|
|
return lserrNone;
|
|
}
|
|
|
|
|
|
/*
|
|
* OlsFetchPap (pols, cp, plspap)
|
|
*
|
|
* @func
|
|
* Fetch paragraph properties
|
|
*
|
|
* @rdesc
|
|
* LSERR
|
|
*/
|
|
LSERR WINAPI OlsFetchPap(
|
|
POLS pols, //(IN): pols
|
|
LSCP cpLs, //(IN): an arbitrary cp value inside paragraph
|
|
PLSPAP plspap) //(OUT): Paragraph properties.
|
|
{
|
|
CMeasurer *pme = pols->GetMeasurer();
|
|
pme->SetCp(pols->_cp);
|
|
|
|
const CParaFormat *pPF = pme->Get_pPF();
|
|
CTxtEdit * ped = pme->GetPed();
|
|
|
|
// Default all results to 0
|
|
ZeroMemory(plspap, sizeof(*plspap));
|
|
|
|
//LS doesn't really care where the paragraph starts
|
|
plspap->cpFirst = pols->_cp;
|
|
|
|
if(plspap->cpFirst && !pme->fFirstInPara()) // Not first in para: say para
|
|
plspap->cpFirst--; // starts one char earlier
|
|
|
|
plspap->cpFirstContent = plspap->cpFirst;
|
|
|
|
if (pPF->IsRtlPara() && !pPF->InTable())
|
|
plspap->lstflow = lstflowWS;
|
|
|
|
// Alignment
|
|
plspap->lskal = (LSKALIGN) g_rgREtoTOMAlign[pPF->_bAlignment];
|
|
|
|
if (pPF->_bAlignment == PFA_FULL_INTERWORD)
|
|
{
|
|
plspap->lskal = lskalLeft;
|
|
plspap->lskj = lskjFullInterWord;
|
|
}
|
|
|
|
// Kind of EOP
|
|
plspap->lskeop = ped->fUseCRLF() ? lskeopEndPara12 : lskeopEndPara1;
|
|
|
|
if (pPF->IsRtlPara())
|
|
{ //For Line Services, left means near and right means far.
|
|
if (plspap->lskal == lskalLeft)
|
|
plspap->lskal = lskalRight;
|
|
else if (plspap->lskal == lskalRight)
|
|
plspap->lskal = lskalLeft;
|
|
}
|
|
|
|
if (pols->_fCheckFit)
|
|
plspap->lskal = lskalLeft;
|
|
|
|
// Line breaking
|
|
if (pPF->_bAlignment > PFA_FULL_INTERWORD || !ped->fUseSimpleLineBreak() ||
|
|
!pme->GetPdp()->GetWordWrap()) // No word wrap
|
|
{
|
|
plspap->grpf |= fFmiApplyBreakingRules;
|
|
}
|
|
|
|
LONG dx = pPF->_dxRightIndent;
|
|
|
|
plspap->uaRightBreak = dx;
|
|
plspap->uaRightJustify = dx;
|
|
if(ped->IsInOutlineView())
|
|
{
|
|
plspap->uaLeft = lDefaultTab/2 * (pPF->_bOutlineLevel + 1);
|
|
plspap->duaIndent = 0;
|
|
}
|
|
else
|
|
{
|
|
plspap->uaLeft = pPF->_dxStartIndent + pPF->_dxOffset;
|
|
plspap->duaIndent = -pPF->_dxOffset;
|
|
}
|
|
|
|
if(!pPF->InTable() && plspap->uaLeft < 0)
|
|
plspap->uaLeft = 0;
|
|
|
|
// Is this a bulleted paragraph? - ignore bullets in a password
|
|
if(pPF->_wNumbering && pme->fFirstInPara() && !pme->GetPasswordChar() &&
|
|
!pPF->IsNumberSuppressed())
|
|
{
|
|
CCcs *pccs = pme->GetCcsBullet(&pols->_CFBullet);
|
|
if (pccs)
|
|
pccs->Release();
|
|
|
|
plspap->grpf |= fFmiAnm;
|
|
WCHAR *pchAnm = pols->_szAnm;
|
|
pols->_cchAnm = 0;
|
|
|
|
if (pPF->IsRtlPara()) //open character
|
|
*pchAnm++ = ' ';
|
|
|
|
//FUTURE (KeithCu) we turn off Indic digits if there is any Hebrew,
|
|
//which should be refined to do a better job with worldwide documents.
|
|
pols->_cchAnm += pPF->NumToStr(pchAnm, pme->GetNumber(),
|
|
(pme->GetPed()->GetCharFlags() & fHEBREW) ? 0 : fIndicDigits);
|
|
pchAnm += pols->_cchAnm;
|
|
|
|
if (pPF->IsRtlPara()) //End character for reverser
|
|
{
|
|
*pchAnm++ = wchObjectEnd;
|
|
pols->_cchAnm += 2; //alloc space for open and close
|
|
}
|
|
*pchAnm++ = ' '; //Ensure a little extra space
|
|
*pchAnm++ = wchObjectEnd; //End character for Anm
|
|
pols->_cchAnm += 2;
|
|
}
|
|
|
|
return lserrNone;
|
|
}
|
|
|
|
/*
|
|
* OlsFetchTabs(pols, LSCP cp, PLSTABS plstabs, BOOL *pfHangingTab,
|
|
* long *pduaHangingTab, WCHAR *pwchHangingTabLeader)
|
|
* @func
|
|
* Fetch tabs
|
|
*
|
|
* @rdesc
|
|
* LSERR
|
|
*/
|
|
LSERR WINAPI OlsFetchTabs(
|
|
POLS pols, //(IN): (COls *)
|
|
LSCP cp, //(IN): Arbitrary cp value inside para
|
|
PLSTABS plstabs, //(OUT): Tabs array
|
|
BOOL * pfHangingTab, //(OUT): There is hanging tab
|
|
long * pduaHangingTab, //(OUT): dua of hanging tab
|
|
WCHAR * pwchHangingTabLeader) //(OUT): Leader of hanging tab
|
|
{
|
|
CMeasurer *pme = pols->GetMeasurer();
|
|
|
|
const CParaFormat *pPF = pme->Get_pPF();
|
|
const char rgchTabLeader[] = {0, '.', '-', '_', '_', '='};
|
|
|
|
LONG cTabCount = pPF->_bTabCount;
|
|
LONG i, iActual;
|
|
LSTBD * prgTab = pols->_rgTab;
|
|
const LONG *prgxTabs = pPF->GetTabs();
|
|
|
|
Assert(cTabCount <= MAX_TAB_STOPS && (prgxTabs || !cTabCount));
|
|
|
|
plstabs->duaIncrementalTab = pme->GetPed()->GetDefaultTab();
|
|
|
|
*pwchHangingTabLeader = 0;
|
|
*pduaHangingTab = pPF->_dxStartIndent + pPF->_dxOffset;
|
|
*pfHangingTab = (!(pPF->InTable()) && pPF->_dxOffset > 0);
|
|
|
|
for(i = 0, iActual = 0; i < cTabCount; i++)
|
|
{
|
|
LONG tbAlign, tbLeader;
|
|
pPF->GetTab(i, &prgTab[iActual].ua, &tbAlign, &tbLeader, prgxTabs);
|
|
|
|
pme->SetUseTargetDevice(FALSE);
|
|
if (pme->LXtoDX(prgTab[iActual].ua) > pols->_xWidth)
|
|
break;
|
|
|
|
if(pPF->InTable())
|
|
{
|
|
tbAlign = lsktLeft; // Don't have alignment and
|
|
tbLeader = 0; // leader yet
|
|
}
|
|
if(tbAlign <= tomAlignDecimal) // Don't include tomAlignBar
|
|
{
|
|
prgTab[iActual].lskt = (lsktab) tbAlign;
|
|
prgTab[iActual].wchTabLeader = rgchTabLeader[tbLeader];
|
|
iActual++;
|
|
}
|
|
}
|
|
|
|
plstabs->pTab = prgTab;
|
|
plstabs->iTabUserDefMac = iActual;
|
|
return lserrNone;
|
|
}
|
|
|
|
/*
|
|
* OlsCheckParaBoundaries (pols, cpOld, cpNew, pfChanged)
|
|
*
|
|
* @func
|
|
* Determine if para formatting between para containing cpOld and
|
|
* that containing cpNew are incompatible and shouldn't be formatted
|
|
* on the same line when connected by hidden text.
|
|
*
|
|
* @rdesc
|
|
* LSERR
|
|
*/
|
|
LSERR WINAPI OlsCheckParaBoundaries(
|
|
POLS pols, //(IN): Interface object
|
|
LONG cpOld, //(IN): cp in one paragraph
|
|
LONG cpNew, //(IN): cp in another paragraph
|
|
BOOL * pfChanged) //(OUT): "Dangerous" change between para properties
|
|
{
|
|
|
|
// It's easier (and safer) to allow LS decide which para properties to take.
|
|
// Else we have to close objects (BiDi, for instance) before hidden EOP.
|
|
|
|
*pfChanged = fFalse; // they're always compatible
|
|
|
|
return lserrNone;
|
|
}
|
|
|
|
/*
|
|
* OlsGetRunCharWidths (pols, plrun, deviceID, lpwchRun, cwchRun, du,
|
|
* kTFlow, prgDu, pduRun, plimDu)
|
|
* @func
|
|
* Get run character widths
|
|
*
|
|
* @rdesc
|
|
* LSERR
|
|
*/
|
|
LSERR WINAPI OlsGetRunCharWidths(
|
|
POLS pols, //(IN): Interface object
|
|
PLSRUN plsrun, //(IN): Run (cp here)
|
|
enum lsdevice deviceID, //(IN): Preview, reference, or absolute
|
|
LPCWSTR lpwchRun, //(IN): Run of characters
|
|
DWORD cwchRun, //(IN): Count of characters in run
|
|
long du, //(IN): Available space for characters
|
|
LSTFLOW kTFlow, //(IN): Text direction and orientation
|
|
int * prgDu, //(OUT): Widths of characters
|
|
long * pduRun, //(OUT): Sum of widths in rgDx[0] to rgDu[limDx-1]
|
|
long * plimDu) //(OUT): Number of widths fetched
|
|
{
|
|
CMeasurer *pme = pols->GetMeasurer();
|
|
BOOL fBullet = pols->SetRun(plsrun);
|
|
DWORD i = 0;
|
|
LONG xWidth, xAdjust, duCalc = 0;
|
|
BOOL fGlyphRun = FALSE;
|
|
pme->SetUseTargetDevice(deviceID == lsdevReference);
|
|
CCcs *pccs = pme->Check_pccs(fBullet);
|
|
if(!pccs)
|
|
return lserrOutOfMemory;
|
|
|
|
if (pme->GetPed()->IsComplexScript() &&
|
|
plsrun->_a.eScript && !plsrun->IsBullet())
|
|
{
|
|
const SCRIPT_PROPERTIES *psp = pme->Getusp()->GeteProp(plsrun->_a.eScript);
|
|
if (psp->fComplex)
|
|
fGlyphRun = TRUE;
|
|
}
|
|
|
|
xAdjust = pme->LXtoDX(plsrun->_pCF->_sSpacing);
|
|
for(;i < cwchRun; i++, lpwchRun++)
|
|
{
|
|
if (!fGlyphRun)
|
|
{
|
|
if (IsZerowidthCharacter(*lpwchRun))
|
|
xWidth = 0;
|
|
else
|
|
{
|
|
pccs->Include(*lpwchRun, xWidth);
|
|
xWidth = max(xWidth + xAdjust, 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
xWidth = 0;
|
|
if (!IsDiacriticOrKashida(*lpwchRun, 0))
|
|
xWidth = pccs->_xAveCharWidth;
|
|
}
|
|
|
|
duCalc += xWidth; // Keep running total of width
|
|
*prgDu++ = xWidth; // Store width in output array
|
|
if(xWidth + duCalc > du) // Width exceeds width available
|
|
{
|
|
i++; // Count this char as processed
|
|
break;
|
|
}
|
|
}
|
|
*plimDu = i; // Store total chars processed
|
|
*pduRun = duCalc; // Store output total width
|
|
return lserrNone;
|
|
}
|
|
|
|
/*
|
|
* OlsGetRunTextMetrics (pols, plsrun, deviceID, kTFlow, plsTxMet)
|
|
*
|
|
* @func
|
|
* Get run text metrics
|
|
*
|
|
* @rdesc
|
|
* LSERR
|
|
*/
|
|
LSERR WINAPI OlsGetRunTextMetrics(
|
|
POLS pols, //(IN): interface object
|
|
PLSRUN plsrun, //(IN): run (cp here)
|
|
enum lsdevice deviceID, //(IN): presentation or reference
|
|
LSTFLOW kTFlow, //(IN): text direction and orientation
|
|
PLSTXM plsTxMet) //(OUT): Text metrics
|
|
{
|
|
CMeasurer *pme = pols->GetMeasurer();
|
|
BOOL fBullet = pols->SetRun(plsrun);
|
|
|
|
// Make sure right font is set for run
|
|
pme->SetUseTargetDevice(deviceID == lsdevReference);
|
|
CCcs *pccs = pme->Check_pccs(fBullet);
|
|
if(!pccs)
|
|
return lserrOutOfMemory;
|
|
|
|
LONG yFEAdjust = pccs->AdjustFEHeight(pme->fAdjustFELineHt());
|
|
|
|
// Cache descent to save a few indirections
|
|
LONG yDescent = pccs->_yDescent + yFEAdjust;
|
|
|
|
// Fill in metric structure
|
|
plsTxMet->dvAscent = pccs->_yHeight + (yFEAdjust << 1) - yDescent;
|
|
plsTxMet->dvDescent = yDescent;
|
|
plsTxMet->dvMultiLineHeight = plsTxMet->dvAscent + yDescent;
|
|
plsTxMet->fMonospaced = pccs->_fFixPitchFont;
|
|
|
|
if (plsrun->_pCF->_yOffset)
|
|
{
|
|
LONG yOffset, yAdjust;
|
|
pccs->GetOffset(plsrun->_pCF, deviceID == lsdevReference ? pme->GetDyrInch() :
|
|
pme->GetDypInch(), &yOffset, &yAdjust);
|
|
|
|
if (yOffset < 0)
|
|
plsTxMet->dvDescent -= yOffset;
|
|
else
|
|
plsTxMet->dvAscent += yOffset;
|
|
|
|
}
|
|
|
|
return lserrNone;
|
|
}
|
|
|
|
/*
|
|
* OlsGetRunUnderlineInfo (pols, plsrun, pcheights, kTFlow, plsStInfo)
|
|
*
|
|
* @func
|
|
* Get run underline info
|
|
*
|
|
* @rdesc
|
|
* LSERR
|
|
*/
|
|
LSERR WINAPI OlsGetRunUnderlineInfo(
|
|
POLS pols, //(IN): Interface object
|
|
PLSRUN plsrun, //(IN): Run (cp here)
|
|
PCHEIGHTS pcheights, //(IN): Height of line
|
|
LSTFLOW kTFlow, //(IN): Text direction and orientation
|
|
PLSULINFO plsUlInfo) //(OUT): Underline information
|
|
{
|
|
CMeasurer *pme = pols->GetMeasurer();
|
|
BOOL fBullet = pols->SetRun(plsrun);
|
|
|
|
// Initialize output buffer
|
|
ZeroMemory(plsUlInfo, sizeof(*plsUlInfo));
|
|
//REVIEW KeithCu
|
|
|
|
// Make sure right font is set for run
|
|
CCcs *pccs = pme->Check_pccs(fBullet);
|
|
if(!pccs)
|
|
return lserrOutOfMemory;
|
|
|
|
long dvpUlOffset = pccs->_dyULOffset;
|
|
|
|
plsUlInfo->cNumberOfLines = 1;
|
|
|
|
// Set underline type
|
|
if (plsrun->_pCF->_dwEffects & CFE_LINK)
|
|
plsUlInfo->kulbase = CFU_UNDERLINE;
|
|
else if (plsrun->_pCF->_dwEffects & (CFE_UNDERLINE | CFE_REVISED))
|
|
plsUlInfo->kulbase = plsrun->_pCF->_bUnderlineType;
|
|
else
|
|
{
|
|
Assert(pme->GetPed()->GetCpAccelerator() == plsrun->_cp);
|
|
plsUlInfo->kulbase = CFU_UNDERLINE;
|
|
}
|
|
|
|
LONG yDescent = pccs->_yDescent + pccs->AdjustFEHeight(pme->fAdjustFELineHt());
|
|
|
|
// Some fonts report invalid offset so we fix it up here
|
|
//BUGBUG: subscripts with Line Services don't display.
|
|
if(dvpUlOffset >= yDescent)
|
|
dvpUlOffset = yDescent - 1;
|
|
|
|
plsUlInfo->dvpFirstUnderlineOffset = dvpUlOffset;
|
|
plsUlInfo->dvpFirstUnderlineSize = pccs->_dyULWidth;
|
|
|
|
return lserrNone;
|
|
}
|
|
|
|
/*
|
|
* OlsGetRunStrikethroughInfo (pols, plsrun, pcheights, kTFlow, plsStInfo)
|
|
*
|
|
* @func
|
|
* Get run strikethrough info
|
|
*
|
|
* @rdesc
|
|
* LSERR
|
|
*/
|
|
LSERR WINAPI OlsGetRunStrikethroughInfo(
|
|
POLS pols, //(IN): interface object
|
|
PLSRUN plsrun, //(IN): run
|
|
PCHEIGHTS pcheights, //(IN): height of line
|
|
LSTFLOW kTFlow, //(IN): text direction and orientation
|
|
PLSSTINFO plsStInfo) //(OUT): Strikethrough information
|
|
{
|
|
CMeasurer *pme = pols->GetMeasurer();
|
|
BOOL fBullet = pols->SetRun(plsrun);
|
|
|
|
AssertSz(plsrun->_pCF->_dwEffects & CFE_STRIKEOUT, "no strikeout");
|
|
|
|
// Make sure right font is set for run
|
|
CCcs *pccs = pme->Check_pccs(fBullet);
|
|
if(!pccs)
|
|
return lserrOutOfMemory;
|
|
|
|
// Default number of lines
|
|
plsStInfo->cNumberOfLines = 1;
|
|
plsStInfo->dvpLowerStrikethroughOffset = -pccs->_dySOOffset;
|
|
plsStInfo->dvpLowerStrikethroughSize = pccs->_dySOWidth;
|
|
|
|
return lserrNone;
|
|
}
|
|
|
|
|
|
/* OlsDrawUnderline (pols, plsrun, kUlbase, pptStart, dupUL, dvpUL,
|
|
* kTFlow, kDisp, prcClip)
|
|
* @func
|
|
* Draw underline
|
|
*
|
|
* @rdesc
|
|
* LSERR
|
|
*/
|
|
LSERR WINAPI OlsDrawUnderline(
|
|
POLS pols, //(IN): interface object
|
|
PLSRUN plsrun, //(IN): run (cp) to use for underlining
|
|
UINT kUlbase, //(IN): underline kind
|
|
const POINT *pptStart, //(IN): starting position (top left)
|
|
DWORD dupUL, //(IN): underline width
|
|
DWORD dvpUL, //(IN): underline thickness
|
|
LSTFLOW lstflow, //(IN): text direction and orientation
|
|
UINT kDisp, //(IN): display mode - opaque, transparent
|
|
const RECT *prcClip) //(IN): clipping rectangle
|
|
{
|
|
CRenderer *pre = pols->GetRenderer();
|
|
Assert(pre->IsRenderer());
|
|
|
|
pols->SetRun(plsrun);
|
|
pre->Check_pccs();
|
|
|
|
pre->SetSelected(plsrun->IsSelected());
|
|
pre->SetFontAndColor(plsrun->_pCF);
|
|
|
|
pre->SetupUnderline(kUlbase);
|
|
pre->RenderUnderline(lstflow == lstflowWS ? pptStart->x - dupUL - 1:
|
|
pptStart->x, pptStart->y, dupUL, dvpUL);
|
|
|
|
return lserrNone;
|
|
}
|
|
|
|
/*
|
|
* OlsDrawStrikethrough (pols, plsrun, kStbase, pptStart, dupSt, dvpSt,
|
|
* kTFlow, kDisp, prcClip)
|
|
* @func
|
|
* Draw strikethrough
|
|
*
|
|
* @rdesc
|
|
* LSERR
|
|
*/
|
|
LSERR WINAPI OlsDrawStrikethrough(
|
|
POLS pols, //(IN): Interface object
|
|
PLSRUN plsrun, //(IN): run (cp) for strikethrough
|
|
UINT kStbase, //(IN): strikethrough kind
|
|
const POINT *pptStart, //(IN): starting position (top left)
|
|
DWORD dupSt, //(IN): strikethrough width
|
|
DWORD dvpSt, //(IN): strikethrough thickness
|
|
LSTFLOW lstflow, //(IN): text direction and orientation
|
|
UINT kDisp, //(IN): display mode - opaque, transparent
|
|
const RECT *prcClip) //(IN): clipping rectangle
|
|
{
|
|
CRenderer *pre = pols->GetRenderer();
|
|
Assert(pre->IsRenderer());
|
|
|
|
pols->SetRun(plsrun);
|
|
pre->SetSelected(plsrun->IsSelected());
|
|
|
|
pre->RenderStrikeOut(lstflow == lstflowWS ? pptStart->x - dupSt - 1:
|
|
pptStart->x, pptStart->y, dupSt, dvpSt);
|
|
|
|
return lserrNone;
|
|
}
|
|
|
|
|
|
/*
|
|
* OlsFInterruptUnderline(pols, plsrunFirst, cpLastFirst, plsrunSecond,
|
|
* cpStartSecond, pfInterruptUnderline)
|
|
* @func
|
|
* Says whether client wants to interrupt drawing of underline
|
|
* between the first and second runs
|
|
*
|
|
* @rdesc
|
|
* LSERR
|
|
*/
|
|
LSERR WINAPI OlsFInterruptUnderline(
|
|
POLS pols, //(IN): Client context
|
|
PLSRUN plsrunFirst, //(IN): Run pointer for previous run
|
|
LSCP cpLastFirst, //(IN): cp of last character of previous run
|
|
PLSRUN plsrunSecond, //(IN): Run pointer for current run
|
|
LSCP cpStartSecond, //(IN): cp of first character of current run
|
|
BOOL * pfInterruptUnderline)//(OUT): Interrupt underline between runs?
|
|
{
|
|
CRenderer *pre = pols->GetRenderer();
|
|
Assert(pre->IsRenderer());
|
|
|
|
pre->SetSelected(FALSE); //Selection is handled below
|
|
|
|
COLORREF cr = pre->GetTextColor(plsrunFirst->_pCF);
|
|
|
|
// Interrupt underline if run text colors differ
|
|
*pfInterruptUnderline = cr != pre->GetTextColor(plsrunSecond->_pCF) ||
|
|
plsrunFirst->IsSelected() != plsrunSecond->IsSelected();
|
|
return lserrNone;
|
|
}
|
|
|
|
/*
|
|
* OlsDrawTextRun (pols, plsrun, kStbase, pptStart, dupSt, dvpSt,
|
|
* kTFlow, kDisp, prcClip)
|
|
* @func
|
|
* Draw text run
|
|
*
|
|
* @rdesc
|
|
* LSERR
|
|
*/
|
|
LSERR WINAPI OlsDrawTextRun(
|
|
POLS pols, //(IN): Interface object
|
|
PLSRUN plsrun, //(IN): Run (cp) to use for text
|
|
BOOL fStrikeoutOkay, //(IN): TRUE <==> allow strikeout
|
|
BOOL fUnderlineOkay, //(IN): TRUE <==> allow underlining
|
|
const POINT *ppt, //(IN): Starting position
|
|
LPCWSTR pwchRun, //(IN): Run of characters
|
|
const int * rgDupRun, //(IN): Character widths
|
|
DWORD cwchRun, //(IN): Count of chars in run
|
|
LSTFLOW lstflow, //(IN): Text direction and orientation
|
|
UINT kDisp, //(IN): Display mode - opaque, transparent
|
|
const POINT *pptRun, //(IN): Starting point of run
|
|
PCHEIGHTS pheightsPres, //(IN): Presentation heights for run
|
|
long dupRun, //(IN): Presentation width for run
|
|
long dupUlLimRun, //(IN): Underlining limit
|
|
const RECT *prcClip) //(IN): Clipping rectangle
|
|
{
|
|
CRenderer *pre = pols->GetRenderer();
|
|
RECT rc = *prcClip;
|
|
Assert(pre->IsRenderer());
|
|
|
|
// Set up drawing point and options
|
|
BOOL fBullet = pols->SetRun(plsrun);
|
|
CCcs *pccs = pre->Check_pccs(fBullet);
|
|
if(!pccs)
|
|
return lserrOutOfMemory;
|
|
|
|
// y needs to be moved from baseline to top of character
|
|
POINT pt = {ppt->x, ppt->y - (pccs->_yHeight - pccs->_yDescent)};
|
|
|
|
if (lstflow == lstflowWS)
|
|
pt.x -= dupRun - 1;
|
|
|
|
pre->SetSelected(plsrun->IsSelected());
|
|
pre->SetFontAndColor(plsrun->_pCF);
|
|
|
|
if(!fBullet && pre->fBackgroundColor())
|
|
{
|
|
kDisp = ETO_OPAQUE | ETO_CLIPPED;
|
|
|
|
POINT ptCur = pre->GetCurPoint();
|
|
ptCur.x = pt.x;
|
|
pre->SetCurPoint(ptCur);
|
|
pre->SetClipLeftRight(dupRun);
|
|
rc = pre->GetClipRect();
|
|
}
|
|
else if (cwchRun == 1 && pwchRun[0] == ' ') //Don't waste time drawing a space.
|
|
return lserrNone; //(helps grid perf test a lot)
|
|
|
|
pre->RenderExtTextOut(pt.x, pt.y, kDisp, &rc, pwchRun, cwchRun, rgDupRun);
|
|
|
|
return lserrNone;
|
|
}
|
|
|
|
/*
|
|
* GetBreakingClasses (pols, plsrun, ch, pbrkclsBefore, pbrkclsAfter)
|
|
*
|
|
* @func
|
|
* Line services calls this callback for each run, to obtain the
|
|
* breaking classes (line breaking behaviors) for each character
|
|
*
|
|
* For Quill and RichEdit, the breaking class of a character is
|
|
* independent of whether it occurs Before or After a break opportunity.
|
|
*
|
|
* @rdesc
|
|
* LSERR
|
|
*/
|
|
LSERR WINAPI OlsGetBreakingClasses(
|
|
POLS pols, //(IN): Interface object
|
|
PLSRUN plsrun, //(IN): Run (cp) to use for text
|
|
LSCP cpLs, //(IN): cp of the character
|
|
WCHAR ch, //(IN): Char to return breaking classes for
|
|
BRKCLS *pbrkclsBefore, //(OUT): Breaking class if ch is lead char in pair
|
|
BRKCLS *pbrkclsAfter) //(OUT): Breaking class if ch is trail char in pair
|
|
{
|
|
long cpRe = pols->GetCpReFromCpLs(cpLs);
|
|
CMeasurer *pme = pols->GetMeasurer();
|
|
CTxtBreaker *pbrk = pme->GetPed()->_pbrk;
|
|
|
|
// Get line breaking class and report it twice
|
|
*pbrkclsBefore = *pbrkclsAfter = (pbrk && pbrk->CanBreakCp(BRK_WORD, cpRe)) ?
|
|
brkclsOpen :
|
|
GetKinsokuClass(ch);
|
|
return lserrNone;
|
|
}
|
|
|
|
/*
|
|
* OlsFTruncateBefore (pols, cpCur, wchCur, durCur, cpPrev, wchPrev,
|
|
* durPrev, durCut, pfTruncateBefore)
|
|
* @func
|
|
* Line services support function. This should always return
|
|
* FALSE for best performance
|
|
*
|
|
* @rdesc
|
|
* LSERR
|
|
*/
|
|
LSERR WINAPI OlsFTruncateBefore(
|
|
POLS pols, // (IN): Client context
|
|
PLSRUN plsrunCur, // (IN): PLSRUN of cp
|
|
LSCP cpCur, // (IN): cp of truncation char
|
|
WCHAR wchCur, // (IN): Truncation character
|
|
long durCur, // (IN): Width of truncation char
|
|
PLSRUN plsrunPrev, // (IN): PLSRUN of cpPrev
|
|
LSCP cpPrev, // (IN): cp of truncation char
|
|
WCHAR wchPrev, // (IN): Truncation character
|
|
long durPrev, // (IN): Width of truncation character
|
|
long durCut, // (IN): Width from RM until end of current char
|
|
BOOL * pfTruncateBefore) // (OUT): Truncation point is before this char
|
|
{
|
|
*pfTruncateBefore = FALSE;
|
|
return lserrNone;
|
|
}
|
|
|
|
/*
|
|
* OlsCanBreakBeforeChar (pols, brkcls, pcond)
|
|
*
|
|
* @func
|
|
* Line services calls this callback for a break candidate following an
|
|
* inline object, to determine whether breaks are prevented, possible or
|
|
* mandatory
|
|
*
|
|
* @rdesc
|
|
* LSERR
|
|
*/
|
|
LSERR WINAPI OlsCanBreakBeforeChar(
|
|
POLS pols, //(IN): Client context
|
|
BRKCLS brkcls, //(IN): Breaking class
|
|
BRKCOND *pcond) //(OUT): Corresponding break condition
|
|
{
|
|
switch (brkcls)
|
|
{
|
|
default:
|
|
*pcond = brkcondCan;
|
|
break;
|
|
|
|
case brkclsClose:
|
|
case brkclsNoStartIdeo:
|
|
case brkclsExclaInterr:
|
|
case brkclsGlueA:
|
|
*pcond = brkcondNever;
|
|
break;
|
|
|
|
case brkclsIdeographic:
|
|
case brkclsSpaceN:
|
|
case brkclsSlash:
|
|
*pcond = brkcondPlease;
|
|
break;
|
|
};
|
|
return lserrNone;
|
|
}
|
|
|
|
/*
|
|
* OlsCanBreakAfterChar (pols, brkcls, pcond)
|
|
*
|
|
* @func
|
|
* Line services calls this callback for a break candidate preceding an
|
|
* inline object, to determine whether breaks are prevented, possible or
|
|
* mandatory
|
|
*
|
|
* @rdesc
|
|
* LSERR
|
|
*/
|
|
LSERR WINAPI OlsCanBreakAfterChar(
|
|
POLS pols, //(IN): Client context
|
|
BRKCLS brkcls, //(IN): Breaking class
|
|
BRKCOND *pcond) //(OUT): Corresponding break condition
|
|
{
|
|
switch (brkcls)
|
|
{
|
|
default:
|
|
*pcond = brkcondCan;
|
|
break;
|
|
|
|
case brkclsOpen:
|
|
case brkclsGlueA:
|
|
*pcond = brkcondNever;
|
|
break;
|
|
|
|
case brkclsIdeographic:
|
|
case brkclsSpaceN:
|
|
case brkclsSlash:
|
|
*pcond = brkcondPlease;
|
|
break;
|
|
};
|
|
return lserrNone;
|
|
}
|
|
|
|
/*
|
|
* OlsFInterruptShaping (pols, kTFlow, plsrunFirst, plsrunSecond, pfInterruptShaping)
|
|
*
|
|
* @func
|
|
* Line services calls this callback to find out if you
|
|
* would like to ligate across these two runs.
|
|
*
|
|
* @rdesc
|
|
* LSERR
|
|
*/
|
|
LSERR WINAPI OlsFInterruptShaping(
|
|
POLS pols, //(IN): Client context
|
|
LSTFLOW kTFlow, //(IN): Text direction and orientation
|
|
PLSRUN plsrunFirst, //(IN): Run #1
|
|
PLSRUN plsrunSecond, //(IN): Run #2
|
|
BOOL *pfInterruptShaping) //(OUT): Shape across these 2 runs?
|
|
{
|
|
*pfInterruptShaping = FALSE;
|
|
|
|
const CCharFormat* pCFFirst = plsrunFirst->_pCF;
|
|
const CCharFormat* pCFSecond = plsrunSecond->_pCF;
|
|
|
|
Assert (plsrunFirst->_a.eScript && plsrunSecond->_a.eScript);
|
|
|
|
const DWORD dwMask = CFE_BOLD | CFE_ITALIC | CFM_SUBSCRIPT;
|
|
|
|
if (pCFFirst == pCFSecond ||
|
|
(plsrunFirst->_a.eScript == plsrunSecond->_a.eScript &&
|
|
!((pCFFirst->_dwEffects ^ pCFSecond->_dwEffects) & dwMask) &&
|
|
pCFFirst->_iFont == pCFSecond->_iFont &&
|
|
pCFFirst->_yOffset == pCFSecond->_yOffset &&
|
|
pCFFirst->_yHeight == pCFSecond->_yHeight))
|
|
{
|
|
// establish link
|
|
plsrunFirst->_pNext = plsrunSecond;
|
|
return lserrNone;
|
|
}
|
|
|
|
*pfInterruptShaping = TRUE;
|
|
return lserrNone;
|
|
}
|
|
|
|
|
|
// LS calls this callback to shape the codepoint string to a glyph indices string
|
|
// for handling glyph based script such as Arabic, Hebrew and Thai.
|
|
//
|
|
LSERR OlsGetGlyphs(
|
|
POLS pols,
|
|
PLSRUN plsrun,
|
|
LPCWSTR pwch,
|
|
DWORD cch,
|
|
LSTFLOW kTFlow,
|
|
PGMAP pgmap, // OUT: array of logical cluster information
|
|
PGINDEX* ppgi, // OUT: array of output glyph indices
|
|
PGPROP* ppgprop, // OUT: array of glyph's properties
|
|
DWORD* pcgi) // OUT: number of glyph generated
|
|
{
|
|
pols->SetRun(plsrun);
|
|
|
|
CMeasurer* pme = pols->GetMeasurer();
|
|
CUniscribe* pusp = pme->Getusp();
|
|
Assert (pusp);
|
|
|
|
WORD* pwgi;
|
|
SCRIPT_VISATTR *psva;
|
|
int cgi;
|
|
|
|
pme->SetGlyphing(TRUE);
|
|
|
|
// Glyphing doesn't care about the target device but always
|
|
// using target device reduces creation of Cccs in general.
|
|
pme->SetUseTargetDevice(TRUE);
|
|
|
|
AssertSz(IN_RANGE(1, plsrun->_a.eScript, SCRIPT_MAX_COUNT - 1), "Bad script ID!");
|
|
|
|
// Digit substitution
|
|
pusp->SubstituteDigitShaper(plsrun, pme);
|
|
|
|
if (!(cgi = (DWORD)pusp->ShapeString(plsrun, &plsrun->_a, pme, pwch, (int)cch, pwgi, pgmap, psva)))
|
|
{
|
|
const SCRIPT_ANALYSIS saUndef = {SCRIPT_UNDEFINED,0,0,0,0,0,0,{0}};
|
|
|
|
// Current font cant shape given string.
|
|
// Try SCRIPT_UNDEF so it generates invalid glyphs
|
|
if (!(cgi = (DWORD)pusp->ShapeString(plsrun, (SCRIPT_ANALYSIS*)&saUndef, pme, pwch, (int)cch, pwgi, pgmap, psva)))
|
|
{
|
|
// For whatever reason we still fails.
|
|
// Abandon glyph processing.
|
|
plsrun->_a.fNoGlyphIndex = TRUE;
|
|
cgi = (DWORD)pusp->ShapeString(plsrun, &plsrun->_a, pme, pwch, (int)cch, pwgi, pgmap, psva);
|
|
}
|
|
}
|
|
|
|
*pcgi = cgi;
|
|
|
|
DupShapeState(plsrun, cch);
|
|
|
|
*ppgi = (PGINDEX)pwgi;
|
|
*ppgprop = (PGPROP)psva;
|
|
pme->SetGlyphing(FALSE);
|
|
return lserrNone;
|
|
}
|
|
|
|
// LS calls this callback to find out glyph positioning for complex scripts
|
|
//
|
|
LSERR OlsGetGlyphPositions(
|
|
POLS pols,
|
|
PLSRUN plsrun,
|
|
LSDEVICE deviceID,
|
|
LPWSTR pwch,
|
|
PCGMAP pgmap,
|
|
DWORD cch,
|
|
PCGINDEX pgi,
|
|
PCGPROP pgprop,
|
|
DWORD cgi,
|
|
LSTFLOW kTFlow,
|
|
int* pgdx, // OUT: array of glyph advanced width
|
|
PGOFFSET pgduv) // OUT: array of offset between glyphs
|
|
{
|
|
pols->SetRun(plsrun);
|
|
|
|
CMeasurer* pme = pols->GetMeasurer();
|
|
CUniscribe* pusp = pme->Getusp();
|
|
Assert (pusp);
|
|
Assert(pgduv);
|
|
pme->SetGlyphing(TRUE);
|
|
|
|
// zero out before passing to shaping engine
|
|
ZeroMemory ((void*)pgduv, cgi*sizeof(GOFFSET));
|
|
|
|
pme->SetUseTargetDevice(deviceID == lsdevReference);
|
|
|
|
AssertSz(IN_RANGE(1, plsrun->_a.eScript, SCRIPT_MAX_COUNT - 1), "Bad script ID!");
|
|
|
|
if (!pusp->PlaceString(plsrun, &plsrun->_a, pme, pgi, cgi, (const SCRIPT_VISATTR*)pgprop, pgdx, pgduv, NULL))
|
|
{
|
|
SCRIPT_ANALYSIS saUndef = {SCRIPT_UNDEFINED,0,0,0,0,0,0,{0}};
|
|
|
|
if (!pusp->PlaceString(plsrun, &saUndef, pme, pgi, cgi, (const SCRIPT_VISATTR*)pgprop, pgdx, pgduv, NULL))
|
|
{
|
|
plsrun->_a.fNoGlyphIndex = TRUE;
|
|
pusp->PlaceString(plsrun, &plsrun->_a, pme, pgi, cgi, (const SCRIPT_VISATTR*)pgprop, pgdx, pgduv, NULL);
|
|
}
|
|
}
|
|
|
|
DupShapeState(plsrun, cch);
|
|
|
|
pme->SetGlyphing(FALSE);
|
|
return lserrNone;
|
|
}
|
|
|
|
LSERR OlsDrawGlyphs(
|
|
POLS pols,
|
|
PLSRUN plsrun,
|
|
BOOL fStrikeOut,
|
|
BOOL fUnderline,
|
|
PCGINDEX pcgi,
|
|
const int* pgdx, // array of glyph width
|
|
const int* pgdxo, // array of original glyph width (before justification)
|
|
PGOFFSET pgduv, // array of glyph offset
|
|
PGPROP pgprop, // array of glyph's properties
|
|
PCEXPTYPE pgxtype, // array of expansion type
|
|
DWORD cgi,
|
|
LSTFLOW kTFlow,
|
|
UINT kDisp,
|
|
const POINT* pptRun,
|
|
PCHEIGHTS pHeight,
|
|
long dupRun,
|
|
long dupLimUnderline,
|
|
const RECT* prectClip)
|
|
{
|
|
BOOL fBullet = pols->SetRun(plsrun);
|
|
CRenderer* pre = pols->GetRenderer();
|
|
CUniscribe* pusp = pre->Getusp();
|
|
Assert(pusp && pre->IsRenderer());
|
|
pre->SetGlyphing(TRUE);
|
|
|
|
RECT rc = *prectClip;
|
|
CCcs* pccs = pre->Check_pccs(fBullet);
|
|
|
|
if (!pccs)
|
|
return lserrOutOfMemory;
|
|
|
|
// Apply fallback font if we need to
|
|
if (!fBullet)
|
|
pccs = pre->ApplyFontCache(plsrun->IsFallback());
|
|
|
|
pre->SetSelected(plsrun->IsSelected());
|
|
pre->SetFontAndColor(plsrun->_pCF);
|
|
|
|
// y needs to be moved from baseline to top of character
|
|
POINT pt = {pptRun->x, pptRun->y - (pccs->_yHeight - pccs->_yDescent)};
|
|
|
|
if (kTFlow == lstflowWS)
|
|
pt.x -= dupRun - 1;
|
|
|
|
if(!fBullet && pre->fBackgroundColor())
|
|
{
|
|
kDisp = ETO_OPAQUE | ETO_CLIPPED;
|
|
|
|
POINT ptCur = pre->GetCurPoint();
|
|
ptCur.x = pt.x;
|
|
pre->SetCurPoint(ptCur);
|
|
pre->SetClipLeftRight(dupRun);
|
|
rc = pre->GetClipRect();
|
|
}
|
|
|
|
if (rc.left == rc.right)
|
|
goto Exit;
|
|
|
|
if (pre->GetPdp()->IsMetafile() && !IsEnhancedMetafileDC(pre->GetDC()))
|
|
{
|
|
// -WMF metafile handling-
|
|
//
|
|
// If the rendering device is WMF metafile. We metafile the codepoint array
|
|
// instead of glyph indices. This requires that the target OS must know how to
|
|
// playback complex script text (shaping, Bidi algorithm, etc.).
|
|
// Metafiling glyph indices only works for EMF since the WMF's META_EXTTEXTOUT
|
|
// record stores the input string as an array of byte but a glyph index is 16-bit
|
|
// word element.
|
|
// WMF also must NOT be used to record ExtTextOutW call otherwise the Unicode
|
|
// string will be converted to mutlibyte text using system codepage. Anything
|
|
// outside the codepage then becomes '?'.
|
|
// We have the workaround for such case in REExtTextOut to make sure we only
|
|
// metafile ExtTextOutA to WMF. (wchao)
|
|
//
|
|
|
|
LONG cch;
|
|
const WCHAR* pwch = pre->GetPch(cch);
|
|
PINT piDx;
|
|
|
|
cch = min(cch, pre->GetCchLeftRunCF());
|
|
cch = min(cch, pre->GetLine()._cch - plsrun->_cp + pols->_cp);
|
|
|
|
// make sure that we record ETO with proper reading order.
|
|
kDisp |= plsrun->_a.fRTL ? ETO_RTLREADING : 0;
|
|
|
|
if (pusp->PlaceMetafileString(plsrun, pre, pwch, (int)cch, &piDx))
|
|
{
|
|
pre->RenderExtTextOut(pt.x, pt.y, kDisp, &rc, pwch, cch, piDx);
|
|
goto Exit;
|
|
}
|
|
|
|
TRACEERRORSZ("Recording metafile failed!");
|
|
|
|
// Fall through... with unexpected error
|
|
|
|
// Else, metafile glyph indices for EMF...
|
|
}
|
|
|
|
//This is duplicated from RenderExtTextOut but the params are different so simplest solution
|
|
//was to copy code.
|
|
if(pre->_fDisabled)
|
|
{
|
|
if(pre->_crForeDisabled != pre->_crShadowDisabled)
|
|
{
|
|
// The shadow should be offset by a hairline point, namely
|
|
// 3/4 of a point. Calculate how big this is in device units,
|
|
// but make sure it is at least 1 pixel.
|
|
DWORD offset = MulDiv(3, pre->_dypInch, 4*72);
|
|
offset = max(offset, 1);
|
|
|
|
// Draw shadow
|
|
pre->SetTextColor(pre->_crShadowDisabled);
|
|
|
|
ScriptTextOut(pre->GetDC(), &pccs->_sc, pt.x + offset, pt.y + offset, kDisp, &rc, &plsrun->_a,
|
|
NULL, 0, pcgi, (int)cgi, pgdx, NULL, pgduv);
|
|
|
|
// Now set drawing mode to transparent
|
|
kDisp &= ~ETO_OPAQUE;
|
|
}
|
|
pre->SetTextColor(pre->_crForeDisabled);
|
|
}
|
|
|
|
ScriptTextOut(pre->GetDC(), &pccs->_sc, pt.x, pt.y, kDisp, &rc, &plsrun->_a,
|
|
NULL, 0, pcgi, (int)cgi, pgdx, NULL, pgduv);
|
|
|
|
Exit:
|
|
if (!fBullet)
|
|
pre->ApplyFontCache(0); // reset font fallback if any
|
|
|
|
pre->SetGlyphing(FALSE);
|
|
return lserrNone;
|
|
}
|
|
|
|
|
|
/*
|
|
* OlsResetRunContents (pols, brkcls, pcond)
|
|
*
|
|
* @func
|
|
* Line Services calls this routine when a ligature
|
|
* extends across run boundaries.
|
|
*
|
|
* We don't have to do anything special here if we are
|
|
* careful about how we use our PLSRUNs.
|
|
* @rdesc
|
|
* LSERR
|
|
*/
|
|
LSERR WINAPI OlsResetRunContents(
|
|
POLS pols, //(IN): Client context
|
|
PLSRUN plsrun, //(IN): Run being combined
|
|
LSCP cpFirstOld, //(IN): cp of the first run being combined
|
|
LSDCP dcpOld, //(IN): dcp of the first run being combined
|
|
LSCP cpFirstNew, //(IN): new cp of the run
|
|
LSDCP dcpNew) //(IN): new dcp of the run
|
|
{
|
|
return lserrNone;
|
|
}
|
|
|
|
/*
|
|
* OlsCheckForDigit (pols, cp, plspap)
|
|
*
|
|
* @func
|
|
* Get numeric separators needed, e.g., for decimal tabs
|
|
*
|
|
* @rdesc
|
|
* LSERR
|
|
*/
|
|
LSERR WINAPI OlsCheckForDigit(
|
|
POLS pols, //(IN): pols
|
|
PLSRUN plsrun, //(IN): Run (cp here)
|
|
WCHAR wch, //(IN): Character to check
|
|
BOOL * pfIsDigit) //(OUT): This character is digit
|
|
{
|
|
WORD wType;
|
|
|
|
// We could get the run LCID to use for the first parm in the following
|
|
// call, but the digit property should be independent of LCID.
|
|
W32->GetStringTypeEx(0, CT_CTYPE1, &wch, 1, &wType);
|
|
*pfIsDigit = (wType & C1_DIGIT) != 0;
|
|
|
|
return lserrNone;
|
|
}
|
|
|
|
/*
|
|
* OlsGetBreakThroughTab(pols, uaRightMargin, uaTabPos, puaRightMarginNew)
|
|
*
|
|
* @func
|
|
* Just follow word95 behavior.
|
|
*
|
|
* @rdesc
|
|
* LSERR
|
|
*/
|
|
LSERR WINAPI OlsGetBreakThroughTab(
|
|
POLS pols, //(IN): client context
|
|
long uaRightMargin, //(IN): right margin for breaking
|
|
long uaTabPos, //(IN): breakthrough tab position
|
|
long * puaRightMarginNew) //(OUT): new right margin
|
|
{
|
|
*puaRightMarginNew = 20 * 1440;
|
|
return lserrNone;
|
|
}
|
|
|
|
/*
|
|
* OlsFGetLastLineJustification(pols, lskj, endr, pfJustifyLastLine)
|
|
*
|
|
* @func
|
|
* Just say no to justify last line.
|
|
*
|
|
* @rdesc
|
|
* LSERR
|
|
*/
|
|
LSERR WINAPI OlsFGetLastLineJustification(
|
|
POLS pols, //(IN): client context
|
|
LSKJUST lskj, //(IN): kind of justification
|
|
LSKALIGN lskal, //(IN): kind of alignment
|
|
ENDRES endr, //(IN): result of formatting
|
|
BOOL *pfJustifyLastLine, //(OUT): should last line be fully justified
|
|
LSKALIGN *plskalLine) //(OUT): kind of alignment for this line
|
|
{
|
|
*pfJustifyLastLine = FALSE;
|
|
*plskalLine = lskal;
|
|
return lserrNone;
|
|
}
|
|
|
|
/*
|
|
* OlsGetHyphenInfo(pols, plsrun, pkysr, pwchYsr)
|
|
*
|
|
* @func
|
|
* We don't support fancy YSR types, tell LS so.
|
|
*
|
|
* @rdesc
|
|
* LSERR
|
|
*/
|
|
LSERR WINAPI OlsGetHyphenInfo(
|
|
POLS pols, //(IN): client context
|
|
PLSRUN plsrun, //(IN)
|
|
DWORD* pkysr, //(OUT): Ysr type - see "lskysr.h"
|
|
WCHAR* pwchYsr) //(OUT): Character code of YSR
|
|
{
|
|
*pkysr = kysrNil;
|
|
*pwchYsr = 0;
|
|
return lserrNone;
|
|
}
|
|
|
|
/*
|
|
* OlsReleaseRun (pols, plsrun)
|
|
*
|
|
* @func
|
|
* We do nothing because the run is in an array and is
|
|
* released automatically.
|
|
*
|
|
* @rdesc
|
|
* LSERR
|
|
*/
|
|
LSERR WINAPI OlsReleaseRun(
|
|
POLS pols, //(IN): interface object
|
|
PLSRUN plsrun) //(IN): run (cp) to use for underlining
|
|
{
|
|
return lserrNone;
|
|
}
|
|
|
|
/*
|
|
* OlsNewPtr(pols, cBytes)
|
|
*
|
|
* @func
|
|
* Memory allocator.
|
|
*/
|
|
void* WINAPI OlsNewPtr(
|
|
POLS pols, //@parm Not used
|
|
DWORD cBytes) //@parm Count of bytes to alloc
|
|
{
|
|
return PvAlloc(cBytes, 0);
|
|
}
|
|
|
|
/*
|
|
* OlsDisposePtr(pols, pv)
|
|
*
|
|
* @func
|
|
* Memory deallocator.
|
|
*/
|
|
void WINAPI OlsDisposePtr(
|
|
POLS pols, //@parm Not used
|
|
void * pv) //@parm [in]: ptr to free
|
|
{
|
|
FreePv(pv);
|
|
}
|
|
|
|
/*
|
|
* OlsDisposePtr(pols, pv, cBytes)
|
|
*
|
|
* @func
|
|
* Memory reallocator.
|
|
*/
|
|
void* WINAPI OlsReallocPtr(
|
|
POLS pols, //@parm Not used
|
|
void * pv, //@parm [in/out]: ptr to realloc
|
|
DWORD cBytes) //@parm Count of bytes to realloc
|
|
{
|
|
return PvReAlloc(pv, cBytes);
|
|
}
|
|
const REVERSEINIT reverseinit =
|
|
{
|
|
REVERSE_VERSION,
|
|
wchObjectEnd
|
|
};
|
|
|
|
LSERR WINAPI OlsGetObjectHandlerInfo(POLS pols, DWORD idObj, void* pObjectInfo)
|
|
{
|
|
switch (idObj)
|
|
{
|
|
case OBJID_REVERSE:
|
|
memcpy(pObjectInfo, (void *)&reverseinit, sizeof(REVERSEINIT));
|
|
break;
|
|
default:
|
|
AssertSz(0, "Undefined Object handler. Add missing case.");
|
|
}
|
|
return lserrNone;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
/* Debugging APIs */
|
|
void WINAPI OlsAssertFailed(
|
|
char *sz,
|
|
char *szFile,
|
|
int iLine)
|
|
{
|
|
AssertSzFn(sz, szFile, iLine);
|
|
}
|
|
#endif
|
|
|
|
|
|
extern const LSCBK lscbk =
|
|
{
|
|
OlsNewPtr, // pfnNewPtr
|
|
OlsDisposePtr, // pfnDisposePtr
|
|
OlsReallocPtr, // pfnReallocPtr
|
|
OlsFetchRun, // pfnFetchRun
|
|
OlsGetAutoNumberInfo, // pfnGetAutoNumberInfo
|
|
OlsGetNumericSeparators, // pfnGetNumericSeparators
|
|
OlsCheckForDigit, // pfnCheckForDigit
|
|
OlsFetchPap, // pfnFetchPap
|
|
OlsFetchTabs, // pfnFetchTabs
|
|
OlsGetBreakThroughTab, // pfnGetBreakThroughTab
|
|
OlsFGetLastLineJustification,// pfnFGetLastLineJustification
|
|
OlsCheckParaBoundaries, // pfnCheckParaBoundaries
|
|
OlsGetRunCharWidths, // pfnGetRunCharWidths
|
|
0, // pfnCheckRunKernability
|
|
0, // pfnGetRunCharKerning
|
|
OlsGetRunTextMetrics, // pfnGetRunTextMetrics
|
|
OlsGetRunUnderlineInfo, // pfnGetRunUnderlineInfo
|
|
OlsGetRunStrikethroughInfo, // pfnGetRunStrikethroughInfo
|
|
0, // pfnGetBorderInfo
|
|
OlsReleaseRun, // pfnReleaseRun
|
|
0, // pfnHyphenate
|
|
OlsGetHyphenInfo, // pfnGetHyphenInfo
|
|
OlsDrawUnderline, // pfnDrawUnderline
|
|
OlsDrawStrikethrough, // pfnDrawStrikethrough
|
|
0, // pfnDrawBorder
|
|
0, // pfnDrawUnderlineAsText //REVIEW (keithcu) Need to implement this??
|
|
OlsFInterruptUnderline, // pfnFInterruptUnderline
|
|
0, // pfnFInterruptShade
|
|
0, // pfnFInterruptBorder
|
|
0, // pfnShadeRectangle
|
|
OlsDrawTextRun, // pfnDrawTextRun
|
|
0, // pfnDrawSplatLine
|
|
OlsFInterruptShaping, // pfnFInterruptShaping
|
|
OlsGetGlyphs, // pfnGetGlyphs
|
|
OlsGetGlyphPositions, // pfnGetGlyphPositions
|
|
OlsResetRunContents, // pfnResetRunContents
|
|
OlsDrawGlyphs, // pfnDrawGlyphs
|
|
0, // pfnGetGlyphExpansionInfo
|
|
0, // pfnGetGlyphExpansionInkInfo
|
|
0, // pfnGetEms
|
|
0, // pfnPunctStartLine
|
|
0, // pfnModWidthOnRun
|
|
0, // pfnModWidthSpace
|
|
0, // pfnCompOnRun
|
|
0, // pfnCompWidthSpace
|
|
0, // pfnExpOnRun
|
|
0, // pfnExpWidthSpace
|
|
0, // pfnGetModWidthClasses
|
|
OlsGetBreakingClasses, // pfnGetBreakingClasses
|
|
OlsFTruncateBefore, // pfnFTruncateBefore
|
|
OlsCanBreakBeforeChar, // pfnCanBreakBeforeChar
|
|
OlsCanBreakAfterChar, // pfnCanBreakAfterChar
|
|
0, // pfnFHangingPunct
|
|
0, // pfnGetSnapGrid
|
|
0, // pfnDrawEffects
|
|
0, // pfnFCancelHangingPunct
|
|
0, // pfnModifyCompAtLastChar
|
|
0, // pfnEnumText
|
|
0, // pfnEnumTab
|
|
0, // pfnEnumPen
|
|
OlsGetObjectHandlerInfo, // pfnGetObjectHandlerInfo
|
|
#ifdef DEBUG
|
|
OlsAssertFailed // pfnAssertFailed
|
|
#else
|
|
0 // pfnAssertFailed
|
|
#endif
|
|
};
|
|
|
|
#endif // LINESERVICES
|