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.
 
 
 
 
 
 

1278 lines
42 KiB

//////////////////////////////////////////////////////////////////////////////
// //
// Module Name : LPK_GDI.c //
// //
// Entry points (formal interfaces) for GDI32 to call //
// and route their APIs, so that we can implement our language-specific //
// features. //
// //
// Created : Oct 24, 1996 //
// Author : Mohamed AbdEl Hamid [mhamid] //
// //
// Copyright (c) 1996, Microsoft Corporation. All rights reserved. //
//////////////////////////////////////////////////////////////////////////////
#include "precomp.hxx"
//// FontHasWesternScript
//
// Detect if the current selected font in the hdc has Western script or not by using
// the cached data g_FontIDCache. and also add the selected font to the cache it it is
// not cached before.
// All calles used inside this function is Client-mode calls except if the font will be
// cached then calling GetGlyphIndices (Kernel-mode call) will take place.
// This function used in the optimization checking for ExtTextOut and GetTextExtent.
//
// entry hdc - Device context
//
// return value: TRUE if font has western script. FALSE otherwise.
//
BOOL FontHasWesternScript(HDC hdc)
{
REALIZATION_INFO ri;
int i;
WORD Glyphs[4];
BOOL fRet;
if (!GdiRealizationInfo(hdc, &ri)) {
return FALSE;
}
EnterCriticalSection(&csFontIdCache);
if (g_cCachedFontsID > 0) {
for (i=0 ; i<g_cCachedFontsID ; i++) {
if (ri.uFontFileID == g_FontIDCache[i].uFontFileID) {
fRet = g_FontIDCache[i].bHasWestern;
LeaveCriticalSection(&csFontIdCache);
return (fRet);
}
}
}
if ((GetGlyphIndicesW(hdc , L"dMr\"" , 4 , Glyphs , GGI_MARK_NONEXISTING_GLYPHS) == 4) &&
(Glyphs[0] != 0xFFFF && Glyphs[1] != 0xFFFF && Glyphs[2] != 0xFFFF && Glyphs[3] != 0xFFFF)) {
g_FontIDCache[g_pCurrentAvailablePos].bHasWestern = fRet = TRUE;
} else {
g_FontIDCache[g_pCurrentAvailablePos].bHasWestern = fRet = FALSE;
}
g_FontIDCache[g_pCurrentAvailablePos].uFontFileID = ri.uFontFileID ;
g_pCurrentAvailablePos++;
if (g_pCurrentAvailablePos >= MAX_FONT_ID_CACHE) {
g_pCurrentAvailablePos = 0;
}
if (g_cCachedFontsID < MAX_FONT_ID_CACHE) {
g_cCachedFontsID++;
}
LeaveCriticalSection(&csFontIdCache);
return (fRet);
}
//// InternalTextOut
//
// Display text with possible font association
//
// entry hdc - Device context
// x,y - Starting coords (Unless TA_UPDATECP)
// uOptions - Flags (see below)
// prc - Pointer to clipping rectangle
// pString - Unicode string
// cbCount - String length in unicode characters
// pdx - Overriding logical dx array
// iCharset - Original ANSI iCharset, or -1 if unicode
//
// exit TRUE if string drawn OK
//
// options ETO_CLIPPED - Clip to clipping rectangle
// ETO_OPAQUE - Extend background colour to bounds of clipping rectangle
// ETO_RTLREADING - Render text with right to left reading order
// ETO_NUMERICSLOCAL
// ETO_NUMERICSLATIN
// ETO_PDY - lpdx array contains DX,DY pairs - causes LPK to be bypassed
//
// note LpkExtTextOut also obeys options set by SetTextAlign:
// TA_LEFT - x,y is position of left edge of displayed glyphs
// TA_CENTRE - x,y is position at centre of diplayed glyphs
// TA_RIGHT - x,y is position at right edge of displayed glyphs
// TA_RTLREADING - Render text with right to left reading order
// TA_UPDATECP - Get x,y from current position, ignoring x,y parameters,
// update current position following textout.
//
// history Oct 22, 1996 -by- Samer Arafeh [samera]
// Oct 24, 1996 -by- Mohamed AbdEl Hamid [mhamid]
// Feb 18, 1887 dbrown - Support font association
///// InternalTextOut
//
//
BOOL InternalTextOut(
HDC hdc,
int x,
int y,
UINT uOptions,
const RECT *prc,
const WCHAR *pStr,
UINT cbCount,
const int *piDX,
int iCharset,
int *piWidth,
int iRequiredWidth) {
BOOL fRTLreading;
int iDigitSubstitute;
int iTextAlign;
HRESULT hr;
DWORD dwObjType;
DWORD dwSicFlags; // Flags for ScriptIsComplex
int iCurrentCharSet;
STRING_ANALYSIS *psa;
UNREFERENCED_PARAMETER(iRequiredWidth) ;
if (!cbCount || !pStr) {
// Empty string - no glyph processing required. Optimise ...
return ExtTextOutW(hdc, x, y, uOptions|ETO_GLYPH_INDEX, prc,
pStr, cbCount, piDX);
}
// ETO_PDY is not relevant for complex script strings. Let GDI
// handle it. (APps wanting to adjust the y coordinate of glyphs in
// complex script strings should use Uniscribe and manipulate the
// pGoffset parameter to ScriptTextOut.
if (uOptions & ETO_PDY) {
return ExtTextOutW(hdc, x, y, uOptions | ETO_IGNORELANGUAGE, prc,
pStr, cbCount, piDX);
}
// Establish Bidi reading order.
//
// Note, it is possible for us to be passed an hdc that does not
// support GetTextAlign, in which case GetTextAlign will return -1.
// Treat this as left to right reading order.
fRTLreading = ((uOptions & ETO_RTLREADING)
|| (((iTextAlign = GetTextAlign(hdc)) & TA_RTLREADING) && (iTextAlign != -1)))
? TRUE : FALSE;
// Interpret ETO_NUMERICS* flags:
// If both bits are set, the digit substitute = context. This is a win95
// compatability issue and is used mainly by Access.
if ((uOptions&(ETO_NUMERICSLOCAL|ETO_NUMERICSLATIN)) == (ETO_NUMERICSLOCAL|ETO_NUMERICSLATIN)) {
iDigitSubstitute = SCRIPT_DIGITSUBSTITUTE_CONTEXT;
} else if (uOptions & ETO_NUMERICSLOCAL) {
iDigitSubstitute = SCRIPT_DIGITSUBSTITUTE_TRADITIONAL;
} else if (uOptions & ETO_NUMERICSLATIN) {
iDigitSubstitute = SCRIPT_DIGITSUBSTITUTE_NONE;
} else {
iDigitSubstitute = -1;
}
uOptions = uOptions & ~(ETO_NUMERICSLOCAL | ETO_NUMERICSLATIN);
// Check for plain text that can bypass the LPK entirely
dwSicFlags = SIC_COMPLEX;
if ( iDigitSubstitute == SCRIPT_DIGITSUBSTITUTE_CONTEXT
|| iDigitSubstitute == SCRIPT_DIGITSUBSTITUTE_TRADITIONAL
|| g_DigitSubstitute.DigitSubstitute != SCRIPT_DIGITSUBSTITUTE_NONE) {
dwSicFlags |= SIC_ASCIIDIGIT;
}
if (fRTLreading != !!(GetLayout(hdc) & LAYOUT_RTL)) {
dwSicFlags |= SIC_NEUTRAL;
}
if (( ScriptIsComplex(pStr,cbCount,dwSicFlags) == S_FALSE
&& FontHasWesternScript(hdc))
|| GetTextCharacterExtra(hdc) != 0)
{
// No complex script processing required
return ExtTextOutW(hdc, x, y, uOptions | ETO_IGNORELANGUAGE, prc,
pStr, cbCount, piDX);
}
dwObjType = GetObjectType(hdc);
// Analyse the string
hr = LpkStringAnalyse(
hdc, pStr, cbCount, 0, -1,
SSA_GLYPHS
| (dwObjType == OBJ_METADC || dwObjType == OBJ_ENHMETADC ? SSA_DONTGLYPH : 0)
| (iCharset==-1 || GdiIsPlayMetafileDC(hdc) ? SSA_FALLBACK : SSA_LPKANSIFALLBACK)
| (fRTLreading ? SSA_RTL : 0),
iDigitSubstitute, iRequiredWidth,
NULL, NULL,
piDX,
NULL, NULL,
&psa);
if (FAILED(hr)) {
ASSERTHR(hr, ("InternalTextOut - LpkStringAnalyse"));
return FALSE;
}
// Return string width if required (for DrawText)
if (piWidth) {
*piWidth = psa->size.cx;
}
hr = ScriptStringOut(psa, x, y, uOptions, prc, 0, 0, FALSE);
ScriptStringFree((void**)&psa);
if (SUCCEEDED(hr)) {
return TRUE;
} else {
ASSERTHR(hr, ("InternalTextOut - ScriptStringOut"));
return FALSE;
}
}
BOOL LpkExtTextOut(
HDC hdc,
int x,
int y,
UINT uOptions,
CONST RECT *prc,
PCWSTR pStr,
UINT cbCount,
CONST INT *pDx,
int iCharset) {
return InternalTextOut(hdc, x, y, uOptions, prc, pStr, cbCount, pDx, iCharset, NULL, -1);
}
//////////////////////////////////////////////////////////////////////////////
// GDI32 GetTextExtentExPoint will call this function for supporting //
// Multilingual Text handling. //
// //
// LpkGetTextExtentExPoint( HDC hdc, PWSTR pStr, int cchString, //
// int nMaxExtent, PINT pnFit, PINT pDx, PSIZE pSize, int iCharset) //
// //
// hDC Identifies the device context //
// pStr Points to string for which extents are to be retrieved. //
// cchString Count of characters in input string //
// nMaxExtent Specifies the maximum allowable width, in logical units, //
// of the formatted string. //
// pnFit Maximum characters that fit in the formatted string //
// When the pnFit parameter is NULL, the nMaxExtent parameter //
// is ignored. //
// //
// pDx address of array for partial string widths //
// pSize Address for string dimension //
// fl ???? //
// iCharset Indicates character set of codes. to optimizing the work. ??//
// //
// Return //
// If the function succeeds, the return value is TRUE. //
// If the function fails, the return value is FALSE. //
// And we seted the error by call SetLastError. //
// To get extended error information, call GetLastError. //
// //
// History //
// Oct 22, 1996 -by- Samer Arafeh [samera] //
// Oct 25, 1996 -by- MOhammed Abdul Hammed [mhamid] //
//////////////////////////////////////////////////////////////////////////////
BOOL LpkGetTextExtentExPoint(
HDC hdc,
PCWSTR pStr,
int cchString,
int nMaxExtent,
PINT pnFit,
PINT pDx,
PSIZE pSize,
FLONG fl,
int iCharset)
{
int iTextAlign;
BOOL fRTLreading;
int i;
HRESULT hr;
DWORD dwObjType;
DWORD dwSicFlags; // Flags for ScriptIsComplex
int iCurrentCharSet;
STRING_ANALYSIS *psa;
STRING_ANALYSIS *psaFit;
UNREFERENCED_PARAMETER(fl) ;
// Check required parameters
if (!hdc || !pSize) {
ASSERTS(hdc, ("LpkGetTextExtentPoint - required parameter hdc is NULL"));
ASSERTS(pSize, ("LpkGetTextExtentPoint - required parameter pSize is NULL"));
return FALSE;
}
//Do we have a string
if (!cchString || !pStr) {
//no then go away
pSize->cx = 0;
pSize->cy = 0;
if (pnFit) {
*pnFit = 0;
}
return TRUE;
}
iTextAlign = GetTextAlign(hdc);
fRTLreading = (iTextAlign & TA_RTLREADING) && (iTextAlign != -1);
// Check for plain text that can bypass the LPK entirely
dwSicFlags = SIC_COMPLEX;
if (g_DigitSubstitute.DigitSubstitute != SCRIPT_DIGITSUBSTITUTE_NONE) {
dwSicFlags |= SIC_ASCIIDIGIT;
}
if (fRTLreading != !!(GetLayout(hdc) & LAYOUT_RTL)) {
dwSicFlags |= SIC_NEUTRAL;
}
if (( ScriptIsComplex(pStr, cchString, dwSicFlags) == S_FALSE
&& FontHasWesternScript(hdc))
|| GetTextCharacterExtra(hdc) != 0)
{
// No complex script processing required
return GetTextExtentExPointWPri(hdc, pStr, cchString, nMaxExtent, pnFit, pDx, pSize);
}
dwObjType = GetObjectType(hdc);
// Analyse the string
hr = LpkStringAnalyse(
hdc, pStr, cchString, 0, -1,
SSA_GLYPHS
// if the DC is Meta-File DC, we should enable the FallBack because it is enabled for ETOA while playing any Meta-File.
| (iCharset==-1 || dwObjType == OBJ_METADC || dwObjType == OBJ_ENHMETADC ? SSA_FALLBACK : SSA_LPKANSIFALLBACK)
| (fRTLreading ? SSA_RTL : 0)
| (pnFit ? SSA_FULLMEASURE : 0),
-1, nMaxExtent,
NULL, NULL, NULL, NULL, NULL,
&psa);
if (FAILED(hr)) {
ASSERTHR(hr, ("LpkGetTextExtentExPoint - LpkStringAnalyse"));
return FALSE;
}
if (pDx) {
// if we have pnFit and psa->cOutChars>=cchString so we should fill lpDx.
if (!pnFit || psa->cOutChars>=cchString) {
ScriptStringGetLogicalWidths(psa, pDx);
}
// we need to update the width of last fit glyph.
if (pnFit && psa->cOutChars<cchString && psa->cOutChars>0) {
hr = LpkStringAnalyse(
hdc, pStr, psa->cOutChars, 0, -1,
SSA_GLYPHS
// if the DC is Meta-File DC, we should enable the FallBack because it is enabled for ETOA while playing any Meta-File.
| (iCharset==-1 || dwObjType == OBJ_METADC || dwObjType == OBJ_ENHMETADC ? SSA_FALLBACK : SSA_LPKANSIFALLBACK)
| (fRTLreading ? SSA_RTL : 0),
-1, 0,
NULL, NULL, NULL, NULL, NULL,
&psaFit);
if (FAILED(hr)) {
ScriptStringFree((void**)&psa);
ASSERTHR(hr, ("LpkGetTextExtentExPoint - LpkStringAnalyse"));
return FALSE;
}
ScriptStringGetLogicalWidths(psaFit, pDx);
ScriptStringFree((void**)&psaFit);
}
// Accumulate extents
for (i=1; i<(pnFit==NULL?cchString:psa->cOutChars); i++) {
pDx[i] += pDx[i-1];
}
}
if (pnFit) {
*pnFit = psa->cOutChars;
}
*pSize = psa->size;
ScriptStringFree((void**)&psa);
return TRUE;
}
//// GetCharacterPlacement support
//
//
//// GCPgenerateOutString
//
// Creates a reordered copy of the input string
void GCPgenerateOutString(
STRING_ANALYSIS *psa,
WCHAR *pwOutString) {
int i,j;
int iItem;
int iStart;
int iLen;
WCHAR *pwch;
// Copy items one by one in visual order
for (i=0; i<psa->cItems; i++) {
iItem = psa->piVisToLog[i];
iStart = psa->pItems[iItem].iCharPos;
iLen = psa->pItems[iItem+1].iCharPos - iStart;
if (psa->pItems[iItem].a.fRTL) {
// Right to left item
pwch = psa->pwInChars + iStart + iLen - 1;
for (j=0; j<iLen; j++) {
*pwOutString++ = *pwch--;
}
} else {
// Left to right item
memcpy(pwOutString, psa->pwInChars+iStart, sizeof(WCHAR) * iLen);
pwOutString += iLen;
}
}
}
//// GCPgenerateClass
//
// Creates an array of character classifications using
// GetCharacterPlacement legacy definitons
void GCPgenerateClass(
STRING_ANALYSIS *psa,
BYTE *pbClass) {
int iItem;
int iStart;
int iLen;
int iClass;
int iChar;
// Map LogClust entries item by item in logical order
for (iItem=0; iItem<psa->cItems; iItem++) {
iStart = psa->pItems[iItem].iCharPos;
iLen = psa->pItems[iItem+1].iCharPos - iStart;
if (g_ppScriptProperties[psa->pItems[iItem].a.eScript]->fNumeric) {
if (psa->pItems[iItem].a.fLayoutRTL) {
iClass = GCPCLASS_LOCALNUMBER;
} else {
iClass = GCPCLASS_LATINNUMBER;
}
} else {
if (psa->pItems[iItem].a.fLayoutRTL) {
iClass = GCPCLASS_ARABIC; // (Same constant as GCPCLASS_HEBREW)
} else {
iClass = GCPCLASS_LATIN;
}
}
memset(pbClass, iClass, iLen);
pbClass += iLen;
}
}
/******************************Public*Routine******************************\
* GCPJustification
*
* Justifies text according to piJustify and returns proper pwgi and piDx
* arrays.
*
* IMPORTANT : Caller should free (USPFREE(*ppwgi)) allocated buffer if
* the fn succeeds (SUCCEEDED(hr)) and return code isn't S_FALSE.
* S_FALSE means no justification is to be applied here since the piJustify and
* piDx are either identical or the total width to justify for is less than the
* min kashida width.
*
* All param are DWORD aligned.
*
* History :
*
* Mar 23, 1998 -by- Samer Arafeh [samera]
* wrote it
\**************************************************************************/
#define BlankPriority 10
HRESULT GCPJustification( WORD **ppwgi, // Out Output buffer width justified glyphs
int **ppiJustifyDx, // Out Newly generated piDx buffer
WORD *pwgi, // In Incoming GIs
const int *piAdvWidth, // In Advance wdiths
const SCRIPT_VISATTR *pVisAttr, // In Visual attributes
int *piJustify, // In Justification advanced widths
int cGlyphs, // In number of glyphs
int iKashidaWidth, // In Minimum width of kashida
int *pcJustifiedGlyphs, // Out Receives the total # of glyphs in output buf
DWORD dwgKashida, // In Kashida GI
DWORD dwgSpace) // In Space GI
{
DWORD dwSize;
int iInsert=0L, iGlyph, iAmount, iJustDx;
int cNewGlyphs = cGlyphs;
WORD *pwNewGlyph;
int *piNewAdvWidth;
int cMaxGlyphs = *pcJustifiedGlyphs;
int cNonArabicGlyph;
int cNonBlank;
int iDelta;
HRESULT hr;
INT iPartialKashida;
//
// Point to caller's data initially
//
*ppwgi = pwgi;
*ppiJustifyDx = (INT *)piJustify;
*pcJustifiedGlyphs = cGlyphs;
//
// If Kashida width is less than or equal 0, then justify with spaces only.
//
if(iKashidaWidth <= 0L) {
iKashidaWidth = -1;
}
//
// 1- Analyze input buffer to see how many kashida to insert, If Kashida is used.
//
if (iKashidaWidth != -1 ) {
cNonArabicGlyph = 0;
cNonBlank = 0;
iDelta = 0;
for( iGlyph=cGlyphs-1 ; iGlyph >= 0L ; iGlyph-- )
{
if( (pVisAttr[iGlyph].uJustification == SCRIPT_JUSTIFY_NONE) ||
(pVisAttr[iGlyph].uJustification >= SCRIPT_JUSTIFY_ARABIC_NORMAL)) {
iAmount = piJustify[iGlyph]-piAdvWidth[iGlyph];
if (iAmount > 0 && cNewGlyphs < cMaxGlyphs){
iPartialKashida = iAmount % iKashidaWidth;
iAmount /= iKashidaWidth;
if (iPartialKashida > 0 && iAmount>0)
{
iAmount++;
}
if (cNewGlyphs + iAmount > cMaxGlyphs) {
iAmount = (cMaxGlyphs - cNewGlyphs) * iKashidaWidth;
cNewGlyphs = cMaxGlyphs;
iDelta += piJustify[iGlyph] - piAdvWidth[iGlyph] - iAmount;
piJustify[iGlyph] = piAdvWidth[iGlyph] + iAmount;
} else {
cNewGlyphs += iAmount;
}
} else {
iDelta += iAmount;
piJustify[iGlyph] = piAdvWidth[iGlyph];
}
} else {
if( (pVisAttr[iGlyph].uJustification == SCRIPT_JUSTIFY_ARABIC_BLANK) ||
(pVisAttr[iGlyph].uJustification == SCRIPT_JUSTIFY_BLANK)) {
cNonBlank++;
} else {
cNonArabicGlyph++;
}
}
}
if (iDelta > 0 && cNonArabicGlyph+cNonBlank>0) {
// The Space has 10-times higher priority than Latin characters.
iAmount = iDelta / (cNonArabicGlyph + (cNonBlank * BlankPriority));
for( iGlyph=0 ; iGlyph < cGlyphs ; iGlyph++ )
{
if( (pVisAttr[iGlyph].uJustification != SCRIPT_JUSTIFY_NONE) &&
(pVisAttr[iGlyph].uJustification < SCRIPT_JUSTIFY_ARABIC_NORMAL)) {
if( (pVisAttr[iGlyph].uJustification == SCRIPT_JUSTIFY_ARABIC_BLANK) ||
(pVisAttr[iGlyph].uJustification == SCRIPT_JUSTIFY_BLANK)) {
piJustify[iGlyph] += BlankPriority * iAmount;
iDelta -= BlankPriority * iAmount;
} else {
piJustify[iGlyph] += iAmount;
iDelta -= iAmount;
}
cNonArabicGlyph = iGlyph;
}
}
if (iDelta > 0) {
piJustify[cNonArabicGlyph] += iDelta;
}
}
}
//
// 2- Allocate for new glyphs and piDx
//
dwSize = (cNewGlyphs * (sizeof(INT)+sizeof(WORD)));
hr = USPALLOCTEMP( dwSize , (void **)&pwNewGlyph );
if(FAILED(hr))
{
ASSERTHR(hr, ("Not ennough memory for JustifiyArabicStringWithKashida()"));
return hr;
}
piNewAdvWidth = (INT *)(pwNewGlyph+cNewGlyphs);
//
// 3- Begin inserting and formulating the justified buffer
//
int iJustReminder = 0;
for( iGlyph=cGlyphs-1, iInsert=cNewGlyphs-1; iGlyph >= 0L && iInsert>=0; iGlyph-- )
{
iJustDx = (piJustify[iGlyph] - piAdvWidth[iGlyph]) + iJustReminder;
iJustReminder = 0;
if( iJustDx > 0)
{
if( (pVisAttr[iGlyph].uJustification == SCRIPT_JUSTIFY_NONE) ||
(pVisAttr[iGlyph].uJustification >= SCRIPT_JUSTIFY_ARABIC_NORMAL))
{
//Arabic glyph then justify with kashida
if(( iJustDx >= iKashidaWidth ) && (iKashidaWidth != -1))
{
pwNewGlyph[iInsert] = pwgi[iGlyph];
piNewAdvWidth[iInsert] = piAdvWidth[iGlyph];
iInsert--;
while( (iJustDx >= iKashidaWidth) && (iInsert >= 0L) )
{
pwNewGlyph[iInsert] = (WORD)dwgKashida;
piNewAdvWidth[iInsert] = iKashidaWidth;
iInsert--;
iJustDx -= iKashidaWidth;
}
if(( iJustDx > 0L ) && (iInsert >= 0L))
{
pwNewGlyph[iInsert] = (WORD)dwgKashida;
piNewAdvWidth[iInsert] = iJustDx;
iInsert--;
iJustDx = 0L;
}
}
else
{
pwNewGlyph[iInsert] = pwgi[iGlyph];
piNewAdvWidth[iInsert] = piAdvWidth[iGlyph];
iJustReminder = iJustDx;
iInsert--;
}
}
else
{
pwNewGlyph[iInsert] = pwgi[iGlyph];
piNewAdvWidth[iInsert] = piAdvWidth[iGlyph] + iJustDx;
iInsert--;
}
}
else
{
pwNewGlyph[iInsert] = pwgi[iGlyph];
piNewAdvWidth[iInsert] = piJustify[iGlyph];
iInsert--;
}
}
//
// In case there is a space glyph, it will be expanded in locatio rather
// than inserting kashida GIs
//
while( iInsert >= 0L )
{
piNewAdvWidth[iInsert] = 0L;
pwNewGlyph[iInsert] = (WORD)dwgSpace;
iInsert--;
}
//
// 4- Update results
//
*ppwgi = pwNewGlyph;
*ppiJustifyDx = piNewAdvWidth;
*pcJustifiedGlyphs = cNewGlyphs;
return S_OK;
}
//////////////////////////////////////////////////////////////////////////////
// GDI32 GetCharacterPlacement will call this function for //
// supporting Multilingual Text handling. //
// //
// LpkGetCharacterPlacement( HDC hdc, PWSTR pStr, int nCount, //
// int nMaxExtent, LPGCP_RESULTSW pResults, DWORD dwFlags, //
// int iCharset) //
// //
// hDC : Handle to device context //
// pStr : Input string //
// nCount : Count of characters in input string //
// nMaxExtent : Maximum width for formatting string //
// pResults : Pointer to GCP_RESULTS strucutre for output //
// dwFlags : GCP Processing Flags //
// iCharset : Origianl character set of pStr //
// //
// Return //
// If the function succeeds, the return value is Width an the Height //
// of the string //
// If the function fails, the return value is 0. //
// And we seted the error by call SetLastError. //
// To get extended error information, call GetLastError. //
// //
// History : //
// Oct 22, 1996 -by- Samer Arafeh [samera] //
// Oct 29, 1996 -by- MOhammed Abdul Hammed [mhamid] //
// Jan 13, 1997 -by- David C Brown (dbrown) //
// New justification widths buffer //
// ANALYSE field name changes //
//////////////////////////////////////////////////////////////////////////////
DWORD LpkGetCharacterPlacement(
HDC hdc,
const WCHAR *pwcInChars,
int cInChars,
int nMaxExtent,
GCP_RESULTSW *pResults,
DWORD dwFlags,
int iCharset) {
UINT uBufferOptions;
int iDigitSubstitute, i, cMaxGlyphs;
int *pLocalDX;
DWORD dwRet = 0;
HRESULT hr;
DWORD dwSSAflags;
SCRIPT_CONTROL scriptControl = {0}; // Analysis control
SCRIPT_STATE scriptState = {0}; // Initial state
STRING_ANALYSIS *psa;
SCRIPT_FONTPROPERTIES sfp;
WORD *pwLocalGlyphs;
TRACE(GDI, ("LpkGetCharacterPlacement begins"));
//////////////////////////////////////////////////////////////////////////
// 1-Check parameters //
//////////////////////////////////////////////////////////////////////////
// Check required parameters
ASSERTS(hdc, ("LpkGetCharacterPlacement - required parameter hdc is NULL"));
//GCP_MAXEXTENT and no nMaxExtent
if ((dwFlags & GCP_MAXEXTENT) && (nMaxExtent < 0)) {
TRACEMSG(("LpkGetCharacterPlacement: Invalid parameter - GCP_MAXEXTENT and no nMaxExtent"));
GdiSetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
//GCP_CLASSIN set and no pClass
if ((dwFlags & GCP_CLASSIN) && !(pResults->lpClass)) {
TRACEMSG(("LpkGetCharacterPlacement: Invalid parameter - GCP_CLASSIN set and no pClass"));
GdiSetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
//////////////////////////////////////////////////////////////////////////
// 2 - Interpret control flags //
//////////////////////////////////////////////////////////////////////////
switch (dwFlags & (GCP_NUMERICSLOCAL|GCP_NUMERICSLATIN)) {
case GCP_NUMERICSLOCAL:
iDigitSubstitute = SCRIPT_DIGITSUBSTITUTE_TRADITIONAL;
break;
case GCP_NUMERICSLOCAL|GCP_NUMERICSLATIN:
iDigitSubstitute = SCRIPT_DIGITSUBSTITUTE_CONTEXT;
break;
case GCP_NUMERICSLATIN:
iDigitSubstitute = SCRIPT_DIGITSUBSTITUTE_NONE;
break;
default:
iDigitSubstitute = -1;
}
dwSSAflags = 0;
if (dwFlags & GCP_REORDER) {
if (GetTextAlign(hdc) & TA_RTLREADING ? 1 : 0) {
dwSSAflags |= SSA_RTL;
}
} else {
scriptState.fOverrideDirection = TRUE;
}
if (dwFlags & GCP_DISPLAYZWG) scriptState.fDisplayZWG = TRUE;
if (!(dwFlags & GCP_LIGATE)) scriptState.fInhibitLigate = TRUE;
if (dwFlags & GCP_SYMSWAPOFF) scriptState.fInhibitSymSwap = TRUE;
if (dwFlags & GCP_NEUTRALOVERRIDE) scriptControl.fNeutralOverride = TRUE;
if (dwFlags & GCP_NUMERICOVERRIDE) scriptControl.fNumericOverride = TRUE;
if (pResults->lpGlyphs) {
scriptControl.fLinkStringBefore = pResults->lpGlyphs[0] & GCPGLYPH_LINKBEFORE ? TRUE : FALSE;
scriptControl.fLinkStringAfter = pResults->lpGlyphs[0] & GCPGLYPH_LINKAFTER ? TRUE : FALSE;
}
if (dwFlags & GCP_MAXEXTENT) {
dwSSAflags |= SSA_CLIP;
if (dwFlags & GCP_JUSTIFY) {
dwSSAflags |= SSA_FIT;
if (!(dwFlags & GCP_KASHIDA) || !(dwFlags & (GCP_GLYPHSHAPE | GCP_LIGATE))) {
dwSSAflags |= SSA_NOKASHIDA;
}
}
}
if (dwFlags & GCP_CLASSIN) {
if (((const BYTE *)pResults->lpClass)[0] & (GCPCLASS_PREBOUNDLTR | GCPCLASS_PREBOUNDRTL)) {
scriptControl.fInvertPreBoundDir = (((const BYTE *)pResults->lpClass)[0] & GCPCLASS_PREBOUNDRTL ? 1 : 0)
^ (dwSSAflags & SSA_RTL ? 1 : 0);
}
if (((const BYTE *)pResults->lpClass)[0] & (GCPCLASS_POSTBOUNDLTR | GCPCLASS_POSTBOUNDRTL)) {
scriptControl.fInvertPostBoundDir = (((const BYTE *)pResults->lpClass)[0] & GCPCLASS_POSTBOUNDRTL ? 1 : 0)
^ (dwSSAflags & SSA_RTL ? 1 : 0);
}
}
//////////////////////////////////////////////////////////////////////////
// 3-Call LPK_ANA to do Layout and Shaping //
//////////////////////////////////////////////////////////////////////////
if (dwFlags & GCP_CLASSIN) {
hr = LpkStringAnalyse(
hdc, pwcInChars, cInChars, pResults->nGlyphs, -1,
dwSSAflags | SSA_GLYPHS | SSA_GCP,
iDigitSubstitute, nMaxExtent,
&scriptControl, &scriptState,
NULL,
NULL,
(BYTE*)pResults->lpClass,
&psa);
} else {
hr = LpkStringAnalyse(
hdc, pwcInChars, cInChars, pResults->nGlyphs, -1,
dwSSAflags | SSA_GLYPHS | SSA_GCP,
iDigitSubstitute, nMaxExtent,
&scriptControl, &scriptState,
NULL,
NULL,
NULL,
&psa);
}
if (FAILED(hr)) {
ASSERTHR(hr, ("LpkGetTextExtentExPoint - LpkStringAnalyse"));
return FALSE;
}
//
// If the user's suppled buffer isn't sufficient to hold the
// output, then let's truncate it.
//
if (pResults->nGlyphs < (UINT) psa->cOutGlyphs) {
psa->cOutGlyphs = (UINT) pResults->nGlyphs;
}
if (pResults->lpOutString) {
GCPgenerateOutString(psa, pResults->lpOutString);
}
if (pResults->lpOrder) {
ScriptStringGetOrder(psa, pResults->lpOrder);
}
if (pResults->lpClass) {
GCPgenerateClass(psa, (PBYTE) pResults->lpClass);
}
BOOL bGlyphsCopied = FALSE;
if (pResults->lpDx) {
if (psa->piJustify && (dwFlags & GCP_JUSTIFY)) {
sfp.cBytes = sizeof(SCRIPT_FONTPROPERTIES);
hr = ScriptGetFontProperties(hdc,
&psa->sc[0],
&sfp);
if(SUCCEEDED(hr)) {
if (!(dwFlags & GCP_KASHIDA) || !(dwFlags & (GCP_GLYPHSHAPE | GCP_LIGATE)))
sfp.iKashidaWidth = -1;
else {
ASSERTS(sfp.wgKashida, ("LpkGetCharacterPlacement - ther is no Kashida glyph"));
}
cMaxGlyphs = pResults->nGlyphs;
hr = GCPJustification( (WORD **)&pwLocalGlyphs,
(int **)&pLocalDX,
(WORD *)psa->pwGlyphs,
(int *)psa->piAdvance,
psa->pVisAttr,
psa->piJustify,
psa->cOutGlyphs,
sfp.iKashidaWidth,
&cMaxGlyphs,
sfp.wgKashida,
sfp.wgBlank);
if(SUCCEEDED(hr)) {
int iOffset = 0;
if (cMaxGlyphs > (int)pResults->nGlyphs) {
iOffset = cMaxGlyphs - pResults->nGlyphs;
cMaxGlyphs = pResults->nGlyphs;
}
psa->cOutGlyphs = cMaxGlyphs;
if (pResults->lpGlyphs) {
memcpy (pResults->lpGlyphs, (LPVOID)(pwLocalGlyphs+iOffset), sizeof(WORD) * cMaxGlyphs);
bGlyphsCopied = TRUE;
}
psa->size.cx = 0;
for (i=0; i<cMaxGlyphs; i++) {
pResults->lpDx[i] = pLocalDX[i+iOffset];
psa->size.cx += pLocalDX[i+iOffset];
}
USPFREE((LPVOID)pwLocalGlyphs);
}
}
} else {
memcpy(pResults->lpDx, psa->piAdvance, sizeof(int) * psa->cOutGlyphs);
}
}
if (!bGlyphsCopied && pResults->lpGlyphs) {
memcpy(pResults->lpGlyphs, psa->pwGlyphs, sizeof(WORD) * psa->cOutGlyphs);
}
if (dwFlags & (GCP_GLYPHSHAPE | GCP_LIGATE))
pResults->nGlyphs = psa->cOutGlyphs;
else
pResults->nGlyphs = cInChars;
pResults->nMaxFit = psa->cOutChars;
// If there was justification we may have zero glyphs
if (!psa->cOutGlyphs) {
pResults->nGlyphs = 0;
ScriptStringFree((void**)&psa);
// Weird Middle East Win95 compatability rules
if ( (dwFlags & GCP_MAXEXTENT)
|| !(dwFlags & GCP_GLYPHSHAPE)
|| !pResults->lpGlyphs) {
return 1;
} else {
return 0;
}
}
//////////////////////////////////////////////////////////////////////////
// 4-Generate lpCaretPos //
//////////////////////////////////////////////////////////////////////////
if (pResults->lpCaretPos) {
char *pbClass = NULL;
INT *piAdvance = pResults->lpDx ? pResults->lpDx : psa->piAdvance;
UINT *puiOrder = NULL;
INT *pCaretCalc;
INT iWidth = 0;
UINT j, uOrder;
hr = USPALLOC(sizeof(INT)*pResults->nGlyphs, (void **)&pCaretCalc);
if(FAILED(hr)) {
ScriptStringFree((void**)&psa);
return 0;
}
// Allocate for pbClass if pResults->lpClass is NULL otherwise us it.
if (pResults->lpClass == NULL) {
hr = USPALLOC(sizeof(char)*cInChars, (void **)&pbClass);
if(FAILED(hr)) {
USPFREE(pCaretCalc);
ScriptStringFree((void**)&psa);
return 0;
}
GCPgenerateClass(psa, (PBYTE) pbClass);
} else {
pbClass = pResults->lpClass;
}
// Allocate for puiOrder if pResults->lpOrder is NULL otherwise us it.
if (pResults->lpOrder == NULL) {
hr = USPALLOC(sizeof(UINT)*cInChars, (void **)&puiOrder);
if(FAILED(hr)) {
if (pResults->lpClass == NULL) {
USPFREE(pbClass);
}
USPFREE(pCaretCalc);
ScriptStringFree((void**)&psa);
return 0;
}
ScriptStringGetOrder(psa, puiOrder);
} else {
puiOrder = pResults->lpOrder;
}
// simple-minded loop used to generate glyph-offsets
// as the same code used in NT4/MET.
UINT caretPosCount = min(pResults->nGlyphs, (UINT)cInChars);
for( j=0 ; j<caretPosCount ; j++ ) {
if (pbClass[j] == GCPCLASS_ARABIC) {
iWidth += piAdvance[j];
pCaretCalc[j] = iWidth - 1;
if (iWidth == 0) {
pCaretCalc[j] = 0;
}
} else {
pCaretCalc[j] = iWidth;
iWidth += piAdvance[j];
}
}
if (pResults->nGlyphs > (UINT)cInChars)
{
for( j=cInChars ; j<pResults->nGlyphs ; j++)
{
pCaretCalc[j] = iWidth;
iWidth += piAdvance[j];
}
}
// Convert to char-indexing. We need to take care if the
// user supplied in sufficient visual buffers
for( j=0 ; j<(UINT)cInChars ; j++ ) {
uOrder = puiOrder[j];
if ((uOrder+1) > (UINT)psa->cOutGlyphs) {
uOrder = 0;
}
pResults->lpCaretPos[j] = pCaretCalc[uOrder];
}
if (pResults->lpOrder == NULL) {
USPFREE(puiOrder);
}
if (pResults->lpClass == NULL) {
USPFREE(pbClass);
}
USPFREE(pCaretCalc);
}
//////////////////////////////////////////////////////////////////////////
// 5 - Return width and height //
//////////////////////////////////////////////////////////////////////////
dwRet = (psa->size.cx & 0xffff) + (psa->size.cy << 16);
//////////////////////////////////////////////////////////////////////////
// 6 - Free allocated memory and exit //
//////////////////////////////////////////////////////////////////////////
ScriptStringFree((void**)&psa);
return dwRet;
}
/******************************Public*Routine******************************\
*
* BOOL LpkUseGDIWidthCache( HDC hDC , LPCSTR psz , int count ,
* LONG fl , BOOL fUnicode)
*
* Checks whether the LPK can use GDI cached widths for the ASCII (0<=x<=127)
* by inspecting the following variables :
* - System numeric shape setting
* - DC Align state
* - The selected font has Western script
* - The string code points in range 0<=x<=127 with Ansi calls
*
* Returns TRUE if it is OK to use GDI width cache, otherwise FALSE
*
* History:
* 28-Aug-1997 -by- Samer Arafeh [SamerA]
* Wrote it.
\**************************************************************************/
BOOL LpkUseGDIWidthCache( HDC hDC , LPCSTR psz , int count , LONG fl , BOOL fUnicode)
{
BOOL bRet;
int i;
BYTE cTest;
LPSTR pstr;
//
// Let's make sure that :
// 1- Text is LTR Reading
// 2- Digits shape setting is Arabic
// 3- if Unicode call, make sure the font has Western script
// if Ansi call check if all code points less than 0x80 and font has Wetern script.
bRet = (!!(fl & TA_RTLREADING) == !!(GetLayout(hDC) & LAYOUT_RTL))
&& g_DigitSubstitute.DigitSubstitute == SCRIPT_DIGITSUBSTITUTE_NONE;
TRACE( GDI, ("LpkUseGDIWidthCache: g_DigitSubstitute.DigitSubstitute=%x, bRet=%x",
g_DigitSubstitute.DigitSubstitute, bRet));
if (bRet) {
// We don't need this check for Unicdoe calls because it is done in GDI.
if (!fUnicode) {
cTest = 0;
i = count;
pstr = (LPSTR) psz;
unroll_here:
switch(i)
{
default:
cTest |= pstr[9];
case 9:
cTest |= pstr[8];
case 8:
cTest |= pstr[7];
case 7:
cTest |= pstr[6];
case 6:
cTest |= pstr[5];
case 5:
cTest |= pstr[4];
case 4:
cTest |= pstr[3];
case 3:
cTest |= pstr[2];
case 2:
cTest |= pstr[1];
case 1:
cTest |= pstr[0];
}
if ((i > 10) && !(cTest & 0x80))
{
i -= 10;
pstr += 10;
goto unroll_here;
}
bRet = !(cTest & 0x80);
}
return (bRet && FontHasWesternScript(hDC));
}
return bRet ;
}