Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1601 lines
44 KiB

  1. /*
  2. * @doc INTERNAL
  3. *
  4. * @module FONT.CPP -- font cache |
  5. *
  6. * Includes font cache, char width cache;
  7. * create logical font if not in cache, look up
  8. * character widths on an as needed basis (this
  9. * has been abstracted away into a separate class
  10. * so that different char width caching algos can
  11. * be tried.) <nl>
  12. *
  13. * Owner: <nl>
  14. * RichEdit 1.0 code: David R. Fulmer
  15. * Christian Fortini (initial conversion to C++)
  16. * Jon Matousek <nl>
  17. *
  18. * History: <nl>
  19. * 7/26/95 jonmat cleanup and reorganization, factored out
  20. * char width caching code into a separate class.
  21. *
  22. * Copyright (c) 1995-1998 Microsoft Corporation. All rights reserved.
  23. */
  24. #include "_common.h"
  25. #include "_font.h"
  26. #include "_rtfconv.h" // Needed for GetCodePage
  27. #include "_uspi.h"
  28. #define CLIP_DFA_OVERRIDE 0x40 // Used to disable Korea & Taiwan font association
  29. #define FF_BIDI 7
  30. ASSERTDATA
  31. // Corresponds to yHeightCharPtsMost in richedit.h
  32. #define yHeightCharMost 32760
  33. // NOTE: this is global across all instances in the same process.
  34. static CFontCache *g_fc;
  35. static FONTINFO *g_pFontInfo = NULL;
  36. static LONG g_cFontInfo = 0;
  37. static LONG g_cFontInfoMax = 0;
  38. //Fonts automatically added to our font table
  39. const WCHAR *szArial = L"Arial"; // IFONT_ARIAL
  40. const WCHAR *szTimesNewRoman = L"Times New Roman"; // IFONT_TIMESNEWROMAN
  41. const WCHAR *szSymbol = L"Symbol"; // IFONT_SYMBOL
  42. const WCHAR *szSystem = L"System"; // IFONT_SYSTEM
  43. const int cfontsDflt = 4;
  44. //Other fonts that we do use, but aren't automatically added to our font table
  45. const WCHAR *szMicrosSansSerif = L"Microsoft Sans Serif";
  46. const WCHAR *szMSSansSerif = L"MS Sans Serif";
  47. const WCHAR *szMangal = L"Mangal";
  48. const WCHAR *szLatha = L"Latha";
  49. const WCHAR *szCordiaNew = L"Cordia New";
  50. const WCHAR *szTahoma = L"Tahoma";
  51. const WCHAR *szArialUnicode = L"Arial Unicode MS";
  52. const WCHAR *szWingdings = L"Wingdings";
  53. #define szFontOfChoice szArial
  54. /*
  55. * GetFontNameIndex(pFontName)
  56. *
  57. * @func
  58. * return index into global pszFontName table for fontname pFontName.
  59. * If fontname isn't in table, add it and return index.
  60. *
  61. * @rdesc
  62. * fontname index corresponding to pFontName
  63. *
  64. * @devnote
  65. * This uses a linear search, so the most common font names should be
  66. * up front. Internally, we use the fontname indices, so the search
  67. * isn't done that often. Note also that the fontname table only grows,
  68. * but this is probably OK for most clients. Else we need ref counting...
  69. */
  70. SHORT GetFontNameIndex(
  71. const WCHAR *pFontName)
  72. {
  73. CLock Lock; // Wonder how much this slows things down...
  74. for(LONG i = 0; i < g_cFontInfo; i++)
  75. {
  76. // A hash could speed this up if perf turns out poor
  77. if(!wcscmp(pFontName, g_pFontInfo[i].szFontName))
  78. return i;
  79. }
  80. if(g_cFontInfo + 1 >= g_cFontInfoMax)
  81. {
  82. // Note that PvReAlloc() reverts to PvAlloc() if g_pFontInfo is NULL
  83. FONTINFO *pFI = (FONTINFO *)PvReAlloc((LPVOID)g_pFontInfo,
  84. sizeof(FONTINFO) * (8 + g_cFontInfo));
  85. if(!pFI)
  86. return IFONT_ARIAL; // Out of memory...
  87. // Initialize the structure
  88. ZeroMemory (&pFI[g_cFontInfo], 8 * sizeof(FONTINFO));
  89. // attempts to fill them in
  90. if(!g_cFontInfoMax) // First allocation
  91. {
  92. Assert(IFONT_ARIAL == 0 && IFONT_TMSNEWRMN == 1 &&
  93. IFONT_SYMBOL == 2 && IFONT_SYSTEM == 3);
  94. pFI[IFONT_ARIAL].szFontName = szArial;
  95. pFI[IFONT_TMSNEWRMN].szFontName = szTimesNewRoman;
  96. pFI[IFONT_SYMBOL].szFontName = szSymbol;
  97. pFI[IFONT_SYSTEM].szFontName = szSystem;
  98. g_cFontInfo = cfontsDflt;
  99. }
  100. g_pFontInfo = pFI;
  101. g_cFontInfoMax += 8;
  102. }
  103. LONG cb = (wcslen(pFontName) + 1)*sizeof(WCHAR);
  104. WCHAR * pch = (WCHAR *)PvAlloc(cb, GMEM_MOVEABLE);
  105. if(!pch)
  106. return IFONT_ARIAL; // Out of memory...
  107. g_pFontInfo[g_cFontInfo].szFontName = pch;
  108. CopyMemory((void *)pch, pFontName, cb);
  109. return g_cFontInfo++;
  110. }
  111. /*
  112. * GetFontName(iFont)
  113. *
  114. * @func
  115. * return fontname given by g_pFontInfo[iFont].szFontName.
  116. *
  117. * @rdesc
  118. * fontname corresponding to fontname index iFont
  119. */
  120. const WCHAR *GetFontName(
  121. LONG iFont)
  122. {
  123. return (iFont < g_cFontInfo) ? g_pFontInfo[iFont].szFontName : NULL;
  124. }
  125. /*
  126. * SetFontLegitimateSize(iFont, fUIFont, iSize)
  127. *
  128. * @func
  129. * Set the legitimate size (readable smallest size to use) of a given font
  130. */
  131. BOOL SetFontLegitimateSize(
  132. LONG iFont,
  133. BOOL fUIFont,
  134. BYTE bSize,
  135. int cpg)
  136. {
  137. if (iFont < g_cFontInfo)
  138. {
  139. // Far East wanted to do it per codepage.
  140. //
  141. // FUTURE: Bear in mind that this approach is bug-prone. Once there's
  142. // any new FE font created with different metric from the existing one.
  143. // Font scaling will not perform well or even broken for such font [wchao].
  144. g_pFontInfo[iFont].ff.fScaleByCpg = W32->IsFECodePage(cpg);
  145. if (fUIFont)
  146. {
  147. if (!g_pFontInfo[iFont].bSizeUI)
  148. g_pFontInfo[iFont].bSizeUI = bSize;
  149. else
  150. // more than one legit size were updated per font,
  151. // We fallback to the codepage-driven approach.
  152. g_pFontInfo[iFont].ff.fScaleByCpg = g_pFontInfo[iFont].bSizeUI != bSize;
  153. }
  154. else
  155. {
  156. if (!g_pFontInfo[iFont].bSizeNonUI)
  157. g_pFontInfo[iFont].bSizeNonUI = bSize;
  158. else
  159. g_pFontInfo[iFont].ff.fScaleByCpg = g_pFontInfo[iFont].bSizeNonUI != bSize;
  160. }
  161. return TRUE;
  162. }
  163. return FALSE;
  164. }
  165. BYTE GetFontLegitimateSize(
  166. LONG iFont,
  167. BOOL fUIFont,
  168. int cpg) // requested size for given codepage
  169. {
  170. SHORT iDefFont;
  171. BYTE bDefPaf;
  172. BYTE yHeight = 0;
  173. if (iFont < g_cFontInfo && !g_pFontInfo[iFont].ff.fScaleByCpg)
  174. yHeight = fUIFont ? g_pFontInfo[iFont].bSizeUI : g_pFontInfo[iFont].bSizeNonUI;
  175. if (!yHeight && fc().GetInfoFlags(iFont).fNonBiDiAscii)
  176. {
  177. // non-BiDi ASCII font uses table font (of the same charset) legitimate height
  178. DWORD dwSig = GetFontSignatureFromFace(iFont) & ~((fASCII | fFE) >> 8);
  179. int cpg = GetCodePage(GetFirstAvailCharSet(dwSig));
  180. W32->GetPreferredFontInfo(cpg, fUIFont ? true : false, iDefFont, yHeight, bDefPaf);
  181. SetFontLegitimateSize(iFont, fUIFont ? true : false, yHeight ? yHeight : fUIFont ? 8 : 10, cpg);
  182. }
  183. if (!yHeight)
  184. {
  185. if (fc().GetInfoFlags(iFont).fThaiDTP)
  186. {
  187. cpg = THAI_INDEX;
  188. fUIFont = FALSE;
  189. }
  190. W32->GetPreferredFontInfo(cpg, fUIFont ? true : false, iDefFont, yHeight, bDefPaf);
  191. }
  192. return yHeight ? yHeight : fUIFont ? 8 : 10;
  193. }
  194. /*
  195. * GetTextCharsetInfoPri(hdc, pFontSig, dwFlags)
  196. *
  197. * @func
  198. * Wrapper to GDI's GetTextCharsetInfo. This to handle BiDi old-style fonts
  199. */
  200. UINT GetTextCharsetInfoPri(
  201. HDC hdc,
  202. FONTSIGNATURE* pFontSig,
  203. DWORD dwFlags)
  204. {
  205. OUTLINETEXTMETRICA otm;
  206. INT uCharSet = -1;
  207. if (pFontSig && GetOutlineTextMetricsA(hdc, sizeof(OUTLINETEXTMETRICA), &otm))
  208. {
  209. ZeroMemory (pFontSig, sizeof(FONTSIGNATURE));
  210. switch (otm.otmfsSelection & 0xFF00)
  211. {
  212. case 0xB200: // Arabic Simplified
  213. case 0xB300: // Arabic Traditional
  214. case 0xB400: // Arabic Old UDF
  215. uCharSet = ARABIC_CHARSET; break;
  216. case 0xB100: // Hebrew Old style
  217. uCharSet = HEBREW_CHARSET;
  218. }
  219. }
  220. if (uCharSet == -1)
  221. uCharSet = GetTextCharsetInfo(hdc, pFontSig, dwFlags);
  222. if (uCharSet == DEFAULT_CHARSET)
  223. uCharSet = ANSI_CHARSET; // never return ambiguous
  224. return (UINT)uCharSet;
  225. }
  226. /*
  227. * GetFontSignatureFromFace(iFont, DWORD* pdwFontSig)
  228. *
  229. * @func
  230. * Giving font signature matching the index of given facename.
  231. * This signature may not match the one in Cccs since this is the
  232. * signature of the font of given facename. The Cccs one is
  233. * per GDI realization.
  234. *
  235. * @rdesc
  236. * - font signature if pdwFontSig is NULL.
  237. * - If pdwFontSig != NULL. It's a boolean.
  238. * ZERO means returned signature is not sensible by following reasons
  239. * 1. Bad facename (junk like "!@#$" or name that doesnt exist in the system)
  240. * 2. Given face doesnt support even one valid ANSI codepage (symbol fonts i.e, Marlett)
  241. */
  242. DWORD GetFontSignatureFromFace(
  243. int iFont,
  244. DWORD* pdwFontSig)
  245. {
  246. Assert((unsigned)iFont < (unsigned)g_cFontInfo);
  247. DWORD dwFontSig = g_pFontInfo[iFont].dwFontSig;
  248. FONTINFO_FLAGS ff;
  249. ff.wFlags = g_pFontInfo[iFont].ff.wFlags;
  250. if(!ff.fCached)
  251. {
  252. LOGFONT lf;
  253. HDC hdc = GetDC(NULL);
  254. WCHAR* pwchTag = lf.lfFaceName;
  255. int i = 0;
  256. ZeroMemory(&lf, sizeof(LOGFONT));
  257. wcscpy(lf.lfFaceName, GetFontName(iFont));
  258. // exclude Win95's tag name e.g. "Arial(Greek)"
  259. while (pwchTag[i] && pwchTag[i] != '(')
  260. i++;
  261. if(pwchTag[i] && i > 0)
  262. {
  263. while (i > 0 && pwchTag[i-1] == 0x20)
  264. i--;
  265. pwchTag[i] = 0;
  266. }
  267. lf.lfCharSet = DEFAULT_CHARSET;
  268. // obtain a charset supported by given facename
  269. // to force GDI gives facename priority over charset.
  270. W32->GetFacePriCharSet(hdc, &lf);
  271. HFONT hfont = CreateFontIndirect(&lf);
  272. if(hfont)
  273. {
  274. HFONT hfontOld = SelectFont(hdc, hfont);
  275. WCHAR szNewFaceName[LF_FACESIZE];
  276. GetTextFace(hdc, LF_FACESIZE, szNewFaceName);
  277. if(!wcsicmp(szNewFaceName, lf.lfFaceName) || // Got it
  278. ((GetCharFlags(szNewFaceName[0]) & fFE) && // or Get back FE font name for English name
  279. (GetCharFlags(lf.lfFaceName[0]) & fASCII))) // because NT5 supports dual font names.
  280. {
  281. CHARSETINFO csi;
  282. // Try to get FONTSIGNATURE data
  283. UINT uCharSet = GetTextCharsetInfoPri(hdc, &(csi.fs), 0);
  284. DWORD dwUsb0 = W32->OnWin9x() ? 0 : csi.fs.fsUsb[0];
  285. if ((csi.fs.fsCsb[0] | dwUsb0) ||
  286. TranslateCharsetInfo((DWORD *)(DWORD_PTR)uCharSet, &csi, TCI_SRCCHARSET))
  287. {
  288. CUniscribe* pusp;
  289. SCRIPT_CACHE sc = NULL;
  290. WORD wGlyph;
  291. dwFontSig = csi.fs.fsCsb[0];
  292. // Also look at Unicode subrange if available
  293. // FUTURE: we may want to drive Unicode ranges with a
  294. // table approach, i.e., use for loop shifting dwUsb0 right
  295. // to convert each bit into an index into a table of BYTEs
  296. // that return the appropriate script index for rgCpgCharSet:
  297. //
  298. // for(LONG i = 0; dwUsb0; dwUsb0 >>= 1, i++)
  299. // {
  300. // static const BYTE iScript[32] = {...};
  301. // if(dwUsb0 & 1)
  302. // dwFontSig |= W32->GetFontSigFromScript(iScript[i]);
  303. // }
  304. if (dwUsb0 & 0x00008000)
  305. dwFontSig |= fDEVANAGARI >> 8;
  306. if (dwUsb0 & 0x00100000)
  307. dwFontSig |= fTAMIL >> 8;
  308. if (dwUsb0 & 0x00000400)
  309. dwFontSig |= fARMENIAN >> 8;
  310. if (dwUsb0 & 0x04000000)
  311. dwFontSig |= fGEORGIAN >> 8;
  312. if((dwFontSig & fCOMPLEX_SCRIPT >> 8) && !(dwFontSig & fHILATIN1 >> 8)
  313. && (pusp = GetUniscribe()))
  314. {
  315. // signature says no Latin-1 support
  316. // Search for the 'a' and '0' glyph in the font to determine if the font
  317. // supports ASCII or European Digit. This is necessary to overcome
  318. // the font having incomplete font signature.
  319. //
  320. if (ScriptGetCMap(hdc, &sc, L"a", 1, 0, &wGlyph) == S_OK)
  321. dwFontSig |= fASCIIUPR >> 8;
  322. if (ScriptGetCMap(hdc, &sc, L"0", 1, 0, &wGlyph) == S_OK)
  323. dwFontSig |= fBELOWX40 >> 8;
  324. if (!IsBiDiCharSet(uCharSet) &&
  325. (dwFontSig & (fASCII >> 8)) == fASCII >> 8)
  326. ff.fNonBiDiAscii = 1; // non-BiDi ASCII font
  327. ScriptFreeCache(&sc);
  328. }
  329. if (dwFontSig & fHILATIN1 >> 8)
  330. dwFontSig |= fASCII >> 8; // fLATIN1 has 3 bits
  331. // HACK for symbol font. We assign 0x04000(fSYMBOL >> 8) for Symbol font signature.
  332. if (uCharSet == SYMBOL_CHARSET && !(dwFontSig & 0x3fffffff))
  333. dwFontSig |= fSYMBOL >> 8;
  334. }
  335. }
  336. else
  337. {
  338. ff.fBadFaceName = TRUE;
  339. }
  340. TEXTMETRIC tm;
  341. GetTextMetrics(hdc, &tm);
  342. ff.fTrueType = tm.tmPitchAndFamily & TMPF_TRUETYPE ? 1 : 0;
  343. ff.fBitmap = tm.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_VECTOR | TMPF_DEVICE) ? 0 : 1;
  344. if (!ff.fBadFaceName && dwFontSig & (fTHAI >> 8))
  345. {
  346. // Some heuristic test on Thai fonts.
  347. // Most Thai fonts will fall to this category currently except for
  348. // Tahoma and Microsoft Sans Serif.
  349. ff.fThaiDTP = tm.tmDescent && tm.tmAscent/tm.tmDescent < 3;
  350. }
  351. SelectObject(hdc, hfontOld);
  352. SideAssert(DeleteObject(hfont));
  353. }
  354. ReleaseDC(NULL, hdc);
  355. // Cache code pages supported by this font
  356. ff.fCached = TRUE;
  357. g_pFontInfo[iFont].dwFontSig = dwFontSig;
  358. g_pFontInfo[iFont].ff.wFlags = ff.wFlags;
  359. }
  360. if (!pdwFontSig)
  361. return dwFontSig;
  362. *pdwFontSig = dwFontSig;
  363. // Exclude bit 30-31 (as system reserved - NT masks 31 as symbol codepage)
  364. // 22-29 are reserved for alternate ANSI/OEM, as of now we use 21, 22 for Devanagari and Tamil
  365. return (DWORD)((dwFontSig & 0x3fffffff) && !ff.fBadFaceName);
  366. }
  367. /*
  368. * FreeFontNames()
  369. *
  370. * @func
  371. * Free fontnames given by g_pFontInfo[i].szFontName allocated by
  372. * GetFontNameIndex() as well as g_pFontInfo itself.
  373. */
  374. void FreeFontNames()
  375. {
  376. for(LONG i = cfontsDflt; i < g_cFontInfo; i++)
  377. FreePv((LPVOID)g_pFontInfo[i].szFontName);
  378. FreePv(g_pFontInfo);
  379. g_pFontInfo = NULL;
  380. }
  381. SHORT g_iFontJapanese;
  382. SHORT g_iFontHangul;
  383. SHORT g_iFontBig5;
  384. SHORT g_iFontGB2312;
  385. /*
  386. * InitFontCache()
  387. *
  388. * @func
  389. * Initializes font cache.
  390. *
  391. * @devnote
  392. * This is exists so reinit.cpp doesn't have to know all about the
  393. * font cache.
  394. */
  395. void InitFontCache()
  396. {
  397. g_fc = new CFontCache;
  398. g_fc->Init();
  399. }
  400. /*
  401. * FreeFontCache()
  402. *
  403. * @mfunc
  404. * Frees font cache.
  405. *
  406. * @devnote
  407. * This is exists so reinit.cpp doesn't have to know all about the
  408. * font cache.
  409. */
  410. void FreeFontCache()
  411. {
  412. delete g_fc;
  413. g_fc = NULL;
  414. FreeFontNames();
  415. }
  416. /*
  417. * CFontCache & fc()
  418. *
  419. * @func
  420. * initialize the global g_fc.
  421. * @comm
  422. * current #defined to store 16 logical fonts and
  423. * respective character widths.
  424. *
  425. */
  426. CFontCache & fc()
  427. {
  428. TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "fc");
  429. return *g_fc;
  430. }
  431. FONTINFO_FLAGS CFontCache::GetInfoFlags(int ifont)
  432. {
  433. if (!g_pFontInfo[ifont].ff.fCached)
  434. GetFontSignatureFromFace(ifont);
  435. return g_pFontInfo[ifont].ff;
  436. }
  437. // =================================== CFontCache ====================================
  438. /*
  439. * CFontCache::Init()
  440. *
  441. * @mfunc
  442. * Initializes font cache.
  443. *
  444. * @devnote
  445. * This is not a constructor because something bad seems to happen
  446. * if we try to construct a global object.
  447. */
  448. void CFontCache::Init()
  449. {
  450. TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CFontCache::CFontCache");
  451. _dwAgeNext = 0;
  452. }
  453. /*
  454. * CFontCache::MakeHashKey(pCF)
  455. *
  456. * @mfunc
  457. * Build a hash key for quick searches for a CCcs matching
  458. * the pCF.
  459. * Format:
  460. * iFont : 14
  461. * Bold/Italic : 2
  462. * Height : 16
  463. *
  464. */
  465. CCSHASHKEY CFontCache::MakeHashKey(const CCharFormat *pCF)
  466. {
  467. CCSHASHKEY ccshashkey;
  468. ccshashkey = pCF->_iFont | ((pCF->_dwEffects & 3) << 14);
  469. ccshashkey |= pCF->_yHeight << 16;
  470. return ccshashkey;
  471. }
  472. /*
  473. * CFontCache::GetCcs(pCF, dypInch, yPixelsPerInch)
  474. *
  475. * @mfunc
  476. * Search the font cache for a matching logical font and return it.
  477. * If a match is not found in the cache, create one.
  478. *
  479. * @rdesc
  480. * A logical font matching the given CHARFORMAT info.
  481. *
  482. * @devnote
  483. * The calling chain must be protected by a CLock, since this present
  484. * routine access the global (shared) FontCache facility.
  485. */
  486. CCcs* CFontCache::GetCcs(
  487. const CCharFormat *const pCF, //@parm description of desired logical font
  488. const LONG dypInch, //@parm Y pixels per inch
  489. HDC hdc, //@parm HDC font is to be created for
  490. BOOL fForceTrueType) //@parm Force a TrueType font to be used
  491. {
  492. TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CFontCache::GetCcs");
  493. // display font
  494. const CCcs * const pccsMost = &_rgccs[FONTCACHESIZE - 1];
  495. CCcs * pccs;
  496. CCSHASHKEY ccshashkey;
  497. int iccsHash;
  498. CCharFormat CF = *pCF;
  499. if (fForceTrueType)
  500. {
  501. //On Win '9x Thai/Vietnamese, you cannot force truetype fonts! Therefore,
  502. //we will force Tahoma if the font doesn't support the right charset.
  503. if (W32->OnWin9x())
  504. {
  505. UINT acp = GetACP();
  506. if (acp == 1258 || acp == 874)
  507. {
  508. DWORD fontsig = GetFontSignatureFromFace(CF._iFont);
  509. if (CF._bCharSet == THAI_CHARSET && ((fontsig & fTHAI >> 8) == 0) ||
  510. CF._bCharSet == VIETNAMESE_CHARSET && ((fontsig & fVIETNAMESE >> 8) == 0) ||
  511. !g_fc->GetInfoFlags(pCF->_iFont).fTrueType)
  512. {
  513. CF._iFont = GetFontNameIndex(szTahoma);
  514. }
  515. }
  516. }
  517. else if (!g_fc->GetInfoFlags(pCF->_iFont).fTrueType)
  518. CF._dwEffects |= CFE_TRUETYPEONLY;
  519. }
  520. if (hdc == NULL)
  521. hdc = W32->GetScreenDC();
  522. // Change CF._yHeight in the case of sub/superscript
  523. if(CF._dwEffects & (CFE_SUPERSCRIPT | CFE_SUBSCRIPT))
  524. CF._yHeight = 2*CF._yHeight/3;
  525. //Convert CCharFormat into logical units (round)
  526. CF._yHeight = (CF._yHeight * dypInch + LY_PER_INCH / 2) / LY_PER_INCH;
  527. if (CF._yHeight == 0)
  528. CF._yHeight = 1;
  529. ccshashkey = MakeHashKey(&CF);
  530. // Check our hash before going sequential.
  531. iccsHash = ccshashkey % CCSHASHSEARCHSIZE;
  532. if(ccshashkey == quickHashSearch[iccsHash].ccshashkey)
  533. {
  534. pccs = quickHashSearch[iccsHash].pccs;
  535. if(pccs && pccs->_fValid)
  536. {
  537. if(pccs->Compare(&CF, hdc))
  538. goto matched;
  539. }
  540. }
  541. else //Setup this hash hint for next time
  542. quickHashSearch[iccsHash].ccshashkey = ccshashkey;
  543. // Sequentially search ccs for same character format
  544. for(pccs = &_rgccs[0]; pccs <= pccsMost; pccs++)
  545. {
  546. if(pccs->_ccshashkey == ccshashkey && pccs->_fValid)
  547. {
  548. if(!pccs->Compare(&CF, hdc))
  549. continue;
  550. quickHashSearch[iccsHash].pccs = pccs;
  551. matched:
  552. //$ FUTURE: make this work even with wrap around of dwAgeNext
  553. // Mark as most recently used if it isn't already in use.
  554. if(pccs->_dwAge != _dwAgeNext - 1)
  555. pccs->_dwAge = _dwAgeNext++;
  556. pccs->_cRefs++; // bump up ref. count
  557. return pccs;
  558. }
  559. }
  560. // We did not find a match: init a new font cache.
  561. pccs = GrabInitNewCcs(&CF, hdc);
  562. quickHashSearch[iccsHash].pccs = pccs;
  563. pccs->_ccshashkey = ccshashkey;
  564. pccs->_fForceTrueType = (CF._dwEffects & CFE_TRUETYPEONLY) ? TRUE : FALSE;
  565. return pccs;
  566. }
  567. /*
  568. * CFontCache::GrabInitNewCcs(pCF)
  569. *
  570. * @mfunc
  571. * create a logical font and store it in our cache.
  572. *
  573. * @rdesc
  574. * New CCcs created
  575. */
  576. CCcs* CFontCache::GrabInitNewCcs(
  577. const CCharFormat * const pCF, //@parm description of desired logical font
  578. HDC hdc)
  579. {
  580. TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CFontCache::GrabInitNewCcs");
  581. DWORD dwAgeOldest = 0xffffffff;
  582. CCcs * pccs;
  583. const CCcs * const pccsMost = &_rgccs[FONTCACHESIZE - 1];
  584. CCcs * pccsOldest = NULL;
  585. // Look for unused entry and oldest in use entry
  586. for(pccs = &_rgccs[0]; pccs <= pccsMost && pccs->_fValid; pccs++)
  587. if(pccs->_cRefs == 0 && pccs->_dwAge < dwAgeOldest)
  588. {
  589. dwAgeOldest = pccs->_dwAge;
  590. pccsOldest = pccs;
  591. }
  592. if(pccs > pccsMost) // Didn't find an unused entry, use oldest entry
  593. {
  594. pccs = pccsOldest;
  595. if(!pccs)
  596. {
  597. AssertSz(FALSE, "CFontCache::GrabInitNewCcs oldest entry is NULL");
  598. return NULL;
  599. }
  600. }
  601. // Initialize new CCcs
  602. pccs->_hdc = hdc;
  603. if(!pccs->Init(pCF))
  604. return NULL;
  605. pccs->_cRefs++;
  606. return pccs;
  607. }
  608. // ============================= CCcs class ===================================================
  609. /*
  610. * BOOL CCcs::Init()
  611. *
  612. * @mfunc
  613. * Init one font cache object. The global font cache stores
  614. * individual CCcs objects.
  615. */
  616. BOOL CCcs::Init (
  617. const CCharFormat * const pCF) //@parm description of desired logical font
  618. {
  619. TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CCcs::Init");
  620. if(_fValid)
  621. Free(); // recycle already in-use fonts.
  622. if(MakeFont(pCF))
  623. {
  624. _iFont = pCF->_iFont;
  625. _dwAge = g_fc->_dwAgeNext++;
  626. _fValid = TRUE; // successfully created a new font cache.
  627. }
  628. return _fValid;
  629. }
  630. /*
  631. * void CCcs::Free()
  632. *
  633. * @mfunc
  634. * Free any dynamic memory allocated by an individual font's cache.
  635. */
  636. void CCcs::Free()
  637. {
  638. TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CCcs::Free");
  639. Assert(_fValid);
  640. _widths.Free();
  641. if(_hfont)
  642. DestroyFont();
  643. if (_sc && g_pusp)
  644. ScriptFreeCache(&_sc);
  645. _fValid = FALSE;
  646. _cRefs = 0;
  647. }
  648. /*
  649. * CCcs::BestCharSet(bCharSet, bCharSetDefault)
  650. *
  651. * @mfunc
  652. * This function returns the best charset that the currently selected font
  653. * is capable of rendering. If the currently selected font cannot support
  654. * the requested charset, then the function returns bCharSetDefault, which
  655. * is generally taken from the charformat.
  656. *
  657. * @rdesc
  658. * The closest charset to bCharSet that can be rendered by the current
  659. * font.
  660. *
  661. * @devnote
  662. * Currently this function is only used with plain text, however I don't
  663. * believe there is any special reason it couldn't be used to improve
  664. * rendering of rich text as well.
  665. */
  666. BYTE CCcs::BestCharSet(BYTE bCharSet, BYTE bCharSetDefault, int fFontMatching)
  667. {
  668. TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CCcs::BestCharSet");
  669. // Does desired charset match currently selected charset or is it
  670. // supported by the currently selected font?
  671. if((bCharSet != _bCharSet || !bCharSet) &&
  672. (fFontMatching == MATCH_CURRENT_CHARSET || !(_dwFontSig & GetFontSig(bCharSet))))
  673. {
  674. // If desired charset is not selected and we can't switch to it,
  675. // switch to fallback charset (probably from backing store).
  676. return bCharSetDefault;
  677. }
  678. // We already match desired charset, or it is supported by the font.
  679. // Either way, we can just return the requested charset.
  680. return bCharSet;
  681. }
  682. /*
  683. * CCcs::FillWidth (ch, dxp)
  684. *
  685. * @mfunc
  686. * Fill in this CCcs with metrics info for given device
  687. *
  688. * @rdesc
  689. * TRUE if OK, FALSE if failed
  690. */
  691. BOOL CCcs::FillWidth(
  692. WCHAR ch, //@parm WCHAR character we need a width for.
  693. LONG &dxp) //@parm the width of the character
  694. {
  695. TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CCcs::FillWidths");
  696. AssertSz(_hfont, "CCcs::Fill - CCcs has no font");
  697. HFONT hfontOld = SelectFont(_hdc, _hfont);
  698. BOOL fRes = _widths.FillWidth(_hdc, ch, _xOverhangAdjust, dxp, _wCodePage, _xAveCharWidth);
  699. SelectFont(_hdc, hfontOld);
  700. return fRes;
  701. }
  702. /*
  703. * BOOL CCcs::MakeFont(pCF)
  704. *
  705. * @mfunc
  706. * Wrapper, setup for CreateFontIndirect() to create the font to be
  707. * selected into the HDC.
  708. *
  709. * @devnote The pCF here is in logical units
  710. *
  711. * @rdesc
  712. * TRUE if OK, FALSE if allocation failure
  713. */
  714. BOOL CCcs::MakeFont(
  715. const CCharFormat * const pCF) //@parm description of desired logical font
  716. {
  717. TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CCcs::MakeFont");
  718. LOGFONT lf;
  719. ZeroMemory(&lf, sizeof(lf));
  720. _bCMDefault = pCF->_dwEffects & CFE_RUNISDBCS ? CVT_LOWBYTE : CVT_NONE;
  721. _yHeightRequest = pCF->_yHeight;
  722. _bCharSetRequest = pCF->_bCharSet;
  723. lf.lfHeight = -_yHeightRequest;
  724. if(pCF->_wWeight)
  725. _weight = pCF->_wWeight;
  726. else
  727. _weight = (pCF->_dwEffects & CFE_BOLD) ? FW_BOLD : FW_NORMAL;
  728. lf.lfWeight = _weight;
  729. lf.lfItalic = _fItalic = (pCF->_dwEffects & CFE_ITALIC) != 0;
  730. lf.lfCharSet = _bCMDefault == CVT_LOWBYTE ? ANSI_CHARSET : GetGdiCharSet(pCF->_bCharSet);
  731. lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
  732. if (pCF->_dwEffects & CFE_TRUETYPEONLY)
  733. {
  734. lf.lfOutPrecision = OUT_TT_ONLY_PRECIS;
  735. if (!W32->OnWin9x() && g_fc->GetInfoFlags(pCF->_iFont).fTrueType)
  736. lf.lfOutPrecision = OUT_SCREEN_OUTLINE_PRECIS;
  737. }
  738. lf.lfClipPrecision = CLIP_DEFAULT_PRECIS | CLIP_DFA_OVERRIDE;
  739. lf.lfPitchAndFamily = _bPitchAndFamily = pCF->_bPitchAndFamily;
  740. // If family is virtual BiDi family (FF_BIDI), replace by FF_ROMAN
  741. if((lf.lfPitchAndFamily & 0xF0) == (FF_BIDI << 4))
  742. lf.lfPitchAndFamily = (FF_ROMAN << 4) | (lf.lfPitchAndFamily & 0xF);
  743. // If the run is DBCS, that means the font's codepage is not available in
  744. // this system. Use the English ANSI codepage instead so we will display
  745. // ANSI characters correctly. NOTE: _wCodePage is only used for Win95.
  746. _wCodePage = (WORD)GetCodePage(lf.lfCharSet);
  747. wcscpy(lf.lfFaceName, GetFontName(pCF->_iFont));
  748. // In BiDi system, always create ANSI bitmap font with system charset
  749. BYTE bSysCharSet = W32->GetSysCharSet();
  750. if (IsBiDiCharSet(bSysCharSet) && lf.lfCharSet == ANSI_CHARSET &&
  751. fc().GetInfoFlags(pCF->_iFont).fBitmap &&
  752. !fc().GetInfoFlags(pCF->_iFont).fBadFaceName)
  753. lf.lfCharSet = bSysCharSet;
  754. // Reader! A bundle of spagghetti code lies ahead of you!
  755. // But go on boldly, for these spagghetti are seasoned with
  756. // lots of comments, and ... good luck to you...
  757. HFONT hfontOriginalCharset = NULL;
  758. BYTE bOriginalCharset = lf.lfCharSet;
  759. WCHAR szNewFaceName[LF_FACESIZE];
  760. GetFontWithMetrics(&lf, szNewFaceName);
  761. if(0 != wcsicmp(szNewFaceName, lf.lfFaceName))
  762. {
  763. BOOL fCorrectFont = FALSE;
  764. if(lf.lfCharSet == SYMBOL_CHARSET)
  765. {
  766. // #1. if the face changed, and the specified charset was SYMBOL,
  767. // but the face name exists and suports ANSI, we give preference
  768. // to the face name
  769. lf.lfCharSet = ANSI_CHARSET;
  770. hfontOriginalCharset = _hfont;
  771. GetFontWithMetrics(&lf, szNewFaceName);
  772. if(0 == wcsicmp(szNewFaceName, lf.lfFaceName))
  773. // That's right, ANSI is the asnwer
  774. fCorrectFont = TRUE;
  775. else
  776. // No, fall back by default; the charset we got was right
  777. lf.lfCharSet = bOriginalCharset;
  778. }
  779. else if(lf.lfCharSet == DEFAULT_CHARSET && _bCharSet == DEFAULT_CHARSET)
  780. {
  781. // #2. If we got the "default" font back, we don't know what it means
  782. // (could be anything) so we veryfy that this guy's not SYMBOL
  783. // (symbol is never default, but the OS could be lying to us!!!)
  784. // we would like to veryfy more like whether it actually gave us
  785. // Japanese instead of ANSI and labeled it "default"...
  786. // but SYMBOL is the least we can do
  787. lf.lfCharSet = SYMBOL_CHARSET;
  788. wcscpy(lf.lfFaceName, szNewFaceName);
  789. hfontOriginalCharset = _hfont;
  790. GetFontWithMetrics(&lf, szNewFaceName);
  791. if(0 == wcsicmp(szNewFaceName, lf.lfFaceName))
  792. // That's right, it IS symbol!
  793. // 'correct' the font to the 'true' one,
  794. // and we'll get fMappedToSymbol
  795. fCorrectFont = TRUE;
  796. // Always restore the charset name, we didn't want to
  797. // question the original choice of charset here
  798. lf.lfCharSet = bOriginalCharset;
  799. }
  800. else if(lf.lfCharSet == ARABIC_CHARSET || lf.lfCharSet == HEBREW_CHARSET)
  801. {
  802. DestroyFont();
  803. wcscpy(lf.lfFaceName, szNewFaceName);
  804. GetFontWithMetrics(&lf, szNewFaceName);
  805. fCorrectFont = TRUE;
  806. }
  807. else if(_bConvertMode != CVT_LOWBYTE && IsFECharSet(lf.lfCharSet)
  808. && !OnWinNTFE() && !W32->OnWin9xFE())
  809. {
  810. const WCHAR *pch = NULL;
  811. if(_bCharSet != lf.lfCharSet && W32->OnWin9x())
  812. {
  813. // On Win95 when rendering to PS driver, we'll get something
  814. // other than what we asked. So try a known font we got from GDI
  815. switch (lf.lfCharSet)
  816. {
  817. case CHINESEBIG5_CHARSET:
  818. pch = GetFontName(g_iFontBig5);
  819. break;
  820. case SHIFTJIS_CHARSET:
  821. pch = GetFontName(g_iFontJapanese);
  822. break;
  823. case HANGEUL_CHARSET:
  824. pch = GetFontName(g_iFontHangul);
  825. break;
  826. case GB2312_CHARSET:
  827. pch = GetFontName(g_iFontGB2312);
  828. break;
  829. }
  830. }
  831. else // FE Font (from Lang pack)
  832. pch = szNewFaceName; // on a nonFEsystem
  833. if(pch)
  834. wcscpy(lf.lfFaceName, pch);
  835. hfontOriginalCharset = _hfont;
  836. GetFontWithMetrics(&lf, szNewFaceName);
  837. if(0 == wcsicmp(szNewFaceName, lf.lfFaceName))
  838. {
  839. // That's right, it IS the FE font we want!
  840. // 'correct' the font to the 'true' one.
  841. fCorrectFont = TRUE;
  842. if(W32->OnWin9x())
  843. {
  844. // Save up the GDI font names for later printing use
  845. switch(lf.lfCharSet)
  846. {
  847. case CHINESEBIG5_CHARSET:
  848. g_iFontBig5 = GetFontNameIndex(lf.lfFaceName);
  849. break;
  850. case SHIFTJIS_CHARSET:
  851. g_iFontJapanese = GetFontNameIndex(lf.lfFaceName);
  852. break;
  853. case HANGEUL_CHARSET:
  854. g_iFontHangul = GetFontNameIndex(lf.lfFaceName);
  855. break;
  856. case GB2312_CHARSET:
  857. g_iFontGB2312 = GetFontNameIndex(lf.lfFaceName);
  858. break;
  859. }
  860. }
  861. }
  862. }
  863. if(hfontOriginalCharset)
  864. {
  865. // Either keep old font or new one
  866. if(fCorrectFont)
  867. {
  868. SideAssert(DeleteObject(hfontOriginalCharset));
  869. }
  870. else
  871. {
  872. // Fall back to original font
  873. DestroyFont();
  874. _hfont = hfontOriginalCharset;
  875. GetMetrics();
  876. }
  877. hfontOriginalCharset = NULL;
  878. }
  879. }
  880. RetryCreateFont:
  881. {
  882. // Could be that we just plain simply get mapped to symbol.
  883. // Avoid it
  884. BOOL fMappedToSymbol = (_bCharSet == SYMBOL_CHARSET &&
  885. lf.lfCharSet != SYMBOL_CHARSET);
  886. BOOL fChangedCharset = (_bCharSet != lf.lfCharSet &&
  887. lf.lfCharSet != DEFAULT_CHARSET);
  888. if(fChangedCharset || fMappedToSymbol)
  889. {
  890. // Here, the system did not preserve the font language or mapped
  891. // our non-symbol font onto a symbol font, which will look awful
  892. // when displayed. Giving us a symbol font when we asked for a
  893. // non-symbol font (default can never be symbol) is very bizarre
  894. // and means that either the font name is not known or the system
  895. // has gone complete nuts. The charset language takes priority
  896. // over the font name. Hence, I would argue that nothing can be
  897. // done to save the situation at this point, and we have to
  898. // delete the font name and retry.
  899. if (fChangedCharset && lf.lfCharSet == THAI_CHARSET && _bCharSet == ANSI_CHARSET)
  900. {
  901. // We have charset substitution entries in Thai platforms that
  902. // will substitute all the core fonts with THAI_CHARSET to
  903. // ANSI_CHARSET. This is because we dont have Thai in such fonts.
  904. // Here we'll internally substitute the core font to Thai default
  905. // font so it matches its underlying THAI_CHARSET request (wchao).
  906. SHORT iDefFont;
  907. BYTE yDefHeight;
  908. BYTE bDefPaf;
  909. W32->GetPreferredFontInfo(874, TRUE, iDefFont, (BYTE&)yDefHeight, bDefPaf);
  910. const WCHAR* szThaiDefault = GetFontName(iDefFont);
  911. if (szThaiDefault)
  912. {
  913. DestroyFont();
  914. wcscpy(lf.lfFaceName, szThaiDefault);
  915. GetFontWithMetrics(&lf, szNewFaceName);
  916. goto GetOutOfHere;
  917. }
  918. }
  919. if(!wcsicmp(lf.lfFaceName, szFontOfChoice))
  920. {
  921. // We've been here already; no font with an appropriate
  922. // charset is on the system. Try getting the ANSI one for
  923. // the original font name. Next time around, we'll null
  924. // out the name as well!!
  925. if (lf.lfCharSet == ANSI_CHARSET)
  926. {
  927. TRACEINFOSZ("Asking for ANSI ARIAL and not getting it?!");
  928. // Those Win95 guys have definitely outbugged me
  929. goto GetOutOfHere;
  930. }
  931. DestroyFont();
  932. wcscpy(lf.lfFaceName, GetFontName(pCF->_iFont));
  933. lf.lfCharSet = ANSI_CHARSET;
  934. }
  935. else
  936. {
  937. DestroyFont();
  938. wcscpy(lf.lfFaceName, szFontOfChoice);
  939. }
  940. GetFontWithMetrics(&lf, szNewFaceName);
  941. goto RetryCreateFont;
  942. }
  943. }
  944. GetOutOfHere:
  945. if (hfontOriginalCharset)
  946. SideAssert(DeleteObject(hfontOriginalCharset));
  947. // If we're really really stuck, just get the system font and hope for the best.
  948. if(!_hfont)
  949. _hfont = W32->GetSystemFont();
  950. Assert(_hfont);
  951. // Cache essential FONTSIGNATURE and GetFontLanguageInfo() information
  952. _dwFontSig = 0;
  953. if(_hfont)
  954. {
  955. CHARSETINFO csi;
  956. HFONT hfontOld = SelectFont(_hdc, _hfont);
  957. UINT uCharSet;
  958. // Try to get FONTSIGNATURE data
  959. uCharSet = GetTextCharsetInfo(_hdc, &(csi.fs), 0);
  960. if(!(csi.fs.fsCsb[0] | csi.fs.fsCsb[1] | csi.fs.fsUsb[0]))
  961. {
  962. // We should only get here if the font is non-TrueType; See
  963. // GetTextCharsetInfo() for details. In this case we use
  964. // TranslateCharsetInfo() to fill in the data for us.
  965. TranslateCharsetInfo((DWORD *)(DWORD_PTR)uCharSet, &csi, TCI_SRCCHARSET);
  966. }
  967. // Cache ANSI code pages supported by this font
  968. _dwFontSig = csi.fs.fsCsb[0];
  969. SelectFont(_hdc, hfontOld);
  970. }
  971. return TRUE;
  972. }
  973. /*
  974. * HFONT CCcs::GetFontWithMetrics (szNewFaceName)
  975. *
  976. * @mfunc
  977. * Get metrics used by the measurer and renderer and the new face name.
  978. *
  979. * @rdesc
  980. * HFONT if successful
  981. */
  982. HFONT CCcs::GetFontWithMetrics (LOGFONT *plf,
  983. WCHAR* szNewFaceName)
  984. {
  985. _hfont = CreateFontIndirect(plf);
  986. if(_hfont)
  987. GetMetrics(szNewFaceName);
  988. return (_hfont);
  989. }
  990. /*
  991. * CCcs::GetOffset(pCF, lZoomNumerator, lZoomDenominator, pyOffset, pyAdjust);
  992. *
  993. * @mfunc
  994. * Return the offset information for
  995. *
  996. * @rdesc
  997. * void
  998. *
  999. * @comm
  1000. * Return the offset value (used in line height calculations)
  1001. * and the amount to raise or lower the text because of superscript
  1002. * or subscript considerations.
  1003. */
  1004. void CCcs::GetOffset(const CCharFormat * const pCF, LONG dypInch,
  1005. LONG *pyOffset, LONG *pyAdjust)
  1006. {
  1007. *pyOffset = 0;
  1008. *pyAdjust = 0;
  1009. if (pCF->_yOffset)
  1010. *pyOffset = MulDiv(pCF->_yOffset, dypInch, LY_PER_INCH);
  1011. if (pCF->_dwEffects & CFE_SUPERSCRIPT)
  1012. *pyAdjust = _yOffsetSuperscript;
  1013. else if (pCF->_dwEffects & CFE_SUBSCRIPT)
  1014. *pyAdjust = _yOffsetSubscript;
  1015. }
  1016. /*
  1017. * BOOL CCcs::GetMetrics()
  1018. *
  1019. * @mfunc
  1020. * Get metrics used by the measurer and renderer.
  1021. *
  1022. * @rdesc
  1023. * TRUE if successful
  1024. *
  1025. * @comm
  1026. * These are in logical coordinates which are dependent
  1027. * on the mapping mode and font selected into the hdc.
  1028. */
  1029. BOOL CCcs::GetMetrics(WCHAR *szNewFaceName)
  1030. {
  1031. TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CCcs::GetMetrics");
  1032. HFONT hfontOld;
  1033. BOOL fRes = TRUE;
  1034. TEXTMETRIC tm;
  1035. if (szNewFaceName)
  1036. *szNewFaceName = 0;
  1037. AssertSz(_hfont, "No font has been created.");
  1038. hfontOld = SelectFont(_hdc, _hfont);
  1039. if(!hfontOld)
  1040. {
  1041. DestroyFont();
  1042. return FALSE;
  1043. }
  1044. if (szNewFaceName)
  1045. GetTextFace(_hdc, LF_FACESIZE, szNewFaceName);
  1046. if(!GetTextMetrics(_hdc, &tm))
  1047. {
  1048. SelectFont(_hdc, hfontOld);
  1049. DestroyFont();
  1050. return FALSE;
  1051. }
  1052. // the metrics, in logical units, dependent on the map mode and font.
  1053. _yHeight = (SHORT) tm.tmHeight;
  1054. _yDescent = (SHORT) tm.tmDescent;
  1055. _xAveCharWidth = (SHORT) tm.tmAveCharWidth;
  1056. _xOverhangAdjust= (SHORT) tm.tmOverhang;
  1057. //FUTURE (keithcu) Get these metrics from the font.
  1058. //FUTURE (keithcu) The height of the line if the font is superscript
  1059. //should be the NORMAL height of the text.
  1060. _yOffsetSuperscript = _yHeight * 2 / 5;
  1061. _yOffsetSubscript = -_yDescent * 3 / 5;
  1062. _xOverhang = 0;
  1063. _xUnderhang = 0;
  1064. if(_fItalic)
  1065. {
  1066. _xOverhang = SHORT((tm.tmAscent + 1) >> 2);
  1067. _xUnderhang = SHORT((tm.tmDescent + 1) >> 2);
  1068. }
  1069. // if fix pitch, the tm bit is clear
  1070. _fFixPitchFont = !(TMPF_FIXED_PITCH & tm.tmPitchAndFamily);
  1071. _bCharSet = tm.tmCharSet;
  1072. _fFECharSet = IsFECharSet(_bCharSet);
  1073. // Use convert-mode proposed by CF, for which we are creating the font and
  1074. // then tweak as necessary below.
  1075. _bConvertMode = _bCMDefault;
  1076. // If SYMBOL_CHARSET is used, use the A APIs with the low bytes of the
  1077. // characters in the run
  1078. if(_bCharSet == SYMBOL_CHARSET)
  1079. _bConvertMode = CVT_LOWBYTE;
  1080. else if (_bConvertMode == CVT_NONE)
  1081. _bConvertMode = W32->DetermineConvertMode(_hdc, tm.tmCharSet );
  1082. W32->CalcUnderlineInfo(_hdc, this, &tm);
  1083. SelectFont(_hdc, hfontOld);
  1084. return fRes;
  1085. }
  1086. /*
  1087. * CCcs::DestroyFont()
  1088. *
  1089. * @mfunc
  1090. * Destroy font handle for this CCcs
  1091. */
  1092. void CCcs::DestroyFont()
  1093. {
  1094. TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CCcs::DestroyFont");
  1095. // clear out any old font
  1096. if(_hfont)
  1097. {
  1098. SideAssert(DeleteObject(_hfont));
  1099. _hfont = 0;
  1100. }
  1101. }
  1102. /*
  1103. * CCcs::Compare (pCF, lfHeight)
  1104. *
  1105. * @mfunc
  1106. * Compares this font cache with the font properties of a
  1107. * given CHARFORMAT
  1108. * @devnote The pCF size here is in logical units
  1109. *
  1110. * @rdesc
  1111. * FALSE iff did not match exactly.
  1112. */
  1113. BOOL CCcs::Compare (
  1114. const CCharFormat * const pCF, //@parm Description of desired font
  1115. HDC hdc)
  1116. {
  1117. TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CCcs::Compare");
  1118. BOOL result =
  1119. _iFont == pCF->_iFont &&
  1120. _yHeightRequest == pCF->_yHeight &&
  1121. (_bCharSetRequest == pCF->_bCharSet || _bCharSet == pCF->_bCharSet) &&
  1122. _weight == pCF->_wWeight &&
  1123. _fForceTrueType == ((pCF->_dwEffects & CFE_TRUETYPEONLY) ? TRUE : FALSE) &&
  1124. _fItalic == ((pCF->_dwEffects & CFE_ITALIC) != 0) &&
  1125. _hdc == hdc &&
  1126. _bPitchAndFamily == pCF->_bPitchAndFamily &&
  1127. (!(pCF->_dwEffects & CFE_RUNISDBCS) || _bConvertMode == CVT_LOWBYTE);
  1128. return result;
  1129. }
  1130. // ========================= WidthCache by jonmat =========================
  1131. /*
  1132. * CWidthCache::CheckWidth(ch, dxp)
  1133. *
  1134. * @mfunc
  1135. * check to see if we have a width for a WCHAR character.
  1136. *
  1137. * @comm
  1138. * Used prior to calling FillWidth(). Since FillWidth
  1139. * may require selecting the map mode and font in the HDC,
  1140. * checking here first saves time.
  1141. *
  1142. * @comm
  1143. * Statistics are maintained to determine when to
  1144. * expand the cache. The determination is made after a constant
  1145. * number of calls in order to make calculations faster.
  1146. *
  1147. * @rdesc
  1148. * returns TRUE if we have the width of the given WCHAR.
  1149. */
  1150. BOOL CWidthCache::CheckWidth (
  1151. const WCHAR ch, //@parm char, can be Unicode, to check width for.
  1152. LONG &dxp) //@parm Width of character
  1153. {
  1154. TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CWidthCache::CheckWidth");
  1155. BOOL fExist;
  1156. //30,000 FE characters all have the same width
  1157. if (FLookasideCharacter(ch))
  1158. {
  1159. FetchLookasideWidth(ch, dxp);
  1160. return dxp != 0;
  1161. }
  1162. const CacheEntry * pWidthData = GetEntry ( ch );
  1163. fExist = (ch == pWidthData->ch // Have we fetched the width?
  1164. && pWidthData->width); // only because we may have ch == 0.
  1165. dxp = fExist ? pWidthData->width : 0;
  1166. if(!_fMaxPerformance) // if we have not grown to the max...
  1167. {
  1168. _accesses++;
  1169. if(!fExist) // Only interesting on collision.
  1170. {
  1171. if(0 == pWidthData->width) // Test width not ch, 0 is valid ch.
  1172. {
  1173. _cacheUsed++; // Used another entry.
  1174. AssertSz( _cacheUsed <= _cacheSize+1, "huh?");
  1175. }
  1176. else
  1177. _collisions++; // We had a collision.
  1178. if(_accesses >= PERFCHECKEPOCH)
  1179. CheckPerformance(); // After some history, tune cache.
  1180. }
  1181. }
  1182. #ifdef DEBUG // Continue to monitor performance
  1183. else
  1184. {
  1185. _accesses++;
  1186. if(!fExist) // Only interesting on collision.
  1187. {
  1188. if(0 == pWidthData->width) // Test width not ch, 0 is valid ch.
  1189. {
  1190. _cacheUsed++; // Used another entry.
  1191. AssertSz( _cacheUsed <= _cacheSize+1, "huh?");
  1192. }
  1193. else
  1194. _collisions++; // We had a collision.
  1195. }
  1196. if(_accesses > PERFCHECKEPOCH)
  1197. {
  1198. _accesses = 0;
  1199. _collisions = 0;
  1200. }
  1201. }
  1202. #endif
  1203. return fExist;
  1204. }
  1205. /*
  1206. * CWidthCache::CheckPerformance()
  1207. *
  1208. * @mfunc
  1209. * check performance and increase cache size if deemed necessary.
  1210. *
  1211. * @devnote
  1212. * To calculate 25% collision rate, we make use of the fact that
  1213. * we are only called once every 64 accesses. The inequality is
  1214. * 100 * collisions / accesses >= 25. By converting from 100ths to
  1215. * 8ths, the ineqaulity becomes (collisions << 3) / accesses >= 2.
  1216. * Substituting 64 for accesses, this becomes (collisions >> 3) >= 2.
  1217. */
  1218. void CWidthCache::CheckPerformance()
  1219. {
  1220. TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CWidthCache::CheckPerformance");
  1221. if(_fMaxPerformance) // Exit if already grown to our max.
  1222. return;
  1223. // Grow the cache when cacheSize > 0 && 75% utilized or approx 25%
  1224. // collision rate
  1225. if (_cacheSize > DEFAULTCACHESIZE &&
  1226. (_cacheSize >> 1) + (_cacheSize >> 2) < _cacheUsed ||
  1227. (_collisions >> COLLISION_SHIFT) >= 2)
  1228. {
  1229. GrowCache( &_pWidthCache, &_cacheSize, &_cacheUsed );
  1230. }
  1231. _collisions = 0; // This prevents wraps but makes
  1232. _accesses = 0; // calc a local rate, not global.
  1233. if(_cacheSize >= maxCacheSize)// Note if we've max'ed out
  1234. _fMaxPerformance = TRUE;
  1235. AssertSz( _cacheSize <= maxCacheSize, "max must be 2^n-1");
  1236. AssertSz( _cacheUsed <= _cacheSize+1, "huh?");
  1237. }
  1238. /*
  1239. * CWidthCache::GrowCache(ppWidthCache, pCacheSize, pCacheUsed)
  1240. *
  1241. * @mfunc
  1242. * Exponentially expand the size of the cache.
  1243. *
  1244. * @comm
  1245. * The cache size must be of the form 2^n as we use a
  1246. * logical & to get the hash MOD by storing 2^n-1 as
  1247. * the size and using this as the modulo.
  1248. *
  1249. * @rdesc
  1250. * Returns TRUE if we were able to allocate the new cache.
  1251. * All in params are also out params.
  1252. *
  1253. */
  1254. BOOL CWidthCache::GrowCache(
  1255. CacheEntry **ppWidthCache, //@parm cache
  1256. INT * pCacheSize, //@parm cache's respective size.
  1257. INT * pCacheUsed) //@parm cache's respective utilization.
  1258. {
  1259. TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CWidthCache::GrowCache");
  1260. CacheEntry *pNewWidthCache, *pOldWidthCache, *pWidthData;
  1261. INT j, newCacheSize, newCacheUsed;
  1262. WCHAR ch;
  1263. j = *pCacheSize; // Allocate cache of 2^n.
  1264. newCacheSize = max ( INITIALCACHESIZE, (j << 1) + 1);
  1265. pNewWidthCache = (CacheEntry *)
  1266. PvAlloc( sizeof(CacheEntry) * (newCacheSize + 1 ), GMEM_ZEROINIT);
  1267. if(pNewWidthCache)
  1268. {
  1269. newCacheUsed = 0;
  1270. *pCacheSize = newCacheSize; // Update out params.
  1271. pOldWidthCache = *ppWidthCache;
  1272. *ppWidthCache = pNewWidthCache;
  1273. for (; j >= 0; j--) // Move old cache info to new.
  1274. {
  1275. ch = pOldWidthCache[j].ch;
  1276. if ( ch )
  1277. {
  1278. pWidthData = &pNewWidthCache [ch & newCacheSize];
  1279. if ( 0 == pWidthData->ch )
  1280. newCacheUsed++; // Used another entry.
  1281. pWidthData->ch = ch;
  1282. pWidthData->width = pOldWidthCache[j].width;
  1283. }
  1284. }
  1285. *pCacheUsed = newCacheUsed; // Update out param.
  1286. // Free old cache.
  1287. if (pOldWidthCache < &_defaultWidthCache[0] ||
  1288. pOldWidthCache >= &_defaultWidthCache[DEFAULTCACHESIZE+1])
  1289. {
  1290. FreePv(pOldWidthCache);
  1291. }
  1292. }
  1293. return NULL != pNewWidthCache;
  1294. }
  1295. /*
  1296. * CWidthCache::FillWidth(hdc, ch, xOverhang, dxp)
  1297. *
  1298. * @mfunc
  1299. * Call GetCharWidth() to obtain the width of the given char.
  1300. *
  1301. * @comm
  1302. * The HDC must be setup with the mapping mode and proper font
  1303. * selected *before* calling this routine.
  1304. *
  1305. * @rdesc
  1306. * Returns TRUE if we were able to obtain the widths.
  1307. */
  1308. BOOL CWidthCache::FillWidth(
  1309. HDC hdc, //@parm Current HDC we want font info for.
  1310. const WCHAR ch, //@parm Char to obtain width for.
  1311. const SHORT xOverhang, //@parm Equivalent to GetTextMetrics() tmOverhang.
  1312. LONG & dxp, //@parm Width of character
  1313. UINT uiCodePage, //@parm code page for text
  1314. INT iDefWidth) //@parm Default width to use if font calc's zero
  1315. //width. (Handles Win95 problem).
  1316. {
  1317. TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CWidthCache::FillWidth");
  1318. if (FLookasideCharacter(ch))
  1319. {
  1320. SHORT *pdxp = IN_RANGE(0xAC00, ch, 0xD79F) ? &_dxpHangul : &_dxpHan;
  1321. W32->REGetCharWidth(hdc, ch, pdxp, uiCodePage, xOverhang, iDefWidth);
  1322. dxp = *pdxp;
  1323. return TRUE;
  1324. }
  1325. CacheEntry * pWidthData = GetEntry (ch);
  1326. W32->REGetCharWidth(hdc, ch, &pWidthData->width, uiCodePage, xOverhang, iDefWidth);
  1327. pWidthData->ch = ch;
  1328. dxp = pWidthData->width;
  1329. return TRUE;
  1330. }
  1331. /*
  1332. * CWidthCache::Free()
  1333. *
  1334. * @mfunc
  1335. * Free any dynamic memory allocated by the width cache and prepare
  1336. * it to be recycled.
  1337. */
  1338. void CWidthCache::Free()
  1339. {
  1340. TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CWidthCache::Free");
  1341. _fMaxPerformance = FALSE;
  1342. _dxpHangul = _dxpHan = 0;
  1343. _cacheSize = DEFAULTCACHESIZE;
  1344. _cacheUsed = 0;
  1345. _collisions = 0;
  1346. _accesses = 0;
  1347. if(_pWidthCache != &_defaultWidthCache[0])
  1348. {
  1349. FreePv(_pWidthCache);
  1350. _pWidthCache = &_defaultWidthCache[0];
  1351. }
  1352. ZeroMemory(_pWidthCache, sizeof(CacheEntry)*(DEFAULTCACHESIZE + 1));
  1353. }
  1354. /*
  1355. * CWidthCache::CWidthCache()
  1356. *
  1357. * @mfunc
  1358. * Point the caches to the defaults.
  1359. */
  1360. CWidthCache::CWidthCache()
  1361. {
  1362. TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CWidthCache::CWidthCache");
  1363. _pWidthCache = &_defaultWidthCache[0];
  1364. }
  1365. /*
  1366. * CWidthCache::~CWidthCache()
  1367. *
  1368. * @mfunc
  1369. * Free any allocated caches.
  1370. */
  1371. CWidthCache::~CWidthCache()
  1372. {
  1373. TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CWidthCache::~CWidthCache");
  1374. if (_pWidthCache != &_defaultWidthCache[0])
  1375. FreePv(_pWidthCache);
  1376. }