Leaked source code of windows server 2003
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.
 
 
 
 
 
 

3456 lines
96 KiB

/******************************module*header*******************************\
* Module Name: textobj.cxx
*
* Supporting routines for text output calls, ExtTextOut etc.
*
* Created: 08-Feb-1991 09:25:14
* Author: Bodin Dresevic [BodinD]
*
* Copyright (c) 1999 Microsoft Corporation
\**************************************************************************/
#include "precomp.hxx"
#define XFORMNULL (EXFORMOBJ *) NULL
VOID vGenWidths
(
FIX *pfxD1,
FIX *pfxD2,
EFLOAT& efRA,
EFLOAT& efRB,
FIX fxWidth,
FIX fxTop,
FIX fxBottom,
FIX fxMaxAscent
);
#define SO_ACCEL_FLAGS (SO_FLAG_DEFAULT_PLACEMENT | SO_MAXEXT_EQUAL_BM_SIDE \
| SO_CHAR_INC_EQUAL_BM_BASE | SO_ZERO_BEARINGS)
BOOL bAdjusBaseLine(RFONTOBJ &rfoBase, RFONTOBJ &rfoLink, POINTL *pptlAdjustBaseLine);
/******************************Member*Function*****************************\
* ESTROBJ::vInit
*
* Constructor for ESTROBJ. Performs the following operations:
*
* 1) Initialize the STROBJ fields for the driver.
* 2) Compute all character positions.
* 3) Compute the TextBox.
* 4) In the simplest cases, computes rectangles for underline and
* strikeout.
*
* The TextBox is a parallelogram in device coordinates, whose sides are
* parallel to the transformed sides of a character cell, and which bounds
* all the character cells. A and C spacings are taken into account to
* assure that it is a proper bound.
*
* We record the TextBox in an unusual notation. If a line is constructed
* through the character origin of the first character in the string and in
* the direction of the ascent, then what we record as the "left" and
* "right" of the TextBox are really the distances from this line to each
* of those edges of the parallelogram, in device coordinates. Likewise,
* the "top" and "bottom" are the distances from the baseline of the first
* character. This completely determines the parallelogram, and is
* surprisingly easy to compute. ExtTextOut later turns this data into the
* actual parallelogram, whereas the TextExtent functions turn this data
* into scalar extents.
*
* History:
* Fri 13-Mar-1992 00:47:05 -by- Charles Whitmer [chuckwh]
* Rewrote from the ground up.
*
* 21-Jan-1991 -by- Bodin Dresevic [BodinD]
* Wrote it.
\**************************************************************************/
VOID ESTROBJ::vInit
(
PWSZ pwsz,
LONG cwc,
XDCOBJ& dco,
RFONTOBJ& rfo,
EXFORMOBJ& xo,
LONG *pdx,
BOOL bPdy,
LONG lEsc,
LONG lExtra,
LONG lBreakExtra,
LONG cBreak,
FIX xRef,
FIX yRef ,
FLONG flControl,
LONG *pdxOut,
PVOID pvBuffer,
DWORD CodePage
)
{
EGLYPHPOS *pg;
GLYPHDATA *pgd;
UINT ii;
EFLOAT efScaleX = xo.efM11();
cGlyphs = cwc;
prfo = &rfo;
flTO = 0L;
flAccel = bPdy ? SO_DXDY : 0L;
// we don't want to substitute the symbol font
// if its glyph set has been extended
{
PFE *ppfe = rfo.ppfe();
ASSERTGDI(ppfe->pfdg, "ESTROBJ::vInit invalid ppfe->pfdg \n");
if (ppfe->pfdg->flAccel & GS_EXTENDED)
{
ASSERTGDI(ppfe->pifi->jWinCharSet == SYMBOL_CHARSET,
"ESTROBJ::vInit(): GS_EXTENDED set for on SYMBOL_CHARSET fonts\n");
flAccel |= SO_DO_NOT_SUBSTITUTE_DEVICE_FONT;
}
}
ulCharInc = 0L;
cgposCopied = 0;
cgposPositionsEnumerated = 0;
cExtraRects = 0;
pgp = NULL;
pgpos = NULL;
pwszOrg = pwsz;
dwCodePage = CodePage;
xExtra = 0; // will be computed later if needed
xBreakExtra = 0; // will be computed later if needed
// fix the string if in GLYPH_INDEX mode, waste of time to be win95 compatible
if (rfo.prfnt->flType & RFONT_TYPE_HGLYPH)
{
flAccel |= SO_GLYPHINDEX_TEXTOUT;
rfo.vFixUpGlyphIndices((USHORT *)pwsz, cwc);
}
// Remember if the printer driver wants 28.4 char positions.
PDEVOBJ po(rfo.hdevConsumer());
if (po.bCapsHighResText())
flTO |= TO_HIGHRESTEXT;
// Locate the GLYPHPOS array. Use the buffer given, if there is one. We
// need one more GLYPHPOS structure than there are glyphs. The concatenation
// point is computed in the last one.
if (pvBuffer)
{
pg = (EGLYPHPOS *) pvBuffer;
}
else
{
pg = (EGLYPHPOS *) PVALLOCTEMPBUFFER(SIZEOF_STROBJ_BUFFER(cwc));
if (pg == (PGLYPHPOS) NULL)
return;
// Note that the memory has been allocated.
flTO |= TO_MEM_ALLOCATED;
}
pgpos = pg; // Make sure our cached value is in the structure.
// In Win 3.1 compatibility mode, we always assume that escapement is
// the same as orientation, except for vector fonts. Make it explicitly so.
if (rfo.iGraphicsMode() == GM_COMPATIBLE)
{
if (!(rfo.prfnt->flInfo & FM_INFO_TECH_STROKE))
lEsc = rfo.ulOrientation();
}
// Offset the reference point for TA_TOP and TA_BOTTOM. Our GLYPHPOS
// structures always contain positions along the baseline. The TA_BASELINE
// case is therefore already correct.
switch (flControl & (TA_TOP | TA_BASELINE | TA_BOTTOM))
{
case TA_TOP:
xRef -= rfo.ptfxMaxAscent().x;
yRef -= rfo.ptfxMaxAscent().y;
break;
case TA_BOTTOM:
xRef -= rfo.ptfxMaxDescent().x;
yRef -= rfo.ptfxMaxDescent().y;
break;
}
/**************************************************************************\
* [Win 3.1 compatibility issue]
*
* Adjust pdx array if there is a non-zero lExtra.
*
* This is only done under compatibility mode and only when using non-vector
* fonts on a non-display device.
\**************************************************************************/
if ( lExtra
&& (pdx != (LONG *) NULL)
&& (rfo.iGraphicsMode() == GM_COMPATIBLE)
&& (!(rfo.prfnt->flInfo & FM_INFO_TECH_STROKE))
&& po.bDisplayPDEV() )
{
LONG *pdxAdj = pdx;
LONG *pdxEnd;
if (!bPdy)
{
pdxEnd = pdx + cwc;
for (;pdxAdj < pdxEnd; pdxAdj += 1)
*pdxAdj += lExtra;
}
else
{
pdxEnd = pdx + 2*cwc;
for (;pdxAdj < pdxEnd; pdxAdj += 2)
*pdxAdj += lExtra;
}
}
/**************************************************************************\
* Handle all horizontal special cases.
\**************************************************************************/
if (
((lEsc | rfo.ulOrientation()) == 0)
&& xo.bScale()
&& !xo.efM22().bIsNegative() // Otherwise the ascent goes down.
&& !efScaleX.bIsNegative() // Otherwise A and C spaces get confused.
)
{
/**********************************************************************\
* We provide several special routines to calculate the character
* positions and TextBox. Each routine is responsible for calculating:
*
* 1) Each position
* 2) Bounding coordinates.
* 3) The ptfxUpdate vector.
* 4) The flAccel flags.
\**********************************************************************/
if (pdx != NULL)
{
// Case H1: A PDX array is provided.
if (!bPdy)
{
vCharPos_H1(
dco,
rfo,xRef,yRef,pdx,efScaleX);
}
else
{
if (flControl & (TSIM_UNDERLINE1 | TSIM_STRIKEOUT))
{
// In H4 case we want to force individual character underlining
// as we do for esc!=orientation case,
if (!rfo.bCalcEscapement(xo,lEsc))
return;
flTO |= TO_ESC_NOT_ORIENT;
}
vCharPos_H4(
dco,
rfo,xRef,yRef,pdx, efScaleX,xo.efM22());
}
}
else if (rfo.lCharInc() && ((lExtra | lBreakExtra) == 0))
{
// Case H2: Characters have constant integer width.
vCharPos_H2(dco,rfo,xRef,yRef,efScaleX);
}
else
{
// Case H3: The general case.
vCharPos_H3(dco,rfo,xRef,yRef,lExtra,lBreakExtra,cBreak,efScaleX);
}
/**********************************************************************\
* Horizontal special cases done!
*
* It remains only to finish the alignment.
\**********************************************************************/
// Offset all the relevant points if our alignment is RIGHT or CENTER.
// Also correct the CP update vector.
ptfxEscapement.x = ptfxUpdate.x; // Remember this for underlining.
ptfxEscapement.y = ptfxUpdate.y;
if (flControl & (TA_RIGHT | TA_CENTER))
{
FIX dx = ptfxUpdate.x;
if ((flControl & TA_CENTER) == TA_CENTER)
{
dx /= 2;
ptfxUpdate.x = 0; // Don't update CP.
}
else
{
ptfxUpdate.x = -ptfxUpdate.x; // Reverse the CP update.
}
pg = pgpos;
dx = FXTOL(dx + 8); // Convert to LONG for pgp adjusts
xRef = ((pg++)->ptl.x -= dx); // Always adjust the first one.
xRef = LTOFX(xRef); // Convert back to FIX for later
if (ulCharInc == 0)
{
for (ii=0; ii<(ULONG) cwc-1; ii++)
(pg++)->ptl.x -= dx;
}
}
// Fill in a pdxOut array.
if (pdxOut != (LONG *) NULL)
{
EFLOAT efDtoW = rfo.efDtoWBase();
if (ulCharInc && !bLinkedGlyphs())
{
FIX dx, xSum;
dx = lCvt(efDtoW,LTOFX(ulCharInc));
xSum = 0;
for (ii=0; ii<(ULONG) cwc; ii++)
{
xSum += dx;
*pdxOut++ = xSum;
}
}
else
{
pg = pgpos + 1; // skip the first element
for (ii=0; ii<(ULONG) cwc-1; ii++,pg++)
*pdxOut++ = lCvt(efDtoW,LTOFX(pg->ptl.x) - xRef);
// Unfortunately, to be consistent with the rounding we've done
// in the above cases, we can't simply compute this last one
// as: *pdxOut = lCvt(efDtoW,ptfxUpdate.x):
*pdxOut = lCvt(efDtoW, ((ptfxUpdate.x + xRef) & ~0xf) - xRef);
}
}
ptfxRef.x = LTOFX(pgpos->ptl.x);
ptfxRef.y = LTOFX(pgpos->ptl.y);
}
/**************************************************************************\
* Handle the harder cases, i.e general orientations and transforms are
* allowed.
\**************************************************************************/
else
{
if (!bPdy)
{
if (lEsc == (LONG)rfo.ulOrientation())
{
if (pdx != NULL)
{
// Case G1: A PDX array is provided. Escapement == Orientation.
vCharPos_G1(dco,rfo,xRef,yRef,pdx,pdxOut);
}
else // (pdx == NULL)
{
// Case G2: No PDX array. Escapement == Orientation.
vCharPos_G2(dco,rfo,xRef,yRef,lExtra,lBreakExtra,cBreak,pdxOut);
}
}
else // lEsc!=rfo.ulOrientation()
{
// Case G3: Escapement != Orientation.
// Make sure escapement vectors and data are up to date.
if (!rfo.bCalcEscapement(xo,lEsc))
return;
flTO |= TO_ESC_NOT_ORIENT;
flAccel |= SO_ESC_NOT_ORIENT;
vCharPos_G3
(
dco,
rfo,
xRef,yRef,
lExtra,lBreakExtra,cBreak,
pdx,
pdxOut
);
}
}
else // pdy case
{
// Make sure escapement vectors and data are up to date.
if (!rfo.bCalcEscapement(xo,lEsc))
return;
flTO |= TO_ESC_NOT_ORIENT;
vCharPos_G4
(
dco,
rfo,
xRef,
yRef,
pdx
);
}
/**********************************************************************\
* General special cases done!
*
* It remains only to finish the alignment.
\**********************************************************************/
// Offset all the relevant points if our alignment is RIGHT or CENTER.
// Also correct the CP update vector.
ptfxEscapement = ptfxUpdate; // Remember this for underlining.
if (flControl & (TA_RIGHT | TA_CENTER))
{
FIX dx = ptfxUpdate.x;
FIX dy = ptfxUpdate.y;
if ((flControl & TA_CENTER) == TA_CENTER)
{
dx /= 2;
dy /= 2;
ptfxUpdate.x = 0; // No CP update.
ptfxUpdate.y = 0;
}
else
{
ptfxUpdate.x = -ptfxUpdate.x; // Reverse the CP update.
ptfxUpdate.y = -ptfxUpdate.y;
}
pg = pgpos;
for (ii=0; ii<(ULONG)cwc; ii++)
{
pg->ptl.x -= dx;
pg->ptl.y -= dy;
pg++;
}
xRef -= dx;
yRef -= dy;
}
// Convert the FIX coordinates to LONG for low res devices.
pg = pgpos;
ptfxRef.x = xRef;
ptfxRef.y = yRef;
for (ii=0; ii<(ULONG)cwc; ii++,pg++)
{
pg->ptl.x = FXTOL(pg->ptl.x + 8);
pg->ptl.y = FXTOL(pg->ptl.y + 8);
}
}
// Fill in the underline and strikeout rectangles. The horizontal cases
// are the only ones that can use the extra rectangles of DrvTextOut to
// draw the lines quickly. We'll use a PATHOBJ (in a separate optional
// method) for the complex cases.
if (flControl & (TSIM_UNDERLINE1 | TSIM_STRIKEOUT))
{
flTO |= flControl & (TSIM_UNDERLINE1 | TSIM_STRIKEOUT);
if (((lEsc | rfo.ulOrientation() | bPdy) == 0) && xo.bScale())
{
ERECTL *prcl = (ERECTL *) &arclExtra[cExtraRects];
// Round off the starting point and update width to pels.
LONG x = FXTOL(xRef+8);
LONG y = FXTOL(yRef+8);
LONG dx = FXTOL(ptfxEscapement.x+8);
if (flControl & TSIM_UNDERLINE1)
{
prcl->right = (prcl->left = x + rfo.ptlUnderline1().x) + dx;
prcl->bottom = (prcl->top = y + rfo.ptlUnderline1().y)
+ rfo.ptlULThickness().y;
prcl->vOrder();
cExtraRects++;
prcl++;
}
if (flControl & TSIM_STRIKEOUT)
{
prcl->right = (prcl->left = x + rfo.ptlStrikeOut().x) + dx;
prcl->bottom = (prcl->top = y + rfo.ptlStrikeOut().y)
+ rfo.ptlSOThickness().y;
prcl->vOrder();
cExtraRects++;
prcl++;
}
// Put a NULL rectangle at the end of the list.
prcl->left = 0;
prcl->right = 0;
prcl->bottom = 0;
prcl->top = 0;
}
}
if ( rfo.prfnt->fobj.flFontType & RASTER_FONTTYPE )
{
flTO |= TO_BITMAPS;
}
else
{
flTO &= ~TO_BITMAPS;
}
}
/******************************Member*Function*****************************\
* ESTROBJ::vInitSimple
*
* Constructor for ESTROBJ. Performs the following operations:
*
* 1) Initialize the STROBJ fields for the driver.
* 2) Compute all character positions.
* 3) Compute the TextBox.
* 4) Stores the TextBox in the ESTROBJ. <--- Note!
*
* The is the special case code for the console, so we ignore transforms
* and other fancy options.
*
* Note that you don't need to call bOpaqueArea to get rclBkGround
* initialized. This is a difference from the normal vInit.
*
* History:
* Thu 17-Sep-1992 17:32:10 -by- Charles Whitmer [chuckwh]
* Took the vInit code and simplified it for the special console case.
\**************************************************************************/
VOID ESTROBJ::vInitSimple
(
PWSZ pwsz,
LONG cwc,
XDCOBJ& dco,
RFONTOBJ& rfo,
LONG xRef,
LONG yRef,
PVOID pvBuffer
)
{
// Characters have constant integer width. We will also ignore the A and C spaces.
EGLYPHPOS *pg;
cGlyphs = cwc;
prfo = &rfo;
cgposCopied = 0;
pgp = NULL;
pwszOrg = pwsz;
ASSERTGDI(cwc > 0, "Count of glyphs must be greater than zero");
// Locate the GLYPHPOS array. Try to use the default one on the stack. We
// need one more GLYPHPOS structure than there are glyphs. The concatenation
// point is computed in the last one.
if (pvBuffer)
{
pg = (EGLYPHPOS *) pvBuffer;
}
else
{
pg = (EGLYPHPOS *) PVALLOCTEMPBUFFER(SIZEOF_STROBJ_BUFFER(cwc));
if (pg == (PGLYPHPOS) NULL)
return;
// Note that the memory has been allocated.
flTO |= TO_MEM_ALLOCATED;
}
pgpos = pg; // Make sure our cached value is in the structure.
// Compute device driver accelerator flags.
flAccel = SO_HORIZONTAL | (rfo.flRealizedType() & SO_ACCEL_FLAGS);
BOOL bAccel;
if (!rfo.bGetGlyphMetricsPlus(cwc, pg, pwsz, &bAccel, &dco, this))
return;
if ( bAccel )
{
flTO |= TO_ALL_PTRS_VALID;
pgp = pgpos;
}
// We leave these variables uninitialized in this simple case.
// BE CAREFUL!
// cExtraRects = 0;
// ptfxUpdate.x = dx;
// ptfxUpdate.y = 0;
// ptfxEscapement = ptfxUpdate;
// ptfxRef.x = xRef;
// ptfxRef.y = yRef;
// Offset the reference point for TA_TOP. Our GLYPHPOS structures always contain
// positions along the baseline.
// We assume that displays never set the GCAPS_HIGHRESTEXT bit.
pg->ptl.x = xRef;
pg->ptl.y = yRef + rfo.lMaxAscent();
// Set the width accelerator. When the device driver sees ulCharInc
// non-zero, it must not expect any more x coordinates.
ulCharInc = rfo.lCharInc();
#ifdef FE_SB
// If this is a SBCS linked to a DBCS font the font is really "binary pitch" and we
// should ignore ulCharInc. In the case of a DBCS TT font, the font is also really
// "binary pitch" and the TT driver will set ulCharInc to 0. In either case use
// the individual glyph metrics to lay out glyphs. This is slightly slower than
// the fixed pitch optimization but not enough to be significant.
if( bLinkedGlyphs() || (ulCharInc == 0) )
{
FIX xA , xLeft , xRight;
UINT ii;
ulCharInc = 0;
// Calculate the left bound using only the first glyph.
xLeft = pg->pgd()->fxA;
// Handle the remaining glyphs in this batch.
xA = 0; // Distance along the escapement (x) in device coords.
for (ii=1; ii<(unsigned) cwc; ii++)
{
// Compute the next position.
xA += pg->pgd()->fxD;
pg++;
pg->ptl.x = xRef + FXTOL(8 + xA);
pg->ptl.y = yRef + rfo.lMaxAscent();
}
// Calculate the right bound. This is easier than the general case since
// there's no extra spacing.
xRight = xA + pg->pgd()->fxAB;
// Compute the bounding rectangle.
rclBkGround.left = xRef + FXTOL(xLeft);
rclBkGround.right = xRef + FXTOLCEILING(xRight);
rclBkGround.top = yRef;
rclBkGround.bottom = yRef + rfo.lMaxHeight();
}
else
{
#endif
// Compute the bounding rectangle.
rclBkGround.left = xRef;
rclBkGround.right = xRef + cwc * ulCharInc;
rclBkGround.top = yRef;
rclBkGround.bottom = yRef + rfo.lMaxHeight();
}
// color filtering causes spill on each side for cleartype
if (rfo.pfo()->flFontType & FO_CLEARTYPE_X)
{
rclBkGround.left -= 1;
rclBkGround.right += 1;
}
flTO |= TO_VALID;
}
/******************************Public*Routine******************************\
*
* Routine Name: "ESTROBJ::vCorrectBackGround"
*
* Routine Description:
*
* If a glyph lies outside the background rectangle
* the background rectangle is expanded to contain
* the errant glyph. This is a hack routine to fix
* some anomalies found when rendering texts at
* arbitrary angles.
*
* Arguments:
*
* pfo a pointer to the associated FONTOBJ
* This is used to inform us if the
* font contains bitmaps
*
* Return Value: TRUE if string is consistent FALSE if not.
*
\**************************************************************************/
#if DBG
void ESTROBJ::vCorrectBackGroundError(GLYPHPOS *pgp)
{
GLYPHBITS *pgb = pgp->pgdf->pgb;
char *psz = "vCorrectBackGround: Glyph found outside background rectangle";
DbgPrint("\n");
DbgPrint("\n");
DbgPrint("%s\n", psz);
DbgPrint("STROJB* = %-#x\n", this);
DbgPrint(
"rclBkGround = %d %d %d %d\n",
rclBkGround.left,
rclBkGround.top,
rclBkGround.right,
rclBkGround.bottom
);
DbgPrint("pgp = %-#x\n", pgp);
DbgPrint(" hg = %-#x\n", pgp->hg);
DbgPrint(" ptl = %d %d\n", pgp->ptl.x, pgp->ptl.y);
DbgPrint(" pgb = %-#x\n", pgb);
DbgPrint(" ptlOrigin = %d %d\n", pgb->ptlOrigin.x, pgb->ptlOrigin.y);
DbgPrint(" sizlBitmap = %d %d\n", pgb->sizlBitmap.cx, pgb->sizlBitmap.cy);
DbgPrint("\n");
RIP("Stopping for debugging\n");
}
void ESTROBJ::vCorrectBackGround()
{
LONG l; // place holder for glyph bitmap coord
GLYPHPOS *pgp, *pgp_;
GLYPHBITS *pgb;
if (
((flAccel & SO_FLAG_DEFAULT_PLACEMENT) == 0 ) &&
( flTO & TO_BITMAPS ) &&
( pgpos )
)
{
pgp_ = pgpos + cGlyphs;
for ( pgp = pgpos ; pgp < pgp_ ; pgp++ ) {
if ( pgb = pgp->pgdf->pgb )
{
l = pgp->ptl.x + pgb->ptlOrigin.x;
if ( l < rclBkGround.left )
{
vCorrectBackGroundError( pgp );
rclBkGround.left = l;
}
l += pgb->sizlBitmap.cx;
if ( l > rclBkGround.right )
{
vCorrectBackGroundError( pgp );
rclBkGround.right = l;
}
l = pgp->ptl.y + pgb->ptlOrigin.y;
if ( l < rclBkGround.top )
{
vCorrectBackGroundError( pgp );
rclBkGround.top = l;
}
l += pgb->sizlBitmap.cy;
if ( l > rclBkGround.bottom )
{
vCorrectBackGroundError( pgp );
rclBkGround.bottom = l;
}
}
}
}
}
#endif
/******************************Public*Routine******************************\
* ESTROBJ::vCharPos_G3 (rfo,x,y,lExtra,lBreakExtra,cBreak,pdx,pdxOut)
*
* Computes character positions in the case where escapement does not equal
* orientation. This is pretty tricky. We have to make up vertical
* spacings. Also, the character positions we record in the GLYPHPOS
* structure all have to be adjusted because for general escapements the
* concatenation point is not the same as the character origin.
*
* The following fields of the ESTROBJ are filled in:
*
* 1) Each x,y position.
* 2) Bounding coordinates in rcfx.
* 3) The ptfxUpdate vector.
*
* History:
* Sun 22-Mar-1992 23:31:55 -by- Charles Whitmer [chuckwh]
* Wrote it.
\**************************************************************************/
VOID ESTROBJ::vCharPos_G3
(
XDCOBJ& dco,
RFONTOBJ& rfo,
FIX xRef,
FIX yRef,
LONG lExtra,
LONG lBreakExtra,
LONG cBreak,
LONG *pdx,
LONG *pdxOut
)
{
// In this case, we keep track of how far we have traveled along the
// escapement vector in device coordinates. We need scales to project
// this distance onto the base and ascent, as well as a unit escapement
// vector to find the position.
POINTFL pteEsc = rfo.pteUnitEsc(); // Unit escapement vector.
EFLOAT efScaleX = rfo.efEscToBase(); // Project escapement to baseline.
EFLOAT efScaleY = rfo.efEscToAscent(); // Project escapement to ascent.
// Compute logical to device transforms.
EFLOAT efWtoDEsc = rfo.efWtoDEsc(); // Forward transform for pdx.
EFLOAT efDtoWEsc = rfo.efDtoWEsc(); // Back transform for pdxOut.
// Compute extra spacing for the non-pdx case.
FIX fxD1,fxD2; // Pre- and Post-center character widths.
FIX fxAscent = rfo.fxMaxAscent(); // Cache locally.
HGLYPH hgBreak;
if (pdx == (LONG *) NULL)
{
// Calculate the lExtra and lBreakExtra spacing.
xExtra = 0;
xBreakExtra = 0;
hgBreak = 0;
if (lExtra)
{
xExtra = lCvt(rfo.efWtoDEsc(),lExtra);
}
if (lBreakExtra && cBreak)
{
xBreakExtra = lCvt(rfo.efWtoDEsc(),lBreakExtra) / cBreak;
// Windows won't let us back up over a break.
vGenWidths
(
&fxD1, // Pre-center spacing
&fxD2, // Post-center spacing
efScaleY, // Ascent projection
efScaleX, // Baseline projection
rfo.fxBreak(), // Character width
fxAscent, // Ink box top
0, // Ink box bottom
fxAscent // Maximum Ascent
);
if (fxD1 + fxD2 + xBreakExtra + xExtra < 0)
xBreakExtra = -(fxD1 + fxD2 + xExtra);
hgBreak = rfo.hgBreak();
}
}
// Make some local pointers.
EGLYPHPOS *pg = pgpos;
GLYPHDATA *pgd;
WCHAR *pwsz = pwszOrg;
// Set the first character position.
pg->ptl.x = xRef;
pg->ptl.y = yRef;
// Set up for character loop.
LONG xSum; // Keep pdx sum here.
FIX xA,xB; // Baseline coordinates.
FIX yA,yB; // Ascent coordinates.
FIX sA; // Position on the escapement vector.
RECTFX rcfxBounds; // Accumulate bounds here.
UINT ii;
FIX fxDescent = rfo.fxMaxDescent(); // Cache locally.
rcfxBounds.xLeft = LONG_MAX; // Start with an empty TextBox.
rcfxBounds.xRight = LONG_MIN;
rcfxBounds.yTop = LONG_MIN;
rcfxBounds.yBottom = LONG_MAX;
ASSERTGDI(cGlyphs > 0, "G3, cGlyphs == 0\n");
// We keep the current concatenation point in sA. Note that this is NOT
// where the character origin will be placed.
sA = 0;
xSum = 0;
BOOL bAccel;
if (!rfo.bGetGlyphMetricsPlus(cGlyphs, pg, pwsz, &bAccel, &dco, this))
return;
if (bAccel)
{
flTO |= TO_ALL_PTRS_VALID;
pgp = pgpos;
}
BOOL bZeroBearing = ( rfo.flRealizedType() & SO_ZERO_BEARINGS ) && !bLinkedGlyphs();
for (ii=0; ii<cGlyphs; ii++)
{
pgd = pg->pgd();
// Using the GenWidths function, determine where the
// character center should be placed.
vGenWidths
(
&fxD1, // Pre-center spacing
&fxD2, // Post-center spacing
efScaleY, // Ascent projection
efScaleX, // Baseline projection
pgd->fxD, // Character width
pgd->fxInkTop, // Ink box top
pgd->fxInkBottom, // Ink box bottom
fxAscent // Maximum Ascent
);
sA += fxD1; // Advance to the character center.
// Update ascent bounds.
yA = lCvt(efScaleY,sA); // Project onto ascent.
yB = yA + fxDescent;
if (yB < rcfxBounds.yBottom)
rcfxBounds.yBottom = yB;
yB = yA + fxAscent;
if (yB > rcfxBounds.yTop)
rcfxBounds.yTop = yB;
ASSERTGDI(!rfo.bSmallMetrics(),"ESTROBJ__vCharPos_G3: Small Metrics in cache\n");
// Project the center position onto the baseline and
// move back to the character origin.
xA = lCvt(efScaleX,sA) - pgd->fxD / 2;
// Update the width bounds. Fudge a quarter pel on each side for
// roundoff.
if( bZeroBearing )
{
xB = xA - 4;
if (xB < rcfxBounds.xLeft)
rcfxBounds.xLeft = xB;
xB = xA + pgd->fxD + 4;
if (xB > rcfxBounds.xRight)
rcfxBounds.xRight = xB;
}
else
{
xB = xA + pgd->fxA - 4;
if (xB < rcfxBounds.xLeft)
rcfxBounds.xLeft = xB;
xB = xA + pgd->fxAB + 4;
if (xB > rcfxBounds.xRight)
rcfxBounds.xRight = xB;
}
// Save the adjusted character origin.
pg->ptl.x
= xRef + lCvt(pteEsc.x,sA) - pgd->ptqD.x.u.HighPart / 2;
pg->ptl.y
= yRef + lCvt(pteEsc.y,sA) - pgd->ptqD.y.u.HighPart / 2;
// Advance to the next concatenation point.
if (pdx != (LONG *) NULL)
{
xSum += *pdx++;
sA = lCvt(efWtoDEsc,xSum);
if (pdxOut != (LONG *) NULL)
*pdxOut++ = xSum;
}
else
{
sA += fxD2 + xExtra;
if (xBreakExtra && (pg->hg == hgBreak))
{
sA += xBreakExtra;
}
// Record the concatenation point for GetTextExtentEx. This
// would be very difficult to reconstruct at a later time.
if (pdxOut != (LONG *) NULL)
*pdxOut++ = lCvt(efDtoWEsc,sA);
}
pg++;
}
ptfxUpdate.x = lCvt(pteEsc.x,sA);
ptfxUpdate.y = lCvt(pteEsc.y,sA);
rcfx = rcfxBounds;
flTO |= TO_VALID;
}
/******************************Public*Routine******************************\
* ESTROBJ::vCharPos_G2 (rfo,xRef,yRef,lExtra,lBreakExtra,cBreak,pdxOut)
*
* Computes character positions in the case where no pdx array is provided
* and escapement equals orientation.
*
* The following fields of the ESTROBJ are filled in:
*
* 1) Each x,y position.
* 2) Bounding coordinates in rcfx.
* 3) The ptfxUpdate vector.
* 4) The fxExtent.
*
* History:
* Sun 22-Mar-1992 23:31:55 -by- Charles Whitmer [chuckwh]
* Wrote it.
\**************************************************************************/
VOID ESTROBJ::vCharPos_G2
(
XDCOBJ& dco,
RFONTOBJ& rfo,
FIX xRef,
FIX yRef,
LONG lExtra,
LONG lBreakExtra,
LONG cBreak,
LONG *pdxOut
)
{
// Calculate the lExtra and lBreakExtra spacing.
HGLYPH hgBreak = 0;
EPOINTQF ptqExtra; // Accurate spacing along the escapement.
EPOINTQF ptqBreakExtra;
if (lExtra)
{
xExtra = lCvt(rfo.efWtoDBase(),lExtra);
ptqExtra = rfo.pteUnitBase();
ptqExtra *= (LONG) xExtra;
}
if (lBreakExtra && cBreak)
{
xBreakExtra = lCvt(rfo.efWtoDBase(),lBreakExtra) / cBreak;
// Windows won't let us back up over a break.
if (rfo.fxBreak() + xBreakExtra + xExtra < 0)
xBreakExtra = -(rfo.fxBreak() + xExtra);
ptqBreakExtra = rfo.pteUnitBase();
ptqBreakExtra *= (LONG) xBreakExtra;
hgBreak = rfo.hgBreak();
}
// Prepare for a pdxOut.
EFLOAT efDtoW = rfo.efDtoWBase();
// Make some local pointers.
EGLYPHPOS *pg = pgpos;
GLYPHDATA *pgd;
WCHAR *pwsz = pwszOrg;
// Set the first character position.
pg->ptl.x = xRef;
pg->ptl.y = yRef;
// Set up for character loop.
FIX xA,xB;
FIX xLeft,xRight; // Accumulate bounds here.
UINT ii;
EPOINTQF ptq; // Record the current position here.
ptq.y = ptq.x = (LONGLONG) LONG_MAX + 1;
xLeft = 0; // Start with an empty TextBox.
xRight = 0;
xA = 0; // Distance along the escapement (x) in device coords.
BOOL bAccel;
if (!rfo.bGetGlyphMetricsPlus(cGlyphs,pg, pwsz, &bAccel,&dco,this))
return;
if (bAccel)
{
flTO |= TO_ALL_PTRS_VALID;
pgp = pgpos;
}
ASSERTGDI(!rfo.bSmallMetrics(),"ESTROBJ__vCharPos_G2: Small Metrics in cache\n");
BOOL bZeroBearing = ( rfo.flRealizedType() & SO_ZERO_BEARINGS ) && !bLinkedGlyphs();
ii = cGlyphs;
while (TRUE)
{
// Update the bounds.
if( bZeroBearing )
{
pgd = pg->pgd();
if (xA < xLeft)
xLeft = xA;
xB = xA + pgd->fxD;
if (xB > xRight)
xRight = xB;
}
else
{
pgd = pg->pgd();
xB = xA + pgd->fxA;
if (xB < xLeft)
xLeft = xB;
xB = xA + pgd->fxAB;
if (xB > xRight)
xRight = xB;
}
// Move to the next position.
xA += pgd->fxD;
ptq += pgd->ptqD;
if ( (xExtra) && (xExtra + pgd->fxD > 0) )
{
xA += xExtra;
ptq += ptqExtra;
}
if (xBreakExtra && (pg->hg == hgBreak))
{
xA += xBreakExtra;
ptq += ptqBreakExtra;
}
// Handle a pdxOut.
if (pdxOut != (LONG *) NULL)
*pdxOut++ = lCvt(efDtoW,xA);
if (--ii == 0)
break;
// Save the next position.
pg++;
pg->ptl.x = xRef + (LONG) (ptq.x >> 32);
pg->ptl.y = yRef + (LONG) (ptq.y >> 32);
}
fxExtent = xA;
ptfxUpdate.x = (LONG) (ptq.x >> 32);
ptfxUpdate.y = (LONG) (ptq.y >> 32);
// Expand the text box out to the concatenation point. This allows us to
// continue on with another opaqued string with no visible gap in the
// opaquing.
if (xA > xRight)
xRight = xA;
else if (xA < xLeft)
xLeft = xA; // possible if lExtra < 0
rcfx.xLeft = xLeft;
rcfx.xRight = xRight;
rcfx.yTop = rfo.fxMaxAscent();
rcfx.yBottom = rfo.fxMaxDescent();
flTO |= TO_VALID;
}
/******************************Public*Routine******************************\
* ESTROBJ::vCharPos_G1 (rfo,xRef,yRef,pdx,pdxOut)
*
* Computes character positions in the case where a pdx array is provided
* and escapement equals orientation.
*
* The following fields of the ESTROBJ are filled in:
*
* 1) Each x,y position.
* 2) Bounding coordinates in rcfx.
* 3) The ptfxUpdate vector.
*
* History:
* Sun 22-Mar-1992 18:48:19 -by- Charles Whitmer [chuckwh]
* Wrote it.
\**************************************************************************/
VOID ESTROBJ::vCharPos_G1
(
XDCOBJ& dco,
RFONTOBJ& rfo,
FIX xRef,
FIX yRef,
LONG *pdx,
LONG *pdxOut
)
{
ASSERTGDI(!rfo.bSmallMetrics(),"ESTROBJ__vCharPos_G1: Small Metrics in cache\n");
// Our X scale is measured along the escapement direction. We cache a
// local copy of the unit escapement vector.
EFLOAT efScaleX = rfo.efWtoDBase();
POINTFL pteEsc = rfo.pteUnitBase();
// Make some local pointers.
EGLYPHPOS *pg = pgpos;
GLYPHDATA *pgd;
WCHAR *pwsz = pwszOrg;
// Set the first character position.
pg->ptl.x = xRef;
pg->ptl.y = yRef;
// Set up for character loop.
FIX xA,xB;
LONG xSum;
FIX xLeft,xRight; // Accumulate bounds here.
UINT ii;
xLeft = 0; // Start with an empty TextBox.
xRight = 0;
xA = 0; // Distance along the escapement (x) in device coords.
// To avoid roundoff errors, we accumulate the DX widths in logical
// coordinates and transform the sums.
xSum = 0;
BOOL bAccel;
if (!rfo.bGetGlyphMetricsPlus(cGlyphs, pg, pwsz, &bAccel,&dco,this))
return;
if (bAccel)
{
flTO |= TO_ALL_PTRS_VALID;
pgp = pgpos;
}
BOOL bZeroBearing = (rfo.flRealizedType() & SO_ZERO_BEARINGS) && !bLinkedGlyphs();
ii = cGlyphs;
while (TRUE)
{
// Update the bounds.
if( bZeroBearing )
{
pgd = pg->pgd();
if (xA < xLeft)
xLeft = xA;
xB = xA + pgd->fxD;
if (xB > xRight)
xRight = xB;
}
else
{
pgd = pg->pgd();
xB = xA + pgd->fxA;
if (xB < xLeft)
xLeft = xB;
xB = xA + pgd->fxAB;
if (xB > xRight)
xRight = xB;
}
// Add the next offset.
xSum += *pdx++;
if (pdxOut != (LONG *) NULL)
*pdxOut++ = xSum;
// Scale the new offset.
xA = lCvt(efScaleX,xSum);
if (--ii == 0)
break;
// Save the next position.
pg++;
pg->ptl.x = xRef + lCvt(pteEsc.x,xA);
pg->ptl.y = yRef + lCvt(pteEsc.y,xA);
}
// Expand the text box out to the concatenation point. This allows us to
// continue on with another opaqued string with no visible gap in the
// opaquing.
if (xA > xRight)
xRight = xA;
ptfxUpdate.x = lCvt(pteEsc.x,xA);
ptfxUpdate.y = lCvt(pteEsc.y,xA);
rcfx.xLeft = xLeft;
rcfx.xRight = xRight;
rcfx.yTop = rfo.fxMaxAscent();
rcfx.yBottom = rfo.fxMaxDescent();
flTO |= TO_VALID;
}
/******************************Public*Routine******************************\
* ESTROBJ::vCharPos_H1 (rfo,xRef,yRef,pdx,efScale)
*
* Computes character positions in the simple horizontal case when a pdx
* array is provided. We use the ABC spacing info for the TextBox.
*
* The following fields of the ESTROBJ are filled in:
*
* 1) Each x,y position.
* 2) Bounding coordinates in rcfx.
* 3) The ptfxUpdate vector.
* 4) The flAccel flags.
*
* History:
* Sun 22-Mar-1992 18:48:19 -by- Charles Whitmer [chuckwh]
* Wrote it.
\**************************************************************************/
VOID ESTROBJ::vCharPos_H1
(
XDCOBJ& dco,
RFONTOBJ& rfo,
FIX xRef,
FIX yRef,
LONG *pdx,
EFLOAT efScale
)
{
// Make some local pointers.
EGLYPHPOS *pg = pgpos;
GLYPHDATA *pgd;
WCHAR *pwsz = pwszOrg;
// Compute device driver accelerator flags.
flAccel |= SO_HORIZONTAL
| (rfo.flRealizedType() & SO_MAXEXT_EQUAL_BM_SIDE);
// The transform is pretty simple, we're going to handle it ourselves.
// Accelerate on a really simple transform.
BOOL bUnity = efScale.bIs16();
BOOL bAccel;
if (!rfo.bGetGlyphMetricsPlus(cGlyphs, pg, pwsz, &bAccel,&dco,this))
return;
if (bAccel)
{
flTO |= TO_ALL_PTRS_VALID;
pgp = pgpos;
}
// Set up for character loop.
FIX xA,xB;
LONG xSum;
FIX xLeft,xRight; // Accumulate bounds here.
UINT ii;
xLeft = 0; // Start with an empty TextBox.
xRight = 0;
// To avoid roundoff errors, we accumulate the DX widths in logical
// coordinates and transform the sums.
xSum = 0; // Distance along the escapement in logical coords.
xA = 0; // Distance along the escapement (x) in device coords.
// Set the first character position:
yRef = FXTOL(yRef + 8); // Convert y to LONG coordinate
xRef += 8; // Add in rounding offset for later
// converting x to LONG coordinate
pg->ptl.x = FXTOL(xRef);
pg->ptl.y = yRef;
if((rfo.flRealizedType() & SO_ZERO_BEARINGS) && !bLinkedGlyphs())
{
ii = cGlyphs;
while (TRUE)
{
// Update the bounds.
pgd = pg->pgd();
if (xA < xLeft)
xLeft = xA;
xB = xA + pgd->fxD;
if (xB > xRight)
xRight = xB;
// Add the next offset.
xSum += *pdx++;
// Scale the new offset.
xA = bUnity ? LTOFX(xSum) : lCvt(efScale,xSum);
if (--ii == 0)
break;
// Save the next position.
pg++;
pg->ptl.y = yRef;
pg->ptl.x = FXTOL(xA + xRef);
}
}
else
{
ii = cGlyphs;
while (TRUE)
{
// Update the bounds.
pgd = pg->pgd();
xB = xA + pgd->fxA;
if (xB < xLeft)
xLeft = xB;
xB = xA + pgd->fxAB;
if (xB > xRight)
xRight = xB;
// Add the next offset.
xSum += *pdx++;
// Scale the new offset.
xA = bUnity ? LTOFX(xSum) : lCvt(efScale,xSum);
if (--ii == 0)
break;
// Save the next position.
pg++;
pg->ptl.y = yRef;
pg->ptl.x = FXTOL(xA + xRef);
}
}
// Expand the text box out to the concatenation point. This allows us to
// continue on with another opaqued string with no visible gap in the
// opaquing.
if (xA > xRight)
xRight = xA;
ptfxUpdate.x = xA;
ptfxUpdate.y = 0;
rcfx.xLeft = xLeft;
rcfx.xRight = xRight;
if (dco.pdc->bYisUp())
{
rcfx.yTop = -rfo.fxMaxDescent();
rcfx.yBottom = -rfo.fxMaxAscent();
}
else
{
rcfx.yTop = rfo.fxMaxAscent();
rcfx.yBottom = rfo.fxMaxDescent();
}
flTO |= TO_VALID;
}
/******************************Public*Routine******************************\
* ESTROBJ::vCharPos_H2()
*
* Computes character positions in the simple horizontal case when
* characters have constant integer width.
*
* The following fields of the ESTROBJ are filled in:
*
* 1) Each x,y position.
* 2) Bounding coordinates in rcfx.
* 3) The ptfxUpdate vector.
* 4) The flAccel flags.
* 5) The fxExtent.
*
* History:
* Sun 22-Mar-1992 18:48:19 -by- Charles Whitmer [chuckwh]
* Wrote it.
\**************************************************************************/
VOID ESTROBJ::vCharPos_H2
(
XDCOBJ& dco,
RFONTOBJ& rfo,
FIX xRef,
FIX yRef
,EFLOAT efScale
)
{
// In this case we will also assume that the A and C spaces are
// constants.
//
// NOTE: this is actually wrong assumption for tt fixed pitch fonts
// however, we will set ZERO_BEARINGS flag when we want to lie
// about this to the engine. [bodind]
LONG dx;
// Make some local pointers.
EGLYPHPOS *pg = pgpos;
GLYPHDATA *pgd;
WCHAR *pwsz = pwszOrg;
// Set the first character position. This is the only y position
// we'll fill in.
pg->ptl.x = FXTOL(xRef + 8);
pg->ptl.y = FXTOL(yRef + 8);
// Compute device driver accelerator flags.
flAccel |= SO_HORIZONTAL | (rfo.flRealizedType() & SO_ACCEL_FLAGS);
// Set the width accelerator. When the device driver sees ulCharInc
// non-zero, it must not expect any more x coordinates.
ulCharInc = rfo.lCharInc();
dx = LTOFX(cGlyphs * ulCharInc);
fxExtent = dx;
BOOL bAccel;
if (!rfo.bGetGlyphMetricsPlus(cGlyphs, pg, pwsz, &bAccel,&dco,this))
return;
#ifdef FE_SB
// if there are linked glyphs we can't count on this optimization
if(bLinkedGlyphs())
{
ASSERTGDI((dco.pdc->lTextExtra()|dco.pdc->lBreakExtra()) == 0,
"vCharPos_H3 lTextExtra or lBreakExtra non zero\n");
vCharPos_H3(dco,rfo,xRef,yRef,0,0,dco.pdc->cBreak(),efScale,&bAccel);
return;
}
#endif
if (bAccel)
{
flTO |= TO_ALL_PTRS_VALID;
pgp = pgpos;
}
pgd = pg->pgd();
if (flAccel & SO_ZERO_BEARINGS)
{
rcfx.xLeft = 0;
rcfx.xRight = dx;
}
else
{
//
// Old Comment from bodind might point out possible work item:
// these lines of code rely on the const a,c spaces
// assumption which is only true for simulated italic
// fonts [bodind]
rcfx.xLeft = pgd->fxA;
rcfx.xRight = dx - LTOFX(ulCharInc) + pgd->fxAB;
}
if (dco.pdc->bYisUp())
{
rcfx.yTop = -rfo.fxMaxDescent();
rcfx.yBottom = -rfo.fxMaxAscent();
}
else
{
rcfx.yTop = rfo.fxMaxAscent();
rcfx.yBottom = rfo.fxMaxDescent();
}
ptfxUpdate.x = dx;
ptfxUpdate.y = 0;
flTO |= TO_VALID;
}
/******************************Public*Routine******************************\
* ESTROBJ::vCharPos_H3 (rfo,xRef,yRef,lExtra,lBreakExtra,cBreak,efScale)
*
* Computes character positions in the simple horizontal case when no pdx
* array is provided and the character widths are not constant.
*
* The following fields of the ESTROBJ are filled in:
*
* 1) Each x,y position.
* 2) Bounding coordinates in rcfx.
* 3) The ptfxUpdate vector.
* 4) The flAccel flags.
* 5) The fxExtent.
*
* History:
* Sun 22-Mar-1992 18:48:19 -by- Charles Whitmer [chuckwh]
* Wrote it.
\**************************************************************************/
VOID ESTROBJ::vCharPos_H3
(
XDCOBJ& dco,
RFONTOBJ& rfo,
FIX xRef,
FIX yRef,
LONG lExtra,
LONG lBreakExtra,
LONG cBreak,
EFLOAT efScale,
PBOOL pbAccel
)
{
// Calculate the lExtra and lBreakExtra spacing.
HGLYPH hgBreak = 0;
if ((lExtra | lBreakExtra) == 0)
{
// Will use only character increments given to us with the font
flAccel |= SO_HORIZONTAL | (rfo.flRealizedType() & SO_ACCEL_FLAGS);
}
else
{
flAccel |= SO_HORIZONTAL
| (rfo.flRealizedType() & SO_MAXEXT_EQUAL_BM_SIDE);
if (lExtra)
{
xExtra = lCvt(efScale,lExtra);
if (xExtra > 0)
flAccel |= SO_CHARACTER_EXTRA;
}
if (lBreakExtra && cBreak)
{
xBreakExtra = lCvt(efScale,lBreakExtra) / cBreak;
// Windows won't let us back up over a break.
if (rfo.fxBreak() + xBreakExtra + xExtra < 0)
xBreakExtra = -(rfo.fxBreak() + xExtra);
hgBreak = rfo.hgBreak();
flAccel |= SO_BREAK_EXTRA;
}
}
// Make some local pointers.
EGLYPHPOS *pg = pgpos;
GLYPHDATA *pgd;
WCHAR *pwsz = pwszOrg;
// Set up for FIX to LONG conversion.
xRef += 8;
yRef = FXTOL(yRef + 8);
// Set the first character position.
pg->ptl.x = FXTOL(xRef);
pg->ptl.y = yRef;
// Set up for character loop.
FIX xA,xB;
FIX xLeft,xRight; // Accumulate bounds here.
UINT ii;
xLeft = 0; // Start with an empty TextBox.
xRight = 0;
xA = 0; // Distance along the escapement (x) in device coords.
BOOL bAccel;
// If pbAccel is NULL it means we have encountered the case where we were
// going to do a fixed pitch optimization of the base font but encountered
// linked characters which weren't fixed pitch. In this case we have
// already called bGetGlyphMetricsPlus and will signal this by passing
// in a pointer to bAccel
if( pbAccel == (BOOL*) NULL )
{
if (!rfo.bGetGlyphMetricsPlus(cGlyphs, pg, pwsz, &bAccel, &dco, this))
return;
}
else
{
bAccel = *pbAccel;
}
if (bAccel)
{
flTO |= TO_ALL_PTRS_VALID;
pgp = pgpos;
}
// if there are linked glyphs then SO_ZERO_BEARINGS and SO_CHAR_INC_EQUAL_BM_BASE
// will be turned off in the ESTROBJ::bPartitionInit
if (((flAccel & (SO_ZERO_BEARINGS | SO_CHAR_INC_EQUAL_BM_BASE))
== (SO_ZERO_BEARINGS | SO_CHAR_INC_EQUAL_BM_BASE)) &&
(xExtra >= 0) &&
(xBreakExtra == 0))
{
// This handles the case when the glyphs won't overlap, thus simplifying
// the computation of the bounding box.
ii = cGlyphs;
while (TRUE)
{
pgd = pg->pgd();
// Move to the next position.
xA += pgd->fxD + xExtra;
if (--ii == 0)
break;
// Save the next position.
pg++;
pg->ptl.x = FXTOL(xA + xRef);
pg->ptl.y = yRef;
}
// Expand the text box out to the concatenation point. This allows us to
// continue on with another opaqued string with no visible gap in the
// opaquing.
xLeft = 0;
xRight = xA;
}
else
{
// This handles the general case.
ii = cGlyphs;
while (TRUE)
{
// Update the bounds.
pgd = pg->pgd();
xB = xA + pgd->fxA;
if (xB < xLeft)
xLeft = xB;
xB = xA + pgd->fxAB;
if (xB > xRight)
xRight = xB;
// Move to the next position.
xA += pgd->fxD;
// don't let xExtra backup past the origin of the previous glyph
if( ( xExtra ) && (pgd->fxD + xExtra > 0) )
{
xA += xExtra;
}
if (pg->hg == hgBreak)
xA += xBreakExtra;
if (--ii == 0)
break;
// Save the next position.
pg++;
pg->ptl.x = FXTOL(xA + xRef);
pg->ptl.y = yRef;
}
// Expand the text box out to the concatenation point. This allows us to
// continue on with another opaqued string with no visible gap in the
// opaquing.
if (xA > xRight)
xRight = xA;
}
fxExtent = xA;
ptfxUpdate.x = xA;
ptfxUpdate.y = 0;
rcfx.xLeft = xLeft;
rcfx.xRight = xRight;
if (dco.pdc->bYisUp())
{
rcfx.yTop = -rfo.fxMaxDescent();
rcfx.yBottom = -rfo.fxMaxAscent();
}
else
{
rcfx.yTop = rfo.fxMaxAscent();
rcfx.yBottom = rfo.fxMaxDescent();
}
flTO |= TO_VALID;
}
/******************************Public*Routine******************************\
* ESTROBJ::bOpaqueArea (pptfx,prcl)
*
* Computes the opaquing area for the text.
*
* In the complex case, i.e. non-horizontal case, the TextBox is written as
* a parallelogram into pptfx. A bounding rectangle is computed in prcl.
* TRUE is returned.
*
* In the simple case, the opaque area is also the bounds and is returned
* only in prcl. FALSE is returned.
*
* History:
* Fri 13-Mar-1992 19:52:00 -by- Charles Whitmer [chuckwh]
* Wrote it.
\**************************************************************************/
BOOL ESTROBJ::bOpaqueArea(POINTFIX *pptfx,RECTL *prcl)
{
// Handle the simplest case.
if (flAccel & SO_HORIZONTAL)
{
// The opaque area is a simple rectangle. We do the rounding in a
// particular order so that if the whole string is displaced by a
// fraction of a pel in any direction, the TextBox will not change size,
// and will move exactly with the characters.
LONG x = FXTOL(ptfxRef.x + 8);
prcl->left = x + FXTOL(rcfx.xLeft);
prcl->right = x + FXTOLCEILING(rcfx.xRight);
// Making the background rectangle compatible with WFW
//
// The problem surfaced with Dbase for Windows where one found
// that a console window would not be repainted correctly. The
// problem was that the Borland coders relied on the fact that WFW
// vga drivers make the background rectangle for text equal to the
// rectangle returned in GetTextExtent(). Using this they would
// erase blank lines by printing lines with a lot of blanks. Of
// course this is an extremely bad thing to do but it got the
// job done, all be it slowly. Now the interesting part: Under
// WFW the background rectangle for emboldend (via simulation)
// text has a horizontal extend equal to one greater than the sum
// of character widths. NT is compatible with this. However,
// under NT 3.5 Gdi calculated the horizontal extent of the
// background rectangle equal to the sum of the character widths
// (at least for simple fonts e.g. MS Sans Serif) because this
// was guaranteed to cover all of the bits of the glyphy. Thus,
// for the case of emboldened text, NT had BackGround.x =
// GetTextExtent.x - 1 where WFW had BackGround.x =
// GetTextExtent.x. So if the text is simulated bold we bump the
// horizontal extent of the background rectangle by 1.
if (
(prfo->prfnt->fobj.flFontType & FO_SIM_BOLD) &&
(prfo->prfnt->flInfo & (FM_INFO_TECH_BITMAP | FM_INFO_TECH_STROKE))
)
{
prcl->right++;
// The line of code that follows this comment is a
// hack to compensate for a bug in many of the video
// drivers shipped with 3.5. This started with the
// S3 driver which is the prototype of all of the NT
// video driver shipped from Microsoft. The S3
// driver has the following line of code in textout.c:
//
// bTextPerfectFit = (pstro->flAccel &
// (SO_ZERO_BEARINGS | SO_FLAG_DEFAULT_PLACEMENT |
// SO_MAXEXT_EQUAL_BM_SIDE |
// SO_CHAR_INC_EQUAL_BM_BASE)) == (SO_ZERO_BEARINGS |
// SO_FLAG_DEFAULT_PLACEMENT |
// SO_MAXEXT_EQUAL_BM_SIDE |
// SO_CHAR_INC_EQUAL_BM_BASE);
//
// it says that if flAccel sets all of the following
// bits {SO_ZERO_BEARINGS, SO_FLAG_DEFAULT_PLACEMENT,
// SO_MAXEXT_EQUAL_BM_SIDE, SO_CHAR_INC_EQUAL_BM_BASE}
// then the S3 driver will assume that the text
// passed down from the driver is "perfect". The S3
// driver assumes that if a STROBJ is perfect, then
// it just has to lay down the text. This conclusion
// applys to the background rectangle as well. That
// is, the S3 driver assume that if this magic set of
// bits is set, then it can safely ignore the
// background rectangle safe in the knowlege that it
// will be taken care of in the process of laying
// down the glyphs. Unfortunately, this assumption of
// perfection is not correct now that I have bumped
// up the background rectangle by one. What is a
// coder to do? Well I have decided to make the text
// not so perfect by erasing one of the bits. This
// will have a couple of result. First it correct the
// bug as demonstrated by Dbase for Windows. Second
// it will slow down the case of emboldened text.
// How important is this? I don't know. We usually
// base our performance decisions on WinBench tests.
// If this hack has a noticeable impact then we may
// have to rethink this strategy.
//
// Ref. Bug No. 25102 "T1 DBaseWin X86 Command ..."
//
// Wed 07-Dec-1994 08:25:21 by Kirk Olynyk [kirko]
flAccel &= ~SO_ZERO_BEARINGS;
}
LONG y = FXTOL(ptfxRef.y + 8);
prcl->top = y - FXTOLCEILING(rcfx.yTop);
prcl->bottom = y - FXTOL(rcfx.yBottom);
return(FALSE);
}
// An inverting transform or an escapement could get us here.
EPOINTFL *ppteBase = &prfo->pteUnitBase();
EPOINTFL *ppteAscent = &prfo->pteUnitAscent();
if (ppteBase->y.bIsZero() && ppteAscent->x.bIsZero())
{
LONG x = FXTOL(ptfxRef.x + 8);
if (ppteBase->x.bIsNegative())
{
prcl->left = x - FXTOLCEILING(rcfx.xRight);
prcl->right = x - FXTOL(rcfx.xLeft);
}
else
{
prcl->left = x + FXTOL(rcfx.xLeft);
prcl->right = x + FXTOLCEILING(rcfx.xRight);
}
LONG y = FXTOL(ptfxRef.y + 8);
if (ppteAscent->y.bIsNegative())
{
prcl->top = y - FXTOLCEILING(rcfx.yTop);
prcl->bottom = y - FXTOL(rcfx.yBottom);
}
else
{
prcl->top = y + FXTOL(rcfx.yBottom);
prcl->bottom = y + FXTOLCEILING(rcfx.yTop);
}
// fudge an extra pixel, do not have a better solution for a space
// character at the beginning or at the end of string written horizontally
// It should not be necessary to do any of
//
// prcl->top--;
// prcl->bottom++;
//
// because space character is positioned at the baseline, so it should not
// stick out at the top or at the bottom
//
// Also it should not be neccessary to do
//
// prcl->left--;
//
// because we always ADD a cx = 1 for the space character to the
// origin of the space character
prcl->right++;
return(FALSE);
}
// A 90 degree rotation could get us here.
if (ppteBase->x.bIsZero() && ppteAscent->y.bIsZero())
{
LONG x = FXTOL(ptfxRef.x + 8);
if (ppteAscent->x.bIsNegative())
{
prcl->left = x - FXTOLCEILING(rcfx.yTop);
prcl->right = x - FXTOL(rcfx.yBottom);
}
else
{
prcl->left = x + FXTOL(rcfx.yBottom);
prcl->right = x + FXTOLCEILING(rcfx.yTop);
}
LONG y = FXTOL(ptfxRef.y + 8);
if (ppteBase->y.bIsNegative())
{
prcl->top = y - FXTOLCEILING(rcfx.xRight);
prcl->bottom = y - FXTOL(rcfx.xLeft);
}
else
{
prcl->top = y + FXTOL(rcfx.xLeft);
prcl->bottom = y + FXTOLCEILING(rcfx.xRight);
}
// fudge an extra pixel, do not have a better solution for a space
// character at the beginning or at the end of string written vertically.
// It should not be necessary to do any of
//
// prcl->right++;
// prcl->left--;
//
// because space character is positioned at the baseline, so it should not
// stick out left or right.
//
// Also it should not be neccessary to do
//
// prcl->top--;
//
// because we always ADD a cy = 1 for the space character to the
// origin of the space character
prcl->bottom++;
return(FALSE);
}
// The opaque area is a parallelogram. We multiply the orientation and
// ascent unit vectors by the computed bounding widths to get the
// displacements from the reference point.
POINTFIX ptfxLeft,ptfxRight,ptfxTop,ptfxBottom;
ptfxLeft.x = lCvt(ppteBase->x,rcfx.xLeft);
ptfxLeft.y = lCvt(ppteBase->y,rcfx.xLeft);
ptfxRight.x = lCvt(ppteBase->x,rcfx.xRight);
ptfxRight.y = lCvt(ppteBase->y,rcfx.xRight);
ptfxTop.x = lCvt(ppteAscent->x,rcfx.yTop);
ptfxTop.y = lCvt(ppteAscent->y,rcfx.yTop);
ptfxBottom.x = lCvt(ppteAscent->x,rcfx.yBottom);
ptfxBottom.y = lCvt(ppteAscent->y,rcfx.yBottom);
pptfx[0].x = ptfxRef.x + ptfxLeft.x + ptfxTop.x;
pptfx[1].x = ptfxRef.x + ptfxRight.x + ptfxTop.x;
pptfx[2].x = ptfxRef.x + ptfxRight.x + ptfxBottom.x;
pptfx[3].x = ptfxRef.x + ptfxLeft.x + ptfxBottom.x;
pptfx[0].y = ptfxRef.y + ptfxLeft.y + ptfxTop.y;
pptfx[1].y = ptfxRef.y + ptfxRight.y + ptfxTop.y;
pptfx[2].y = ptfxRef.y + ptfxRight.y + ptfxBottom.y;
pptfx[3].y = ptfxRef.y + ptfxLeft.y + ptfxBottom.y;
// Bound the parallelogram. (Using Black Magic.)
int ii;
ii = (pptfx[1].x > pptfx[0].x)
== (pptfx[1].x > pptfx[2].x);
prcl->left = pptfx[ii].x;
prcl->right = pptfx[ii+2].x;
ii = (pptfx[1].y > pptfx[0].y)
== (pptfx[1].y > pptfx[2].y);
prcl->top = pptfx[ii].y;
prcl->bottom = pptfx[ii+2].y;
((ERECTL *) prcl)->vOrder();
prcl->left = FXTOL(prcl->left) - 2; // to be safe, 1 not enough [bodind]
prcl->top = FXTOL(prcl->top) - 2; // to be safe, 1 not enough [bodind]
prcl->right = FXTOLCEILING(prcl->right) + 2; // to be safe, 1 not enough [bodind]
prcl->bottom = FXTOLCEILING(prcl->bottom) + 2; // to be safe, 1 not enough [bodind]
#if 0
vCorrectBackGround();
#endif
return(TRUE);
}
/******************************Public*Routine******************************\
* ESTROBJ::bTextExtent (psize)
*
* Simply transforms the TextBox extents back to logical coordinates.
*
* History:
* Wed 31-Mar-1993 03:15:04 -by- Charles Whitmer [chuckwh]
* Removed the overhang hack since the RFONTOBJ version of this function is
* the Windows compatible one. Made it return an advance width based
* extent when (escapement==orientation). Otherwise, the bounding box is
* the only sensible definition.
*
* Thu 24-Sep-1992 18:36:14 -by- Charles Whitmer [chuckwh]
* Added the compatibility hack.
*
* Sat 14-Mar-1992 22:08:21 -by- Charles Whitmer [chuckwh]
* Wrote it.
\**************************************************************************/
BOOL ESTROBJ::bTextExtent(RFONTOBJ& rfo,LONG lEsc,PSIZE pSize)
{
if (flTO & TO_ESC_NOT_ORIENT)
{
pSize->cx = lCvt(prfo->efDtoWBase(),rcfx.xRight-rcfx.xLeft);
pSize->cy = lCvt(prfo->efDtoWAscent(),rcfx.yTop-rcfx.yBottom);
}
else
{
// Computes a more Windows compatible extent. This is only possible
// when (escapement==orientation) and no pdx vector was provided.
// The field fxExtent is only defined in these cases! We neglect adding
// in the overhang, since only GetTextExtentEx, a Win32 function, can
// get here.
pSize->cx = lCvt(prfo->efDtoWBase(),fxExtent);
pSize->cy = lCvt(prfo->efDtoWAscent(),prfo->lMaxHeight() << 4);
}
#ifdef FE_SB
if( gbDBCSCodePage && // Only in DBCS system locale
(rfo.iGraphicsMode() == GM_COMPATIBLE) && // We are in COMPATIBLE mode
!(rfo.flInfo() & FM_INFO_ARB_XFORMS) && // Driver can't do arbitrary rotations
!(rfo.flInfo() & FM_INFO_TECH_STROKE) && // Not a vector driver
(rfo.flInfo() & FM_INFO_90DEGREE_ROTATIONS) && // Driver does 90 deg. rotations
(lEsc == 900L || lEsc == 2700L) // Current font Escapement is 900 or 2700
)
{
LONG lSwap = pSize->cx;
pSize->cx = pSize->cy;
pSize->cy = lSwap;
}
#endif FE_SB
return(TRUE);
}
/******************************Public*Routine******************************\
* bTextToPath (po)
*
* Draws the outlines of all the glyphs in the string into the given path.
*
* History:
* Wed 17-Jun-1992 15:43:22 -by- Charles Whitmer [chuckwh]
* Wrote it.
\**************************************************************************/
BOOL ESTROBJ::bTextToPath(EPATHOBJ& po, XDCOBJ& dco, BOOL bNeedUnflattened)
{
BOOL ReturnValue;
if (bLinkedGlyphs())
{
ReturnValue = bLinkedTextToPath(po, dco, bNeedUnflattened);
}
else
{
ReturnValue = bTextToPathWorkhorse(po, bNeedUnflattened);
}
return(ReturnValue);
}
BOOL ESTROBJ::bTextToPathWorkhorse(EPATHOBJ& po, BOOL bNeedUnflattened)
{
ULONG ii, iFound;
ULONG cLeft, cGot;
POINTFIX ptfxOffset;
EGLYPHPOS *pg;
FIX dx;
BOOL bMore;
cLeft = 0;
vEnumStart();
do
{
bMore = STROBJ_bEnum(this, &iFound, (GLYPHPOS**)&pg);
if (iFound == 0 || pg == 0)
{
break;
}
if (ulCharInc)
{
ASSERTGDI(flAccel & SO_HORIZONTAL, "bTextToPath,text not horizontal\n");
ptfxOffset.x = pg->ptl.x;
ptfxOffset.y = pg->ptl.y;
if (!(flTO & TO_HIGHRESTEXT))
{
ptfxOffset.x <<=4;
ptfxOffset.y <<=4;
}
dx = (FIX)(ulCharInc << 4);
ptfxOffset.x -= dx;
}
else
{
dx = 0;
}
for (cGot = cLeft = iFound; cLeft; cLeft -= cGot)
{
//
// If the paths in the cache have been flattened we will need to
// call directly to the driver to get unflatttened paths [gerritv].
//
if ( bNeedUnflattened )
{
if (!prfo->bInsertPathLookaside(pg,FALSE))
{
break;
}
cGot = 1;
}
else if (!(flTO & TO_ALL_PTRS_VALID))
{
if ((cGot = prfo->cGetGlyphData(cLeft,pg)) == 0)
{
break;
}
}
for (ii = 0; ii < cGot; ii++, pg++)
{
if (dx)
{
ptfxOffset.x += dx;
}
else
{
ptfxOffset.x = pg->ptl.x;
ptfxOffset.y = pg->ptl.y;
if (!(flTO & TO_HIGHRESTEXT))
{
ptfxOffset.x <<=4;
ptfxOffset.y <<=4;
}
}
if (!po.bAppend((EPATHOBJ *) pg->pgdf->ppo, &ptfxOffset))
{
if (bNeedUnflattened)
pg->pgdf = NULL;
break;
}
if (bNeedUnflattened)
pg->pgdf = NULL;
}
if (ii < cGot)
{
break;
}
}
if (cLeft)
{
break;
}
} while (bMore);
if (bNeedUnflattened)
{
flTO = (flTO & ~TO_ALL_PTRS_VALID);
}
return(cLeft == 0);
}
/******************************Public*Routine******************************\
*
* Routine Name:
*
* ESTROBJ::bLinkedTextToPath
*
* Routine Description:
*
* Renders a text in the form of a path for the case where font linking
* is in place.
*
* Arguments:
*
* po - a referece to an EPATHOBJ. This will receive the text path.
*
* bNeedUnflattened - a variable that indicates that whether the path
* should be unflattened. If it needs to be unflattened we must
* go back to the font driver to get the glyph path in its original
* form before being flattend.
*
* Called by:
*
* ESTROBJ::bTextToPath
*
* Return Value:
*
\**************************************************************************/
BOOL ESTROBJ::bLinkedTextToPath(EPATHOBJ& po, XDCOBJ& dco, BOOL bNeedUnflattened)
{
RFONTOBJ *prfoSave;
WCHAR *pwszSave, *pwcTmp, *pwcSource;
LONG *plPart, *plPartLast, lFontLast, lFont, lInflatedMax = 0;
ULONG count;
prfoSave = prfo;
pgp = 0; // forces enumeration
flAccel = 0;
ulCharInc = 0;
pwszSave = pwszOrg;
plPartLast = plPartition + cGlyphs; // useful
lFontLast = EUDCTYPE_FACENAME + (LONG) prfo->uiNumFaceNameLinks();
for (lFont = EUDCTYPE_BASEFONT; lFont < lFontLast ; lFont++)
{
RFONTTMPOBJ rfoLink;
RFONTOBJ *prfoLink;
// (re)initialize prfo, because it could be pointing to an old
// or invalid rfoLink from a previous iteration.
prfo = prfoSave;
if (lFont == EUDCTYPE_BASEFONT)
{
prfoLink = prfo;
}
else
{
RFONT *prfnt;
switch (lFont)
{
case EUDCTYPE_SYSTEM_TT_FONT:
if(cTTSysGlyphs == 0)
{
continue;
}
prfnt = prfo->prfntSystemTT();
break;
case EUDCTYPE_SYSTEM_WIDE:
if(cSysGlyphs == 0)
{
continue;
}
prfnt = prfo->prfntSysEUDC();
break;
case EUDCTYPE_DEFAULT:
if(cDefGlyphs == 0)
{
continue;
}
prfnt = prfo->prfntDefEUDC();
break;
default:
if(cFaceNameGlyphsGet(lFont - EUDCTYPE_FACENAME) == 0)
{
continue;
}
prfnt = prfo->prfntFaceName(lFont - EUDCTYPE_FACENAME);
break;
}
if (prfnt == 0)
{
RIP("prfnt == 0\n");
return(FALSE);
}
rfoLink.vInit(prfnt);
prfoLink = (RFONTOBJ*) &rfoLink;
}
plPart = plPartition;
pwcTmp = pwcPartition;
pwcSource = pwszSave;
count = 0;
for ( ; plPart < plPartLast; plPart++, pwcSource++)
{
if (*plPart == lFont)
{
*pwcTmp++ = *pwcSource;
count++;
}
}
if (count)
{
cGlyphs = count;
pwszOrg = pwcPartition;
prfo = prfoLink;
vFontSet(lFont);
if (lFont != EUDCTYPE_BASEFONT)
{
POINTL ptlAdjustBaseLine;
if (bAdjusBaseLine(*prfo, rfoLink, &ptlAdjustBaseLine))
{
ptlBaseLineAdjustSet(ptlAdjustBaseLine);
}
}
if (!bTextToPathWorkhorse(po))
{
pwszOrg = pwszSave;
prfo = prfoSave;
return(FALSE);
}
}
}
pwszOrg = pwszSave;
prfo = prfoSave;
return(TRUE);
}
/******************************Public*Routine******************************\
* bAddPgmToPath (po,x,y,dx1,dy1,dx2,dy2)
*
* Performs the often repeated adding of a parallelogram to a path.
*
* Tue 07-Apr-1992 00:17:57 -by- Charles Whitmer [chuckwh]
* Wrote it.
\**************************************************************************/
BOOL bAddPgmToPath(EPATHOBJ& po,FIX x,FIX y,FIX dx1,FIX dy1,FIX dx2,FIX dy2)
{
POINTFIX aptfx[4];
aptfx[0].x = x;
aptfx[0].y = y;
aptfx[1].x = x + dx1;
aptfx[1].y = y + dy1;
aptfx[2].x = x + dx1 + dx2;
aptfx[2].y = y + dy1 + dy2;
aptfx[3].x = x + dx2;
aptfx[3].y = y + dy2;
return(po.bAddPolygon(XFORMNULL,(POINTL *) aptfx,4));
}
/******************************Public*Routine******************************\
* bExtraRectsToPath (po)
*
* Draws all the underlines and strikeouts into a path, suitable for
* filling.
*
* History:
* Tue 07-Apr-1992 00:44:00 -by- Charles Whitmer [chuckwh]
* Wrote it.
\**************************************************************************/
BOOL ESTROBJ::bExtraRectsToPath(EPATHOBJ& po, BOOL bNeedUnflattend)
{
FIX x;
FIX y;
// Get local copies of the offsets and thicknesses.
POINTFIX ptfxUL;
POINTFIX ptfxULThick;
ptfxUL.x = LTOFX(prfo->ptlUnderline1().x);
ptfxUL.y = LTOFX(prfo->ptlUnderline1().y);
ptfxULThick.x = LTOFX(prfo->ptlULThickness().x);
ptfxULThick.y = LTOFX(prfo->ptlULThickness().y);
POINTFIX ptfxSO;
POINTFIX ptfxSOThick;
ptfxSO.x = LTOFX(prfo->ptlStrikeOut().x);
ptfxSO.y = LTOFX(prfo->ptlStrikeOut().y);
ptfxSOThick.x = LTOFX(prfo->ptlSOThickness().x);
ptfxSOThick.y = LTOFX(prfo->ptlSOThickness().y);
// Underlines and strikeouts are continuous and parallel the escapement
// vector, if escapement equals orientation.
if (!(flTO & TO_ESC_NOT_ORIENT))
{
// Round off the starting point.
x = (ptfxRef.x + 8) & -16;
y = (ptfxRef.y + 8) & -16;
if (flTO & TSIM_UNDERLINE1)
{
if (!bAddPgmToPath
(
po,
x + ptfxUL.x,
y + ptfxUL.y,
ptfxEscapement.x,
ptfxEscapement.y,
ptfxULThick.x,
ptfxULThick.y
)
)
return(FALSE);
}
if (flTO & TSIM_STRIKEOUT)
{
if (!bAddPgmToPath
(
po,
x + ptfxSO.x,
y + ptfxSO.y,
ptfxEscapement.x,
ptfxEscapement.y,
ptfxSOThick.x,
ptfxSOThick.y
)
)
return(FALSE);
}
return(TRUE);
}
// Otherwise underlines and strikeouts apply to each character.
UINT ii;
ULONG cLeft;
ULONG cGot;
EGLYPHPOS *pg = pgpos;
WCHAR *pwsz = pwszOrg;
for (cGot=cLeft=cGlyphs; cLeft; cLeft-=cGot)
{
if (bNeedUnflattend)
{
if ((cGot = prfo->cGetGlyphDataLookaside(cLeft, pg)) == 0)
return (FALSE);
}
else if (!(flTO & TO_ALL_PTRS_VALID))
{
if ((cGot = prfo->cGetGlyphData(cLeft,pg)) == 0)
return(FALSE);
}
pwsz += cGot;
EPOINTFL *ppteBase = &prfo->pteUnitBase();
POINTFIX ptfxA;
POINTFIX ptfxB;
for (ii=0; ii<cGot; ii++,pg++)
{
x = pg->ptl.x;
y = pg->ptl.y;
// If pg->ptl contains LONG's, we must convert them to FIX's.
if (!(flTO & TO_HIGHRESTEXT))
{
x <<=4;
y <<=4;
}
ptfxA.x = lCvt(ppteBase->x,pg->pgd()->fxA);
ptfxA.y = lCvt(ppteBase->y,pg->pgd()->fxA);
ptfxB.x = lCvt(ppteBase->x,(pg->pgd()->fxAB - pg->pgd()->fxA));
ptfxB.y = lCvt(ppteBase->y,(pg->pgd()->fxAB - pg->pgd()->fxA));
if (flTO & TSIM_UNDERLINE1)
{
if (!bAddPgmToPath
(
po,
x + ptfxUL.x + ptfxA.x,
y + ptfxUL.y + ptfxA.y,
ptfxB.x,
ptfxB.y,
ptfxULThick.x,
ptfxULThick.y
)
)
return(FALSE);
}
if (flTO & TSIM_STRIKEOUT)
{
if (!bAddPgmToPath
(
po,
x + ptfxSO.x + ptfxA.x,
y + ptfxSO.y + ptfxA.y,
ptfxB.x,
ptfxB.y,
ptfxSOThick.x,
ptfxSOThick.y
)
)
return(FALSE);
}
}
}
return(TRUE);
}
/******************************Public*Routine******************************\
* VOID STROBJ_vEnumStart (pstro)
*
* Initialize the string enumerator.
*
* History
* Tue 17-Mar-1992 10:33:09 -by- Charles Whitmer [chuckwh]
* Simplified it.
*
* Fri 25-Jan-1991 -by- Bodin Dresevic [BodinD]
* Wrote it.
\**************************************************************************/
extern "C" VOID STROBJ_vEnumStart(STROBJ *pso)
{
((ESTROBJ *)pso)->vEnumStart();
}
/******************************Public*Routine******************************\
* BOOL STROBJ_bEnum
*
* The glyph enumerator.
*
* History:
* Tue 17-Mar-1992 10:35:05 -by- Charles Whitmer [chuckwh]
* Simplified it and gave it the quick exit. Also let drivers call here
* direct.
*
* 02-Oct-1991 -by- Bodin Dresevic [BodinD]
* Wrote it.
\**************************************************************************/
BOOL STROBJ_bEnumLinked(ESTROBJ *peso, ULONG *pc,PGLYPHPOS *ppgpos);
extern "C" BOOL STROBJ_bEnum(STROBJ *pso, ULONG *pc, PGLYPHPOS *ppgpos)
{
// Quick exit.
#ifdef FE_SB
if(((ESTROBJ*)pso)->flTO & (TO_PARTITION_INIT|TO_SYS_PARTITION))
{
return(STROBJ_bEnumLinked( (ESTROBJ*) pso, pc, ppgpos ));
}
#endif
if (((ESTROBJ*)pso)->flTO & TO_ALL_PTRS_VALID)
{
*pc = ((ESTROBJ*)pso)->cGlyphs;
*ppgpos = ((ESTROBJ*)pso)->pgpos;
return(FALSE);
}
// compute the number of wc's in the string for which the GLYPHPOS strucs
// have not yet been written to the driver's buffer
ULONG cgposYetToCopy = ((ESTROBJ*)pso)->cGlyphs - ((ESTROBJ*)pso)->cgposCopied;
if (cgposYetToCopy == 0)
{
*pc = 0;
return(FALSE);
}
GLYPHPOS *pgp = ((ESTROBJ*)pso)->pgpos + ((ESTROBJ*)pso)->cgposCopied;
ULONG cgposActual; // # of chars enumerated during this call to strobj
if ( ((ESTROBJ*)pso)->prfo == NULL) // check for journaling
{
WARNING("ESTROBJ::bEnum(), bitmap font, prfo == NULL\n");
*pc = 0;
return(FALSE);
}
cgposActual =((ESTROBJ*)pso)->prfo->cGetGlyphData(cgposYetToCopy,pgp);
if (cgposActual == 0)
{
*pc = 0;
return(FALSE);
}
// The first glyph in a batch should always have the position info.
if (((ESTROBJ*)pso)->cgposCopied && ((ESTROBJ*)pso)->ulCharInc)
{
if (((ESTROBJ*)pso)->flTO & TO_HIGHRESTEXT)
{
pgp->ptl.x = ((ESTROBJ*)pso)->pgpos->ptl.x +
((((ESTROBJ*)pso)->cgposCopied * ((ESTROBJ*)pso)->ulCharInc) << 4);
}
else
{
pgp->ptl.x = ((ESTROBJ*)pso)->pgpos->ptl.x +
((ESTROBJ*)pso)->cgposCopied * ((ESTROBJ*)pso)->ulCharInc;
}
pgp->ptl.y = ((ESTROBJ*)pso)->pgpos->ptl.y;
}
((ESTROBJ*)pso)->cgposCopied += cgposActual; // update enumeration state
*pc = cgposActual;
*ppgpos = pgp;
return(((ESTROBJ*)pso)->cgposCopied < ((ESTROBJ*)pso)->cGlyphs); // TRUE => more to come.
}
/******************************Public*Routine******************************\
* vGenWidths
*
* Computes the advance widths for escapement not equal to orientation.
* The theory is pretty simple. We construct an 'egg' whose upper and
* lower outlines are half ellipses with sizes determined by the top and
* bottom of the ink box. The escapement vector always goes through the
* center of the character baseline. We call that point (halfway between
* the normal concatenation points of the glyph) the 'center'. We advance
* the current position from where the escapement vector first pierces the
* egg to the center, and then from the center to where the vector exits
* the egg. These are the D1 and D2 returned widths from this routine.
*
* Mon 07-Jun-1993 12:27:22 -by- Charles Whitmer [chuckwh]
* Wrote it long ago. My apologies for the undocumented math.
\**************************************************************************/
VOID vGenWidths
(
FIX *pfxD1, // First part of advance width. (Returned.)
FIX *pfxD2, // Second part of advance width. (Returned.)
EFLOAT& efRA, // Projection ratio of escapement onto Ascent.
EFLOAT& efRB, // Projection ratio of escapement onto Baseline.
FIX fxWidth, // Normal width.
FIX fxTop, // Top of ink box.
FIX fxBottom, // Bottom of ink box.
FIX fxMaxAscent
)
{
EFLOAT efA,efB,efOne;
FIX fxTemp;
// Worry about the width being zero. This can occur in a Unicode font.
// Never advance in this case, since the character is some kind of
// overpainting glyph.
if (fxWidth == 0)
{
*pfxD2 = 0;
*pfxD1 = 0;
return;
}
// For escapement along the baseline, we return the normal width, separated
// into two parts.
if (efRA.bIsZero())
{
*pfxD1 = fxWidth / 2;
*pfxD2 = fxWidth - *pfxD1;
return;
}
if (fxBottom == fxTop) // Probably a break character.
{
fxBottom = -(fxMaxAscent / 4);
fxTop = fxMaxAscent/2 + fxBottom;
}
if (fxBottom >= 0)
fxBottom = 0;
if (fxTop <= 0)
fxTop = 0;
if (efRA.bIsNegative())
{
fxTemp = fxBottom;
fxBottom = -fxTop;
fxTop = -fxTemp;
}
// Provide a little extra spacing in addition to the ink box.
fxTop += fxMaxAscent / 16;
if ( fxTop == 0 )
{
WARNING1("vGenWidths: fxTop == 0 .. setting to +1\n");
fxTop = 1;
}
fxBottom -= fxMaxAscent / 16;
if ( fxBottom == 0 )
{
WARNING1("vGenWidths: fxBottom == 0 .. setting to -1\n");
fxBottom = -1;
}
// For escapement in the ascent direction, we return a width that will
// traverse the ink box. Note that the general code below would give the
// same result, only take longer!
if (efRB.bIsZero())
{
*pfxD2 = fxTop;
*pfxD1 = -fxBottom;
return;
}
// Compute the displacements.
ASSERTGDI(fxWidth, "fxWidth == 0\n");
efB = fxWidth; // Converts to EFLOAT.
efB.vDivBy2();
efB.eqDiv(efRB,efB);
efB.eqMul(efB,efB);
efA = fxBottom;
efA.eqDiv(efRA,efA);
efA.eqMul(efA,efA);
efA.eqAdd(efA,efB);
efA.eqSqrt(efA);
efOne.vSetToOne();
efA.eqDiv(efOne,efA);
efA.bEfToL(*pfxD1);
efA = fxTop;
efA.eqDiv(efRA,efA);
efA.eqMul(efA,efA);
efA.eqAdd(efA,efB);
efA.eqSqrt(efA);
efA.eqDiv(efOne,efA);
efA.bEfToL(*pfxD2);
return;
}
/******************************Public*Routine******************************\
*
* VOID ESTROBJ::vCharPos_H4, pdxdy case for zero esc and orientation
*
* History:
* 18-Sep-1996 -by- Bodin Dresevic [BodinD]
* Wrote it.
\**************************************************************************/
VOID ESTROBJ::vCharPos_H4
(
XDCOBJ& dco,
RFONTOBJ& rfo,
FIX xRef,
FIX yRef,
LONG *pdxdy,
EFLOAT efXScale,
EFLOAT efYScale
)
{
// Make some local pointers.
EGLYPHPOS *pg = pgpos;
GLYPHDATA *pgd;
WCHAR *pwsz = pwszOrg;
POINTL *pDxDy = (POINTL *)pdxdy;
// The transform is pretty simple, we're going to handle it ourselves.
// Accelerate on a really simple transforms.
BOOL bUnityX = efXScale.bIs16();
BOOL bUnityY = efYScale.bIs16();
BOOL bAccel;
if (!rfo.bGetGlyphMetricsPlus(cGlyphs, pg, pwsz, &bAccel,&dco,this))
return;
if (bAccel)
{
flTO |= TO_ALL_PTRS_VALID;
pgp = pgpos;
}
// Set up for character loop.
FIX xA,xB;
FIX yT,yB;
LONG xSum, ySum;
FIX xLeft,xRight, yTop, yBottom; // Accumulate bounds here.
UINT ii;
FIX fxAscent, fxDescent;
if (dco.pdc->bYisUp())
{
fxAscent = -rfo.fxMaxDescent();
fxDescent = -rfo.fxMaxAscent();
}
else
{
fxAscent = rfo.fxMaxAscent();
fxDescent = rfo.fxMaxDescent();
}
xLeft = 0; // Start with an empty TextBox.
xRight = 0;
yTop = 0;
yBottom = 0;
// To avoid roundoff errors, we accumulate the DX widths in logical
// coordinates and transform the sums.
xSum = 0; // Distance along the escapement in logical coords.
ySum = 0; // Distance along the ascender in logical coords.
xA = 0; // Distance along the escapement (x) in device coords.
yT = 0; // Distance along the ascender (y) in device coords
// Set the first character position:
yRef += 8; // Add in rounding offset for later
xRef += 8; // converting x and y to LONG coordinates
pg->ptl.x = FXTOL(xRef);
pg->ptl.y = FXTOL(yRef);
ii = cGlyphs;
while (TRUE)
{
// Update the bounds.
pgd = pg->pgd();
xB = xA + pgd->fxA;
if (xB < xLeft)
xLeft = xB;
xB = xA + pgd->fxAB;
if (xB > xRight)
xRight = xB;
// make top and bottom independent of glyphs, ie replace glyph in the
// string by another one, want to get the same top and bottom, possibly
// different left and right.
// (this is similar to how all other H? cases work)
yB = yT + fxAscent;
if (yB > yTop)
yTop = yB;
yB = yT + fxDescent;
if (yB < yBottom)
yBottom = yB;
// Add the next offset.
xSum += pDxDy->x;
ySum += pDxDy->y;
pDxDy++;
// Scale the new offset.
xA = bUnityX ? LTOFX(xSum) : lCvt(efXScale,xSum);
yT = bUnityY ? LTOFX(ySum) : lCvt(efYScale,ySum);
if (--ii == 0)
break;
// Save the next position.
pg++;
pg->ptl.x = FXTOL(xA + xRef);
pg->ptl.y = FXTOL(-yT + yRef); // y goes down, not opposite from ascender
}
// Expand the text box out to the concatenation point. This allows us to
// continue on with another opaqued string with no visible gap in the
// opaquing.
if (xA > xRight)
xRight = xA;
ptfxUpdate.x = xA;
ptfxUpdate.y = -yT;
rcfx.xLeft = xLeft;
rcfx.xRight = xRight;
if (dco.pdc->bYisUp())
{
rcfx.yTop = -yBottom;
rcfx.yBottom = -yTop;
}
else
{
rcfx.yTop = yTop;
rcfx.yBottom = yBottom;
}
flTO |= TO_VALID;
}
/******************************Public*Routine******************************\
*
* VOID ESTROBJ::vCharPos_G4
*
* handles pdy case with esc != orientation
*
* History:
* 17-Sep-1996 -by- Bodin Dresevic [BodinD]
* Wrote it.
\**************************************************************************/
VOID ESTROBJ::vCharPos_G4
(
XDCOBJ& dco,
RFONTOBJ& rfo,
FIX xRef,
FIX yRef,
LONG *pdxdy
)
{
// In this case, we keep track of how far we have traveled along the
// escapement vector in device coordinates. We need scales to project
// this distance onto the base and ascent, as well as a unit escapement
// vector to find the position.
POINTFL pteEsc = rfo.pteUnitEsc(); // Unit escapement vector.
POINTFL pteAsc = rfo.pteUnitAscent(); // Unit ascender vector.
EFLOAT efScaleX = rfo.efEscToBase(); // Project escapement to baseline.
EFLOAT efScaleY = rfo.efEscToAscent(); // Project escapement to ascent.
// Compute logical to device transforms.
EFLOAT efWtoDEsc = rfo.efWtoDEsc(); // Forward transform for pdx.
EFLOAT efWtoDAsc = rfo.efWtoDAscent(); // Forward transform for pdy
BOOL bUnityEsc = efWtoDEsc.bIs16();
BOOL bUnityAsc = efWtoDAsc.bIs16();
// Compute extra spacing for the non-pdx case.
FIX fxD1,fxD2; // Pre- and Post-center character widths.
FIX fxAscent = rfo.fxMaxAscent(); // Cache locally.
FIX fxDescent = rfo.fxMaxDescent(); // Cache locally.
ASSERTGDI(pdxdy, "G4 text out case: pdxdy is null\n");
ASSERTGDI(cGlyphs > 0, "G4, cGlyphs == 0\n");
// Make some local pointers.
EGLYPHPOS *pg = pgpos;
GLYPHDATA *pgd;
WCHAR *pwsz = pwszOrg;
// Set the first character position.
pg->ptl.x = xRef;
pg->ptl.y = yRef;
// Set up for character loop.
LONG xSum; // Keep pdx sum here.
LONG ySum; // Keep pdy sum here.
FIX xA,xB; // Baseline coordinates.
FIX yA,yB; // Ascent coordinates.
FIX sA; // Position on the escapement vector.
FIX sT; // Position on the ascender vector.
RECTFX rcfxBounds; // Accumulate bounds here.
UINT ii;
POINTL *pDxDy = (POINTL *)pdxdy;
rcfxBounds.xLeft = LONG_MAX; // Start with an empty TextBox.
rcfxBounds.xRight = LONG_MIN;
rcfxBounds.yTop = LONG_MIN;
rcfxBounds.yBottom = LONG_MAX;
// We keep the current concatenation point in sA. Note that this is NOT
// where the character origin will be placed.
sA = sT = 0;
xSum = ySum = 0;
BOOL bAccel;
if (!rfo.bGetGlyphMetricsPlus(cGlyphs, pg, pwsz, &bAccel, &dco, this))
return;
if (bAccel)
{
flTO |= TO_ALL_PTRS_VALID;
pgp = pgpos;
}
for (ii=0; ii<cGlyphs; ii++, pg++, pDxDy++)
{
pgd = pg->pgd();
// Using the GenWidths function, determine where the
// character center should be placed.
vGenWidths
(
&fxD1, // Pre-center spacing
&fxD2, // Post-center spacing
efScaleY, // Ascent projection
efScaleX, // Baseline projection
pgd->fxD, // Character width
pgd->fxInkTop, // Ink box top
pgd->fxInkBottom, // Ink box bottom
fxAscent // Maximum Ascent
);
sA += fxD1; // Advance to the character center.
// Update ascent bounds.
yA = lCvt(efScaleY,sA) + sT; // Project onto ascent.
yB = yA + fxDescent;
if (yB < rcfxBounds.yBottom)
rcfxBounds.yBottom = yB;
yB = yA + fxAscent;
if (yB > rcfxBounds.yTop)
rcfxBounds.yTop = yB;
ASSERTGDI(!rfo.bSmallMetrics(),"ESTROBJ__vCharPos_G3: Small Metrics in cache\n");
// Project the center position onto the baseline and
// move back to the character origin.
xA = lCvt(efScaleX,sA) - pgd->fxD / 2;
// Update the width bounds. Fudge a quarter pel on each side for
// roundoff.
xB = xA + pgd->fxA - 4;
if (xB < rcfxBounds.xLeft)
rcfxBounds.xLeft = xB;
xB = xA + pgd->fxAB + 4;
if (xB > rcfxBounds.xRight)
rcfxBounds.xRight = xB;
// Save the adjusted character origin.
pg->ptl.x
= xRef + lCvt(pteEsc.x,sA) + lCvt(pteAsc.x,sT) - pgd->ptqD.x.u.HighPart / 2;
pg->ptl.y
= yRef + lCvt(pteEsc.y,sA) + lCvt(pteAsc.y,sT) - pgd->ptqD.y.u.HighPart / 2;
// Advance to the next concatenation point.
xSum += pDxDy->x;
ySum += pDxDy->y;
sA = bUnityEsc ? LTOFX(xSum) : lCvt(efWtoDEsc,xSum);
sT = bUnityAsc ? LTOFX(ySum) : lCvt(efWtoDAsc,ySum);
}
// ptfxUpdate, continue where the last glyph is written:
ptfxUpdate.x = lCvt(pteEsc.x,sA) + lCvt(pteAsc.x,sT);
ptfxUpdate.y = lCvt(pteEsc.y,sA) + lCvt(pteAsc.y,sT);
rcfx = rcfxBounds;
flTO |= TO_VALID;
}
/******************************Public*Routine******************************\
* STROBJ_bGetAdvanceWidthsLinked(
*
* returns the widths for the batch of glyphs from the current font
*
* Warnings: assumes that the glyph has been partitioned properly
*
* History:
* 27-Jan-1998 -by- Bodin Dresevic [BodinD]
* Wrote it.
\**************************************************************************/
BOOL STROBJ_bGetAdvanceWidthsLinked(
ESTROBJ * peso,
ULONG iFirst,
ULONG cBatch,
POINTQF *pptqD
)
{
// Quick exit.
BOOL bRet = FALSE;
ULONG iG, iLast;
iG = 0;
iLast = iFirst + cBatch;
if (iLast <= peso->cGlyphs)
{
bRet = TRUE;
for(iG = 0, peso->plNext = peso->plPartition, peso->pgpNext = peso->pgpos;
iG < iLast;
(peso->pgpNext)++, (peso->plNext)++)
{
if (*(peso->plNext) == peso->lCurrentFont)
{
if (iG >= iFirst)
{
EGLYPHPOS *pg = (EGLYPHPOS *)peso->pgpNext;
if (peso->prfo->bSmallMetrics())
{
pptqD->x.u.HighPart = pg->pgd()->fxD;
pptqD->x.u.LowPart = 0;
pptqD->y.u.HighPart = 0;
pptqD->y.u.LowPart = 0;
}
else
{
*pptqD = pg->pgd()->ptqD;
}
// get to the next adv. width
pptqD++;
}
// increment iG, count of glyphs from this font
iG++;
}
}
}
return bRet;
}
/******************************Public*Routine******************************\
*
*
* Effects: accelerator for the printer drivers, they need to know how to
* update current position, so they need advance vectors of glyphs
* in this string.
*
* Warnings: I think this will not work for linked fonts
* On the other hand printer driver never get called with STROBJ
* for linked string, so this should be ok.
*
* History:
* 09-Jan-1998 -by- Bodin Dresevic [BodinD]
* Wrote it.
\**************************************************************************/
extern "C" BOOL APIENTRY STROBJ_bGetAdvanceWidths(
STROBJ *pso, ULONG iFirst, ULONG cBatch,POINTQF *pptqD
)
{
// We know that all the widths are in the cache, computed already
// just need to copy them down
if(((ESTROBJ*)pso)->flTO & (TO_PARTITION_INIT|TO_SYS_PARTITION))
{
return STROBJ_bGetAdvanceWidthsLinked((ESTROBJ*)pso,iFirst,cBatch,pptqD);
}
BOOL bRet = FALSE;
if ((iFirst + cBatch) <= ((ESTROBJ*)pso)->cGlyphs)
{
EGLYPHPOS *pg = &((ESTROBJ*)pso)->pgpos[iFirst];
EGLYPHPOS *pgEnd = pg + cBatch;
bRet = TRUE;
if (((ESTROBJ*)pso)->prfo->bSmallMetrics())
{
for ( ; pg < pgEnd; pg++, pptqD++)
{
pptqD->x.u.HighPart = pg->pgd()->fxD;
pptqD->x.u.LowPart = 0;
pptqD->y.u.HighPart = 0;
pptqD->y.u.LowPart = 0;
}
}
else
{
for ( ; pg < pgEnd; pg++)
{
*pptqD++ = pg->pgd()->ptqD;
}
}
}
return bRet;
}
/******************************Public*Routine******************************\
*
* BOOL STROBJ_bEnumPositionsOnlyLinked
*
* Enumerates positions only in paralel with STROBJ_bEnumLinked in misceudc.cxx
*
* History:
* 20-Jan-1998 -by- Bodin Dresevic [BodinD]
* Wrote it.
\**************************************************************************/
BOOL STROBJ_bEnumPositionsOnlyLinked(ESTROBJ *peso, ULONG *pc,PGLYPHPOS *ppgpos)
{
// Quick exit.
if( peso->cgposPositionsEnumerated == 0 )
{
for( peso->plNext = peso->plPartition, peso->pgpNext = peso->pgpos;
*(peso->plNext) != peso->lCurrentFont;
(peso->pgpNext)++, (peso->plNext)++ );
{
}
}
else
{
if (peso->cgposPositionsEnumerated == peso->cGlyphs)
{
// no more glyphs so just return
*pc = 0;
return(FALSE);
}
else
{
// find next glyph
for( (peso->plNext)++, (peso->pgpNext)++;
*(peso->plNext) != (peso->lCurrentFont);
(peso->pgpNext)++, (peso->plNext)++ );
{
}
}
}
peso->cgposPositionsEnumerated += 1; // update enumeration state
*pc = 1;
*ppgpos = peso->pgpNext;
return(peso->cgposPositionsEnumerated < peso->cGlyphs); // TRUE => more to come.
}
BOOL APIENTRY STROBJ_bEnumPositionsOnly(
STROBJ *pso,
ULONG *pc,
PGLYPHPOS *ppgpos
)
{
// Quick exit.
if(((ESTROBJ*)pso)->flTO & (TO_PARTITION_INIT|TO_SYS_PARTITION))
{
return STROBJ_bEnumPositionsOnlyLinked((ESTROBJ*)pso, pc, ppgpos);
}
// if not linked all positions are guaranteed to be in the cache:
*pc = ((ESTROBJ*)pso)->cGlyphs;
*ppgpos = ((ESTROBJ*)pso)->pgpos;
return(FALSE); // no more
}