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.
 
 
 
 
 
 

4302 lines
146 KiB

/******************************Module*Header*******************************\
* Module Name: textgdi.cxx
*
* Text APIs for NT graphics engine
*
* Created: 18-Dec-1990 10:09:19
* Author: Donald Sidoroff [donalds]
*
* Copyright (c) 1990-1999 Microsoft Corporation
\**************************************************************************/
#include "precomp.hxx"
EFLOAT efCos(EFLOAT x);
EFLOAT efSin(EFLOAT x);
extern PBRUSH gpbrText;
extern PBRUSH gpbrBackground;
#define XFORMNULL (EXFORMOBJ *) NULL
// This is a tiny class just to make sure we don't forget to free up any
// objects before leaving ExtTextOut.
class TXTCLEANUP
{
XDCOBJ *pdco;
public:
TXTCLEANUP() {pdco=(DCOBJ *) NULL;}
~TXTCLEANUP() {if (pdco != (DCOBJ *) NULL) vMopUp();}
VOID vSet(XDCOBJ& dco) {pdco = &dco;}
VOID vMopUp();
};
VOID TXTCLEANUP::vMopUp()
{
RGNOBJ ro(pdco->pdc->prgnAPI());
ro.bDeleteRGNOBJ();
pdco->pdc->prgnAPI(NULL);
}
// Helper routine to draw a device coordinate RECTL into a path.
BOOL bAddRectToPath(EPATHOBJ& po,RECTL *prcl)
{
POINTFIX aptfx[4];
aptfx[3].x = aptfx[0].x = LTOFX(prcl->left);
aptfx[1].y = aptfx[0].y = LTOFX(prcl->top);
aptfx[2].x = aptfx[1].x = LTOFX(prcl->right);
aptfx[3].y = aptfx[2].y = LTOFX(prcl->bottom);
return(po.bAddPolygon(XFORMNULL,(POINTL *) aptfx,4));
}
/******************************Public*Routine******************************\
* GreExtTextOutRect: Wrapper for common GreExtTextOutW code. This routine just
* acquires locks then calls GreExtTextOutWLocked
*
* Arguments:
*
* Same as ExtTextOutRect
*
* Return Value:
*
* BOOL States
*
* History:
*
* 30-Oct-1995 - Initial version of wrapper
*
\**************************************************************************/
BOOL
GreExtTextOutRect(
HDC hdc,
LPRECT prcl
)
{
BOOL bRet = FALSE;
//
// call GreExtTextOutW
//
XDCOBJ dcoDst(hdc);
//
// Validate the destination DC.
//
if (dcoDst.bValid())
{
DEVLOCKOBJ dlo;
if (dlo.bLock(dcoDst))
{
if ((!dcoDst.bDisplay() || dcoDst.bRedirection()) || UserScreenAccessCheck())
{
bRet = ExtTextOutRect(
dcoDst,
prcl);
}
else
{
SAVE_ERROR_CODE(ERROR_ACCESS_DENIED);
}
}
else
{
bRet = dcoDst.bFullScreen();
}
dcoDst.vUnlock();
}
return(bRet);
}
/******************************Public*Routine******************************\
* ExtTextOutRect
*
* Called when just a Rectangle is passed. Over-used by Winbench4.0 the
* perf app we aim to please.
*
* History:
* 02-Nov-1993 -by- Patrick Haluptzok patrickh
* Wrote it.
\**************************************************************************/
BOOL
ExtTextOutRect(
XDCOBJ &dcoDst,
LPRECT prcl
)
{
// Assume failure.
BOOL bReturn;
// Validate the destination DC.
if (dcoDst.bValid())
{
ASSERTGDI(prcl != NULL, "This API must be past a valid rect");
EXFORMOBJ xoDst(dcoDst, WORLD_TO_DEVICE);
if (!xoDst.bRotation())
{
ERECTL erclDst(prcl->left, prcl->top, prcl->right, prcl->bottom);
xoDst.bXform(erclDst);
erclDst.vOrder();
if (!erclDst.bEmpty())
{
// Accumulate bounds. We can do this before knowing if the operation is
// successful because bounds can be loose.
if (dcoDst.fjAccum())
dcoDst.vAccumulate(erclDst);
// Check surface is included in DC.
SURFACE *pSurfDst = dcoDst.pSurface();
if (pSurfDst != NULL)
{
// With a fixed DC origin we can change the destination to SCREEN coordinates.
erclDst += dcoDst.eptlOrigin();
ECLIPOBJ *pco = NULL;
// This is a pretty knarly expression to save a return in here.
// Basically pco can be NULL if the rect is completely in the
// cached rect in the DC or if we set up a clip object that isn't empty.
if (((erclDst.left >= dcoDst.prclClip()->left) &&
(erclDst.right <= dcoDst.prclClip()->right) &&
(erclDst.top >= dcoDst.prclClip()->top) &&
(erclDst.bottom <= dcoDst.prclClip()->bottom)) ||
(pco = dcoDst.pco(),
pco->vSetup(dcoDst.prgnEffRao(), erclDst,CLIP_NOFORCETRIV),
erclDst = pco->erclExclude(),
(!erclDst.bEmpty())))
{
// Set up the brush if necessary.
EBRUSHOBJ *pbo = dcoDst.peboBackground();
if (dcoDst.bDirtyBrush(DIRTY_BACKGROUND))
{
dcoDst.vCleanBrush(DIRTY_BACKGROUND);
XEPALOBJ palDst(pSurfDst->ppal());
XEPALOBJ palDstDC(dcoDst.ppal());
pbo->vInitBrush(dcoDst.pdc,
gpbrBackground,
palDstDC,
palDst,
pSurfDst,
(dcoDst.flGraphicsCaps() & GCAPS_ARBRUSHOPAQUE) ? TRUE : FALSE);
}
DEVEXCLUDEOBJ dxo(dcoDst,&erclDst,pco);
// Inc the target surface uniqueness
INC_SURF_UNIQ(pSurfDst);
// Dispatch the call.
bReturn = (*(pSurfDst->pfnBitBlt()))
(pSurfDst->pSurfobj(),
(SURFOBJ *) NULL,
(SURFOBJ *) NULL,
pco,
NULL,
&erclDst,
(POINTL *) NULL,
(POINTL *) NULL,
pbo,
&dcoDst.pdc->ptlFillOrigin(),
0x0000f0f0);
}
else
{
bReturn = TRUE;
}
}
else
{
bReturn = TRUE;
}
}
else
{
bReturn = TRUE;
}
}
else
{
// There is rotation involved - send it off to ExtTextOutW to handle it.
bReturn = GreExtTextOutWLocked(dcoDst, 0, 0, ETO_OPAQUE,
prcl, (LPWSTR) NULL, 0, NULL, dcoDst.pdc->jBkMode(), NULL, 0);
}
}
else
{
WARNING1("ERROR TextOutRect called on invalid DC\n");
bReturn = FALSE;
}
return(bReturn);
}
/******************************Public*Routine******************************\
* BOOL GrePolyTextOut
*
* Write text with lots of random spacing and alignment options.
*
* Arguments:
*
* hdc - handle to DC
* lpto - Ptr to array of polytext structures
* nStrings - number of polytext structures
*
* Return Value:
*
* History:
* Tue 09-Nov-1993 -by- Patrick Haluptzok [patrickh]
* For code size we'll call ExtTextOutW. After the RFONT and brushes are
* realized, the RaoRegion is setup etc, future calls to ExtTextOutW
* will be very cheap. Plus ExtTextOut will be in the working set where a
* huge GrePolyTextOut won't be. It is still a savings by avoiding kernel
* transitions
*
\**************************************************************************/
BOOL GrePolyTextOutW(
HDC hdc,
POLYTEXTW *lpto,
UINT nStrings,
DWORD dwCodePage
)
{
BYTE CaptureBuffer[TEXT_CAPTURE_BUFFER_SIZE];
BOOL bRet = TRUE;
//
// call GreExtTextOutW
//
XDCOBJ dcoDst(hdc);
//
// Validate the destination DC.
//
if (dcoDst.bValid())
{
DEVLOCKOBJ dlo;
if (dlo.bLock(dcoDst))
{
for (POLYTEXTW *ppt = lpto; ppt < lpto+nStrings; ppt += 1)
{
//
// Try to use stack buffer
//
PVOID pStrObjBuffer = NULL;
ULONG cjStrObj = SIZEOF_STROBJ_BUFFER(ppt->n);
if (TEXT_CAPTURE_BUFFER_SIZE >= cjStrObj)
{
pStrObjBuffer = CaptureBuffer;
}
if (!GreExtTextOutWLocked(
dcoDst,
ppt->x,
ppt->y,
ppt->uiFlags,
&(ppt->rcl),
(LPWSTR) ppt->lpstr,
ppt->n,
ppt->pdx,
dcoDst.pdc->jBkMode(),
pStrObjBuffer,
dwCodePage))
{
WARNING1("GrePolyTextOutW failed a call to GreExtTextOutW\n");
bRet = FALSE;
break;
}
}
}
else
{
bRet = dcoDst.bFullScreen();
}
dcoDst.vUnlock();
}
else
{
WARNING1("GrePolyTextOutW: can not lock dc \n");
SAVE_ERROR_CODE(ERROR_INVALID_HANDLE);
bRet = FALSE;
}
return(bRet);
}
/******************************Public*Routine******************************\
* GreExtTextOutW: Wrapper for common GreExtTextOutW code. This routine just
* acquires locks then calls GreExtTextOutWLocked
*
* Arguments:
*
* Same as GreExtTextOutW
*
* Return Value:
*
* BOOL States
*
* History:
*
* 30-Oct-1995 - Initial version of wrapper
*
\**************************************************************************/
// for user/kernel consumption only
BOOL GreExtTextOutW(
HDC hdc,
int x,
int y,
UINT flOpts,
CONST RECT *prcl,
LPCWSTR pwsz,
UINT cwc,
CONST INT *pdx
)
{
return GreExtTextOutWInternal(
hdc,
x,
y,
flOpts,
(LPRECT)prcl,
(LPWSTR)pwsz,
(int) cwc,
(LPINT)pdx,
NULL,
0
);
}
BOOL GreExtTextOutWInternal(
HDC hdc,
int x,
int y,
UINT flOpts,
LPRECT prcl,
LPWSTR pwsz,
int cwc,
LPINT pdx,
PVOID pvBuffer,
DWORD dwCodePage
)
{
BOOL bRet = FALSE;
//
// call GreExtTextOutW
//
XDCOBJ dcoDst(hdc);
//
// Validate the destination DC.
//
if (dcoDst.bValid())
{
DEVLOCKOBJ dlo;
if (dlo.bLock(dcoDst))
{
bRet = GreExtTextOutWLocked(
dcoDst,
x,
y,
flOpts,
prcl,
pwsz,
cwc,
pdx,
dcoDst.pdc->jBkMode(),
pvBuffer,
dwCodePage);
}
else
{
bRet = dcoDst.bFullScreen();
}
dcoDst.vUnlock();
}
return(bRet);
}
/******************************Public*Routine******************************\
* GreBatchTextOut: Set propper DC flags and attributes, then call textout
* pr textoutrect.
*
* Arguments:
*
* dcoDst Locked DCOBJ
* xoDst XFORMOBJ
* pbText Batched textout entry
*
* Return Value:
*
* Status
*
* 14-Oct-1995 -by- Mark Enstrom [marke]
*
\**************************************************************************/
#define ETO_NULL_PRCL 0x80000000
BOOL
GreBatchTextOut(
XDCOBJ &dcoDst,
PBATCHTEXTOUT pbText,
ULONG cjBatchLength // cannot trust the Length field in pbText
// because it is accessible to user-mode
)
{
//
// set foreground and background color in DC
// and restore after call (if needed)
//
BYTE CaptureBuffer[TEXT_CAPTURE_BUFFER_SIZE];
COLORREF crSaveText = (COLORREF)-1;
COLORREF crSaveBack = (COLORREF)-1;
HANDLE hlfntNewSave = NULL;
UINT flTextAlignSave = -1;
POINTL ptlViewportOrgSave;
PVOID pStrObjBuffer = NULL;
ULONG ulSaveText, ulSaveBack;
if (pbText->Type == BatchTypeTextOutRect)
{
ASSERTGDI(((PBATCHTEXTOUTRECT)pbText)->fl & ETO_OPAQUE, "GreBatchTextOut, flags\n");
crSaveBack = dcoDst.pdc->crBackClr();
ulSaveBack = dcoDst.pdc->ulBackClr();
if (crSaveBack != ((PBATCHTEXTOUTRECT)pbText)->BackColor)
{
dcoDst.pdc->crBackClr(((PBATCHTEXTOUTRECT)pbText)->BackColor);
dcoDst.pdc->ulBackClr(((PBATCHTEXTOUTRECT)pbText)->ulBackColor);
dcoDst.ulDirtyAdd(DIRTY_FILL|DIRTY_LINE|DIRTY_BACKGROUND);
}
ptlViewportOrgSave = dcoDst.pdc->ptlViewportOrg();
if ((ptlViewportOrgSave.x != ((PBATCHTEXTOUTRECT)pbText)->ptlViewportOrg.x) ||
(ptlViewportOrgSave.y != ((PBATCHTEXTOUTRECT)pbText)->ptlViewportOrg.y))
{
dcoDst.pdc->lViewportOrgX(((PBATCHTEXTOUTRECT)pbText)->ptlViewportOrg.x);
dcoDst.pdc->lViewportOrgY(((PBATCHTEXTOUTRECT)pbText)->ptlViewportOrg.y);
dcoDst.pdc->flSet_flXform(
PAGE_XLATE_CHANGED |
DEVICE_TO_WORLD_INVALID);
}
ExtTextOutRect(dcoDst,(LPRECT)&((PBATCHTEXTOUTRECT)pbText)->rcl);
if (dcoDst.pdc->crBackClr() != crSaveBack)
{
dcoDst.pdc->crBackClr(crSaveBack);
dcoDst.pdc->ulBackClr(ulSaveBack);
dcoDst.ulDirtyAdd(DIRTY_FILL|DIRTY_LINE|DIRTY_BACKGROUND);
}
if ((ptlViewportOrgSave.x != dcoDst.pdc->lViewportOrgX()) ||
(ptlViewportOrgSave.y != dcoDst.pdc->lViewportOrgY()))
{
dcoDst.pdc->lViewportOrgX(ptlViewportOrgSave.x);
dcoDst.pdc->lViewportOrgY(ptlViewportOrgSave.y);
dcoDst.pdc->flSet_flXform(
PAGE_XLATE_CHANGED |
DEVICE_TO_WORLD_INVALID);
}
}
else
{
RECT newRect;
LPRECT prcl;
LPINT pdx = NULL;
//
// Capture the buffer length parameters from the TEB batch.
// They can be overwritten by user-mode, so are not to be
// trusted.
//
ULONG fl = pbText->fl;
ULONG cChar = pbText->cChar;
ULONG PdxOffset = pbText->PdxOffset;
//
// Check that char data will not overflow the data buffer.
//
ULONG cjBuf = cjBatchLength - offsetof(BATCHTEXTOUT, ulBuffer);
if (cChar > (cjBuf / sizeof(WCHAR)))
{
WARNING("GreBatchTextOut: batch overflow char\n");
return FALSE;
}
//
// Check that optional pdx data will not overflow the data buffer.
//
if (PdxOffset != 0)
{
ULONG cjPdxPerChar;
//
// The pdx array must have cChar number of horizontal
// deltas and, if the optional ETO_PDY flag is set, cChar
// number of vertical deltas.
//
cjPdxPerChar = sizeof(INT);
if (fl & ETO_PDY)
cjPdxPerChar *= 2;
//
// Is the start and end of the pdx array in range?
//
if ((PdxOffset > cjBuf) ||
(cChar > ((cjBuf - PdxOffset) / cjPdxPerChar)))
{
WARNING("GreBatchTextOut: batch overflow pdx\n");
return FALSE;
}
//
// Since we know that the pdx array is in range, go ahead
// and set pdx.
//
pdx = (LPINT)((PUCHAR)&pbText->ulBuffer[0] + PdxOffset);
}
//
// Set foreground and background text colors.
//
crSaveText = dcoDst.pdc->crTextClr();
ulSaveText = dcoDst.pdc->ulTextClr();
if (crSaveText != pbText->TextColor)
{
dcoDst.pdc->crTextClr(pbText->TextColor);
dcoDst.pdc->ulTextClr(pbText->ulTextColor);
dcoDst.ulDirtyAdd(DIRTY_FILL|DIRTY_LINE|DIRTY_TEXT);
}
crSaveBack = dcoDst.pdc->crBackClr();
ulSaveBack = dcoDst.pdc->ulBackClr();
if (crSaveBack != pbText->BackColor)
{
dcoDst.pdc->crBackClr(pbText->BackColor);
dcoDst.pdc->ulBackClr(pbText->ulBackColor);
dcoDst.ulDirtyAdd(DIRTY_FILL|DIRTY_LINE|DIRTY_BACKGROUND);
}
if (dcoDst.pdc->hlfntNew() != pbText->hlfntNew)
{
hlfntNewSave = dcoDst.pdc->hlfntNew();
dcoDst.pdc->hlfntNew((HLFONT)pbText->hlfntNew);
dcoDst.ulDirtyAdd(DIRTY_CHARSET);
dcoDst.ulDirtySub(SLOW_WIDTHS);
}
if (dcoDst.pdc->flTextAlign() != pbText->flTextAlign)
{
flTextAlignSave = dcoDst.pdc->flTextAlign();
dcoDst.pdc->flTextAlign(pbText->flTextAlign);
}
ptlViewportOrgSave = dcoDst.pdc->ptlViewportOrg();
if ((ptlViewportOrgSave.x != pbText->ptlViewportOrg.x) ||
(ptlViewportOrgSave.y != pbText->ptlViewportOrg.y))
{
dcoDst.pdc->lViewportOrgX(pbText->ptlViewportOrg.x);
dcoDst.pdc->lViewportOrgY(pbText->ptlViewportOrg.y);
dcoDst.pdc->flSet_flXform(
PAGE_XLATE_CHANGED |
DEVICE_TO_WORLD_INVALID);
}
if (fl & ETO_NULL_PRCL)
{
prcl = NULL;
fl &= ~ETO_NULL_PRCL;
}
else
{
__try
{
newRect = ProbeAndReadStructure((LPRECT)&pbText->rcl,RECT);
prcl = &newRect;
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
WARNINGX(119);
return FALSE;
}
}
//
// try to use stack based temp buffer
//
ULONG cjStrObj = SIZEOF_STROBJ_BUFFER(cChar);
if (TEXT_CAPTURE_BUFFER_SIZE >= cjStrObj)
{
pStrObjBuffer = &CaptureBuffer[0];
}
GreExtTextOutWLocked(
dcoDst,
pbText->x,
pbText->y,
fl,
prcl,
(LPWSTR)&pbText->ulBuffer[0],
cChar,
pdx,
pbText->BackMode,
pStrObjBuffer,
pbText->dwCodePage);
//
// Restore the foreground and background text colors.
//
if (dcoDst.pdc->crTextClr() != crSaveText)
{
dcoDst.pdc->crTextClr(crSaveText);
dcoDst.pdc->ulTextClr(ulSaveText);
dcoDst.ulDirtyAdd(DIRTY_FILL|DIRTY_LINE|DIRTY_TEXT);
}
if (dcoDst.pdc->crBackClr() != crSaveBack)
{
dcoDst.pdc->crBackClr(crSaveBack);
dcoDst.pdc->ulBackClr(ulSaveBack);
dcoDst.ulDirtyAdd(DIRTY_FILL|DIRTY_LINE|DIRTY_BACKGROUND);
}
if (hlfntNewSave != NULL)
{
dcoDst.pdc->hlfntNew(((HLFONT)hlfntNewSave));
dcoDst.ulDirtyAdd(DIRTY_CHARSET);
dcoDst.ulDirtySub(SLOW_WIDTHS);
}
if (flTextAlignSave != -1)
{
dcoDst.pdc->flTextAlign(flTextAlignSave);
}
if ((ptlViewportOrgSave.x != dcoDst.pdc->lViewportOrgX()) ||
(ptlViewportOrgSave.y != dcoDst.pdc->lViewportOrgY()))
{
dcoDst.pdc->lViewportOrgX(ptlViewportOrgSave.x);
dcoDst.pdc->lViewportOrgY(ptlViewportOrgSave.y);
dcoDst.pdc->flSet_flXform(
PAGE_XLATE_CHANGED |
DEVICE_TO_WORLD_INVALID);
}
}
return(TRUE);
}
/******************************Public*Routine******************************\
* BOOL GreExtTextOutW (hdc,x,y,flOpts,prcl,pwsz,cwc,pdx,pvBuffer)
*
* Write text with lots of random spacing and alignment options.
*
* Below are the string length distributions from running the Winstone 95
* scenarios for Excel and Winword, courtesy of KirkO:
*
* [Excel]
* ------ OPAQUE ------- ----- TRANSPARENT ---
* string length pdx == 0 pdx != 0 pdx == 0 pdx != 0
* ------------- ---------- ---------- ---------- ----------
* 0--9 3799 0 15323 9143
* 10--19 695 0 1651 983
* 20--29 527 0 196 22
* 30--39 200 0 289 53
* 40--49 12 0 3 0
* 50--59 96 0 12 0
* 60--69 10 0 4 0
* 70--79 3 0 0 0
* 80--89 0 0 0 0
* 90--99 0 0 49 16
* 100--109 11 0 0 0
*
* [Winword]
*
* ------ OPAQUE ------- ----- TRANSPARENT ---
* string length pdx == 0 pdx != 0 pdx == 0 pdx != 0
* ------------- ---------- ---------- ---------- ----------
* 0--9 1875 9 3350 0
* 10--19 878 17 324 0
* 20--29 254 17 25 0
* 30--39 93 6 28 2
* 40--49 146 30 18 12
* 50--59 34 16 3 14
* 60--69 20 13 8 7
* 70--79 128 73 13 18
* 80--89 53 139 6 23
* 90--99 41 903 9 127
* 100--109 92 1878 3 217
* 110--119 5 23 0 0
*
* History:
* Fri 05-Nov-1993 -by- Patrick Haluptzok [patrickh]
* Make smaller and faster.
*
* Sat 14-Mar-1992 06:08:26 -by- Charles Whitmer [chuckwh]
* Rewrote it.
*
* Thu 03-Jan-1991 -by- Bodin Dresevic [BodinD]
* Wrote it.
\**************************************************************************/
#define TS_DRAW_TEXT 0x0001
#define TS_DRAW_OPAQUE_PGM 0x0002
#define TS_DRAW_COMPLEX_UNDERLINES 0x0004
#define TS_DRAW_OPAQUE_RECT 0x0008
#define TS_DRAW_BACKGROUND_PGM 0x0010
#define TS_DRAW_VECTOR_FONT 0x0020
#define TS_DRAW_OUTLINE_FONT 0x0040
#define TS_START_WITH_SUCCESS 0x0080
// Due to the massiveness of this function we will use 2 space tabs.
#if DBG // statistics
typedef struct _TSTATENTRY {
int c; // number observed
} TSTATENTRY;
typedef struct _TSTAT {
TSTATENTRY NO; // pdx == 0, opaque
TSTATENTRY DO; // pdx != 0, opaque
TSTATENTRY NT; // pdx == 0, transparent
TSTATENTRY DT; // pdx != 0, transparent
} TSTAT;
#define MAX_CHAR_COUNT 200 // observe for 0,1,..200,201 and above
typedef struct _TEXTSTATS {
int cchMax; // = MAX_CHAR_COUNT
TSTAT ats[MAX_CHAR_COUNT+2];
} TEXTSTATS;
TEXTSTATS gTS = {MAX_CHAR_COUNT};
#define TS_START 1
#define TS_CONTINUE 2
#define TS_VERBOSE 4
#define TS_STOP 8
FLONG gflTS = 0; // flags that control text statistics
#endif // statistics
BOOL GreExtTextOutWLocked(
XDCOBJ &dco, // Locked DC
int x, // Initial x position
int y, // Initial y position
UINT flOpts, // Options
LPRECT prcl, // Clipping rectangle
LPWSTR pwsz, // Unicode character array
int cwc, // Count of characters
LPINT pdx, // Character spacing
ULONG ulBkMode, // Current background mode
PVOID pvBuffer, // If non-NULL, contains a buffer for storing
// the STROBJ, so that we don't need to
// allocate one on the fly. Must be at
// least SIZEOF_STROBJ_BUFFER(cwc) bytes in
// size.
// If NULL, we will try to use the global
// STROBJ buffer, otherwise we will allocate
// on the fly.
DWORD dwCodePage // Code page for current textout
)
{
// flState gets set with BITS for things we need to draw.
// We also use this for the return value, setting it to 0 if an error occurs.
BOOL bIsVectorFont = FALSE;
BOOL bPathFont = FALSE;
FLONG flState = TS_START_WITH_SUCCESS;
BOOL flType = ((flOpts & ETO_GLYPH_INDEX) ? RFONT_TYPE_HGLYPH : RFONT_TYPE_UNICODE);
// Win95 comaptibility: If in path bracket and ETO_CLIPPED is set, then fail call
if ( dco.pdc->bActive() && ( flOpts & ETO_CLIPPED ) )
{
EngSetLastError( ERROR_INVALID_PARAMETER );
return( FALSE );
}
if (dco.bDisplay() && !dco.bRedirection() && !UserScreenAccessCheck())
{
SAVE_ERROR_CODE(ERROR_ACCESS_DENIED);
return( FALSE );
}
// ignore RTOLREADING flag,
// only does something if lpk dlls are loaded under win95
BOOL bPdy = flOpts & ETO_PDY;
flOpts &= ~(ETO_PDY | ETO_GLYPH_INDEX | ETO_RTLREADING | ETO_IGNORELANGUAGE | ETO_NUMERICSLOCAL | ETO_NUMERICSLATIN);
if (!prcl)
{
flOpts &= ~(ETO_CLIPPED | ETO_OPAQUE); // ignore flags if no rect, win95 compat
}
else if ((prcl->left == prcl->right) || (prcl->top == prcl->bottom))
{
prcl->left = prcl->right = x;
prcl->top = prcl->bottom = y;
// now prcl is guaranteed to be within text rect
if ((flOpts & (ETO_OPAQUE|ETO_CLIPPED)) == ETO_OPAQUE)
{
prcl = NULL; // since it is empty, we may as well ignore it
flOpts &= ~ETO_OPAQUE; // ignore flag if no rect
}
}
PFN_DrvTextOut pfnTextOut;
MIX mix = R2_COPYPEN + 256*R2_COPYPEN; // default mix sent to TextOut routines
// Lock the DC and set the new attributes.
if (dco.bValid())
{
#if DBG // statistics
if (gflTS & TS_START) {
KdPrint((
"\n"
"***************************************\n"
"*** BEGIN GreExtTextOutW statistics ***\n"
"***************************************\n"
"\n"
));
RtlZeroMemory(gTS.ats, sizeof(TSTAT)*(gTS.cchMax+2));
gflTS ^= TS_START;
gflTS |= TS_CONTINUE;
}
if (gflTS & TS_CONTINUE) {
TSTATENTRY *ptse;
char *psz;
TSTAT *pts;
pts = gTS.ats + min(cwc,gTS.cchMax+1);
ptse = 0;
switch (ulBkMode)
{
case OPAQUE:
ptse = (pdx) ? &(pts->DO) : &(pts->NO);
psz = "OPAQUE";
break;
case TRANSPARENT:
ptse = (pdx) ? &(pts->DT) : &(pts->NT);
psz = "TRANSP";
break;
default:
KdPrint(("jBkMode = UNKNOWN\n"));
break;
}
if (ptse) {
ptse->c += 1;
if (gflTS & TS_VERBOSE)
KdPrint((
"%10d %10d %s %s"
, min(cwc,gTS.cchMax+1)
, ptse->c
, psz
, (pdx) ? "(pdx)" : ""
));
}
}
if (gflTS & TS_STOP) {
KdPrint((
"\n"
"*************************************\n"
"*** END GreExtTextOutW statistics ***\n"
"*************************************\n"
"\n"
));
gflTS = 0;
}
#endif // statistics
// Check the validity of the flags.
if
(
((flOpts == 0) || ((prcl != (RECT *) NULL) && ((flOpts & ~(ETO_CLIPPED | ETO_OPAQUE)) == 0)))
&&
!(dco.pdc->bActive() && (flOpts & ETO_CLIPPED)) // no clipping in a path
)
{
FLONG flControl = 0; // Set to 0 in case cwc is 0
ERECTL rclExclude(0,0,0,0);
// Lock the Rao region if we are drawing on a display surface. The Rao region
// might otherwise change asynchronously. The DEVLOCKOBJ also makes sure that
// the VisRgn is up to date, calling the window manager if necessary to
// recompute it. This is needed if want to compute positions in screen coords.
// No DEVLOCK is needed if we are in path accumulation mode.
POINTL ptlOrigin; // The window origin.
POINTFIX ptfxOrigin; // Same thing in FIX.
if (dco.pdc->bActive())
{
ptlOrigin.x = 0;
ptlOrigin.y = 0;
}
else
{
ptlOrigin = dco.eptlOrigin();
}
ptfxOrigin.x = LTOFX(ptlOrigin.x);
ptfxOrigin.y = LTOFX(ptlOrigin.y);
// Get the transform from the DC.
EXFORMOBJ xo(dco, WORLD_TO_DEVICE);
// Transform the input rectangle. In the simple case the result is in
// rclInput. In the general case a parallelogram is in ptfxInput[4], and
// bounds for the parallelogram are in rclInput.
ERECTL rclInput;
POINTFIX ptfxInput[4];
// this must go after the DCOBJ, so that the DCOBJ still exists at cleanup
TXTCLEANUP clean; // Mops up before exit.
if (prcl != (RECT *) NULL)
{
if (flOpts & ETO_OPAQUE)
{
flState |= TS_DRAW_OPAQUE_RECT;
}
// The intent of the following bTranslationsOnly and bScale cases is to provide
// faster inline versions of the same code that would be executed by xo.bXform(prcl).
// We must be completely compatible with the normal xform code because apps expect
// to opaque and clip to the same rectangles as a PatBlt would cover. So, if you
// intend to modify the behavior of this code, make sure you change PatBlt and
// BitBlt as well! (I.e. just don't do it.) [chuckwh]
if (xo.bTranslationsOnly())
{
rclInput.left = prcl->left + ptlOrigin.x + FXTOL(xo.fxDx() + 8);
rclInput.right = prcl->right + ptlOrigin.x + FXTOL(xo.fxDx() + 8);
rclInput.top = prcl->top + ptlOrigin.y + FXTOL(xo.fxDy() + 8);
rclInput.bottom = prcl->bottom + ptlOrigin.y + FXTOL(xo.fxDy() + 8);
}
else if (xo.bScale()) // Simple scaling.
{
rclInput.left = FXTOL(xo.fxFastX(prcl->left) + 8) + ptlOrigin.x;
rclInput.right = FXTOL(xo.fxFastX(prcl->right) + 8) + ptlOrigin.x;
rclInput.top = FXTOL(xo.fxFastY(prcl->top) + 8) + ptlOrigin.y;
rclInput.bottom = FXTOL(xo.fxFastY(prcl->bottom) + 8) + ptlOrigin.y;
}
else // General case.
{
//
// Construct three vertices of the input rectangle, in drawing order.
//
ptfxInput[0].x = prcl->left;
ptfxInput[0].y = prcl->bottom;
ptfxInput[1].x = prcl->left;
ptfxInput[1].y = prcl->top;
ptfxInput[2].x = prcl->right;
ptfxInput[2].y = prcl->top;
// Transform the vertices.
xo.bXform((POINTL *) ptfxInput,ptfxInput,3);
// We construct the fourth vertex ourselves to avoid excess transform
// work and roundoff errors.
ptfxInput[3].x = ptfxInput[0].x + ptfxInput[2].x - ptfxInput[1].x;
ptfxInput[3].y = ptfxInput[0].y + ptfxInput[2].y - ptfxInput[1].y;
// Bound the parallelogram. (Using Black Magic.)
// DChinn: At this point, we know that EITHER points 0 and 2 OR
// points 1 and 3 are the extreme x coordinates (left and right).
// Similarly, EITHER points 0 and 2 OR points 1 and 3 are the extreme y
// coordinates.
//
// ASSERT: it is possible that at this point, prcl is inverted
// (i.e., left > right or top > bottom). If this is true, then
// ptfxInput[] will have its points ordered counterclockwise rather
// than clockwise.
int ii;
ii = (ptfxInput[1].x > ptfxInput[0].x)
== (ptfxInput[1].x > ptfxInput[2].x);
// Rounding direction depends on whether the parallelogram is inverted or not.
if (ptfxInput[ii].x <= ptfxInput[ii+2].x)
{
rclInput.left = ptlOrigin.x + FXTOL(ptfxInput[ii].x);
rclInput.right = ptlOrigin.x + FXTOLCEILING(ptfxInput[ii+2].x);
}
else
{
rclInput.left = ptlOrigin.x + FXTOLCEILING(ptfxInput[ii].x);
rclInput.right = ptlOrigin.x + FXTOL(ptfxInput[ii+2].x);
}
ii = (ptfxInput[1].y > ptfxInput[0].y)
== (ptfxInput[1].y > ptfxInput[2].y);
// Rounding direction depends on whether the parallelogram is inverted or not.
if (ptfxInput[ii].y <= ptfxInput[ii+2].y)
{
rclInput.top = ptlOrigin.y + FXTOL(ptfxInput[ii].y);
rclInput.bottom = ptlOrigin.y + FXTOLCEILING(ptfxInput[ii+2].y);
}
else
{
rclInput.top = ptlOrigin.y + FXTOLCEILING(ptfxInput[ii].y);
rclInput.bottom = ptlOrigin.y + FXTOL(ptfxInput[ii+2].y);
}
// Take care of a complex clipping request now. We'll set things up
// so that the clipping will just happen automatically, and then clear
// the clipping flag. This simplifies the rest of the code.
if (flOpts & ETO_CLIPPED)
{
// Allocate a path. We know we're not already in a path bracket
// since clipping requests in path brackets were rejected above.
// Draw the parallelogram into it.
PATHMEMOBJ po;
if (po.bValid() && po.bAddPolygon(XFORMNULL,(POINTL *)&ptfxInput[0],4))
{
//
// Construct a region from the path.
//
RECTL Bounds, *pBounds;
// Only the top and bottom are used for clipping and
// they have to be initialized as FIX.
Bounds.top = LTOFX(dco.erclClip().top - ptlOrigin.y);
Bounds.bottom = LTOFX(dco.erclClip().bottom - ptlOrigin.y);
pBounds = &Bounds;
RGNMEMOBJ rmo(po, ALTERNATE, pBounds);
if (rmo.bValid())
{
// Stuff the region handle into the DC. This is a nifty trick
// (courtesy of DonaldS) that uses this region in the next
// calculation of the clipping pipeline. We'll clear the prgnAPI
// after we're through.
dco.pdc->prgnAPI(rmo.prgnGet());
// Clipping is now transparent, so forget about it.
clean.vSet(dco); // This frees the region on exit.
if (dco.pdc->prgnRao() != (REGION *)NULL)
{
dco.pdc->vReleaseRao();
}
if (dco.pdc->bCompute())
{
flOpts &= ~ETO_CLIPPED;
}
// Now that we have a complex clip area, we can opaque with a
// simple BitBlt. So we don't need to set TS_DRAW_OPAQUE_PGM.
}
}
//
// Well if we have succeeded we will have erased the ETO_CLIPPED flag.
// Otherwise we failed and need to return FALSE. We can't return here
// without generating tons of destructors so we set flState and a few
// other fields to 0 so we bop down and return FALSE.
//
if (flOpts & ETO_CLIPPED)
{
flOpts = 0;
flState = 0;
cwc = 0;
}
}
else if (flOpts & ETO_OPAQUE)
{
flState &= ~TS_DRAW_OPAQUE_RECT;
flState |= TS_DRAW_OPAQUE_PGM;
// Since we're actually going to use it, offset the parallelogram
// to screen coordinates. use unrolled loop
EPOINTFIX *pptfx = (EPOINTFIX *) &ptfxInput[0];
*pptfx++ += ptfxOrigin;
*pptfx++ += ptfxOrigin;
*pptfx++ += ptfxOrigin;
*pptfx += ptfxOrigin;
}
}
// If it a mirrored DC then shift the rect one pixel to the right
// This will give the effect of including the right edge of the rect and exclude the left edge.
if (MIRRORED_DC(dco.pdc)) {
++rclInput.left;
++rclInput.right;
}
// Force the rectangle to be well ordered.
rclInput.vOrder();
// Add any opaquing into the exclusion area.
if (flState &
(TS_DRAW_OPAQUE_RECT | TS_DRAW_OPAQUE_PGM))
{
rclExclude += rclInput;
}
}
POINTFIX aptfxBackground[4]; // The TextBox.
BOOL bComplexBackground; // TRUE if the TextBox is a parallelogram.
RECTL *prclBackground = NULL; // Bkgnd rectangle passed to DrvTextOut.
RECTL *prclExtraRects = NULL; // Extra rectangles passed to DrvTextOut.
RFONTOBJ rfo;
ESTROBJ to;
if (cwc)
{
//
// Locate the font cache. Demand PATHOBJ's if we are in a path bracket.
//
rfo.vInit(dco, dco.pdc->bActive() ? TRUE : FALSE, flType);
if (rfo.bValid())
{
bPathFont = rfo.bPathFont();
bIsVectorFont = rfo.bPathFont() && !rfo.bReturnsOutlines();
// The recording of the simulation flags and escapement must come AFTER
// the rfont constructor since they're cached values that are copied from
// the LFONT only when the font is realized.
flControl = (dco.pdc->flTextAlign() & TA_MASK)
| dco.pdc->flSimulationFlags();
// Get the reference point.
EPOINTFIX ptfx;
if (flControl & TA_UPDATECP)
{
if (dco.pdc->bValidPtfxCurrent())
{
// Mark that we'll be updating the DC's CP in device coords only:
dco.pdc->vInvalidatePtlCurrent();
ptfx.x = dco.ptfxCurrent().x + ptfxOrigin.x;
ptfx.y = dco.ptfxCurrent().y + ptfxOrigin.y;
}
else
{
// DC's CP is in logical coords; we have to transform it to device
// space and mark that we'll be updating the CP in device space:
dco.pdc->vValidatePtfxCurrent();
dco.pdc->vInvalidatePtlCurrent();
if (xo.bTranslationsOnly())
{
ptfx.x = LTOFX(dco.ptlCurrent().x) + xo.fxDx();
ptfx.y = LTOFX(dco.ptlCurrent().y) + xo.fxDy();
}
else if (xo.bScale())
{
ptfx.x = xo.fxFastX(dco.ptlCurrent().x);
ptfx.y = xo.fxFastY(dco.ptlCurrent().y);
}
else
{
xo.bXform((POINTL *) &dco.ptlCurrent(), &ptfx, 1);
}
dco.ptfxCurrent() = ptfx;
ptfx += ptfxOrigin;
}
}
else
{
//
// The reference point is passed in. Transform it to device coords.
//
if (xo.bTranslationsOnly())
{
ptfx.x = LTOFX(x) + xo.fxDx() + ptfxOrigin.x;
ptfx.y = LTOFX(y) + xo.fxDy() + ptfxOrigin.y;
}
else if (xo.bScale())
{
ptfx.x = xo.fxFastX(x) + ptfxOrigin.x;
ptfx.y = xo.fxFastY(y) + ptfxOrigin.y;
}
else
{
ptfx.x = x;
ptfx.y = y;
xo.bXform((POINTL *) &ptfx,&ptfx,1);
ptfx += ptfxOrigin;
}
}
// The STROBJ will now compute the text alignment, character positions,
// and TextBox.
to.vInit
(
pwsz,
cwc,
dco,
rfo,
xo,
(LONG *) pdx,
bPdy,
dco.pdc->lEscapement(), // Only read this after RFONTOBJ!
dco.pdc->lTextExtra(),
dco.pdc->lBreakExtra(),
dco.pdc->cBreak(),
ptfx.x,ptfx.y,
flControl,
(LONG *) NULL,
pvBuffer,
dwCodePage
);
if (to.bValid())
{
// Compute the bounding box and the background opaquing area. The
// parallelogram aptfxBackground is only computed when it's complex.
bComplexBackground = to.bOpaqueArea(aptfxBackground,
&to.rclBkGround);
// This hack, due to filtering needs to be put in textgdi.cxx
// filtering leeks color one pixel to the left and one pixel to the right
// The trouble with it is that is it going to add a pixel on each side
// to the text background when text is painted in the OPAQUE mode.
if (rfo.pfo()->flFontType & FO_CLEARTYPE_X)
{
to.rclBkGround.left--;
to.rclBkGround.right++;
}
if (to.bLinkedGlyphs())
{
to.vEudcOpaqueArea(aptfxBackground, bComplexBackground);
}
// Accumulate the touched area to the exclusion rect.
rclExclude += to.rclBkGround;
// Make notes of exactly what drawing needs to be done.
if (ulBkMode == OPAQUE)
{
if (bComplexBackground)
{
flState |= TS_DRAW_BACKGROUND_PGM;
}
else
{
prclBackground = &to.rclBkGround; // No TS_ bit since this gets
} // passed directly to DrvTextOut.
}
// In a few bizarre cases the STROBJ can have an empty text rectangle
// we don't want to call DrvTextOut in those case but still need
// to worry about the opaque rectangle.
BOOL bEmptyTextRectangle = ((ERECTL *)&(to.rclBkGround))->bEmpty();
// Attempt to combine the rectangles. Even in transparent mode we should
// attempt to send an opaquing rectangle to DrvTextOut in prclBackground.
if ((flState & TS_DRAW_OPAQUE_RECT) &&
(rclInput.bContain(to.rclBkGround)) &&
(!bEmptyTextRectangle) )
{
prclBackground = &rclInput;
flState &= ~TS_DRAW_OPAQUE_RECT;
}
if ( ((prclBackground != NULL ) && !((ERECTL *)prclBackground)->bEmpty() ) ||
((prclBackground == NULL) && !bEmptyTextRectangle) )
{
flState |= TS_DRAW_TEXT;
}
if (flControl & (TSIM_UNDERLINE1 | TSIM_STRIKEOUT))
{
if ((prclExtraRects = to.prclExtraRects()) == NULL)
{
flState |= TS_DRAW_COMPLEX_UNDERLINES;
}
else // include extra rectangles into the touched area:
{
ERECTL *eprcl = (ERECTL *) prclExtraRects;
for (; !eprcl->bEmpty(); eprcl++)
{
rclExclude += *eprcl;
}
}
}
// Accelerate the clipping case when it's irrelevant. (I'm concerned
// that a spreadsheet might tell us to clip to a cell, even though the
// string lies completely within.)
if (flOpts & ETO_CLIPPED)
{
if (rclInput.bContain(rclExclude))
{
flOpts &= ~ETO_CLIPPED;
}
else
{
rclExclude *= rclInput;
}
}
}
else // if (to.bValid())
{
flState = 0;
}
}
else // if (rfo.bValid())
{
flState = 0;
}
} // if (cwc)
if (flControl & TA_UPDATECP)
{
dco.ptfxCurrent().x += to.ptfxAdvance().x;
dco.ptfxCurrent().y += to.ptfxAdvance().y;
}
//
// Draw the text into a path if we're in a path bracket.
//
if (dco.pdc->bActive())
{
//
// We fail this call if we are asked to CLIP while in a path bracket.
//
if (flOpts & ETO_CLIPPED)
{
flState = 0;
}
XEPATHOBJ po(dco);
if (po.bValid())
{
// Draw the various background shapes.
if (flState & TS_DRAW_OPAQUE_RECT)
{
if (!bAddRectToPath(po,&rclInput))
{
flState = 0;
}
}
if (flState & TS_DRAW_OPAQUE_PGM)
{
if (!po.bAddPolygon(XFORMNULL,(POINTL *) &ptfxInput[0],4))
{
flState = 0;
}
}
if (flState & TS_DRAW_BACKGROUND_PGM)
{
if (!po.bAddPolygon(XFORMNULL,(POINTL *) aptfxBackground,4))
{
flState = 0;
}
}
BOOL bNeedUnflattend = FALSE;
//
// Draw the background rect, text, and extra rectangles.
//
if (flState & TS_DRAW_TEXT)
{
//
// Draw a background rectangle.
//
if ((prclBackground == (RECTL *) NULL) ||
bAddRectToPath(po,prclBackground))
{
//
// Draw the text.
//
bNeedUnflattend =
( dco.flGraphicsCaps() & GCAPS_BEZIERS ) ? FALSE : TRUE;
if (!to.bTextToPath(po, dco, bNeedUnflattend))
{
flState = 0;
}
else
{
//
// Draw extra rectangles.
//
if (prclExtraRects != (ERECTL *) NULL)
{
ERECTL *eprcl = (ERECTL *) prclExtraRects;
for (; !eprcl->bEmpty(); eprcl++)
{
if (!bAddRectToPath(po,eprcl))
{
flState = 0;
break;
}
}
}
}
} // if ((prclBackground==0)||bAddRectToPath(po,prclBackground))
} // if (flState & TS_DRAW_TEXT)
//
// Handle complex cases of strikeout and underlines.
//
if (flState & TS_DRAW_COMPLEX_UNDERLINES)
{
if (!to.bExtraRectsToPath(po, bNeedUnflattend))
{
flState = 0;
}
}
} // if (po.bValid())
}
else // if (dco.pdc->bActive())
{
//
// If there's nothing to do, get out now.
//
if (!rclExclude.bEmpty())
{
// Accumulate bounds. We can do this before knowing if the operation is
// successful because bounds can be loose.
if (dco.fjAccum())
{
// Use a temporary exclusion rect in device coordinates.
ERECTL rclExcludeDev;
rclExcludeDev.left = rclExclude.left - ptlOrigin.x;
rclExcludeDev.right = rclExclude.right - ptlOrigin.x;
rclExcludeDev.top = rclExclude.top - ptlOrigin.y;
rclExcludeDev.bottom = rclExclude.bottom - ptlOrigin.y;
dco.vAccumulate(*((ERECTL *) &rclExcludeDev));
}
// Compute the clipping complexity and maybe reduce the exclusion rectangle.
// It appears that a 10pt script font (on a screen DC) can be outside of
// the bounds of the background rectangle. We have a situation where
// an app creates a memory DC exactly the size of the bouding rectangle
// and then draws a vector font to it. We compute the cliping to be
// trivial since we think the vector string fits completely inside
// the bounds of the DC and blow up later in the code. Rather than tweak
// the vector font driver at this point in the game I am going to force
// cliping to be set for vector fonts. Later on we should figure out
// why this is happening and fix the vector font driver. [gerritv]
// Under some xform, the text might be "stretched" to some extent length
// and it will go to the bSimpleFill path code. Force clipping for this
// case, otherwise we might hit the AV since recl's generated from the
// pathobj might be a pixel off than the clip rclBounds <seen in stress>.
ECLIPOBJ co(dco.prgnEffRao(),rclExclude,
bPathFont || (flOpts & ETO_CLIPPED) ? CLIP_FORCE :
CLIP_NOFORCE);
rclExclude = co.erclExclude();
// Check the destination which is reduced by clipping.
if (!co.erclExclude().bEmpty())
{
SURFACE *pSurf = dco.pSurface();
if (pSurf != NULL)
{
PDEVOBJ pdo(pSurf->hdev());
XEPALOBJ palDest(pSurf->ppal());
XEPALOBJ palDestDC(dco.ppal());
// Get the foreground and opaque brushes
FLONG flCaps = dco.flGraphicsCaps();
EBRUSHOBJ *peboText = dco.peboText();
EBRUSHOBJ *peboBackground = dco.peboBackground();
BOOL bDitherText = FALSE;
if ( flCaps & GCAPS_ARBRUSHTEXT )
{
// Even if a printer driver sets GCAPS_ARBRUSHTEXT, we
// can't allow vector fonts to be dithered.
bDitherText = !bIsVectorFont;
// We always dirty the text brush if ARBRUSHTEXT is set to
// catch the cases where we transition between vector and
// non-vector fonts but keep the same brush -- if we didn't
// do this, we might try to use a cached, dithered brush
// after switching from a TrueType font to a vector font.
dco.ulDirtyAdd(DIRTY_TEXT);
// Get through the are-you-really-dirty check in vInitBrush:
peboText->vInvalidateUniqueness();
}
if ( dco.bDirtyBrush(DIRTY_TEXT|DIRTY_BACKGROUND) )
{
if ( dco.bDirtyBrush(DIRTY_TEXT) )
{
peboText->vInitBrush(dco.pdc,
gpbrText,
palDestDC,
palDest,
pSurf,
bDitherText);
}
if ( dco.bDirtyBrush(DIRTY_BACKGROUND) )
{
peboBackground->vInitBrush(dco.pdc,
gpbrBackground,
palDestDC, palDest, pSurf,
(flCaps & GCAPS_ARBRUSHOPAQUE) ? TRUE : FALSE);
}
dco.vCleanBrush(DIRTY_TEXT|DIRTY_BACKGROUND);
}
// exclude the pointer
DEVEXCLUDEOBJ dxo(dco,&co.erclExclude(),&co);
//
// Draw background shapes that are too complex for DrvTextOut.
//
POINTL *pptlBO = &dco.pdc->ptlFillOrigin();
// There's an extra layer of conditional here so that simple text can just
// jump over it all.
if (flState &
(TS_DRAW_OPAQUE_RECT | TS_DRAW_OPAQUE_PGM | TS_DRAW_BACKGROUND_PGM))
{
if ((flState & TS_DRAW_OPAQUE_RECT) && !rclInput.bEmpty())
{
// intersect the dest rect with the clip rect and set it in co
// we can only get away with touching co.rclBounds after
// the ECLIPOBJ constructor because of two reasons:
// a) the target rectangle passed to bitblt is contained in the
// original bounds set by ECLIPOBJ, being the intersection
// of the origianal bounds with THE intended target rectangle.
// b) clipping complexity may have changed when we changed
// co.erclExclude, but it only could have gotten simpler,
// so at worst in those rare situations we would not go
// through the optimal code path.
// By changing clipping bounds we accomplish that no intersection
// of the target rectangle with clipping region rectangle is emtpy
// relieving the driver of extra work [bodind]
co.erclExclude().left = max(rclExclude.left,rclInput.left);
co.erclExclude().right = min(rclExclude.right,rclInput.right);
co.erclExclude().top = max(rclExclude.top,rclInput.top);
co.erclExclude().bottom = min(rclExclude.bottom,rclInput.bottom);
// if not clipped, Just paint the rectangle.
if ((co.erclExclude().left < co.erclExclude().right) &&
(co.erclExclude().top < co.erclExclude().bottom))
{
// Inc target surface uniqueness
INC_SURF_UNIQ(pSurf);
TextOutBitBlt(pSurf,
rfo,
(SURFOBJ *) NULL,
(SURFOBJ *) NULL,
&co,
NULL,
&co.rclBounds,
(POINTL *) NULL,
(POINTL *) NULL,
(BRUSHOBJ *)peboBackground,
pptlBO,
0x0000f0f0);
}
co.erclExclude() = rclExclude;
}
if (flState & (TS_DRAW_OPAQUE_PGM | TS_DRAW_BACKGROUND_PGM))
{
PATHMEMOBJ po;
if (po.bValid())
{
if (flState & TS_DRAW_OPAQUE_PGM)
{
if (!po.bAddPolygon(XFORMNULL,(POINTL *) &ptfxInput[0],4))
{
flState = 0;
}
}
if (flState & TS_DRAW_BACKGROUND_PGM)
{
if (!po.bAddPolygon(XFORMNULL,(POINTL *) aptfxBackground,4))
{
flState = 0;
}
}
if (flState & (TS_DRAW_OPAQUE_PGM | TS_DRAW_BACKGROUND_PGM))
{
if (!po.bTextOutSimpleFill(dco,
rfo,
&pdo,
pSurf,
&co,
(BRUSHOBJ *)peboBackground,
pptlBO,
(R2_COPYPEN << 8) | R2_COPYPEN,
WINDING))
{
flState = 0;
}
} // if (flState & (TS_DRAW_OPAQUE_PGM | TS_DRAW_BACKGROUND_PGM))
} // if (po.bValid())
} // if (flState & (TS_DRAW_OPAQUE_PGM | TS_DRAW_BACKGROUND_PGM))
} // if (flState & (TS_DRAW_OPAQUE_RECT | TS_DRAW_OPAQUE_PGM | TS_DRAW_BACKGROUND_PGM))
//
// Draw the background rect, text, and extra rectangles.
//
if (flState & TS_DRAW_TEXT)
{
ERECTL *prclSimExtra = NULL;
// Prepare for a vector font simulation. Note that we'll also need to
// simulate the background and extra rectangles.
if (bPathFont)
{
flCaps &= ~(GCAPS_OPAQUERECT);
flState |= (rfo.bReturnsOutlines()) ? TS_DRAW_OUTLINE_FONT:TS_DRAW_VECTOR_FONT;
}
// Simulate a background rectangle.
if ((prclBackground != (RECTL *) NULL) &&
!(flCaps & GCAPS_OPAQUERECT))
{
// intersect the dest rect with the clip rect and set it in co
// we can only get away with touching co.rclBounds after
// the ECLIPOBJ constructor because of two reasons:
// a) the target rectangle passed to bitblt is contained in the
// original bounds set by ECLIPOBJ, being the intersection
// of the origianal bounds with THE intended target rectangle.
// b) clipping complexity may have changed when we changed
// co.erclExclude, but it only could have gotten simpler,
// so at worst in those rare situations we would not go
// through the optimal code path.
// By changing clipping bounds we accomplish that no intersection
// of the target rectangle with clipping region rectangle is emtpy
// relieving the driver of extra work [bodind]
co.erclExclude().left = max(rclExclude.left,prclBackground->left);
co.erclExclude().right = min(rclExclude.right,prclBackground->right);
co.erclExclude().top = max(rclExclude.top,prclBackground->top);
co.erclExclude().bottom = min(rclExclude.bottom,prclBackground->bottom);
// if not clipped, Just paint the rectangle.
if ((co.erclExclude().left < co.erclExclude().right) &&
(co.erclExclude().top < co.erclExclude().bottom))
{
// Inc target surface uniqueness
INC_SURF_UNIQ(pSurf);
TextOutBitBlt(pSurf,
rfo,
(SURFOBJ *) NULL,
(SURFOBJ *) NULL,
&co,
NULL,
&co.rclBounds,
(POINTL *) NULL,
(POINTL *) NULL,
(BRUSHOBJ *)peboBackground,
pptlBO,
0x0000f0f0);
}
co.erclExclude() = rclExclude;
prclBackground = NULL;
}
// Prepare for the extra rectangle simulation.
// For TTY drivers, we need to pass prclExtraRects
// to the DrvTextOut call.
if ((prclExtraRects != (ERECTL *) NULL) &&
(pdo.ulTechnology() != DT_CHARSTREAM))
{
prclSimExtra = (ERECTL *) prclExtraRects;
prclExtraRects = NULL;
}
// Draw the text.
if (flState & TS_DRAW_VECTOR_FONT)
{
#ifdef FE_SB
if( to.bLinkedGlyphs() )
{
if( ! bProxyDrvTextOut
(
dco,
pSurf,
to,
co,
(RECTL*) NULL,
(RECTL*) NULL,
(BRUSHOBJ*) peboText,
(BRUSHOBJ*) peboBackground,
pptlBO,
rfo,
&pdo,
dco.flGraphicsCaps(),
&rclExclude
))
{
flState = 0;
}
}
else
{
#endif
PATHMEMOBJ po;
if ((!po.bValid()) ||
(!to.bTextToPath(po, dco)) ||
(!po.bTextOutSimpleStroke1(dco,
rfo,
&pdo,
pSurf,
&co,
(BRUSHOBJ *)peboText,
pptlBO,
(R2_COPYPEN | (R2_COPYPEN << 8)))))
{
flState = 0;
}
}
}
else // if (flState & TS_DRAW_VECTOR_FONT)
{
if (pSurf->pdcoAA)
{
RIP("pdcoAA != 0\n");
pSurf->pdcoAA = 0; // let the free build run
}
pfnTextOut = pSurf->pfnTextOut();
// If the pointer to the TextOut function points to SpTextOut then
// we know that AntiAliased text can be handled and we can skip
// the funny business in the else clause
if (pfnTextOut == SpTextOut)
{
if (rfo.prfnt->fobj.flFontType & (FO_GRAY16 | FO_CLEARTYPE_X))
{
pSurf->pdcoAA = &dco;
}
}
else
{
if
(
(rfo.prfnt->fobj.flFontType & FO_GRAY16) &&
(!(dco.flGraphicsCaps() & GCAPS_GRAY16) ||
(rfo.prfnt->fobj.flFontType & FO_CLEARTYPE_X))
)
{
// Inform SpTextOut that this call came from GreExtTextOutW
// for the purpose of rendering anti aliased text on a device
// that does not support it. Remember to set this to zero
// before releasing the surface to other users.
if (pfnTextOut != EngTextOut)
pSurf->pdcoAA = &dco;
pfnTextOut = SpTextOut;
}
} // if (pfnTextOut == SpTextOut) else
if (flState & TS_DRAW_OUTLINE_FONT)
{
#ifdef FE_SB
if( to.bLinkedGlyphs() )
{
if( ! bProxyDrvTextOut
(
dco,
pSurf,
to,
co,
(RECTL*) NULL,
(RECTL*) NULL,
(BRUSHOBJ*) peboText,
(BRUSHOBJ*) peboBackground,
pptlBO,
rfo,
&pdo,
dco.flGraphicsCaps(),
&rclExclude
))
{
flState = 0;
}
}
else
{
#endif
PATHMEMOBJ po;
if ((!po.bValid()) ||
(!to.bTextToPath(po, dco)) ||
( ( po.cCurves > 1 ) &&
!po.bTextOutSimpleFill(dco,
rfo,
&pdo,
pSurf,
&co,
(BRUSHOBJ *)peboText,
pptlBO,
(R2_COPYPEN | (R2_COPYPEN << 8)),
WINDING)))
{
flState = 0;
}
}
}
else // if (flState & TS_DRAW_OUTLINE_FONT)
{
if (flState & TS_DRAW_COMPLEX_UNDERLINES)
{
INC_SURF_UNIQ(pSurf);
// Unfortunately, many drivers destroy the coordinates in our
// GLYPHPOS array. This means that any complex underlining has
// to be calculated now.
PATHMEMOBJ po;
if ((!po.bValid()) ||
(!to.bExtraRectsToPath(po)) ||
#ifdef FE_SB
((to.bLinkedGlyphs() ) ?
(!bProxyDrvTextOut
(
dco,
pSurf,
to,
co,
prclExtraRects,
prclBackground,
peboText,
peboBackground,
pptlBO,
rfo,
(PDEVOBJ *) NULL,
(FLONG) 0L,
&rclExclude
)) :
#endif
(!((*pfnTextOut)
(pSurf->pSurfobj(),
(STROBJ *) &to,
rfo.pfo(),
&co,
prclExtraRects,
prclBackground,
(BRUSHOBJ *)peboText,
(BRUSHOBJ *)peboBackground,
pptlBO,
mix)))
) ||
(!po.bTextOutSimpleFill(dco,
rfo,
&pdo,
pSurf,
&co,
(BRUSHOBJ *)peboText,
pptlBO,
(R2_COPYPEN | (R2_COPYPEN << 8)),
WINDING)))
{
flState = 0;
}
flState &= ~TS_DRAW_COMPLEX_UNDERLINES;
} // if (flState & TS_DRAW_COMPLEX_UNDERLINES)
else
{
// Inc target surface uniqueness
INC_SURF_UNIQ(pSurf);
#ifdef FE_SB
if( to.bLinkedGlyphs() )
{
if( !bProxyDrvTextOut
(
dco,
pSurf,
to,
co,
prclExtraRects,
prclBackground,
peboText,
peboBackground,
pptlBO,
rfo,
&pdo,
(FLONG) 0,
&rclExclude
))
{
flState = 0;
}
}
else
{
#endif
rfo.PreTextOut(dco);
if (!(*pfnTextOut)(pSurf->pSurfobj(),
(STROBJ *) &to,
rfo.pfo(),
&co,
prclExtraRects,
prclBackground,
(BRUSHOBJ *)peboText,
(BRUSHOBJ *)peboBackground,
pptlBO,
mix))
{
flState = 0;
}
rfo.PostTextOut(dco);
}
} // if (flState & TS_DRAW_COMPLEX_UNDERLINES) else
} // if (flState & TS_DRAW_OUTLINE_FONT) else
ASSERTGDI(pSurf != NULL, "pSurf null after EngTextOut");
/* we have seen pSurf being null with privates binaries */
if (pSurf != NULL)
pSurf->pdcoAA = 0; // clear AA state;
} // if (flState & TS_DRAW_VECTOR_FONT) else
// Simulate extra rectangles.
if (prclSimExtra != (ERECTL *) NULL)
{
ERECTL erclOrg;
erclOrg = co.erclExclude();
// Inc target surface uniqueness
INC_SURF_UNIQ(pSurf);
for (; !prclSimExtra->bEmpty(); prclSimExtra++)
{
// intersect the dest rect with the clip rect and set it in co
co.erclExclude().left = max(erclOrg.left,prclSimExtra->left);
co.erclExclude().right = min(erclOrg.right,prclSimExtra->right);
if (co.erclExclude().left >= co.erclExclude().right)
continue;
co.erclExclude().top = max(erclOrg.top,prclSimExtra->top);
co.erclExclude().bottom = min(erclOrg.bottom,prclSimExtra->bottom);
if (co.erclExclude().top >= co.erclExclude().bottom)
{
continue;
}
//
// not completely clipped, do the bitblt
//
TextOutBitBlt(pSurf,
rfo,
(SURFOBJ *) NULL,
(SURFOBJ *) NULL,
&co,
NULL,
&co.rclBounds,
(POINTL *) NULL,
(POINTL *) NULL,
(BRUSHOBJ *)peboText,
pptlBO,
0x0000f0f0);
}
co.erclExclude() = erclOrg;
} // if (prclSimExtra != (ERECTL *) NULL)
} // if (flState & TS_DRAW_TEXT)
//
// Handle complex cases of strikeout and underlines.
//
if (flState & TS_DRAW_COMPLEX_UNDERLINES)
{
PATHMEMOBJ po;
if (po.bValid())
{
if (!to.bExtraRectsToPath(po) ||
!po.bTextOutSimpleFill(dco,
rfo,
&pdo,
pSurf,
&co,
(BRUSHOBJ *)peboText,
pptlBO,
(R2_COPYPEN | (R2_COPYPEN << 8)),
WINDING))
{
flState = 0;
}
}
else // if (po.bValid())
{
flState = 0;
}
} // if (flState & TS_DRAW_COMPLEX_UNDERLINES)
} // if (pSurf != NULL)
} // if (!co.erclExclude().bEmpty())
} // if (!rclExclude.bEmpty())
} // else if (dco.pdc->bActive())
}
else // if (((flOpts == 0) ||
// ((prcl != (RECT *) NULL) &&
// ((flOpts & ~(ETO_CLIPPED | ETO_OPAQUE)) == 0)))
// && !(dco.pdc->bActive() && (flOpts & ETO_CLIPPED)))
{
WARNING1("Invalid flags for ExtTextOut.\n");
flState = 0;
}
}
else
{
flState = 0;
}
//
// flState is filled with all the stuff we need to do.
// If it is 0 we failed.
//
// return(flState /* != 0 */);
return(flState != 0);
}
/******************************Public*Routine******************************\
* BOOL GreGetTextExtentW
*
* Computes the size of the text box in logical coordinates. The text box
* has sides which are parallel to the baseline of the text and its ascent
* direction. The height is measured in the ascent direction. The width
* is measured in the baseline direction. This definition does not change
* for transformed text.
*
* History:
* Sat 14-Mar-1992 05:39:04 -by- Charles Whitmer [chuckwh]
* Wrote it.
\**************************************************************************/
#define PDXNULL (LONG *) NULL
BOOL GreGetTextExtentW(
HDC hdc, // device context
LPWSTR pwsz, // pointer to a UNICODE text string
int cwc, // Count of chars.
PSIZE pSize, // address to return the dimensions of the string to
UINT fl // internal flags
)
{
BOOL bRet = FALSE;
if (cwc == 0)
{
//
// Nothing to do, but we do not fail.
//
pSize->cx = 0;
pSize->cy = 0;
bRet = TRUE;
}
else
{
//
// Lock the DC and set the new attributes.
//
DCOBJ dco(hdc);
if (dco.bValid())
{
RFONTOBJ rfo(dco,FALSE, (fl & GGTE_GLYPH_INDEX) ? RFONT_TYPE_HGLYPH : RFONT_TYPE_UNICODE);
if (rfo.bValid())
{
// fix up glyph indices for bitmap fonts
if (rfo.prfnt->flType & RFONT_TYPE_HGLYPH)
rfo.vFixUpGlyphIndices((USHORT *)pwsz, cwc);
// If we have (escapement != orientation) we return the extents of the
// bounding parallelogram. This is not Windows compatible, but then
// Windows can't draw it either.
LONG lEsc = dco.pdc->lEscapement(); // Only read this after RFONTOBJ!
if ((lEsc != (LONG)rfo.ulOrientation()) &&
((rfo.iGraphicsMode() != GM_COMPATIBLE) ||
(rfo.prfnt->flInfo & FM_INFO_TECH_STROKE)))
{
//
// Get the transform from the DC.
//
EXFORMOBJ xo(dco,WORLD_TO_DEVICE);
//
// The STROBJ will now compute the text alignment, character positions,
// and TextBox.
//
ESTROBJ to(pwsz,
cwc,
dco,
rfo,
xo,
PDXNULL,FALSE,
lEsc,
dco.pdc->lTextExtra(),
dco.pdc->lBreakExtra(),
dco.pdc->cBreak(),
0,
0,
0,
PDXNULL);
if (to.bValid())
{
//
// Transform the TextBox to logical coordinates.
//
bRet = to.bTextExtent(rfo,lEsc,pSize);
}
}
else
{
//
// At this point we have (escapement == orientation) so we can just run
// some pretty trivial code.
//
bRet = rfo.bTextExtent(dco,
pwsz,
cwc,
lEsc,
dco.pdc->lTextExtra(),
dco.pdc->lBreakExtra(),
dco.pdc->cBreak(),
fl,
pSize);
//
// finally if this is compatible mode and a vector font, do win31
// crazyness about text extent: "rotate" cx and cy by esc vector.
// This is totally crazy, and is different from what win31 is doing
// for tt, but it turns out that quatro pro for windows has figured
// this out and that they use this "feature" [bodind]
//
if (bRet &&
lEsc &&
(dco.pdc->iGraphicsMode() == GM_COMPATIBLE) &&
!dco.pdc->bUseMetaPtoD() &&
(rfo.prfnt->flInfo & FM_INFO_TECH_STROKE))
{
EVECTORFL evfl((LONG)pSize->cx, (LONG)pSize->cy);
EFLOATEXT efAngle = lEsc;
efAngle /= (LONG)10;
MATRIX mx;
mx.efM11 = efCos(efAngle);
mx.efM11.vAbs();
mx.efM22 = mx.efM11;
mx.efM12 = efSin(efAngle);
mx.efM12.vAbs();
mx.efM21 = mx.efM12;
mx.efDx.vSetToZero();
mx.efDy.vSetToZero();
EXFORMOBJ xoExt(&mx,COMPUTE_FLAGS | XFORM_FORMAT_LTOL);
if ((bRet = xoExt.bXform(evfl)) != FALSE)
{
evfl.x.vAbs();
evfl.y.vAbs();
bRet = evfl.bToPOINTL(*(POINTL *)pSize);
}
}
}
}
else
{
WARNING("gdisrv!GreGetTextExtentW(): could not lock HRFONT\n");
}
}
}
return(bRet);
}
/******************************Public*Routine******************************\
* RFONTOBJ::bTextExtent (pwsz,lExtra,lBreakExtra,cBreak,cc,fl,psizl) *
* *
* A quick function to compute text extents on the server side. Only *
* handles the case where (escapement==orientation). Call the ESTROBJ *
* version for the other very hard case. *
* *
* Thu 14-Jan-1993 04:00:57 -by- Charles Whitmer [chuckwh] *
* Wrote it. OK, so it's a blatant ripoff of my bComputeTextExtent from *
* the client side. Fine. *
\**************************************************************************/
#define CTE_BATCH 82
BOOL RFONTOBJ::bTextExtent(
XDCOBJ &dco,
LPWSTR pwsz,
int cc,
LONG lEsc,
LONG lExtra,
LONG lBreakExtra,
LONG cBreak,
UINT fl,
SIZE *psizl
)
{
LONG fxBasicExtent;
int ii, cNoBackup;
FIX fxCharExtra = 0;
FIX fxBreakExtra;
FIX fxExtra = 0;
GLYPHPOS agpos[CTE_BATCH]; // Default set of GLYPHPOS structures.
// Compute the basic extent. Batch the glyphs through our array.
if (lExtra)
{
fxCharExtra = lCvt(efWtoDBase(),lExtra);
cNoBackup = 0;
}
#ifndef FE_SB
if(lCharInc() == 0)
#endif
{
fxBasicExtent = 0;
// NOTE PERF: This is the loop that PaulB would like to optimize with a
// special cache access function. Why create the GLYPHPOS
// array? [chuckwh]
int cBatch;
int cLeft = cc;
WCHAR *pwc = pwsz;
while (cLeft)
{
cBatch = cLeft;
if (cBatch > CTE_BATCH)
cBatch = CTE_BATCH;
// Get the glyph data.
if (!bGetGlyphMetrics(cBatch,agpos,pwc,&dco))
return(FALSE);
// Sum the advance widths.
for (ii=0; ii<cBatch; ii++)
{
fxBasicExtent += ((EGLYPHPOS *) &agpos[ii])->pgd()->fxD;
// the layout code won't allow lExtra to backup a character behind
// its origin so keep track of the number of times this happens
if( ( fxCharExtra < 0 ) &&
( ((EGLYPHPOS *) &agpos[ii])->pgd()->fxD + fxCharExtra <= 0 ) )
{
cNoBackup += 1;
}
}
cLeft -= cBatch;
pwc += cBatch;
}
}
// Adjust for CharExtra.
if (lExtra)
{
PDEVOBJ pdo(prfnt->hdevConsumer);
ASSERTGDI(pdo.bValid(), "bTextExtentRFONTOBJ(): PDEVOBJ constructor failed\n");
if ( (fl & GGTE_WIN3_EXTENT) && pdo.bDisplayPDEV()
&& (!(prfnt->flInfo & FM_INFO_TECH_STROKE)) )
fxExtra = fxCharExtra * ((lExtra > 0) ? cc : (cc - 1));
else
fxExtra = fxCharExtra * ( cc - cNoBackup );
}
// Adjust for lBreakExtra.
if (lBreakExtra && cBreak)
{
// Track down the break character.
PFEOBJ pfeo(ppfe());
IFIOBJ ifio(pfeo.pifi());
// Compute the extra space in device units.
fxBreakExtra = lCvt(efWtoDBase(),lBreakExtra) / cBreak;
// Windows won't let us back up over a break. Set up the BreakExtra
// to just cancel out what we've already got.
if (fxBreakExtra + fxBreak() + fxCharExtra < 0)
fxBreakExtra = -(fxBreak() + fxCharExtra);
// Add it up for all breaks.
WCHAR wcBreak = (fl & GGTE_GLYPH_INDEX)?
(WCHAR)hgBreak():ifio.wcBreakChar();
WCHAR *pwc = pwsz;
for (ii=0; ii<cc; ii++)
{
if (*pwc++ == wcBreak)
fxExtra += fxBreakExtra;
}
}
// Add in the extra stuff.
fxBasicExtent += fxExtra;
// Add in the overhang for font simulations.
if (fl & GGTE_WIN3_EXTENT)
fxBasicExtent += lOverhang() << 4;
// Transform the result to logical coordinates.
if (efDtoWBase_31().bIs1Over16())
psizl->cx = (fxBasicExtent + 8) >> 4;
else
psizl->cx = lCvt(efDtoWBase_31(),fxBasicExtent);
if (efDtoWAscent_31().bIs1Over16())
psizl->cy = lMaxHeight();
else
psizl->cy = lCvt(efDtoWAscent_31(),lMaxHeight() << 4);
#ifdef FE_SB
if( gbDBCSCodePage && // Only in DBCS system locale
(iGraphicsMode() == GM_COMPATIBLE) && // We are in COMPAPIBLE mode
!(flInfo() & FM_INFO_ARB_XFORMS) && // The driver couldnt do arbitrary rotations
!(flInfo() & FM_INFO_TECH_STROKE) && // The driver is not vector driver
(flInfo() & FM_INFO_90DEGREE_ROTATIONS) && // Driver does 90 degree rotations
(lEsc == 900L || lEsc == 2700L) // Current font Escapemant is 900 or 2700
)
{
LONG lSwap = psizl->cx;
psizl->cx = psizl->cy;
psizl->cy = lSwap;
}
#endif
return(TRUE);
}
/******************************Public*Routine******************************\
* GreSetTextJustification (hdc,lBreakExtra,cBreak) *
* *
* Sets the amount of extra spacing we'd like to add for each break (space) *
* character to (lBreakExtra/cBreak) in logical coordinates. *
* *
* History: *
* Fri 13-Mar-1992 02:25:12 -by- Charles Whitmer [chuckwh] *
* Wrote it. *
\**************************************************************************/
BOOL APIENTRY
NtGdiSetTextJustification(
HDC hdc,
int lBreakExtra, // Space in logical units to be added to the line.
int cBreak // Number of break chars in the line.
)
{
BOOL bRet;
DCOBJ dco(hdc);
if ((bRet = dco.bValid()) != FALSE)
{
dco.pdc->lBreakExtra(lBreakExtra);
dco.pdc->cBreak(cBreak);
}
return(bRet);
}
/******************************Public*Routine******************************\
* BOOL GreGetTextExtentExW *
* *
* Determines the number of characters in the input string that fit into *
* the given max width (with the widths computed along the escapement *
* vector). The partial widths (the distance from the string origin to *
* a given character with the width of that character included) for each of *
* character. *
* *
* Returns: *
* TRUE if successful, FALSE otherwise. *
* *
* History: *
* Sat 14-Mar-1992 06:03:32 -by- Charles Whitmer [chuckwh] *
* Rewrote with new ESTROBJ technology. *
* *
* 06-Jan-1992 -by- Gilman Wong [gilmanw] *
* Wrote it. *
\**************************************************************************/
BOOL GreGetTextExtentExW(
HDC hdc, // device context
LPWSTR pwsz, // pointer to a UNICODE text string
COUNT cwc, // count of WCHARs in the string
ULONG dxMax, // maximum width to return
COUNT *pcChars, // number of chars that fit in dxMax
PULONG pdxOut, // offset of each character from string origin
LPSIZE pSize, // return height and width of string
FLONG fl
)
{
BOOL bRet = FALSE;
PVOID pv;
#ifdef DUMPCALL
DbgPrint("\nGreGetTextExtentExW(" );
DbgPrint("\n HDC hdc = %-#8lx\n", hdc );
DbgPrint("\n LPWSTR pwsz = %-#8lx -> \"%ws\"\n", pwsz ,pwsz );
DbgPrint("\n COUNT cwc = %d\n", cwc );
DbgPrint("\n ULONG dxMax = %-#8lx\n", dxMax );
DbgPrint("\n COUNT *pcChars = %-#8lx\n", pcChars);
DbgPrint("\n PULONG pdxOut = %-#8lx\n", pdxOut );
DbgPrint("\n LPSIZE pSize = %-#8lx\n", pSize );
DbgPrint("\n )\n" );
#endif
// Parameter validation.
if ( ((pwsz == (LPWSTR) NULL) && (cwc != 0))
|| (pSize == (LPSIZE) NULL) )
{
WARNING("gdisrv!GreGetTextExtentExW(): invalid parameter\n");
SAVE_ERROR_CODE(ERROR_INVALID_PARAMETER);
return (FALSE);
}
// Early out.
if (cwc == 0L) // Nothing to do, but we do not fail.
{
if ( pcChars != (COUNT *) NULL )
{
*pcChars = 0;
}
return(TRUE);
}
// Lock the DC and set the new attributes.
DCOBJ dco(hdc);
if (!dco.bValid())
{
WARNING("gdisrv!GreGetTextExtentExW(): invalid HDC\n");
SAVE_ERROR_CODE(ERROR_INVALID_HANDLE);
}
else
{
// Get the transform.
EXFORMOBJ xo(dco, WORLD_TO_DEVICE);
// Realize font and get the simulation flags and escapement.
RFONTOBJ rfo(dco, FALSE, (fl & GTEEX_GLYPH_INDEX) ? RFONT_TYPE_HGLYPH : RFONT_TYPE_UNICODE);
if (!rfo.bValid())
{
WARNING("gdisrv!GreGetTextExtentExW(): could not lock HRFONT\n");
}
else
{
if (rfo.prfnt->flType & RFONT_TYPE_HGLYPH)
rfo.vFixUpGlyphIndices((USHORT *)pwsz, cwc);
// If there is no pdxOut buffer provided, but we still need one to compute the
// number of characters that fit (pcChars not NULL), then we will have to
// allocate one of our own.
#define DXOUTLEN 40
ULONG dxOut[DXOUTLEN];
PULONG pdxAlloc = NULL;
if ((pdxOut == (PULONG) NULL) && (pcChars != (COUNT *) NULL))
{
if (cwc <= DXOUTLEN)
{
pdxOut = &dxOut[0];
}
else
{
if ((pdxAlloc = (PULONG) PALLOCMEM(cwc * sizeof(ULONG), 'txtG')) == (PULONG) NULL)
{
WARNING("gdisrv!GreGetTextExtentExW(): could not alloc temp buffer\n");
SAVE_ERROR_CODE(ERROR_NOT_ENOUGH_MEMORY);
}
pdxOut = pdxAlloc;
}
}
// The STROBJ will now compute the text alignment, character positions,
// and TextBox.
ESTROBJ to(
pwsz,cwc,
dco,
rfo,
xo,PDXNULL,FALSE, // xo, PDXNULL, bPdy = FALSE
dco.pdc->lEscapement(),
dco.pdc->lTextExtra(),
dco.pdc->lBreakExtra(),
dco.pdc->cBreak(),
0,0,0,(LONG *) pdxOut
);
if (to.bValid())
{
// Transform the TextExtent to logical coordinates. Because this is a
// new NT function, we need not worry about the extent compatibility hack.
if (to.bTextExtent(rfo,0L,pSize))
{
// Count number of characters that fit in the max. width.
// If pcChars is NULL, we skip this and ignore the dxMax limit.
if (pcChars && pdxOut)
{
ULONG c;
for (c=0; c<cwc && *pdxOut<=dxMax; c++,pdxOut++)
{}
*pcChars = c;
}
bRet = TRUE;
}
}
// Free temp buffer.
if (pdxAlloc)
VFREEMEM(pdxAlloc);
}
}
return(bRet);
}
/******************************Public*Routine******************************\
* BOOL GreConsoleTextOut
*
* Write text with no spacing and alignment options, thereby saving a ton
* of time.
*
* History:
* Fri 12-Nov-1993 -by- Patrick Haluptzok [patrickh]
* Smaller and Faster
*
* Wed 16-Sep-1992 17:36:17 -by- Charles Whitmer [chuckwh]
* Duplicated GrePolyTextOut, and then deleted the unneeded code.
\**************************************************************************/
extern "C" BOOL GreConsoleTextOut(
HDC hdc,
POLYTEXTW *lpto, // Ptr to array of polytext structures
UINT nStrings, // number of polytext structures
RECTL *prclBounds
)
{
// make sure CSR is calling us
if (PsGetCurrentProcess() != gpepCSRSS)
return(FALSE);
//
// Assume we will succeed, set Failure if we don't
//
BOOL bRet = TRUE;
//
// Lock the DC.
//
DCOBJ dco(hdc);
if (dco.bValid())
{
//
// Accumulate bounds. We can do this before knowing if
// the operation is successful because bounds can be loose.
//
if (dco.bAccum())
dco.erclBounds() |= *prclBounds;
//
// Lock the Rao region.
//
DEVLOCKOBJ dlo;
if (dlo.bLock(dco))
{
//
// Locate the font realization.
//
RFONTOBJ rfo;
rfo.vInit(dco,FALSE);
if (rfo.bValid() && !rfo.bPathFont())
{
POINTL ptlOrigin;
ptlOrigin = dco.eptlOrigin();
SURFACE *pSurf = dco.pSurface();
XEPALOBJ palDest(pSurf->ppal());
XEPALOBJ palDestDC(dco.ppal());
POINTL *pptlBO = &dco.pdc->ptlFillOrigin();
EBRUSHOBJ *peboText = dco.peboText();
EBRUSHOBJ *peboBackground = dco.peboBackground();
BOOL bDitherText = FALSE;
if ( dco.flGraphicsCaps() & GCAPS_ARBRUSHTEXT )
{
// Even if a printer driver sets GCAPS_ARBRUSHTEXT, we
// can't allow vector fonts to be dithered.
bDitherText = (!rfo.bPathFont() || rfo.bReturnsOutlines());
// We always dirty the text brush if ARBRUSHTEXT is set to
// catch the cases where we transition between vector and
// non-vector fonts but keep the same brush -- if we didn't
// do this, we might try to use a cached, dithered brush
// after switching from a TrueType font to a vector font.
dco.ulDirtyAdd(DIRTY_TEXT);
// Get through the are-you-really-dirty check in vInitBrush:
peboText->vInvalidateUniqueness();
}
if ( dco.bDirtyBrush(DIRTY_TEXT|DIRTY_BACKGROUND) )
{
if ( dco.bDirtyBrush(DIRTY_TEXT) )
{
peboText->vInitBrush(dco.pdc,
gpbrText,
palDestDC, palDest,
pSurf,
bDitherText);
}
if ( dco.bDirtyBrush(DIRTY_BACKGROUND) )
{
peboBackground->vInitBrush(
dco.pdc,
gpbrBackground,
palDestDC, palDest, pSurf,
(dco.flGraphicsCaps() & GCAPS_ARBRUSHOPAQUE) ?
TRUE : FALSE);
}
dco.vCleanBrush(DIRTY_TEXT|DIRTY_BACKGROUND);
}
//
// Compute the clipping complexity and maybe reduce the exclusion
// rectangle. The bounding rectangle must be converted to Screen
// coordinates.
//
ERECTL rclExclude;
rclExclude.left = prclBounds->left + ptlOrigin.x;
rclExclude.right = prclBounds->right + ptlOrigin.x;
rclExclude.top = prclBounds->top + ptlOrigin.y;
rclExclude.bottom = prclBounds->bottom + ptlOrigin.y;
ECLIPOBJ co(
dco.prgnEffRao(),
rclExclude,
(rfo.prfnt->fobj.flFontType & FO_CLEARTYPE_X) ? CLIP_FORCE : CLIP_NOFORCE
);
rclExclude = co.erclExclude();
//
// Check the destination which is reduced by clipping.
//
if (!rclExclude.bEmpty())
{
DEVEXCLUDEOBJ dxo(dco,&rclExclude,&co);
//
// We now begin the 'Big Loop'. We will pass thru this loop once for
// each entry in the array of PolyText structures. Increment the
// pSurface once before we enter. We assume success from here on out
// unless we hit a failure.
//
INC_SURF_UNIQ(pSurf);
PFN_DrvBitBlt pfnBitBlt = pSurf->pfnBitBlt();
PFN_DrvTextOut pfnTextOut;
if
(
((rfo.prfnt->fobj.flFontType & FO_GRAY16) && !(dco.flGraphicsCaps() & GCAPS_GRAY16)) ||
(rfo.prfnt->fobj.flFontType & FO_CLEARTYPE_X)
)
{
pfnTextOut = SpTextOut;
pSurf->pdcoAA = &dco; // make sure this is needed
}
else
{
pfnTextOut = pSurf->pfnTextOut();
}
ERECTL rclInput;
for (POLYTEXTW *ppt = lpto; ppt < lpto + nStrings; ppt += 1)
{
//
// Process the rectangle in prcl.
//
rclInput.left = ppt->rcl.left + ptlOrigin.x;
rclInput.right = ppt->rcl.right + ptlOrigin.x;
rclInput.top = ppt->rcl.top + ptlOrigin.y;
rclInput.bottom = ppt->rcl.bottom + ptlOrigin.y;
//
// Process the string.
//
if (ppt->n)
{
//
// The STROBJ will now compute the text alignment,
// character positions, and TextBox.
//
ESTROBJ to;
to.vInitSimple(
(PWSZ) ppt->lpstr,
ppt->n,
dco,
rfo,ppt->x+ptlOrigin.x,
ppt->y+ptlOrigin.y,NULL);
if (to.bValid())
{
// Draw the text.
#ifdef FE_SB
if( to.bLinkedGlyphs() )
{
// If there are linked glyphs, then the bounds of the
// glyphs (to.rclBkGround) might exceed the bounds of the
// clipping object (co.rclBounds). If so, then we need
// to increase the complexity of the clipping object from
// DC_TRIVIAL to DC_RECT.
if ((co.iDComplexity == DC_TRIVIAL) &&
((to.rclBkGround.left < co.rclBounds.left) ||
(to.rclBkGround.right > co.rclBounds.right) ||
(to.rclBkGround.top < co.rclBounds.top) ||
(to.rclBkGround.bottom > co.rclBounds.bottom)))
{
co.iDComplexity = DC_RECT;
}
bProxyDrvTextOut
(
dco,
pSurf,
to,
co,
(RECTL *) NULL,
&rclInput,
peboText,
peboBackground,
pptlBO,
rfo,
(PDEVOBJ*)NULL,
(FLONG) 0L,
&rclExclude
);
}
else
{
#endif
(*pfnTextOut)(pSurf->pSurfobj(),
(STROBJ *) &to,
rfo.pfo(),
&co,
(RECTL *) NULL,
&rclInput,
peboText,
peboBackground,
pptlBO,
(R2_COPYPEN | (R2_COPYPEN << 8)));
}
}
else
{
bRet = FALSE;
break;
}
}
else
{
// intersect the dest rect with the clip rect and set it in co
// we can only get away with touching co.rclBounds after
// the ECLIPOBJ constructor because of two reasons:
// a) the target rectangle passed to bitblt is contained in the
// original bounds set by ECLIPOBJ, being the intersection
// of the origianal bounds with THE intended target rectangle.
// b) clipping complexity may have changed when we changed
// co.erclExclude, but it only could have gotten simpler,
// so at worst in those rare situations we would not go
// through the optimal code path.
// By changing clipping bounds we accomplish that no intersection
// of the target rectangle with clipping region rectangle is emtpy
// relieving the driver of extra work [bodind]
co.erclExclude().left = max(rclExclude.left,rclInput.left);
co.erclExclude().right = min(rclExclude.right,rclInput.right);
co.erclExclude().top = max(rclExclude.top,rclInput.top);
co.erclExclude().bottom = min(rclExclude.bottom,rclInput.bottom);
// if not clipped, Just paint the rectangle.
if ((co.erclExclude().left < co.erclExclude().right) &&
(co.erclExclude().top < co.erclExclude().bottom))
{
(*pfnBitBlt)(pSurf->pSurfobj(),
(SURFOBJ *) NULL,
(SURFOBJ *) NULL,
&co,
NULL,
&co.rclBounds,
(POINTL *) NULL,
(POINTL *) NULL,
(BRUSHOBJ *)peboBackground,
pptlBO,
0x0000f0f0);
}
co.erclExclude() = rclExclude;
}
}
pSurf->pdcoAA = NULL;
}
}
else
{
WARNING("gdisrv!GreExtTextOutW(): could not lock HRFONT\n");
bRet = FALSE;
}
}
else
{
bRet = dco.bFullScreen();
}
}
else
{
bRet = FALSE;
WARNING("Invalid DC passed to GreConsoleTextOut\n");
}
return(bRet);
}
/******************************Public*Routine******************************\
* DWORD GreSetTextAlign (hdc,flOpts) *
* *
* Set the text alignment flags in the DC. *
* *
* History: *
* *
* Tue 28-Dec-1993 -by- Patrick Haluptzok [patrickh] *
* smaller and faster *
* *
* 18-Dec-1990 -by- Donald Sidoroff [donalds] *
* Wrote it. *
\**************************************************************************/
UINT APIENTRY GreSetTextAlign(HDC hdc,UINT flOpts)
{
ULONG ulReturn = 0;
XDCOBJ dco( hdc );
if(!dco.bValid())
{
WARNING("Invalid DC or offset passed to GreSetTextAlign\n");
}
else
{
ulReturn = (UINT)dco.pdc->lTextAlign();
dco.pdc->lTextAlign(flOpts);
if (MIRRORED_DC(dco.pdc) && ((flOpts & TA_CENTER) != TA_CENTER)) {
flOpts = flOpts ^ TA_RIGHT;
}
dco.pdc->flTextAlign(flOpts & (TA_UPDATECP | TA_CENTER | TA_BASELINE));
dco.vUnlockFast();
}
return((UINT)ulReturn);
}
/******************************Public*Routine******************************\
* int GreSetTextCharacterExtra (hdc,lExtra) *
* *
* Sets the amount of intercharcter spacing for TextOut. *
* *
* History: *
* Tue 28-Dec-1993 -by- Patrick Haluptzok [patrickh] *
* smaller and faster *
* *
* Tue 08-Jan-1991 -by- Bodin Dresevic [BodinD] *
* Update: bug, used to return lExtra instead of lOld, transform stuff *
* deleted since it will be done at the TextOut time. *
* *
* 18-Dec-1990 -by- Donald Sidoroff [donalds] *
* Wrote it. *
\**************************************************************************/
int APIENTRY GreSetTextCharacterExtra(HDC hdc,int lExtra)
{
ULONG ulOld = 0x80000000;
XDCOBJ dco( hdc );
if(!dco.bValid())
{
WARNING("Invalid DC or offset passed to GreSetTextCharacterExtra\n");
}
else
{
ulOld = dco.pdc->lTextExtra();
dco.pdc->lTextExtra(lExtra);
dco.vUnlockFast();
}
return(ulOld);
}
/******************************Public*Routine******************************\
* int GreGetTextCharacterExtra (hdc) *
* *
* Gets the amount of intercharcter spacing for TextOut. *
* *
* 29-Jun-1995 -by- Fritz Sands [fritzs] *
* Wrote it. *
\**************************************************************************/
int APIENTRY GreGetTextCharacterExtra(HDC hdc)
{
ULONG ulOld = 0;
XDCOBJ dco( hdc );
if(!dco.bValid())
{
WARNING("Invalid DC or offset passed to GreGetTextCharacterExtra\n");
}
else
{
ulOld = dco.pdc->lTextExtra();
dco.vUnlockFast();
}
return(ulOld);
}
/******************************Public*Routine******************************\
*
* CalcJustInArray
*
* Effects: mimics win95 asm code, except that their code does not have
* if (b_lpDx) clause, for all of their arrays are 16 bit.
*
* if GCP_JUSTIFYIN flag is set, the lpDx array on input contains
* justifying priorities.
* For latin this means the lpDx array will contain
* 0's or 1's where 0 means that the glyph at this position can not be
* used for spacing while 1 means that the glyph at this position should
* be used for spacing. If GCP_JUSTIFYIN is NOT set, than space chars ' ',
* in the input string are used to do justification.
*
* History:
* 21-Jul-1995 -by- Bodin Dresevic [BodinD]
* Wrote it.
\**************************************************************************/
DWORD nCalcJustInArray(
UINT **ppJustIn, // place to store the pointer to array
WCHAR Glyph, // glyph to find
VOID *pvIn, // input array
BOOL b_lpDx, // input array is lpDx or pwc
UINT cGlyphs // length of the input array
)
{
WCHAR *pwc, *pwcEnd;
int *pDx, *pDxEnd;
int iGlyph;
COUNT nJustIn = 0;
UINT *piInit;
// look for Glyph in the string
if (b_lpDx)
{
iGlyph = (int)Glyph;
pDxEnd = (int*)pvIn + cGlyphs;
for (pDx = (int*)pvIn; pDx < pDxEnd; pDx++)
{
if (*pDx == iGlyph)
nJustIn++;
}
}
else
{
pwcEnd = (WCHAR*)pvIn + cGlyphs;
for (pwc = (WCHAR*)pvIn; pwc < pwcEnd; pwc++)
{
if (*pwc == Glyph)
nJustIn++;
}
}
if ((nJustIn == 0) || // did not find any Glyphs in the string
!(piInit = (UINT *)PALLOCMEM(nJustIn * sizeof(UINT), 'ylgG')))
{
*ppJustIn = NULL;
return 0;
}
// store locations where Glyph's are found in the input array
UINT *pi = piInit;
if (b_lpDx)
{
for (pDx = (int*)pvIn; pDx < pDxEnd; pDx++)
{
if (*pDx == iGlyph)
{
//Sundown: safe to truncate since pDxEnd = pvIn + cGlyphs
*pi++ = (UINT)(pDx - (int*)pvIn);
}
}
}
else
{
for (pwc = (WCHAR*)pvIn; pwc < pwcEnd; pwc++)
{
if (*pwc == Glyph)
{
//Sundown: same as above
*pi++ = (UINT)(pwc - (WCHAR*)pvIn);
}
}
}
// return the pointer with array of locations of uiGlyphs
*ppJustIn = piInit;
return nJustIn;
}
/******************************Public*Routine******************************\
*
* VOID RFONTOBJ::vFixUpGlyphIndices(USHORT *pgi, UINT cgi)
*
* Effects: Windows 95 returns glyph indices for bitmap fonts that are the
* same as ansi values. On NT glyph handles are zero based, so
* we need to add chFirstChar to NT handles to get win95 indices
* which is what we do in GetGlyphIndicesA/W and GetCharacterPlacement.
* Conversely, when those indices are passed to us through
* text routines we have to subtract chFirstChar from indices to
* produce NT handles. This is what this routine does:
*
* History:
* 04-Mar-1997 -by- Bodin Dresevic [BodinD]
* Wrote it.
\**************************************************************************/
VOID RFONTOBJ::vFixUpGlyphIndices(USHORT *pgi, UINT cgi)
{
USHORT usFirst = prfnt->ppfe->pifi->chFirstChar;
ASSERTGDI(prfnt->flType & RFONT_TYPE_HGLYPH, "vFixUpGlyphIndices\n");
ASSERTGDI(prfnt->ppfe->pfdg, "RFONTOBJ::vFixUpGlyphIndices invalid ppfe->pfdg \n");
if ((prfnt->ppfe->pfdg->flAccel & GS_8BIT_HANDLES) && usFirst)
{
// win95 does not return true glyph indicies but ansi
// values for raster, vector, ps fonts
for (USHORT *pgiEnd = pgi + cgi; pgi < pgiEnd; pgi++)
*pgi -= usFirst;
}
}
/******************************Public*Routine******************************\
*
* GreGetGlyphIndicesW (
*
* Effects: designed to emulate win95 behavior
*
* Warnings:
*
* History:
* 25-Jul-1995 -by- Bodin Dresevic [BodinD]
* Wrote it.
\**************************************************************************/
DWORD GreGetGlyphIndicesW (
HDC hdc,
WCHAR *pwc,
DWORD cwc,
USHORT *pgi,
DWORD iMode,
BOOL bSubset
)
{
DWORD dwRet = GDI_ERROR;
HGLYPH *phg, *phgInit;
USHORT *pgiEnd = pgi + cwc;
XDCOBJ dco(hdc); // Lock the DC.
if (dco.bValid()) // Check if it's good.
{
// Locate the RFONT.
// It might be better to set the type to RFONT_TYPE_HGLYPH
// in anticipation of ETO_GLYPH_INDEX ExtTextOut calls
RFONTOBJ rfo(dco, FALSE, RFONT_TYPE_UNICODE);
if (rfo.bValid())
{
USHORT usFirst = rfo.prfnt->ppfe->pifi->chFirstChar;
// if cwc == 0 all we are care for is the # of distinct glyph indices
if (cwc==0)
{
ASSERTGDI(iMode == 0, "GreGetGlyphIndicesW parameters bogus\n");
if (rfo.prfnt->ppfe->pifi->cjIfiExtra > offsetof(IFIEXTRA,cig))
{
dwRet = ((IFIEXTRA *)(rfo.prfnt->ppfe->pifi + 1))->cig;
}
else
{
dwRet = 0;
}
}
else
{
// if we ever switch to 16 bit HGLYPHS in ddi, this alloc will no
// longer be necessary [bodind]
if (phgInit = (phg = (HGLYPH *)PALLOCMEM(cwc * sizeof(HGLYPH), 'ylgG')))
{
rfo.vXlatGlyphArray(pwc, cwc, phg, iMode, bSubset);
// separate loops for faster processing:
ASSERTGDI(rfo.prfnt->ppfe->pfdg, "GreGetGlyphIndicesW invalid ppfe->pfdg \n");
if (rfo.prfnt->ppfe->pfdg->flAccel & (GS_8BIT_HANDLES|GS_16BIT_HANDLES))
{
if ((rfo.prfnt->ppfe->pfdg->flAccel & GS_8BIT_HANDLES) && usFirst)
{
// win95 does not return true glyph indicies but ansi
// values for raster, vector, ps fonts
for ( ; pgi < pgiEnd; pgi++, pwc++, phg++)
*pgi = (USHORT)*phg + usFirst;
}
else
{
for ( ; pgi < pgiEnd; pgi++, pwc++, phg++)
*pgi = (USHORT)*phg;
}
dwRet = cwc; // can not fail any more
}
else
{
dwRet = GDI_ERROR;
}
VFREEMEM(phgInit);
}
}
}
dco.vUnlockFast();
}
return dwRet;
}
/******************************Public*Routine******************************\
*
* DWORD GreGetCharacterPlacementW
*
*
*
* History:
* 06-Jan-1995 -by- Bodin Dresevic [BodinD]
* Wrote it.
\**************************************************************************/
DWORD GreGetCharacterPlacementW(
HDC hdc,
LPWSTR pwsz,
DWORD nCountIn,
DWORD nMaxExtent,
LPGCP_RESULTSW pResults,
DWORD dwFlags
)
{
SIZE size;
GCP_RESULTSW gcpw;
DWORD nCount = nCountIn;
DWORD dwWidthType = 0;
int *pDx = NULL;
UINT *pJustIn = NULL;
DWORD dwJustInOff = 0; // used by calc routine for spacing
DWORD nJustIn = 0;
ULONG nKern = 0;
KERNINGPAIR *pKern = NULL;
KERNINGPAIR *pKernSave = NULL; // esential initialization
int nExtentLeft = 0;
int nExtentRem = 0;
WORD *pwc,*pwcEnd;
DWORD i, j;
// init size
size.cx = size.cy = 0;
// we will only be implementing the simple version of this api,
// that is for now we will not be calling LPK dlls
if (!pResults)
{
if (!GreGetTextExtentW(hdc, (LPWSTR)pwsz, (int)nCount, &size, GGTE_WIN3_EXTENT))
{
WARNING("GreGetCharacterPlacementW, GreGetTextExtentW failed\n");
return 0;
}
// now do unthinkable win95 stuff, chop off 32 bit values to 16 bits
return (DWORD)((USHORT)size.cx) | (DWORD)(size.cy << 16);
}
// main code starts here. We are following win95 code as closely as possible.
// Copy pResults to the stack, for faster access I presume.
gcpw = *pResults;
// take nCount to be the smaller of the nCounts and nGlyphs
if (nCount > gcpw.nGlyphs)
nCount = gcpw.nGlyphs;
// Calc pJustIn array if any
if (dwFlags & GCP_JUSTIFY) // if have this
dwFlags |= GCP_MAXEXTENT; // then must also have this
if ((dwFlags & GCP_JUSTIFYIN) && gcpw.lpDx)
{
// if this flag is set, the lpDx array on input contains
// justifying priorities so we can not continue if lpDx is not present.
// For latin this means the lpDx array will contain
// 0's or 1's where 0 means that the glyph at this position can not be
// used for spacing while 1 means that the glyph at this position should
// be used for spacing. If GCP_JUSTIFYIN is NOT set, than space chars ' ',
// in the string are used to do justification.
// Now that we have everything in place we can call CalcJustInArray
// to compute the array in pJustIn
nJustIn = nCalcJustInArray(&pJustIn, 1,
(VOID *)gcpw.lpDx, TRUE ,gcpw.nGlyphs);
if (!nJustIn)
{
// if this computation fails must pretend that this flag is not set
dwFlags &= ~GCP_JUSTIFYIN;
}
}
else
{
// either GCP_JUSTIFYIN not set or lpDx is NULL input,
// in either case can kill the bit
dwFlags &= ~GCP_JUSTIFYIN;
}
// we could have either the lpDx, lpCaretPos or neither or both. Take this
// into account and call GetTextExtentEx
// we could or could not be asking for a maximum. If we are, put it in the
// local version of the results structure.
if (gcpw.lpDx)
dwWidthType += 1; // bogus way of doing it, i.e. win95 way
if (gcpw.lpCaretPos)
dwWidthType += 2; // bogus way of doing it, i.e. win95 way
// dwWidthType can be 0,1,2,3
pDx = gcpw.lpDx;
if (dwWidthType == 2) // CaretPos only
pDx = gcpw.lpCaretPos; // reset the array pointer
// Check if the count should be reduced even further
COUNT *pnCount = NULL;
if (dwFlags & GCP_MAXEXTENT)
{
pnCount = (COUNT*)&nCount;
}
// now call GetTextExtentEx
if (!GreGetTextExtentExW(
hdc, // device context
(LPWSTR)pwsz, // pointer to a UNICODE text string
nCount, // count of WCHARs in the string
(ULONG)nMaxExtent, // maximum width to return
pnCount, // number of chars that fit in dxMax
(PULONG)pDx, // offset of each character from string origin
&size,0))
{
if (pJustIn)
VFREEMEM(pJustIn);
return 0;
}
// these few lines of code do not exist in Win95, presumably because
// their internal version of GetTextExtentExW does not return positions
// of glyphs (relative to the first glyph) but character increments along
// baseline.
if (pDx && (nCount > 0))
{
for (int * pDxEnd = &pDx[nCount - 1]; pDxEnd > pDx; pDxEnd--)
{
*pDxEnd -= pDxEnd[-1];
}
}
if ((dwFlags & GCP_MAXEXTENT) && (nCount == 0))
{
if (pJustIn)
VFREEMEM(pJustIn);
return (DWORD)((USHORT)size.cx) | (DWORD)(size.cy << 16);
}
// Kerning:
// It only makes sense to do kerning if there are more than 2 glyphs
if ((dwFlags & GCP_USEKERNING) && (dwWidthType != 0) && (nCount >= 2))
{
// Get the number of kerning pairs, if zero done
if (nKern = GreGetKerningPairs(hdc,0,NULL))
{
if (pKernSave = (KERNINGPAIR*)PALLOCMEM(nKern * sizeof(KERNINGPAIR), 'txtG'))
{
// consistency check for GetKerningPairs:
if (GreGetKerningPairs(hdc,nKern,pKernSave) != nKern)
{
// something is gone wrong, out of here
if (pJustIn)
VFREEMEM(pJustIn);
if (pKernSave)
VFREEMEM(pKernSave);
return 0;
}
KERNINGPAIR *pKernEnd = pKernSave + nKern;
for (pKern = pKernSave; pKern < pKernEnd; pKern++)
{
// now go over the sting and find all the instances of
// THIS kerning pair in the string:
register WORD wcFirst = pKern->wFirst;
// note that this is the loop for trying the wcFirst
// so that the end condition is &pwsz[nCount - 2],
// wcSecond could go up to &pwsz[nCount - 1],
// Either I do not understand Win95 code or they have
// a bug in that they could fault on trying to access the
// the second glyph in a pair in the input string
pwcEnd = (WORD*)pwsz + (nCount - 1);
for (pwc = (WORD *)pwsz; pwc < pwcEnd; pwc++)
{
if ((wcFirst == pwc[0]) && (pwc[1] == pKern->wSecond))
{
// found a kerning pair in the string,
// we need to modify the pDx vector for the second
// glyph in the kerning pair
pDx[pwc - (WORD *)pwsz] += pKern->iKernAmount;
// also adjust the return value accordingly:
size.cx += pKern->iKernAmount;
}
}
} // on to the next pair
// done with kerning pairs can free the memory
VFREEMEM(pKernSave);
// if we have kerned positive amounts, then the string could well
// have gone over the preset limit. If so, reduce the number of
// characters we found [win95 comment]
if (dwFlags & GCP_MAXEXTENT)
{
while (((DWORD)size.cx > nMaxExtent) && (nCount > 0))
{
// point to the last glyph in the string
size.cx -= pDx[nCount - 1];
nCount -= 1;
}
// see if there are any glyphs left to process
if (nCount == 0)
{
if (pJustIn)
VFREEMEM(pJustIn);
pResults->nGlyphs = nCount;
pResults->nMaxFit = (int)nCount;
return 0;
}
}
} // no memory
} // no kern pairs
}
// Justification, check flags and the presence of array
if ((dwFlags & GCP_JUSTIFY) && dwWidthType && (nCount > 0))
{
int *pDxEnd = &pDx[nCount - 1]; // must have nCount > 0 for this
// check trailing spaces, adjust nCount further to remove trailing spaces
for
(
pwcEnd = (WORD*)&pwsz[nCount-1];
(pwcEnd >= (WORD*)pwsz) && (*pwcEnd == L' ');
nCount--, pwcEnd--, pDxEnd--
)
{
size.cx -= *pDxEnd;
}
if (nCount == 0)
{
if (pJustIn)
VFREEMEM(pJustIn);
pResults->nGlyphs = nCount;
pResults->nMaxFit = (int)nCount;
return 0;
}
// See if we need to justify.
// Can not justify one character, need at least two...
nExtentLeft = (int)nMaxExtent - (int)size.cx;
if ((nExtentLeft >= 0) && (nCount >= 2))
{
// ... yes, we do need to justify
// if GCP_JUSTIFYIN was set, pJustIn and nJustIn have
// already been set.
if (!nJustIn) // try to use ' ' as a "spacer glyph"
{
nJustIn = nCalcJustInArray(&pJustIn,L' ',
(VOID *)pwsz, FALSE, nCount);
}
if (nJustIn)
{
// Make sure that the array doesn't say to
// space a character beyond the new nCount.
j = nCount - 1; // convert count to index of the last glyph
int ii;
for (ii = (int)(nJustIn - 1); ii >= 0; ii--)
{
if ((UINT)j >= pJustIn[ii])
break;
}
// pJustIn array is zero based, to get the effective number
// of "spacers" in the array must add 1 to the index of the last "spacer" in the array
i = (DWORD)ii + 1;
// run a sort of primitive DDA a'la DavidMS
nExtentRem = (int) (((DWORD)nExtentLeft) % i);
nExtentLeft /= i;
for (j = 0; j < i; j++, nExtentRem--)
{
int dxCor = nExtentLeft;
if (nExtentRem > 0)
dxCor += 1;
pDx[pJustIn[j]] += dxCor;
}
}
else
{
// no spaces. justify by expanding every character evenly.
while (nExtentLeft > 0)
{
// Note the end condition: nCount - 1, rather
// than usual nCount; This is because there is no point
// in adding spaces to the last glyph in the string
j = nCount - 1;
for (i = 0; i < j; i++)
{
pDx[i] += 1;
if (!(--nExtentLeft))
break;
}
}
}
}
#if DBG
else
{
if (nCount < 2)
RIP("GetCharacterPlacement, justification wrong\n");
}
#endif
// we now have exactly this:
size.cx = (LONG)nMaxExtent;
}
// fill other width array, that is CaretPos
if (dwWidthType == 3) // both lpDx and lpCaretPos are non NULL
RtlCopyMemory(gcpw.lpCaretPos, gcpw.lpDx, nCount * sizeof(int));
// caret positioning is from the start of the string,
// not the previous character.
if (gcpw.lpCaretPos)
{
int iCaretPos = 0, iDx = 0;
for (i = 0; i < nCount; i++)
{
iDx = gcpw.lpCaretPos[i];
gcpw.lpCaretPos[i] = iCaretPos;
iCaretPos += iDx;
}
}
// Fix Output String
if (gcpw.lpOutString)
RtlCopyMemory(gcpw.lpOutString, pwsz, nCount * sizeof(WCHAR));
// Classification
if (gcpw.lpClass)
RtlFillMemory(gcpw.lpClass, nCount, GCPCLASS_LATIN);
// Ordering
if (gcpw.lpOrder)
{
for (i = 0; i < nCount; i++)
{
gcpw.lpOrder[i] = i;
}
}
// Get lpGlyphs
if (gcpw.lpGlyphs)
{
if (GreGetGlyphIndicesW(
hdc,(WCHAR*)pwsz,nCount,
(USHORT*)gcpw.lpGlyphs,0, FALSE) == GDI_ERROR)
{
nCount = 0;
size.cx = size.cy = 0;
}
}
// Finally fix counters in the returned structure
if (pJustIn)
VFREEMEM(pJustIn);
pResults->nGlyphs = nCount;
pResults->nMaxFit = nCount;
return (DWORD)((USHORT)size.cx) | (DWORD)(size.cy << 16);
}
/**************************************************************************\
* NtGdiGetWidthTable
*
* Gets a table of character advance widths for a font. Returns SHORTs
* over the C/S interface to save space. (Note that 99+% of all requests
* will be happy with this limitation.)
*
* We will try real hard to get widths for the "special" characters at the
* start of the array. Other widths are returned only when they are not
* expensive. (Expensive is True Type rasterizing a glyph, for example.)
* The value NO_WIDTH is returned for those expensive glyphs.
*
* We return GDI_ERROR (0xFFFFFFFF) in case of an error. TRUE indicates
* that all widths were easy.
*
* History:
* Tue 13-Jun-1995 22:24:49 by Gerrit van Wingerden [gerritv]
* Moved to kernel mode
*
* Mon 11-Jan-1993 22:24:39 -by- Charles Whitmer [chuckwh]
* Wrote it. Sorry about the wierd structure, I'm trying to get good tail
* merging.
\**************************************************************************/
BOOL APIENTRY NtGdiGetWidthTable
(
HDC hdc, // Device context
ULONG cSpecial,
WCHAR *pwc, // Pointer to a UNICODE text codepoints.
ULONG cwc, // Count of chars.
USHORT *psWidth, // Width table (returned).
WIDTHDATA *pwd, // Useful font data (returned).
FLONG *pflInfo // Font info flags.
)
{
ULONG ii;
BOOL bRet = (BOOL) GDI_ERROR;
XDCOBJ dco(hdc); // Lock the DC.
if (dco.bValid()) // Check if it's good.
{
WIDTHDATA wd;
FLONG flInfo;
USHORT *psWidthTmp = NULL;
WCHAR *pwcTmp;
if (!BALLOC_OVERFLOW2(cwc, USHORT, WCHAR))
{
psWidthTmp = (USHORT*) AllocFreeTmpBuffer(cwc*(sizeof(USHORT)+sizeof(WCHAR)));
}
if( psWidthTmp )
{
pwcTmp = (WCHAR*) (psWidthTmp + cwc);
__try
{
ProbeForRead(pwc,sizeof(WCHAR)*cwc,sizeof(WORD));
RtlCopyMemory(pwcTmp,pwc,cwc*sizeof(WCHAR));
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
cwc = 0;
}
}
else
{
WARNING("NtGdiGetWidthTable unable to allocate memory\n");
cwc = 0;
}
if (cwc)
{
// Locate the RFONT.
RFONTOBJ rfo(dco,FALSE);
if (rfo.bValid())
{
// Grab the flInfo flags.
flInfo = rfo.flInfo();
if (rfo.cxMax() < 0xFFF)
{
// Check for a simple case. We still have to fill the table
// because some one may want it.
if (rfo.lCharInc())
{
USHORT *psWidth1 = psWidthTmp;
LONG fxInc = rfo.lCharInc() << 4;
for (ii=0; ii<cwc; ii++)
*psWidth1++ = (USHORT) fxInc;
bRet = TRUE;
}
else
{
bRet = rfo.bGetWidthTable(dco,cSpecial,pwcTmp,cwc,psWidthTmp);
}
// If things are going well, get the WIDTHDATA.
if (bRet != GDI_ERROR)
{
if (!rfo.bGetWidthData(&wd,dco))
bRet = (BOOL) GDI_ERROR;
}
}
}
else
{
WARNING("gdisrv!GreGetWidthTable(): could not lock HRFONT\n");
}
}
if( bRet != GDI_ERROR )
{
__try
{
ProbeForWrite(psWidth,sizeof(USHORT)*cwc,sizeof(USHORT));
RtlCopyMemory(psWidth,psWidthTmp,cwc*sizeof(USHORT));
if( pwd )
{
ProbeForWrite(pwd,sizeof(WIDTHDATA),sizeof(DWORD));
RtlCopyMemory(pwd,&wd,sizeof(WIDTHDATA));
}
ProbeForWrite(pflInfo,sizeof(FLONG),sizeof(DWORD));
RtlCopyMemory(pflInfo,&flInfo,sizeof(FLONG));
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
bRet = GDI_ERROR;
}
}
if (psWidthTmp)
{
FreeTmpBuffer( psWidthTmp );
}
dco.vUnlockFast();
}
return(bRet);
}
/******************************Public*Routine******************************\
* iGetPublicWidthTable()
*
*
* History:
* 28-Feb-1996 -by- Eric Kutter [erick]
* Wrote it.
\**************************************************************************/
EFLOAT_S ef_16 = EFLOAT_16;
EFLOAT_S ef_1_16 = EFLOAT_1Over16;
int iGetPublicWidthTable(
HDC hdc)
{
ULONG ii = MAX_PUBLIC_CFONT; // this means failure
BOOL bRet = FALSE;
HLFONT hf;
XDCOBJ dco(hdc); // Lock the DC.
if (dco.bValid()) // Check if it's good.
{
// only want to support no transforms
if ((dco.pdc->ulMapMode() == MM_TEXT) && (dco.ulDirty() & DISPLAY_DC))
{
// Get the hfont and check if it is public
hf = dco.pdc->hlfntNew();
if ((hf != NULL) &&
GreGetObjectOwner((HOBJ)hf,LFONT_TYPE) == OBJECT_OWNER_PUBLIC)
{
RFONTOBJ rfo(dco,FALSE);
if (rfo.bValid() && (rfo.cxMax() < 0xFFF))
{
// lets see if we can find an available cpf
for (ii = 0; ii < MAX_PUBLIC_CFONT; ++ii)
{
if (gpGdiSharedMemory->acfPublic[ii].hf == 0)
break;
if (gpGdiSharedMemory->acfPublic[ii].hf == (HFONT)hf)
{
WARNING("iGetPublicWidthTable - font already in public list\n");
ii = MAX_PUBLIC_CFONT;
}
}
if (ii < MAX_PUBLIC_CFONT)
{
PCFONT pcf = &gpGdiSharedMemory->acfPublic[ii];
pcf->timeStamp = gpGdiSharedMemory->timeStamp;
// Grab the flInfo flags.
pcf->flInfo = rfo.flInfo();
// Check for a simple case. We still have to fill the table
// because some one may want it.
if (rfo.lCharInc())
{
USHORT usInc = (USHORT)(rfo.lCharInc() << 4);
for (ii=0; ii<256; ii++)
pcf->sWidth[ii] = usInc;
bRet = TRUE;
}
else
{
WCHAR wch[256];
LFONTOBJ lfo(hf);
if (lfo.bValid())
{
if (IS_ANY_DBCS_CHARSET(lfo.plfw()->lfCharSet))
{
ULONG uiCodePage = ulCharsetToCodePage((UINT)lfo.plfw()->lfCharSet);
for (ii = 0; ii < 256; ++ii)
{
UCHAR j = (UCHAR) ii;
EngMultiByteToWideChar(uiCodePage,
&wch[ii],
sizeof(WCHAR),
(LPSTR)&j,
1);
}
}
else
{
UCHAR ach[256];
ULONG BytesReturned;
for (ii = 0; ii < 256; ++ii)
ach[ii] = (UCHAR)ii;
RtlMultiByteToUnicodeN(wch,
sizeof(wch),
&BytesReturned,
(PCHAR)ach,
sizeof(ach));
}
bRet = rfo.bGetWidthTable(dco,256,wch,256,pcf->sWidth);
if (bRet == GDI_ERROR)
bRet = FALSE;
}
}
// If things are going well, get the WIDTHDATA , metrics and
// RealizationInfo
DCOBJ dcof(hdc);
if (bRet &&
rfo.bGetWidthData(&pcf->wd,dco) &&
#ifdef LANGPACK
rfo.GetRealizationInfo(&pcf->ri) &&
#endif
bGetTextMetrics(rfo, dcof, &pcf->tmw)
)
{
pcf->fl = CFONT_COMPLETE |
CFONT_CACHED_METRICS |
CFONT_CACHED_WIDTHS |
#ifdef LANGPACK
CFONT_CACHED_RI |
#endif
CFONT_PUBLIC;
// since we are always MM_TEXT, the xform is identity
pcf->efM11 = ef_16;
pcf->efM22 = ef_16;
pcf->efDtoWBaseline = ef_1_16;
pcf->efDtoWAscent = ef_1_16;
pcf->hf = (HFONT)hf;
pcf->hdc = 0;
pcf->cRef = 0;
pcf->lHeight = FXTOL((LONG) pcf->wd.sHeight);
}
else
{
// if got error... we should retuen MAX_PUBLIC_CFONT..
// At this point "ii" is not equal to MAX_PUBLIC_CFONT, it
// points first free entry of cache table...
// Just force set it to error..
ii = MAX_PUBLIC_CFONT;
}
}
}
else
{
WARNING("gdisrv!GreGetWidthTable(): could not lock HRFONT\n");
}
}
}
dco.vUnlockFast();
}
return(ii);
}
/******************************Public*Routine******************************\
* NtGdiSetupPublicCFONT()
*
* Modify the cached public cfont.
*
* if HDC is non-null, textmetrics must be set
* if HF is non-null, cached ave width must be set
*
* returns index of cfont modified
*
* History:
* 23-Feb-1996 -by- Eric Kutter [erick]
* Wrote it.
\**************************************************************************/
int APIENTRY NtGdiSetupPublicCFONT(
HDC hdc,
HFONT hf,
ULONG ulAve)
{
int ii = MAX_PUBLIC_CFONT;
// get metrics and widths if necesary
if (hdc)
{
ii = iGetPublicWidthTable(hdc);
}
// now see if we need to fill in the ave width
if (hf)
{
// if we havn't found it yet, find it
if (ii == MAX_PUBLIC_CFONT)
{
for (ii = 0; ii < MAX_PUBLIC_CFONT; ++ii)
{
if (gpGdiSharedMemory->acfPublic[ii].hf == hf)
break;
}
}
if (ii < MAX_PUBLIC_CFONT)
{
gpGdiSharedMemory->acfPublic[ii].ulAveWidth = ulAve;
gpGdiSharedMemory->acfPublic[ii].fl |= CFONT_CACHED_AVE;
}
}
return(ii);
}
UINT APIENTRY GreGetTextAlign(HDC hdc)
{
XDCOBJ dco( hdc );
if(dco.bValid())
{
UINT uTextAlign = dco.pdc->lTextAlign();
dco.vUnlockFast();
return uTextAlign;
}
else
{
WARNING("GreGetTextAlign: invalid DC\n");
return(0);
}
}