/* * @doc INTERNAL * * @module FONT.CPP -- font cache | * * Includes font cache, char width cache; * create logical font if not in cache, look up * character widths on an as needed basis (this * has been abstracted away into a separate class * so that different char width caching algos can * be tried.) * * Owner: * RichEdit 1.0 code: David R. Fulmer * Christian Fortini (initial conversion to C++) * Jon Matousek * * History: * 7/26/95 jonmat cleanup and reorganization, factored out * char width caching code into a separate class. * 7/1/99 KeithCu Removed multiple levels in CWidthCache, cached * 30K FE characters in 2 bytes, sped up cache by * lowering acceptable collision rate, halved memory * usage by storing widths in 2 bytes instead of 4 * Shrunk much out of CCcs (i.e. LOGFONT) * * Copyright (c) 1995-2000 Microsoft Corporation. All rights reserved. */ #include "_common.h" #include "_font.h" #include "_rtfconv.h" // Needed for GetCodePage #include "_uspi.h" #define CLIP_DFA_OVERRIDE 0x40 // Used to disable Korea & Taiwan font association #define FF_BIDI 7 extern ICustomTextOut *g_pcto; ASSERTDATA // Corresponds to yHeightCharPtsMost in richedit.h #define yHeightCharMost 32760 // NOTE: this is global across all instances in the same process. static CFontCache *g_fc; static FONTINFO *g_pFontInfo = NULL; static LONG g_cFontInfo = 0; static LONG g_cFontInfoMax = 0; //Fonts automatically added to our font table const WCHAR *szArial = L"Arial"; // IFONT_ARIAL const WCHAR *szTimesNewRoman = L"Times New Roman"; // IFONT_TIMESNEWROMAN const WCHAR *szSymbol = L"Symbol"; // IFONT_SYMBOL const WCHAR *szSystem = L"System"; // IFONT_SYSTEM const int cfontsDflt = 4; //Other fonts that we do use, but aren't automatically added to our font table const WCHAR *szMicrosSansSerif = L"Microsoft Sans Serif"; const WCHAR *szMSSansSerif = L"MS Sans Serif"; const WCHAR *szMangal = L"Mangal"; const WCHAR *szLatha = L"Latha"; const WCHAR *szRaavi = L"Raavi"; const WCHAR *szShruti = L"Shruti"; const WCHAR *szTunga = L"Tunga"; const WCHAR *szGautami = L"Gautami"; const WCHAR *szCordiaNew = L"Cordia New"; const WCHAR *szTahoma = L"Tahoma"; const WCHAR *szArialUnicode = L"Arial Unicode MS"; const WCHAR *szWingdings = L"Wingdings"; const WCHAR *szSylfaen = L"Sylfaen"; const WCHAR *szSyriac = L"Estrangelo Edessa"; const WCHAR *szThaana = L"MV Boli"; #define szFontOfChoice szArial /* * GetFontNameIndex(pFontName) * * @func * return index into global pszFontName table for fontname pFontName. * If fontname isn't in table, add it and return index. * * @rdesc * fontname index corresponding to pFontName * * @devnote * This uses a linear search, so the most common font names should be * up front. Internally, we use the fontname indices, so the search * isn't done that often. Note also that the fontname table only grows, * but this is probably OK for most clients. Else we need ref counting... */ SHORT GetFontNameIndex( const WCHAR *pFontName) { CLock Lock; // Wonder how much this slows things down... for(LONG i = 0; i < g_cFontInfo; i++) { // A hash could speed this up if perf turns out poor if(!wcscmp(pFontName, g_pFontInfo[i].szFontName)) return i; } if(g_cFontInfo + 1 >= g_cFontInfoMax) { // Note that PvReAlloc() reverts to PvAlloc() if g_pFontInfo is NULL FONTINFO *pFI = (FONTINFO *)PvReAlloc((LPVOID)g_pFontInfo, sizeof(FONTINFO) * (8 + g_cFontInfo)); if(!pFI) return IFONT_ARIAL; // Out of memory... // Initialize the structure ZeroMemory (&pFI[g_cFontInfo], 8 * sizeof(FONTINFO)); // attempts to fill them in if(!g_cFontInfoMax) // First allocation { Assert(IFONT_ARIAL == 0 && IFONT_TMSNEWRMN == 1 && IFONT_SYMBOL == 2 && IFONT_SYSTEM == 3); pFI[IFONT_ARIAL].szFontName = szArial; pFI[IFONT_TMSNEWRMN].szFontName = szTimesNewRoman; pFI[IFONT_SYMBOL].szFontName = szSymbol; pFI[IFONT_SYSTEM].szFontName = szSystem; g_cFontInfo = cfontsDflt; } g_pFontInfo = pFI; g_cFontInfoMax += 8; } LONG cb = (wcslen(pFontName) + 1)*sizeof(WCHAR); WCHAR * pch = (WCHAR *)PvAlloc(cb, GMEM_MOVEABLE); if(!pch) return IFONT_ARIAL; // Out of memory... g_pFontInfo[g_cFontInfo].szFontName = pch; CopyMemory((void *)pch, pFontName, cb); return g_cFontInfo++; } /* * GetFontName(iFont) * * @func * return fontname given by g_pFontInfo[iFont].szFontName. * * @rdesc * fontname corresponding to fontname index iFont */ const WCHAR *GetFontName( LONG iFont) { return (iFont >= 0 && iFont < g_cFontInfo) ? g_pFontInfo[iFont].szFontName : NULL; } void SetFontSignature( LONG iFont, QWORD qwFontSig) { if(iFont >= 0 && iFont < g_cFontInfo) g_pFontInfo[iFont].qwFontSig |= qwFontSig; } /* * SetFontLegitimateSize(iFont, fUIFont, bSize, fFEcpg) * * @func * Set the legitimate size (readable smallest size to use) of a given font * * @rdesc * TRUE if successful */ BOOL SetFontLegitimateSize( LONG iFont, BOOL fUIFont, BYTE bSize, BOOL fFEcpg) { if (iFont < g_cFontInfo) { // East Asia wanted to do it per codepage. // // FUTURE: Bear in mind that this approach is bug-prone. Once there's // any new FE font created with different metric from the existing one. // Font scaling will not perform well or even broken for such font [wchao]. g_pFontInfo[iFont].ff.fScaleByCpg = fFEcpg; if (fUIFont) { if (!g_pFontInfo[iFont].bSizeUI) g_pFontInfo[iFont].bSizeUI = bSize; else // more than one legit size were updated per font, // We fallback to the codepage-driven approach. g_pFontInfo[iFont].ff.fScaleByCpg = g_pFontInfo[iFont].bSizeUI != bSize; } else { if (!g_pFontInfo[iFont].bSizeNonUI) g_pFontInfo[iFont].bSizeNonUI = bSize; else g_pFontInfo[iFont].ff.fScaleByCpg = g_pFontInfo[iFont].bSizeNonUI != bSize; } return TRUE; } return FALSE; } /* * GetFontLegitimateSize(iFont, fUIFont, iCharRep) * * @func * Get the legitimate size (readable smallest size to use) of a given font * * @rdesc * Legitimate size of font */ BYTE GetFontLegitimateSize( LONG iFont, //@parm Font to get size for BOOL fUIFont, //@parm TRUE if for UI font int iCharRep) //@parm Char repertoire to use { BYTE bDefPaf; SHORT iDefFont; BYTE yHeight = 0; if (iFont < g_cFontInfo && !g_pFontInfo[iFont].ff.fScaleByCpg) yHeight = fUIFont ? g_pFontInfo[iFont].bSizeUI : g_pFontInfo[iFont].bSizeNonUI; if (!yHeight && fc().GetInfoFlags(iFont).fNonBiDiAscii) { // Non-BiDi ASCII font uses table font (of the same charset) legitimate height QWORD qwFontSig = GetFontSignatureFromFace(iFont) & ~(FASCII | FFE); LONG iCharRepT = GetFirstAvailCharRep(qwFontSig); if(W32->GetPreferredFontInfo(iCharRepT, fUIFont ? true : false, iDefFont, yHeight, bDefPaf)) { SetFontLegitimateSize(iFont, fUIFont, yHeight ? yHeight : fUIFont ? 8 : 10, IsFECharRep(iCharRepT)); } } if (!yHeight) { if (fc().GetInfoFlags(iFont).fThaiDTP) { iCharRep = THAI_INDEX; fUIFont = FALSE; } W32->GetPreferredFontInfo(iCharRep, fUIFont ? true : false, iDefFont, yHeight, bDefPaf); } return yHeight ? yHeight : fUIFont ? 8 : 10; } /* * GetTextCharsetInfoPri(hdc, pFontSig, dwFlags) * * @func * Wrapper to GDI's GetTextCharsetInfo. This to handle BiDi old-style fonts * * @rdesc * CharSet for info */ UINT GetTextCharsetInfoPri( HDC hdc, FONTSIGNATURE* pFontSig, DWORD dwFlags) { #ifndef NOCOMPLEXSCRIPTS OUTLINETEXTMETRICA otm; INT uCharSet = -1; if (pFontSig && GetOutlineTextMetricsA(hdc, sizeof(OUTLINETEXTMETRICA), &otm)) { ZeroMemory (pFontSig, sizeof(FONTSIGNATURE)); switch (otm.otmfsSelection & 0xFF00) { case 0xB200: // Arabic Simplified case 0xB300: // Arabic Traditional case 0xB400: // Arabic Old UDF uCharSet = ARABIC_CHARSET; break; case 0xB100: // Hebrew Old style uCharSet = HEBREW_CHARSET; } } if (uCharSet == -1) uCharSet = W32->GetTextCharsetInfo(hdc, pFontSig, dwFlags); if (uCharSet == DEFAULT_CHARSET) uCharSet = ANSI_CHARSET; // never return ambiguous return (UINT)uCharSet; #else return DEFAULT_CHARSET; #endif } /* * GetFontSignatureFromDC(hdc, &fNonBiDiAscii) * * @func * Compute RichEdit font signature for font selected into hdc. Uses * info from OS font signature * * @rdesc * RichEdit font signature for font selected into hdc */ QWORD GetFontSignatureFromDC( HDC hdc, BOOL & fNonBiDiAscii) { union { // Endian-dependent way of QWORD qwFontSig; // avoiding 64-bit shifts DWORD dwFontSig[2]; }; #ifndef NOCOMPLEXSCRIPTS // Try to get FONTSIGNATURE data CHARSETINFO csi; UINT uCharSet = GetTextCharsetInfoPri(hdc, &(csi.fs), 0); DWORD dwUsb0 = 0; DWORD dwUsb2 = 0; if(!W32->OnWin9x()) { dwUsb0 = csi.fs.fsUsb[0]; dwUsb2 = csi.fs.fsUsb[2]; } if ((csi.fs.fsCsb[0] | dwUsb0 | dwUsb2) || TranslateCharsetInfo((DWORD *)(DWORD_PTR)uCharSet, &csi, TCI_SRCCHARSET)) { DWORD fsCsb0 = csi.fs.fsCsb[0]; CUniscribe * pusp; SCRIPT_CACHE sc = NULL; WORD wGlyph; qwFontSig = ((fsCsb0 & 0x1FF) << 8) // Shift left since we use | ((fsCsb0 & 0x1F0000) << 3); // low byte for fBiDi, etc. // Also look at Unicode subrange if available // FUTURE: we may want to drive Unicode ranges with a // table approach, i.e., use for loop shifting dwUsb0 right // to convert each bit into an index into a table of BYTEs // that return the appropriate script index for rgCpgCharSet: // // for(LONG i = 0; dwUsb0; dwUsb0 >>= 1, i++) // { // static const BYTE rgiCharRep[32] = {...}; // if(dwUsb0 & 1) // dwFontSig |= FontSigFromCharRep(rgiCharRep[i]); // } if(dwUsb0) { if (dwUsb0 & 0x00000400) qwFontSig |= FARMENIAN; Assert(FDEVANAGARI == 0x0000000800000000); dwFontSig[1] |= (dwUsb0 & 0x00FF8000) >> 12; // 9 Indic scripts if (dwUsb0 & 0x02000000) qwFontSig |= FLAO; if (dwUsb0 & 0x04000000) qwFontSig |= FGEORGIAN; if (dwUsb0 & 0x10000000) qwFontSig |= FJAMO; } // The new Unicode 3.0 scripts are defined by dwUsb2 as follows // (see \\sparrow\sysnls\nlsapi\font-sig.txt): // 128 32 Script //---------------------- // 70 6 Tibetan // 71 7 Syriac // 72 8 Thaana // 73 9 Sinhala // 74 10 Myanmar // 75 11 Ethiopic // 76 12 Cherokee // 77 13 Canadian Aboriginal Syllabics // 78 14 Ogham // 79 15 Runic // 80 16 Khmer // 81 17 Mongolian // 82 18 Braille // 83 19 Yi if(dwUsb2 & 0xFFFC0) // Bits 6 - 19 { if(dwUsb2 & 0x40) // Bit 6 of dwUsb[2] dwFontSig[1] |= FTIBETAN > 32; // is Tibetan dwFontSig[1] |= (dwUsb2 & 0x180) >> 6; // Syriac (7), Thaana (8) if(dwUsb2 & 0x200) // Bit 9 of dwUsb[2] dwFontSig[1] |= FSINHALA > 32; // is Sinhala if(dwUsb2 & 0x400) // Bit 10 of dwUsb[2] dwFontSig[1] |= FMYANMAR > 32; // is Myanmar dwFontSig[1] |= (dwUsb2 & 0xFF800) << 6;// Bits 11-19 of dwUsb[2] } if((qwFontSig & FCOMPLEX_SCRIPT) && !(qwFontSig & FHILATIN1) && (pusp = GetUniscribe())) { // Signature says no Latin-1 support // Search for the 'a' and '0' glyph in the font to // determine if the font supports ASCII or European // Digit. This is necessary to overcome the font having // an incomplete font signature. if(ScriptGetCMap(hdc, &sc, L"a", 1, 0, &wGlyph) == S_OK) qwFontSig |= FASCIIUPR; if(ScriptGetCMap(hdc, &sc, L"0", 1, 0, &wGlyph) == S_OK) qwFontSig |= FBELOWX40; if(!IsBiDiCharSet(uCharSet) && (qwFontSig & FASCII) == FASCII) fNonBiDiAscii = TRUE; // Non-BiDi ASCII font ScriptFreeCache(&sc); } if (qwFontSig & FHILATIN1) qwFontSig |= FASCII; // FLATIN1 has 3 bits // HACK for symbol font. We assign FSYMBOL for Symbol font signature. // REVIEW: should we just use csi.fs.fsCsb[0] bit 31 for symbol bit? if (uCharSet == SYMBOL_CHARSET && !qwFontSig || fsCsb0 & 0x80000000) qwFontSig = FSYMBOL; } else // No font signature info qwFontSig = FontSigFromCharRep(CharRepFromCharSet(uCharSet)); #else qwFontSig = FLATIN1; // Default Latin1 #endif // NOCOMPLEXSCRIPTS return qwFontSig; } /* * GetFontSignatureFromFace(iFont, pqwFontSig) * * @func * Giving font signature matching the index of given facename. * This signature may not match the one in Cccs since this is the * signature of the font of given facename. The Cccs one is * per GDI realization. * * @rdesc * - font signature if pqwFontSig is NULL. * - If pqwFontSig != NULL. It's a boolean. * ZERO means returned signature is not sensible by following reasons * 1. Bad facename (junk like "!@#$" or name that doesnt exist in the system) * 2. Given face doesnt support even one valid ANSI codepage (symbol fonts, * e.g., Marlett) */ QWORD GetFontSignatureFromFace( int iFont, QWORD * pqwFontSig) { Assert((unsigned)iFont < (unsigned)g_cFontInfo); FONTINFO_FLAGS ff; QWORD qwFontSig = g_pFontInfo[iFont].qwFontSig; ff.wFlags = g_pFontInfo[iFont].ff.wFlags; if(!ff.fCached) { int i = 0; HDC hdc = GetDC(NULL); LOGFONT lf; WCHAR* pwchTag = lf.lfFaceName; ZeroMemory(&lf, sizeof(LOGFONT)); wcscpy(lf.lfFaceName, GetFontName(iFont)); // Exclude Win95's tag name e.g. "Arial(Greek)" while (pwchTag[i] && pwchTag[i] != '(') i++; if(pwchTag[i] && i > 0) { while (i > 0 && pwchTag[i-1] == 0x20) i--; pwchTag[i] = 0; } lf.lfCharSet = DEFAULT_CHARSET; // Obtain a charset supported by given facename // to force GDI gives facename priority over charset. W32->GetFacePriCharSet(hdc, &lf); HFONT hfont = CreateFontIndirect(&lf); if(hfont) { HFONT hfontOld = SelectFont(hdc, hfont); WCHAR szNewFaceName[LF_FACESIZE]; GetTextFace(hdc, LF_FACESIZE, szNewFaceName); if(!wcsicmp(szNewFaceName, lf.lfFaceName) || // Got it ((GetCharFlags(szNewFaceName, 2) & FFE) && // or Get back FE font name for English name (GetCharFlags(lf.lfFaceName, 2) & FASCII)))// because NT5 supports dual font names. { BOOL fNonBiDiAscii = FALSE; qwFontSig = GetFontSignatureFromDC(hdc, fNonBiDiAscii); if(fNonBiDiAscii) ff.fNonBiDiAscii = TRUE; } else ff.fBadFaceName = TRUE; TEXTMETRIC tm; GetTextMetrics(hdc, &tm); ff.fTrueType = tm.tmPitchAndFamily & TMPF_TRUETYPE ? 1 : 0; ff.fBitmap = tm.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_VECTOR | TMPF_DEVICE) ? 0 : 1; if(!ff.fBadFaceName && qwFontSig & FTHAI) { // Some heuristic test on Thai fonts. // Most Thai fonts will fall to this category currently except for // Tahoma and Microsoft Sans Serif. ff.fThaiDTP = tm.tmDescent && tm.tmAscent/tm.tmDescent < 3; } SelectObject(hdc, hfontOld); SideAssert(DeleteObject(hfont)); } ReleaseDC(NULL, hdc); // Cache code pages supported by this font ff.fCached = TRUE; g_pFontInfo[iFont].qwFontSig |= qwFontSig; g_pFontInfo[iFont].ff.wFlags = ff.wFlags; } if (!pqwFontSig) return qwFontSig; *pqwFontSig = qwFontSig; // 22-29 are reserved for alternate ANSI/OEM, as of now we use 21, 22 for Devanagari and Tamil return qwFontSig && !ff.fBadFaceName; } /* * FreeFontNames() * * @func * Free fontnames given by g_pFontInfo[i].szFontName allocated by * GetFontNameIndex() as well as g_pFontInfo itself. */ void FreeFontNames() { for(LONG i = cfontsDflt; i < g_cFontInfo; i++) FreePv((LPVOID)g_pFontInfo[i].szFontName); FreePv(g_pFontInfo); g_pFontInfo = NULL; } SHORT g_iFontJapanese; SHORT g_iFontHangul; SHORT g_iFontBig5; SHORT g_iFontGB2312; /* * InitFontCache() * * @func * Initializes font cache. * * @devnote * This is exists so reinit.cpp doesn't have to know all about the * font cache. */ void InitFontCache() { g_fc = new CFontCache; g_fc->Init(); } /* * FreeFontCache() * * @mfunc * Frees font cache. * * @devnote * This is exists so reinit.cpp doesn't have to know all about the * font cache. */ void FreeFontCache() { for (int i = 0; i < g_cFontInfo; i++) delete g_pFontInfo[i]._pffm; delete g_fc; g_fc = NULL; FreeFontNames(); } /* * CFontCache & fc() * * @func * initialize the global g_fc. * @comm * current #defined to store 16 logical fonts and * respective character widths. */ CFontCache & fc() { TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "fc"); return *g_fc; } FONTINFO_FLAGS CFontCache::GetInfoFlags(int ifont) { if (!g_pFontInfo[ifont].ff.fCached) GetFontSignatureFromFace(ifont); return g_pFontInfo[ifont].ff; } CFontFamilyMgr::~CFontFamilyMgr() { for (int i = 0; i < _rgf.Count(); i++) { CFontFamilyMember *pf = _rgf.Elem(i); pf->Free(); } } CFontFamilyMember* CFontFamilyMgr::GetFontFamilyMember(LONG weight, BOOL fItalic) { for (int i = 0; i < _rgf.Count(); i++) { CFontFamilyMember *pf = _rgf.Elem(i); if (pf->_weight == weight && pf->_fItalic == fItalic) return pf; } CFontFamilyMember f(weight, fItalic); CFontFamilyMember *pf = _rgf.Add(1, 0); *pf = f; return pf; } CKernCache * CFontCache::GetKernCache(LONG iFont, LONG weight, BOOL fItalic) { if (!g_fc->GetInfoFlags(iFont).fTrueType) return 0; CFontFamilyMgr *pffm = GetFontFamilyMgr(iFont); CFontFamilyMember *pf = pffm->GetFontFamilyMember(weight, fItalic); return pf->GetKernCache(); } CFontFamilyMgr* CFontCache::GetFontFamilyMgr(LONG iFont) { if (!g_pFontInfo[iFont]._pffm) g_pFontInfo[iFont]._pffm = new CFontFamilyMgr(); return g_pFontInfo[iFont]._pffm; } // =================================== CFontCache ==================================== /* * CFontCache::Init() * * @mfunc * Initializes font cache. * * @devnote * This is not a constructor because something bad seems to happen * if we try to construct a global object. */ void CFontCache::Init() { TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CFontCache::CFontCache"); _dwAgeNext = 0; } /* * CFontCache::MakeHashKey(pCF) * * @mfunc * Build a hash key for quick searches for a CCcs matching * the pCF. * Format: * iFont : 14 * Bold/Italic : 2 * Height : 16 * */ CCSHASHKEY CFontCache::MakeHashKey( const CCharFormat *pCF) { CCSHASHKEY ccshashkey; ccshashkey = pCF->_iFont | ((pCF->_dwEffects & 3) << 14); ccshashkey |= pCF->_yHeight << 16; return ccshashkey; } /* * CFontCache::GetCcs(pCF, dvpInch, dwFlags, hdc) * * @mfunc * Search the font cache for a matching logical font and return it. * If a match is not found in the cache, create one. * * @rdesc * A logical font matching the given CHARFORMAT info. * * @devnote * The calling chain must be protected by a CLock, since this present * routine access the global (shared) FontCache facility. */ CCcs* CFontCache::GetCcs( CCharFormat *pCF, //@parm Logical font (routine is allowed to change it) const LONG dvpInch, //@parm Y pixels per inch DWORD dwFlags, //@parm flags HDC hdc) //@parm HDC font is to be created for { TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CFontCache::GetCcs"); // display font const CCcs * const pccsMost = &_rgccs[FONTCACHESIZE - 1]; CCcs * pccs; CCSHASHKEY ccshashkey; int iccsHash; if (dwFlags & FGCCSUSETRUETYPE) { //On Win '9x Thai/Vietnamese, you cannot force truetype fonts! Therefore, //we will force Tahoma if the font doesn't support the right charset. if (W32->OnWin9x()) { UINT acp = GetACP(); if (acp == 1258 || acp == 874) { QWORD qwFontSig = GetFontSignatureFromFace(pCF->_iFont); if (pCF->_iCharRep == THAI_INDEX && (qwFontSig & FTHAI) == 0 || pCF->_iCharRep == VIET_INDEX && (qwFontSig & FVIETNAMESE) == 0 || !g_fc->GetInfoFlags(pCF->_iFont).fTrueType) { pCF->_iFont = GetFontNameIndex(szTahoma); } } } else if (!g_fc->GetInfoFlags(pCF->_iFont).fTrueType) dwFlags |= FGCCSUSETRUETYPE; } if (hdc == NULL) hdc = W32->GetScreenDC(); // Change _yHeight in the case of sub/superscript if(pCF->_dwEffects & (CFE_SUPERSCRIPT | CFE_SUBSCRIPT)) pCF->_yHeight = 2 * pCF->_yHeight / 3; //Convert CCharFormat into logical units (round) pCF->_yHeight = (pCF->_yHeight * dvpInch + LY_PER_INCH / 2) / LY_PER_INCH; if (pCF->_yHeight == 0) pCF->_yHeight = 1; if ((dwFlags & FGCCSUSEATFONT) && !IsFECharRep(pCF->_iCharRep)) { QWORD qwFontSig = GetFontSignatureFromFace(pCF->_iFont); if (!(qwFontSig & FFE)) // No At font for non-FE charset and dwFlags &= ~FGCCSUSEATFONT; // font signature doesen't support FE } ccshashkey = MakeHashKey(pCF); // Check our hash before going sequential. iccsHash = ccshashkey % CCSHASHSEARCHSIZE; if(ccshashkey == quickHashSearch[iccsHash].ccshashkey) { pccs = quickHashSearch[iccsHash].pccs; if(pccs && pccs->_fValid) { if(pccs->Compare(pCF, hdc, dwFlags)) goto matched; } } else //Setup this hash hint for next time quickHashSearch[iccsHash].ccshashkey = ccshashkey; // Sequentially search ccs for same character format for(pccs = &_rgccs[0]; pccs <= pccsMost; pccs++) { if(pccs->_ccshashkey == ccshashkey && pccs->_fValid) { if(!pccs->Compare(pCF, hdc, dwFlags)) continue; quickHashSearch[iccsHash].pccs = pccs; matched: //$ FUTURE: make this work even with wrap around of dwAgeNext // Mark as most recently used if it isn't already in use. if(pccs->_dwAge != _dwAgeNext - 1) pccs->_dwAge = _dwAgeNext++; pccs->_cRefs++; // bump up ref. count return pccs; } } // We did not find a match: init a new font cache. pccs = GrabInitNewCcs(pCF, hdc, dwFlags); quickHashSearch[iccsHash].pccs = pccs; pccs->_ccshashkey = ccshashkey; return pccs; } /* * CFontCache::GrabInitNewCcs(pCF, hdc, dwFlags) * * @mfunc * Create a logical font and store it in our cache. * * @rdesc * New CCcs created */ CCcs* CFontCache::GrabInitNewCcs( const CCharFormat * const pCF, //@parm Description of desired logical font HDC hdc, DWORD dwFlags) { TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CFontCache::GrabInitNewCcs"); DWORD dwAgeOldest = 0xffffffff; CCcs * pccs; const CCcs * const pccsMost = &_rgccs[FONTCACHESIZE - 1]; CCcs * pccsOldest = NULL; // Look for unused entry and oldest in use entry for(pccs = &_rgccs[0]; pccs <= pccsMost && pccs->_fValid; pccs++) if(pccs->_cRefs == 0 && pccs->_dwAge < dwAgeOldest) { dwAgeOldest = pccs->_dwAge; pccsOldest = pccs; } if(pccs > pccsMost) // Didn't find an unused entry, use oldest entry { pccs = pccsOldest; if(!pccs) { AssertSz(FALSE, "CFontCache::GrabInitNewCcs oldest entry is NULL"); return NULL; } } // Initialize new CCcs pccs->_hdc = hdc; pccs->_fFECharSet = IsFECharRep(pCF->_iCharRep); pccs->_fUseAtFont = (dwFlags & FGCCSUSEATFONT) != 0; pccs->_tflow = dwFlags & 0x3; if(!pccs->Init(pCF)) return NULL; pccs->_cRefs++; return pccs; } // ============================= CCcs class =================================================== /* * BOOL CCcs::Init(pCF) * * @mfunc * Init one font cache object. The global font cache stores * individual CCcs objects. */ BOOL CCcs::Init ( const CCharFormat * const pCF) //@parm description of desired logical font { TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CCcs::Init"); if(_fValid) Free(); // recycle already in-use fonts. if(MakeFont(pCF)) { _iFont = pCF->_iFont; _dwAge = g_fc->_dwAgeNext++; _fValid = TRUE; // successfully created a new font cache. } return _fValid; } /* * void CCcs::Free() * * @mfunc * Free any dynamic memory allocated by an individual font's cache. */ void CCcs::Free() { TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CCcs::Free"); Assert(_fValid); _widths.Free(); if(_hfont) { DestroyFont(); if (_fCustomTextOut) g_pcto->NotifyDestroyFont(_hfont); } #ifndef NOCOMPLEXSCRIPTS if (_sc && g_pusp) ScriptFreeCache(&_sc); #endif _fValid = FALSE; _cRefs = 0; } /* * CCcs::BestCharRep(iCharRep, iCharRepDefault, fFontMatching) * * @mfunc * This function returns the best charset that the currently selected font * is capable of rendering. If the currently selected font cannot support * the requested charset, then the function returns bCharSetDefault, which * is generally taken from the charformat. * * @rdesc * The closest charset to bCharSet that can be rendered by the current * font. * * @devnote * Currently this function is only used with plain text, however I don't * believe there is any special reason it couldn't be used to improve * rendering of rich text as well. */ BYTE CCcs::BestCharRep( BYTE iCharRep, BYTE iCharRepDefault, int fFontMatching) { TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CCcs::BestCharSet"); // Does desired charset match currently selected charset or is it // supported by the currently selected font? if((iCharRep != CharRepFromCharSet(_bCharSet) || !iCharRep) && (fFontMatching == MATCH_CURRENT_CHARSET || !(_qwFontSig & FontSigFromCharRep(iCharRep)))) { // If desired charset is not selected and we can't switch to it, // switch to fallback charset (probably from backing store). return iCharRepDefault; } // We already match desired charset, or it is supported by the font. // Either way, we can just return the requested charset. return iCharRep; } /* * CCcs::FillWidth (ch, &dup) * * @mfunc * Fill in width for given character. Sometimes we don't * call the OS for the certain characters because fonts have bugs. * * @rdesc * TRUE if OK, FALSE if failed */ BOOL CCcs::FillWidth( WCHAR ch, //@parm WCHAR character we need a width for. LONG &dup) //@parm the width of the character { TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CCcs::FillWidth"); AssertSz(_hfont, "CCcs::Fill - CCcs has no font"); dup = 0; WCHAR chWidth = ch; HFONT hfontOld = SelectFont(_hdc, _hfont); BOOL fLookaside = _widths.FLookasideCharacter(ch); if (fLookaside) chWidth = 0x4E00; else switch(ch) { case NBHYPHEN: case SOFTHYPHEN: chWidth = '-'; break; case NBSPACE: chWidth = ' '; break; case EMSPACE: chWidth = EMDASH; break; case ENSPACE: chWidth = ENDASH; break; } W32->REGetCharWidth(_hdc, chWidth, (INT*) &dup, _wCodePage, _fCustomTextOut); dup -= _xOverhangAdjust; if (dup <= 0) dup = max(_xAveCharWidth, 1); if (fLookaside) _widths._dupCJK = dup; else { CacheEntry *pWidthData = _widths.GetEntry(ch); pWidthData->ch = ch; pWidthData->width = dup; } SelectFont(_hdc, hfontOld); return TRUE; } /* * BOOL CCcs::MakeFont(pCF) * * @mfunc * Wrapper, setup for CreateFontIndirect() to create the font to be * selected into the HDC. * * @devnote The pCF here is in logical units * * @rdesc * TRUE if OK, FALSE if allocation failure */ BOOL CCcs::MakeFont( const CCharFormat * const pCF) //@parm description of desired logical font { TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CCcs::MakeFont"); LONG iFont = pCF->_iFont; LOGFONT lf; ZeroMemory(&lf, sizeof(lf)); _bCMDefault = pCF->_dwEffects & CFE_RUNISDBCS ? CVT_LOWBYTE : CVT_NONE; _yHeightRequest = pCF->_yHeight; _bCharSetRequest = CharSetFromCharRep(pCF->_iCharRep); _fCustomTextOut = (pCF->_dwEffects & CFE_CUSTOMTEXTOUT) ? TRUE : FALSE; lf.lfHeight = -_yHeightRequest; if(pCF->_wWeight) _weight = pCF->_wWeight; else _weight = (pCF->_dwEffects & CFE_BOLD) ? FW_BOLD : FW_NORMAL; lf.lfWeight = _weight; lf.lfItalic = _fItalic = (pCF->_dwEffects & CFE_ITALIC) != 0; lf.lfCharSet = _bCMDefault == CVT_LOWBYTE ? ANSI_CHARSET : CharSetFromCharRep(pCF->_iCharRep); if (lf.lfCharSet == PC437_CHARSET) lf.lfCharSet = DEFAULT_CHARSET; lf.lfOutPrecision = OUT_DEFAULT_PRECIS; if (_tflow) lf.lfOrientation = lf.lfEscapement = (4 - _tflow) * 900; #ifndef UNDER_CE if (_fForceTrueType || _tflow && g_fc->GetInfoFlags(GetFontNameIndex(lf.lfFaceName)).fBitmap) { lf.lfOutPrecision = OUT_TT_ONLY_PRECIS; if (!W32->OnWin9x() && g_fc->GetInfoFlags(iFont).fTrueType) lf.lfOutPrecision = OUT_SCREEN_OUTLINE_PRECIS; } #endif lf.lfClipPrecision = CLIP_DFA_OVERRIDE; lf.lfPitchAndFamily = _bPitchAndFamily = pCF->_bPitchAndFamily; lf.lfQuality = _bQuality = pCF->_bQuality; #ifdef UNDER_CE // DEBUGGGGGG for EBOOK!! Presumably this should be a registry setting // that overrules DEFAULT_QUALITY (0) the way ANTIALIASED_QUALITY, etc., do #ifndef CLEARTYPE_QUALITY #define CLEARTYPE_QUALITY 5 #endif lf.lfQuality = CLEARTYPE_QUALITY; #endif // If family is virtual BiDi family (FF_BIDI), replace by FF_ROMAN if((lf.lfPitchAndFamily & 0xF0) == (FF_BIDI << 4)) lf.lfPitchAndFamily = (FF_ROMAN << 4) | (lf.lfPitchAndFamily & 0xF); // If the run is DBCS, that means the font's codepage is not available in // this system. Use the English ANSI codepage instead so we will display // ANSI characters correctly. NOTE: _wCodePage is only used for Win95. _wCodePage = CodePageFromCharRep(CharRepFromCharSet(lf.lfCharSet)); wcscpy(lf.lfFaceName, GetFontName(iFont)); if (_fUseAtFont && lf.lfFaceName[0] != L'@') { wcscpy(&(lf.lfFaceName[1]), GetFontName(iFont)); lf.lfFaceName[0] = L'@'; } // In BiDi system, always create ANSI bitmap font with system charset BYTE bCharSetSys = W32->GetSysCharSet(); if (IsBiDiCharSet(bCharSetSys) && lf.lfCharSet == ANSI_CHARSET && fc().GetInfoFlags(iFont).fBitmap && !fc().GetInfoFlags(iFont).fBadFaceName) lf.lfCharSet = bCharSetSys; // Reader! A bundle of spagghetti code lies ahead of you! // But go on boldly, for these spagghetti are seasoned with // lots of comments, and ... good luck to you... HFONT hfontOriginalCharset = NULL; BYTE bCharSetOriginal = lf.lfCharSet; WCHAR szNewFaceName[LF_FACESIZE]; if(pCF->_dwEffects & (CFE_BOLD | CFE_ITALIC)) iFont = -1; // Don't use cached font info unless // normal font GetFontWithMetrics(&lf, szNewFaceName); if(0 != wcsicmp(szNewFaceName, lf.lfFaceName)) { BOOL fCorrectFont = FALSE; iFont = -1; // pCF->_iFont wasn't used if(lf.lfCharSet == SYMBOL_CHARSET) { // #1. if the face changed, and the specified charset was SYMBOL, // but the face name exists and suports ANSI, we give preference // to the face name lf.lfCharSet = ANSI_CHARSET; hfontOriginalCharset = _hfont; GetFontWithMetrics(&lf, szNewFaceName); if(0 == wcsicmp(szNewFaceName, lf.lfFaceName)) // That's right, ANSI is the asnwer fCorrectFont = TRUE; else // No, fall back by default; the charset we got was right lf.lfCharSet = bCharSetOriginal; } else if(lf.lfCharSet == DEFAULT_CHARSET && _bCharSet == DEFAULT_CHARSET) { // #2. If we got the "default" font back, we don't know what it means // (could be anything) so we veryfy that this guy's not SYMBOL // (symbol is never default, but the OS could be lying to us!!!) // we would like to veryfy more like whether it actually gave us // Japanese instead of ANSI and labeled it "default"... // but SYMBOL is the least we can do lf.lfCharSet = SYMBOL_CHARSET; wcscpy(lf.lfFaceName, szNewFaceName); hfontOriginalCharset = _hfont; GetFontWithMetrics(&lf, szNewFaceName); if(0 == wcsicmp(szNewFaceName, lf.lfFaceName)) // That's right, it IS symbol! // 'correct' the font to the 'true' one, // and we'll get fMappedToSymbol fCorrectFont = TRUE; // Always restore the charset name, we didn't want to // question the original choice of charset here lf.lfCharSet = bCharSetOriginal; } else if(lf.lfCharSet == ARABIC_CHARSET || lf.lfCharSet == HEBREW_CHARSET) { DestroyFont(); wcscpy(lf.lfFaceName, szNewFaceName); GetFontWithMetrics(&lf, szNewFaceName); fCorrectFont = TRUE; } else if(_bConvertMode != CVT_LOWBYTE && IsFECharSet(lf.lfCharSet) && !OnWinNTFE() && !W32->OnWin9xFE()) { const WCHAR *pch = NULL; if(_bCharSet != lf.lfCharSet && W32->OnWin9x()) { // On Win95 when rendering to PS driver, we'll get something // other than what we asked. So try a known font we got from GDI switch (lf.lfCharSet) { case CHINESEBIG5_CHARSET: pch = GetFontName(g_iFontBig5); break; case SHIFTJIS_CHARSET: pch = GetFontName(g_iFontJapanese); break; case HANGEUL_CHARSET: pch = GetFontName(g_iFontHangul); break; case GB2312_CHARSET: pch = GetFontName(g_iFontGB2312); break; } } else // FE Font (from Lang pack) pch = szNewFaceName; // on a nonFEsystem if(pch) wcscpy(lf.lfFaceName, pch); hfontOriginalCharset = _hfont; GetFontWithMetrics(&lf, szNewFaceName); if(0 == wcsicmp(szNewFaceName, lf.lfFaceName)) { // That's right, it IS the FE font we want! // 'correct' the font to the 'true' one. fCorrectFont = TRUE; if(W32->OnWin9x()) { // Save up the GDI font names for later printing use switch(lf.lfCharSet) { case CHINESEBIG5_CHARSET: g_iFontBig5 = GetFontNameIndex(lf.lfFaceName); break; case SHIFTJIS_CHARSET: g_iFontJapanese = GetFontNameIndex(lf.lfFaceName); break; case HANGEUL_CHARSET: g_iFontHangul = GetFontNameIndex(lf.lfFaceName); break; case GB2312_CHARSET: g_iFontGB2312 = GetFontNameIndex(lf.lfFaceName); break; } } } } if(hfontOriginalCharset) { // Either keep old font or new one if(fCorrectFont) { SideAssert(DeleteObject(hfontOriginalCharset)); } else { // Fall back to original font DestroyFont(); _hfont = hfontOriginalCharset; GetMetrics(); } hfontOriginalCharset = NULL; } } RetryCreateFont: { // Could be that we just plain simply get mapped to symbol. // Avoid it BOOL fMappedToSymbol = (_bCharSet == SYMBOL_CHARSET && lf.lfCharSet != SYMBOL_CHARSET); BOOL fChangedCharset = (_bCharSet != lf.lfCharSet && lf.lfCharSet != DEFAULT_CHARSET); if(fChangedCharset || fMappedToSymbol) { // Here, the system did not preserve the font language or mapped // our non-symbol font onto a symbol font, which will look awful // when displayed. Giving us a symbol font when we asked for a // non-symbol font (default can never be symbol) is very bizarre // and means that either the font name is not known or the system // has gone complete nuts. The charset language takes priority // over the font name. Hence, I would argue that nothing can be // done to save the situation at this point, and we have to // delete the font name and retry. if (fChangedCharset && lf.lfCharSet == THAI_CHARSET && _bCharSet == ANSI_CHARSET) { // We have charset substitution entries in Thai platforms that // will substitute all the core fonts with THAI_CHARSET to // ANSI_CHARSET. This is because we dont have Thai in such fonts. // Here we'll internally substitute the core font to Thai default // font so it matches its underlying THAI_CHARSET request (wchao). SHORT iDefFont; BYTE yDefHeight; BYTE bDefPaf; W32->GetPreferredFontInfo(THAI_INDEX, TRUE, iDefFont, (BYTE&)yDefHeight, bDefPaf); const WCHAR* szThaiDefault = GetFontName(iDefFont); if (szThaiDefault) { DestroyFont(); wcscpy(lf.lfFaceName, szThaiDefault); GetFontWithMetrics(&lf, szNewFaceName); goto GetOutOfHere; } } if(!wcsicmp(lf.lfFaceName, szFontOfChoice)) { // We've been here already; no font with an appropriate // charset is on the system. Try getting the ANSI one for // the original font name. Next time around, we'll null // out the name as well!! if (lf.lfCharSet == ANSI_CHARSET) { TRACEINFOSZ("Asking for ANSI ARIAL and not getting it?!"); // Those Win95 guys have definitely outbugged me goto GetOutOfHere; } DestroyFont(); wcscpy(lf.lfFaceName, GetFontName(pCF->_iFont)); lf.lfCharSet = ANSI_CHARSET; } else { DestroyFont(); wcscpy(lf.lfFaceName, szFontOfChoice); } GetFontWithMetrics(&lf, szNewFaceName); goto RetryCreateFont; } } GetOutOfHere: if (hfontOriginalCharset) SideAssert(DeleteObject(hfontOriginalCharset)); // If we're really really stuck, get system font and hope for the best if(!_hfont) { iFont = IFONT_SYSTEM; _hfont = W32->GetSystemFont(); } // Cache essential FONTSIGNATURE and GetFontLanguageInfo() information Assert(_hfont); if(iFont >= 0) // Use cached value _qwFontSig = GetFontSignatureFromFace(iFont, NULL); if(_hfont && (iFont < 0 || _fCustomTextOut)) { BOOL fNonBiDiAscii; HFONT hfontOld = SelectFont(_hdc, _hfont); if (_fCustomTextOut) g_pcto->NotifyCreateFont(_hdc); if(iFont < 0) _qwFontSig = GetFontSignatureFromDC(_hdc, fNonBiDiAscii); SelectFont(_hdc, hfontOld); } return TRUE; } /* * HFONT CCcs::GetFontWithMetrics (plf, szNewFaceName) * * @mfunc * Get metrics used by the measurer and renderer and the new face name. * * @rdesc * HFONT if successful */ HFONT CCcs::GetFontWithMetrics ( LOGFONT *plf, WCHAR * szNewFaceName) { _hfont = CreateFontIndirect(plf); if(_hfont) GetMetrics(szNewFaceName); return (_hfont); } /* * CCcs::GetOffset(pCF, dvpInch, pyOffset, pyAdjust); * * @mfunc * Return the offset information for * * @comm * Return the offset value (used in line height calculations) * and the amount to raise or lower the text because of superscript * or subscript considerations. */ void CCcs::GetOffset( const CCharFormat * const pCF, LONG dvpInch, LONG * pyOffset, LONG * pyAdjust) { *pyOffset = 0; *pyAdjust = 0; if (pCF->_yOffset) *pyOffset = MulDiv(pCF->_yOffset, dvpInch, LY_PER_INCH); if (pCF->_dwEffects & CFE_SUPERSCRIPT) *pyAdjust = _yHeight * 2 / 5; else if (pCF->_dwEffects & CFE_SUBSCRIPT) *pyAdjust = -_yDescent * 3 / 5; } /* * void CCcs::GetFontOverhang(pdupOverhang, pdupUnderhang) * * @mfunc * Synthesize font overhang/underhang information. * Only applies to italic fonts. */ void CCcs::GetFontOverhang( LONG *pdupOverhang, LONG *pdupUnderhang) { if(_fItalic) { *pdupOverhang = (_yHeight - _yDescent + 1) / 4; *pdupUnderhang = (_yDescent + 1) / 4; } else { *pdupOverhang = 0; *pdupUnderhang = 0; } } /* * BOOL CCcs::GetMetrics(szNewFaceName) * * @mfunc * Get metrics used by the measurer and renderer. * * @rdesc * TRUE if successful * * @comm * These are in logical coordinates which are dependent * on the mapping mode and font selected into the hdc. */ BOOL CCcs::GetMetrics( WCHAR *szNewFaceName) { TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CCcs::GetMetrics"); AssertSz(_hfont, "No font has been created."); if (szNewFaceName) *szNewFaceName = 0; HFONT hfontOld = SelectFont(_hdc, _hfont); if(!hfontOld) { DestroyFont(); return FALSE; } if (szNewFaceName) GetTextFace(_hdc, LF_FACESIZE, szNewFaceName); TEXTMETRIC tm; if(!GetTextMetrics(_hdc, &tm)) { SelectFont(_hdc, hfontOld); DestroyFont(); return FALSE; } // The metrics, in logical units, dependent on the map mode and font. _yHeight = (SHORT) tm.tmHeight; _yDescent = (SHORT) tm.tmDescent; _xAveCharWidth = (SHORT) tm.tmAveCharWidth; _xOverhangAdjust= (SHORT) tm.tmOverhang; // If fixed pitch, the tm bit is clear _fFixPitchFont = !(TMPF_FIXED_PITCH & tm.tmPitchAndFamily); _bCharSet = tm.tmCharSet; _fFECharSet = IsFECharSet(_bCharSet); // Use convert-mode proposed by CF, for which we are creating the font and // then tweak as necessary below. _bConvertMode = _bCMDefault; // If SYMBOL_CHARSET is used, use the A APIs with the low bytes of the // characters in the run if(_bCharSet == SYMBOL_CHARSET) _bConvertMode = CVT_LOWBYTE; else if (_bConvertMode == CVT_NONE) _bConvertMode = W32->DetermineConvertMode(_hdc, tm.tmCharSet); W32->CalcUnderlineInfo(_hdc, this, &tm); SelectFont(_hdc, hfontOld); return TRUE; } /* * CCcs::DestroyFont() * * @mfunc * Destroy font handle for this CCcs */ void CCcs::DestroyFont() { TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CCcs::DestroyFont"); // Clear out any old font if(_hfont) { SideAssert(DeleteObject(_hfont)); _hfont = 0; } } /* * CCcs::Compare (pCF, hdc, dwFlags) * * @mfunc * Compares this font cache with the font properties of a * given CHARFORMAT * @devnote The pCF size here is in logical units * * @rdesc * FALSE iff did not match exactly. */ BOOL CCcs::Compare ( const CCharFormat * const pCF, //@parm Description of desired font HDC hdc, DWORD dwFlags) { TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CCcs::Compare"); BYTE bCharSet = CharSetFromCharRep(pCF->_iCharRep); BOOL result = _iFont == pCF->_iFont && _weight == pCF->_wWeight && _fItalic == ((pCF->_dwEffects & CFE_ITALIC) != 0) && _hdc == hdc && _yHeightRequest == pCF->_yHeight && (_bCharSetRequest == bCharSet || _bCharSet == bCharSet // || _qwFontSig & FontSigFromCharRep(pCF->_iCharRep)// FUTURE: ) && // ok except for codepage conversions (metafiles and Win9x) _fCustomTextOut == ((pCF->_dwEffects & CFE_CUSTOMTEXTOUT) != 0) && _fForceTrueType == ((dwFlags & FGCCSUSETRUETYPE) != 0) && _fUseAtFont == ((dwFlags & FGCCSUSEATFONT) != 0) && _tflow == (dwFlags & 0x3) && _bPitchAndFamily == pCF->_bPitchAndFamily && (!(pCF->_dwEffects & CFE_RUNISDBCS) || _bConvertMode == CVT_LOWBYTE); return result; } // ========================= WidthCache by jonmat ========================= /* * CWidthCache::CheckWidth(ch, &dup) * * @mfunc * Check to see if we have a width for a WCHAR character. * * @comm * Used prior to calling FillWidth(). Since FillWidth * may require selecting the map mode and font in the HDC, * checking here first saves time. * * @comm * Statistics are maintained to determine when to * expand the cache. The determination is made after a constant * number of calls in order to make calculations faster. * * @rdesc * returns TRUE if we have the width of the given WCHAR. */ BOOL CWidthCache::CheckWidth ( const WCHAR ch, //@parm char, can be Unicode, to check width for LONG & dup) //@parm Width of character { TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CWidthCache::CheckWidth"); BOOL fExist; // 30,000 FE characters all have the same width if (FLookasideCharacter(ch)) { dup = _dupCJK; return dup != 0; } const CacheEntry * pWidthData = GetEntry ( ch ); fExist = (ch == pWidthData->ch // Have we fetched the width? && pWidthData->width); // only because we may have ch == 0. dup = fExist ? pWidthData->width : 0; if(!_fMaxPerformance) // if we have not grown to the max... { _accesses++; if(!fExist) // Only interesting on collision. { if(0 == pWidthData->width) // Test width not ch, 0 is valid ch. { _cacheUsed++; // Used another entry. AssertSz( _cacheUsed <= _cacheSize+1, "huh?"); } else _collisions++; // We had a collision. if(_accesses >= PERFCHECKEPOCH) CheckPerformance(); // After some history, tune cache. } } #ifdef DEBUG // Continue to monitor performance else { _accesses++; if(!fExist) // Only interesting on collision. { if(0 == pWidthData->width) // Test width not ch, 0 is valid ch. { _cacheUsed++; // Used another entry. AssertSz( _cacheUsed <= _cacheSize+1, "huh?"); } else _collisions++; // We had a collision. } if(_accesses > PERFCHECKEPOCH) { _accesses = 0; _collisions = 0; } } #endif return fExist; } /* * CWidthCache::CheckPerformance() * * @mfunc * check performance and increase cache size if deemed necessary. * */ void CWidthCache::CheckPerformance() { TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CWidthCache::CheckPerformance"); if(_fMaxPerformance) // Exit if already grown to our max. return; // Grow the cache when cacheSize > 0 && 75% utilized or approx 8% // collision rate if (_cacheSize > DEFAULTCACHESIZE && (_cacheSize >> 1) + (_cacheSize >> 2) < _cacheUsed || _collisions > 0 && _accesses / _collisions <= 12) { GrowCache( &_pWidthCache, &_cacheSize, &_cacheUsed ); } _collisions = 0; // This prevents wraps but makes _accesses = 0; // calc a local rate, not global. if(_cacheSize >= maxCacheSize) // Note if we've max'ed out _fMaxPerformance = TRUE; AssertSz( _cacheSize <= maxCacheSize, "max must be 2^n-1"); AssertSz( _cacheUsed <= _cacheSize+1, "huh?"); } /* * CWidthCache::GrowCache(ppWidthCache, pCacheSize, pCacheUsed) * * @mfunc * Exponentially expand the size of the cache. * * @comm * The cache size must be of the form 2^n as we use a * logical & to get the hash MOD by storing 2^n-1 as * the size and using this as the modulo. * * @rdesc * Returns TRUE if we were able to allocate the new cache. * All in params are also out params. * */ BOOL CWidthCache::GrowCache( CacheEntry **ppWidthCache, //@parm cache INT * pCacheSize, //@parm cache's respective size. INT * pCacheUsed) //@parm cache's respective utilization. { TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CWidthCache::GrowCache"); CacheEntry *pNewWidthCache, *pOldWidthCache, *pWidthData; INT j, newCacheSize, newCacheUsed; WCHAR ch; j = *pCacheSize; // Allocate cache of 2^n. newCacheSize = max ( INITIALCACHESIZE, (j << 1) + 1); pNewWidthCache = (CacheEntry *) PvAlloc( sizeof(CacheEntry) * (newCacheSize + 1 ), GMEM_ZEROINIT); if(pNewWidthCache) { newCacheUsed = 0; *pCacheSize = newCacheSize; // Update out params. pOldWidthCache = *ppWidthCache; *ppWidthCache = pNewWidthCache; for (; j >= 0; j--) // Move old cache info to new. { ch = pOldWidthCache[j].ch; if ( ch ) { pWidthData = &pNewWidthCache [ch & newCacheSize]; if ( 0 == pWidthData->ch ) newCacheUsed++; // Used another entry. pWidthData->ch = ch; pWidthData->width = pOldWidthCache[j].width; } } *pCacheUsed = newCacheUsed; // Update out param. // Free old cache. if (pOldWidthCache < &_defaultWidthCache[0] || pOldWidthCache >= &_defaultWidthCache[DEFAULTCACHESIZE+1]) { FreePv(pOldWidthCache); } } return NULL != pNewWidthCache; } /* * CWidthCache::Free() * * @mfunc * Free any dynamic memory allocated by the width cache and prepare * it to be recycled. */ void CWidthCache::Free() { TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CWidthCache::Free"); _fMaxPerformance = FALSE; _dupCJK = 0; _cacheSize = DEFAULTCACHESIZE; _cacheUsed = 0; _collisions = 0; _accesses = 0; if(_pWidthCache != &_defaultWidthCache[0]) { FreePv(_pWidthCache); _pWidthCache = &_defaultWidthCache[0]; } ZeroMemory(_pWidthCache, sizeof(CacheEntry)*(DEFAULTCACHESIZE + 1)); } /* * CWidthCache::CWidthCache() * * @mfunc * Point the caches to the defaults. */ CWidthCache::CWidthCache() { TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CWidthCache::CWidthCache"); _pWidthCache = &_defaultWidthCache[0]; } /* * CWidthCache::~CWidthCache() * * @mfunc * Free any allocated caches. */ CWidthCache::~CWidthCache() { TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CWidthCache::~CWidthCache"); if (_pWidthCache != &_defaultWidthCache[0]) FreePv(_pWidthCache); }