windows-nt/Source/XPSP1/NT/windows/richedit/lssrc/disptext.c
2020-09-26 16:20:57 +08:00

410 lines
11 KiB
C

/*
* Contains Display and CalcPres methods of display object
*/
#include "disptext.h"
#include "dispmisc.h"
#include "lsdevice.h"
#include "lstfset.h"
#include "lstxtmap.h"
#include "dispi.h"
#include "txtobj.h"
#include "txtln.h"
#include "txtils.h"
#include "lschp.h"
#define TABBUFSIZE 32
static LSERR DisplayGlyphs(PTXTOBJ ptxtobj, PCDISPIN pdispin);
static LSERR DisplayTabLeader(PCDISPIN pdispin, PILSOBJ pilsobj, WCHAR wchtl);
// %%Function: DisplayText
// %%Contact: victork
//
LSERR WINAPI DisplayText(PDOBJ pdo, PCDISPIN pdispin)
{
LSERR lserr;
PTXTOBJ ptxtobj;
WCHAR wchSave;
WCHAR* pwch;
int iwch;
int cwch;
PILSOBJ pilsobj;
POINT ptOrg, ptExtTextOut;
POINTUV ptLeftCut;
long dupStart;
long dupPenStart;
long* pdup;
long* pdupPen;
long* rgdupLeftCut;
int i;
void* (WINAPI* pfnNewPtr)(POLS, DWORD);
void (WINAPI* pfnDisposePtr)(POLS, void*);
ptxtobj = (PTXTOBJ) pdo;
pilsobj = ptxtobj->plnobj->pilsobj;
Assert(ptxtobj->txtkind == txtkindRegular ||
ptxtobj->txtkind == txtkindHardHyphen ||
ptxtobj->txtkind == txtkindTab ||
ptxtobj->txtkind == txtkindNonReqHyphen ||
ptxtobj->txtkind == txtkindYsrChar ||
ptxtobj->txtkind == txtkindNonBreakSpace ||
ptxtobj->txtkind == txtkindNonBreakHyphen ||
ptxtobj->txtkind == txtkindOptNonBreak ||
ptxtobj->txtkind == txtkindSpecSpace ||
ptxtobj->txtkind == txtkindOptBreak ||
ptxtobj->txtkind == txtkindEOL );
if (ptxtobj->txtkind == txtkindTab)
{
Assert(ptxtobj->dupBefore == 0);
if (pdispin->dup <= 0) /* do nothing for zero-length tab */
{
return lserrNone;
}
// Draw tab only if it is visi case
if (ptxtobj->txtf&txtfVisi)
{
lserr = (*pilsobj->plscbk->pfnDrawTextRun)(pilsobj->pols, pdispin->plsrun,
FALSE, FALSE, /* Tab leader will take care of UL */
&(pdispin->ptPen), &(pilsobj->wchVisiTab),
(const int*) &(pdispin->dup), 1,
pdispin->lstflow, (const) pdispin->kDispMode,
&(pdispin->ptPen), &(pdispin->heightsPres), pdispin->dup,
pdispin->dupLimUnderline, pdispin->prcClip);
if (lserr != lserrNone) return lserr;
}
if (ptxtobj->u.tab.wchTabLeader == pilsobj->wchSpace)
{
// it is OK to draw the space in one take
lserr = (*pilsobj->plscbk->pfnDrawTextRun)(pilsobj->pols, pdispin->plsrun,
pdispin->fDrawStrikethrough, pdispin->fDrawUnderline,
&(pdispin->ptPen), &(pilsobj->wchSpace),
(const int*) &(pdispin->dup), 1,
pdispin->lstflow, (const) pdispin->kDispMode,
&(pdispin->ptPen), &(pdispin->heightsPres), pdispin->dup,
pdispin->dupLimUnderline, pdispin->prcClip);
}
else
{
// we should apply tab leader alingment logic
lserr = DisplayTabLeader(pdispin, pilsobj, ptxtobj->u.tab.wchTabLeader);
}
return lserr;
}
if (ptxtobj->txtf & txtfGlyphBased)
{
return DisplayGlyphs(ptxtobj, pdispin);
}
iwch = ptxtobj->iwchFirst;
pwch = ptxtobj->plnobj->pwch + iwch;
cwch = ptxtobj->iwchLim - iwch;
if (cwch == 0) // nothing to display
{
return lserrNone;
}
Assert(ptxtobj->plnobj->pdupPen == ptxtobj->plnobj->pdup || ptxtobj->plnobj->pdupPen == ptxtobj->plnobj->pdupPenAlloc);
pdupPen = ptxtobj->plnobj->pdupPen + iwch;
ptOrg = pdispin->ptPen;
if (ptxtobj->dupBefore == 0)
{
ptExtTextOut = ptOrg;
}
else
{
ptLeftCut.u = -ptxtobj->dupBefore;
ptLeftCut.v = 0;
LsPointXYFromPointUV(&(ptOrg), pdispin->lstflow, &ptLeftCut, &ptExtTextOut);
}
// Have to deal with special spaces before DrawTextRun
if (ptxtobj->txtkind == txtkindSpecSpace && !(ptxtobj->txtf&txtfVisi))
{
wchSave = *pwch; // remember actual code
for (i = 0; i < cwch; i++)
{
pwch[i] = pilsobj->wchSpace; // replace special spaces with the normal space
}
lserr = (*pilsobj->plscbk->pfnDrawTextRun)(pilsobj->pols, pdispin->plsrun,
pdispin->fDrawStrikethrough, pdispin->fDrawUnderline,
&ptExtTextOut, pwch, (const int*) pdupPen, cwch,
pdispin->lstflow, pdispin->kDispMode,
&ptOrg, &(pdispin->heightsPres),
pdispin->dup, pdispin->dupLimUnderline, pdispin->prcClip);
if (lserr != lserrNone) return lserr;
for (i = 0; i < cwch; i++)
{
pwch[i] = wchSave; // restore special spaces
}
}
else
{
lserr = (*pilsobj->plscbk->pfnDrawTextRun)(pilsobj->pols, pdispin->plsrun,
pdispin->fDrawStrikethrough, pdispin->fDrawUnderline,
&ptExtTextOut, pwch, (const int*) pdupPen, cwch,
pdispin->lstflow, pdispin->kDispMode,
&ptOrg, &(pdispin->heightsPres),
pdispin->dup, pdispin->dupLimUnderline, pdispin->prcClip);
if (lserr != lserrNone) return lserr;
}
if (pdispin->plschp->EffectsFlags)
{
pfnNewPtr = pilsobj->plscbk->pfnNewPtr;
pfnDisposePtr = pilsobj->plscbk->pfnDisposePtr;
/* create array for LeftCut info */
rgdupLeftCut = pfnNewPtr(pilsobj->pols, cwch * sizeof(*rgdupLeftCut));
if (rgdupLeftCut == NULL)
return lserrOutOfMemory;
/* fill LeftCut info array */
pdup = ptxtobj->plnobj->pdup + iwch;
dupStart = pdup[0]; /* the beginning of char */
dupPenStart = pdupPen[0]; /* starting position for drawing char */
for (i = 1; i < cwch; i++)
{
rgdupLeftCut[i] = dupStart - dupPenStart;
dupStart += pdup[i];
dupPenStart += pdupPen[i];
}
rgdupLeftCut[0] = ptxtobj->dupBefore;
lserr = (*pilsobj->plscbk->pfnDrawEffects)(pilsobj->pols, pdispin->plsrun, pdispin->plschp->EffectsFlags,
&(ptOrg), pwch, (const int*) pdup, (const int*) rgdupLeftCut,
ptxtobj->iwchLim - iwch,
pdispin->lstflow, pdispin->kDispMode, &(pdispin->heightsPres),
pdispin->dup, pdispin->dupLimUnderline, pdispin->prcClip);
/* dispose of the array for LeftCut info */
pfnDisposePtr(pilsobj->pols, rgdupLeftCut);
}
return lserr;
}
// %%Function: DisplayTabLeader
// %%Contact: victork
//
static LSERR DisplayTabLeader(PCDISPIN pdispin, PILSOBJ pilsobj, WCHAR wchtl)
{
LSTFLOW lstflow = pdispin->lstflow;
LONG dupSum, dupCh, dupAdj, z, zOnGrid;
BOOL fGrow;
WCHAR rgwch[TABBUFSIZE];
LONG rgdup[TABBUFSIZE];
LONG dupbuf;
int i = 0, cwch, cwchout;
LSERR lserr;
POINT pt;
POINTUV ptAdj = {0,0};
lserr = (*pilsobj->plscbk->pfnGetRunCharWidths)(pilsobj->pols, pdispin->plsrun,
lsdevPres, (LPCWSTR) &wchtl, 1,
pdispin->dup, lstflow, (int*) &dupCh, &dupSum, (LONG*) &i);
if (lserr != lserrNone) return lserr;
if (i == 0 || dupCh <= 0) dupCh = 1;
for (i = 0; i < TABBUFSIZE; ++i)
{
rgwch[i] = wchtl;
rgdup[i] = dupCh;
}
/* advance to next multiple of dupCh
dupAdj is the distance between "pt.z" and the next integral multiple of dupch.
I.e. dupAdj = N * dupCh - "pt.x" where N is the smallest integer such that
N * dupCh is not less than "pt.x" in Latin case.
The starting pen position will be "rounded" to this "dupCh stop" by the assignment
"pt.z += dupAdj" in the code below.
Complications are:
depending on lstflow "z" can be either x or y;
depending on lstflow next can be bigger (Grow) or smaller;
Simple formula dupAdj = (ptPen.x + dupCh - 1) / dupCh * dupCh - ptPen.x does not
necessarily work if ptPen.x is negative;
*/
if (lstflow & fUVertical)
{
z = pdispin->ptPen.y;
}
else
{
z = pdispin->ptPen.x;
}
if (lstflow & fUDirection)
{
fGrow = fFalse;
}
else
{
fGrow = fTrue;
}
zOnGrid = (z / dupCh) * dupCh;
// zOnGrid is on grid, but maybe from the wrong side
if (zOnGrid == z)
{
dupAdj = 0;
}
else if (zOnGrid > z)
{
if (fGrow)
{
dupAdj = zOnGrid - z; // zOnGrid is the point we want
}
else
{
dupAdj = dupCh - (zOnGrid - z); // zOnGrid is on the wrong side
}
}
else // zOnGrid < z
{
if (!fGrow)
{
dupAdj = z - zOnGrid; // zOnGrid is the point we want
}
else
{
dupAdj = dupCh - (z - zOnGrid); // zOnGrid is on the wrong side
}
}
cwch = (pdispin->dup - dupAdj) / dupCh; /* always round down */
dupbuf = dupCh * TABBUFSIZE;
#ifdef NEVER // We've decided to kill rcClip optimization for now.
while (cwch > 0 && up <= pdispin->rcClip.right && lserr == lserrNone)
#endif /* NEVER */
while (cwch > 0 && lserr == lserrNone)
{
cwchout = cwch < TABBUFSIZE ? cwch : TABBUFSIZE;
ptAdj.u = dupAdj;
LsPointXYFromPointUV(&(pdispin->ptPen), lstflow, &ptAdj, &(pt));
lserr = (*pilsobj->plscbk->pfnDrawTextRun)(pilsobj->pols, pdispin->plsrun,
pdispin->fDrawStrikethrough, pdispin->fDrawUnderline,
&pt, rgwch, (const int*) rgdup, cwchout,
lstflow, pdispin->kDispMode, &pt, &(pdispin->heightsPres),
dupCh * cwchout, pdispin->dupLimUnderline, pdispin->prcClip);
cwch -= cwchout;
dupAdj += dupbuf;
}
return lserr;
}
// %%Function: DisplayGlyphs
// %%Contact: victork
//
static LSERR DisplayGlyphs(PTXTOBJ ptxtobj, PCDISPIN pdispin)
{
LSERR lserr;
PLNOBJ plnobj = ptxtobj->plnobj;
PILSOBJ pilsobj = plnobj->pilsobj;
WCHAR* pwch;
int iwch;
int cwch;
if (plnobj->fDrawInCharCodes)
{
// for meta-file output we call pfnDrawTextRun without widths
iwch = ptxtobj->iwchFirst;
pwch = ptxtobj->plnobj->pwch + iwch;
cwch = ptxtobj->iwchLim - iwch;
lserr = (*pilsobj->plscbk->pfnDrawTextRun)(pilsobj->pols, pdispin->plsrun,
pdispin->fDrawStrikethrough, pdispin->fDrawUnderline,
&(pdispin->ptPen), pwch, NULL, cwch,
pdispin->lstflow, pdispin->kDispMode,
&(pdispin->ptPen), &(pdispin->heightsPres),
pdispin->dup, pdispin->dupLimUnderline, pdispin->prcClip);
}
else
{
lserr = (*pilsobj->plscbk->pfnDrawGlyphs)(pilsobj->pols, pdispin->plsrun,
pdispin->fDrawStrikethrough, pdispin->fDrawUnderline,
&plnobj->pgind[ptxtobj->igindFirst],
(const int*)&plnobj->pdupGind[ptxtobj->igindFirst],
(const int*)&plnobj->pdupBeforeJust[ptxtobj->igindFirst],
&plnobj->pgoffs[ptxtobj->igindFirst],
&plnobj->pgprop[ptxtobj->igindFirst],
&plnobj->pexpt[ptxtobj->igindFirst],
ptxtobj->igindLim - ptxtobj->igindFirst,
pdispin->lstflow, pdispin->kDispMode,
&(pdispin->ptPen), &(pdispin->heightsPres),
pdispin->dup, pdispin->dupLimUnderline, pdispin->prcClip);
}
return lserr;
}
// %%Function: CalcPresentationText
// %%Contact: victork
//
/*
* CalcPres for text is called only for the dnode between autonumbering dnode and main text.
* The dnode should contain one character (space).
*/
LSERR WINAPI CalcPresentationText(PDOBJ pdobj, long dup, LSKJUST lskj, BOOL fLastOnLine)
{
PTXTOBJ ptxtobj = (PTXTOBJ)pdobj;
Unreferenced(lskj);
Unreferenced(fLastOnLine);
Assert(ptxtobj->txtkind == txtkindRegular);
Assert(ptxtobj->iwchFirst + 1 == ptxtobj->iwchLim);
(ptxtobj->plnobj->pdup)[ptxtobj->iwchFirst] = dup;
return lserrNone;
}