windows-nt/Source/XPSP1/NT/shell/comctl32/v5/fontlink.cpp
2020-09-26 16:20:57 +08:00

3432 lines
114 KiB
C++

//+---------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation, 1994-1995
//
// File: fontlink.cpp
//
// Contents: Wrappers for font link functions.
//
//----------------------------------------------------------------------------
#include "ctlspriv.h"
#ifdef FONT_LINK
#include <mlang.h>
#include "fontlink.h"
#include "unicwrap.h"
#undef DrawTextW
#undef DrawTextExW
#undef ExtTextOutW
#undef TextOutW
#undef GetCharWidthW
#undef GetTextExtentPointW
#undef GetTextExtentPoint32W
#ifndef WINNT
//
// Win9x will crash if uLastChar == 0xFFFF. You can't squeak the width of
// the 0xFFFF character by setting uLastChar to 0x10000, since the Win16
// thunk will truncate it to 0x0000, and then the validation layer will
// reject the request since uLastChar > uFirstChar.
//
// So assume that the width of 0xFFFF is equal to the width of 0xFFFE.
// Both are invalid unicode characters, so hopefully the widths are the same.
//
BOOL SafeGetCharWidthW(HDC hdc, UINT uFirstChar, UINT uLastChar, LPINT lpnWidths)
{
BOOL fRc;
ASSERT(uFirstChar <= uLastChar);
ASSERT(uLastChar <= 0xFFFF);
if (uLastChar != 0xFFFF) {
fRc = GetCharWidthW(hdc, uFirstChar, uLastChar, lpnWidths);
} else {
if (uLastChar > uFirstChar) {
// If asking for multiple characters, this means we are asking
// for a range that ends at 0xFFFF. Change it to a range that
// ends at 0xFFFE, and copy the result for 0xFFFE into 0xFFFF.
fRc = GetCharWidthW(hdc, uFirstChar, uLastChar - 1, lpnWidths);
lpnWidths[uLastChar-uFirstChar] = lpnWidths[uLastChar-uFirstChar-1];
} else {
// If asking for just one character, that character must be
// 0xFFFF, so change it to a request for 0xFFFE.
fRc = GetCharWidthW(hdc, 0xFFFE, 0xFFFE, lpnWidths);
}
}
return fRc;
}
#define GetCharWidthW SafeGetCharWidthW
#endif
#ifdef WINNT
HRESULT (*g_pfnGetGlobalFontLinkObject)(IMLangFontLink **) = NULL;
BOOL LoadMLFontLink(IMLangFontLink **ppMLFontLink)
{
ENTERCRITICAL;
*ppMLFontLink = NULL;
if (NULL == g_pfnGetGlobalFontLinkObject)
{
HMODULE hmod = LoadLibrary(TEXT("MLANG.DLL"));
if (hmod)
g_pfnGetGlobalFontLinkObject = (HRESULT (*)(IMLangFontLink **))GetProcAddress(hmod, "GetGlobalFontLinkObject");
}
if (g_pfnGetGlobalFontLinkObject)
g_pfnGetGlobalFontLinkObject(ppMLFontLink);
LEAVECRITICAL;
return (*ppMLFontLink)? TRUE: FALSE;
}
#else
HDPA g_hdpaMLANG = NULL;
typedef struct tagPROCITEM
{
DWORD dwProcessId;
HRESULT (*pfnGetGlobalFontLinkObject)(IMLangFontLink **);
} PROCITEM, *PPROCITEM;
PPROCITEM CreatePROCITEM(void)
{
PPROCITEM pItem = (PPROCITEM)Alloc(sizeof(PROCITEM));
if (pItem)
{
HMODULE hmod = LoadLibrary(TEXT("MLANG.DLL"));
if (hmod)
{
pItem->dwProcessId = GetCurrentProcessId();
pItem->pfnGetGlobalFontLinkObject = (HRESULT (*)(IMLangFontLink **))GetProcAddress(hmod, "GetGlobalFontLinkObject");
}
}
return pItem;
}
int GetPROCITEM(DWORD dwProcessId)
{
int i, cItems = 0;
ASSERTCRITICAL;
if (g_hdpaMLANG)
cItems = DPA_GetPtrCount(g_hdpaMLANG);
for (i = 0; i < cItems; i++)
{
PPROCITEM pItem = (PPROCITEM)DPA_FastGetPtr(g_hdpaMLANG, i);
if (pItem && pItem->dwProcessId == dwProcessId)
return i;
}
return -1;
}
STDAPI_(void) InitMLANG(void)
{
ENTERCRITICAL;
if (NULL == g_hdpaMLANG)
{
ASSERTCRITICAL;
g_hdpaMLANG = DPA_Create(4);
}
int i = GetPROCITEM(GetCurrentProcessId());
// If the previous app with my process id crashed, erase my entry
// so I won't crash, too.
if (0 <= i)
{
Free((PPROCITEM)DPA_FastGetPtr(g_hdpaMLANG, i));
DPA_DeletePtr(g_hdpaMLANG, i);
}
LEAVECRITICAL;
}
STDAPI_(void) DeinitMLANG(int cProcesses)
{
int i = GetPROCITEM(GetCurrentProcessId());
ASSERTCRITICAL;
if (0 <= i)
{
Free((PPROCITEM)DPA_FastGetPtr(g_hdpaMLANG, i));
DPA_DeletePtr(g_hdpaMLANG, i);
}
if (g_hdpaMLANG && 1 == cProcesses) // This is last process detach
{
DPA_Destroy(g_hdpaMLANG);
g_hdpaMLANG = NULL;
}
}
BOOL LoadMLFontLink(IMLangFontLink **ppMLFontLink)
{
PPROCITEM pItem = NULL;
int i;
ENTERCRITICAL;
*ppMLFontLink = NULL;
i = GetPROCITEM(GetCurrentProcessId());
if (0 <= i)
{
pItem = (PPROCITEM)DPA_FastGetPtr(g_hdpaMLANG, i);
}
else
{
pItem = CreatePROCITEM();
if (pItem)
DPA_AppendPtr(g_hdpaMLANG, pItem);
}
if (pItem && pItem->pfnGetGlobalFontLinkObject)
pItem->pfnGetGlobalFontLinkObject(ppMLFontLink);
LEAVECRITICAL;
return (*ppMLFontLink)? TRUE: FALSE;
}
#endif
#define _MAX_WCHAR_BUFFER_SIZE 256 // Should be enough for shell space name strings
#define _MAX_MB_BUFFER_SIZE _MAX_WCHAR_BUFFER_SIZE * sizeof(WCHAR)
//
// Helper function to decide whether we need MLang font link
// On return:
// S_OK : Yes, we need MLang font link and MLang can be loaded succesfully
// S_FALSE : No, we don't need MLang since text can be handled natively with font assciation (ANSI) and font link.
// E_FAIL : Need MLang, but, we couldn't load it
HRESULT FDoFontLink(HDC hdc, IMLangFontLink **ppMLFontLink, LPCWSTR pwszName, int cch)
{
HRESULT hr;
BOOL fNotDisplayable = TRUE;
int cchWChar;
int cchMB = cch * sizeof(WCHAR);
// When possible, we'll use internal buffer to avoid memory operations
char szBuffer[_MAX_MB_BUFFER_SIZE];
WCHAR wszBuffer[_MAX_WCHAR_BUFFER_SIZE];
char *pszBuffer = szBuffer;
WCHAR *pwszBuffer = wszBuffer;
UINT uiCharset;
CHARSETINFO csi;
ASSERT(ppMLFontLink);
uiCharset = GetTextCharsetInfo(hdc, NULL, 0);
TranslateCharsetInfo(IntToPtr_(DWORD *, uiCharset), &csi, TCI_SRCCHARSET);
if (cch>0 && g_uiACP == csi.ciACP)
{
// Alloc buffer if multibyte buffer is not enough
if (cch > _MAX_WCHAR_BUFFER_SIZE)
pszBuffer = (char *)LocalAlloc(LPTR, cchMB);
if (pszBuffer)
{
cchMB = WideCharToMultiByte(CP_ACP, 0, pwszName, cch, pszBuffer, cchMB, NULL, &fNotDisplayable);
// Round-trip verification
if (!fNotDisplayable)
{
// Alloc buffer if wide char buffer is not enough
if (cch > _MAX_WCHAR_BUFFER_SIZE)
pwszBuffer = (WCHAR *)LocalAlloc(LPTR, cch*sizeof(WCHAR));
if (pwszBuffer)
{
cchWChar = MultiByteToWideChar(CP_ACP, 0, pszBuffer, cchMB, pwszBuffer, cch);
if (cch == cchWChar)
{
for (int i=0; i<cch; i++)
{
if (pwszBuffer[i] != pwszName[i])
{
fNotDisplayable = TRUE;
break;
}
}
}
else
{
fNotDisplayable = TRUE;
}
if (pwszBuffer != wszBuffer)
LocalFree(pwszBuffer);
}
else
{
fNotDisplayable = TRUE;
}
}
if (pszBuffer != szBuffer)
LocalFree(pszBuffer);
}
}
if (fNotDisplayable)
{
if (LoadMLFontLink(ppMLFontLink))
hr = S_OK; // Need MLang font link
else
hr = E_FAIL; // Unable to load MLang
}
else
{
hr = S_FALSE; // Doesn't need MLang font link, but, we still need to call GDI in ANSI for Win9x FA to work properly
}
return hr;
}
//
// For _GetCharWidthWFontLink()
//
HRESULT FDoFontLink(HDC hdc, IMLangFontLink **ppMLFontLink, WCHAR wFirstChar, WCHAR wLastChar)
{
WCHAR wszBuffer[_MAX_WCHAR_BUFFER_SIZE];
int i = 0;
int cch = wFirstChar - wLastChar + 1;
WCHAR *pwszBuffer = wszBuffer;
HRESULT hr = E_FAIL;
if (cch > _MAX_WCHAR_BUFFER_SIZE)
pwszBuffer = (WCHAR *)LocalAlloc(LPTR, cch*sizeof(WCHAR));
if (pwszBuffer)
{
while (i < cch)
{
pwszBuffer[i] = wFirstChar+i;
i++;
}
hr = FDoFontLink(hdc, ppMLFontLink, pwszBuffer, cch);
if (pwszBuffer != wszBuffer)
LocalFree(pwszBuffer);
}
return hr;
}
//
// BUGBUG: Review for removing below big table and UsrFromWch() ...
//
__inline BOOL FChsDbcs(UINT chs)
{
return (chs == SHIFTJIS_CHARSET ||
chs == HANGEUL_CHARSET ||
chs == CHINESEBIG5_CHARSET ||
chs == GB2312_CHARSET);
}
__inline int FChsBiDi(int chs)
{
return (chs == ARABIC_CHARSET ||
chs == HEBREW_CHARSET);
}
__inline BOOL FCpgChinese(UINT cpg)
{
if (cpg == CP_ACP)
cpg = GetACP();
return (cpg == CP_TAIWAN || cpg == CP_CHINA);
}
__inline BOOL FCpgTaiwan(UINT cpg)
{
if (cpg == CP_ACP)
cpg = GetACP();
return (cpg == CP_TAIWAN);
}
__inline BOOL FCpgPRC(UINT cpg)
{
if (cpg == CP_ACP)
cpg = GetACP();
return (cpg == CP_CHINA);
}
__inline BOOL FCpgFarEast(UINT cpg)
{
if (cpg == CP_ACP)
cpg = GetACP();
return (cpg == CP_JAPAN || cpg == CP_TAIWAN || cpg == CP_CHINA ||
cpg == CP_KOREA || cpg == CP_MAC_JAPAN);
}
__inline BOOL FCpgDbcs(UINT cpg)
{
return (cpg == CP_JAPAN ||
cpg == CP_KOREA ||
cpg == CP_TAIWAN ||
cpg == CP_CHINA);
}
__inline int FCpgBiDi(int cpg)
{
return (cpg == CP_ARABIC ||
cpg == CP_HEBREW);
}
#ifndef WINNT
HFONT GetBiDiFont(HDC hdc)
{
HFONT hfont = NULL;
HFONT hfontTmp;
LOGFONT lf;
hfontTmp = (HFONT)GetCurrentObject(hdc, OBJ_FONT);
GetObject(hfontTmp, sizeof(lf), &lf);
// BUGBUG: Should I loop on the string to check if it contains BiDi chars?
if ( !FChsBiDi(lf.lfCharSet))
{
lf.lfCharSet = DEFAULT_CHARSET;
hfont = CreateFontIndirect(&lf);
}
return hfont;
}
#endif
// Table to map Unicode high byte value to first sub range for this high byte
const BYTE mpbHighusr[256] =
{
/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
/* 0 */ 0, 2, 3, 6, 9, 10, 13, 0, 0, 15, 17, 19, 21, 23, 24, 0,
/* 1 */ 26, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 30,
/* 2 */ 31, 35, 38, 39, 40, 43, 46, 47, 0, 0, 0, 0, 0, 0, 0, 0,
/* 3 */ 48, 51, 54, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 4 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, 59,
/* 5 */ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
/* 6 */ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
/* 7 */ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
/* 8 */ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
/* 9 */ 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
/* a */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 56, 56, 56,
/* b */ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
/* c */ 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
/* d */ 56, 56, 56, 56, 56, 56, 56, 56, 0, 0, 0, 0, 0, 0, 0, 0,
/* e */ 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60,
/* f */ 60, 60, 60, 60, 60, 60, 60, 60, 60, 61, 61, 62, 63, 63, 64, 68
};
int UsrFromWch(WCHAR wch)
{
UCHAR bLow = LOBYTE(wch);
int usr;
switch (usr = mpbHighusr[HIBYTE(wch)])
{
case usrBasicLatin:
if (bLow < 0x80)
break;
++usr;
break;
case usrLatinXA:
if (bLow < 0x80)
break;
++usr;
break;
case usrLatinXB:
if (bLow < 0x50)
{
// ASSERT(wch <= 0x217);
break;
}
++usr;
if (bLow < 0xb0)
{
// ASSERT(wch <= 0x2a8);
break;;
}
++usr;
// ASSERT(wch <= 0x2e9);
break;
case usrCombDiacritical:
if (bLow < 0x70)
{
// ASSERT(wch <= 0x345 || wch == 0x360 || wch == 0x361);
break;
}
++usr;
if (bLow < 0xd0)
{
// ASSERT(wch == 0x374 || wch == 0x375 || wch == 0x37a || wch == 0x37e || (wch >= 0x384 && wch <= 0x38a) || wch == 0x38c ||
// (wch >= 0x38e && wch <= 0x3ce));
break;
}
++usr;
// ASSERT(wch <= 0x3d6 || wch == 0x3da || wch == 0x3dc || wch == 0x3de || wch == 0x3e0 || (wch >= 0x3e2 && wch <= 0x3f3));
break;
case usrCyrillic:
// ASSERT((wch >= 0x401 && wch <= 0x40c) || (wch >= 0x40e && wch <= 0x44f) || (wch >= 0x450 && wch <= 0x45c) ||
// (wch >= 0x45e && wch <= 0x486) || (wch >= 0x490 && wch <= 0x4cc) || (wch >= 0x4d0 && wch <= 0x4f9));
break;
case usrArmenian:
if (bLow < 0x90)
{
// ASSERT((wch >= 0x531 && wch <= 0x556) || (wch >= 0x559 && wch <= 0x55f) || (wch >= 0x561 && wch <= 0x587) || wch == 0x589);
break;
}
++usr;
if (bLow >= 0xd0)
{
// ASSERT(wch <= 0x5ea || (wch >= 0x5f0 && wch <= 0x5f4));
break;
}
++usr;
// ASSERT(wch >= 0x5b0 && wch <= 0x5c3);
break;
case usrBasicArabic:
if (bLow < 0x53)
{
// ASSERT(wch == 0x60c || wch == 0x61b || wch == 0x61f || (wch >= 0x621 && wch <= 0x63a) || (wch >= 0x640 && wch <= 0x652));
break;
}
++usr;
// ASSERT((wch >= 0x660 && wch <= 0x66d) || (wch >= 0x670 && wch <= 0x6b7) || (wch >= 0x6ba && wch <= 0x6be) ||
// (wch >= 0x6c0 && wch <= 0x6ce) || (wch >= 0x6d0 && wch <= 0x6ed) || (wch >= 0x6f0 && wch <= 0x6f9));
break;
case usrDevangari:
case usrGurmukhi:
case usrOriya:
case usrTelugu:
case usrThai:
// REVIEW: Added asserts for these
if (bLow < 0x80)
break;
++usr;
break;
case usrMalayalam:
// ASSERT(wch < 0xd80);
break;
case usrBasicGeorgian:
if (bLow >= 0xD0)
break;
++usr;
// ASSERT(bLow >= 0xa0);
break;
case usrHangulJamo:
// ASSERT(wch <= 0x11f9);
break;
case usrLatinExtendedAdd:
// ASSERT(wch <= 0x1ef9);
break;
case usrGreekExtended:
// ASSERT(wch <= 0x1ffe);
break;
case usrGeneralPunct:
if (bLow < 0x70)
{
// ASSERT(wch <= 0x2046 || (wch >= 0x206a && wch <= 0x206f));
break;
}
++usr;
if (bLow < 0xa0)
{
// ASSERT(wch == 0x2070 || (wch >= 0x2074 && wch <= 0x208e));
break;
}
++usr;
if (bLow < 0xd0)
{
// ASSERT(wch <= 0x20aa);
break;
}
++usr;
// ASSERT(wch <= 0x20e1);
break;
case usrLetterlikeSymbols:
if (bLow < 0x50)
{
// ASSERT(wch <= 0x2138);
break;
}
++usr;
if (bLow < 0x90)
{
// ASSERT((wch >= 0x2153 && wch <= 0x2182));
break;
}
++usr;
// ASSERT(wch <= 0x21ea);
break;
case usrMathematicalOps:
// ASSERT(wch <= 0x22f1);
break;
case usrMiscTechnical:
// ASSERT(wch <= 0x237a);
break;
case usrControlPictures:
if (bLow < 0x40)
{
// ASSERT(wch <= 0x2424);
break;
}
++usr;
if (bLow < 0x60)
{
// ASSERT(wch <= 0x244a);
break;
}
++usr;
// ASSERT(wch <= 0x24ea);
break;
case usrBoxDrawing:
if (bLow < 0x80)
break;
++usr;
if (bLow < 0xa0)
{
// ASSERT(wch <= 0x2595);
break;
}
++usr;
// ASSERT(wch <= 0x25ef);
break;
case usrMiscDingbats:
// ASSERT(wch <= 0x2613 || (wch >= 0x261a && wch <= 0x266f));
break;
case usrDingbats:
break;
case usrCJKSymAndPunct:
if (bLow < 0x40)
{
// ASSERT(wch <= 0x3037 || wch == 0x303f);
break;
}
++usr;
if (bLow < 0xa0)
{
// ASSERT((wch >= 0x3041 && wch <= 0x3094) || (wch >= 0x3099 && wch <= 0x309e));
break;
}
++usr;
// ASSERT(wch >= 0x30a1 && wch <= 0x30fe);
break;
case usrBopomofo:
if (bLow < 0x30)
{
// ASSERT(wch >= 0x3105 && wch <= 0x312c);
break;
}
++usr;
if (bLow < 0x90)
{
// ASSERT(wch >= 0x3131 && wch <= 0x318e);
break;
}
++usr;
// ASSERT(wch <= 0x319f);
break;
case usrEnclosedCJKLtMnth:
// ASSERT((wch >= 0x3200 && wch <= 0x321c) || (wch >= 0x3220 && wch <= 0x3243) || (wch >= 0x3260 && wch <= 0x327b) ||
// (wch >= 0x327f && wch <= 0x32b0) || (wch >= 0x32c0 && wch <= 0x32cb) || (wch >= 0x32d0 && wch <= 0x32fe));
break;
case usrCJKCompatibility:
// ASSERT(wch <= 0x3376 || (wch >= 0x337b && wch <= 0x33dd) || (wch >= 0x33e0 && wch <= 0x33FE));
break;
case usrHangul:
// ASSERT(wch <= 0xd7a3);
break;
case usrCJKUnifiedIdeo:
break;
case usrPrivateUseArea:
break;
case usrCJKCompatibilityIdeographs:
// ASSERT(wch <= 0xfa2d);
break;
case usrAlphaPresentationForms:
if (bLow < 0x50)
{
break;
}
++usr;
break;
case usrArabicPresentationFormsA:
// ASSERT(wch <= 0xfdfb);
break;
case usrCombiningHalfMarks:
if (bLow < 0x30)
{
// ASSERT(wch >= 0xfe20 && wch <= 0xfe23);
break;
}
++usr;
if (bLow < 0x50)
{
// ASSERT((wch >= 0xfe30 && wch <= 0xfe44) || wch >= 0xfe49);
break;
}
++usr;
if (bLow < 0x70)
{
// ASSERT((wch >= 0xfe50 && wch <= 0xfe52) || (wch >= 0xfe54 && wch <= 0xfe66) || (wch >= 0xfe68 && wch <= 0xfe6b));
break;
}
++usr;
// REVIEW : Need assert for this range
break;
case usrHFWidthForms:
if (bLow < 0xf0)
{
// ASSERT((wch >= 0xff01 && wch <= 0xff5e) || (wch >= 0xff61 && wch <= 0xffbe) || (wch >= 0xffc2 && wch <= 0xffc7) ||
// (wch >= 0xffca && wch <= 0xffcf) || (wch >= 0xffd2 && wch <= 0xffd7) || (wch >= 0xffda && wch <= 0xffdc) ||
// (wch >= 0xffe0 && wch <= 0xffe6));
break;
}
++usr;
// ASSERT(wch == 0xfffd);
break;
default:
ASSERT(FALSE); // bad Unicode character!!
break;
}
return usr;
}
BOOL _OtherGetCharWidthW(HDC hdc, UINT uFirstChar, UINT uLastChar, LPINT lpnWidths)
{
ASSERT(uFirstChar <= uLastChar);
#ifdef WINNT
return GetCharWidthW(hdc, uFirstChar, uLastChar, lpnWidths);
#else
if (uLastChar <= 127)
{
if (GetCharWidthA(hdc, uFirstChar, uLastChar, lpnWidths))
return TRUE;
}
if (g_fDBCSEnabled)
{
TEXTMETRIC tm;
WCHAR wch;
int iWidth;
BOOL fRet;
// This is a workaround for FE Win95 bug which causes GPF when obtaining widths of
// characters between 0xF000 to 0xF0FF if the font isn't SYMBOL_CHARSET.
if (uLastChar >= 0xF000 && uFirstChar <= 0xF0FF)
{
GetTextMetrics(hdc, &tm);
if (SYMBOL_CHARSET != tm.tmCharSet)
{
fRet = GetCharWidthW(hdc, tm.tmDefaultChar, tm.tmDefaultChar, &iWidth);
for (wch = (WCHAR)uFirstChar; wch <= (WCHAR)uLastChar; wch++)
lpnWidths[wch - uFirstChar] = iWidth;
return fRet;
}
else
goto LCallGetCharWidthW;
}
else if (!FCpgChinese(g_uiACP))
goto LCheck00b7;
GetTextMetrics(hdc, &tm);
if (!FChsDbcs(tm.tmCharSet))
goto LCheck00b7;
if (FCpgPRC(g_uiACP))
// This is a workaround for Win95 PRC bug which causes quite a few Chinese
// characters to have only nearly half the actual width.
{
#define uPUAFirst 0xe815
#define uPUALast 0xe964
int usr;
if (fRet = GetCharWidthW(hdc, uFirstChar, uLastChar, lpnWidths))
{
if (((usr = UsrFromWch((WCHAR)uFirstChar)) == usrCJKUnifiedIdeo ||
usr == usrCJKCompatibilityIdeographs || FBetween(uFirstChar, uPUAFirst, uPUALast)) ||
((usr = UsrFromWch((WCHAR)uLastChar)) == usrCJKUnifiedIdeo ||
usr == usrCJKCompatibilityIdeographs || FBetween(uLastChar, uPUAFirst, uPUALast)))
{
fRet = GetCharWidthW(hdc, 0x4e00, 0x4e00, &iWidth);
for (wch = (WCHAR)uFirstChar; wch <= (WCHAR)uLastChar; wch ++)
if ((usr = UsrFromWch(wch)) == usrCJKUnifiedIdeo ||
usr == usrCJKCompatibilityIdeographs || FBetween(wch, uPUAFirst, uPUALast))
lpnWidths[wch - uFirstChar] = iWidth;
}
}
}
else if (FCpgTaiwan(g_uiACP))
// This is a workaround for Win95 TWN, since GetCharWidthW may return wrong values
// for some characters. We assume all Full-Width characters have the same width, and
// still use GetCharWidthA for the common used Half-Width characters. - willisc
{
UINT ich;
int dxpWidth;
fRet = TRUE;
GetCharWidthW(hdc, 0x4e00, 0x4e00, &dxpWidth); // width of Chinese character '1'
for (ich = uFirstChar; ich <= uLastChar; ich++)
{
if ((ich >= 0x4e00 && ich <= 0x9fff) || (ich >= 0xe000 && ich <= 0xffe5))
// hit most Full-Width characters, always use width of Chinese '1'
lpnWidths[ich - uFirstChar] = dxpWidth;
else if (ich >= 0x20 && ich <= 0x7d)
// hit most Half-Width characters, use trustable GetCharWidthA
GetCharWidthA(hdc, ich, ich, &(lpnWidths[ich - uFirstChar]));
else
{
if (ich == 0x2018 || ich == 0x2019) // these two may be mapped to full
lpnWidths[ich - uFirstChar] = dxpWidth;
else
{ // for others, use WideChar->MultiByte to check its full or half width
UCHAR uChar[3];
if (WideCharToMultiByte(CP_TAIWAN, 0, (LPCWSTR)&ich, 1,
(LPSTR)uChar, 2, NULL, NULL) == 2)
lpnWidths[ich - uFirstChar] = dxpWidth;
else // half-width, use GetCharWidthA
GetCharWidthA(hdc, *uChar, *uChar, &(lpnWidths[ich - uFirstChar]));
}
}
// REVIEW: PETERO: fRet only reflects uLastChar
}
}
return fRet;
}
else
LCheck00b7:
if (FBetween(0x00b7, uFirstChar, uLastChar))
{
// This is a workaround for Win95 bug which causes U+00B7 character to
// be rendered improperly when using the wide API (substituting U+2219
// achieves the desired effect for some reason!)
//
// REVIEW nobuyah: is this a performance bottleneck?
BOOL fRet;
fRet = GetCharWidthW(hdc, uFirstChar, uLastChar, lpnWidths);
if (fRet)
fRet = GetCharWidthW(hdc, 0x2219, 0x2219, lpnWidths + 0x00b7 - uFirstChar);
return (fRet);
}
else
{
LCallGetCharWidthW:
return (GetCharWidthW(hdc, uFirstChar, uLastChar, lpnWidths));
}
#endif
}
//
// _GetCharWidthWFontLink
//
// This is a filter for _GetCharWidthW() calls that does font linking.
//
// The input string is scanned and fonts are switched if not all chars are
// supported by the current font in the HDC.
//
BOOL _GetCharWidthWFontLink(HDC hdc, UINT uFirstChar, UINT uLastChar, LPINT lpnWidths)
{
HFONT hfont = NULL;
HFONT hfontSav = NULL;
HFONT hfontMap = NULL;
WCHAR xch, xchFirst, xchLast = (WCHAR)uLastChar;
DWORD dwFontCodePages, dwCharCodePages;
BOOL fRet = FALSE;
IMLangFontLink *pMLFontLink = NULL;
switch (FDoFontLink(hdc, &pMLFontLink, (WCHAR)uFirstChar, (WCHAR)uLastChar))
{
case S_OK:
break;
case S_FALSE:
UINT ui;
UINT uCharAnsi;
ASSERT(uFirstChar <= uLastChar);
for (ui=uFirstChar; ui<= uLastChar; ui++)
{
uCharAnsi = 0;
if (WideCharToMultiByte(CP_ACP, 0, (LPCWSTR) &ui, 1, (LPSTR) &uCharAnsi, sizeof(uCharAnsi), NULL, NULL))
fRet = GetCharWidthA(hdc, uCharAnsi, uCharAnsi, &(lpnWidths[ui - uFirstChar]));
if (!fRet)
break;
}
return fRet;
default:
return FALSE;
}
hfont = (HFONT)GetCurrentObject(hdc, OBJ_FONT);
pMLFontLink->GetFontCodePages(hdc, hfont, &dwFontCodePages);
// See if whole string can be handled by current font
for (xch = (WCHAR)uFirstChar; xch <= xchLast; xch++)
{
pMLFontLink->GetCharCodePages(xch, &dwCharCodePages);
if (!(dwFontCodePages & dwCharCodePages))
{
// Output the run
if ((xch - uFirstChar) > 0)
fRet = _OtherGetCharWidthW(hdc, uFirstChar, xch - 1, lpnWidths);
break;
}
}
while (xch <= xchLast)
{
xchFirst = xch;
pMLFontLink->MapFont(hdc, dwCharCodePages, hfont, &hfontMap);
hfontSav = (HFONT)SelectObject(hdc, hfontMap);
pMLFontLink->GetFontCodePages(hdc, hfontMap, &dwFontCodePages);
// Collect up run of characters supported by this font
for (xch++; xch <= xchLast; xch++)
{
pMLFontLink->GetCharCodePages(xch, &dwCharCodePages);
if (!(dwFontCodePages & dwCharCodePages))
break;
}
// Output the run
fRet = _OtherGetCharWidthW(hdc, xchFirst, xch - 1, lpnWidths + (xchFirst - uFirstChar));
SelectObject(hdc, hfontSav);
pMLFontLink->ReleaseFont(hfontMap);
// BUGBUG:
if (!fRet)
break;
}
pMLFontLink->Release();
return fRet;
}
BOOL GetCharWidthWrap(HDC hdc, UINT uFirstChar, UINT uLastChar, LPINT lpnWidths)
{
BOOL fRet = FALSE;
#ifndef WINNT
HFONT hfont = NULL;
HFONT hfontSav = NULL;
#endif
// On NT5, we use system API behavior including fontlink
if (g_bRunOnNT5)
return GetCharWidthW(hdc, uFirstChar, uLastChar, lpnWidths);
if (uLastChar > 127) // Optimization, skip for below 127
{
#ifndef WINNT
if (g_fMEEnabled && (hfont = GetBiDiFont(hdc)))
hfontSav = (HFONT)SelectObject(hdc, hfont);
#endif
fRet = _GetCharWidthWFontLink(hdc, uFirstChar, uLastChar, lpnWidths);
}
if (!fRet)
fRet = _OtherGetCharWidthW(hdc, uFirstChar, uLastChar, lpnWidths);
#ifndef WINNT
if (hfont)
{
SelectObject(hdc, hfontSav);
DeleteObject(hfont);
}
#endif
return fRet;
}
BOOL _OtherGetTextExtentPointW(HDC hdc, LPCWSTR lpwch, int cch, LPSIZE lpSize)
{
BOOL fRet;
if (cch == 0)
{
fRet = GetTextExtentPointA(hdc, " ", 1, lpSize);
lpSize->cx = 0;
return fRet;
}
#ifdef WINNT
return GetTextExtentPoint32W(hdc, lpwch, cch, lpSize);
#else
if (g_fMEEnabled || g_fDBCSEnabled)
{
UINT cpg, chs;
CHARSETINFO csi;
chs = GetTextCharsetInfo(hdc, NULL, 0);
TranslateCharsetInfo(IntToPtr_(DWORD *, chs), &csi, TCI_SRCCHARSET);
cpg = csi.ciACP;
if ((FChsBiDi(chs) || FChsDbcs(chs)) && IsValidCodePage(cpg))
{
int cchOut = cch;
int fUsedDefaultChar;
char *pchT;
int cchMax;
int fRet = FALSE;
cchMax = cchOut * sizeof(WCHAR);
pchT = (char *)LocalAlloc(LPTR, cchMax);
if (pchT)
{
cchOut = WideCharToMultiByte(cpg, 0, lpwch, cchOut, pchT, cchMax, NULL, &fUsedDefaultChar);
if (fUsedDefaultChar)
{
LocalFree(pchT);
goto L95GDIHack;
}
fRet = GetTextExtentPointA(hdc, pchT, cchOut, lpSize);
LocalFree(pchT);
}
return fRet;
}
}
L95GDIHack:
if (g_fDBCSEnabled)
{
WCHAR wchX = 'X';
int dxp, dxpX, dxpT;
// GetTextExtentPoint32W() GPFs on Win 95 FE for chars above U+00FF
// Instead use a sum of GetCharWidthWrap() calls, plus the difference
// between GetCharWidthWrap() of 'X' and GetTextExtentPoint32W() of 'X'
fRet = GetTextExtentPoint32W(hdc, &wchX, 1, lpSize);
if (fRet)
{
LPWSTR lpwchT;
LPWSTR lpwchEnd = (LPWSTR)lpwch + cch;
_OtherGetCharWidthW(hdc, 'X', 'X', &dxpX);
for (dxp = 0, lpwchT = (LPWSTR)lpwch; lpwchT < lpwchEnd; lpwchT++)
{
_OtherGetCharWidthW(hdc, *lpwchT, *lpwchT, &dxpT);
dxp += dxpT;
}
lpSize->cx = lpSize->cx - dxpX + dxp;
}
}
else
fRet = GetTextExtentPoint32W(hdc, lpwch, cch, lpSize);
return fRet;
#endif
}
//
// _GetTextExtentPointWFontLink
//
// This is a filter for GetTextExtentPointW() that does font linking.
//
// The input string is scanned and fonts are switched if not all chars are
// supported by the current font in the HDC.
//
BOOL _GetTextExtentPointWFontLink(HDC hdc, LPCWSTR lpwch, int cch, LPSIZE lpSize)
{
HFONT hfont = NULL;
HFONT hfontSav = NULL;
HFONT hfontMap = NULL;
BOOL fRet = FALSE;
int cchDone;
long cchCodePages;
DWORD dwACP, dwFontCodePages, dwCodePages;
SIZE size;
IMLangFontLink *pMLFontLink = NULL;
ASSERT(cch != 0);
switch (FDoFontLink(hdc, &pMLFontLink, lpwch, cch))
{
case S_OK:
break;
case S_FALSE:
{
// Times by the size of WCHAR should be enough for multibyte string buffer
char *lpStr = (char *)LocalAlloc(LPTR, cch*sizeof(WCHAR));
if (lpStr)
{
if (cch = WideCharToMultiByte(CP_ACP,0,lpwch, cch, lpStr, cch*sizeof(WCHAR), NULL, NULL))
fRet = GetTextExtentPointA(hdc, lpStr, cch, lpSize);
LocalFree(lpStr);
}
}
return fRet;
default:
return FALSE;
}
hfont = (HFONT)GetCurrentObject(hdc, OBJ_FONT);
pMLFontLink->GetFontCodePages(hdc, hfont, &dwFontCodePages);
pMLFontLink->CodePageToCodePages(g_uiACP, &dwACP); // Give priority to CP_ACP
// See if whole string can be handled by current font
pMLFontLink->GetStrCodePages(lpwch, cch, dwACP, &dwCodePages, &cchCodePages);
// current font supports whole string ?
if ((dwFontCodePages & dwCodePages) && cch == cchCodePages)
{
pMLFontLink->Release();
return FALSE;
}
// Get Hight of DC font
if (!(fRet = GetTextExtentPointA(hdc, " ", 1, lpSize)))
{
pMLFontLink->Release();
return FALSE;
}
lpSize->cx = 0;
for (cchDone = 0; cchDone < cch; cchDone += cchCodePages)
{
pMLFontLink->GetStrCodePages(lpwch + cchDone, cch - cchDone, dwACP, &dwCodePages, &cchCodePages);
if (!(dwFontCodePages & dwCodePages))
{
pMLFontLink->MapFont(hdc, dwCodePages, hfont, &hfontMap);
hfontSav = (HFONT)SelectObject(hdc, hfontMap);
}
// cchCodePages shouldn't be 0
ASSERT(cchCodePages);
if (cchCodePages > 0)
{
fRet = _OtherGetTextExtentPointW(hdc, lpwch + cchDone, cchCodePages, &size);
lpSize->cx += size.cx;
}
if (NULL != hfontSav)
{
SelectObject(hdc, hfontSav);
pMLFontLink->ReleaseFont(hfontMap);
hfontSav = NULL;
}
}
pMLFontLink->Release();
return fRet;
}
BOOL GetTextExtentPointWrap(HDC hdc, LPCWSTR lpwch, int cch, LPSIZE lpSize)
{
BOOL fRet = FALSE;
#ifndef WINNT
HFONT hfont = NULL;
HFONT hfontSav = NULL;
#endif
// On NT5, we use system API behavior including fontlink
if (g_bRunOnNT5)
return GetTextExtentPointW(hdc, lpwch, cch, lpSize);
if (cch)
{
// Optimize for all < 128 case
if (cch < 256 && lpwch[0] <= 127)
{
char lpchA[256];
int ich;
BOOL fAscii = TRUE;
for (ich = 0; ich < cch; ich++)
{
WCHAR wch = lpwch[ich];
if (wch <= 127)
lpchA[ich] = (char) wch;
else
{
fAscii = FALSE;
break;
}
}
if (fAscii)
return GetTextExtentPointA(hdc, lpchA, cch, lpSize);
}
#ifndef WINNT
if (g_fMEEnabled && (hfont = GetBiDiFont(hdc)))
hfontSav = (HFONT)SelectObject(hdc, hfont);
#endif
fRet = _GetTextExtentPointWFontLink(hdc, lpwch, cch, lpSize);
}
if (!fRet)
fRet = _OtherGetTextExtentPointW(hdc, lpwch, cch, lpSize);
#ifndef WINNT
if (hfont)
{
SelectObject(hdc, hfontSav);
DeleteObject(hfont);
}
#endif
return fRet;
}
BOOL GetTextExtentPoint32Wrap(HDC hdc, LPCWSTR lpwch, int cch, LPSIZE lpSize)
{
return GetTextExtentPointWrap(hdc, lpwch, cch, lpSize);
}
void _RgwchRgdxpCpToRgchRgdxp(const WCHAR *pwchSrc,
const int *pdxpSrc,
char *pchDest,
int *pdxpDest,
int *pcch,
UINT cpg,
int *pfUsedDefaultChar)
{
int cchIn = *pcch;
int cch;
char *pch = pchDest;
char rgch[2];
int fUsedDefaultChar;
if (pfUsedDefaultChar)
*pfUsedDefaultChar = 0;
while (--cchIn >= 0)
{
cch = WideCharToMultiByte(cpg,
0,
pwchSrc++,
1,
rgch,
sizeof(rgch),
NULL,
&fUsedDefaultChar);
if (pfUsedDefaultChar)
*pfUsedDefaultChar |= fUsedDefaultChar;
if (cch == 0)
{
cch = WideCharToMultiByte(CP_ACP,
0,
pwchSrc-1,
1,
rgch,
sizeof(rgch),
NULL,
NULL);
}
if (cch == 0)
{
ASSERT(FALSE);
return;
}
*(pch++) = rgch[0];
*(pdxpDest++) = *(pdxpSrc++);
if (cch == 2)
{
*(pch++) = rgch[1];
*(pdxpDest++) = 0;
}
}
*pcch = (int)(pch - pchDest);
}
BOOL _ExtTextOutWForWin95(HDC hdc, int xp, int yp, UINT eto,
CONST RECT *lprect, LPCWSTR lpwch, UINT cLen,
CONST INT *lpdxp)
{
WCHAR sz[256];
LPWSTR lpwchAlloc = sz;
LPWSTR lpwchT = (LPWSTR)lpwch;
LPWSTR lpwchEnd = (LPWSTR)lpwch + cLen;
BOOL fCopied = FALSE;
BOOL fRet;
// This is a workaround for Win95 bug which causes U+00B7 character to
// be rendered improperly when using the wide API (substituting U+2219
// achieves the desired effect for some reason!)
while (lpwchT < lpwchEnd)
{
if (*lpwchT == 0x00b7)
{
if (!fCopied)
{
if (cLen > 256)
lpwchAlloc = (LPWSTR)LocalAlloc(LPTR, cLen * sizeof(WCHAR));
memcpy(lpwchAlloc, lpwch, cLen * sizeof(WCHAR));
lpwchT = lpwchAlloc + (lpwchT - lpwch);
lpwchEnd = lpwchAlloc + cLen;
fCopied = TRUE;
}
*lpwchT = 0x2219;
}
lpwchT++;
}
fRet = ExtTextOutW(hdc, xp, yp, eto, lprect, fCopied ? lpwchAlloc : lpwch, cLen, lpdxp);
if (lpwchAlloc != sz)
LocalFree(lpwchAlloc);
return fRet;
}
BOOL _OtherExtTextOutW(HDC hdc, int xp, int yp, UINT eto, CONST RECT *lprect,
LPCWSTR lpwch, UINT cLen, CONST INT *lpdxp)
{
UINT cpg = CP_DEFAULT;
if (!(eto & ETO_GLYPH_INDEX) && cLen < 256 && lpwch[0] <= 127)
{
char lpchA[256];
UINT ich;
BOOL fAscii = TRUE;
for (ich = 0; ich < cLen; ich++)
{
WCHAR wch = lpwch[ich];
if (wch <= 127)
lpchA[ich] = (char) wch;
else
{
fAscii = FALSE;
break;
}
}
if (fAscii)
return ExtTextOutA(hdc, xp, yp, eto, lprect, lpchA, cLen, lpdxp);
}
#ifdef WINNT
return (ExtTextOutW(hdc, xp, yp, eto, lprect, lpwch, cLen, lpdxp));
#else
{
UINT chs;
CHARSETINFO csi;
// BUGBUG: Do we need DT_METAFILE handling in comctl32?
ASSERT(GetDeviceCaps(hdc, TECHNOLOGY) != DT_METAFILE);
chs = GetTextCharsetInfo(hdc, NULL, 0);
TranslateCharsetInfo(IntToPtr_(DWORD *, chs), &csi, TCI_SRCCHARSET);
cpg = csi.ciACP;
// This is a workaround for Win95ME bug
if (FChsBiDi(chs) && IsValidCodePage(cpg) && g_fMEEnabled && !g_bRunOnMemphis)
{
int cchOut = cLen;
int *pdxp = NULL;
char *pchT;
int cchMax;
int fRet = FALSE;
cchMax = cchOut * sizeof(WCHAR);
pchT = (char *)LocalAlloc(LPTR, cchMax);
if (pchT)
{
if (lpdxp)
{
pdxp = (int *)LocalAlloc(LPTR, cchMax * sizeof(int));
if (pdxp)
_RgwchRgdxpCpToRgchRgdxp(lpwch, lpdxp, pchT, pdxp, &cchOut,
cpg, NULL);
}
else
cchOut = WideCharToMultiByte(cpg, 0, lpwch, cchOut, pchT, cchMax, NULL, NULL);
fRet = ExtTextOutA(hdc, xp, yp, eto, lprect, pchT, cchOut, pdxp);
LocalFree(pchT);
}
if (pdxp)
LocalFree(pdxp);
return fRet;
}
// This is a workaround for Win95FE bug
if (FChsDbcs(chs) && IsValidCodePage(cpg) && g_fDBCSEnabled)
{
int cchOut = cLen;
int fUsedDefaultChar;
int *pdxp = NULL;
char *pchT;
int cchMax;
int fRet;
cchMax = cchOut * sizeof(WCHAR);
pchT = (char *)LocalAlloc(LPTR, cchMax);
if (pchT)
{
if (lpdxp)
{
pdxp = (int *)LocalAlloc(LPTR, cchMax * sizeof(int));
_RgwchRgdxpCpToRgchRgdxp(lpwch, lpdxp, pchT, pdxp, &cchOut,
cpg, &fUsedDefaultChar);
if (fUsedDefaultChar)
{
LocalFree(pchT);
if (pdxp)
LocalFree(pdxp);
goto L95GDIHack;
}
fRet = ExtTextOutA(hdc, xp, yp, eto, lprect, pchT, cchOut, pdxp);
}
else
{
cchOut = WideCharToMultiByte(cpg, 0, lpwch, cchOut, pchT, cchMax, NULL, &fUsedDefaultChar);
if (fUsedDefaultChar)
{
LocalFree(pchT);
goto L95GDIHack;
}
fRet = ExtTextOutA(hdc, xp, yp, eto, lprect, pchT, cchOut, NULL);
}
LocalFree(pchT);
}
if (pdxp)
LocalFree(pdxp);
return fRet;
}
else
{
L95GDIHack:
if (g_fDBCSEnabled) // Running on an FE system?
{
// This is a work around for Win95FE bugs that cause GPFs in GDI if multiple
// characters above Unicode 0xFF are passed to ExtTextOutW.
LPCWSTR lpwchT = lpwch;
LPCWSTR lpwchStart = lpwchT;
LPCWSTR lpwchEnd = lpwch + cLen;
BOOL fFirst = TRUE;
BOOL fErased = FALSE;
POINT pt;
int cwch;
BOOL fRet;
int dxpMul = 0;
int dypMul = 0;
int dzpAdvance;
CONST int *lpdxpCur;
while (lpwchT < lpwchEnd)
{
if (*lpwchT > 0x00FF)
{
if (fFirst)
{
// Ok, time to figure out the Escapement
// this is done by rendering a space and discovering the
// delta from the current position UGLY but it works!!!
RECT rect;
WCHAR wch = SPACE_CHAR;
POINT ptT;
UINT uiTextAlign = GetTextAlign(hdc);
// Cache current location
MoveToEx(hdc, xp, yp, &pt);
// Render a safe character (clipped) at xp,yp
rect.top = rect.bottom = rect.left = rect.right = 0;
if (!(uiTextAlign & TA_UPDATECP))
SetTextAlign(hdc, TA_UPDATECP);
fRet = _ExtTextOutWForWin95(hdc, xp, yp, ETO_CLIPPED, &rect, &wch, 1, NULL);
if (!(uiTextAlign & TA_UPDATECP))
SetTextAlign(hdc, TA_NOUPDATECP);
// Get the current point location and calculate escapement from that point
MoveToEx(hdc, pt.x, pt.y, &ptT);
if (ptT.x != xp)
dxpMul = ptT.x > xp ? 1 : -1;
else
dypMul = ptT.y > yp ? 1 : -1;
fFirst = FALSE;
}
if ((cwch = lpwchT - lpwchStart) > 0)
{
lpdxpCur = lpdxp ? lpdxp + (lpwchStart - lpwch) : (int *)NULL;
// Output the run of chars less than 0xFF
fRet = _ExtTextOutWForWin95(hdc, xp, yp, eto, lprect, lpwchStart, cwch, lpdxpCur);
if (!fRet)
return fRet;
eto &= ~ETO_OPAQUE; // Don't erase mutliple times!!!
// Calculate advance amount
if (lpdxpCur)
{
dzpAdvance = 0;
while (cwch--)
dzpAdvance += *lpdxpCur++;
}
else
{
SIZE size;
GetTextExtentPointWrap(hdc, lpwchStart, cwch, &size);
dzpAdvance = size.cx;
}
if (dxpMul)
xp += dzpAdvance * dxpMul;
else
yp += dzpAdvance * dypMul;
lpwchStart = lpwchT;
}
// Output chars above 0xFF one at a time to prevent GPF
lpdxpCur = lpdxp ? lpdxp + (lpwchStart - lpwch) : (int *)NULL;
fRet = _ExtTextOutWForWin95(hdc, xp, yp, eto, lprect, lpwchStart, 1, lpdxpCur);
if (!fRet)
return fRet;
eto &= ~ETO_OPAQUE; // Don't erase mutliple times!!!
if (lpdxpCur)
dzpAdvance = *lpdxpCur;
else
{
SIZE size;
GetTextExtentPointWrap(hdc, lpwchStart, 1, &size);
dzpAdvance = size.cx;
}
if (dxpMul)
xp += dzpAdvance * dxpMul;
else
yp += dzpAdvance * dypMul;
lpwchStart++;
}
lpwchT++;
}
if ((cwch = lpwchT - lpwchStart) > 0)
{
fRet = _ExtTextOutWForWin95(hdc, xp, yp, eto, lprect, lpwchStart, cwch,
lpdxp ? lpdxp + (lpwchStart - lpwch) : (int *)NULL);
}
return fRet;
}
else
return (_ExtTextOutWForWin95(hdc, xp, yp, eto, lprect, lpwch, cLen, lpdxp));
}
}
#endif
ASSERT(FALSE);
return FALSE;
}
BOOL _ExtTextOutWFontLink(HDC hdc, int xp, int yp, UINT eto, CONST RECT *lprect,
LPCWSTR lpwch, UINT cLen, CONST INT *lpdxp)
{
HFONT hfont = NULL;
HFONT hfontSav = NULL;
HFONT hfontMap = NULL;
BOOL fRet = FALSE;
UINT ta;
int fDoTa = FALSE;
int fQueryTa = TRUE;
POINT pt;
int cchDone;
DWORD dwACP, dwFontCodePages, dwCodePages;
long cchCodePages;
IMLangFontLink *pMLFontLink = NULL;
if (cLen == 0)
return FALSE;
switch (FDoFontLink(hdc, &pMLFontLink, lpwch, cLen))
{
case S_OK:
break;
case S_FALSE:
{
// Times by the size of WCHAR should be enough for multibyte string buffer
char *lpStr = (char *)LocalAlloc(LPTR, cLen*sizeof(WCHAR));
if (lpStr)
{
if (cLen = WideCharToMultiByte(CP_ACP,0,lpwch, cLen, lpStr, cLen*sizeof(WCHAR), NULL, NULL))
fRet = ExtTextOutA(hdc, xp, yp, eto, lprect, lpStr, cLen, lpdxp);
LocalFree(lpStr);
}
}
return fRet;
default:
return FALSE;
}
hfont = (HFONT)GetCurrentObject(hdc, OBJ_FONT);
pMLFontLink->GetFontCodePages(hdc, hfont, &dwFontCodePages);
pMLFontLink->CodePageToCodePages(g_uiACP, &dwACP); // Give priority to CP_ACP
// See if whole string can be handled by current font
pMLFontLink->GetStrCodePages(lpwch, cLen, dwACP, &dwCodePages, &cchCodePages);
// current font supports whole string ?
if ((dwFontCodePages & dwCodePages) && cLen == (UINT)cchCodePages)
{
pMLFontLink->Release();
return FALSE;
}
for (cchDone = 0; (UINT)cchDone < cLen; cchDone += cchCodePages)
{
pMLFontLink->GetStrCodePages(lpwch + cchDone, cLen - cchDone, dwACP, &dwCodePages, &cchCodePages);
if (!(dwFontCodePages & dwCodePages))
{
pMLFontLink->MapFont(hdc, dwCodePages, hfont, &hfontMap); // BUGBUG: Baseline?
hfontSav = (HFONT)SelectObject(hdc, hfontMap);
}
// cchCodePages shouldn't be 0
ASSERT(cchCodePages);
if (cchCodePages > 0)
{
// If rendering in multiple parts, need to use TA_UPDATECP
if ((UINT)cchCodePages != cLen && fQueryTa)
{
ta = GetTextAlign(hdc);
if ((ta & TA_UPDATECP) == 0) // Don't do the move if x, y aren't being used
{
MoveToEx(hdc, xp, yp, &pt);
fDoTa = TRUE;
}
fQueryTa = FALSE;
}
if (fDoTa)
SetTextAlign(hdc, ta | TA_UPDATECP);
fRet = _OtherExtTextOutW(hdc, xp, yp, eto, lprect, lpwch + cchDone, cchCodePages,
lpdxp ? lpdxp + cchDone : NULL);
eto = eto & ~ETO_OPAQUE; // Don't do mupltiple OPAQUEs!!!
if (fDoTa)
SetTextAlign(hdc, ta);
if (!fRet)
break;
}
if (NULL != hfontSav)
{
SelectObject(hdc, hfontSav);
pMLFontLink->ReleaseFont(hfontMap);
hfontSav = NULL;
}
}
if (fDoTa) // Don't do the move if x, y aren't being used
MoveToEx(hdc, pt.x, pt.y, NULL);
pMLFontLink->Release();
return fRet;
}
BOOL ExtTextOutWrap(HDC hdc, int xp, int yp, UINT eto, CONST RECT *lprect, LPCWSTR lpwch, UINT cLen, CONST INT *lpdxp)
{
BOOL fRet = FALSE;
#ifndef WINNT
HFONT hfont = NULL;
HFONT hfontSav = NULL;
#endif
// On NT5, we use system API behavior including fontlink
if (g_bRunOnNT5)
return ExtTextOutW(hdc, xp, yp, eto, lprect, lpwch, cLen, lpdxp);
if (cLen == 0)
{
char chT;
return ExtTextOutA(hdc, xp, yp, eto, lprect, &chT, cLen, lpdxp);
}
// Optimize for all < 128 case
if (!(eto & ETO_GLYPH_INDEX) && cLen < 256 && lpwch[0] <= 127)
{
char lpchA[256];
UINT ich;
BOOL fAscii = TRUE;
for (ich = 0; ich < cLen; ich++)
{
WCHAR wch = lpwch[ich];
if (wch <= 127)
lpchA[ich] = (char) wch;
else
{
fAscii = FALSE;
break;
}
}
if (fAscii)
return ExtTextOutA(hdc, xp, yp, eto, lprect, lpchA, cLen, lpdxp);
}
#ifdef DEBUG
TEXTMETRIC tm;
// BisharaK: fix bugs#40706, 43200 -- Meta is assumed false for Glyph out.
if (!(eto & ETO_GLYPH_INDEX))
{
switch (GetObjectType(hdc))
{
case OBJ_METADC:
case OBJ_ENHMETADC:
ASSERT(0 && "MetafileExtTextOutW wrapper need.");
break;
default:
break;
}
}
GetTextMetrics(hdc, &tm);
if (tm.tmCharSet == SYMBOL_CHARSET)
{
ASSERT(0 && "SymbolExtTextOutW wrapper need.");
}
else if (tm.tmPitchAndFamily & TMPF_DEVICE)
{
ASSERT(0 && "DevExtTextOutW wrapper need.");
}
else
#endif
{
#ifndef WINNT
if (g_fMEEnabled && !(eto & ETO_GLYPH_INDEX) && (hfont = GetBiDiFont(hdc)))
hfontSav = (HFONT)SelectObject(hdc, hfont);
#endif
// Font linking support for UI rendering
fRet = _ExtTextOutWFontLink(hdc, xp, yp, eto, lprect, lpwch, cLen, lpdxp);
}
if (!fRet)
fRet = _OtherExtTextOutW(hdc, xp, yp, eto, lprect, lpwch, cLen, lpdxp);
#ifndef WINNT
if (hfont)
{
SelectObject(hdc, hfontSav);
DeleteObject(hfont);
}
#endif
return fRet;
}
BOOL TextOutWrap(HDC hdc, int xp, int yp, LPCWSTR lpwch, int cLen)
{
return ExtTextOutWrap(hdc, xp, yp, 0, NULL, lpwch, cLen, NULL);
}
typedef struct {
RECT rcFormat; // Format rectangle.
int cxTabLength; // Tab length in pixels.
int iXSign;
int iYSign;
int cyLineHeight; // Height of a line based on DT_EXTERNALLEADING
int cxMaxWidth; // Width of the format rectangle.
int cxMaxExtent; // Width of the longest line drawn.
int cxRightMargin; // Right margin in pixels (with proper sign) on DT_NOPREFIX flag.
int cxOverhang; // Character overhang.
} DRAWTEXTDATA, *LPDRAWTEXTDATA;
#define CR 13
#define LF 10
#define DT_HFMTMASK 0x03
#define DT_VFMTMASK 0x0C
// FE support both Kanji and English mnemonic characters,
// toggled from control panel. Both mnemonics are embedded in menu
// resource templates. The following prefixes guide their parsing.
#define CH_ENGLISHPREFIX 0x1E
#define CH_KANJIPREFIX 0x1F
#define CCHELLIPSIS 3
static CONST WCHAR szEllipsis[CCHELLIPSIS+1] = TEXT("...");
// Max length of a full path is around 260. But, most of the time, it will
// be less than 128. So, we alloc only this much on stack. If the string is
// longer, we alloc from local heap (which is slower).
//
// BOGUS: For international versions, we need to give some more margin here.
//
#define MAXBUFFSIZE 128
/***************************************************************************\
* There are word breaking characters which are compatible with
* Japanese Windows 3.1 and FarEast Windows 95.
*
* SJ - Country Japan , Charset SHIFTJIS, Codepage 932.
* GB - Country PRC , Charset GB2312 , Codepage 936.
* B5 - Country Taiwan, Charset BIG5 , Codepage 950.
* WS - Country Korea , Charset WANGSUNG, Codepage 949.
* JB - Country Korea , Charset JOHAB , Codepage 1361. *** LATER ***
*
* [START BREAK CHARACTERS]
*
* These character should not be the last charatcer of the line.
*
* Unicode Japan PRC Taiwan Korea
* -------+---------+---------+---------+---------+
*
* + ASCII
*
* U+0024 (SJ+0024) (WS+0024) Dollar sign
* U+0028 (SJ+0028) (WS+0028) Opening parenthesis
* U+003C (SJ+003C) Less-than sign
* U+005C (SJ+005C) Backslash
* U+005B (SJ+005B) (GB+005B) (WS+005B) Opening square bracket
* U+007B (SJ+007B) (GB+007B) (WS+007B) Opening curly bracket
*
* + General punctuation
*
* U+2018 (WS+A1AE) Single Turned Comma Quotation Mark
* U+201C (WS+A1B0) Double Comma Quotation Mark
*
* + CJK symbols and punctuation
*
* U+3008 (WS+A1B4) Opening Angle Bracket
* U+300A (SJ+8173) (WS+A1B6) Opening Double Angle Bracket
* U+300C (SJ+8175) (WS+A1B8) Opening Corner Bracket
* U+300E (SJ+8177) (WS+A1BA) Opening White Corner Bracket
* U+3010 (SJ+9179) (WS+A1BC) Opening Black Lenticular Bracket
* U+3014 (SJ+816B) (WS+A1B2) Opening Tortoise Shell Bracket
*
* + Fullwidth ASCII variants
*
* U+FF04 (WS+A3A4) Fullwidth Dollar Sign
* U+FF08 (SJ+8169) (WS+A3A8) Fullwidth opening parenthesis
* U+FF1C (SJ+8183) Fullwidth less-than sign
* U+FF3B (SJ+816D) (WS+A3DB) Fullwidth opening square bracket
* U+FF5B (SJ+816F) (WS+A3FB) Fullwidth opening curly bracket
*
* + Halfwidth Katakana variants
*
* U+FF62 (SJ+00A2) Halfwidth Opening Corner Bracket
*
* + Fullwidth symbol variants
*
* U+FFE1 (WS+A1CC) Fullwidth Pound Sign
* U+FFE6 (WS+A3DC) Fullwidth Won Sign
*
* [END BREAK CHARACTERS]
*
* These character should not be the top charatcer of the line.
*
* Unicode Japan PRC Taiwan Korea
* -------+---------+---------+---------+---------+
*
* + ASCII
*
* U+0021 (SJ+0021) (GB+0021) (B5+0021) (WS+0021) Exclamation mark
* U+0025 (WS+0025) Percent Sign
* U+0029 (SJ+0029) (WS+0029) Closing parenthesis
* U+002C (SJ+002C) (GB+002C) (B5+002C) (WS+002C) Comma
* U+002E (SJ+002E) (GB+002E) (B5+002E) (WS+002E) Priod
* U+003A (WS+003A) Colon
* U+003B (WS+003B) Semicolon
* U+003E (SJ+003E) Greater-than sign
* U+003F (SJ+003F) (GB+003F) (B5+003F) (WS+003F) Question mark
* U+005D (SJ+005D) (GB+005D) (B5+005D) (WS+005D) Closing square bracket
* U+007D (SJ+007D) (GB+007D) (B5+007D) (WS+007D) Closing curly bracket
*
* + Latin1
*
* U+00A8 (GB+A1A7) Spacing diaeresis
* U+00B0 (WS+A1C6) Degree Sign
* U+00B7 (B5+A150) Middle Dot
*
* + Modifier letters
*
* U+02C7 (GB+A1A6) Modifier latter hacek
* U+02C9 (GB+A1A5) Modifier letter macron
*
* + General punctuation
*
* U+2013 (B5+A156) En Dash
* U+2014 (b5+A158) Em Dash
* U+2015 (GB+A1AA) Quotation dash
* U+2016 (GB+A1AC) Double vertical bar
* U+2018 (GB+A1AE) Single turned comma quotation mark
* U+2019 (GB+A1AF) (B5+A1A6) (WS+A1AF) Single comma quotation mark
* U+201D (GB+A1B1) (B5+A1A8) (WS+A1B1) Double comma quotation mark
* U+2022 (GB+A1A4) Bullet
* U+2025 (B5+A14C) Two Dot Leader
* U+2026 (GB+A1AD) (B5+A14B) Horizontal ellipsis
* U+2027 (B5+A145) Hyphenation Point
* U+2032 (B5+A1AC) (WS+A1C7) Prime
* U+2033 (WS+A1C8) Double Prime
*
* + Letterlike symbols
*
* U+2103 (WS+A1C9) Degrees Centigrade
*
* + Mathemetical opetartors
*
* U+2236 (GB+A1C3) Ratio
*
* + Form and Chart components
*
* U+2574 (B5+A15A) Forms Light Left
*
* + CJK symbols and punctuation
*
* U+3001 (SJ+8141) (GB+A1A2) (B5+A142) Ideographic comma
* U+3002 (SJ+8142) (GB+A1A3) (B5+A143) Ideographic period
* U+3003 (GB+A1A8) Ditto mark
* U+3005 (GB+A1A9) Ideographic iteration
* U+3009 (GB+A1B5) (B5+A172) (WS+A1B5) Closing angle bracket
* U+300B (SJ+8174) (GB+A1B7) (B5+A16E) (WS+A1B7) Closing double angle bracket
* U+300D (SJ+8176) (GB+A1B9) (B5+A176) (WS+A1B9) Closing corner bracket
* U+300F (SJ+8178) (GB+A1BB) (B5+A17A) (WS+A1BB) Closing white corner bracket
* U+3011 (SJ+817A) (GB+A1BF) (B5+A16A) (WS+A1BD) Closing black lenticular bracket
* U+3015 (SJ+816C) (GB+A1B3) (B5+A166) (WS+A1B3) Closing tortoise shell bracket
* U+3017 (GB+A1BD) Closing white lenticular bracket
* U+301E (B5+A1AA) Double Prime Quotation Mark
*
* + Hiragana
*
* U+309B (SJ+814A) Katakana-Hiragana voiced sound mark
* U+309C (SJ+814B) Katakana-Hiragana semi-voiced sound mark
*
* + CNS 11643 compatibility
*
* U+FE30 (B5+A14A) Glyph for Vertical 2 Dot Leader
* U+FE31 (B5+A157) Glyph For Vertical Em Dash
* U+FE33 (B5+A159) Glyph for Vertical Spacing Underscore
* U+FE34 (B5+A15B) Glyph for Vertical Spacing Wavy Underscore
* U+FE36 (B5+A160) Glyph For Vertical Closing Parenthesis
* U+FE38 (B5+A164) Glyph For Vertical Closing Curly Bracket
* U+FE3A (B5+A168) Glyph For Vertical Closing Tortoise Shell Bracket
* U+FE3C (B5+A16C) Glyph For Vertical Closing Black Lenticular Bracket
* U+FE3E (B5+A16E) Closing Double Angle Bracket
* U+FE40 (B5+A174) Glyph For Vertical Closing Angle Bracket
* U+FE42 (B5+A178) Glyph For Vertical Closing Corner Bracket
* U+FE44 (B5+A17C) Glyph For Vertical Closing White Corner Bracket
* U+FE4F (B5+A15C) Spacing Wavy Underscore
*
* + Small variants
*
* U+FE50 (B5+A14D) Small Comma
* U+FE51 (B5+A14E) Small Ideographic Comma
* U+FE52 (B5+A14F) Small Period
* U+FE54 (B5+A151) Small Semicolon
* U+FE55 (B5+A152) Small Colon
* U+FE56 (B5+A153) Small Question Mark
* U+FE57 (B5+A154) Small Exclamation Mark
* U+FE5A (B5+A17E) Small Closing Parenthesis
* U+FE5C (B5+A1A2) Small Closing Curly Bracket
* U+FE5E (B5+A1A4) Small Closing Tortoise Shell Bracket
*
* + Fullwidth ASCII variants
*
* U+FF01 (SJ+8149) (GB+A3A1) (B5+A149) (WS+A3A1) Fullwidth exclamation mark
* U+FF02 (GB+A3A2) Fullwidth Quotation mark
* U+FF05 (WS+A3A5) Fullwidth Percent Sign
* U+FF07 (GB+A3A7) Fullwidth Apostrophe
* U+FF09 (SJ+816A) (GB+A3A9) (B5+A15E) (WS+A3A9) Fullwidth Closing parenthesis
* U+FF0C (SJ+8143) (GB+A3AC) (B5+A141) (WS+A3AC) Fullwidth comma
* U+FF0D (GB+A3AD) Fullwidth Hyphen-minus
* U+FF0E (SJ+8144) (B5+A144) (WS+A3AE) Fullwidth period
* U+FF1A (GB+A3BA) (B4+A147) (WS+A3BA) Fullwidth colon
* U+FF1B (GB+A3BB) (B5+A146) (WS+A3BB) Fullwidth semicolon
* U+FF1E (SJ+8184) Fullwidth Greater-than sign
* U+FF1F (SJ+8148) (GB+A3BF) (B5+A148) (WS+A3BF) Fullwidth question mark
* U+FF3D (SJ+816E) (GB+A3DD) (WS+A3DD) Fullwidth Closing square bracket
* U+FF5C (B5+A155) Fullwidth Vertical Bar
* U+FF5D (SJ+8170) (B5+A162) (WS+A3FD) Fullwidth Closing curly bracket
* U+FF5E (GB+A1AB) Fullwidth Spacing tilde
*
* + Halfwidth Katakana variants
*
* U+FF61 (SJ+00A1) Halfwidth Ideographic period
* U+FF63 (SJ+00A3) Halfwidth Closing corner bracket
* U+FF64 (SJ+00A4) Halfwidth Ideographic comma
* U+FF9E (SJ+00DE) Halfwidth Katakana voiced sound mark
* U+FF9F (SJ+00DF) Halfwidth Katakana semi-voiced sound mark
*
* + Fullwidth symbol variants
*
* U+FFE0 (WS+A1CB) Fullwidth Cent Sign
*
\***************************************************************************/
#if 0 // not currently used --- FYI only
/***************************************************************************\
* Start Break table
* These character should not be the last charatcer of the line.
\***************************************************************************/
CONST BYTE aASCII_StartBreak[] = {
/* 00 0 1 2 3 4 5 6 7 8 9 A B C D E F */
/* 2X */ 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
/* 3X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
/* 4X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 5X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0,
/* 6X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 7X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
};
CONST BYTE aCJKSymbol_StartBreak[] = {
/* 30 0 1 2 3 4 5 6 7 8 9 A B C D E F */
/* 0X */ 1, 0, 1, 0, 1, 0, 1, 0,
/* 1X */ 1, 0, 0, 0, 1
};
CONST BYTE aFullWidthHalfWidthVariants_StartBreak[] = {
/* FF 0 1 2 3 4 5 6 7 8 9 A B C D E F */
/* 0X */ 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
/* 1X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
/* 2X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 3X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
/* 4X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 5X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
/* 6X */ 0, 0, 1
};
#endif
/***************************************************************************\
* End Break table.
* These character should not be the top charatcer of the line.
\***************************************************************************/
CONST BYTE aASCII_Latin1_EndBreak[] = {
/* 00 0 1 2 3 4 5 6 7 8 9 A B C D E F */
/* 2X */ 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0,
/* 3X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1,
/* 4X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 5X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
/* 6X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 7X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
/* 8X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 9X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* AX */ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
/* BX */ 1, 0, 0, 0, 0, 0, 0, 1
};
CONST BYTE aGeneralPunctuation_EndBreak[] = {
/* 20 0 1 2 3 4 5 6 7 8 9 A B C D E F */
/* 1X */ 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0,
/* 2X */ 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
/* 3X */ 0, 0, 1, 1
};
CONST BYTE aCJKSymbol_EndBreak[] = {
/* 30 0 1 2 3 4 5 6 7 8 9 A B C D E F */
/* 0X */ 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1,
/* 1X */ 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1
};
CONST BYTE aCNS11643_SmallVariants_EndBreak[] = {
/* FE 0 1 2 3 4 5 6 7 8 9 A B C D E F */
/* 3X */ 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
/* 4X */ 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
/* 5X */ 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1
};
CONST BYTE aFullWidthHalfWidthVariants_EndBreak[] = {
/* FF 0 1 2 3 4 5 6 7 8 9 A B C D E F */
/* 0X */ 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0,
/* 1X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1,
/* 2X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 3X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
/* 4X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 5X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0,
/* 6X */ 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 7X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 8X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 9X */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1
};
/***************************************************************************\
* UserIsFELineBreak() - Detects Far East word breaking characters. *
* *
* History: *
* 10-Mar-1996 HideyukN Created. *
\***************************************************************************/
#if 0 // not currently used --- FYI only
BOOL UserIsFELineBreakStart(WCHAR wch)
{
switch (wch>>8)
{
case 0x00:
// Check if word breaking chars in ASCII.
if ((wch >= 0x0024) && (wch <= 0x007B))
return ((BOOL)(aASCII_StartBreak[wch - 0x0024]));
else
return FALSE;
case 0x20:
// Check if work breaking chars in "General punctuation"
if ((wch == 0x2018) || (wch == 0x201C))
return TRUE;
else
return FALSE;
case 0x30:
// Check if word breaking chars in "CJK symbols and punctuation"
// and Hiragana.
if ((wch >= 0x3008) && (wch <= 0x3014))
return ((BOOL)(aCJKSymbol_StartBreak[wch - 0x3008]));
else
return FALSE;
case 0xFF:
// Check if word breaking chars in "Fullwidth ASCII variants",
// "Halfwidth Katakana variants" or "Fullwidth Symbol variants".
if ((wch >= 0xFF04) && (wch <= 0xFF62))
return ((BOOL)(aFullWidthHalfWidthVariants_StartBreak[wch - 0xFF04]));
else if ((wch == 0xFFE1) || (wch == 0xFFE6))
return TRUE;
else
return FALSE;
default:
return FALSE;
}
}
#endif
BOOL UserIsFELineBreakEnd(WCHAR wch)
{
switch (wch>>8)
{
case 0x00:
// Check if word breaking chars in ASCII or Latin1.
if ((wch >= 0x0021) && (wch <= 0x00B7))
return ((BOOL)(aASCII_Latin1_EndBreak[wch - 0x0021]));
else
return FALSE;
case 0x02:
// Check if work breaking chars in "Modifier letters"
if ((wch == 0x02C7) || (wch == 0x02C9))
return TRUE;
else
return FALSE;
case 0x20:
// Check if work breaking chars in "General punctuation"
if ((wch >= 0x2013) && (wch <= 0x2033))
return ((BOOL)(aGeneralPunctuation_EndBreak[wch - 0x2013]));
else
return FALSE;
case 0x21:
// Check if work breaking chars in "Letterlike symbols"
if (wch == 0x2103)
return TRUE;
else
return FALSE;
case 0x22:
// Check if work breaking chars in "Mathemetical opetartors"
if (wch == 0x2236)
return TRUE;
else
return FALSE;
case 0x25:
// Check if work breaking chars in "Form and Chart components"
if (wch == 0x2574)
return TRUE;
else
return FALSE;
case 0x30:
// Check if word breaking chars in "CJK symbols and punctuation"
// and Hiragana.
if ((wch >= 0x3001) && (wch <= 0x301E))
return ((BOOL)(aCJKSymbol_EndBreak[wch - 0x3001]));
else if ((wch == 0x309B) || (wch == 0x309C))
return TRUE;
else
return FALSE;
case 0xFE:
// Check if word breaking chars in "CNS 11643 compatibility"
// or "Small variants".
if ((wch >= 0xFE30) && (wch <= 0xFE5E))
return ((BOOL)(aCNS11643_SmallVariants_EndBreak[wch - 0xFE30]));
else
return FALSE;
case 0xFF:
// Check if word breaking chars in "Fullwidth ASCII variants",
// "Halfwidth Katakana variants" or "Fullwidth symbol variants".
if ((wch >= 0xFF01) && (wch <= 0xFF9F))
return ((BOOL)(aFullWidthHalfWidthVariants_EndBreak[wch - 0xFF01]));
else if (wch >= 0xFFE0)
return TRUE;
else
return FALSE;
default:
return FALSE;
}
}
#define UserIsFELineBreak(wChar) UserIsFELineBreakEnd(wChar)
typedef struct _FULLWIDTH_UNICODE {
WCHAR Start;
WCHAR End;
} FULLWIDTH_UNICODE, *PFULLWIDTH_UNICODE;
#define NUM_FULLWIDTH_UNICODES 4
CONST FULLWIDTH_UNICODE FullWidthUnicodes[] =
{
{ 0x4E00, 0x9FFF }, // CJK_UNIFIED_IDOGRAPHS
{ 0x3040, 0x309F }, // HIRAGANA
{ 0x30A0, 0x30FF }, // KATAKANA
{ 0xAC00, 0xD7A3 } // HANGUL
};
BOOL UserIsFullWidth(WCHAR wChar)
{
int index;
// Early out for ASCII.
if (wChar < 0x0080)
{
// if the character < 0x0080, it should be a halfwidth character.
return FALSE;
}
// Scan FullWdith definition table... most of FullWidth character is
// defined here... this is more faster than call NLS API.
for (index = 0; index < NUM_FULLWIDTH_UNICODES; index++)
{
if ((wChar >= FullWidthUnicodes[index].Start) && (wChar <= FullWidthUnicodes[index].End))
return TRUE;
}
// BUGBUG: We need one more case here to match NT5 implementation - beomoh
// if this Unicode character is mapped to Double-Byte character,
// this is also FullWidth character..
return FALSE;
}
LPCWSTR GetNextWordbreak(LPCWSTR lpch,
LPCWSTR lpchEnd,
DWORD dwFormat,
LPDRAWTEXTDATA lpDrawInfo)
{
/* ichNonWhite is used to make sure we always make progress. */
int ichNonWhite = 1;
int ichComplexBreak = 0; // Breaking opportunity for complex scripts
#if ((DT_WORDBREAK & ~0xff) != 0)
#error cannot use BOOLEAN for DT_WORDBREAK, or you should use "!!" before assigning it
#endif
BOOLEAN fBreakSpace = (BOOLEAN)(dwFormat & DT_WORDBREAK);
// If DT_WORDBREAK and DT_NOFULLWIDTHCHARBREAK are both set, we must
// stop assuming FullWidth characters as word as we're doing in
// NT4 and Win95. Instead, CR/LF and/or white space will only be
// a line-break characters.
BOOLEAN fDbcsCharBreak = (fBreakSpace && !(dwFormat & DT_NOFULLWIDTHCHARBREAK));
// We must terminate this loop before lpch == lpchEnd, otherwise, we may gp fault during *lpch.
while (lpch < lpchEnd)
{
switch (*lpch)
{
case CR:
case LF:
return lpch;
case '\t':
case ' ':
if (fBreakSpace)
return (lpch + ichNonWhite);
// FALL THRU //
default:
// Since most Japanese writing don't use space character
// to separate each word, we define each Kanji character
// as a word.
if (fDbcsCharBreak && UserIsFullWidth(*lpch))
{
if (!ichNonWhite)
return lpch;
// if the next character is the last character of this string,
// We return the character, even this is a "KINSOKU" charcter...
if ((lpch+1) != lpchEnd)
{
// Check next character of FullWidth character.
// if the next character is "KINSOKU" character, the character
// should be handled as a part of previous FullWidth character.
// Never handle is as A character, and should not be a Word also.
if (UserIsFELineBreak(*(lpch+1)))
{
// Then if the character is "KINSOKU" character, we return
// the next of this character,...
return (lpch + 1 + 1);
}
}
// Otherwise, we just return the chracter that is next of FullWidth
// Character. Because we treat A FullWidth chacter as A Word.
return (lpch + 1);
}
lpch++;
ichNonWhite = 0;
}
}
return lpch;
}
// This routine returns the count of accelerator mnemonics and the
// character location (starting at 0) of the character to underline.
// A single CH_PREFIX character will be striped and the following character
// underlined, all double CH_PREFIX character sequences will be replaced by
// a single CH_PREFIX (this is done by PSMTextOut). This routine is used
// to determine the actual character length of the string that will be
// printed, and the location the underline should be placed. Only
// cch characters from the input string will be processed. If the lpstrCopy
// parameter is non-NULL, this routine will make a printable copy of the
// string with all single prefix characters removed and all double prefix
// characters collapsed to a single character. If copying, a maximum
// character count must be specified which will limit the number of
// characters copied.
//
// The location of the single CH_PREFIX is returned in the low order
// word, and the count of CH_PREFIX characters that will be striped
// from the string during printing is in the hi order word. If the
// high order word is 0, the low order word is meaningless. If there
// were no single prefix characters (i.e. nothing to underline), the
// low order word will be -1 (to distinguish from location 0).
//
// These routines assume that there is only one single CH_PREFIX character
// in the string.
//
// WARNING! this rountine returns information in BYTE count not CHAR count
// (so it can easily be passed onto GreExtTextOutW which takes byte
// counts as well)
LONG GetPrefixCount(
LPCWSTR lpstr,
int cch,
LPWSTR lpstrCopy,
int charcopycount)
{
int chprintpos = 0; // Num of chars that will be printed
int chcount = 0; // Num of prefix chars that will be removed
int chprefixloc = -1; // Pos (in printed chars) of the prefix
WCHAR ch;
// If not copying, use a large bogus count...
if (lpstrCopy == NULL)
charcopycount = 32767;
while ((cch-- > 0) && *lpstr && charcopycount-- != 0)
{
// Is this guy a prefix character ?
if ((ch = *lpstr++) == CH_PREFIX)
{
// Yup - increment the count of characters removed during print.
chcount++;
// Is the next also a prefix char?
if (*lpstr != CH_PREFIX)
{
// Nope - this is a real one, mark its location.
chprefixloc = chprintpos;
}
else
{
// yup - simply copy it if copying.
if (lpstrCopy != NULL)
*(lpstrCopy++) = CH_PREFIX;
cch--;
lpstr++;
chprintpos++;
}
}
else if (ch == CH_ENGLISHPREFIX) // Still needs to be parsed
{
// Yup - increment the count of characters removed during print.
chcount++;
// Next character is a real one, mark its location.
chprefixloc = chprintpos;
}
else if (ch == CH_KANJIPREFIX) // Still needs to be parsed
{
// We only support Alpha Numeric(CH_ENGLISHPREFIX).
// no support for Kana(CH_KANJIPREFIX).
// Yup - increment the count of characters removed during print.
chcount++;
if(cch)
{
// don't copy the character
chcount++;
lpstr++;
cch--;
}
}
else
{
// Nope - just inc count of char. that will be printed
chprintpos++;
if (lpstrCopy != NULL)
*(lpstrCopy++) = ch;
}
}
if (lpstrCopy != NULL)
*lpstrCopy = 0;
// Return the character counts
return MAKELONG(chprefixloc, chcount);
}
// Returns total width of prefix character. Japanese Windows has
// three shortcut prefixes, '&',\036 and \037. They may have
// different width.
int KKGetPrefixWidth(HDC hdc, LPCWSTR lpStr, int cch)
{
SIZE size;
SIZE iPrefix1 = {-1L,-1L};
SIZE iPrefix2 = {-1L,-1L};
SIZE iPrefix3 = {-1L,-1L};
int iTotal = 0;
while (cch-- > 0 && *lpStr)
{
switch(*lpStr)
{
case CH_PREFIX:
if (lpStr[1] != CH_PREFIX)
{
if (iPrefix1.cx == -1)
GetTextExtentPointWrap(hdc, lpStr, 1, &iPrefix1);
iTotal += iPrefix1.cx;
}
else
{
lpStr++;
cch--;
}
break;
case CH_ENGLISHPREFIX:
if (iPrefix2.cx == -1)
GetTextExtentPointWrap(hdc, lpStr, 1, &iPrefix2);
iTotal += iPrefix2.cx;
break;
case CH_KANJIPREFIX:
if (iPrefix3.cx == -1)
GetTextExtentPointWrap(hdc, lpStr, 1, &iPrefix3);
iTotal += iPrefix3.cx;
// In NT, always alpha numeric mode, Then we have to sum
// KANA accel key prefix non visible char width.
// so always add the extent for next char.
GetTextExtentPointWrap(hdc, lpStr, 1, &size);
iTotal += size.cx;
break;
default:
// No need to taking care of Double byte since 2nd byte of
// DBC is grater than 0x2f but all shortcut keys are less
// than 0x30.
break;
}
lpStr++;
}
return iTotal;
}
// Outputs the text and puts and _ below the character with an &
// before it. Note that this routine isn't used for menus since menus
// have their own special one so that it is specialized and faster...
void PSMTextOut(
HDC hdc,
int xLeft,
int yTop,
LPWSTR lpsz,
int cch,
DWORD dwFlags)
{
int cx;
LONG textsize, result;
WCHAR achWorkBuffer[255];
WCHAR *pchOut = achWorkBuffer;
TEXTMETRICW textMetric;
SIZE size;
RECT rc;
COLORREF color;
if (dwFlags & DT_NOPREFIX)
{
TextOutWrap(hdc, xLeft, yTop, lpsz, cch);
return;
}
if (cch > sizeof(achWorkBuffer)/sizeof(WCHAR))
{
pchOut = (WCHAR*)LocalAlloc(LPTR, (cch+1) * sizeof(WCHAR));
if (pchOut == NULL)
return;
}
result = GetPrefixCount(lpsz, cch, pchOut, cch);
// DT_PREFIXONLY is a new 5.0 option used when switching from keyboard cues off to on.
if (!(dwFlags & DT_PREFIXONLY))
TextOutWrap(hdc, xLeft, yTop, pchOut, cch - HIWORD(result));
// Any true prefix characters to underline?
if (LOWORD(result) == 0xFFFF || dwFlags & DT_HIDEPREFIX)
{
if (pchOut != achWorkBuffer)
LocalFree(pchOut);
return;
}
if (!GetTextMetricsW(hdc, &textMetric))
{
textMetric.tmOverhang = 0;
textMetric.tmAscent = 0;
}
// For proportional fonts, find starting point of underline.
if (LOWORD(result) != 0)
{
// How far in does underline start (if not at 0th byte.).
GetTextExtentPointWrap(hdc, pchOut, LOWORD(result), &size);
xLeft += size.cx;
// Adjust starting point of underline if not at first char and there is
// an overhang. (Italics or bold fonts.)
xLeft = xLeft - textMetric.tmOverhang;
}
// Adjust for proportional font when setting the length of the underline and
// height of text.
GetTextExtentPointWrap(hdc, pchOut + LOWORD(result), 1, &size);
textsize = size.cx;
// Find the width of the underline character. Just subtract out the overhang
// divided by two so that we look better with italic fonts. This is not
// going to effect embolded fonts since their overhang is 1.
cx = LOWORD(textsize) - textMetric.tmOverhang / 2;
// Get height of text so that underline is at bottom.
yTop += textMetric.tmAscent + 1;
// Draw the underline using the foreground color.
SetRect(&rc, xLeft, yTop, xLeft+cx, yTop+1);
color = SetBkColor(hdc, GetTextColor(hdc));
ExtTextOutWrap(hdc, xLeft, yTop, ETO_OPAQUE, &rc, TEXT(""), 0, NULL);
SetBkColor(hdc, color);
if (pchOut != achWorkBuffer)
LocalFree(pchOut);
}
int DT_GetExtentMinusPrefixes(HDC hdc, LPCWSTR lpchStr, int cchCount, UINT wFormat, int iOverhang)
{
int iPrefixCount;
int cxPrefixes = 0;
WCHAR PrefixChar = CH_PREFIX;
SIZE size;
if (!(wFormat & DT_NOPREFIX) &&
(iPrefixCount = HIWORD(GetPrefixCount(lpchStr, cchCount, NULL, 0))))
{
// Kanji Windows has three shortcut prefixes...
if (g_fDBCSEnabled)
{
// 16bit apps compatibility
cxPrefixes = KKGetPrefixWidth(hdc, lpchStr, cchCount) - (iPrefixCount * iOverhang);
}
else
{
cxPrefixes = GetTextExtentPointWrap(hdc, &PrefixChar, 1, &size);
cxPrefixes = size.cx - iOverhang;
cxPrefixes *= iPrefixCount;
}
}
GetTextExtentPointWrap(hdc, lpchStr, cchCount, &size);
return (size.cx - cxPrefixes);
}
// This will draw the given string in the given location without worrying
// about the left/right justification. Gets the extent and returns it.
// If fDraw is TRUE and if NOT DT_CALCRECT, this draws the text.
// NOTE: This returns the extent minus Overhang.
int DT_DrawStr(HDC hdc, int xLeft, int yTop, LPCWSTR lpchStr,
int cchCount, BOOL fDraw, UINT wFormat,
LPDRAWTEXTDATA lpDrawInfo)
{
LPCWSTR lpch;
int iLen;
int cxExtent;
int xOldLeft = xLeft; // Save the xLeft given to compute the extent later
int xTabLength = lpDrawInfo->cxTabLength;
int iTabOrigin = lpDrawInfo->rcFormat.left;
// Check if the tabs need to be expanded
if (wFormat & DT_EXPANDTABS)
{
while (cchCount)
{
// Look for a tab
for (iLen = 0, lpch = lpchStr; iLen < cchCount; iLen++)
if(*lpch++ == TEXT('\t'))
break;
// Draw text, if any, upto the tab
if (iLen)
{
// Draw the substring taking care of the prefixes.
if (fDraw && !(wFormat & DT_CALCRECT)) // Only if we need to draw text
PSMTextOut(hdc, xLeft, yTop, (LPWSTR)lpchStr, iLen, wFormat);
// Get the extent of this sub string and add it to xLeft.
xLeft += DT_GetExtentMinusPrefixes(hdc, lpchStr, iLen, wFormat, lpDrawInfo->cxOverhang) - lpDrawInfo->cxOverhang;
}
//if a TAB was found earlier, calculate the start of next sub-string.
if (iLen < cchCount)
{
iLen++; // Skip the tab
if (xTabLength) // Tab length could be zero
xLeft = (((xLeft - iTabOrigin)/xTabLength) + 1)*xTabLength + iTabOrigin;
}
// Calculate the details of the string that remains to be drawn.
cchCount -= iLen;
lpchStr = lpch;
}
cxExtent = xLeft - xOldLeft;
}
else
{
// If required, draw the text
if (fDraw && !(wFormat & DT_CALCRECT))
PSMTextOut(hdc, xLeft, yTop, (LPWSTR)lpchStr, cchCount, wFormat);
// Compute the extent of the text.
cxExtent = DT_GetExtentMinusPrefixes(hdc, lpchStr, cchCount, wFormat, lpDrawInfo->cxOverhang) - lpDrawInfo->cxOverhang;
}
return cxExtent;
}
// This function draws one complete line with proper justification
void DT_DrawJustifiedLine(HDC hdc, int yTop, LPCWSTR lpchLineSt, int cchCount, UINT wFormat, LPDRAWTEXTDATA lpDrawInfo)
{
LPRECT lprc;
int cxExtent;
int xLeft;
lprc = &(lpDrawInfo->rcFormat);
xLeft = lprc->left;
// Handle the special justifications (right or centered) properly.
if (wFormat & (DT_CENTER | DT_RIGHT))
{
cxExtent = DT_DrawStr(hdc, xLeft, yTop, lpchLineSt, cchCount, FALSE, wFormat, lpDrawInfo)
+ lpDrawInfo->cxOverhang;
if(wFormat & DT_CENTER)
xLeft = lprc->left + (((lprc->right - lprc->left) - cxExtent) >> 1);
else
xLeft = lprc->right - cxExtent;
}
else
xLeft = lprc->left;
// Draw the whole line.
cxExtent = DT_DrawStr(hdc, xLeft, yTop, lpchLineSt, cchCount, TRUE, wFormat, lpDrawInfo)
+ lpDrawInfo->cxOverhang;
if (cxExtent > lpDrawInfo->cxMaxExtent)
lpDrawInfo->cxMaxExtent = cxExtent;
}
// This is called at the begining of DrawText(); This initializes the
// DRAWTEXTDATA structure passed to this function with all the required info.
BOOL DT_InitDrawTextInfo(
HDC hdc,
LPRECT lprc,
UINT wFormat,
LPDRAWTEXTDATA lpDrawInfo,
LPDRAWTEXTPARAMS lpDTparams)
{
SIZE sizeViewPortExt = {0, 0}, sizeWindowExt = {0, 0};
TEXTMETRICW tm;
LPRECT lprcDest;
int iTabLength = 8; // Default Tab length is 8 characters.
int iLeftMargin;
int iRightMargin;
if (lpDTparams)
{
// Only if DT_TABSTOP flag is mentioned, we must use the iTabLength field.
if (wFormat & DT_TABSTOP)
iTabLength = lpDTparams->iTabLength;
iLeftMargin = lpDTparams->iLeftMargin;
iRightMargin = lpDTparams->iRightMargin;
}
else
iLeftMargin = iRightMargin = 0;
// Get the View port and Window extents for the given DC
// If this call fails, hdc must be invalid
if (!GetViewportExtEx(hdc, &sizeViewPortExt))
return FALSE;
GetWindowExtEx(hdc, &sizeWindowExt);
// For the current mapping mode, find out the sign of x from left to right.
lpDrawInfo->iXSign = (((sizeViewPortExt.cx ^ sizeWindowExt.cx) & 0x80000000) ? -1 : 1);
// For the current mapping mode, find out the sign of y from top to bottom.
lpDrawInfo->iYSign = (((sizeViewPortExt.cy ^ sizeWindowExt.cy) & 0x80000000) ? -1 : 1);
// Calculate the dimensions of the current font in this DC.
GetTextMetrics(hdc, &tm);
// cyLineHeight is in pixels (This will be signed).
lpDrawInfo->cyLineHeight = (tm.tmHeight +
((wFormat & DT_EXTERNALLEADING) ? tm.tmExternalLeading : 0)) * lpDrawInfo->iYSign;
// cxTabLength is the tab length in pixels (This will not be signed)
lpDrawInfo->cxTabLength = tm.tmAveCharWidth * iTabLength;
// Set the cxOverhang
lpDrawInfo->cxOverhang = tm.tmOverhang;
// Set up the format rectangle based on the margins.
lprcDest = &(lpDrawInfo->rcFormat);
*lprcDest = *lprc;
// We need to do the following only if the margins are given
if (iLeftMargin | iRightMargin)
{
lprcDest->left += iLeftMargin * lpDrawInfo->iXSign;
lprcDest->right -= (lpDrawInfo->cxRightMargin = iRightMargin * lpDrawInfo->iXSign);
}
else
lpDrawInfo->cxRightMargin = 0; // Initialize to zero.
// cxMaxWidth is unsigned.
lpDrawInfo->cxMaxWidth = (lprcDest->right - lprcDest->left) * lpDrawInfo->iXSign;
lpDrawInfo->cxMaxExtent = 0; // Initialize this to zero.
return TRUE;
}
// In the case of WORDWRAP, we need to treat the white spaces at the
// begining/end of each line specially. This function does that.
// lpStNext = points to the begining of next line.
// lpiCount = points to the count of characters in the current line.
LPCWSTR DT_AdjustWhiteSpaces(LPCWSTR lpStNext, LPINT lpiCount, UINT wFormat)
{
switch (wFormat & DT_HFMTMASK)
{
case DT_LEFT:
// Prevent a white space at the begining of a left justfied text.
// Is there a white space at the begining of next line......
if ((*lpStNext == L' ') || (*lpStNext == L'\t'))
{
// ...then, exclude it from next line.
lpStNext++;
}
break;
case DT_RIGHT:
// Prevent a white space at the end of a RIGHT justified text.
// Is there a white space at the end of current line,.......
if ((*(lpStNext-1) == L' ') || (*(lpStNext - 1) == L'\t'))
{
// .....then, Skip the white space from the current line.
(*lpiCount)--;
}
break;
case DT_CENTER:
// Exclude white spaces from the begining and end of CENTERed lines.
// If there is a white space at the end of current line.......
if ((*(lpStNext-1) == L' ') || (*(lpStNext - 1) == L'\t'))
(*lpiCount)--; //...., don't count it for justification.
// If there is a white space at the begining of next line.......
if ((*lpStNext == L' ') || (*lpStNext == L'\t'))
lpStNext++; //...., exclude it from next line.
break;
}
return lpStNext;
}
// A word needs to be broken across lines and this finds out where to break it.
LPCWSTR DT_BreakAWord(HDC hdc, LPCWSTR lpchText, int iLength, int iWidth, UINT wFormat, int iOverhang)
{
int iLow = 0, iHigh = iLength;
int iNew;
while ((iHigh - iLow) > 1)
{
iNew = iLow + (iHigh - iLow)/2;
if(DT_GetExtentMinusPrefixes(hdc, lpchText, iNew, wFormat, iOverhang) > iWidth)
iHigh = iNew;
else
iLow = iNew;
}
// If the width is too low, we must print atleast one char per line.
// Else, we will be in an infinite loop.
if(!iLow && iLength)
iLow = 1;
return (lpchText+iLow);
}
// This finds out the location where we can break a line.
// Returns LPCSTR to the begining of next line.
// Also returns via lpiLineLength, the length of the current line.
// NOTE: (lpstNextLineStart - lpstCurrentLineStart) is not equal to the
// line length; This is because, we exclude some white spaces at the begining
// and/or end of lines; Also, CR/LF is excluded from the line length.
LPWSTR DT_GetLineBreak(
HDC hdc,
LPCWSTR lpchLineStart,
int cchCount,
DWORD dwFormat,
LPINT lpiLineLength,
LPDRAWTEXTDATA lpDrawInfo)
{
LPCWSTR lpchText, lpchEnd, lpch, lpchLineEnd;
int cxStart, cxExtent, cxNewExtent;
BOOL fAdjustWhiteSpaces = FALSE;
WCHAR ch;
cxStart = lpDrawInfo->rcFormat.left;
cxExtent = cxNewExtent = 0;
lpchText = lpchLineStart;
lpchEnd = lpchLineStart + cchCount;
while(lpchText < lpchEnd)
{
lpchLineEnd = lpch = GetNextWordbreak(lpchText, lpchEnd, dwFormat, lpDrawInfo);
// DT_DrawStr does not return the overhang; Otherwise we will end up
// adding one overhang for every word in the string.
// For simulated Bold fonts, the summation of extents of individual
// words in a line is greater than the extent of the whole line. So,
// always calculate extent from the LineStart.
// BUGTAG: #6054 -- Win95B -- SANKAR -- 3/9/95 --
cxNewExtent = DT_DrawStr(hdc, cxStart, 0, lpchLineStart, (int)(((PBYTE)lpch - (PBYTE)lpchLineStart)/sizeof(WCHAR)),
FALSE, dwFormat, lpDrawInfo);
if ((dwFormat & DT_WORDBREAK) && ((cxNewExtent + lpDrawInfo->cxOverhang) > lpDrawInfo->cxMaxWidth))
{
// Are there more than one word in this line?
if (lpchText != lpchLineStart)
{
lpchLineEnd = lpch = lpchText;
fAdjustWhiteSpaces = TRUE;
}
else
{
//One word is longer than the maximum width permissible.
//See if we are allowed to break that single word.
if((dwFormat & DT_EDITCONTROL) && !(dwFormat & DT_WORD_ELLIPSIS))
{
lpchLineEnd = lpch = DT_BreakAWord(hdc, lpchText, (int)(((PBYTE)lpch - (PBYTE)lpchText)/sizeof(WCHAR)),
lpDrawInfo->cxMaxWidth - cxExtent, dwFormat, lpDrawInfo->cxOverhang); //Break that word
//Note: Since we broke in the middle of a word, no need to
// adjust for white spaces.
}
else
{
fAdjustWhiteSpaces = TRUE;
// Check if we need to end this line with ellipsis
if(dwFormat & DT_WORD_ELLIPSIS)
{
// Don't do this if already at the end of the string.
if (lpch < lpchEnd)
{
// If there are CR/LF at the end, skip them.
if ((ch = *lpch) == CR || ch == LF)
{
if ((++lpch < lpchEnd) && (*lpch == (WCHAR)(ch ^ (LF ^ CR))))
lpch++;
fAdjustWhiteSpaces = FALSE;
}
}
}
}
}
// Well! We found a place to break the line. Let us break from this loop;
break;
}
else
{
// Don't do this if already at the end of the string.
if (lpch < lpchEnd)
{
if ((ch = *lpch) == CR || ch == LF)
{
if ((++lpch < lpchEnd) && (*lpch == (WCHAR)(ch ^ (LF ^ CR))))
lpch++;
fAdjustWhiteSpaces = FALSE;
break;
}
}
}
// Point at the beginning of the next word.
lpchText = lpch;
cxExtent = cxNewExtent;
}
// Calculate the length of current line.
*lpiLineLength = (INT)((PBYTE)lpchLineEnd - (PBYTE)lpchLineStart)/sizeof(WCHAR);
// Adjust the line length and lpch to take care of spaces.
if(fAdjustWhiteSpaces && (lpch < lpchEnd))
lpch = DT_AdjustWhiteSpaces(lpch, lpiLineLength, dwFormat);
// return the begining of next line;
return (LPWSTR)lpch;
}
// This function checks whether the given string fits within the given
// width or we need to add end-ellipse. If it required end-ellipses, it
// returns TRUE and it returns the number of characters that are saved
// in the given string via lpCount.
BOOL NeedsEndEllipsis(
HDC hdc,
LPCWSTR lpchText,
LPINT lpCount,
LPDRAWTEXTDATA lpDTdata,
UINT wFormat)
{
int cchText;
int ichMin, ichMax, ichMid;
int cxMaxWidth;
int iOverhang;
int cxExtent;
SIZE size;
cchText = *lpCount; // Get the current count.
if (cchText == 0)
return FALSE;
cxMaxWidth = lpDTdata->cxMaxWidth;
iOverhang = lpDTdata->cxOverhang;
cxExtent = DT_GetExtentMinusPrefixes(hdc, lpchText, cchText, wFormat, iOverhang);
if (cxExtent <= cxMaxWidth)
return FALSE;
// Reserve room for the "..." ellipses;
// (Assumption: The ellipses don't have any prefixes!)
GetTextExtentPointWrap(hdc, szEllipsis, CCHELLIPSIS, &size);
cxMaxWidth -= size.cx - iOverhang;
// If no room for ellipses, always show first character.
//
ichMax = 1;
if (cxMaxWidth > 0)
{
// Binary search to find characters that will fit.
ichMin = 0;
ichMax = cchText;
while (ichMin < ichMax)
{
// Be sure to round up, to make sure we make progress in
// the loop if ichMax == ichMin + 1.
ichMid = (ichMin + ichMax + 1) / 2;
cxExtent = DT_GetExtentMinusPrefixes(hdc, lpchText, ichMid, wFormat, iOverhang);
if (cxExtent < cxMaxWidth)
ichMin = ichMid;
else
{
if (cxExtent > cxMaxWidth)
ichMax = ichMid - 1;
else
{
// Exact match up up to ichMid: just exit.
ichMax = ichMid;
break;
}
}
}
// Make sure we always show at least the first character...
if (ichMax < 1)
ichMax = 1;
}
*lpCount = ichMax;
return TRUE;
}
// Returns a pointer to the last component of a path string.
//
// in:
// path name, either fully qualified or not
//
// returns:
// pointer into the path where the path is. if none is found
// returns a poiter to the start of the path
//
// c:\foo\bar -> bar
// c:\foo -> foo
// c:\foo\ -> c:\foo\ (REVIEW: is this case busted?)
// c:\ -> c:\ (REVIEW: this case is strange)
// c: -> c:
// foo -> foo
LPWSTR PathFindFileName(LPCWSTR pPath, int cchText)
{
LPCWSTR pT;
for (pT = pPath; cchText > 0 && *pPath; pPath++, cchText--)
{
if ((pPath[0] == L'\\' || pPath[0] == L':') && pPath[1])
pT = pPath + 1;
}
return (LPWSTR)pT;
}
// This adds a path ellipse to the given path name.
// Returns TRUE if the resultant string's extent is less the the
// cxMaxWidth. FALSE, if otherwise.
int AddPathEllipsis(
HDC hdc,
LPWSTR lpszPath,
int cchText,
UINT wFormat,
int cxMaxWidth,
int iOverhang)
{
int iLen;
UINT dxFixed, dxEllipsis;
LPWSTR lpEnd; /* end of the unfixed string */
LPWSTR lpFixed; /* start of text that we always display */
BOOL bEllipsisIn;
int iLenFixed;
SIZE size;
lpFixed = PathFindFileName(lpszPath, cchText);
if (lpFixed != lpszPath)
lpFixed--; // point at the slash
else
return cchText;
lpEnd = lpFixed;
bEllipsisIn = FALSE;
iLenFixed = cchText - (int)(lpFixed - lpszPath);
dxFixed = DT_GetExtentMinusPrefixes(hdc, lpFixed, iLenFixed, wFormat, iOverhang);
// It is assumed that the "..." string does not have any prefixes ('&').
GetTextExtentPointWrap(hdc, szEllipsis, CCHELLIPSIS, &size);
dxEllipsis = size.cx - iOverhang;
while (TRUE)
{
iLen = dxFixed + DT_GetExtentMinusPrefixes(hdc, lpszPath, (int)((PBYTE)lpEnd - (PBYTE)lpszPath)/sizeof(WCHAR),
wFormat, iOverhang) - iOverhang;
if (bEllipsisIn)
iLen += dxEllipsis;
if (iLen <= cxMaxWidth)
break;
bEllipsisIn = TRUE;
if (lpEnd <= lpszPath)
{
// Things didn't fit.
lpEnd = lpszPath;
break;
}
// Step back a character.
lpEnd--;
}
if (bEllipsisIn && (lpEnd + CCHELLIPSIS < lpFixed))
{
// NOTE: the strings could over lap here. So, we use LCopyStruct.
MoveMemory((lpEnd + CCHELLIPSIS), lpFixed, iLenFixed * sizeof(WCHAR));
CopyMemory(lpEnd, szEllipsis, CCHELLIPSIS * sizeof(WCHAR));
cchText = (int)(lpEnd - lpszPath) + CCHELLIPSIS + iLenFixed;
// now we can NULL terminate the string
*(lpszPath + cchText) = TEXT('\0');
}
return cchText;
}
// This function returns the number of characters actually drawn.
int AddEllipsisAndDrawLine(
HDC hdc,
int yLine,
LPCWSTR lpchText,
int cchText,
DWORD dwDTformat,
LPDRAWTEXTDATA lpDrawInfo)
{
LPWSTR pEllipsis = NULL;
WCHAR szTempBuff[MAXBUFFSIZE];
LPWSTR lpDest;
BOOL fAlreadyCopied = FALSE;
// Check if this is a filename with a path AND
// Check if the width is too narrow to hold all the text.
if ((dwDTformat & DT_PATH_ELLIPSIS) &&
((DT_GetExtentMinusPrefixes(hdc, lpchText, cchText, dwDTformat, lpDrawInfo->cxOverhang)) > lpDrawInfo->cxMaxWidth))
{
// We need to add Path-Ellipsis. See if we can do it in-place.
if (!(dwDTformat & DT_MODIFYSTRING)) {
// NOTE: When you add Path-Ellipsis, the string could grow by
// CCHELLIPSIS bytes.
if((cchText + CCHELLIPSIS + 1) <= MAXBUFFSIZE)
lpDest = szTempBuff;
else
{
// Alloc the buffer from local heap.
if(!(pEllipsis = (LPWSTR)LocalAlloc(LPTR, (cchText+CCHELLIPSIS+1)*sizeof(WCHAR))))
return 0;
lpDest = (LPWSTR)pEllipsis;
}
// Source String may not be NULL terminated. So, copy just
// the given number of characters.
CopyMemory(lpDest, lpchText, cchText*sizeof(WCHAR));
lpchText = lpDest; // lpchText points to the copied buff.
fAlreadyCopied = TRUE; // Local copy has been made.
}
// Add the path ellipsis now!
cchText = AddPathEllipsis(hdc, (LPWSTR)lpchText, cchText, dwDTformat, lpDrawInfo->cxMaxWidth, lpDrawInfo->cxOverhang);
}
// Check if end-ellipsis are to be added.
if ((dwDTformat & (DT_END_ELLIPSIS | DT_WORD_ELLIPSIS)) &&
NeedsEndEllipsis(hdc, lpchText, &cchText, lpDrawInfo, dwDTformat))
{
// We need to add end-ellipsis; See if we can do it in-place.
if (!(dwDTformat & DT_MODIFYSTRING) && !fAlreadyCopied)
{
// See if the string is small enough for the buff on stack.
if ((cchText+CCHELLIPSIS+1) <= MAXBUFFSIZE)
lpDest = szTempBuff; // If so, use it.
else {
// Alloc the buffer from local heap.
if (!(pEllipsis = (LPWSTR)LocalAlloc(LPTR, (cchText+CCHELLIPSIS+1)*sizeof(WCHAR))))
return 0;
lpDest = pEllipsis;
}
// Make a copy of the string in the local buff.
CopyMemory(lpDest, lpchText, cchText*sizeof(WCHAR));
lpchText = lpDest;
}
// Add an end-ellipsis at the proper place.
CopyMemory((LPWSTR)(lpchText+cchText), szEllipsis, (CCHELLIPSIS+1)*sizeof(WCHAR));
cchText += CCHELLIPSIS;
}
// Draw the line that we just formed.
DT_DrawJustifiedLine(hdc, yLine, lpchText, cchText, dwDTformat, lpDrawInfo);
// Free the block allocated for End-Ellipsis.
if (pEllipsis)
LocalFree(pEllipsis);
return cchText;
}
#ifdef WINNT
BOOL IsComplexScriptPresent(LPWSTR lpchText, int cchText)
{
if (g_bComplexPlatform) {
for (int i = 0; i < cchText; i++) {
if (InRange(lpchText[i], 0x0590, 0x0FFF)) {
return TRUE;
}
}
}
return FALSE;
}
#else
#define IsComplexScriptPresent(lpchText, cchText) g_bComplexPlatform
#endif
int DrawTextExPrivWrap(
HDC hdc,
LPWSTR lpchText,
int cchText,
LPRECT lprc,
UINT dwDTformat,
LPDRAWTEXTPARAMS lpDTparams)
{
DRAWTEXTDATA DrawInfo;
WORD wFormat = LOWORD(dwDTformat);
LPWSTR lpchTextBegin;
LPWSTR lpchEnd;
LPWSTR lpchNextLineSt;
int iLineLength;
int iySign;
int yLine;
int yLastLineHeight;
HRGN hrgnClip;
int iLineCount;
RECT rc;
BOOL fLastLine;
WCHAR ch;
UINT oldAlign;
// On NT5, we use system API behavior including fontlink
if (g_bRunOnNT5)
return DrawTextExW(hdc, lpchText, cchText, lprc, dwDTformat, lpDTparams);
if ((cchText == 0) && lpchText && (*lpchText))
{
// infoview.exe passes lpchText that points to '\0'
// Lotus Notes doesn't like getting a zero return here
return 1;
}
if (cchText == -1)
cchText = lstrlenW(lpchText);
else if (lpchText[cchText - 1] == L'\0')
cchText--; // accommodate counting of NULLS for ME
// We got the string length, then check if it a complex string or not.
// If yes then call the system DrawTextEx API to do the job it knows how to
// handle the complex scripts.
if (IsComplexScriptPresent(lpchText, cchText))
{
#ifdef WINNT
//Call the system DrawtextExW
return DrawTextExW(hdc, lpchText, cchText, lprc, dwDTformat, lpDTparams);
#else
HFONT hfont = NULL;
HFONT hfontSav = NULL;
int iRet;
if (hfont = GetBiDiFont(hdc))
hfontSav = (HFONT)SelectObject(hdc, hfont);
CStrIn str(lpchText, cchText);
iRet = DrawTextExA(hdc, str, str.strlen(), lprc, dwDTformat, lpDTparams);
if (hfont)
{
SelectObject(hdc, hfontSav);
DeleteObject(hfont);
}
return iRet;
#endif
}
if ((lpDTparams) && (lpDTparams->cbSize != sizeof(DRAWTEXTPARAMS)))
{
ASSERT(0 && "DrawTextExWorker: cbSize is invalid");
return 0;
}
// If DT_MODIFYSTRING is specified, then check for read-write pointer.
if ((dwDTformat & DT_MODIFYSTRING) &&
(dwDTformat & (DT_END_ELLIPSIS | DT_PATH_ELLIPSIS)))
{
if(IsBadWritePtr(lpchText, cchText))
{
ASSERT(0 && "DrawTextExWorker: For DT_MODIFYSTRING, lpchText must be read-write");
return 0;
}
}
// Initialize the DrawInfo structure.
if (!DT_InitDrawTextInfo(hdc, lprc, dwDTformat, (LPDRAWTEXTDATA)&DrawInfo, lpDTparams))
return 0;
// If the rect is too narrow or the margins are too wide.....Just forget it!
//
// If wordbreak is specified, the MaxWidth must be a reasonable value.
// This check is sufficient because this will allow CALCRECT and NOCLIP
// cases. --SANKAR.
//
// This also fixed all of our known problems with AppStudio.
if (DrawInfo.cxMaxWidth <= 0)
{
if (wFormat & DT_WORDBREAK)
{
ASSERT(0 && "DrawTextExW: FAILURE DrawInfo.cxMaxWidth <= 0");
return 1;
}
}
// if we're not doing the drawing, initialise the lpk-dll
if (dwDTformat & DT_RTLREADING)
oldAlign = SetTextAlign(hdc, TA_RTLREADING | GetTextAlign(hdc));
// If we need to clip, let us do that.
if (!(wFormat & DT_NOCLIP))
{
// Save clipping region so we can restore it later.
hrgnClip = CreateRectRgn(0,0,0,0);
if (hrgnClip != NULL)
{
if (GetClipRgn(hdc, hrgnClip) != 1)
{
DeleteObject(hrgnClip);
hrgnClip = (HRGN)-1;
}
rc = *lprc;
IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);
}
}
else
hrgnClip = NULL;
lpchTextBegin = lpchText;
lpchEnd = lpchText + cchText;
ProcessDrawText:
iLineCount = 0; // Reset number of lines to 1.
yLine = lprc->top;
if (wFormat & DT_SINGLELINE)
{
iLineCount = 1; // It is a single line.
// Process single line DrawText.
switch (wFormat & DT_VFMTMASK)
{
case DT_BOTTOM:
yLine = lprc->bottom - DrawInfo.cyLineHeight;
break;
case DT_VCENTER:
yLine = lprc->top + ((lprc->bottom - lprc->top - DrawInfo.cyLineHeight) / 2);
break;
}
cchText = AddEllipsisAndDrawLine(hdc, yLine, lpchText, cchText, dwDTformat, &DrawInfo);
yLine += DrawInfo.cyLineHeight;
lpchText += cchText;
}
else
{
// Multiline
// If the height of the rectangle is not an integral multiple of the
// average char height, then it is possible that the last line drawn
// is only partially visible. However, if DT_EDITCONTROL style is
// specified, then we must make sure that the last line is not drawn if
// it is going to be partially visible. This will help imitate the
// appearance of an edit control.
if (wFormat & DT_EDITCONTROL)
yLastLineHeight = DrawInfo.cyLineHeight;
else
yLastLineHeight = 0;
iySign = DrawInfo.iYSign;
fLastLine = FALSE;
// Process multiline DrawText.
while ((lpchText < lpchEnd) && (!fLastLine))
{
// Check if the line we are about to draw is the last line that needs
// to be drawn.
// Let us check if the display goes out of the clip rect and if so
// let us stop here, as an optimisation;
if (!(wFormat & DT_CALCRECT) && // We don't need to calc rect?
!(wFormat & DT_NOCLIP) && // Must we clip the display ?
// Are we outside the rect?
((yLine + DrawInfo.cyLineHeight + yLastLineHeight)*iySign > (lprc->bottom*iySign)))
{
fLastLine = TRUE; // Let us quit this loop
}
// We do the Ellipsis processing only for the last line.
if (fLastLine && (dwDTformat & (DT_END_ELLIPSIS | DT_PATH_ELLIPSIS)))
lpchText += AddEllipsisAndDrawLine(hdc, yLine, lpchText, cchText, dwDTformat, &DrawInfo);
else
{
lpchNextLineSt = (LPWSTR)DT_GetLineBreak(hdc, lpchText, cchText, dwDTformat, &iLineLength, &DrawInfo);
// Check if we need to put ellipsis at the end of this line.
// Also check if this is the last line.
if ((dwDTformat & DT_WORD_ELLIPSIS) ||
((lpchNextLineSt >= lpchEnd) && (dwDTformat & (DT_END_ELLIPSIS | DT_PATH_ELLIPSIS))))
AddEllipsisAndDrawLine(hdc, yLine, lpchText, iLineLength, dwDTformat, &DrawInfo);
else
DT_DrawJustifiedLine(hdc, yLine, lpchText, iLineLength, dwDTformat, &DrawInfo);
cchText -= (int)((PBYTE)lpchNextLineSt - (PBYTE)lpchText) / sizeof(WCHAR);
lpchText = lpchNextLineSt;
}
iLineCount++; // We draw one more line.
yLine += DrawInfo.cyLineHeight;
}
// For Win3.1 and NT compatibility, if the last char is a CR or a LF
// then the height returned includes one more line.
if (!(dwDTformat & DT_EDITCONTROL) &&
(lpchEnd > lpchTextBegin) && // If zero length it will fault.
(((ch = (*(lpchEnd-1))) == CR) || (ch == LF)))
yLine += DrawInfo.cyLineHeight;
}
// If DT_CALCRECT, modify width and height of rectangle to include
// all of the text drawn.
if (wFormat & DT_CALCRECT)
{
DrawInfo.rcFormat.right = DrawInfo.rcFormat.left + DrawInfo.cxMaxExtent * DrawInfo.iXSign;
lprc->right = DrawInfo.rcFormat.right + DrawInfo.cxRightMargin;
// If the Width is more than what was provided, we have to redo all
// the calculations, because, the number of lines can be less now.
// (We need to do this only if we have more than one line).
if((iLineCount > 1) && (DrawInfo.cxMaxExtent > DrawInfo.cxMaxWidth))
{
DrawInfo.cxMaxWidth = DrawInfo.cxMaxExtent;
lpchText = lpchTextBegin;
cchText = (int)((PBYTE)lpchEnd - (PBYTE)lpchTextBegin) / sizeof(WCHAR);
goto ProcessDrawText; // Start all over again!
}
lprc->bottom = yLine;
}
if (hrgnClip != NULL)
{
if (hrgnClip == (HRGN)-1)
ExtSelectClipRgn(hdc, NULL, RGN_COPY);
else
{
ExtSelectClipRgn(hdc, hrgnClip, RGN_COPY);
DeleteObject(hrgnClip);
}
}
if (dwDTformat & DT_RTLREADING)
SetTextAlign(hdc, oldAlign);
// Copy the number of characters actually drawn
if(lpDTparams != NULL)
lpDTparams->uiLengthDrawn = (UINT)((PBYTE)lpchText - (PBYTE)lpchTextBegin) / sizeof(WCHAR);
if (yLine == lprc->top)
return 1;
return (yLine - lprc->top);
}
int DrawTextWrap(HDC hdc, LPCWSTR lpchText, int cchText, LPRECT lprc, UINT format)
{
DRAWTEXTPARAMS DTparams;
LPDRAWTEXTPARAMS lpDTparams = NULL;
if (cchText < -1)
return(0);
if (format & DT_TABSTOP)
{
DTparams.cbSize = sizeof(DRAWTEXTPARAMS);
DTparams.iLeftMargin = DTparams.iRightMargin = 0;
DTparams.iTabLength = (format & 0xff00) >> 8;
lpDTparams = &DTparams;
format &= 0xffff00ff;
}
return DrawTextExPrivWrap(hdc, (LPWSTR)lpchText, cchText, lprc, format, lpDTparams);
}
#endif // FONT_LINK