|
|
/******************************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
}
|