//+--------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation, 1994-1995 // // File: fontlink.cpp // // Contents: Wrappers for font link functions. // //---------------------------------------------------------------------------- #include "ctlspriv.h" #ifdef FONT_LINK #include #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 _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