xref: /reactos/win32ss/gdi/ntgdi/freetype.c (revision fe11f7a2)
1 /*
2  * PROJECT:         ReactOS win32 kernel mode subsystem
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * PURPOSE:         FreeType font engine interface
5  * PROGRAMMERS:     Copyright 2001 Huw D M Davies for CodeWeavers.
6  *                  Copyright 2006 Dmitry Timoshkov for CodeWeavers.
7  *                  Copyright 2016-2024 Katayama Hirofumi MZ.
8  */
9 
10 /** Includes ******************************************************************/
11 
12 #include <win32k.h>
13 
14 #include FT_GLYPH_H
15 #include FT_TYPE1_TABLES_H
16 #include FT_TRUETYPE_TABLES_H
17 #include FT_TRUETYPE_TAGS_H
18 #include FT_TRIGONOMETRY_H
19 #include FT_BITMAP_H
20 #include FT_OUTLINE_H
21 #include FT_WINFONTS_H
22 #include FT_SFNT_NAMES_H
23 #include FT_SYNTHESIS_H
24 #include FT_TRUETYPE_IDS_H
25 
26 #ifndef FT_INTERNAL_INTERNAL_H
27     #define  FT_INTERNAL_INTERNAL_H  <freetype/internal/internal.h>
28     #include FT_INTERNAL_INTERNAL_H
29 #endif
30 #include FT_INTERNAL_TRUETYPE_TYPES_H
31 
32 #include <gdi/eng/floatobj.h>
33 #include "font.h"
34 
35 #define NDEBUG
36 #include <debug.h>
37 // DBG_DEFAULT_CHANNEL(GdiFont); // TODO: Re-enable when using TRACE/ERR...
38 
39 typedef struct _FONTLINK
40 {
41     LIST_ENTRY ListEntry; //< Entry in the FONTLINK_CHAIN::FontLinkList
42     BOOL bIgnore;
43     LOGFONTW LogFont;
44     PSHARED_FACE SharedFace;
45 } FONTLINK, *PFONTLINK;
46 
47 typedef struct _FONTLINK_CHAIN
48 {
49     LIST_ENTRY FontLinkList; //< List of FONTLINK's
50     LOGFONTW LogFont;
51     PZZWSTR pszzFontLink;
52     PTEXTOBJ pBaseTextObj;
53     FT_Face pDefFace;
54 } FONTLINK_CHAIN, *PFONTLINK_CHAIN;
55 
56 typedef struct _FONTLINK_CACHE
57 {
58     LIST_ENTRY ListEntry;
59     LOGFONTW LogFont;
60     FONTLINK_CHAIN Chain;
61 } FONTLINK_CACHE, *PFONTLINK_CACHE;
62 
63 #define FONTLINK_DEFAULT_CHAR 0x30FB // U+30FB (KATAKANA MIDDLE DOT)
64 
65 static DWORD s_chFontLinkDefaultChar = FONTLINK_DEFAULT_CHAR;
66 static WCHAR s_szDefFontLinkFileName[MAX_PATH] = L"";
67 static WCHAR s_szDefFontLinkFontName[MAX_PATH] = L"";
68 static BOOL s_fFontLinkUseAnsi = FALSE;
69 static BOOL s_fFontLinkUseOem = FALSE;
70 static BOOL s_fFontLinkUseSymbol = FALSE;
71 
72 #define MAX_FONTLINK_CACHE 128
73 static RTL_STATIC_LIST_HEAD(g_FontLinkCache); // The list of FONTLINK_CACHE
74 static LONG g_nFontLinkCacheCount = 0;
75 
76 static SIZE_T
SZZ_GetSize(_In_ PCZZWSTR pszz)77 SZZ_GetSize(_In_ PCZZWSTR pszz)
78 {
79     SIZE_T ret = 0, cch;
80     const WCHAR *pch = pszz;
81     while (*pch)
82     {
83         cch = wcslen(pch) + 1;
84         ret += cch;
85         pch += cch;
86     }
87     ++ret;
88     return ret * sizeof(WCHAR);
89 }
90 
91 static inline NTSTATUS
FontLink_LoadSettings(VOID)92 FontLink_LoadSettings(VOID)
93 {
94     NTSTATUS Status;
95     HKEY hKey;
96     DWORD cbData, dwValue;
97 
98     // Set the default values
99     s_chFontLinkDefaultChar = FONTLINK_DEFAULT_CHAR;
100 
101     // Open the registry key
102     Status = RegOpenKey(
103         L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\FontLink",
104         &hKey);
105     if (!NT_SUCCESS(Status))
106         return Status;
107 
108     cbData = sizeof(dwValue);
109     Status = RegQueryValue(hKey, L"FontLinkDefaultChar", REG_DWORD, &dwValue, &cbData);
110     if (NT_SUCCESS(Status) && cbData == sizeof(dwValue))
111         s_chFontLinkDefaultChar = dwValue;
112 
113     ZwClose(hKey); // Close the registry key
114     return STATUS_SUCCESS;
115 }
116 
117 static inline NTSTATUS
FontLink_LoadDefaultFonts(VOID)118 FontLink_LoadDefaultFonts(VOID)
119 {
120     NTSTATUS Status;
121     HKEY hKey;
122     DWORD cbData;
123     WCHAR szValue[MAX_PATH];
124 
125     // Set the default values
126     s_szDefFontLinkFileName[0] = s_szDefFontLinkFontName[0] = UNICODE_NULL;
127 
128     // Open the registry key
129     Status = RegOpenKey(
130         L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\FontAssoc\\Associated DefaultFonts",
131         &hKey);
132     if (!NT_SUCCESS(Status))
133         return Status;
134 
135     cbData = sizeof(szValue);
136     Status = RegQueryValue(hKey, L"AssocSystemFont", REG_SZ, szValue, &cbData);
137     if (NT_SUCCESS(Status))
138     {
139         szValue[_countof(szValue) - 1] = UNICODE_NULL; // Avoid buffer overrun
140         RtlStringCchCopyW(s_szDefFontLinkFileName, _countof(s_szDefFontLinkFileName), szValue);
141     }
142 
143     cbData = sizeof(szValue);
144     Status = RegQueryValue(hKey, L"FontPackage", REG_SZ, szValue, &cbData);
145     if (NT_SUCCESS(Status))
146     {
147         szValue[_countof(szValue) - 1] = UNICODE_NULL; // Avoid buffer overrun
148         RtlStringCchCopyW(s_szDefFontLinkFontName, _countof(s_szDefFontLinkFontName), szValue);
149     }
150 
151     ZwClose(hKey); // Close the registry key
152     return STATUS_SUCCESS;
153 }
154 
155 static inline NTSTATUS
FontLink_LoadDefaultCharset(VOID)156 FontLink_LoadDefaultCharset(VOID)
157 {
158     NTSTATUS Status;
159     HKEY hKey;
160     DWORD cbData;
161     WCHAR szValue[8];
162 
163     // Set the default values
164     s_fFontLinkUseAnsi = s_fFontLinkUseOem = s_fFontLinkUseSymbol = FALSE;
165 
166     // Open the registry key
167     Status = RegOpenKey(
168         L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\FontAssoc\\Associated Charset",
169         &hKey);
170     if (!NT_SUCCESS(Status))
171         return Status;
172 
173     cbData = sizeof(szValue);
174     Status = RegQueryValue(hKey, L"ANSI(00)", REG_SZ, szValue, &cbData);
175     if (NT_SUCCESS(Status))
176     {
177         szValue[_countof(szValue) - 1] = UNICODE_NULL; // Avoid buffer overrun
178         s_fFontLinkUseAnsi = !_wcsicmp(szValue, L"YES");
179     }
180 
181     cbData = sizeof(szValue);
182     Status = RegQueryValue(hKey, L"OEM(FF)", REG_SZ, szValue, &cbData);
183     if (NT_SUCCESS(Status))
184     {
185         szValue[_countof(szValue) - 1] = UNICODE_NULL; // Avoid buffer overrun
186         s_fFontLinkUseOem = !_wcsicmp(szValue, L"YES");
187     }
188 
189     cbData = sizeof(szValue);
190     Status = RegQueryValue(hKey, L"SYMBOL(02)", REG_SZ, szValue, &cbData);
191     if (NT_SUCCESS(Status))
192     {
193         szValue[_countof(szValue) - 1] = UNICODE_NULL; // Avoid buffer overrun
194         s_fFontLinkUseSymbol = !_wcsicmp(szValue, L"YES");
195     }
196 
197     ZwClose(hKey); // Close the registry key
198     return STATUS_SUCCESS;
199 }
200 
201 static inline VOID
FontLink_Destroy(_Inout_ PFONTLINK pLink)202 FontLink_Destroy(_Inout_ PFONTLINK pLink)
203 {
204     ASSERT(pLink);
205     ExFreePoolWithTag(pLink, TAG_FONT);
206 }
207 
208 static inline BOOL
FontLink_Chain_IsPopulated(const FONTLINK_CHAIN * pChain)209 FontLink_Chain_IsPopulated(const FONTLINK_CHAIN *pChain)
210 {
211     return pChain->LogFont.lfFaceName[0];
212 }
213 
214 static VOID
FontLink_Chain_Free(_Inout_ PFONTLINK_CHAIN pChain)215 FontLink_Chain_Free(
216     _Inout_ PFONTLINK_CHAIN pChain)
217 {
218     PLIST_ENTRY Entry;
219     PFONTLINK pLink;
220 
221     if (!FontLink_Chain_IsPopulated(pChain)) // The chain is not populated yet
222         return;
223 
224     if (pChain->pszzFontLink)
225         ExFreePoolWithTag(pChain->pszzFontLink, TAG_FONT);
226 
227     while (!IsListEmpty(&pChain->FontLinkList))
228     {
229         Entry = RemoveHeadList(&pChain->FontLinkList);
230         pLink = CONTAINING_RECORD(Entry, FONTLINK, ListEntry);
231         FontLink_Destroy(pLink);
232     }
233 }
234 
235 static inline VOID
FontLink_AddCache(_In_ PFONTLINK_CACHE pCache)236 FontLink_AddCache(
237     _In_ PFONTLINK_CACHE pCache)
238 {
239     PLIST_ENTRY Entry;
240 
241     /* Add the new cache entry to the top of the cache list */
242     ++g_nFontLinkCacheCount;
243     InsertHeadList(&g_FontLinkCache, &pCache->ListEntry);
244 
245     /* If there are too many cache entries in the list, remove the oldest one at the bottom */
246     if (g_nFontLinkCacheCount > MAX_FONTLINK_CACHE)
247     {
248         ASSERT(!IsListEmpty(&g_FontLinkCache));
249         Entry = RemoveTailList(&g_FontLinkCache);
250         --g_nFontLinkCacheCount;
251         pCache = CONTAINING_RECORD(Entry, FONTLINK_CACHE, ListEntry);
252         FontLink_Chain_Free(&pCache->Chain);
253         ExFreePoolWithTag(pCache, TAG_FONT);
254     }
255 }
256 
257 static inline VOID
IntRebaseList(_Inout_ PLIST_ENTRY pNewHead,_Inout_ PLIST_ENTRY pOldHead)258 IntRebaseList(
259     _Inout_ PLIST_ENTRY pNewHead,
260     _Inout_ PLIST_ENTRY pOldHead)
261 {
262     PLIST_ENTRY Entry;
263 
264     ASSERT(pNewHead != pOldHead);
265 
266     InitializeListHead(pNewHead);
267     while (!IsListEmpty(pOldHead))
268     {
269         Entry = RemoveTailList(pOldHead);
270         InsertHeadList(pNewHead, Entry);
271     }
272 }
273 
274 /// Add the chain to the cache (g_FontLinkCache) if the chain had been populated.
275 /// @param pChain The chain.
276 static inline VOID
FontLink_Chain_Finish(_Inout_ PFONTLINK_CHAIN pChain)277 FontLink_Chain_Finish(
278     _Inout_ PFONTLINK_CHAIN pChain)
279 {
280     PFONTLINK_CACHE pCache;
281 
282     if (!FontLink_Chain_IsPopulated(pChain))
283         return; // The chain is not populated yet
284 
285     pCache = ExAllocatePoolWithTag(PagedPool, sizeof(FONTLINK_CACHE), TAG_FONT);
286     if (!pCache)
287         return; // Out of memory
288 
289     pCache->LogFont = pChain->LogFont;
290     pCache->Chain = *pChain;
291     IntRebaseList(&pCache->Chain.FontLinkList, &pChain->FontLinkList);
292 
293     FontLink_AddCache(pCache);
294 }
295 
296 static inline PFONTLINK_CACHE
FontLink_FindCache(_In_ const LOGFONTW * pLogFont)297 FontLink_FindCache(
298     _In_ const LOGFONTW* pLogFont)
299 {
300     PLIST_ENTRY Entry;
301     PFONTLINK_CACHE pLinkCache;
302     for (Entry = g_FontLinkCache.Flink; Entry != &g_FontLinkCache; Entry = Entry->Flink)
303     {
304         pLinkCache = CONTAINING_RECORD(Entry, FONTLINK_CACHE, ListEntry);
305         if (RtlEqualMemory(&pLinkCache->LogFont, pLogFont, sizeof(LOGFONTW)))
306             return pLinkCache;
307     }
308     return NULL;
309 }
310 
311 static inline VOID
FontLink_CleanupCache(VOID)312 FontLink_CleanupCache(VOID)
313 {
314     PLIST_ENTRY Entry;
315     PFONTLINK_CACHE pLinkCache;
316 
317     while (!IsListEmpty(&g_FontLinkCache))
318     {
319         Entry = RemoveHeadList(&g_FontLinkCache);
320         pLinkCache = CONTAINING_RECORD(Entry, FONTLINK_CACHE, ListEntry);
321         FontLink_Chain_Free(&pLinkCache->Chain);
322         ExFreePoolWithTag(pLinkCache, TAG_FONT);
323     }
324 
325     g_nFontLinkCacheCount = 0;
326 }
327 
328 /* The ranges of the surrogate pairs */
329 #define HIGH_SURROGATE_MIN 0xD800U
330 #define HIGH_SURROGATE_MAX 0xDBFFU
331 #define LOW_SURROGATE_MIN  0xDC00U
332 #define LOW_SURROGATE_MAX  0xDFFFU
333 
334 #define IS_HIGH_SURROGATE(ch0) (HIGH_SURROGATE_MIN <= (ch0) && (ch0) <= HIGH_SURROGATE_MAX)
335 #define IS_LOW_SURROGATE(ch1)  (LOW_SURROGATE_MIN  <= (ch1) && (ch1) <=  LOW_SURROGATE_MAX)
336 
337 static inline DWORD
Utf32FromSurrogatePair(DWORD ch0,DWORD ch1)338 Utf32FromSurrogatePair(DWORD ch0, DWORD ch1)
339 {
340     return ((ch0 - HIGH_SURROGATE_MIN) << 10) + (ch1 - LOW_SURROGATE_MIN) + 0x10000;
341 }
342 
343 /* TPMF_FIXED_PITCH is confusing; brain-dead api */
344 #ifndef _TMPF_VARIABLE_PITCH
345     #define _TMPF_VARIABLE_PITCH    TMPF_FIXED_PITCH
346 #endif
347 
348 /* Is bold emulation necessary? */
349 #define EMUBOLD_NEEDED(original, request) \
350     (((request) != FW_DONTCARE) && ((request) - (original) >= FW_BOLD - FW_MEDIUM))
351 
352 extern const MATRIX gmxWorldToDeviceDefault;
353 extern const MATRIX gmxWorldToPageDefault;
354 static const FT_Matrix identityMat = {(1 << 16), 0, 0, (1 << 16)};
355 static POINTL PointZero = { 0, 0 };
356 
357 /* HACK!! Fix XFORMOBJ then use 1:16 / 16:1 */
358 #define gmxWorldToDeviceDefault gmxWorldToPageDefault
359 
360 FT_Library  g_FreeTypeLibrary;
361 
362 /* registry */
363 static UNICODE_STRING g_FontRegPath =
364     RTL_CONSTANT_STRING(L"\\REGISTRY\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts");
365 
366 
367 /* The FreeType library is not thread safe, so we have
368    to serialize access to it */
369 static PFAST_MUTEX      g_FreeTypeLock;
370 
371 static RTL_STATIC_LIST_HEAD(g_FontListHead);
372 static BOOL             g_RenderingEnabled = TRUE;
373 
374 #define ASSERT_FREETYPE_LOCK_HELD() \
375     ASSERT(g_FreeTypeLock->Owner == KeGetCurrentThread())
376 
377 #define ASSERT_FREETYPE_LOCK_NOT_HELD() \
378     ASSERT(g_FreeTypeLock->Owner != KeGetCurrentThread())
379 
380 #define IntLockFreeType() \
381 do { \
382     ASSERT_FREETYPE_LOCK_NOT_HELD(); \
383     ExEnterCriticalRegionAndAcquireFastMutexUnsafe(g_FreeTypeLock); \
384 } while (0)
385 
386 #define IntUnLockFreeType() \
387 do { \
388     ASSERT_FREETYPE_LOCK_HELD(); \
389     ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(g_FreeTypeLock); \
390 } while(0)
391 
392 #define MAX_FONT_CACHE 256
393 
394 static RTL_STATIC_LIST_HEAD(g_FontCacheListHead);
395 static UINT g_FontCacheNumEntries;
396 
397 static PWCHAR g_ElfScripts[32] =   /* These are in the order of the fsCsb[0] bits */
398 {
399     L"Western", /* 00 */
400     L"Central_European",
401     L"Cyrillic",
402     L"Greek",
403     L"Turkish",
404     L"Hebrew",
405     L"Arabic",
406     L"Baltic",
407     L"Vietnamese", /* 08 */
408     NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 15 */
409     L"Thai",
410     L"Japanese",
411     L"CHINESE_GB2312",
412     L"Hangul",
413     L"CHINESE_BIG5",
414     L"Hangul(Johab)",
415     NULL, NULL, /* 23 */
416     NULL, NULL, NULL, NULL, NULL, NULL, NULL,
417     L"Symbol" /* 31 */
418 };
419 
420 /*
421  *  For TranslateCharsetInfo
422  */
423 #define CP_SYMBOL   42
424 #define MAXTCIINDEX 32
425 static const CHARSETINFO g_FontTci[MAXTCIINDEX] =
426 {
427     /* ANSI */
428     { ANSI_CHARSET, 1252, {{0,0,0,0},{FS_LATIN1,0}} },
429     { EASTEUROPE_CHARSET, 1250, {{0,0,0,0},{FS_LATIN2,0}} },
430     { RUSSIAN_CHARSET, 1251, {{0,0,0,0},{FS_CYRILLIC,0}} },
431     { GREEK_CHARSET, 1253, {{0,0,0,0},{FS_GREEK,0}} },
432     { TURKISH_CHARSET, 1254, {{0,0,0,0},{FS_TURKISH,0}} },
433     { HEBREW_CHARSET, 1255, {{0,0,0,0},{FS_HEBREW,0}} },
434     { ARABIC_CHARSET, 1256, {{0,0,0,0},{FS_ARABIC,0}} },
435     { BALTIC_CHARSET, 1257, {{0,0,0,0},{FS_BALTIC,0}} },
436     { VIETNAMESE_CHARSET, 1258, {{0,0,0,0},{FS_VIETNAMESE,0}} },
437     /* reserved by ANSI */
438     { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
439     { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
440     { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
441     { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
442     { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
443     { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
444     { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
445     /* ANSI and OEM */
446     { THAI_CHARSET, 874, {{0,0,0,0},{FS_THAI,0}} },
447     { SHIFTJIS_CHARSET, 932, {{0,0,0,0},{FS_JISJAPAN,0}} },
448     { GB2312_CHARSET, 936, {{0,0,0,0},{FS_CHINESESIMP,0}} },
449     { HANGEUL_CHARSET, 949, {{0,0,0,0},{FS_WANSUNG,0}} },
450     { CHINESEBIG5_CHARSET, 950, {{0,0,0,0},{FS_CHINESETRAD,0}} },
451     { JOHAB_CHARSET, 1361, {{0,0,0,0},{FS_JOHAB,0}} },
452     /* Reserved for alternate ANSI and OEM */
453     { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
454     { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
455     { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
456     { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
457     { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
458     { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
459     { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
460     { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
461     /* Reserved for system */
462     { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
463     { SYMBOL_CHARSET, CP_SYMBOL, {{0,0,0,0},{FS_SYMBOL,0}} }
464 };
465 
466 #ifndef CP_OEMCP
467     #define CP_OEMCP  1
468     #define CP_MACCP  2
469 #endif
470 
471 /* Get charset from specified codepage.
472    g_FontTci is used also in TranslateCharsetInfo. */
IntCharSetFromCodePage(UINT uCodePage)473 BYTE FASTCALL IntCharSetFromCodePage(UINT uCodePage)
474 {
475     UINT i;
476 
477     if (uCodePage == CP_OEMCP)
478         return OEM_CHARSET;
479 
480     if (uCodePage == CP_MACCP)
481         return MAC_CHARSET;
482 
483     for (i = 0; i < MAXTCIINDEX; ++i)
484     {
485         if (g_FontTci[i].ciACP == 0)
486             continue;
487 
488         if (g_FontTci[i].ciACP == uCodePage)
489             return g_FontTci[i].ciCharset;
490     }
491 
492     return DEFAULT_CHARSET;
493 }
494 
495 static __inline VOID
496 FindBestFontFromList(FONTOBJ **FontObj, ULONG *MatchPenalty,
497                      const LOGFONTW *LogFont,
498                      const PLIST_ENTRY Head);
499 
500 static BOOL
501 MatchFontName(PSHARED_FACE SharedFace, PUNICODE_STRING Name1, FT_UShort NameID, FT_UShort LangID);
502 
503 static BOOL
FontLink_PrepareFontInfo(_Inout_ PFONTLINK pFontLink)504 FontLink_PrepareFontInfo(
505     _Inout_ PFONTLINK pFontLink)
506 {
507     FONTOBJ *pFontObj;
508     ULONG MatchPenalty;
509     UNICODE_STRING FaceName;
510     PPROCESSINFO Win32Process;
511     PFONTGDI pFontGDI;
512 
513     ASSERT_FREETYPE_LOCK_HELD();
514 
515     if (pFontLink->bIgnore)
516         return FALSE;
517 
518     if (pFontLink->SharedFace)
519         return TRUE;
520 
521     MatchPenalty = MAXULONG;
522     pFontObj = NULL;
523 
524     // Search private fonts
525     Win32Process = PsGetCurrentProcessWin32Process();
526     FindBestFontFromList(&pFontObj, &MatchPenalty, &pFontLink->LogFont,
527                          &Win32Process->PrivateFontListHead);
528 
529     // Search system fonts
530     FindBestFontFromList(&pFontObj, &MatchPenalty, &pFontLink->LogFont,
531                          &g_FontListHead);
532 
533     if (!pFontObj) // Not found?
534     {
535         pFontLink->bIgnore = TRUE;
536         return FALSE;
537     }
538 
539     pFontGDI = ObjToGDI(pFontObj, FONT);
540     pFontLink->SharedFace = pFontGDI->SharedFace;
541 
542     // FontLink uses family name
543     RtlInitUnicodeString(&FaceName, pFontLink->LogFont.lfFaceName);
544     if (!MatchFontName(pFontLink->SharedFace, &FaceName, TT_NAME_ID_FONT_FAMILY, LANG_ENGLISH) &&
545         !MatchFontName(pFontLink->SharedFace, &FaceName, TT_NAME_ID_FONT_FAMILY, gusLanguageID))
546     {
547         pFontLink->bIgnore = TRUE;
548         return FALSE;
549     }
550 
551     return TRUE;
552 }
553 
554 /* list head */
555 static RTL_STATIC_LIST_HEAD(g_FontSubstListHead);
556 
557 static void
SharedMem_AddRef(PSHARED_MEM Ptr)558 SharedMem_AddRef(PSHARED_MEM Ptr)
559 {
560     ASSERT_FREETYPE_LOCK_HELD();
561 
562     ++Ptr->RefCount;
563 }
564 
565 static void
SharedFaceCache_Init(PSHARED_FACE_CACHE Cache)566 SharedFaceCache_Init(PSHARED_FACE_CACHE Cache)
567 {
568     Cache->OutlineRequiredSize = 0;
569     RtlInitUnicodeString(&Cache->FontFamily, NULL);
570     RtlInitUnicodeString(&Cache->FullName, NULL);
571 }
572 
573 static PSHARED_FACE
SharedFace_Create(FT_Face Face,PSHARED_MEM Memory)574 SharedFace_Create(FT_Face Face, PSHARED_MEM Memory)
575 {
576     PSHARED_FACE Ptr;
577     Ptr = ExAllocatePoolWithTag(PagedPool, sizeof(SHARED_FACE), TAG_FONT);
578     if (Ptr)
579     {
580         Ptr->Face = Face;
581         Ptr->RefCount = 1;
582         Ptr->Memory = Memory;
583         SharedFaceCache_Init(&Ptr->EnglishUS);
584         SharedFaceCache_Init(&Ptr->UserLanguage);
585 
586         SharedMem_AddRef(Memory);
587         DPRINT("Creating SharedFace for %s\n", Face->family_name ? Face->family_name : "<NULL>");
588     }
589     return Ptr;
590 }
591 
592 static PSHARED_MEM
SharedMem_Create(PBYTE Buffer,ULONG BufferSize,BOOL IsMapping)593 SharedMem_Create(PBYTE Buffer, ULONG BufferSize, BOOL IsMapping)
594 {
595     PSHARED_MEM Ptr;
596     Ptr = ExAllocatePoolWithTag(PagedPool, sizeof(SHARED_MEM), TAG_FONT);
597     if (Ptr)
598     {
599         Ptr->Buffer = Buffer;
600         Ptr->BufferSize = BufferSize;
601         Ptr->RefCount = 1;
602         Ptr->IsMapping = IsMapping;
603         DPRINT("Creating SharedMem for %p (%i, %p)\n", Buffer, IsMapping, Ptr);
604     }
605     return Ptr;
606 }
607 
608 static void
SharedFace_AddRef(PSHARED_FACE Ptr)609 SharedFace_AddRef(PSHARED_FACE Ptr)
610 {
611     ASSERT_FREETYPE_LOCK_HELD();
612 
613     ++Ptr->RefCount;
614 }
615 
616 static void
RemoveCachedEntry(PFONT_CACHE_ENTRY Entry)617 RemoveCachedEntry(PFONT_CACHE_ENTRY Entry)
618 {
619     ASSERT_FREETYPE_LOCK_HELD();
620 
621     FT_Done_Glyph((FT_Glyph)Entry->BitmapGlyph);
622     RemoveEntryList(&Entry->ListEntry);
623     ExFreePoolWithTag(Entry, TAG_FONT);
624     g_FontCacheNumEntries--;
625     ASSERT(g_FontCacheNumEntries <= MAX_FONT_CACHE);
626 }
627 
628 static void
RemoveCacheEntries(FT_Face Face)629 RemoveCacheEntries(FT_Face Face)
630 {
631     PLIST_ENTRY CurrentEntry, NextEntry;
632     PFONT_CACHE_ENTRY FontEntry;
633 
634     ASSERT_FREETYPE_LOCK_HELD();
635 
636     for (CurrentEntry = g_FontCacheListHead.Flink;
637          CurrentEntry != &g_FontCacheListHead;
638          CurrentEntry = NextEntry)
639     {
640         FontEntry = CONTAINING_RECORD(CurrentEntry, FONT_CACHE_ENTRY, ListEntry);
641         NextEntry = CurrentEntry->Flink;
642 
643         if (FontEntry->Hashed.Face == Face)
644         {
645             RemoveCachedEntry(FontEntry);
646         }
647     }
648 }
649 
SharedMem_Release(PSHARED_MEM Ptr)650 static void SharedMem_Release(PSHARED_MEM Ptr)
651 {
652     ASSERT_FREETYPE_LOCK_HELD();
653     ASSERT(Ptr->RefCount > 0);
654 
655     if (Ptr->RefCount <= 0)
656         return;
657 
658     --Ptr->RefCount;
659     if (Ptr->RefCount == 0)
660     {
661         DPRINT("Releasing SharedMem for %p (%i, %p)\n", Ptr->Buffer, Ptr->IsMapping, Ptr);
662         if (Ptr->IsMapping)
663             MmUnmapViewInSystemSpace(Ptr->Buffer);
664         else
665             ExFreePoolWithTag(Ptr->Buffer, TAG_FONT);
666         ExFreePoolWithTag(Ptr, TAG_FONT);
667     }
668 }
669 
670 static void
SharedFaceCache_Release(PSHARED_FACE_CACHE Cache)671 SharedFaceCache_Release(PSHARED_FACE_CACHE Cache)
672 {
673     RtlFreeUnicodeString(&Cache->FontFamily);
674     RtlFreeUnicodeString(&Cache->FullName);
675 }
676 
677 static void
SharedFace_Release(PSHARED_FACE Ptr)678 SharedFace_Release(PSHARED_FACE Ptr)
679 {
680     IntLockFreeType();
681     ASSERT(Ptr->RefCount > 0);
682 
683     if (Ptr->RefCount <= 0)
684         return;
685 
686     --Ptr->RefCount;
687     if (Ptr->RefCount == 0)
688     {
689         DPRINT("Releasing SharedFace for %s\n", Ptr->Face->family_name ? Ptr->Face->family_name : "<NULL>");
690         RemoveCacheEntries(Ptr->Face);
691         FT_Done_Face(Ptr->Face);
692         SharedMem_Release(Ptr->Memory);
693         SharedFaceCache_Release(&Ptr->EnglishUS);
694         SharedFaceCache_Release(&Ptr->UserLanguage);
695         ExFreePoolWithTag(Ptr, TAG_FONT);
696     }
697     IntUnLockFreeType();
698 }
699 
700 
701 static VOID FASTCALL
CleanupFontEntryEx(PFONT_ENTRY FontEntry,PFONTGDI FontGDI)702 CleanupFontEntryEx(PFONT_ENTRY FontEntry, PFONTGDI FontGDI)
703 {
704     // PFONTGDI FontGDI = FontEntry->Font;
705     PSHARED_FACE SharedFace = FontGDI->SharedFace;
706 
707     if (FontGDI->Filename)
708         ExFreePoolWithTag(FontGDI->Filename, GDITAG_PFF);
709 
710     if (FontEntry->StyleName.Buffer)
711         RtlFreeUnicodeString(&FontEntry->StyleName);
712 
713     if (FontEntry->FaceName.Buffer)
714         RtlFreeUnicodeString(&FontEntry->FaceName);
715 
716     EngFreeMem(FontGDI);
717     SharedFace_Release(SharedFace);
718     ExFreePoolWithTag(FontEntry, TAG_FONT);
719 }
720 
721 static __inline VOID FASTCALL
CleanupFontEntry(PFONT_ENTRY FontEntry)722 CleanupFontEntry(PFONT_ENTRY FontEntry)
723 {
724     CleanupFontEntryEx(FontEntry, FontEntry->Font);
725 }
726 
727 
FTVectorToPOINTFX(FT_Vector * vec,POINTFX * pt)728 static __inline void FTVectorToPOINTFX(FT_Vector *vec, POINTFX *pt)
729 {
730     pt->x.value = vec->x >> 6;
731     pt->x.fract = (vec->x & 0x3f) << 10;
732     pt->x.fract |= ((pt->x.fract >> 6) | (pt->x.fract >> 12));
733     pt->y.value = vec->y >> 6;
734     pt->y.fract = (vec->y & 0x3f) << 10;
735     pt->y.fract |= ((pt->y.fract >> 6) | (pt->y.fract >> 12));
736 }
737 
738 /*
739    This function builds an FT_Fixed from a FIXED. It simply put f.value
740    in the highest 16 bits and f.fract in the lowest 16 bits of the FT_Fixed.
741 */
FT_FixedFromFIXED(FIXED f)742 static __inline FT_Fixed FT_FixedFromFIXED(FIXED f)
743 {
744     return (FT_Fixed)((long)f.value << 16 | (unsigned long)f.fract);
745 }
746 
747 
748 #if DBG
DumpFontEntry(PFONT_ENTRY FontEntry)749 VOID DumpFontEntry(PFONT_ENTRY FontEntry)
750 {
751     const char *family_name;
752     const char *style_name;
753     FT_Face Face;
754     PFONTGDI FontGDI = FontEntry->Font;
755 
756     if (!FontGDI)
757     {
758         DPRINT("FontGDI NULL\n");
759         return;
760     }
761 
762     Face = (FontGDI->SharedFace ? FontGDI->SharedFace->Face : NULL);
763     if (Face)
764     {
765         family_name = Face->family_name;
766         style_name = Face->style_name;
767     }
768     else
769     {
770         family_name = "<invalid>";
771         style_name = "<invalid>";
772     }
773 
774     DPRINT("family_name '%s', style_name '%s', FaceName '%wZ', StyleName '%wZ', FontGDI %p, "
775            "FontObj %p, iUnique %lu, SharedFace %p, Face %p, CharSet %u, Filename '%S'\n",
776            family_name,
777            style_name,
778            &FontEntry->FaceName,
779            &FontEntry->StyleName,
780            FontGDI,
781            &FontGDI->FontObj,
782            FontGDI->iUnique,
783            FontGDI->SharedFace,
784            Face,
785            FontGDI->CharSet,
786            FontGDI->Filename);
787 }
788 
DumpFontList(PLIST_ENTRY Head)789 VOID DumpFontList(PLIST_ENTRY Head)
790 {
791     PLIST_ENTRY Entry;
792     PFONT_ENTRY CurrentEntry;
793 
794     DPRINT("## DumpFontList(%p)\n", Head);
795 
796     for (Entry = Head->Flink; Entry != Head; Entry = Entry->Flink)
797     {
798         CurrentEntry = CONTAINING_RECORD(Entry, FONT_ENTRY, ListEntry);
799         DumpFontEntry(CurrentEntry);
800     }
801 }
802 
DumpFontSubstEntry(PFONTSUBST_ENTRY pSubstEntry)803 VOID DumpFontSubstEntry(PFONTSUBST_ENTRY pSubstEntry)
804 {
805     DPRINT("%wZ,%u -> %wZ,%u\n",
806         &pSubstEntry->FontNames[FONTSUBST_FROM],
807         pSubstEntry->CharSets[FONTSUBST_FROM],
808         &pSubstEntry->FontNames[FONTSUBST_TO],
809         pSubstEntry->CharSets[FONTSUBST_TO]);
810 }
811 
DumpFontSubstList(VOID)812 VOID DumpFontSubstList(VOID)
813 {
814     PLIST_ENTRY         pHead = &g_FontSubstListHead;
815     PLIST_ENTRY         pListEntry;
816     PFONTSUBST_ENTRY    pSubstEntry;
817 
818     DPRINT("## DumpFontSubstList\n");
819 
820     for (pListEntry = pHead->Flink;
821          pListEntry != pHead;
822          pListEntry = pListEntry->Flink)
823     {
824         pSubstEntry = CONTAINING_RECORD(pListEntry, FONTSUBST_ENTRY, ListEntry);
825         DumpFontSubstEntry(pSubstEntry);
826     }
827 }
828 
DumpPrivateFontList(BOOL bDoLock)829 VOID DumpPrivateFontList(BOOL bDoLock)
830 {
831     PPROCESSINFO Win32Process = PsGetCurrentProcessWin32Process();
832 
833     if (!Win32Process)
834         return;
835 
836     if (bDoLock)
837     {
838         IntLockFreeType();
839         IntLockProcessPrivateFonts(Win32Process);
840     }
841 
842     DumpFontList(&Win32Process->PrivateFontListHead);
843 
844     if (bDoLock)
845     {
846         IntUnLockProcessPrivateFonts(Win32Process);
847         IntUnLockFreeType();
848     }
849 }
850 
DumpGlobalFontList(BOOL bDoLock)851 VOID DumpGlobalFontList(BOOL bDoLock)
852 {
853     if (bDoLock)
854         IntLockFreeType();
855 
856     DumpFontList(&g_FontListHead);
857 
858     if (bDoLock)
859         IntUnLockFreeType();
860 }
861 
DumpFontInfo(BOOL bDoLock)862 VOID DumpFontInfo(BOOL bDoLock)
863 {
864     DumpGlobalFontList(bDoLock);
865     DumpPrivateFontList(bDoLock);
866     DumpFontSubstList();
867 }
868 #endif
869 
870 /*
871  * IntLoadFontSubstList --- loads the list of font substitutes
872  */
873 BOOL FASTCALL
IntLoadFontSubstList(PLIST_ENTRY pHead)874 IntLoadFontSubstList(PLIST_ENTRY pHead)
875 {
876     NTSTATUS                        Status;
877     HANDLE                          KeyHandle;
878     OBJECT_ATTRIBUTES               ObjectAttributes;
879     KEY_FULL_INFORMATION            KeyFullInfo;
880     ULONG                           i, Length;
881     UNICODE_STRING                  FromW, ToW;
882     BYTE                            InfoBuffer[128];
883     PKEY_VALUE_FULL_INFORMATION     pInfo;
884     BYTE                            CharSets[FONTSUBST_FROM_AND_TO];
885     LPWSTR                          pch;
886     PFONTSUBST_ENTRY                pEntry;
887 
888     /* the FontSubstitutes registry key */
889     static UNICODE_STRING FontSubstKey =
890         RTL_CONSTANT_STRING(L"\\Registry\\Machine\\Software\\"
891                             L"Microsoft\\Windows NT\\CurrentVersion\\"
892                             L"FontSubstitutes");
893 
894     /* open registry key */
895     InitializeObjectAttributes(&ObjectAttributes, &FontSubstKey,
896                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
897                                NULL, NULL);
898     Status = ZwOpenKey(&KeyHandle, KEY_READ, &ObjectAttributes);
899     if (!NT_SUCCESS(Status))
900     {
901         DPRINT("ZwOpenKey failed: 0x%08X\n", Status);
902         return FALSE;   /* failure */
903     }
904 
905     /* query count of values */
906     Status = ZwQueryKey(KeyHandle, KeyFullInformation,
907                         &KeyFullInfo, sizeof(KeyFullInfo), &Length);
908     if (!NT_SUCCESS(Status))
909     {
910         DPRINT("ZwQueryKey failed: 0x%08X\n", Status);
911         ZwClose(KeyHandle);
912         return FALSE;   /* failure */
913     }
914 
915     /* for each value */
916     for (i = 0; i < KeyFullInfo.Values; ++i)
917     {
918         /* get value name */
919         Status = ZwEnumerateValueKey(KeyHandle, i, KeyValueFullInformation,
920                                      InfoBuffer, sizeof(InfoBuffer), &Length);
921         if (!NT_SUCCESS(Status))
922         {
923             DPRINT("ZwEnumerateValueKey failed: 0x%08X\n", Status);
924             break;      /* failure */
925         }
926 
927         /* create FromW string */
928         pInfo = (PKEY_VALUE_FULL_INFORMATION)InfoBuffer;
929         Length = pInfo->NameLength / sizeof(WCHAR);
930         pInfo->Name[Length] = UNICODE_NULL;   /* truncate */
931         if (!RtlCreateUnicodeString(&FromW, pInfo->Name))
932         {
933             Status = STATUS_INSUFFICIENT_RESOURCES;
934             DPRINT("RtlCreateUnicodeString failed\n");
935             break;      /* failure */
936         }
937 
938         /* query value */
939         Status = ZwQueryValueKey(KeyHandle, &FromW, KeyValueFullInformation,
940                                  InfoBuffer, sizeof(InfoBuffer), &Length);
941         pInfo = (PKEY_VALUE_FULL_INFORMATION)InfoBuffer;
942         if (!NT_SUCCESS(Status) || !pInfo->DataLength)
943         {
944             DPRINT("ZwQueryValueKey failed: 0x%08X\n", Status);
945             RtlFreeUnicodeString(&FromW);
946             break;      /* failure */
947         }
948 
949         /* create ToW string */
950         pch = (LPWSTR)((PUCHAR)pInfo + pInfo->DataOffset);
951         Length = pInfo->DataLength / sizeof(WCHAR);
952         pch[Length] = UNICODE_NULL; /* truncate */
953         if (!RtlCreateUnicodeString(&ToW, pch))
954         {
955             Status = STATUS_INSUFFICIENT_RESOURCES;
956             DPRINT("RtlCreateUnicodeString failed\n");
957             RtlFreeUnicodeString(&FromW);
958             break;      /* failure */
959         }
960 
961         /* does charset exist? (from) */
962         CharSets[FONTSUBST_FROM] = DEFAULT_CHARSET;
963         pch = wcsrchr(FromW.Buffer, L',');
964         if (pch)
965         {
966             /* truncate */
967             *pch = UNICODE_NULL;
968             FromW.Length = (pch - FromW.Buffer) * sizeof(WCHAR);
969             /* parse charset number */
970             CharSets[FONTSUBST_FROM] = (BYTE)_wtoi(pch + 1);
971         }
972 
973         /* does charset exist? (to) */
974         CharSets[FONTSUBST_TO] = DEFAULT_CHARSET;
975         pch = wcsrchr(ToW.Buffer, L',');
976         if (pch)
977         {
978             /* truncate */
979             *pch = UNICODE_NULL;
980             ToW.Length = (pch - ToW.Buffer) * sizeof(WCHAR);
981             /* parse charset number */
982             CharSets[FONTSUBST_TO] = (BYTE)_wtoi(pch + 1);
983         }
984 
985         /* is it identical? */
986         if (RtlEqualUnicodeString(&FromW, &ToW, TRUE) &&
987             CharSets[FONTSUBST_FROM] == CharSets[FONTSUBST_TO])
988         {
989             RtlFreeUnicodeString(&FromW);
990             RtlFreeUnicodeString(&ToW);
991             continue;
992         }
993 
994         /* allocate an entry */
995         pEntry = ExAllocatePoolWithTag(PagedPool, sizeof(FONTSUBST_ENTRY), TAG_FONT);
996         if (pEntry == NULL)
997         {
998             DPRINT("ExAllocatePoolWithTag failed\n");
999             RtlFreeUnicodeString(&FromW);
1000             RtlFreeUnicodeString(&ToW);
1001             break;      /* failure */
1002         }
1003 
1004         /* store to *pEntry */
1005         pEntry->FontNames[FONTSUBST_FROM] = FromW;
1006         pEntry->FontNames[FONTSUBST_TO] = ToW;
1007         pEntry->CharSets[FONTSUBST_FROM] = CharSets[FONTSUBST_FROM];
1008         pEntry->CharSets[FONTSUBST_TO] = CharSets[FONTSUBST_TO];
1009 
1010         /* insert pEntry to *pHead */
1011         InsertTailList(pHead, &pEntry->ListEntry);
1012     }
1013 
1014     /* close now */
1015     ZwClose(KeyHandle);
1016 
1017     return NT_SUCCESS(Status);
1018 }
1019 
1020 BOOL FASTCALL
InitFontSupport(VOID)1021 InitFontSupport(VOID)
1022 {
1023     ULONG ulError;
1024 
1025     g_FontCacheNumEntries = 0;
1026 
1027     g_FreeTypeLock = ExAllocatePoolWithTag(NonPagedPool, sizeof(FAST_MUTEX), TAG_INTERNAL_SYNC);
1028     if (g_FreeTypeLock == NULL)
1029     {
1030         return FALSE;
1031     }
1032     ExInitializeFastMutex(g_FreeTypeLock);
1033 
1034     ulError = FT_Init_FreeType(&g_FreeTypeLibrary);
1035     if (ulError)
1036     {
1037         DPRINT1("FT_Init_FreeType failed with error code 0x%x\n", ulError);
1038         return FALSE;
1039     }
1040 
1041     if (!IntLoadFontsInRegistry())
1042     {
1043         DPRINT1("Fonts registry is empty.\n");
1044 
1045         /* Load font(s) with writing registry */
1046         IntLoadSystemFonts();
1047     }
1048 
1049     IntLoadFontSubstList(&g_FontSubstListHead);
1050 
1051 #if 0
1052     DumpFontInfo(TRUE);
1053 #endif
1054 
1055     FontLink_LoadSettings();
1056     FontLink_LoadDefaultFonts();
1057     FontLink_LoadDefaultCharset();
1058 
1059     return TRUE;
1060 }
1061 
1062 VOID FASTCALL
FreeFontSupport(VOID)1063 FreeFontSupport(VOID)
1064 {
1065     PLIST_ENTRY pHead, pEntry;
1066     PFONT_CACHE_ENTRY pFontCache;
1067     PFONTSUBST_ENTRY pSubstEntry;
1068     PFONT_ENTRY pFontEntry;
1069 
1070     // Cleanup the FontLink cache
1071     FontLink_CleanupCache();
1072 
1073     // Free font cache list
1074     pHead = &g_FontCacheListHead;
1075     while (!IsListEmpty(pHead))
1076     {
1077         pEntry = RemoveHeadList(pHead);
1078         pFontCache = CONTAINING_RECORD(pEntry, FONT_CACHE_ENTRY, ListEntry);
1079         RemoveCachedEntry(pFontCache);
1080     }
1081 
1082     // Free font subst list
1083     pHead = &g_FontSubstListHead;
1084     while (!IsListEmpty(pHead))
1085     {
1086         pEntry = RemoveHeadList(pHead);
1087         pSubstEntry = CONTAINING_RECORD(pEntry, FONTSUBST_ENTRY, ListEntry);
1088         ExFreePoolWithTag(pSubstEntry, TAG_FONT);
1089     }
1090 
1091     // Free font list
1092     pHead = &g_FontListHead;
1093     while (!IsListEmpty(pHead))
1094     {
1095         pEntry = RemoveHeadList(pHead);
1096         pFontEntry = CONTAINING_RECORD(pEntry, FONT_ENTRY, ListEntry);
1097         CleanupFontEntry(pFontEntry);
1098     }
1099 
1100     if (g_FreeTypeLibrary)
1101     {
1102         FT_Done_Library(g_FreeTypeLibrary);
1103         g_FreeTypeLibrary = NULL;
1104     }
1105 
1106     ExFreePoolWithTag(g_FreeTypeLock, TAG_INTERNAL_SYNC);
1107     g_FreeTypeLock = NULL;
1108 }
1109 
IntNormalizeAngle(LONG nTenthsOfDegrees)1110 static LONG IntNormalizeAngle(LONG nTenthsOfDegrees)
1111 {
1112     nTenthsOfDegrees %= 360 * 10;
1113     if (nTenthsOfDegrees >= 0)
1114         return nTenthsOfDegrees;
1115     return nTenthsOfDegrees + 360 * 10;
1116 }
1117 
IntEscapeMatrix(FT_Matrix * pmat,LONG lfEscapement)1118 static VOID FASTCALL IntEscapeMatrix(FT_Matrix *pmat, LONG lfEscapement)
1119 {
1120     FT_Vector vecAngle;
1121     /* Convert the angle in tenths of degrees into degrees as a 16.16 fixed-point value */
1122     FT_Angle angle = INT_TO_FIXED(lfEscapement) / 10;
1123     FT_Vector_Unit(&vecAngle, angle);
1124     pmat->xx = vecAngle.x;
1125     pmat->xy = -vecAngle.y;
1126     pmat->yx = -pmat->xy;
1127     pmat->yy = pmat->xx;
1128 }
1129 
1130 static VOID FASTCALL
IntMatrixFromMx(FT_Matrix * pmat,const MATRIX * pmx)1131 IntMatrixFromMx(FT_Matrix *pmat, const MATRIX *pmx)
1132 {
1133     FLOATOBJ ef;
1134 
1135     /* Create a freetype matrix, by converting to 16.16 fixpoint format */
1136     ef = pmx->efM11;
1137     FLOATOBJ_MulLong(&ef, 0x00010000);
1138     pmat->xx = FLOATOBJ_GetLong(&ef);
1139 
1140     ef = pmx->efM21;
1141     FLOATOBJ_MulLong(&ef, 0x00010000);
1142     pmat->xy = -FLOATOBJ_GetLong(&ef); /* (*1) See below */
1143 
1144     ef = pmx->efM12;
1145     FLOATOBJ_MulLong(&ef, 0x00010000);
1146     pmat->yx = -FLOATOBJ_GetLong(&ef); /* (*1) See below */
1147 
1148     ef = pmx->efM22;
1149     FLOATOBJ_MulLong(&ef, 0x00010000);
1150     pmat->yy = FLOATOBJ_GetLong(&ef);
1151 
1152     // (*1): Y direction is mirrored as follows:
1153     //
1154     // [  M11  -M12 ]   [  X ]    [   M11*X + M12*Y  ]
1155     // [            ] * [    ] == [                  ]
1156     // [ -M21   M22 ]   [ -Y ]    [ -(M21*X + M22*Y) ].
1157 }
1158 
1159 static BOOL
SubstituteFontByList(PLIST_ENTRY pHead,PUNICODE_STRING pOutputName,PUNICODE_STRING pInputName,BYTE RequestedCharSet,BYTE CharSetMap[FONTSUBST_FROM_AND_TO])1160 SubstituteFontByList(PLIST_ENTRY        pHead,
1161                      PUNICODE_STRING    pOutputName,
1162                      PUNICODE_STRING    pInputName,
1163                      BYTE               RequestedCharSet,
1164                      BYTE               CharSetMap[FONTSUBST_FROM_AND_TO])
1165 {
1166     PLIST_ENTRY         pListEntry;
1167     PFONTSUBST_ENTRY    pSubstEntry;
1168     BYTE                CharSets[FONTSUBST_FROM_AND_TO];
1169 
1170     CharSetMap[FONTSUBST_FROM] = DEFAULT_CHARSET;
1171     CharSetMap[FONTSUBST_TO] = RequestedCharSet;
1172 
1173     /* for each list entry */
1174     for (pListEntry = pHead->Flink;
1175          pListEntry != pHead;
1176          pListEntry = pListEntry->Flink)
1177     {
1178         pSubstEntry = CONTAINING_RECORD(pListEntry, FONTSUBST_ENTRY, ListEntry);
1179 
1180         CharSets[FONTSUBST_FROM] = pSubstEntry->CharSets[FONTSUBST_FROM];
1181 
1182         if (CharSets[FONTSUBST_FROM] != DEFAULT_CHARSET &&
1183             CharSets[FONTSUBST_FROM] != RequestedCharSet)
1184         {
1185             continue;   /* not matched */
1186         }
1187 
1188         /* does charset number exist? (to) */
1189         if (pSubstEntry->CharSets[FONTSUBST_TO] != DEFAULT_CHARSET)
1190         {
1191             CharSets[FONTSUBST_TO] = pSubstEntry->CharSets[FONTSUBST_TO];
1192         }
1193         else
1194         {
1195             CharSets[FONTSUBST_TO] = RequestedCharSet;
1196         }
1197 
1198         /* does font name match? */
1199         if (!RtlEqualUnicodeString(&pSubstEntry->FontNames[FONTSUBST_FROM],
1200                                    pInputName, TRUE))
1201         {
1202             continue;   /* not matched */
1203         }
1204 
1205         /* update *pOutputName */
1206         *pOutputName = pSubstEntry->FontNames[FONTSUBST_TO];
1207 
1208         if (CharSetMap[FONTSUBST_FROM] == DEFAULT_CHARSET)
1209         {
1210             /* update CharSetMap */
1211             CharSetMap[FONTSUBST_FROM]  = CharSets[FONTSUBST_FROM];
1212             CharSetMap[FONTSUBST_TO]    = CharSets[FONTSUBST_TO];
1213         }
1214         return TRUE;   /* success */
1215     }
1216 
1217     return FALSE;
1218 }
1219 
1220 static VOID
IntUnicodeStringToBuffer(LPWSTR pszBuffer,SIZE_T cbBuffer,const UNICODE_STRING * pString)1221 IntUnicodeStringToBuffer(LPWSTR pszBuffer, SIZE_T cbBuffer, const UNICODE_STRING *pString)
1222 {
1223     SIZE_T cbLength = pString->Length;
1224 
1225     if (cbBuffer < sizeof(UNICODE_NULL))
1226         return;
1227 
1228     if (cbLength > cbBuffer - sizeof(UNICODE_NULL))
1229         cbLength = cbBuffer - sizeof(UNICODE_NULL);
1230 
1231     RtlCopyMemory(pszBuffer, pString->Buffer, cbLength);
1232     pszBuffer[cbLength / sizeof(WCHAR)] = UNICODE_NULL;
1233 }
1234 
1235 static NTSTATUS
DuplicateUnicodeString(PUNICODE_STRING Source,PUNICODE_STRING Destination)1236 DuplicateUnicodeString(PUNICODE_STRING Source, PUNICODE_STRING Destination)
1237 {
1238     NTSTATUS Status = STATUS_NO_MEMORY;
1239     UNICODE_STRING Tmp;
1240 
1241     Tmp.Buffer = ExAllocatePoolWithTag(PagedPool, Source->MaximumLength, TAG_USTR);
1242     if (Tmp.Buffer)
1243     {
1244         Tmp.MaximumLength = Source->MaximumLength;
1245         Tmp.Length = 0;
1246         RtlCopyUnicodeString(&Tmp, Source);
1247 
1248         Destination->MaximumLength = Tmp.MaximumLength;
1249         Destination->Length = Tmp.Length;
1250         Destination->Buffer = Tmp.Buffer;
1251 
1252         Status = STATUS_SUCCESS;
1253     }
1254 
1255     return Status;
1256 }
1257 
1258 static BOOL
SubstituteFontRecurse(PLOGFONTW pLogFont)1259 SubstituteFontRecurse(PLOGFONTW pLogFont)
1260 {
1261     UINT            RecurseCount = 5;
1262     UNICODE_STRING  OutputNameW = { 0 };
1263     BYTE            CharSetMap[FONTSUBST_FROM_AND_TO];
1264     BOOL            Found;
1265     UNICODE_STRING  InputNameW;
1266 
1267     if (pLogFont->lfFaceName[0] == UNICODE_NULL)
1268         return FALSE;
1269 
1270     RtlInitUnicodeString(&InputNameW, pLogFont->lfFaceName);
1271 
1272     while (RecurseCount-- > 0)
1273     {
1274         Found = SubstituteFontByList(&g_FontSubstListHead,
1275                                      &OutputNameW, &InputNameW,
1276                                      pLogFont->lfCharSet, CharSetMap);
1277         if (!Found)
1278             break;
1279 
1280         IntUnicodeStringToBuffer(pLogFont->lfFaceName, sizeof(pLogFont->lfFaceName), &OutputNameW);
1281         RtlInitUnicodeString(&InputNameW, pLogFont->lfFaceName);
1282 
1283         if (CharSetMap[FONTSUBST_FROM] == DEFAULT_CHARSET ||
1284             CharSetMap[FONTSUBST_FROM] == pLogFont->lfCharSet)
1285         {
1286             pLogFont->lfCharSet = CharSetMap[FONTSUBST_TO];
1287         }
1288     }
1289 
1290     return TRUE;    /* success */
1291 }
1292 
1293 // Quickly initialize a FONTLINK_CHAIN
1294 static inline VOID
FontLink_Chain_Init(_Out_ PFONTLINK_CHAIN pChain,_Inout_ PTEXTOBJ pTextObj,_In_ FT_Face face)1295 FontLink_Chain_Init(
1296     _Out_ PFONTLINK_CHAIN pChain,
1297     _Inout_ PTEXTOBJ pTextObj,
1298     _In_ FT_Face face)
1299 {
1300     RtlZeroMemory(pChain, sizeof(*pChain));
1301     pChain->pBaseTextObj = pTextObj;
1302     pChain->pDefFace = face;
1303     ASSERT(!FontLink_Chain_IsPopulated(pChain));
1304 }
1305 
1306 // The default FontLink data
1307 static const WCHAR s_szzDefFontLink[] =
1308     L"tahoma.ttf,Tahoma\0"
1309     L"msgothic.ttc,MS UI Gothic\0"
1310     L"mingliu.ttc,PMingLiU\0"
1311     L"simsun.ttc,SimSun\0"
1312     L"gulim.ttc,Gulim\0"
1313     L"\0";
1314 // The default fixed-pitch FontLink data
1315 static const WCHAR s_szzDefFixedFontLink[] =
1316     L"cour.ttf,Courier New\0"
1317     L"msgothic.ttc,MS Gothic\0"
1318     L"mingliu.ttc,MingLiU\0"
1319     L"simsun.ttc,NSimSun\0"
1320     L"gulim.ttc,GulimChe\0"
1321     L"\0";
1322 
1323 static NTSTATUS
FontLink_Chain_LoadReg(_Inout_ PFONTLINK_CHAIN pChain,_Inout_ PLOGFONTW pLF)1324 FontLink_Chain_LoadReg(
1325     _Inout_ PFONTLINK_CHAIN pChain,
1326     _Inout_ PLOGFONTW pLF)
1327 {
1328     NTSTATUS Status;
1329     HKEY hKey;
1330     DWORD cbData;
1331     WCHAR szzFontLink[512];
1332     SIZE_T FontLinkSize;
1333     PZZWSTR pszzFontLink = NULL;
1334 
1335     ASSERT(pLF->lfFaceName[0]);
1336 
1337     // Open the registry key
1338     Status = RegOpenKey(
1339         L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\FontLink\\SystemLink",
1340         &hKey);
1341     if (!NT_SUCCESS(Status))
1342         return Status;
1343 
1344     // Load the FontLink entry
1345     cbData = sizeof(szzFontLink);
1346     Status = RegQueryValue(hKey, pLF->lfFaceName, REG_MULTI_SZ, szzFontLink, &cbData);
1347     if (!NT_SUCCESS(Status) &&
1348         (Status != STATUS_BUFFER_OVERFLOW) && (Status != STATUS_BUFFER_TOO_SMALL))
1349     {
1350         // Retry with substituted
1351         SubstituteFontRecurse(pLF);
1352         cbData = sizeof(szzFontLink);
1353         Status = RegQueryValue(hKey, pLF->lfFaceName, REG_MULTI_SZ, szzFontLink, &cbData);
1354     }
1355 
1356     if ((Status == STATUS_BUFFER_OVERFLOW) || (Status == STATUS_BUFFER_TOO_SMALL))
1357     {
1358         // Buffer is too small. Retry with larger buffer
1359         if (cbData >= 2 * sizeof(WCHAR)) // Sanity check
1360         {
1361             FontLinkSize = cbData;
1362             pszzFontLink = ExAllocatePoolWithTag(PagedPool, FontLinkSize, TAG_FONT);
1363             if (!pszzFontLink)
1364             {
1365                 ZwClose(hKey); // Close the registry key
1366                 return STATUS_NO_MEMORY;
1367             }
1368             Status = RegQueryValue(hKey, pLF->lfFaceName, REG_MULTI_SZ, pszzFontLink, &cbData);
1369             if (!NT_SUCCESS(Status))
1370             {
1371                 ExFreePoolWithTag(pszzFontLink, TAG_FONT);
1372                 pszzFontLink = NULL;
1373             }
1374         }
1375     }
1376 
1377     ZwClose(hKey); // Close the registry key
1378 
1379     if (!NT_SUCCESS(Status)) // Failed to get registry value
1380     {
1381         // Use default value
1382         ASSERT(sizeof(szzFontLink) >= sizeof(s_szzDefFontLink));
1383         ASSERT(sizeof(szzFontLink) >= sizeof(s_szzDefFixedFontLink));
1384         if (!(pLF->lfPitchAndFamily & FIXED_PITCH))
1385             RtlCopyMemory(szzFontLink, s_szzDefFontLink, sizeof(s_szzDefFontLink));
1386         else
1387             RtlCopyMemory(szzFontLink, s_szzDefFixedFontLink, sizeof(s_szzDefFixedFontLink));
1388     }
1389 
1390     if (pszzFontLink)
1391     {
1392         // Ensure double-NUL-terminated
1393         ASSERT(FontLinkSize / sizeof(WCHAR) >= 2);
1394         pszzFontLink[FontLinkSize / sizeof(WCHAR) - 1] = UNICODE_NULL;
1395         pszzFontLink[FontLinkSize / sizeof(WCHAR) - 2] = UNICODE_NULL;
1396     }
1397     else
1398     {
1399         // Ensure double-NUL-terminated
1400         szzFontLink[_countof(szzFontLink) - 1] = UNICODE_NULL;
1401         szzFontLink[_countof(szzFontLink) - 2] = UNICODE_NULL;
1402 
1403         FontLinkSize = SZZ_GetSize(szzFontLink);
1404         pszzFontLink = ExAllocatePoolWithTag(PagedPool, FontLinkSize, TAG_FONT);
1405         if (!pszzFontLink)
1406             return STATUS_NO_MEMORY;
1407         RtlCopyMemory(pszzFontLink, szzFontLink, FontLinkSize);
1408     }
1409     pChain->pszzFontLink = pszzFontLink;
1410 
1411     return STATUS_SUCCESS;
1412 }
1413 
1414 static inline PFONTLINK
FontLink_Chain_FindLink(PFONTLINK_CHAIN pChain,PLOGFONTW plf)1415 FontLink_Chain_FindLink(
1416     PFONTLINK_CHAIN pChain,
1417     PLOGFONTW plf)
1418 {
1419     PLIST_ENTRY Entry, Head = &pChain->FontLinkList;
1420     PFONTLINK pLink;
1421 
1422     for (Entry = Head->Flink; Entry != Head; Entry = Entry->Flink)
1423     {
1424         pLink = CONTAINING_RECORD(Entry, FONTLINK, ListEntry);
1425         if (RtlEqualMemory(&pLink->LogFont, plf, sizeof(*plf)))
1426             return pLink;
1427     }
1428 
1429     return NULL;
1430 }
1431 
1432 static inline PFONTLINK
FontLink_Create(_Inout_ PFONTLINK_CHAIN pChain,_In_ const LOGFONTW * plfBase,_In_ LPCWSTR pszLink)1433 FontLink_Create(
1434     _Inout_ PFONTLINK_CHAIN pChain,
1435     _In_ const LOGFONTW *plfBase,
1436     _In_ LPCWSTR pszLink)
1437 {
1438     LPWSTR pch0, pch1;
1439     LOGFONTW lf;
1440     PFONTLINK pLink;
1441 
1442     lf = *plfBase;
1443 
1444     // pszLink: "<FontFileName>,<FaceName>[,...]"
1445     pch0 = wcschr(pszLink, L',');
1446     if (!pch0)
1447     {
1448         DPRINT1("%S\n", pszLink);
1449         return NULL; // Invalid FontLink data
1450     }
1451     ++pch0;
1452 
1453     pch1 = wcschr(pch0, L',');
1454     if (pch1)
1455         RtlStringCchCopyNW(lf.lfFaceName, _countof(lf.lfFaceName), pch0, pch1 - pch0);
1456     else
1457         RtlStringCchCopyW(lf.lfFaceName, _countof(lf.lfFaceName), pch0);
1458 
1459     SubstituteFontRecurse(&lf);
1460     DPRINT("lfFaceName: %S\n", lf.lfFaceName);
1461 
1462     if (RtlEqualMemory(plfBase, &lf, sizeof(lf)) || FontLink_Chain_FindLink(pChain, &lf))
1463         return NULL; // Already exists
1464 
1465     pLink = ExAllocatePoolZero(PagedPool, sizeof(FONTLINK), TAG_FONT);
1466     if (!pLink)
1467         return NULL; // Out of memory
1468 
1469     pLink->LogFont = lf;
1470     return pLink;
1471 }
1472 
1473 static NTSTATUS
FontLink_Chain_Populate(_Inout_ PFONTLINK_CHAIN pChain)1474 FontLink_Chain_Populate(
1475     _Inout_ PFONTLINK_CHAIN pChain)
1476 {
1477     NTSTATUS Status;
1478     PFONTLINK pLink;
1479     LOGFONTW lfBase;
1480     PTEXTOBJ pTextObj = pChain->pBaseTextObj;
1481     PFONTGDI pFontGDI;
1482     PWSTR pszLink;
1483     PFONTLINK_CACHE pLinkCache;
1484     WCHAR szEntry[MAX_PATH];
1485     BOOL bFixCharSet;
1486 
1487     InitializeListHead(&pChain->FontLinkList);
1488 
1489     lfBase = pTextObj->logfont.elfEnumLogfontEx.elfLogFont;
1490     pFontGDI = ObjToGDI(pTextObj->Font, FONT);
1491     lfBase.lfHeight = pFontGDI->lfHeight;
1492     lfBase.lfWidth = pFontGDI->lfWidth;
1493 
1494     // Use pTextObj->TextFace if lfFaceName was empty
1495     if (!lfBase.lfFaceName[0])
1496     {
1497         ASSERT(pTextObj->TextFace[0]);
1498         RtlStringCchCopyW(lfBase.lfFaceName, _countof(lfBase.lfFaceName), pTextObj->TextFace);
1499     }
1500 
1501     // Fix lfCharSet
1502     switch (lfBase.lfCharSet)
1503     {
1504         case ANSI_CHARSET:   bFixCharSet = !s_fFontLinkUseAnsi;    break;
1505         case OEM_CHARSET:    bFixCharSet = !s_fFontLinkUseOem;     break;
1506         case SYMBOL_CHARSET: bFixCharSet = !s_fFontLinkUseSymbol;  break;
1507         default:             bFixCharSet = TRUE;                   break;
1508     }
1509     if (bFixCharSet)
1510         lfBase.lfCharSet = DEFAULT_CHARSET;
1511 
1512     // Use cache if any
1513     pLinkCache = FontLink_FindCache(&lfBase);
1514     if (pLinkCache)
1515     {
1516         RemoveEntryList(&pLinkCache->ListEntry);
1517         *pChain = pLinkCache->Chain;
1518         IntRebaseList(&pChain->FontLinkList, &pLinkCache->Chain.FontLinkList);
1519         ExFreePoolWithTag(pLinkCache, TAG_FONT);
1520         return STATUS_SUCCESS;
1521     }
1522 
1523     pChain->LogFont = lfBase;
1524 
1525     // Load FontLink entry from registry
1526     Status = FontLink_Chain_LoadReg(pChain, &pChain->LogFont);
1527     if (!NT_SUCCESS(Status))
1528         return Status;
1529 
1530     pszLink = pChain->pszzFontLink;
1531     while (*pszLink)
1532     {
1533         DPRINT("pszLink: '%S'\n", pszLink);
1534         pLink = FontLink_Create(pChain, &lfBase, pszLink);
1535         if (pLink)
1536             InsertTailList(&pChain->FontLinkList, &pLink->ListEntry);
1537         pszLink += wcslen(pszLink) + 1;
1538     }
1539 
1540     // Use default settings (if any)
1541     if (s_szDefFontLinkFileName[0] && s_szDefFontLinkFontName[0])
1542     {
1543         RtlStringCchCopyW(szEntry, _countof(szEntry), s_szDefFontLinkFileName);
1544         RtlStringCchCatW(szEntry, _countof(szEntry), L",");
1545         RtlStringCchCatW(szEntry, _countof(szEntry), s_szDefFontLinkFontName);
1546         DPRINT("szEntry: '%S'\n", szEntry);
1547         pLink = FontLink_Create(pChain, &lfBase, szEntry);
1548         if (pLink)
1549             InsertTailList(&pChain->FontLinkList, &pLink->ListEntry);
1550     }
1551 
1552     ASSERT(FontLink_Chain_IsPopulated(pChain));
1553     return Status;
1554 }
1555 
1556 /*
1557  * IntLoadSystemFonts
1558  *
1559  * Search the system font directory and adds each font found.
1560  */
1561 VOID FASTCALL
IntLoadSystemFonts(VOID)1562 IntLoadSystemFonts(VOID)
1563 {
1564     OBJECT_ATTRIBUTES ObjectAttributes;
1565     UNICODE_STRING Directory, FileName, TempString;
1566     IO_STATUS_BLOCK Iosb;
1567     HANDLE hDirectory;
1568     BYTE *DirInfoBuffer;
1569     PFILE_DIRECTORY_INFORMATION DirInfo;
1570     BOOLEAN bRestartScan = TRUE;
1571     NTSTATUS Status;
1572     INT i;
1573     static UNICODE_STRING SearchPatterns[] =
1574     {
1575         RTL_CONSTANT_STRING(L"*.ttf"),
1576         RTL_CONSTANT_STRING(L"*.ttc"),
1577         RTL_CONSTANT_STRING(L"*.otf"),
1578         RTL_CONSTANT_STRING(L"*.otc"),
1579         RTL_CONSTANT_STRING(L"*.fon"),
1580         RTL_CONSTANT_STRING(L"*.fnt")
1581     };
1582     static UNICODE_STRING IgnoreFiles[] =
1583     {
1584         RTL_CONSTANT_STRING(L"."),
1585         RTL_CONSTANT_STRING(L".."),
1586     };
1587 
1588     RtlInitUnicodeString(&Directory, L"\\SystemRoot\\Fonts\\");
1589 
1590     InitializeObjectAttributes(
1591         &ObjectAttributes,
1592         &Directory,
1593         OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
1594         NULL,
1595         NULL);
1596 
1597     Status = ZwOpenFile(
1598                  &hDirectory,
1599                  SYNCHRONIZE | FILE_LIST_DIRECTORY,
1600                  &ObjectAttributes,
1601                  &Iosb,
1602                  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1603                  FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE);
1604 
1605     if (NT_SUCCESS(Status))
1606     {
1607         for (i = 0; i < _countof(SearchPatterns); ++i)
1608         {
1609             DirInfoBuffer = ExAllocatePoolWithTag(PagedPool, 0x4000, TAG_FONT);
1610             if (DirInfoBuffer == NULL)
1611             {
1612                 ZwClose(hDirectory);
1613                 return;
1614             }
1615 
1616             FileName.Buffer = ExAllocatePoolWithTag(PagedPool, MAX_PATH * sizeof(WCHAR), TAG_FONT);
1617             if (FileName.Buffer == NULL)
1618             {
1619                 ExFreePoolWithTag(DirInfoBuffer, TAG_FONT);
1620                 ZwClose(hDirectory);
1621                 return;
1622             }
1623             FileName.Length = 0;
1624             FileName.MaximumLength = MAX_PATH * sizeof(WCHAR);
1625 
1626             while (1)
1627             {
1628                 Status = ZwQueryDirectoryFile(
1629                              hDirectory,
1630                              NULL,
1631                              NULL,
1632                              NULL,
1633                              &Iosb,
1634                              DirInfoBuffer,
1635                              0x4000,
1636                              FileDirectoryInformation,
1637                              FALSE,
1638                              &SearchPatterns[i],
1639                              bRestartScan);
1640 
1641                 if (!NT_SUCCESS(Status) || Status == STATUS_NO_MORE_FILES)
1642                 {
1643                     break;
1644                 }
1645 
1646                 DirInfo = (PFILE_DIRECTORY_INFORMATION)DirInfoBuffer;
1647                 while (1)
1648                 {
1649                     SIZE_T ign;
1650 
1651                     TempString.Buffer = DirInfo->FileName;
1652                     TempString.Length = TempString.MaximumLength = DirInfo->FileNameLength;
1653 
1654                     /* Should we ignore this file? */
1655                     for (ign = 0; ign < _countof(IgnoreFiles); ++ign)
1656                     {
1657                         /* Yes.. */
1658                         if (RtlEqualUnicodeString(IgnoreFiles + ign, &TempString, FALSE))
1659                             break;
1660                     }
1661 
1662                     /* If we tried all Ignore patterns and there was no match, try to create a font */
1663                     if (ign == _countof(IgnoreFiles))
1664                     {
1665                         RtlCopyUnicodeString(&FileName, &Directory);
1666                         RtlAppendUnicodeStringToString(&FileName, &TempString);
1667                         if (!IntGdiAddFontResourceEx(&FileName, 0, AFRX_WRITE_REGISTRY))
1668                         {
1669                             DPRINT1("ERR: Failed to load %wZ\n", &FileName);
1670                         }
1671                     }
1672 
1673                     if (DirInfo->NextEntryOffset == 0)
1674                         break;
1675 
1676                     DirInfo = (PFILE_DIRECTORY_INFORMATION)((ULONG_PTR)DirInfo + DirInfo->NextEntryOffset);
1677                 }
1678 
1679                 bRestartScan = FALSE;
1680             }
1681 
1682             ExFreePoolWithTag(FileName.Buffer, TAG_FONT);
1683             ExFreePoolWithTag(DirInfoBuffer, TAG_FONT);
1684         }
1685         ZwClose(hDirectory);
1686     }
1687 }
1688 
1689 /* NOTE: If nIndex < 0 then return the number of charsets. */
IntGetCharSet(INT nIndex,FT_ULong CodePageRange1)1690 UINT FASTCALL IntGetCharSet(INT nIndex, FT_ULong CodePageRange1)
1691 {
1692     UINT BitIndex, CharSet;
1693     UINT nCount = 0;
1694 
1695     if (CodePageRange1 == 0)
1696     {
1697         return (nIndex < 0) ? 1 : DEFAULT_CHARSET;
1698     }
1699 
1700     for (BitIndex = 0; BitIndex < MAXTCIINDEX; ++BitIndex)
1701     {
1702         if (CodePageRange1 & (1 << BitIndex))
1703         {
1704             CharSet = g_FontTci[BitIndex].ciCharset;
1705             if ((nIndex >= 0) && (nCount == (UINT)nIndex))
1706             {
1707                 return CharSet;
1708             }
1709             ++nCount;
1710         }
1711     }
1712 
1713     return (nIndex < 0) ? nCount : ANSI_CHARSET;
1714 }
1715 
1716 /* pixels to points */
1717 #define PX2PT(pixels) FT_MulDiv((pixels), 72, 96)
1718 
1719 static INT FASTCALL
IntGdiLoadFontsFromMemory(PGDI_LOAD_FONT pLoadFont,PSHARED_FACE SharedFace,FT_Long FontIndex,INT CharSetIndex)1720 IntGdiLoadFontsFromMemory(PGDI_LOAD_FONT pLoadFont,
1721                           PSHARED_FACE SharedFace, FT_Long FontIndex, INT CharSetIndex)
1722 {
1723     FT_Error            Error;
1724     PFONT_ENTRY         Entry;
1725     FONT_ENTRY_MEM*     PrivateEntry = NULL;
1726     FONTGDI *           FontGDI;
1727     NTSTATUS            Status;
1728     FT_Face             Face;
1729     ANSI_STRING         AnsiString;
1730     FT_WinFNT_HeaderRec WinFNT;
1731     INT                 FaceCount = 0, CharSetCount = 0;
1732     PUNICODE_STRING     pFileName       = pLoadFont->pFileName;
1733     DWORD               Characteristics = pLoadFont->Characteristics;
1734     PUNICODE_STRING     pValueName = &pLoadFont->RegValueName;
1735     TT_OS2 *            pOS2;
1736     INT                 BitIndex;
1737     FT_UShort           os2_version;
1738     FT_ULong            os2_ulCodePageRange1;
1739     FT_UShort           os2_usWeightClass;
1740 
1741     ASSERT(SharedFace != NULL);
1742     ASSERT(FontIndex != -1);
1743 
1744     IntLockFreeType();
1745     Face = SharedFace->Face;
1746     SharedFace_AddRef(SharedFace);
1747     IntUnLockFreeType();
1748 
1749     /* allocate a FONT_ENTRY */
1750     Entry = ExAllocatePoolWithTag(PagedPool, sizeof(FONT_ENTRY), TAG_FONT);
1751     if (!Entry)
1752     {
1753         SharedFace_Release(SharedFace);
1754         EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1755         return 0; /* failure */
1756     }
1757 
1758     /* allocate a FONTGDI */
1759     FontGDI = EngAllocMem(FL_ZERO_MEMORY, sizeof(FONTGDI), GDITAG_RFONT);
1760     if (!FontGDI)
1761     {
1762         SharedFace_Release(SharedFace);
1763         ExFreePoolWithTag(Entry, TAG_FONT);
1764         EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1765         return 0; /* failure */
1766     }
1767 
1768     /* set file name */
1769     if (pFileName)
1770     {
1771         FontGDI->Filename = ExAllocatePoolWithTag(PagedPool,
1772                                                   pFileName->Length + sizeof(UNICODE_NULL),
1773                                                   GDITAG_PFF);
1774         if (FontGDI->Filename == NULL)
1775         {
1776             EngFreeMem(FontGDI);
1777             SharedFace_Release(SharedFace);
1778             ExFreePoolWithTag(Entry, TAG_FONT);
1779             EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1780             return 0; /* failure */
1781         }
1782 
1783         RtlCopyMemory(FontGDI->Filename, pFileName->Buffer, pFileName->Length);
1784         FontGDI->Filename[pFileName->Length / sizeof(WCHAR)] = UNICODE_NULL;
1785     }
1786     else
1787     {
1788         FontGDI->Filename = NULL;
1789 
1790         PrivateEntry = ExAllocatePoolWithTag(PagedPool, sizeof(FONT_ENTRY_MEM), TAG_FONT);
1791         if (!PrivateEntry)
1792         {
1793             if (FontGDI->Filename)
1794                 ExFreePoolWithTag(FontGDI->Filename, GDITAG_PFF);
1795             EngFreeMem(FontGDI);
1796             SharedFace_Release(SharedFace);
1797             ExFreePoolWithTag(Entry, TAG_FONT);
1798             EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1799             return 0; /* failure */
1800         }
1801 
1802         PrivateEntry->Entry = Entry;
1803         if (pLoadFont->PrivateEntry)
1804         {
1805             InsertTailList(&pLoadFont->PrivateEntry->ListEntry, &PrivateEntry->ListEntry);
1806         }
1807         else
1808         {
1809             InitializeListHead(&PrivateEntry->ListEntry);
1810             pLoadFont->PrivateEntry = PrivateEntry;
1811         }
1812     }
1813 
1814     /* set face */
1815     FontGDI->SharedFace = SharedFace;
1816     FontGDI->CharSet = ANSI_CHARSET;
1817     FontGDI->OriginalItalic = FALSE;
1818     FontGDI->RequestItalic = FALSE;
1819     FontGDI->OriginalWeight = FALSE;
1820     FontGDI->RequestWeight = FW_NORMAL;
1821 
1822     IntLockFreeType();
1823     pOS2 = (TT_OS2 *)FT_Get_Sfnt_Table(Face, FT_SFNT_OS2);
1824     if (pOS2)
1825     {
1826         FontGDI->OriginalItalic = !!(pOS2->fsSelection & 0x1);
1827         FontGDI->OriginalWeight = pOS2->usWeightClass;
1828     }
1829     else
1830     {
1831         Error = FT_Get_WinFNT_Header(Face, &WinFNT);
1832         if (!Error)
1833         {
1834             FontGDI->OriginalItalic = !!WinFNT.italic;
1835             FontGDI->OriginalWeight = WinFNT.weight;
1836         }
1837     }
1838     IntUnLockFreeType();
1839 
1840     RtlInitAnsiString(&AnsiString, Face->family_name);
1841     Status = RtlAnsiStringToUnicodeString(&Entry->FaceName, &AnsiString, TRUE);
1842     if (NT_SUCCESS(Status))
1843     {
1844         if (Face->style_name && Face->style_name[0] &&
1845             strcmp(Face->style_name, "Regular") != 0)
1846         {
1847             RtlInitAnsiString(&AnsiString, Face->style_name);
1848             Status = RtlAnsiStringToUnicodeString(&Entry->StyleName, &AnsiString, TRUE);
1849             if (!NT_SUCCESS(Status))
1850             {
1851                 RtlFreeUnicodeString(&Entry->FaceName);
1852             }
1853         }
1854         else
1855         {
1856             RtlInitUnicodeString(&Entry->StyleName, NULL);
1857         }
1858     }
1859     if (!NT_SUCCESS(Status))
1860     {
1861         if (PrivateEntry)
1862         {
1863             if (pLoadFont->PrivateEntry == PrivateEntry)
1864             {
1865                 pLoadFont->PrivateEntry = NULL;
1866             }
1867             else
1868             {
1869                 RemoveEntryList(&PrivateEntry->ListEntry);
1870             }
1871             ExFreePoolWithTag(PrivateEntry, TAG_FONT);
1872         }
1873         if (FontGDI->Filename)
1874             ExFreePoolWithTag(FontGDI->Filename, GDITAG_PFF);
1875         EngFreeMem(FontGDI);
1876         SharedFace_Release(SharedFace);
1877         ExFreePoolWithTag(Entry, TAG_FONT);
1878         return 0;
1879     }
1880 
1881     os2_version = 0;
1882     IntLockFreeType();
1883     pOS2 = (TT_OS2 *)FT_Get_Sfnt_Table(Face, FT_SFNT_OS2);
1884     if (pOS2)
1885     {
1886         os2_version = pOS2->version;
1887         os2_ulCodePageRange1 = pOS2->ulCodePageRange1;
1888         os2_usWeightClass = pOS2->usWeightClass;
1889     }
1890     IntUnLockFreeType();
1891 
1892     if (pOS2 && os2_version >= 1)
1893     {
1894         /* get charset and weight from OS/2 header */
1895 
1896         /* Make sure we do not use this pointer anymore */
1897         pOS2 = NULL;
1898 
1899         for (BitIndex = 0; BitIndex < MAXTCIINDEX; ++BitIndex)
1900         {
1901             if (os2_ulCodePageRange1 & (1 << BitIndex))
1902             {
1903                 if (g_FontTci[BitIndex].ciCharset == DEFAULT_CHARSET)
1904                     continue;
1905 
1906                 if ((CharSetIndex == -1 && CharSetCount == 0) ||
1907                     CharSetIndex == CharSetCount)
1908                 {
1909                     FontGDI->CharSet = g_FontTci[BitIndex].ciCharset;
1910                 }
1911 
1912                 ++CharSetCount;
1913             }
1914         }
1915 
1916         /* set actual weight */
1917         FontGDI->OriginalWeight = os2_usWeightClass;
1918     }
1919     else
1920     {
1921         /* get charset from WinFNT header */
1922         IntLockFreeType();
1923         Error = FT_Get_WinFNT_Header(Face, &WinFNT);
1924         if (!Error)
1925         {
1926             FontGDI->CharSet = WinFNT.charset;
1927         }
1928         IntUnLockFreeType();
1929     }
1930 
1931     ++FaceCount;
1932     DPRINT("Font loaded: %s (%s)\n",
1933            Face->family_name ? Face->family_name : "<NULL>",
1934            Face->style_name ? Face->style_name : "<NULL>");
1935     DPRINT("Num glyphs: %d\n", Face->num_glyphs);
1936     DPRINT("CharSet: %d\n", FontGDI->CharSet);
1937 
1938     /* Add this font resource to the font table */
1939     Entry->Font = FontGDI;
1940     Entry->NotEnum = (Characteristics & FR_NOT_ENUM);
1941 
1942     IntLockFreeType();
1943     if (Characteristics & FR_PRIVATE)
1944     {
1945         /* private font */
1946         PPROCESSINFO Win32Process = PsGetCurrentProcessWin32Process();
1947         IntLockProcessPrivateFonts(Win32Process);
1948         InsertTailList(&Win32Process->PrivateFontListHead, &Entry->ListEntry);
1949         IntUnLockProcessPrivateFonts(Win32Process);
1950     }
1951     else
1952     {
1953         /* global font */
1954         InsertTailList(&g_FontListHead, &Entry->ListEntry);
1955     }
1956     IntUnLockFreeType();
1957 
1958     if (CharSetIndex == -1)
1959     {
1960         INT i;
1961         USHORT NameLength = Entry->FaceName.Length;
1962 
1963         if (Entry->StyleName.Length)
1964             NameLength += Entry->StyleName.Length + sizeof(WCHAR);
1965 
1966         if (pLoadFont->RegValueName.Length == 0)
1967         {
1968             pValueName->Length = 0;
1969             pValueName->MaximumLength = NameLength + sizeof(WCHAR);
1970             pValueName->Buffer = ExAllocatePoolWithTag(PagedPool,
1971                                                        pValueName->MaximumLength,
1972                                                        TAG_USTR);
1973             pValueName->Buffer[0] = UNICODE_NULL;
1974             RtlAppendUnicodeStringToString(pValueName, &Entry->FaceName);
1975         }
1976         else
1977         {
1978             UNICODE_STRING NewString;
1979             USHORT Length = pValueName->Length + 3 * sizeof(WCHAR) + NameLength;
1980             NewString.Length = 0;
1981             NewString.MaximumLength = Length + sizeof(WCHAR);
1982             NewString.Buffer = ExAllocatePoolWithTag(PagedPool,
1983                                                      NewString.MaximumLength,
1984                                                      TAG_USTR);
1985             NewString.Buffer[0] = UNICODE_NULL;
1986 
1987             RtlAppendUnicodeStringToString(&NewString, pValueName);
1988             RtlAppendUnicodeToString(&NewString, L" & ");
1989             RtlAppendUnicodeStringToString(&NewString, &Entry->FaceName);
1990 
1991             RtlFreeUnicodeString(pValueName);
1992             *pValueName = NewString;
1993         }
1994         if (Entry->StyleName.Length)
1995         {
1996             RtlAppendUnicodeToString(pValueName, L" ");
1997             RtlAppendUnicodeStringToString(pValueName, &Entry->StyleName);
1998         }
1999 
2000         for (i = 1; i < CharSetCount; ++i)
2001         {
2002             /* Do not count charsets as loaded 'faces' */
2003             IntGdiLoadFontsFromMemory(pLoadFont, SharedFace, FontIndex, i);
2004         }
2005     }
2006 
2007     return FaceCount; /* Number of loaded faces */
2008 }
2009 
2010 static INT FASTCALL
IntGdiLoadFontByIndexFromMemory(PGDI_LOAD_FONT pLoadFont,FT_Long FontIndex)2011 IntGdiLoadFontByIndexFromMemory(PGDI_LOAD_FONT pLoadFont, FT_Long FontIndex)
2012 {
2013     FT_Error Error;
2014     FT_Face Face;
2015     FT_Long iFace, num_faces;
2016     PSHARED_FACE SharedFace;
2017     INT FaceCount = 0;
2018 
2019     IntLockFreeType();
2020 
2021     /* Load a face from memory */
2022     Error = FT_New_Memory_Face(g_FreeTypeLibrary,
2023                                pLoadFont->Memory->Buffer, pLoadFont->Memory->BufferSize,
2024                                ((FontIndex == -1) ? 0 : FontIndex), &Face);
2025     if (Error)
2026     {
2027         if (Error == FT_Err_Unknown_File_Format)
2028             DPRINT1("Unknown font file format\n");
2029         else
2030             DPRINT1("Error reading font (error code: %d)\n", Error);
2031         IntUnLockFreeType();
2032         return 0; /* Failure */
2033     }
2034 
2035     pLoadFont->IsTrueType = FT_IS_SFNT(Face);
2036     num_faces = Face->num_faces;
2037     SharedFace = SharedFace_Create(Face, pLoadFont->Memory);
2038 
2039     IntUnLockFreeType();
2040 
2041     if (!SharedFace)
2042     {
2043         DPRINT1("SharedFace_Create failed\n");
2044         EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
2045         return 0; /* Failure */
2046     }
2047 
2048     if (FontIndex == -1)
2049     {
2050         for (iFace = 1; iFace < num_faces; ++iFace)
2051         {
2052             FaceCount += IntGdiLoadFontByIndexFromMemory(pLoadFont, iFace);
2053         }
2054         FontIndex = 0;
2055     }
2056 
2057     FaceCount += IntGdiLoadFontsFromMemory(pLoadFont, SharedFace, FontIndex, -1);
2058     return FaceCount;
2059 }
2060 
2061 static LPCWSTR FASTCALL
NameFromCharSet(BYTE CharSet)2062 NameFromCharSet(BYTE CharSet)
2063 {
2064     switch (CharSet)
2065     {
2066         case ANSI_CHARSET: return L"ANSI";
2067         case DEFAULT_CHARSET: return L"Default";
2068         case SYMBOL_CHARSET: return L"Symbol";
2069         case SHIFTJIS_CHARSET: return L"Shift_JIS";
2070         case HANGUL_CHARSET: return L"Hangul";
2071         case GB2312_CHARSET: return L"GB 2312";
2072         case CHINESEBIG5_CHARSET: return L"Chinese Big5";
2073         case OEM_CHARSET: return L"OEM";
2074         case JOHAB_CHARSET: return L"Johab";
2075         case HEBREW_CHARSET: return L"Hebrew";
2076         case ARABIC_CHARSET: return L"Arabic";
2077         case GREEK_CHARSET: return L"Greek";
2078         case TURKISH_CHARSET: return L"Turkish";
2079         case VIETNAMESE_CHARSET: return L"Vietnamese";
2080         case THAI_CHARSET: return L"Thai";
2081         case EASTEUROPE_CHARSET: return L"Eastern European";
2082         case RUSSIAN_CHARSET: return L"Russian";
2083         case MAC_CHARSET: return L"Mac";
2084         case BALTIC_CHARSET: return L"Baltic";
2085         default: return L"Unknown";
2086     }
2087 }
2088 
2089 /*
2090  * IntGdiAddFontResource
2091  *
2092  * Adds the font resource from the specified file to the system.
2093  */
2094 
2095 INT FASTCALL
IntGdiAddFontResourceEx(PUNICODE_STRING FileName,DWORD Characteristics,DWORD dwFlags)2096 IntGdiAddFontResourceEx(PUNICODE_STRING FileName, DWORD Characteristics,
2097                         DWORD dwFlags)
2098 {
2099     NTSTATUS Status;
2100     HANDLE FileHandle;
2101     PVOID Buffer = NULL;
2102     IO_STATUS_BLOCK Iosb;
2103     PVOID SectionObject;
2104     SIZE_T ViewSize = 0, Length;
2105     LARGE_INTEGER SectionSize;
2106     OBJECT_ATTRIBUTES ObjectAttributes;
2107     GDI_LOAD_FONT LoadFont;
2108     INT FontCount;
2109     HANDLE KeyHandle;
2110     UNICODE_STRING PathName;
2111     LPWSTR pszBuffer;
2112     PFILE_OBJECT FileObject;
2113     static const UNICODE_STRING TrueTypePostfix = RTL_CONSTANT_STRING(L" (TrueType)");
2114     static const UNICODE_STRING DosPathPrefix = RTL_CONSTANT_STRING(L"\\??\\");
2115 
2116     /* Build PathName */
2117     if (dwFlags & AFRX_DOS_DEVICE_PATH)
2118     {
2119         Length = DosPathPrefix.Length + FileName->Length + sizeof(UNICODE_NULL);
2120         pszBuffer = ExAllocatePoolWithTag(PagedPool, Length, TAG_USTR);
2121         if (!pszBuffer)
2122             return 0;   /* failure */
2123 
2124         RtlInitEmptyUnicodeString(&PathName, pszBuffer, Length);
2125         RtlAppendUnicodeStringToString(&PathName, &DosPathPrefix);
2126         RtlAppendUnicodeStringToString(&PathName, FileName);
2127     }
2128     else
2129     {
2130         Status = DuplicateUnicodeString(FileName, &PathName);
2131         if (!NT_SUCCESS(Status))
2132             return 0;   /* failure */
2133     }
2134 
2135     /* Open the font file */
2136     InitializeObjectAttributes(&ObjectAttributes, &PathName,
2137                                OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);
2138     Status = ZwOpenFile(
2139                  &FileHandle,
2140                  FILE_GENERIC_READ | SYNCHRONIZE,
2141                  &ObjectAttributes,
2142                  &Iosb,
2143                  FILE_SHARE_READ,
2144                  FILE_SYNCHRONOUS_IO_NONALERT);
2145     if (!NT_SUCCESS(Status))
2146     {
2147         DPRINT1("Could not load font file: %wZ\n", &PathName);
2148         RtlFreeUnicodeString(&PathName);
2149         return 0;
2150     }
2151 
2152     Status = ObReferenceObjectByHandle(FileHandle, FILE_READ_DATA, NULL,
2153                                        KernelMode, (PVOID*)&FileObject, NULL);
2154     if (!NT_SUCCESS(Status))
2155     {
2156         DPRINT1("ObReferenceObjectByHandle failed.\n");
2157         ZwClose(FileHandle);
2158         RtlFreeUnicodeString(&PathName);
2159         return 0;
2160     }
2161 
2162     SectionSize.QuadPart = 0LL;
2163     Status = MmCreateSection(&SectionObject,
2164                              STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_READ,
2165                              NULL, &SectionSize, PAGE_READONLY,
2166                              SEC_COMMIT, FileHandle, FileObject);
2167     if (!NT_SUCCESS(Status))
2168     {
2169         DPRINT1("Could not map file: %wZ\n", &PathName);
2170         ZwClose(FileHandle);
2171         ObDereferenceObject(FileObject);
2172         RtlFreeUnicodeString(&PathName);
2173         return 0;
2174     }
2175     ZwClose(FileHandle);
2176 
2177     Status = MmMapViewInSystemSpace(SectionObject, &Buffer, &ViewSize);
2178     if (!NT_SUCCESS(Status))
2179     {
2180         DPRINT1("Could not map file: %wZ\n", &PathName);
2181         ObDereferenceObject(SectionObject);
2182         ObDereferenceObject(FileObject);
2183         RtlFreeUnicodeString(&PathName);
2184         return 0;
2185     }
2186 
2187     RtlZeroMemory(&LoadFont, sizeof(LoadFont));
2188     LoadFont.pFileName          = &PathName;
2189     LoadFont.Memory             = SharedMem_Create(Buffer, ViewSize, TRUE);
2190     LoadFont.Characteristics    = Characteristics;
2191     RtlInitUnicodeString(&LoadFont.RegValueName, NULL);
2192     LoadFont.CharSet            = DEFAULT_CHARSET;
2193     FontCount = IntGdiLoadFontByIndexFromMemory(&LoadFont, -1);
2194 
2195     /* Release our copy */
2196     IntLockFreeType();
2197     SharedMem_Release(LoadFont.Memory);
2198     IntUnLockFreeType();
2199 
2200     ObDereferenceObject(SectionObject);
2201 
2202     ObDereferenceObject(FileObject);
2203 
2204     /* Save the loaded font name into the registry */
2205     if (FontCount > 0 && (dwFlags & AFRX_WRITE_REGISTRY))
2206     {
2207         UNICODE_STRING NewString;
2208         SIZE_T Length;
2209         PWCHAR pszBuffer;
2210         LPCWSTR CharSetName;
2211         if (LoadFont.IsTrueType)
2212         {
2213             /* Append " (TrueType)" */
2214             Length = LoadFont.RegValueName.Length + TrueTypePostfix.Length + sizeof(UNICODE_NULL);
2215             pszBuffer = ExAllocatePoolWithTag(PagedPool, Length, TAG_USTR);
2216             if (pszBuffer)
2217             {
2218                 RtlInitEmptyUnicodeString(&NewString, pszBuffer, Length);
2219                 NewString.Buffer[0] = UNICODE_NULL;
2220                 RtlAppendUnicodeStringToString(&NewString, &LoadFont.RegValueName);
2221                 RtlAppendUnicodeStringToString(&NewString, &TrueTypePostfix);
2222                 RtlFreeUnicodeString(&LoadFont.RegValueName);
2223                 LoadFont.RegValueName = NewString;
2224             }
2225             else
2226             {
2227                 // FIXME!
2228             }
2229         }
2230         else if (LoadFont.CharSet != DEFAULT_CHARSET)
2231         {
2232             /* Append " (CharSetName)" */
2233             CharSetName = NameFromCharSet(LoadFont.CharSet);
2234             Length = LoadFont.RegValueName.Length +
2235                      (wcslen(CharSetName) + 3) * sizeof(WCHAR) +
2236                      sizeof(UNICODE_NULL);
2237 
2238             pszBuffer = ExAllocatePoolWithTag(PagedPool, Length, TAG_USTR);
2239             if (pszBuffer)
2240             {
2241                 RtlInitEmptyUnicodeString(&NewString, pszBuffer, Length);
2242                 NewString.Buffer[0] = UNICODE_NULL;
2243                 RtlAppendUnicodeStringToString(&NewString, &LoadFont.RegValueName);
2244                 RtlAppendUnicodeToString(&NewString, L" (");
2245                 RtlAppendUnicodeToString(&NewString, CharSetName);
2246                 RtlAppendUnicodeToString(&NewString, L")");
2247                 RtlFreeUnicodeString(&LoadFont.RegValueName);
2248                 LoadFont.RegValueName = NewString;
2249             }
2250             else
2251             {
2252                 // FIXME!
2253             }
2254         }
2255 
2256         InitializeObjectAttributes(&ObjectAttributes, &g_FontRegPath,
2257                                    OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
2258                                    NULL, NULL);
2259         Status = ZwOpenKey(&KeyHandle, KEY_WRITE, &ObjectAttributes);
2260         if (NT_SUCCESS(Status))
2261         {
2262             SIZE_T DataSize;
2263             LPWSTR pFileName;
2264 
2265             if (dwFlags & AFRX_ALTERNATIVE_PATH)
2266             {
2267                 pFileName = PathName.Buffer;
2268             }
2269             else
2270             {
2271                 pFileName = wcsrchr(PathName.Buffer, L'\\');
2272             }
2273 
2274             if (pFileName)
2275             {
2276                 if (!(dwFlags & AFRX_ALTERNATIVE_PATH))
2277                 {
2278                     pFileName++;
2279                 }
2280                 DataSize = (wcslen(pFileName) + 1) * sizeof(WCHAR);
2281                 ZwSetValueKey(KeyHandle, &LoadFont.RegValueName, 0, REG_SZ,
2282                               pFileName, DataSize);
2283             }
2284             ZwClose(KeyHandle);
2285         }
2286     }
2287     RtlFreeUnicodeString(&LoadFont.RegValueName);
2288 
2289     RtlFreeUnicodeString(&PathName);
2290     return FontCount;
2291 }
2292 
2293 INT FASTCALL
IntGdiAddFontResource(PUNICODE_STRING FileName,DWORD Characteristics)2294 IntGdiAddFontResource(PUNICODE_STRING FileName, DWORD Characteristics)
2295 {
2296     return IntGdiAddFontResourceEx(FileName, Characteristics, 0);
2297 }
2298 
2299 /* Borrowed from shlwapi!PathIsRelativeW */
PathIsRelativeW(LPCWSTR lpszPath)2300 BOOL WINAPI PathIsRelativeW(LPCWSTR lpszPath)
2301 {
2302     if (!lpszPath || !*lpszPath)
2303         return TRUE;
2304     if (*lpszPath == L'\\' || (*lpszPath && lpszPath[1] == L':'))
2305         return FALSE;
2306     return TRUE;
2307 }
2308 
2309 BOOL FASTCALL
IntLoadFontsInRegistry(VOID)2310 IntLoadFontsInRegistry(VOID)
2311 {
2312     NTSTATUS                        Status;
2313     HANDLE                          KeyHandle;
2314     OBJECT_ATTRIBUTES               ObjectAttributes;
2315     KEY_FULL_INFORMATION            KeyFullInfo;
2316     ULONG                           i, Length;
2317     UNICODE_STRING                  FontTitleW, FileNameW;
2318     SIZE_T                          InfoSize;
2319     LPBYTE                          InfoBuffer;
2320     PKEY_VALUE_FULL_INFORMATION     pInfo;
2321     LPWSTR                          pchPath;
2322     WCHAR                           szPath[MAX_PATH];
2323     INT                             nFontCount = 0;
2324     DWORD                           dwFlags;
2325 
2326     /* open registry key */
2327     InitializeObjectAttributes(&ObjectAttributes, &g_FontRegPath,
2328                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
2329                                NULL, NULL);
2330     Status = ZwOpenKey(&KeyHandle, KEY_READ, &ObjectAttributes);
2331     if (!NT_SUCCESS(Status))
2332     {
2333         DPRINT1("ZwOpenKey failed: 0x%08X\n", Status);
2334         return FALSE;   /* failure */
2335     }
2336 
2337     /* query count of values */
2338     Status = ZwQueryKey(KeyHandle, KeyFullInformation,
2339                         &KeyFullInfo, sizeof(KeyFullInfo), &Length);
2340     if (!NT_SUCCESS(Status))
2341     {
2342         DPRINT1("ZwQueryKey failed: 0x%08X\n", Status);
2343         ZwClose(KeyHandle);
2344         return FALSE;   /* failure */
2345     }
2346 
2347     /* allocate buffer */
2348     InfoSize = (MAX_PATH + 256) * sizeof(WCHAR);
2349     InfoBuffer = ExAllocatePoolWithTag(PagedPool, InfoSize, TAG_FONT);
2350     if (!InfoBuffer)
2351     {
2352         DPRINT1("ExAllocatePoolWithTag failed\n");
2353         ZwClose(KeyHandle);
2354         return FALSE;
2355     }
2356 
2357     /* for each value */
2358     for (i = 0; i < KeyFullInfo.Values; ++i)
2359     {
2360         /* get value name */
2361         Status = ZwEnumerateValueKey(KeyHandle, i, KeyValueFullInformation,
2362                                      InfoBuffer, InfoSize, &Length);
2363         if (Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_BUFFER_TOO_SMALL)
2364         {
2365             /* too short buffer */
2366             ExFreePoolWithTag(InfoBuffer, TAG_FONT);
2367             InfoSize *= 2;
2368             InfoBuffer = ExAllocatePoolWithTag(PagedPool, InfoSize, TAG_FONT);
2369             if (!InfoBuffer)
2370             {
2371                 DPRINT1("ExAllocatePoolWithTag failed\n");
2372                 break;
2373             }
2374             /* try again */
2375             Status = ZwEnumerateValueKey(KeyHandle, i, KeyValueFullInformation,
2376                                          InfoBuffer, InfoSize, &Length);
2377         }
2378         if (!NT_SUCCESS(Status))
2379         {
2380             DPRINT1("ZwEnumerateValueKey failed: 0x%08X\n", Status);
2381             break;      /* failure */
2382         }
2383 
2384         /* create FontTitleW string */
2385         pInfo = (PKEY_VALUE_FULL_INFORMATION)InfoBuffer;
2386         Length = pInfo->NameLength / sizeof(WCHAR);
2387         pInfo->Name[Length] = UNICODE_NULL;   /* truncate */
2388         if (!RtlCreateUnicodeString(&FontTitleW, pInfo->Name))
2389         {
2390             Status = STATUS_INSUFFICIENT_RESOURCES;
2391             DPRINT1("RtlCreateUnicodeString failed\n");
2392             break;      /* failure */
2393         }
2394 
2395         /* query value */
2396         Status = ZwQueryValueKey(KeyHandle, &FontTitleW, KeyValueFullInformation,
2397                                  InfoBuffer, InfoSize, &Length);
2398         if (Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_BUFFER_TOO_SMALL)
2399         {
2400             /* too short buffer */
2401             ExFreePoolWithTag(InfoBuffer, TAG_FONT);
2402             InfoSize *= 2;
2403             InfoBuffer = ExAllocatePoolWithTag(PagedPool, InfoSize, TAG_FONT);
2404             if (!InfoBuffer)
2405             {
2406                 DPRINT1("ExAllocatePoolWithTag failed\n");
2407                 break;
2408             }
2409             /* try again */
2410             Status = ZwQueryValueKey(KeyHandle, &FontTitleW, KeyValueFullInformation,
2411                                      InfoBuffer, InfoSize, &Length);
2412         }
2413         pInfo = (PKEY_VALUE_FULL_INFORMATION)InfoBuffer;
2414         if (!NT_SUCCESS(Status) || !pInfo->DataLength)
2415         {
2416             DPRINT1("ZwQueryValueKey failed: 0x%08X\n", Status);
2417             RtlFreeUnicodeString(&FontTitleW);
2418             break;      /* failure */
2419         }
2420 
2421         /* Build pchPath */
2422         pchPath = (LPWSTR)((PUCHAR)pInfo + pInfo->DataOffset);
2423         Length = pInfo->DataLength / sizeof(WCHAR);
2424         pchPath[Length] = UNICODE_NULL; /* truncate */
2425 
2426         /* Load font(s) without writing registry */
2427         if (PathIsRelativeW(pchPath))
2428         {
2429             dwFlags = 0;
2430             Status = RtlStringCbPrintfW(szPath, sizeof(szPath),
2431                                         L"\\SystemRoot\\Fonts\\%s", pchPath);
2432         }
2433         else
2434         {
2435             dwFlags = AFRX_ALTERNATIVE_PATH | AFRX_DOS_DEVICE_PATH;
2436             Status = RtlStringCbCopyW(szPath, sizeof(szPath), pchPath);
2437         }
2438 
2439         if (NT_SUCCESS(Status))
2440         {
2441             RtlCreateUnicodeString(&FileNameW, szPath);
2442             nFontCount += IntGdiAddFontResourceEx(&FileNameW, 0, dwFlags);
2443             RtlFreeUnicodeString(&FileNameW);
2444         }
2445 
2446         RtlFreeUnicodeString(&FontTitleW);
2447     }
2448 
2449     /* close now */
2450     ZwClose(KeyHandle);
2451 
2452     /* free memory block */
2453     if (InfoBuffer)
2454     {
2455         ExFreePoolWithTag(InfoBuffer, TAG_FONT);
2456     }
2457 
2458     return (KeyFullInfo.Values != 0 && nFontCount != 0);
2459 }
2460 
2461 HANDLE FASTCALL
IntGdiAddFontMemResource(PVOID Buffer,DWORD dwSize,PDWORD pNumAdded)2462 IntGdiAddFontMemResource(PVOID Buffer, DWORD dwSize, PDWORD pNumAdded)
2463 {
2464     HANDLE Ret = NULL;
2465     GDI_LOAD_FONT LoadFont;
2466     PFONT_ENTRY_COLL_MEM EntryCollection;
2467     INT FaceCount;
2468 
2469     PVOID BufferCopy = ExAllocatePoolWithTag(PagedPool, dwSize, TAG_FONT);
2470     if (!BufferCopy)
2471     {
2472         *pNumAdded = 0;
2473         return NULL;
2474     }
2475     RtlCopyMemory(BufferCopy, Buffer, dwSize);
2476 
2477     RtlZeroMemory(&LoadFont, sizeof(LoadFont));
2478     LoadFont.Memory             = SharedMem_Create(BufferCopy, dwSize, FALSE);
2479     LoadFont.Characteristics    = FR_PRIVATE | FR_NOT_ENUM;
2480     RtlInitUnicodeString(&LoadFont.RegValueName, NULL);
2481     FaceCount = IntGdiLoadFontByIndexFromMemory(&LoadFont, -1);
2482 
2483     RtlFreeUnicodeString(&LoadFont.RegValueName);
2484 
2485     /* Release our copy */
2486     IntLockFreeType();
2487     SharedMem_Release(LoadFont.Memory);
2488     IntUnLockFreeType();
2489 
2490     if (FaceCount > 0)
2491     {
2492         EntryCollection = ExAllocatePoolWithTag(PagedPool, sizeof(FONT_ENTRY_COLL_MEM), TAG_FONT);
2493         if (EntryCollection)
2494         {
2495             PPROCESSINFO Win32Process = PsGetCurrentProcessWin32Process();
2496             EntryCollection->Entry = LoadFont.PrivateEntry;
2497             IntLockFreeType();
2498             IntLockProcessPrivateFonts(Win32Process);
2499             EntryCollection->Handle = ULongToHandle(++Win32Process->PrivateMemFontHandleCount);
2500             InsertTailList(&Win32Process->PrivateMemFontListHead, &EntryCollection->ListEntry);
2501             IntUnLockProcessPrivateFonts(Win32Process);
2502             IntUnLockFreeType();
2503             Ret = EntryCollection->Handle;
2504         }
2505     }
2506     *pNumAdded = FaceCount;
2507 
2508     return Ret;
2509 }
2510 
2511 // FIXME: Add RemoveFontResource
2512 
2513 VOID FASTCALL
IntGdiCleanupMemEntry(PFONT_ENTRY_MEM Head)2514 IntGdiCleanupMemEntry(PFONT_ENTRY_MEM Head)
2515 {
2516     PLIST_ENTRY Entry;
2517     PFONT_ENTRY_MEM FontEntry;
2518 
2519     while (!IsListEmpty(&Head->ListEntry))
2520     {
2521         Entry = RemoveHeadList(&Head->ListEntry);
2522         FontEntry = CONTAINING_RECORD(Entry, FONT_ENTRY_MEM, ListEntry);
2523 
2524         CleanupFontEntry(FontEntry->Entry);
2525         ExFreePoolWithTag(FontEntry, TAG_FONT);
2526     }
2527 
2528     CleanupFontEntry(Head->Entry);
2529     ExFreePoolWithTag(Head, TAG_FONT);
2530 }
2531 
2532 static VOID FASTCALL
UnlinkFontMemCollection(PFONT_ENTRY_COLL_MEM Collection)2533 UnlinkFontMemCollection(PFONT_ENTRY_COLL_MEM Collection)
2534 {
2535     PFONT_ENTRY_MEM FontMemEntry = Collection->Entry;
2536     PLIST_ENTRY ListEntry;
2537     RemoveEntryList(&Collection->ListEntry);
2538 
2539     do {
2540         /* Also unlink the FONT_ENTRY stuff from the PrivateFontListHead */
2541         RemoveEntryList(&FontMemEntry->Entry->ListEntry);
2542 
2543         ListEntry = FontMemEntry->ListEntry.Flink;
2544         FontMemEntry = CONTAINING_RECORD(ListEntry, FONT_ENTRY_MEM, ListEntry);
2545 
2546     } while (FontMemEntry != Collection->Entry);
2547 }
2548 
2549 BOOL FASTCALL
IntGdiRemoveFontMemResource(HANDLE hMMFont)2550 IntGdiRemoveFontMemResource(HANDLE hMMFont)
2551 {
2552     PLIST_ENTRY Entry;
2553     PFONT_ENTRY_COLL_MEM CurrentEntry;
2554     PFONT_ENTRY_COLL_MEM EntryCollection = NULL;
2555     PPROCESSINFO Win32Process = PsGetCurrentProcessWin32Process();
2556 
2557     IntLockFreeType();
2558     IntLockProcessPrivateFonts(Win32Process);
2559     for (Entry = Win32Process->PrivateMemFontListHead.Flink;
2560          Entry != &Win32Process->PrivateMemFontListHead;
2561          Entry = Entry->Flink)
2562     {
2563         CurrentEntry = CONTAINING_RECORD(Entry, FONT_ENTRY_COLL_MEM, ListEntry);
2564 
2565         if (CurrentEntry->Handle == hMMFont)
2566         {
2567             EntryCollection = CurrentEntry;
2568             UnlinkFontMemCollection(CurrentEntry);
2569             break;
2570         }
2571     }
2572     IntUnLockProcessPrivateFonts(Win32Process);
2573     IntUnLockFreeType();
2574 
2575     if (EntryCollection)
2576     {
2577         IntGdiCleanupMemEntry(EntryCollection->Entry);
2578         ExFreePoolWithTag(EntryCollection, TAG_FONT);
2579         return TRUE;
2580     }
2581     return FALSE;
2582 }
2583 
2584 
2585 VOID FASTCALL
IntGdiCleanupPrivateFontsForProcess(VOID)2586 IntGdiCleanupPrivateFontsForProcess(VOID)
2587 {
2588     PPROCESSINFO Win32Process = PsGetCurrentProcessWin32Process();
2589     PLIST_ENTRY Entry;
2590     PFONT_ENTRY_COLL_MEM EntryCollection;
2591 
2592     DPRINT("IntGdiCleanupPrivateFontsForProcess()\n");
2593     do {
2594         Entry = NULL;
2595         EntryCollection = NULL;
2596 
2597         IntLockFreeType();
2598         IntLockProcessPrivateFonts(Win32Process);
2599         if (!IsListEmpty(&Win32Process->PrivateMemFontListHead))
2600         {
2601             Entry = Win32Process->PrivateMemFontListHead.Flink;
2602             EntryCollection = CONTAINING_RECORD(Entry, FONT_ENTRY_COLL_MEM, ListEntry);
2603             UnlinkFontMemCollection(EntryCollection);
2604         }
2605         IntUnLockProcessPrivateFonts(Win32Process);
2606         IntUnLockFreeType();
2607 
2608         if (EntryCollection)
2609         {
2610             IntGdiCleanupMemEntry(EntryCollection->Entry);
2611             ExFreePoolWithTag(EntryCollection, TAG_FONT);
2612         }
2613         else
2614         {
2615             /* No Mem fonts anymore, see if we have any other private fonts left */
2616             Entry = NULL;
2617             IntLockFreeType();
2618             IntLockProcessPrivateFonts(Win32Process);
2619             if (!IsListEmpty(&Win32Process->PrivateFontListHead))
2620             {
2621                 Entry = RemoveHeadList(&Win32Process->PrivateFontListHead);
2622             }
2623             IntUnLockProcessPrivateFonts(Win32Process);
2624             IntUnLockFreeType();
2625 
2626             if (Entry)
2627             {
2628                 CleanupFontEntry(CONTAINING_RECORD(Entry, FONT_ENTRY, ListEntry));
2629             }
2630         }
2631 
2632     } while (Entry);
2633 }
2634 
2635 BOOL FASTCALL
IntIsFontRenderingEnabled(VOID)2636 IntIsFontRenderingEnabled(VOID)
2637 {
2638     return (gpsi->BitsPixel > 8) && g_RenderingEnabled;
2639 }
2640 
2641 VOID FASTCALL
IntEnableFontRendering(BOOL Enable)2642 IntEnableFontRendering(BOOL Enable)
2643 {
2644     g_RenderingEnabled = Enable;
2645 }
2646 
2647 FT_Render_Mode FASTCALL
IntGetFontRenderMode(LOGFONTW * logfont)2648 IntGetFontRenderMode(LOGFONTW *logfont)
2649 {
2650     switch (logfont->lfQuality)
2651     {
2652     case ANTIALIASED_QUALITY:
2653         break;
2654     case NONANTIALIASED_QUALITY:
2655         return FT_RENDER_MODE_MONO;
2656     case DRAFT_QUALITY:
2657         return FT_RENDER_MODE_LIGHT;
2658     case CLEARTYPE_QUALITY:
2659         if (!gspv.bFontSmoothing)
2660             break;
2661         if (!gspv.uiFontSmoothingType)
2662             break;
2663         return FT_RENDER_MODE_LCD;
2664     }
2665     return FT_RENDER_MODE_NORMAL;
2666 }
2667 
2668 
2669 NTSTATUS FASTCALL
TextIntCreateFontIndirect(CONST LPLOGFONTW lf,HFONT * NewFont)2670 TextIntCreateFontIndirect(CONST LPLOGFONTW lf, HFONT *NewFont)
2671 {
2672     PLFONT plfont;
2673     LOGFONTW *plf;
2674 
2675     ASSERT(lf);
2676     plfont = LFONT_AllocFontWithHandle();
2677     if (!plfont)
2678     {
2679         return STATUS_NO_MEMORY;
2680     }
2681 
2682     ExInitializePushLock(&plfont->lock);
2683     *NewFont = plfont->BaseObject.hHmgr;
2684     plf = &plfont->logfont.elfEnumLogfontEx.elfLogFont;
2685     RtlCopyMemory(plf, lf, sizeof(LOGFONTW));
2686     if (lf->lfEscapement != lf->lfOrientation)
2687     {
2688         /* This should really depend on whether GM_ADVANCED is set */
2689         plf->lfOrientation = plf->lfEscapement;
2690     }
2691     LFONT_UnlockFont(plfont);
2692 
2693     return STATUS_SUCCESS;
2694 }
2695 
2696 /*************************************************************************
2697  * TranslateCharsetInfo
2698  *
2699  * Fills a CHARSETINFO structure for a character set, code page, or
2700  * font. This allows making the correspondance between different labelings
2701  * (character set, Windows, ANSI, and OEM codepages, and Unicode ranges)
2702  * of the same encoding.
2703  *
2704  * Only one codepage will be set in Cs->fs. If TCI_SRCFONTSIG is used,
2705  * only one codepage should be set in *Src.
2706  *
2707  * RETURNS
2708  *   TRUE on success, FALSE on failure.
2709  *
2710  */
2711 static BOOLEAN
IntTranslateCharsetInfo(PDWORD Src,LPCHARSETINFO Cs,DWORD Flags)2712 IntTranslateCharsetInfo(PDWORD Src, /* [in]
2713                          if flags == TCI_SRCFONTSIG: pointer to fsCsb of a FONTSIGNATURE
2714                          if flags == TCI_SRCCHARSET: a character set value
2715                          if flags == TCI_SRCCODEPAGE: a code page value */
2716                         LPCHARSETINFO Cs, /* [out] structure to receive charset information */
2717                         DWORD Flags /* [in] determines interpretation of lpSrc */)
2718 {
2719     int Index = 0;
2720 
2721     switch (Flags)
2722     {
2723     case TCI_SRCFONTSIG:
2724         while (Index < MAXTCIINDEX && 0 == (*Src >> Index & 0x0001))
2725         {
2726             Index++;
2727         }
2728         break;
2729     case TCI_SRCCODEPAGE:
2730         while (Index < MAXTCIINDEX && *Src != g_FontTci[Index].ciACP)
2731         {
2732             Index++;
2733         }
2734         break;
2735     case TCI_SRCCHARSET:
2736         while (Index < MAXTCIINDEX && *Src != g_FontTci[Index].ciCharset)
2737         {
2738             Index++;
2739         }
2740         break;
2741     case TCI_SRCLOCALE:
2742         UNIMPLEMENTED;
2743         return FALSE;
2744     default:
2745         return FALSE;
2746     }
2747 
2748     if (Index >= MAXTCIINDEX || DEFAULT_CHARSET == g_FontTci[Index].ciCharset)
2749     {
2750         return FALSE;
2751     }
2752 
2753     RtlCopyMemory(Cs, &g_FontTci[Index], sizeof(CHARSETINFO));
2754 
2755     return TRUE;
2756 }
2757 
2758 
face_has_symbol_charmap(FT_Face ft_face)2759 static BOOL face_has_symbol_charmap(FT_Face ft_face)
2760 {
2761     int i;
2762 
2763     for(i = 0; i < ft_face->num_charmaps; i++)
2764     {
2765         if (ft_face->charmaps[i]->platform_id == TT_PLATFORM_MICROSOFT &&
2766             ft_face->charmaps[i]->encoding == FT_ENCODING_MS_SYMBOL)
2767         {
2768             return TRUE;
2769         }
2770     }
2771     return FALSE;
2772 }
2773 
2774 static void FASTCALL
FillTM(TEXTMETRICW * TM,PFONTGDI FontGDI,TT_OS2 * pOS2,TT_HoriHeader * pHori,FT_WinFNT_HeaderRec * pFNT)2775 FillTM(TEXTMETRICW *TM, PFONTGDI FontGDI,
2776        TT_OS2 *pOS2, TT_HoriHeader *pHori,
2777        FT_WinFNT_HeaderRec *pFNT)
2778 {
2779     FT_Fixed XScale, YScale;
2780     int Ascent, Descent;
2781     FT_Face Face = FontGDI->SharedFace->Face;
2782 
2783     ASSERT_FREETYPE_LOCK_HELD();
2784 
2785     XScale = Face->size->metrics.x_scale;
2786     YScale = Face->size->metrics.y_scale;
2787 
2788     if (pFNT)
2789     {
2790         TM->tmHeight           = pFNT->pixel_height;
2791         TM->tmAscent           = pFNT->ascent;
2792         TM->tmDescent          = TM->tmHeight - TM->tmAscent;
2793         TM->tmInternalLeading  = pFNT->internal_leading;
2794         TM->tmExternalLeading  = pFNT->external_leading;
2795         TM->tmAveCharWidth     = pFNT->avg_width;
2796         TM->tmMaxCharWidth     = pFNT->max_width;
2797         TM->tmOverhang         = 0;
2798         TM->tmDigitizedAspectX = pFNT->horizontal_resolution;
2799         TM->tmDigitizedAspectY = pFNT->vertical_resolution;
2800         TM->tmFirstChar        = pFNT->first_char;
2801         TM->tmLastChar         = pFNT->last_char;
2802         TM->tmDefaultChar      = pFNT->default_char + pFNT->first_char;
2803         TM->tmBreakChar        = pFNT->break_char + pFNT->first_char;
2804         TM->tmPitchAndFamily   = pFNT->pitch_and_family;
2805         TM->tmWeight       = FontGDI->RequestWeight;
2806         TM->tmItalic       = FontGDI->RequestItalic;
2807         TM->tmUnderlined   = FontGDI->RequestUnderline;
2808         TM->tmStruckOut    = FontGDI->RequestStrikeOut;
2809         TM->tmCharSet      = FontGDI->CharSet;
2810         return;
2811     }
2812 
2813     ASSERT(pOS2);
2814     if (!pOS2)
2815         return;
2816 
2817     if ((FT_Short)pOS2->usWinAscent + (FT_Short)pOS2->usWinDescent == 0)
2818     {
2819         Ascent = pHori->Ascender;
2820         Descent = -pHori->Descender;
2821     }
2822     else
2823     {
2824         Ascent = (FT_Short)pOS2->usWinAscent;
2825         Descent = (FT_Short)pOS2->usWinDescent;
2826     }
2827 
2828     TM->tmAscent = FontGDI->tmAscent;
2829     TM->tmDescent = FontGDI->tmDescent;
2830     TM->tmHeight = TM->tmAscent + TM->tmDescent;
2831     TM->tmInternalLeading = FontGDI->tmInternalLeading;
2832 
2833     /* MSDN says:
2834      *  el = MAX(0, LineGap - ((WinAscent + WinDescent) - (Ascender - Descender)))
2835      */
2836     TM->tmExternalLeading = max(0, (FT_MulFix(pHori->Line_Gap
2837                                     - ((Ascent + Descent)
2838                                        - (pHori->Ascender - pHori->Descender)),
2839                                     YScale) + 32) >> 6);
2840     if (FontGDI->lfWidth != 0)
2841         TM->tmAveCharWidth = FontGDI->lfWidth;
2842     else
2843         TM->tmAveCharWidth = (FT_MulFix(pOS2->xAvgCharWidth, XScale) + 32) >> 6;
2844 
2845     if (TM->tmAveCharWidth == 0)
2846         TM->tmAveCharWidth = 1;
2847 
2848     /* Correct forumla to get the maxcharwidth from unicode and ansi font */
2849     TM->tmMaxCharWidth = (FT_MulFix(Face->max_advance_width, XScale) + 32) >> 6;
2850 
2851     if (FontGDI->OriginalWeight != FW_DONTCARE &&
2852         FontGDI->OriginalWeight != FW_NORMAL)
2853     {
2854         TM->tmWeight = FontGDI->OriginalWeight;
2855     }
2856     else
2857     {
2858         TM->tmWeight = FontGDI->RequestWeight;
2859     }
2860 
2861     TM->tmOverhang = 0;
2862     TM->tmDigitizedAspectX = 96;
2863     TM->tmDigitizedAspectY = 96;
2864     if (face_has_symbol_charmap(Face) ||
2865         (pOS2->usFirstCharIndex >= 0xf000 && pOS2->usFirstCharIndex < 0xf100))
2866     {
2867         USHORT cpOEM, cpAnsi;
2868 
2869         EngGetCurrentCodePage(&cpOEM, &cpAnsi);
2870         TM->tmFirstChar = 0;
2871         switch(cpAnsi)
2872         {
2873         case 1257: /* Baltic */
2874             TM->tmLastChar = 0xf8fd;
2875             break;
2876         default:
2877             TM->tmLastChar = 0xf0ff;
2878         }
2879         TM->tmBreakChar = 0x20;
2880         TM->tmDefaultChar = 0x1f;
2881     }
2882     else
2883     {
2884         TM->tmFirstChar = pOS2->usFirstCharIndex; /* Should be the first char in the cmap */
2885         TM->tmLastChar = pOS2->usLastCharIndex;   /* Should be min(cmap_last, os2_last) */
2886 
2887         if(pOS2->usFirstCharIndex <= 1)
2888             TM->tmBreakChar = pOS2->usFirstCharIndex + 2;
2889         else if (pOS2->usFirstCharIndex > 0xff)
2890             TM->tmBreakChar = 0x20;
2891         else
2892             TM->tmBreakChar = pOS2->usFirstCharIndex;
2893         TM->tmDefaultChar = TM->tmBreakChar - 1;
2894     }
2895 
2896     if (FontGDI->OriginalItalic || FontGDI->RequestItalic)
2897     {
2898         TM->tmItalic = 0xFF;
2899     }
2900     else
2901     {
2902         TM->tmItalic = 0;
2903     }
2904     TM->tmUnderlined = (FontGDI->RequestUnderline ? 0xFF : 0);
2905     TM->tmStruckOut  = (FontGDI->RequestStrikeOut ? 0xFF : 0);
2906 
2907     if (!FT_IS_FIXED_WIDTH(Face))
2908     {
2909         switch (pOS2->panose[PAN_PROPORTION_INDEX])
2910         {
2911             case PAN_PROP_MONOSPACED:
2912                 TM->tmPitchAndFamily = 0;
2913                 break;
2914             default:
2915                 TM->tmPitchAndFamily = _TMPF_VARIABLE_PITCH;
2916                 break;
2917         }
2918     }
2919     else
2920     {
2921         TM->tmPitchAndFamily = 0;
2922     }
2923 
2924     switch (pOS2->panose[PAN_FAMILYTYPE_INDEX])
2925     {
2926     case PAN_FAMILY_SCRIPT:
2927         TM->tmPitchAndFamily |= FF_SCRIPT;
2928         break;
2929     case PAN_FAMILY_DECORATIVE:
2930         TM->tmPitchAndFamily |= FF_DECORATIVE;
2931         break;
2932 
2933     case PAN_ANY:
2934     case PAN_NO_FIT:
2935     case PAN_FAMILY_TEXT_DISPLAY:
2936     case PAN_FAMILY_PICTORIAL: /* Symbol fonts get treated as if they were text */
2937                                /* Which is clearly not what the panose spec says. */
2938         if (TM->tmPitchAndFamily == 0) /* Fixed */
2939         {
2940             TM->tmPitchAndFamily = FF_MODERN;
2941         }
2942         else
2943         {
2944             switch (pOS2->panose[PAN_SERIFSTYLE_INDEX])
2945             {
2946             case PAN_ANY:
2947             case PAN_NO_FIT:
2948             default:
2949                 TM->tmPitchAndFamily |= FF_DONTCARE;
2950                 break;
2951 
2952             case PAN_SERIF_COVE:
2953             case PAN_SERIF_OBTUSE_COVE:
2954             case PAN_SERIF_SQUARE_COVE:
2955             case PAN_SERIF_OBTUSE_SQUARE_COVE:
2956             case PAN_SERIF_SQUARE:
2957             case PAN_SERIF_THIN:
2958             case PAN_SERIF_BONE:
2959             case PAN_SERIF_EXAGGERATED:
2960             case PAN_SERIF_TRIANGLE:
2961                 TM->tmPitchAndFamily |= FF_ROMAN;
2962                 break;
2963 
2964             case PAN_SERIF_NORMAL_SANS:
2965             case PAN_SERIF_OBTUSE_SANS:
2966             case PAN_SERIF_PERP_SANS:
2967             case PAN_SERIF_FLARED:
2968             case PAN_SERIF_ROUNDED:
2969                 TM->tmPitchAndFamily |= FF_SWISS;
2970                 break;
2971             }
2972         }
2973         break;
2974     default:
2975         TM->tmPitchAndFamily |= FF_DONTCARE;
2976     }
2977 
2978     if (FT_IS_SCALABLE(Face))
2979     {
2980         TM->tmPitchAndFamily |= TMPF_VECTOR;
2981     }
2982     if (FT_IS_SFNT(Face))
2983     {
2984         TM->tmPitchAndFamily |= TMPF_TRUETYPE;
2985     }
2986 
2987     TM->tmCharSet = FontGDI->CharSet;
2988 }
2989 
2990 static NTSTATUS
2991 IntGetFontLocalizedName(PUNICODE_STRING pNameW, PSHARED_FACE SharedFace,
2992                         FT_UShort NameID, FT_UShort LangID);
2993 
2994 typedef struct FONT_NAMES
2995 {
2996     UNICODE_STRING FamilyNameW;     /* family name (TT_NAME_ID_FONT_FAMILY) */
2997     UNICODE_STRING FaceNameW;       /* face name (TT_NAME_ID_FULL_NAME) */
2998     UNICODE_STRING StyleNameW;      /* style name (TT_NAME_ID_FONT_SUBFAMILY) */
2999     UNICODE_STRING FullNameW;       /* unique name (TT_NAME_ID_UNIQUE_ID) */
3000     ULONG OtmSize;                  /* size of OUTLINETEXTMETRICW with extra data */
3001 } FONT_NAMES, *LPFONT_NAMES;
3002 
3003 static __inline void FASTCALL
IntInitFontNames(FONT_NAMES * Names,PSHARED_FACE SharedFace)3004 IntInitFontNames(FONT_NAMES *Names, PSHARED_FACE SharedFace)
3005 {
3006     ULONG OtmSize;
3007 
3008     RtlInitUnicodeString(&Names->FamilyNameW, NULL);
3009     RtlInitUnicodeString(&Names->FaceNameW, NULL);
3010     RtlInitUnicodeString(&Names->StyleNameW, NULL);
3011     RtlInitUnicodeString(&Names->FullNameW, NULL);
3012 
3013     /* family name */
3014     IntGetFontLocalizedName(&Names->FamilyNameW, SharedFace, TT_NAME_ID_FONT_FAMILY, gusLanguageID);
3015     /* face name */
3016     IntGetFontLocalizedName(&Names->FaceNameW, SharedFace, TT_NAME_ID_FULL_NAME, gusLanguageID);
3017     /* style name */
3018     IntGetFontLocalizedName(&Names->StyleNameW, SharedFace, TT_NAME_ID_FONT_SUBFAMILY, gusLanguageID);
3019     /* unique name (full name) */
3020     IntGetFontLocalizedName(&Names->FullNameW, SharedFace, TT_NAME_ID_UNIQUE_ID, gusLanguageID);
3021 
3022     /* Calculate the size of OUTLINETEXTMETRICW with extra data */
3023     OtmSize = sizeof(OUTLINETEXTMETRICW) +
3024               Names->FamilyNameW.Length + sizeof(UNICODE_NULL) +
3025               Names->FaceNameW.Length + sizeof(UNICODE_NULL) +
3026               Names->StyleNameW.Length + sizeof(UNICODE_NULL) +
3027               Names->FullNameW.Length + sizeof(UNICODE_NULL);
3028     Names->OtmSize = OtmSize;
3029 }
3030 
3031 static __inline SIZE_T FASTCALL
IntStoreName(const UNICODE_STRING * pName,BYTE * pb)3032 IntStoreName(const UNICODE_STRING *pName, BYTE *pb)
3033 {
3034     RtlCopyMemory(pb, pName->Buffer, pName->Length);
3035     *(WCHAR *)&pb[pName->Length] = UNICODE_NULL;
3036     return pName->Length + sizeof(UNICODE_NULL);
3037 }
3038 
3039 static __inline BYTE *FASTCALL
IntStoreFontNames(const FONT_NAMES * Names,OUTLINETEXTMETRICW * Otm)3040 IntStoreFontNames(const FONT_NAMES *Names, OUTLINETEXTMETRICW *Otm)
3041 {
3042     BYTE *pb = (BYTE *)Otm + sizeof(OUTLINETEXTMETRICW);
3043 
3044     /* family name */
3045     Otm->otmpFamilyName = (LPSTR)(pb - (BYTE*) Otm);
3046     pb += IntStoreName(&Names->FamilyNameW, pb);
3047 
3048     /* face name */
3049     Otm->otmpFaceName = (LPSTR)(pb - (BYTE*) Otm);
3050     pb += IntStoreName(&Names->FaceNameW, pb);
3051 
3052     /* style name */
3053     Otm->otmpStyleName = (LPSTR)(pb - (BYTE*) Otm);
3054     pb += IntStoreName(&Names->StyleNameW, pb);
3055 
3056     /* unique name (full name) */
3057     Otm->otmpFullName = (LPSTR)(pb - (BYTE*) Otm);
3058     pb += IntStoreName(&Names->FullNameW, pb);
3059 
3060     return pb;
3061 }
3062 
3063 static __inline void FASTCALL
IntFreeFontNames(FONT_NAMES * Names)3064 IntFreeFontNames(FONT_NAMES *Names)
3065 {
3066     RtlFreeUnicodeString(&Names->FamilyNameW);
3067     RtlFreeUnicodeString(&Names->FaceNameW);
3068     RtlFreeUnicodeString(&Names->StyleNameW);
3069     RtlFreeUnicodeString(&Names->FullNameW);
3070 }
3071 
3072 /*************************************************************
3073  * IntGetOutlineTextMetrics
3074  *
3075  */
3076 INT FASTCALL
IntGetOutlineTextMetrics(PFONTGDI FontGDI,UINT Size,OUTLINETEXTMETRICW * Otm,BOOL bLocked)3077 IntGetOutlineTextMetrics(PFONTGDI FontGDI,
3078                          UINT Size,
3079                          OUTLINETEXTMETRICW *Otm,
3080                          BOOL bLocked)
3081 {
3082     TT_OS2 *pOS2;
3083     TT_HoriHeader *pHori;
3084     TT_Postscript *pPost;
3085     FT_Fixed XScale, YScale;
3086     FT_WinFNT_HeaderRec WinFNT;
3087     FT_Error Error;
3088     BYTE *pb;
3089     FONT_NAMES FontNames;
3090     PSHARED_FACE SharedFace = FontGDI->SharedFace;
3091     PSHARED_FACE_CACHE Cache;
3092     FT_Face Face = SharedFace->Face;
3093 
3094     if (bLocked)
3095         ASSERT_FREETYPE_LOCK_HELD();
3096     else
3097         ASSERT_FREETYPE_LOCK_NOT_HELD();
3098 
3099     if (PRIMARYLANGID(gusLanguageID) == LANG_ENGLISH)
3100     {
3101         Cache = &SharedFace->EnglishUS;
3102     }
3103     else
3104     {
3105         Cache = &SharedFace->UserLanguage;
3106     }
3107 
3108     if (Size == 0 && Cache->OutlineRequiredSize > 0)
3109     {
3110         ASSERT(Otm == NULL);
3111         return Cache->OutlineRequiredSize;
3112     }
3113 
3114     if (!bLocked)
3115         IntLockFreeType();
3116 
3117     IntInitFontNames(&FontNames, SharedFace);
3118     Cache->OutlineRequiredSize = FontNames.OtmSize;
3119 
3120     if (Size == 0)
3121     {
3122         ASSERT(Otm == NULL);
3123         IntFreeFontNames(&FontNames);
3124         if (!bLocked)
3125             IntUnLockFreeType();
3126         return Cache->OutlineRequiredSize;
3127     }
3128 
3129     ASSERT(Otm != NULL);
3130 
3131     if (Size < Cache->OutlineRequiredSize)
3132     {
3133         DPRINT1("Size %u < OutlineRequiredSize %u\n", Size,
3134                 Cache->OutlineRequiredSize);
3135         IntFreeFontNames(&FontNames);
3136         if (!bLocked)
3137             IntUnLockFreeType();
3138         return 0;   /* failure */
3139     }
3140 
3141     XScale = Face->size->metrics.x_scale;
3142     YScale = Face->size->metrics.y_scale;
3143 
3144     pOS2 = FT_Get_Sfnt_Table(Face, FT_SFNT_OS2);
3145     pHori = FT_Get_Sfnt_Table(Face, FT_SFNT_HHEA);
3146     pPost = FT_Get_Sfnt_Table(Face, FT_SFNT_POST); /* We can live with this failing */
3147     Error = FT_Get_WinFNT_Header(Face, &WinFNT);
3148 
3149     if (pOS2 == NULL && Error)
3150     {
3151         if (!bLocked)
3152             IntUnLockFreeType();
3153         DPRINT1("Can't find OS/2 table - not TT font?\n");
3154         IntFreeFontNames(&FontNames);
3155         return 0;
3156     }
3157 
3158     if (pHori == NULL && Error)
3159     {
3160         if (!bLocked)
3161             IntUnLockFreeType();
3162         DPRINT1("Can't find HHEA table - not TT font?\n");
3163         IntFreeFontNames(&FontNames);
3164         return 0;
3165     }
3166 
3167     Otm->otmSize = Cache->OutlineRequiredSize;
3168 
3169     FillTM(&Otm->otmTextMetrics, FontGDI, pOS2, pHori, (Error ? NULL : &WinFNT));
3170 
3171     if (!pOS2)
3172         goto skip_os2;
3173 
3174     Otm->otmFiller = 0;
3175     RtlCopyMemory(&Otm->otmPanoseNumber, pOS2->panose, PANOSE_COUNT);
3176     Otm->otmfsSelection = pOS2->fsSelection;
3177     Otm->otmfsType = pOS2->fsType;
3178     Otm->otmsCharSlopeRise = pHori->caret_Slope_Rise;
3179     Otm->otmsCharSlopeRun = pHori->caret_Slope_Run;
3180     Otm->otmItalicAngle = 0; /* POST table */
3181     Otm->otmEMSquare = Face->units_per_EM;
3182 
3183 #define SCALE_X(value)  ((FT_MulFix((value), XScale) + 32) >> 6)
3184 #define SCALE_Y(value)  ((FT_MulFix((value), YScale) + 32) >> 6)
3185 
3186     Otm->otmAscent = SCALE_Y(pOS2->sTypoAscender);
3187     Otm->otmDescent = SCALE_Y(pOS2->sTypoDescender);
3188     Otm->otmLineGap = SCALE_Y(pOS2->sTypoLineGap);
3189     Otm->otmsCapEmHeight = SCALE_Y(pOS2->sCapHeight);
3190     Otm->otmsXHeight = SCALE_Y(pOS2->sxHeight);
3191     Otm->otmrcFontBox.left = SCALE_X(Face->bbox.xMin);
3192     Otm->otmrcFontBox.right = SCALE_X(Face->bbox.xMax);
3193     Otm->otmrcFontBox.top = SCALE_Y(Face->bbox.yMax);
3194     Otm->otmrcFontBox.bottom = SCALE_Y(Face->bbox.yMin);
3195     Otm->otmMacAscent = Otm->otmTextMetrics.tmAscent;
3196     Otm->otmMacDescent = -Otm->otmTextMetrics.tmDescent;
3197     Otm->otmMacLineGap = Otm->otmLineGap;
3198     Otm->otmusMinimumPPEM = 0; /* TT Header */
3199     Otm->otmptSubscriptSize.x = SCALE_X(pOS2->ySubscriptXSize);
3200     Otm->otmptSubscriptSize.y = SCALE_Y(pOS2->ySubscriptYSize);
3201     Otm->otmptSubscriptOffset.x = SCALE_X(pOS2->ySubscriptXOffset);
3202     Otm->otmptSubscriptOffset.y = SCALE_Y(pOS2->ySubscriptYOffset);
3203     Otm->otmptSuperscriptSize.x = SCALE_X(pOS2->ySuperscriptXSize);
3204     Otm->otmptSuperscriptSize.y = SCALE_Y(pOS2->ySuperscriptYSize);
3205     Otm->otmptSuperscriptOffset.x = SCALE_X(pOS2->ySuperscriptXOffset);
3206     Otm->otmptSuperscriptOffset.y = SCALE_Y(pOS2->ySuperscriptYOffset);
3207     Otm->otmsStrikeoutSize = SCALE_Y(pOS2->yStrikeoutSize);
3208     Otm->otmsStrikeoutPosition = SCALE_Y(pOS2->yStrikeoutPosition);
3209 
3210     if (!pPost)
3211     {
3212         Otm->otmsUnderscoreSize = 0;
3213         Otm->otmsUnderscorePosition = 0;
3214     }
3215     else
3216     {
3217         Otm->otmsUnderscoreSize = SCALE_Y(pPost->underlineThickness);
3218         Otm->otmsUnderscorePosition = SCALE_Y(pPost->underlinePosition);
3219     }
3220 
3221 #undef SCALE_X
3222 #undef SCALE_Y
3223 
3224 skip_os2:
3225     if (!bLocked)
3226         IntUnLockFreeType();
3227 
3228     pb = IntStoreFontNames(&FontNames, Otm);
3229     ASSERT(pb - (BYTE*)Otm == Cache->OutlineRequiredSize);
3230 
3231     IntFreeFontNames(&FontNames);
3232 
3233     return Cache->OutlineRequiredSize;
3234 }
3235 
3236 /* See https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2008/bb165625(v=vs.90) */
3237 static BYTE
CharSetFromLangID(LANGID LangID)3238 CharSetFromLangID(LANGID LangID)
3239 {
3240     /* FIXME: Add more and fix if wrong */
3241     switch (PRIMARYLANGID(LangID))
3242     {
3243         case LANG_CHINESE:
3244             switch (SUBLANGID(LangID))
3245             {
3246                 case SUBLANG_CHINESE_TRADITIONAL:
3247                     return CHINESEBIG5_CHARSET;
3248                 case SUBLANG_CHINESE_SIMPLIFIED:
3249                 default:
3250                     break;
3251             }
3252             return GB2312_CHARSET;
3253 
3254         case LANG_CZECH: case LANG_HUNGARIAN: case LANG_POLISH:
3255         case LANG_SLOVAK: case LANG_SLOVENIAN: case LANG_ROMANIAN:
3256             return EASTEUROPE_CHARSET;
3257 
3258         case LANG_RUSSIAN: case LANG_BULGARIAN: case LANG_MACEDONIAN:
3259         case LANG_SERBIAN: case LANG_UKRAINIAN:
3260             return RUSSIAN_CHARSET;
3261 
3262         case LANG_ARABIC:       return ARABIC_CHARSET;
3263         case LANG_GREEK:        return GREEK_CHARSET;
3264         case LANG_HEBREW:       return HEBREW_CHARSET;
3265         case LANG_JAPANESE:     return SHIFTJIS_CHARSET;
3266         case LANG_KOREAN:       return JOHAB_CHARSET;
3267         case LANG_TURKISH:      return TURKISH_CHARSET;
3268         case LANG_THAI:         return THAI_CHARSET;
3269         case LANG_LATVIAN:      return BALTIC_CHARSET;
3270         case LANG_VIETNAMESE:   return VIETNAMESE_CHARSET;
3271 
3272         case LANG_ENGLISH: case LANG_BASQUE: case LANG_CATALAN:
3273         case LANG_DANISH: case LANG_DUTCH: case LANG_FINNISH:
3274         case LANG_FRENCH: case LANG_GERMAN: case LANG_ITALIAN:
3275         case LANG_NORWEGIAN: case LANG_PORTUGUESE: case LANG_SPANISH:
3276         case LANG_SWEDISH: default:
3277             return ANSI_CHARSET;
3278     }
3279 }
3280 
3281 static void
SwapEndian(LPVOID pvData,DWORD Size)3282 SwapEndian(LPVOID pvData, DWORD Size)
3283 {
3284     BYTE b, *pb = pvData;
3285     Size /= 2;
3286     while (Size-- > 0)
3287     {
3288         b = pb[0];
3289         pb[0] = pb[1];
3290         pb[1] = b;
3291         ++pb; ++pb;
3292     }
3293 }
3294 
3295 static NTSTATUS
IntGetFontLocalizedName(PUNICODE_STRING pNameW,PSHARED_FACE SharedFace,FT_UShort NameID,FT_UShort LangID)3296 IntGetFontLocalizedName(PUNICODE_STRING pNameW, PSHARED_FACE SharedFace,
3297                         FT_UShort NameID, FT_UShort LangID)
3298 {
3299     FT_SfntName Name;
3300     INT i, Count, BestIndex, Score, BestScore;
3301     FT_Error Error;
3302     NTSTATUS Status = STATUS_NOT_FOUND;
3303     ANSI_STRING AnsiName;
3304     PSHARED_FACE_CACHE Cache;
3305     FT_Face Face = SharedFace->Face;
3306 
3307     RtlFreeUnicodeString(pNameW);
3308 
3309     /* select cache */
3310     if (PRIMARYLANGID(LangID) == LANG_ENGLISH)
3311     {
3312         Cache = &SharedFace->EnglishUS;
3313     }
3314     else
3315     {
3316         Cache = &SharedFace->UserLanguage;
3317     }
3318 
3319     /* use cache if available */
3320     if (NameID == TT_NAME_ID_FONT_FAMILY && Cache->FontFamily.Buffer)
3321     {
3322         return DuplicateUnicodeString(&Cache->FontFamily, pNameW);
3323     }
3324     if (NameID == TT_NAME_ID_FULL_NAME && Cache->FullName.Buffer)
3325     {
3326         return DuplicateUnicodeString(&Cache->FullName, pNameW);
3327     }
3328 
3329     BestIndex = -1;
3330     BestScore = 0;
3331 
3332     Count = FT_Get_Sfnt_Name_Count(Face);
3333     for (i = 0; i < Count; ++i)
3334     {
3335         Error = FT_Get_Sfnt_Name(Face, i, &Name);
3336         if (Error)
3337         {
3338             continue;   /* failure */
3339         }
3340 
3341         if (Name.name_id != NameID)
3342         {
3343             continue;   /* mismatched */
3344         }
3345 
3346         if (Name.platform_id != TT_PLATFORM_MICROSOFT ||
3347             (Name.encoding_id != TT_MS_ID_UNICODE_CS &&
3348              Name.encoding_id != TT_MS_ID_SYMBOL_CS))
3349         {
3350             continue;   /* not Microsoft Unicode name */
3351         }
3352 
3353         if (Name.string == NULL || Name.string_len == 0 ||
3354             (Name.string[0] == 0 && Name.string[1] == 0))
3355         {
3356             continue;   /* invalid string */
3357         }
3358 
3359         if (Name.language_id == LangID)
3360         {
3361             Score = 30;
3362             BestIndex = i;
3363             break;      /* best match */
3364         }
3365         else if (PRIMARYLANGID(Name.language_id) == PRIMARYLANGID(LangID))
3366         {
3367             Score = 20;
3368         }
3369         else if (PRIMARYLANGID(Name.language_id) == LANG_ENGLISH)
3370         {
3371             Score = 10;
3372         }
3373         else
3374         {
3375             Score = 0;
3376         }
3377 
3378         if (Score > BestScore)
3379         {
3380             BestScore = Score;
3381             BestIndex = i;
3382         }
3383     }
3384 
3385     if (BestIndex >= 0)
3386     {
3387         /* store the best name */
3388         Error = (Score == 30) ? 0 : FT_Get_Sfnt_Name(Face, BestIndex, &Name);
3389         if (!Error)
3390         {
3391             /* NOTE: Name.string is not null-terminated */
3392             UNICODE_STRING Tmp;
3393             Tmp.Buffer = (PWCH)Name.string;
3394             Tmp.Length = Tmp.MaximumLength = Name.string_len;
3395 
3396             pNameW->Length = 0;
3397             pNameW->MaximumLength = Name.string_len + sizeof(WCHAR);
3398             pNameW->Buffer = ExAllocatePoolWithTag(PagedPool, pNameW->MaximumLength, TAG_USTR);
3399 
3400             if (pNameW->Buffer)
3401             {
3402                 Status = RtlAppendUnicodeStringToString(pNameW, &Tmp);
3403                 if (Status == STATUS_SUCCESS)
3404                 {
3405                     /* Convert UTF-16 big endian to little endian */
3406                     SwapEndian(pNameW->Buffer, pNameW->Length);
3407                 }
3408             }
3409             else
3410             {
3411                 Status = STATUS_INSUFFICIENT_RESOURCES;
3412             }
3413         }
3414     }
3415 
3416     if (!NT_SUCCESS(Status))
3417     {
3418         /* defaulted */
3419         if (NameID == TT_NAME_ID_FONT_SUBFAMILY)
3420         {
3421             RtlInitAnsiString(&AnsiName, Face->style_name);
3422             Status = RtlAnsiStringToUnicodeString(pNameW, &AnsiName, TRUE);
3423         }
3424         else
3425         {
3426             RtlInitAnsiString(&AnsiName, Face->family_name);
3427             Status = RtlAnsiStringToUnicodeString(pNameW, &AnsiName, TRUE);
3428         }
3429     }
3430 
3431     if (NT_SUCCESS(Status))
3432     {
3433         /* make cache */
3434         if (NameID == TT_NAME_ID_FONT_FAMILY)
3435         {
3436             ASSERT_FREETYPE_LOCK_HELD();
3437             if (!Cache->FontFamily.Buffer)
3438                 DuplicateUnicodeString(pNameW, &Cache->FontFamily);
3439         }
3440         else if (NameID == TT_NAME_ID_FULL_NAME)
3441         {
3442             ASSERT_FREETYPE_LOCK_HELD();
3443             if (!Cache->FullName.Buffer)
3444                 DuplicateUnicodeString(pNameW, &Cache->FullName);
3445         }
3446     }
3447 
3448     return Status;
3449 }
3450 
3451 static void FASTCALL
FontFamilyFillInfo(PFONTFAMILYINFO Info,LPCWSTR FaceName,LPCWSTR FullName,PFONTGDI FontGDI)3452 FontFamilyFillInfo(PFONTFAMILYINFO Info, LPCWSTR FaceName,
3453                    LPCWSTR FullName, PFONTGDI FontGDI)
3454 {
3455     ANSI_STRING StyleA;
3456     UNICODE_STRING StyleW;
3457     TT_OS2 *pOS2;
3458     FONTSIGNATURE fs;
3459     CHARSETINFO CharSetInfo;
3460     unsigned i, Size;
3461     OUTLINETEXTMETRICW *Otm;
3462     LOGFONTW *Lf;
3463     TEXTMETRICW *TM;
3464     NEWTEXTMETRICW *Ntm;
3465     DWORD fs0;
3466     NTSTATUS status;
3467     PSHARED_FACE SharedFace = FontGDI->SharedFace;
3468     FT_Face Face = SharedFace->Face;
3469     UNICODE_STRING NameW;
3470 
3471     RtlInitUnicodeString(&NameW, NULL);
3472     RtlZeroMemory(Info, sizeof(FONTFAMILYINFO));
3473     ASSERT_FREETYPE_LOCK_HELD();
3474     Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL, TRUE);
3475     if (!Size)
3476         return;
3477     Otm = ExAllocatePoolWithTag(PagedPool, Size, GDITAG_TEXT);
3478     if (!Otm)
3479         return;
3480     ASSERT_FREETYPE_LOCK_HELD();
3481     Size = IntGetOutlineTextMetrics(FontGDI, Size, Otm, TRUE);
3482     if (!Size)
3483     {
3484         ExFreePoolWithTag(Otm, GDITAG_TEXT);
3485         return;
3486     }
3487 
3488     Lf = &Info->EnumLogFontEx.elfLogFont;
3489     TM = &Otm->otmTextMetrics;
3490 
3491     Lf->lfHeight = TM->tmHeight;
3492     Lf->lfWidth = TM->tmAveCharWidth;
3493     Lf->lfWeight = TM->tmWeight;
3494     Lf->lfItalic = TM->tmItalic;
3495     Lf->lfPitchAndFamily = (TM->tmPitchAndFamily & 0xf1) + 1;
3496     Lf->lfCharSet = TM->tmCharSet;
3497     Lf->lfOutPrecision = OUT_OUTLINE_PRECIS;
3498     Lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
3499     Lf->lfQuality = PROOF_QUALITY;
3500 
3501     Ntm = &Info->NewTextMetricEx.ntmTm;
3502     Ntm->tmHeight = TM->tmHeight;
3503     Ntm->tmAscent = TM->tmAscent;
3504     Ntm->tmDescent = TM->tmDescent;
3505     Ntm->tmInternalLeading = TM->tmInternalLeading;
3506     Ntm->tmExternalLeading = TM->tmExternalLeading;
3507     Ntm->tmAveCharWidth = TM->tmAveCharWidth;
3508     Ntm->tmMaxCharWidth = TM->tmMaxCharWidth;
3509     Ntm->tmWeight = TM->tmWeight;
3510     Ntm->tmOverhang = TM->tmOverhang;
3511     Ntm->tmDigitizedAspectX = TM->tmDigitizedAspectX;
3512     Ntm->tmDigitizedAspectY = TM->tmDigitizedAspectY;
3513     Ntm->tmFirstChar = TM->tmFirstChar;
3514     Ntm->tmLastChar = TM->tmLastChar;
3515     Ntm->tmDefaultChar = TM->tmDefaultChar;
3516     Ntm->tmBreakChar = TM->tmBreakChar;
3517     Ntm->tmItalic = TM->tmItalic;
3518     Ntm->tmUnderlined = TM->tmUnderlined;
3519     Ntm->tmStruckOut = TM->tmStruckOut;
3520     Ntm->tmPitchAndFamily = TM->tmPitchAndFamily;
3521     Ntm->tmCharSet = TM->tmCharSet;
3522     Ntm->ntmFlags = TM->tmItalic ? NTM_ITALIC : 0;
3523 
3524     if (550 < TM->tmWeight) Ntm->ntmFlags |= NTM_BOLD;
3525 
3526     if (0 == Ntm->ntmFlags) Ntm->ntmFlags = NTM_REGULAR;
3527 
3528     Info->FontType = (0 != (TM->tmPitchAndFamily & TMPF_TRUETYPE)
3529                       ? TRUETYPE_FONTTYPE : 0);
3530 
3531     if (0 == (TM->tmPitchAndFamily & TMPF_VECTOR))
3532         Info->FontType |= RASTER_FONTTYPE;
3533 
3534 
3535     /* face name */
3536     if (!FaceName)
3537         FaceName = (WCHAR*)((ULONG_PTR)Otm + (ULONG_PTR)Otm->otmpFamilyName);
3538 
3539     RtlStringCbCopyW(Lf->lfFaceName, sizeof(Lf->lfFaceName), FaceName);
3540 
3541     /* full name */
3542     if (!FullName)
3543         FullName = (WCHAR*)((ULONG_PTR) Otm + (ULONG_PTR)Otm->otmpFaceName);
3544 
3545     RtlStringCbCopyW(Info->EnumLogFontEx.elfFullName,
3546                      sizeof(Info->EnumLogFontEx.elfFullName),
3547                      FullName);
3548 
3549     RtlInitAnsiString(&StyleA, Face->style_name);
3550     StyleW.Buffer = Info->EnumLogFontEx.elfStyle;
3551     StyleW.MaximumLength = sizeof(Info->EnumLogFontEx.elfStyle);
3552     status = RtlAnsiStringToUnicodeString(&StyleW, &StyleA, FALSE);
3553     if (!NT_SUCCESS(status))
3554     {
3555         ExFreePoolWithTag(Otm, GDITAG_TEXT);
3556         return;
3557     }
3558     Info->EnumLogFontEx.elfScript[0] = UNICODE_NULL;
3559 
3560     pOS2 = FT_Get_Sfnt_Table(Face, ft_sfnt_os2);
3561 
3562     if (!pOS2)
3563     {
3564         ExFreePoolWithTag(Otm, GDITAG_TEXT);
3565         return;
3566     }
3567 
3568     Ntm->ntmSizeEM = Otm->otmEMSquare;
3569     Ntm->ntmCellHeight = (FT_Short)pOS2->usWinAscent + (FT_Short)pOS2->usWinDescent;
3570     Ntm->ntmAvgWidth = 0;
3571 
3572     ExFreePoolWithTag(Otm, GDITAG_TEXT);
3573 
3574     fs.fsCsb[0] = pOS2->ulCodePageRange1;
3575     fs.fsCsb[1] = pOS2->ulCodePageRange2;
3576     fs.fsUsb[0] = pOS2->ulUnicodeRange1;
3577     fs.fsUsb[1] = pOS2->ulUnicodeRange2;
3578     fs.fsUsb[2] = pOS2->ulUnicodeRange3;
3579     fs.fsUsb[3] = pOS2->ulUnicodeRange4;
3580 
3581     if (0 == pOS2->version)
3582     {
3583         FT_UInt Dummy;
3584 
3585         if (FT_Get_First_Char(Face, &Dummy) < 0x100)
3586             fs.fsCsb[0] |= FS_LATIN1;
3587         else
3588             fs.fsCsb[0] |= FS_SYMBOL;
3589     }
3590 
3591     if (fs.fsCsb[0] == 0)
3592     {
3593         /* Let's see if we can find any interesting cmaps */
3594         for (i = 0; i < (UINT)Face->num_charmaps; i++)
3595         {
3596             switch (Face->charmaps[i]->encoding)
3597             {
3598             case FT_ENCODING_UNICODE:
3599             case FT_ENCODING_APPLE_ROMAN:
3600                 fs.fsCsb[0] |= FS_LATIN1;
3601                 break;
3602             case FT_ENCODING_MS_SYMBOL:
3603                 fs.fsCsb[0] |= FS_SYMBOL;
3604                 break;
3605             default:
3606                 break;
3607             }
3608         }
3609     }
3610 
3611     for (i = 0; i < MAXTCIINDEX; i++)
3612     {
3613         fs0 = 1L << i;
3614         if (fs.fsCsb[0] & fs0)
3615         {
3616             if (!IntTranslateCharsetInfo(&fs0, &CharSetInfo, TCI_SRCFONTSIG))
3617             {
3618                 CharSetInfo.ciCharset = DEFAULT_CHARSET;
3619             }
3620             if (DEFAULT_CHARSET != CharSetInfo.ciCharset)
3621             {
3622                 if (g_ElfScripts[i])
3623                     wcscpy(Info->EnumLogFontEx.elfScript, g_ElfScripts[i]);
3624                 else
3625                 {
3626                     DPRINT1("Unknown elfscript for bit %u\n", i);
3627                 }
3628             }
3629         }
3630     }
3631     Info->NewTextMetricEx.ntmFontSig = fs;
3632 }
3633 
3634 static BOOLEAN FASTCALL
GetFontFamilyInfoForList(const LOGFONTW * LogFont,PFONTFAMILYINFO Info,LPCWSTR NominalName,LONG * pCount,LONG MaxCount,PLIST_ENTRY Head)3635 GetFontFamilyInfoForList(const LOGFONTW *LogFont,
3636                          PFONTFAMILYINFO Info,
3637                          LPCWSTR NominalName,
3638                          LONG *pCount,
3639                          LONG MaxCount,
3640                          PLIST_ENTRY Head)
3641 {
3642     PLIST_ENTRY Entry;
3643     PFONT_ENTRY CurrentEntry;
3644     FONTGDI *FontGDI;
3645     FONTFAMILYINFO InfoEntry;
3646     LONG Count = *pCount;
3647 
3648     for (Entry = Head->Flink; Entry != Head; Entry = Entry->Flink)
3649     {
3650         CurrentEntry = CONTAINING_RECORD(Entry, FONT_ENTRY, ListEntry);
3651         FontGDI = CurrentEntry->Font;
3652         ASSERT(FontGDI);
3653 
3654         if (LogFont->lfCharSet != DEFAULT_CHARSET &&
3655             LogFont->lfCharSet != FontGDI->CharSet)
3656         {
3657             continue;   /* charset mismatch */
3658         }
3659 
3660         /* get one info entry */
3661         FontFamilyFillInfo(&InfoEntry, NULL, NULL, FontGDI);
3662 
3663         if (LogFont->lfFaceName[0] != UNICODE_NULL)
3664         {
3665             /* check name */
3666             if (_wcsnicmp(LogFont->lfFaceName,
3667                           InfoEntry.EnumLogFontEx.elfLogFont.lfFaceName,
3668                           RTL_NUMBER_OF(LogFont->lfFaceName) - 1) != 0 &&
3669                 _wcsnicmp(LogFont->lfFaceName,
3670                           InfoEntry.EnumLogFontEx.elfFullName,
3671                           RTL_NUMBER_OF(LogFont->lfFaceName) - 1) != 0)
3672             {
3673                 continue;
3674             }
3675         }
3676 
3677         if (NominalName)
3678         {
3679             /* store the nominal name */
3680             RtlStringCbCopyW(InfoEntry.EnumLogFontEx.elfLogFont.lfFaceName,
3681                              sizeof(InfoEntry.EnumLogFontEx.elfLogFont.lfFaceName),
3682                              NominalName);
3683         }
3684 
3685         /* store one entry to Info */
3686         if (0 <= Count && Count < MaxCount)
3687         {
3688             RtlCopyMemory(&Info[Count], &InfoEntry, sizeof(InfoEntry));
3689         }
3690         Count++;
3691     }
3692 
3693     *pCount = Count;
3694 
3695     return TRUE;
3696 }
3697 
3698 static BOOLEAN FASTCALL
GetFontFamilyInfoForSubstitutes(const LOGFONTW * LogFont,PFONTFAMILYINFO Info,LONG * pCount,LONG MaxCount)3699 GetFontFamilyInfoForSubstitutes(const LOGFONTW *LogFont,
3700                                 PFONTFAMILYINFO Info,
3701                                 LONG *pCount,
3702                                 LONG MaxCount)
3703 {
3704     PLIST_ENTRY pEntry, pHead = &g_FontSubstListHead;
3705     PFONTSUBST_ENTRY pCurrentEntry;
3706     PUNICODE_STRING pFromW, pToW;
3707     LOGFONTW lf = *LogFont;
3708     PPROCESSINFO Win32Process = PsGetCurrentProcessWin32Process();
3709 
3710     for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink)
3711     {
3712         pCurrentEntry = CONTAINING_RECORD(pEntry, FONTSUBST_ENTRY, ListEntry);
3713 
3714         pFromW = &pCurrentEntry->FontNames[FONTSUBST_FROM];
3715         if (LogFont->lfFaceName[0] != UNICODE_NULL)
3716         {
3717             /* check name */
3718             if (_wcsicmp(LogFont->lfFaceName, pFromW->Buffer) != 0)
3719                 continue;   /* mismatch */
3720         }
3721 
3722         pToW = &pCurrentEntry->FontNames[FONTSUBST_TO];
3723         if (RtlEqualUnicodeString(pFromW, pToW, TRUE) &&
3724             pCurrentEntry->CharSets[FONTSUBST_FROM] ==
3725             pCurrentEntry->CharSets[FONTSUBST_TO])
3726         {
3727             /* identical mapping */
3728             continue;
3729         }
3730 
3731         /* substitute and get the real name */
3732         IntUnicodeStringToBuffer(lf.lfFaceName, sizeof(lf.lfFaceName), pFromW);
3733         SubstituteFontRecurse(&lf);
3734         if (LogFont->lfCharSet != DEFAULT_CHARSET && LogFont->lfCharSet != lf.lfCharSet)
3735             continue;
3736 
3737         /* search in global fonts */
3738         IntLockFreeType();
3739         GetFontFamilyInfoForList(&lf, Info, pFromW->Buffer, pCount, MaxCount, &g_FontListHead);
3740 
3741         /* search in private fonts */
3742         IntLockProcessPrivateFonts(Win32Process);
3743         GetFontFamilyInfoForList(&lf, Info, pFromW->Buffer, pCount, MaxCount,
3744                                  &Win32Process->PrivateFontListHead);
3745         IntUnLockProcessPrivateFonts(Win32Process);
3746         IntUnLockFreeType();
3747 
3748         if (LogFont->lfFaceName[0] != UNICODE_NULL)
3749         {
3750             /* it's already matched to the exact name and charset if the name
3751                was specified at here, then so don't scan more for another name */
3752             break;
3753         }
3754     }
3755 
3756     return TRUE;
3757 }
3758 
3759 BOOL
3760 FASTCALL
ftGdiGetRasterizerCaps(LPRASTERIZER_STATUS lprs)3761 ftGdiGetRasterizerCaps(LPRASTERIZER_STATUS lprs)
3762 {
3763     if ( lprs )
3764     {
3765         lprs->nSize = sizeof(RASTERIZER_STATUS);
3766         lprs->wFlags = TT_AVAILABLE | TT_ENABLED;
3767         lprs->nLanguageID = gusLanguageID;
3768         return TRUE;
3769     }
3770     EngSetLastError(ERROR_INVALID_PARAMETER);
3771     return FALSE;
3772 }
3773 
3774 static DWORD
IntGetHash(IN LPCVOID pv,IN DWORD cdw)3775 IntGetHash(IN LPCVOID pv, IN DWORD cdw)
3776 {
3777     DWORD dwHash = cdw;
3778     const DWORD *pdw = pv;
3779 
3780     while (cdw-- > 0)
3781     {
3782         dwHash *= 3;
3783         dwHash ^= *pdw++;
3784     }
3785 
3786     return dwHash;
3787 }
3788 
3789 static FT_BitmapGlyph
IntFindGlyphCache(IN const FONT_CACHE_ENTRY * pCache)3790 IntFindGlyphCache(IN const FONT_CACHE_ENTRY *pCache)
3791 {
3792     PLIST_ENTRY CurrentEntry;
3793     PFONT_CACHE_ENTRY FontEntry;
3794     DWORD dwHash = pCache->dwHash;
3795 
3796     ASSERT_FREETYPE_LOCK_HELD();
3797 
3798     for (CurrentEntry = g_FontCacheListHead.Flink;
3799          CurrentEntry != &g_FontCacheListHead;
3800          CurrentEntry = CurrentEntry->Flink)
3801     {
3802         FontEntry = CONTAINING_RECORD(CurrentEntry, FONT_CACHE_ENTRY, ListEntry);
3803         if (FontEntry->dwHash == dwHash &&
3804             FontEntry->Hashed.GlyphIndex == pCache->Hashed.GlyphIndex &&
3805             FontEntry->Hashed.Face == pCache->Hashed.Face &&
3806             FontEntry->Hashed.lfHeight == pCache->Hashed.lfHeight &&
3807             FontEntry->Hashed.lfWidth == pCache->Hashed.lfWidth &&
3808             FontEntry->Hashed.AspectValue == pCache->Hashed.AspectValue &&
3809             memcmp(&FontEntry->Hashed.matTransform, &pCache->Hashed.matTransform,
3810                    sizeof(FT_Matrix)) == 0)
3811         {
3812             break;
3813         }
3814     }
3815 
3816     if (CurrentEntry == &g_FontCacheListHead)
3817     {
3818         return NULL;
3819     }
3820 
3821     RemoveEntryList(CurrentEntry);
3822     InsertHeadList(&g_FontCacheListHead, CurrentEntry);
3823     return FontEntry->BitmapGlyph;
3824 }
3825 
3826 static FT_BitmapGlyph
IntGetBitmapGlyphWithCache(IN OUT PFONT_CACHE_ENTRY Cache,IN FT_GlyphSlot GlyphSlot)3827 IntGetBitmapGlyphWithCache(
3828     IN OUT PFONT_CACHE_ENTRY Cache,
3829     IN FT_GlyphSlot GlyphSlot)
3830 {
3831     FT_Glyph GlyphCopy;
3832     INT error;
3833     PFONT_CACHE_ENTRY NewEntry;
3834     FT_Bitmap AlignedBitmap;
3835     FT_BitmapGlyph BitmapGlyph;
3836 
3837     ASSERT_FREETYPE_LOCK_HELD();
3838 
3839     error = FT_Get_Glyph(GlyphSlot, &GlyphCopy);
3840     if (error)
3841     {
3842         DPRINT1("Failure caching glyph.\n");
3843         return NULL;
3844     };
3845 
3846     error = FT_Glyph_To_Bitmap(&GlyphCopy, Cache->Hashed.Aspect.RenderMode, 0, 1);
3847     if (error)
3848     {
3849         FT_Done_Glyph(GlyphCopy);
3850         DPRINT1("Failure rendering glyph.\n");
3851         return NULL;
3852     };
3853 
3854     NewEntry = ExAllocatePoolWithTag(PagedPool, sizeof(FONT_CACHE_ENTRY), TAG_FONT);
3855     if (!NewEntry)
3856     {
3857         DPRINT1("Alloc failure caching glyph.\n");
3858         FT_Done_Glyph(GlyphCopy);
3859         return NULL;
3860     }
3861 
3862     BitmapGlyph = (FT_BitmapGlyph)GlyphCopy;
3863     FT_Bitmap_New(&AlignedBitmap);
3864     if (FT_Bitmap_Convert_ReactOS_Hack(GlyphSlot->library, &BitmapGlyph->bitmap,
3865                                        &AlignedBitmap, 4, TRUE))
3866     {
3867         DPRINT1("Conversion failed\n");
3868         ExFreePoolWithTag(NewEntry, TAG_FONT);
3869         FT_Bitmap_Done(GlyphSlot->library, &AlignedBitmap);
3870         FT_Done_Glyph((FT_Glyph)BitmapGlyph);
3871         return NULL;
3872     }
3873 
3874     FT_Bitmap_Done(GlyphSlot->library, &BitmapGlyph->bitmap);
3875     BitmapGlyph->bitmap = AlignedBitmap;
3876 
3877     NewEntry->BitmapGlyph = BitmapGlyph;
3878     NewEntry->dwHash = Cache->dwHash;
3879     NewEntry->Hashed = Cache->Hashed;
3880 
3881     InsertHeadList(&g_FontCacheListHead, &NewEntry->ListEntry);
3882     if (++g_FontCacheNumEntries > MAX_FONT_CACHE)
3883     {
3884         NewEntry = CONTAINING_RECORD(g_FontCacheListHead.Blink, FONT_CACHE_ENTRY, ListEntry);
3885         RemoveCachedEntry(NewEntry);
3886     }
3887 
3888     return BitmapGlyph;
3889 }
3890 
3891 
get_native_glyph_outline(FT_Outline * outline,unsigned int buflen,char * buf)3892 static unsigned int get_native_glyph_outline(FT_Outline *outline, unsigned int buflen, char *buf)
3893 {
3894     TTPOLYGONHEADER *pph;
3895     TTPOLYCURVE *ppc;
3896     int needed = 0, point = 0, contour, first_pt;
3897     unsigned int pph_start, cpfx;
3898     DWORD type;
3899 
3900     for (contour = 0; contour < outline->n_contours; contour++)
3901     {
3902         /* Ignore contours containing one point */
3903         if (point == outline->contours[contour])
3904         {
3905             point++;
3906             continue;
3907         }
3908 
3909         pph_start = needed;
3910         pph = (TTPOLYGONHEADER *)(buf + needed);
3911         first_pt = point;
3912         if (buf)
3913         {
3914             pph->dwType = TT_POLYGON_TYPE;
3915             FTVectorToPOINTFX(&outline->points[point], &pph->pfxStart);
3916         }
3917         needed += sizeof(*pph);
3918         point++;
3919         while (point <= outline->contours[contour])
3920         {
3921             ppc = (TTPOLYCURVE *)(buf + needed);
3922             type = (outline->tags[point] & FT_Curve_Tag_On) ?
3923                 TT_PRIM_LINE : TT_PRIM_QSPLINE;
3924             cpfx = 0;
3925             do
3926             {
3927                 if (buf)
3928                     FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
3929                 cpfx++;
3930                 point++;
3931             } while (point <= outline->contours[contour] &&
3932                     (outline->tags[point] & FT_Curve_Tag_On) ==
3933                     (outline->tags[point-1] & FT_Curve_Tag_On));
3934             /* At the end of a contour Windows adds the start point, but
3935                only for Beziers */
3936             if (point > outline->contours[contour] &&
3937                !(outline->tags[point-1] & FT_Curve_Tag_On))
3938             {
3939                 if (buf)
3940                     FTVectorToPOINTFX(&outline->points[first_pt], &ppc->apfx[cpfx]);
3941                 cpfx++;
3942             }
3943             else if (point <= outline->contours[contour] &&
3944                       outline->tags[point] & FT_Curve_Tag_On)
3945             {
3946                 /* add closing pt for bezier */
3947                 if (buf)
3948                     FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
3949                 cpfx++;
3950                 point++;
3951             }
3952             if (buf)
3953             {
3954                 ppc->wType = type;
3955                 ppc->cpfx = cpfx;
3956             }
3957             needed += sizeof(*ppc) + (cpfx - 1) * sizeof(POINTFX);
3958         }
3959         if (buf)
3960             pph->cb = needed - pph_start;
3961     }
3962     return needed;
3963 }
3964 
get_bezier_glyph_outline(FT_Outline * outline,unsigned int buflen,char * buf)3965 static unsigned int get_bezier_glyph_outline(FT_Outline *outline, unsigned int buflen, char *buf)
3966 {
3967     /* Convert the quadratic Beziers to cubic Beziers.
3968        The parametric eqn for a cubic Bezier is, from PLRM:
3969        r(t) = at^3 + bt^2 + ct + r0
3970        with the control points:
3971        r1 = r0 + c/3
3972        r2 = r1 + (c + b)/3
3973        r3 = r0 + c + b + a
3974 
3975        A quadratic Bezier has the form:
3976        p(t) = (1-t)^2 p0 + 2(1-t)t p1 + t^2 p2
3977 
3978        So equating powers of t leads to:
3979        r1 = 2/3 p1 + 1/3 p0
3980        r2 = 2/3 p1 + 1/3 p2
3981        and of course r0 = p0, r3 = p2
3982     */
3983     int contour, point = 0, first_pt;
3984     TTPOLYGONHEADER *pph;
3985     TTPOLYCURVE *ppc;
3986     DWORD pph_start, cpfx, type;
3987     FT_Vector cubic_control[4];
3988     unsigned int needed = 0;
3989 
3990     for (contour = 0; contour < outline->n_contours; contour++)
3991     {
3992         pph_start = needed;
3993         pph = (TTPOLYGONHEADER *)(buf + needed);
3994         first_pt = point;
3995         if (buf)
3996         {
3997             pph->dwType = TT_POLYGON_TYPE;
3998             FTVectorToPOINTFX(&outline->points[point], &pph->pfxStart);
3999         }
4000         needed += sizeof(*pph);
4001         point++;
4002         while (point <= outline->contours[contour])
4003         {
4004             ppc = (TTPOLYCURVE *)(buf + needed);
4005             type = (outline->tags[point] & FT_Curve_Tag_On) ?
4006                 TT_PRIM_LINE : TT_PRIM_CSPLINE;
4007             cpfx = 0;
4008             do
4009             {
4010                 if (type == TT_PRIM_LINE)
4011                 {
4012                     if (buf)
4013                         FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
4014                     cpfx++;
4015                     point++;
4016                 }
4017                 else
4018                 {
4019                     /* Unlike QSPLINEs, CSPLINEs always have their endpoint
4020                        so cpfx = 3n */
4021 
4022                     /* FIXME: Possible optimization in endpoint calculation
4023                        if there are two consecutive curves */
4024                     cubic_control[0] = outline->points[point-1];
4025                     if (!(outline->tags[point-1] & FT_Curve_Tag_On))
4026                     {
4027                         cubic_control[0].x += outline->points[point].x + 1;
4028                         cubic_control[0].y += outline->points[point].y + 1;
4029                         cubic_control[0].x >>= 1;
4030                         cubic_control[0].y >>= 1;
4031                     }
4032                     if (point+1 > outline->contours[contour])
4033                         cubic_control[3] = outline->points[first_pt];
4034                     else
4035                     {
4036                         cubic_control[3] = outline->points[point+1];
4037                         if (!(outline->tags[point+1] & FT_Curve_Tag_On))
4038                         {
4039                             cubic_control[3].x += outline->points[point].x + 1;
4040                             cubic_control[3].y += outline->points[point].y + 1;
4041                             cubic_control[3].x >>= 1;
4042                             cubic_control[3].y >>= 1;
4043                         }
4044                     }
4045                     /* r1 = 1/3 p0 + 2/3 p1
4046                        r2 = 1/3 p2 + 2/3 p1 */
4047                     cubic_control[1].x = (2 * outline->points[point].x + 1) / 3;
4048                     cubic_control[1].y = (2 * outline->points[point].y + 1) / 3;
4049                     cubic_control[2] = cubic_control[1];
4050                     cubic_control[1].x += (cubic_control[0].x + 1) / 3;
4051                     cubic_control[1].y += (cubic_control[0].y + 1) / 3;
4052                     cubic_control[2].x += (cubic_control[3].x + 1) / 3;
4053                     cubic_control[2].y += (cubic_control[3].y + 1) / 3;
4054                     if (buf)
4055                     {
4056                         FTVectorToPOINTFX(&cubic_control[1], &ppc->apfx[cpfx]);
4057                         FTVectorToPOINTFX(&cubic_control[2], &ppc->apfx[cpfx+1]);
4058                         FTVectorToPOINTFX(&cubic_control[3], &ppc->apfx[cpfx+2]);
4059                     }
4060                     cpfx += 3;
4061                     point++;
4062                 }
4063             } while (point <= outline->contours[contour] &&
4064                     (outline->tags[point] & FT_Curve_Tag_On) ==
4065                     (outline->tags[point-1] & FT_Curve_Tag_On));
4066             /* At the end of a contour Windows adds the start point,
4067                but only for Beziers and we've already done that.
4068             */
4069             if (point <= outline->contours[contour] &&
4070                outline->tags[point] & FT_Curve_Tag_On)
4071             {
4072                 /* This is the closing pt of a bezier, but we've already
4073                    added it, so just inc point and carry on */
4074                 point++;
4075             }
4076             if (buf)
4077             {
4078                 ppc->wType = type;
4079                 ppc->cpfx = cpfx;
4080             }
4081             needed += sizeof(*ppc) + (cpfx - 1) * sizeof(POINTFX);
4082         }
4083         if (buf)
4084             pph->cb = needed - pph_start;
4085     }
4086     return needed;
4087 }
4088 
4089 static FT_Error
IntRequestFontSize(PDC dc,PFONTGDI FontGDI,LONG lfWidth,LONG lfHeight)4090 IntRequestFontSize(PDC dc, PFONTGDI FontGDI, LONG lfWidth, LONG lfHeight)
4091 {
4092     FT_Error error;
4093     FT_Size_RequestRec  req;
4094     FT_Face face = FontGDI->SharedFace->Face;
4095     TT_OS2 *pOS2;
4096     TT_HoriHeader *pHori;
4097     FT_WinFNT_HeaderRec WinFNT;
4098     LONG Ascent, Descent, Sum, EmHeight, Width64;
4099 
4100     lfWidth = abs(lfWidth);
4101     if (lfHeight == 0)
4102     {
4103         if (lfWidth == 0)
4104         {
4105             DPRINT("lfHeight and lfWidth are zero.\n");
4106             lfHeight = -16;
4107         }
4108         else
4109         {
4110             lfHeight = lfWidth;
4111         }
4112     }
4113 
4114     if (lfHeight == -1)
4115         lfHeight = -2;
4116 
4117     if (FontGDI->Magic == FONTGDI_MAGIC &&
4118         FontGDI->lfHeight == lfHeight &&
4119         FontGDI->lfWidth == lfWidth)
4120     {
4121         return 0; /* Cached */
4122     }
4123 
4124     ASSERT_FREETYPE_LOCK_HELD();
4125     pOS2 = (TT_OS2 *)FT_Get_Sfnt_Table(face, FT_SFNT_OS2);
4126     pHori = (TT_HoriHeader *)FT_Get_Sfnt_Table(face, FT_SFNT_HHEA);
4127 
4128     if (!pOS2 || !pHori)
4129     {
4130         error = FT_Get_WinFNT_Header(face, &WinFNT);
4131         if (error)
4132         {
4133             DPRINT1("%s: Failed to request font size.\n", face->family_name);
4134             ASSERT(FALSE);
4135             return error;
4136         }
4137 
4138         FontGDI->tmHeight           = WinFNT.pixel_height;
4139         FontGDI->tmAscent           = WinFNT.ascent;
4140         FontGDI->tmDescent          = FontGDI->tmHeight - FontGDI->tmAscent;
4141         FontGDI->tmInternalLeading  = WinFNT.internal_leading;
4142         FontGDI->Magic              = FONTGDI_MAGIC;
4143         FontGDI->lfHeight           = lfHeight;
4144         FontGDI->lfWidth            = lfWidth;
4145         return 0;
4146     }
4147 
4148     /*
4149      * NOTE: We cast TT_OS2.usWinAscent and TT_OS2.usWinDescent to signed FT_Short.
4150      * Why? See: https://learn.microsoft.com/en-us/typography/opentype/spec/os2#uswindescent
4151      *
4152      * > usWinDescent is "usually" a positive value ...
4153      *
4154      * We can read it as "not always". See CORE-14994.
4155      * See also: https://learn.microsoft.com/en-us/typography/opentype/spec/os2#fsselection
4156      */
4157 #define FM_SEL_USE_TYPO_METRICS 0x80
4158     if (lfHeight > 0)
4159     {
4160         /* case (A): lfHeight is positive */
4161         Sum = (FT_Short)pOS2->usWinAscent + (FT_Short)pOS2->usWinDescent;
4162         if (Sum == 0 || (pOS2->fsSelection & FM_SEL_USE_TYPO_METRICS))
4163         {
4164             Ascent = pHori->Ascender;
4165             Descent = -pHori->Descender;
4166             Sum = Ascent + Descent;
4167         }
4168         else
4169         {
4170             Ascent = (FT_Short)pOS2->usWinAscent;
4171             Descent = (FT_Short)pOS2->usWinDescent;
4172         }
4173 
4174         FontGDI->tmAscent = FT_MulDiv(lfHeight, Ascent, Sum);
4175         FontGDI->tmDescent = FT_MulDiv(lfHeight, Descent, Sum);
4176         FontGDI->tmHeight = FontGDI->tmAscent + FontGDI->tmDescent;
4177         FontGDI->tmInternalLeading = FontGDI->tmHeight - FT_MulDiv(lfHeight, face->units_per_EM, Sum);
4178     }
4179     else if (lfHeight < 0)
4180     {
4181         /* case (B): lfHeight is negative */
4182         if (pOS2->fsSelection & FM_SEL_USE_TYPO_METRICS)
4183         {
4184             FontGDI->tmAscent = FT_MulDiv(-lfHeight, pHori->Ascender, face->units_per_EM);
4185             FontGDI->tmDescent = FT_MulDiv(-lfHeight, -pHori->Descender, face->units_per_EM);
4186         }
4187         else
4188         {
4189             FontGDI->tmAscent = FT_MulDiv(-lfHeight, (FT_Short)pOS2->usWinAscent, face->units_per_EM);
4190             FontGDI->tmDescent = FT_MulDiv(-lfHeight, (FT_Short)pOS2->usWinDescent, face->units_per_EM);
4191         }
4192         FontGDI->tmHeight = FontGDI->tmAscent + FontGDI->tmDescent;
4193         FontGDI->tmInternalLeading = FontGDI->tmHeight + lfHeight;
4194     }
4195 #undef FM_SEL_USE_TYPO_METRICS
4196 
4197     FontGDI->Magic = FONTGDI_MAGIC;
4198     FontGDI->lfHeight = lfHeight;
4199     FontGDI->lfWidth = lfWidth;
4200 
4201     EmHeight = FontGDI->tmHeight - FontGDI->tmInternalLeading;
4202     EmHeight = max(EmHeight, 1);
4203     EmHeight = min(EmHeight, USHORT_MAX);
4204 
4205 #if 1
4206     /* I think this is wrong implementation but its test result is better. */
4207     if (lfWidth != 0)
4208         Width64 = FT_MulDiv(lfWidth, face->units_per_EM, pOS2->xAvgCharWidth) << 6;
4209     else
4210         Width64 = 0;
4211 #else
4212     /* I think this is correct implementation but it is mismatching to the
4213        other metric functions. The test result is bad. */
4214     if (lfWidth != 0)
4215         Width64 = (FT_MulDiv(lfWidth, 96 * 5, 72 * 3) << 6); /* ??? FIXME */
4216     else
4217         Width64 = 0;
4218 #endif
4219 
4220     req.type           = FT_SIZE_REQUEST_TYPE_NOMINAL;
4221     req.width          = Width64;
4222     req.height         = (EmHeight << 6);
4223     req.horiResolution = 0;
4224     req.vertResolution = 0;
4225     return FT_Request_Size(face, &req);
4226 }
4227 
4228 BOOL
4229 FASTCALL
TextIntUpdateSize(PDC dc,PTEXTOBJ TextObj,PFONTGDI FontGDI,BOOL bDoLock)4230 TextIntUpdateSize(PDC dc,
4231                   PTEXTOBJ TextObj,
4232                   PFONTGDI FontGDI,
4233                   BOOL bDoLock)
4234 {
4235     FT_Face face;
4236     INT error, n;
4237     FT_CharMap charmap, found;
4238     LOGFONTW *plf;
4239 
4240     if (bDoLock)
4241         IntLockFreeType();
4242 
4243     face = FontGDI->SharedFace->Face;
4244     if (face->charmap == NULL)
4245     {
4246         DPRINT("WARNING: No charmap selected!\n");
4247         DPRINT("This font face has %d charmaps\n", face->num_charmaps);
4248 
4249         found = NULL;
4250         for (n = 0; n < face->num_charmaps; n++)
4251         {
4252             charmap = face->charmaps[n];
4253             if (charmap->encoding == FT_ENCODING_UNICODE)
4254             {
4255                 found = charmap;
4256                 break;
4257             }
4258         }
4259         if (!found)
4260         {
4261             for (n = 0; n < face->num_charmaps; n++)
4262             {
4263                 charmap = face->charmaps[n];
4264                 if (charmap->platform_id == TT_PLATFORM_APPLE_UNICODE)
4265                 {
4266                     found = charmap;
4267                     break;
4268                 }
4269             }
4270         }
4271         if (!found)
4272         {
4273             for (n = 0; n < face->num_charmaps; n++)
4274             {
4275                 charmap = face->charmaps[n];
4276                 if (charmap->encoding == FT_ENCODING_MS_SYMBOL)
4277                 {
4278                     found = charmap;
4279                     break;
4280                 }
4281             }
4282         }
4283         if (!found && face->num_charmaps > 0)
4284         {
4285             found = face->charmaps[0];
4286         }
4287         if (!found)
4288         {
4289             DPRINT1("WARNING: Could not find desired charmap!\n");
4290         }
4291         else
4292         {
4293             DPRINT("Found charmap encoding: %i\n", found->encoding);
4294             error = FT_Set_Charmap(face, found);
4295             if (error)
4296             {
4297                 DPRINT1("WARNING: Could not set the charmap!\n");
4298             }
4299         }
4300     }
4301 
4302     plf = &TextObj->logfont.elfEnumLogfontEx.elfLogFont;
4303 
4304     error = IntRequestFontSize(dc, FontGDI, plf->lfWidth, plf->lfHeight);
4305 
4306     if (bDoLock)
4307         IntUnLockFreeType();
4308 
4309     if (error)
4310     {
4311         DPRINT1("Error in setting pixel sizes: %d\n", error);
4312         return FALSE;
4313     }
4314 
4315     return TRUE;
4316 }
4317 
4318 static inline FT_UInt FASTCALL
get_glyph_index_symbol(FT_Face ft_face,UINT glyph)4319 get_glyph_index_symbol(FT_Face ft_face, UINT glyph)
4320 {
4321     FT_UInt ret;
4322 
4323     if (glyph < 0x100) glyph += 0xf000;
4324     /* there are a number of old pre-Unicode "broken" TTFs, which
4325        do have symbols at U+00XX instead of U+f0XX */
4326     if (!(ret = FT_Get_Char_Index(ft_face, glyph)))
4327         ret = FT_Get_Char_Index(ft_face, glyph - 0xf000);
4328 
4329     return ret;
4330 }
4331 
4332 static inline FT_UInt FASTCALL
get_glyph_index(FT_Face ft_face,UINT glyph)4333 get_glyph_index(FT_Face ft_face, UINT glyph)
4334 {
4335     FT_UInt ret;
4336 
4337     if (face_has_symbol_charmap(ft_face))
4338     {
4339         ret = get_glyph_index_symbol(ft_face, glyph);
4340         if (ret != 0)
4341             return ret;
4342     }
4343 
4344     return FT_Get_Char_Index(ft_face, glyph);
4345 }
4346 
4347 static inline FT_UInt FASTCALL
get_glyph_index_flagged(FT_Face face,FT_ULong code,BOOL fCodeAsIndex)4348 get_glyph_index_flagged(FT_Face face, FT_ULong code, BOOL fCodeAsIndex)
4349 {
4350     return (fCodeAsIndex ? code : get_glyph_index(face, code));
4351 }
4352 
4353 static inline VOID
FontLink_Chain_Dump(_In_ PFONTLINK_CHAIN pChain)4354 FontLink_Chain_Dump(
4355     _In_ PFONTLINK_CHAIN pChain)
4356 {
4357 #if 0
4358     PLIST_ENTRY Entry, Head;
4359     PFONTLINK pFontLink;
4360     INT iLink = 0;
4361 
4362     DPRINT1("%S, %p, %p\n", pChain->LogFont.lfFaceName, pChain->pBaseTextObj, pChain->pDefFace);
4363 
4364     Head = &pChain->FontLinkList;
4365     for (Entry = Head->Flink; Entry != Head; Entry = Entry->Flink)
4366     {
4367         pFontLink = CONTAINING_RECORD(Entry, FONTLINK, ListEntry);
4368         DPRINT1("FontLink #%d: %p, %d, %S, %p, %p\n",
4369                 iLink, pFontLink, pFontLink->bIgnore, pFontLink->LogFont.lfFaceName,
4370                 pFontLink->pFontGDI, pFontLink->SharedFace);
4371         ++iLink;
4372     }
4373 #endif
4374 }
4375 
4376 /// Search the target glyph and update the current font info.
4377 /// @return The glyph index
4378 static UINT
FontLink_Chain_FindGlyph(_Inout_ PFONTLINK_CHAIN pChain,_Out_ PFONT_CACHE_ENTRY pCache,_Inout_ FT_Face * pFace,_In_ UINT code,_In_ BOOL fCodeAsIndex)4379 FontLink_Chain_FindGlyph(
4380     _Inout_ PFONTLINK_CHAIN pChain,
4381     _Out_ PFONT_CACHE_ENTRY pCache,
4382     _Inout_ FT_Face *pFace,
4383     _In_ UINT code,
4384     _In_ BOOL fCodeAsIndex)
4385 {
4386     PFONTLINK pFontLink;
4387     PLIST_ENTRY Entry, Head;
4388     UINT index;
4389     FT_Face face;
4390 
4391     // Try the default font at first
4392     index = get_glyph_index_flagged(pChain->pDefFace, code, fCodeAsIndex);
4393     if (index)
4394     {
4395         DPRINT("code: 0x%08X, index: 0x%08X, fCodeAsIndex:%d\n", code, index, fCodeAsIndex);
4396         pCache->Hashed.Face = *pFace = pChain->pDefFace;
4397         return index; // The glyph is found on the default font
4398     }
4399 
4400     if (!FontLink_Chain_IsPopulated(pChain)) // The chain is not populated yet
4401     {
4402         FontLink_Chain_Populate(pChain);
4403         FontLink_Chain_Dump(pChain);
4404     }
4405 
4406     // Now the chain is populated. Looking for the target glyph...
4407     Head = &pChain->FontLinkList;
4408     for (Entry = Head->Flink; Entry != Head; Entry = Entry->Flink)
4409     {
4410         pFontLink = CONTAINING_RECORD(Entry, FONTLINK, ListEntry);
4411         if (!FontLink_PrepareFontInfo(pFontLink))
4412             continue; // This link is not useful, check the next one
4413 
4414         face = pFontLink->SharedFace->Face;
4415         index = get_glyph_index(face, code);
4416         if (!index)
4417             continue; // The glyph does not exist, continue searching
4418 
4419         // The target glyph is found in the chain
4420         DPRINT("code: 0x%08X, index: 0x%08X\n", code, index);
4421         pCache->Hashed.Face = *pFace = face;
4422         FT_Set_Transform(face, &pCache->Hashed.matTransform, NULL);
4423         return index;
4424     }
4425 
4426     // No target glyph found in the chain: use default glyph
4427     code = s_chFontLinkDefaultChar;
4428     index = get_glyph_index(*pFace, code);
4429     DPRINT("code: 0x%08X, index: 0x%08X\n", code, index);
4430     pCache->Hashed.Face = *pFace = pChain->pDefFace;
4431     return index;
4432 }
4433 
4434 /*
4435  * Based on WineEngGetGlyphOutline
4436  */
4437 ULONG
4438 FASTCALL
ftGdiGetGlyphOutline(PDC dc,WCHAR wch,UINT iFormat,LPGLYPHMETRICS pgm,ULONG cjBuf,PVOID pvBuf,LPMAT2 pmat2,BOOL bIgnoreRotation)4439 ftGdiGetGlyphOutline(
4440     PDC dc,
4441     WCHAR wch,
4442     UINT iFormat,
4443     LPGLYPHMETRICS pgm,
4444     ULONG cjBuf,
4445     PVOID pvBuf,
4446     LPMAT2 pmat2,
4447     BOOL bIgnoreRotation)
4448 {
4449     PDC_ATTR pdcattr;
4450     PTEXTOBJ TextObj;
4451     PFONTGDI FontGDI;
4452     HFONT hFont = 0;
4453     GLYPHMETRICS gm;
4454     ULONG Size;
4455     FT_Face ft_face;
4456     FT_UInt glyph_index;
4457     DWORD width, height, pitch, needed = 0;
4458     FT_Bitmap ft_bitmap;
4459     FT_Error error;
4460     INT left, right, top = 0, bottom = 0;
4461     FT_Int load_flags = FT_LOAD_DEFAULT | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
4462     FLOATOBJ eM11, widthRatio, eTemp;
4463     FT_Matrix mat, transMat = identityMat;
4464     BOOL needsTransform = FALSE;
4465     INT orientation;
4466     LONG aveWidth;
4467     INT adv, lsb, bbx; /* These three hold to widths of the unrotated chars */
4468     OUTLINETEXTMETRICW *potm;
4469     XFORMOBJ xo;
4470     XFORML xform;
4471     LOGFONTW *plf;
4472 
4473     DPRINT("%u, %08x, %p, %08lx, %p, %p\n", wch, iFormat, pgm,
4474            cjBuf, pvBuf, pmat2);
4475 
4476     pdcattr = dc->pdcattr;
4477 
4478     XFORMOBJ_vInit(&xo, &dc->pdcattr->mxWorldToDevice);
4479     XFORMOBJ_iGetXform(&xo, &xform);
4480     FLOATOBJ_SetFloat(&eM11, xform.eM11);
4481 
4482     hFont = pdcattr->hlfntNew;
4483     TextObj = RealizeFontInit(hFont);
4484 
4485     if (!TextObj)
4486     {
4487         EngSetLastError(ERROR_INVALID_HANDLE);
4488         return GDI_ERROR;
4489     }
4490     FontGDI = ObjToGDI(TextObj->Font, FONT);
4491     ft_face = FontGDI->SharedFace->Face;
4492 
4493     plf = &TextObj->logfont.elfEnumLogfontEx.elfLogFont;
4494     aveWidth = FT_IS_SCALABLE(ft_face) ? abs(plf->lfWidth) : 0;
4495     orientation = FT_IS_SCALABLE(ft_face) ? plf->lfOrientation : 0;
4496 
4497     ASSERT_FREETYPE_LOCK_NOT_HELD();
4498     Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL, FALSE);
4499     if (!Size)
4500     {
4501         TEXTOBJ_UnlockText(TextObj);
4502         EngSetLastError(ERROR_GEN_FAILURE);
4503         return GDI_ERROR;
4504     }
4505     potm = ExAllocatePoolWithTag(PagedPool, Size, GDITAG_TEXT);
4506     if (!potm)
4507     {
4508         TEXTOBJ_UnlockText(TextObj);
4509         EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
4510         return GDI_ERROR;
4511     }
4512     ASSERT_FREETYPE_LOCK_NOT_HELD();
4513     Size = IntGetOutlineTextMetrics(FontGDI, Size, potm, FALSE);
4514     if (!Size)
4515     {
4516         ExFreePoolWithTag(potm, GDITAG_TEXT);
4517         TEXTOBJ_UnlockText(TextObj);
4518         EngSetLastError(ERROR_GEN_FAILURE);
4519         return GDI_ERROR;
4520     }
4521 
4522     IntLockFreeType();
4523     TextIntUpdateSize(dc, TextObj, FontGDI, FALSE);
4524     IntMatrixFromMx(&mat, DC_pmxWorldToDevice(dc));
4525     FT_Set_Transform(ft_face, &mat, NULL);
4526 
4527     TEXTOBJ_UnlockText(TextObj);
4528 
4529     glyph_index = get_glyph_index_flagged(ft_face, wch, (iFormat & GGO_GLYPH_INDEX));
4530     iFormat &= ~GGO_GLYPH_INDEX;
4531 
4532     if (orientation || (iFormat != GGO_METRICS && iFormat != GGO_BITMAP) || aveWidth || pmat2)
4533         load_flags |= FT_LOAD_NO_BITMAP;
4534 
4535     if (iFormat & GGO_UNHINTED)
4536     {
4537         load_flags |= FT_LOAD_NO_HINTING;
4538         iFormat &= ~GGO_UNHINTED;
4539     }
4540 
4541     error = FT_Load_Glyph(ft_face, glyph_index, load_flags);
4542     if (error)
4543     {
4544         DPRINT1("WARNING: Failed to load and render glyph! [index: %u]\n", glyph_index);
4545         IntUnLockFreeType();
4546         if (potm) ExFreePoolWithTag(potm, GDITAG_TEXT);
4547         return GDI_ERROR;
4548     }
4549     IntUnLockFreeType();
4550 
4551     FLOATOBJ_Set1(&widthRatio);
4552     if (aveWidth && potm)
4553     {
4554         // widthRatio = aveWidth * eM11 / potm->otmTextMetrics.tmAveCharWidth
4555         FLOATOBJ_SetLong(&widthRatio, aveWidth);
4556         FLOATOBJ_Mul(&widthRatio, &eM11);
4557         FLOATOBJ_DivLong(&widthRatio, potm->otmTextMetrics.tmAveCharWidth);
4558     }
4559 
4560     //left = (INT)(ft_face->glyph->metrics.horiBearingX * widthRatio) & -64;
4561     FLOATOBJ_SetLong(&eTemp, ft_face->glyph->metrics.horiBearingX);
4562     FLOATOBJ_Mul(&eTemp, &widthRatio);
4563     left = FLOATOBJ_GetLong(&eTemp) & -64;
4564 
4565     //right = (INT)((ft_face->glyph->metrics.horiBearingX +
4566     //               ft_face->glyph->metrics.width) * widthRatio + 63) & -64;
4567     FLOATOBJ_SetLong(&eTemp, ft_face->glyph->metrics.horiBearingX * ft_face->glyph->metrics.width);
4568     FLOATOBJ_Mul(&eTemp, &widthRatio);
4569     FLOATOBJ_AddLong(&eTemp, 63);
4570     right = FLOATOBJ_GetLong(&eTemp) & -64;
4571 
4572     //adv = (INT)((ft_face->glyph->metrics.horiAdvance * widthRatio) + 63) >> 6;
4573     FLOATOBJ_SetLong(&eTemp, ft_face->glyph->metrics.horiAdvance);
4574     FLOATOBJ_Mul(&eTemp, &widthRatio);
4575     FLOATOBJ_AddLong(&eTemp, 63);
4576     adv = FLOATOBJ_GetLong(&eTemp) >> 6;
4577 
4578     lsb = left >> 6;
4579     bbx = (right - left) >> 6;
4580 
4581     DPRINT("Advance = %d, lsb = %d, bbx = %d\n",adv, lsb, bbx);
4582 
4583     IntLockFreeType();
4584 
4585     /* Width scaling transform */
4586     if (!FLOATOBJ_Equal1(&widthRatio))
4587     {
4588         FT_Matrix scaleMat;
4589 
4590         eTemp = widthRatio;
4591         FLOATOBJ_MulLong(&eTemp, 1 << 16);
4592 
4593         scaleMat.xx = FLOATOBJ_GetLong(&eTemp);
4594         scaleMat.xy = 0;
4595         scaleMat.yx = 0;
4596         scaleMat.yy = INT_TO_FIXED(1);
4597         FT_Matrix_Multiply(&scaleMat, &transMat);
4598         needsTransform = TRUE;
4599     }
4600 
4601     /* World transform */
4602     {
4603         FT_Matrix ftmatrix;
4604         PMATRIX pmx = DC_pmxWorldToDevice(dc);
4605 
4606         /* Create a freetype matrix, by converting to 16.16 fixpoint format */
4607         IntMatrixFromMx(&ftmatrix, pmx);
4608 
4609         if (memcmp(&ftmatrix, &identityMat, sizeof(identityMat)) != 0)
4610         {
4611             FT_Matrix_Multiply(&ftmatrix, &transMat);
4612             needsTransform = TRUE;
4613         }
4614     }
4615 
4616     /* Rotation transform */
4617     if (orientation)
4618     {
4619         FT_Matrix rotationMat;
4620         DPRINT("Rotation Trans!\n");
4621         IntEscapeMatrix(&rotationMat, orientation);
4622         FT_Matrix_Multiply(&rotationMat, &transMat);
4623         needsTransform = TRUE;
4624     }
4625 
4626     /* Extra transformation specified by caller */
4627     if (pmat2)
4628     {
4629         FT_Matrix extraMat;
4630         DPRINT("MAT2 Matrix Trans!\n");
4631         extraMat.xx = FT_FixedFromFIXED(pmat2->eM11);
4632         extraMat.xy = FT_FixedFromFIXED(pmat2->eM21);
4633         extraMat.yx = FT_FixedFromFIXED(pmat2->eM12);
4634         extraMat.yy = FT_FixedFromFIXED(pmat2->eM22);
4635         FT_Matrix_Multiply(&extraMat, &transMat);
4636         needsTransform = TRUE;
4637     }
4638 
4639     if (potm) ExFreePoolWithTag(potm, GDITAG_TEXT); /* It looks like we are finished with potm ATM. */
4640 
4641     if (!needsTransform)
4642     {
4643         DPRINT("No Need to be Transformed!\n");
4644         top = (ft_face->glyph->metrics.horiBearingY + 63) & -64;
4645         bottom = (ft_face->glyph->metrics.horiBearingY -
4646                   ft_face->glyph->metrics.height) & -64;
4647         gm.gmCellIncX = adv;
4648         gm.gmCellIncY = 0;
4649     }
4650     else
4651     {
4652         INT xc, yc;
4653         FT_Vector vec;
4654         for (xc = 0; xc < 2; xc++)
4655         {
4656             for (yc = 0; yc < 2; yc++)
4657             {
4658                 vec.x = (ft_face->glyph->metrics.horiBearingX +
4659                          xc * ft_face->glyph->metrics.width);
4660                 vec.y = ft_face->glyph->metrics.horiBearingY -
4661                         yc * ft_face->glyph->metrics.height;
4662                 DPRINT("Vec %ld,%ld\n", vec.x, vec.y);
4663                 FT_Vector_Transform(&vec, &transMat);
4664                 if (xc == 0 && yc == 0)
4665                 {
4666                     left = right = vec.x;
4667                     top = bottom = vec.y;
4668                 }
4669                 else
4670                 {
4671                     if (vec.x < left) left = vec.x;
4672                     else if (vec.x > right) right = vec.x;
4673                     if (vec.y < bottom) bottom = vec.y;
4674                     else if (vec.y > top) top = vec.y;
4675                 }
4676             }
4677         }
4678         left = left & -64;
4679         right = (right + 63) & -64;
4680         bottom = bottom & -64;
4681         top = (top + 63) & -64;
4682 
4683         DPRINT("Transformed box: (%d,%d - %d,%d)\n", left, top, right, bottom);
4684         vec.x = ft_face->glyph->metrics.horiAdvance;
4685         vec.y = 0;
4686         FT_Vector_Transform(&vec, &transMat);
4687         gm.gmCellIncX = (vec.x+63) >> 6;
4688         gm.gmCellIncY = -((vec.y+63) >> 6);
4689     }
4690     gm.gmBlackBoxX = (right - left) >> 6;
4691     gm.gmBlackBoxY = (top - bottom) >> 6;
4692     gm.gmptGlyphOrigin.x = left >> 6;
4693     gm.gmptGlyphOrigin.y = top >> 6;
4694 
4695     DPRINT("CX %d CY %d BBX %u BBY %u GOX %d GOY %d\n",
4696            gm.gmCellIncX, gm.gmCellIncY,
4697            gm.gmBlackBoxX, gm.gmBlackBoxY,
4698            gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y);
4699 
4700     IntUnLockFreeType();
4701 
4702     if (iFormat == GGO_METRICS)
4703     {
4704         DPRINT("GGO_METRICS Exit!\n");
4705         *pgm = gm;
4706         return 1; /* FIXME */
4707     }
4708 
4709     if (ft_face->glyph->format != ft_glyph_format_outline && iFormat != GGO_BITMAP)
4710     {
4711         DPRINT1("Loaded a bitmap\n");
4712         return GDI_ERROR;
4713     }
4714 
4715     switch (iFormat)
4716     {
4717     case GGO_BITMAP:
4718     {
4719         width = gm.gmBlackBoxX;
4720         height = gm.gmBlackBoxY;
4721         pitch = ((width + 31) >> 5) << 2;
4722         needed = pitch * height;
4723 
4724         if (!pvBuf || !cjBuf) break;
4725         if (!needed) return GDI_ERROR;  /* empty glyph */
4726         if (needed > cjBuf)
4727             return GDI_ERROR;
4728 
4729         switch (ft_face->glyph->format)
4730         {
4731         case ft_glyph_format_bitmap:
4732         {
4733             BYTE *src = ft_face->glyph->bitmap.buffer, *dst = pvBuf;
4734             INT w = min( pitch, (ft_face->glyph->bitmap.width + 7) >> 3 );
4735             INT h = min( height, ft_face->glyph->bitmap.rows );
4736             while (h--)
4737             {
4738                 RtlCopyMemory(dst, src, w);
4739                 src += ft_face->glyph->bitmap.pitch;
4740                 dst += pitch;
4741             }
4742             break;
4743         }
4744 
4745         case ft_glyph_format_outline:
4746         {
4747             ft_bitmap.width = width;
4748             ft_bitmap.rows = height;
4749             ft_bitmap.pitch = pitch;
4750             ft_bitmap.pixel_mode = FT_PIXEL_MODE_MONO;
4751             ft_bitmap.buffer = pvBuf;
4752 
4753             IntLockFreeType();
4754             if (needsTransform)
4755             {
4756                 FT_Outline_Transform(&ft_face->glyph->outline, &transMat);
4757             }
4758             FT_Outline_Translate(&ft_face->glyph->outline, -left, -bottom );
4759             /* Note: FreeType will only set 'black' bits for us. */
4760             RtlZeroMemory(pvBuf, needed);
4761             FT_Outline_Get_Bitmap(g_FreeTypeLibrary, &ft_face->glyph->outline, &ft_bitmap);
4762             IntUnLockFreeType();
4763             break;
4764         }
4765 
4766         default:
4767             DPRINT1("Loaded glyph format %x\n", ft_face->glyph->format);
4768             return GDI_ERROR;
4769         }
4770 
4771         break;
4772     }
4773 
4774     case GGO_GRAY2_BITMAP:
4775     case GGO_GRAY4_BITMAP:
4776     case GGO_GRAY8_BITMAP:
4777     {
4778         unsigned int mult, row, col;
4779         BYTE *start, *ptr;
4780 
4781         width = gm.gmBlackBoxX;
4782         height = gm.gmBlackBoxY;
4783         pitch = (width + 3) / 4 * 4;
4784         needed = pitch * height;
4785 
4786         if (!pvBuf || !cjBuf) break;
4787         if (!needed) return GDI_ERROR;  /* empty glyph */
4788         if (needed > cjBuf)
4789             return GDI_ERROR;
4790 
4791         switch (ft_face->glyph->format)
4792         {
4793         case ft_glyph_format_bitmap:
4794         {
4795             BYTE *src = ft_face->glyph->bitmap.buffer, *dst = pvBuf;
4796             INT h = min( height, ft_face->glyph->bitmap.rows );
4797             INT x;
4798             while (h--)
4799             {
4800                 for (x = 0; (UINT)x < pitch; x++)
4801                 {
4802                     if (x < ft_face->glyph->bitmap.width)
4803                         dst[x] = (src[x / 8] & (1 << ( (7 - (x % 8))))) ? 0xff : 0;
4804                     else
4805                         dst[x] = 0;
4806                 }
4807                 src += ft_face->glyph->bitmap.pitch;
4808                 dst += pitch;
4809             }
4810             break;
4811         }
4812         case ft_glyph_format_outline:
4813         {
4814             ft_bitmap.width = width;
4815             ft_bitmap.rows = height;
4816             ft_bitmap.pitch = pitch;
4817             ft_bitmap.pixel_mode = FT_PIXEL_MODE_GRAY;
4818             ft_bitmap.buffer = pvBuf;
4819 
4820             IntLockFreeType();
4821             if (needsTransform)
4822             {
4823                 FT_Outline_Transform(&ft_face->glyph->outline, &transMat);
4824             }
4825             FT_Outline_Translate(&ft_face->glyph->outline, -left, -bottom );
4826             RtlZeroMemory(ft_bitmap.buffer, cjBuf);
4827             FT_Outline_Get_Bitmap(g_FreeTypeLibrary, &ft_face->glyph->outline, &ft_bitmap);
4828             IntUnLockFreeType();
4829 
4830             if (iFormat == GGO_GRAY2_BITMAP)
4831                 mult = 4;
4832             else if (iFormat == GGO_GRAY4_BITMAP)
4833                 mult = 16;
4834             else if (iFormat == GGO_GRAY8_BITMAP)
4835                 mult = 64;
4836             else
4837             {
4838                 return GDI_ERROR;
4839             }
4840 
4841             start = pvBuf;
4842             for (row = 0; row < height; row++)
4843             {
4844                 ptr = start;
4845                 for (col = 0; col < width; col++, ptr++)
4846                 {
4847                     *ptr = (((int)*ptr) * mult + 128) / 256;
4848                 }
4849                 start += pitch;
4850             }
4851 
4852             break;
4853         }
4854         default:
4855             DPRINT1("Loaded glyph format %x\n", ft_face->glyph->format);
4856             return GDI_ERROR;
4857         }
4858 
4859         break;
4860     }
4861 
4862     case GGO_NATIVE:
4863     {
4864         FT_Outline *outline = &ft_face->glyph->outline;
4865 
4866         if (cjBuf == 0) pvBuf = NULL; /* This is okay, need cjBuf to allocate. */
4867 
4868         IntLockFreeType();
4869         if (needsTransform && pvBuf) FT_Outline_Transform(outline, &transMat);
4870 
4871         needed = get_native_glyph_outline(outline, cjBuf, NULL);
4872 
4873         if (!pvBuf || !cjBuf)
4874         {
4875             IntUnLockFreeType();
4876             break;
4877         }
4878         if (needed > cjBuf)
4879         {
4880             IntUnLockFreeType();
4881             return GDI_ERROR;
4882         }
4883         get_native_glyph_outline(outline, cjBuf, pvBuf);
4884         IntUnLockFreeType();
4885         break;
4886     }
4887 
4888     case GGO_BEZIER:
4889     {
4890         FT_Outline *outline = &ft_face->glyph->outline;
4891         if (cjBuf == 0) pvBuf = NULL;
4892 
4893         if (needsTransform && pvBuf)
4894         {
4895             IntLockFreeType();
4896             FT_Outline_Transform(outline, &transMat);
4897             IntUnLockFreeType();
4898         }
4899         needed = get_bezier_glyph_outline(outline, cjBuf, NULL);
4900 
4901         if (!pvBuf || !cjBuf)
4902             break;
4903         if (needed > cjBuf)
4904             return GDI_ERROR;
4905 
4906         get_bezier_glyph_outline(outline, cjBuf, pvBuf);
4907         break;
4908     }
4909 
4910     default:
4911         DPRINT1("Unsupported format %u\n", iFormat);
4912         return GDI_ERROR;
4913     }
4914 
4915     DPRINT("ftGdiGetGlyphOutline END and needed %lu\n", needed);
4916 
4917     if (gm.gmBlackBoxX == 0)
4918         gm.gmBlackBoxX = 1;
4919     if (gm.gmBlackBoxY == 0)
4920         gm.gmBlackBoxY = 1;
4921 
4922     *pgm = gm;
4923     return needed;
4924 }
4925 
4926 static FT_BitmapGlyph
IntGetRealGlyph(IN OUT PFONT_CACHE_ENTRY Cache)4927 IntGetRealGlyph(
4928     IN OUT PFONT_CACHE_ENTRY Cache)
4929 {
4930     INT error;
4931     FT_GlyphSlot glyph;
4932     FT_BitmapGlyph realglyph;
4933 
4934     ASSERT_FREETYPE_LOCK_HELD();
4935 
4936     Cache->dwHash = IntGetHash(&Cache->Hashed, sizeof(Cache->Hashed) / sizeof(DWORD));
4937 
4938     realglyph = IntFindGlyphCache(Cache);
4939     if (realglyph)
4940         return realglyph;
4941 
4942     error = FT_Load_Glyph(Cache->Hashed.Face, Cache->Hashed.GlyphIndex, FT_LOAD_DEFAULT);
4943     if (error)
4944     {
4945         DPRINT1("WARNING: Failed to load and render glyph! [index: %d]\n", Cache->Hashed.GlyphIndex);
4946         return NULL;
4947     }
4948 
4949     glyph = Cache->Hashed.Face->glyph;
4950 
4951     if (Cache->Hashed.Aspect.Emu.Bold)
4952         FT_GlyphSlot_Embolden(glyph); /* Emulate Bold */
4953 
4954     if (Cache->Hashed.Aspect.Emu.Italic)
4955         FT_GlyphSlot_Oblique(glyph); /* Emulate Italic */
4956 
4957     realglyph = IntGetBitmapGlyphWithCache(Cache, glyph);
4958 
4959     if (!realglyph)
4960         DPRINT1("Failed to render glyph! [index: %d]\n", Cache->Hashed.GlyphIndex);
4961 
4962     return realglyph;
4963 }
4964 
4965 BOOL
4966 FASTCALL
TextIntGetTextExtentPoint(PDC dc,PTEXTOBJ TextObj,LPCWSTR String,INT Count,ULONG MaxExtent,LPINT Fit,LPINT Dx,LPSIZE Size,FLONG fl)4967 TextIntGetTextExtentPoint(PDC dc,
4968                           PTEXTOBJ TextObj,
4969                           LPCWSTR String,
4970                           INT Count,
4971                           ULONG MaxExtent,
4972                           LPINT Fit,
4973                           LPINT Dx,
4974                           LPSIZE Size,
4975                           FLONG fl)
4976 {
4977     PFONTGDI FontGDI;
4978     FT_BitmapGlyph realglyph;
4979     INT glyph_index, i, previous, nTenthsOfDegrees;
4980     ULONGLONG TotalWidth64 = 0;
4981     LOGFONTW *plf;
4982     BOOL use_kerning, bVerticalWriting;
4983     LONG ascender, descender;
4984     FONT_CACHE_ENTRY Cache;
4985     DWORD ch0, ch1;
4986     FONTLINK_CHAIN Chain;
4987 
4988     FontGDI = ObjToGDI(TextObj->Font, FONT);
4989 
4990     Cache.Hashed.Face = FontGDI->SharedFace->Face;
4991     if (NULL != Fit)
4992     {
4993         *Fit = 0;
4994     }
4995 
4996     plf = &TextObj->logfont.elfEnumLogfontEx.elfLogFont;
4997     Cache.Hashed.lfHeight = plf->lfHeight;
4998     Cache.Hashed.lfWidth = plf->lfWidth;
4999     Cache.Hashed.Aspect.Emu.Bold = EMUBOLD_NEEDED(FontGDI->OriginalWeight, plf->lfWeight);
5000     Cache.Hashed.Aspect.Emu.Italic = (plf->lfItalic && !FontGDI->OriginalItalic);
5001 
5002     // Check vertical writing (tategaki)
5003     nTenthsOfDegrees = IntNormalizeAngle(plf->lfEscapement - plf->lfOrientation);
5004     bVerticalWriting = ((nTenthsOfDegrees == 90 * 10) || (nTenthsOfDegrees == 270 * 10));
5005 
5006     if (IntIsFontRenderingEnabled())
5007         Cache.Hashed.Aspect.RenderMode = (BYTE)IntGetFontRenderMode(plf);
5008     else
5009         Cache.Hashed.Aspect.RenderMode = (BYTE)FT_RENDER_MODE_MONO;
5010 
5011     // NOTE: GetTextExtentPoint32 simply ignores lfEscapement and XFORM.
5012     IntLockFreeType();
5013     TextIntUpdateSize(dc, TextObj, FontGDI, FALSE);
5014     Cache.Hashed.matTransform = identityMat;
5015     FT_Set_Transform(Cache.Hashed.Face, NULL, NULL);
5016 
5017     FontLink_Chain_Init(&Chain, TextObj, Cache.Hashed.Face);
5018 
5019     use_kerning = FT_HAS_KERNING(Cache.Hashed.Face);
5020     previous = 0;
5021 
5022     for (i = 0; i < Count; i++)
5023     {
5024         ch0 = *String++;
5025         if (IS_HIGH_SURROGATE(ch0))
5026         {
5027             ++i;
5028             if (i >= Count)
5029                 break;
5030 
5031             ch1 = *String++;
5032             if (IS_LOW_SURROGATE(ch1))
5033                 ch0 = Utf32FromSurrogatePair(ch0, ch1);
5034         }
5035 
5036         glyph_index = FontLink_Chain_FindGlyph(&Chain, &Cache, &Cache.Hashed.Face, ch0,
5037                                                (fl & GTEF_INDICES));
5038         Cache.Hashed.GlyphIndex = glyph_index;
5039 
5040         realglyph = IntGetRealGlyph(&Cache);
5041         if (!realglyph)
5042             break;
5043 
5044         /* Retrieve kerning distance */
5045         if (use_kerning && previous && glyph_index)
5046         {
5047             FT_Vector delta;
5048             FT_Get_Kerning(Cache.Hashed.Face, previous, glyph_index, 0, &delta);
5049             TotalWidth64 += delta.x;
5050         }
5051 
5052         TotalWidth64 += realglyph->root.advance.x >> 10;
5053 
5054         if (((TotalWidth64 + 32) >> 6) <= MaxExtent && NULL != Fit)
5055         {
5056             *Fit = i + 1;
5057         }
5058         if (NULL != Dx)
5059         {
5060             Dx[i] = (TotalWidth64 + 32) >> 6;
5061         }
5062 
5063         previous = glyph_index;
5064     }
5065     ASSERT(FontGDI->Magic == FONTGDI_MAGIC);
5066     ascender = FontGDI->tmAscent; /* Units above baseline */
5067     descender = FontGDI->tmDescent; /* Units below baseline */
5068     IntUnLockFreeType();
5069 
5070     if (bVerticalWriting)
5071     {
5072         Size->cx = ascender + descender;
5073         Size->cy = (TotalWidth64 + 32) >> 6;
5074     }
5075     else
5076     {
5077         Size->cx = (TotalWidth64 + 32) >> 6;
5078         Size->cy = ascender + descender;
5079     }
5080 
5081     FontLink_Chain_Finish(&Chain);
5082 
5083     return TRUE;
5084 }
5085 
5086 
5087 INT
5088 FASTCALL
ftGdiGetTextCharsetInfo(PDC Dc,LPFONTSIGNATURE lpSig,DWORD dwFlags)5089 ftGdiGetTextCharsetInfo(
5090     PDC Dc,
5091     LPFONTSIGNATURE lpSig,
5092     DWORD dwFlags)
5093 {
5094     PDC_ATTR pdcattr;
5095     UINT Ret = DEFAULT_CHARSET;
5096     INT i;
5097     HFONT hFont;
5098     PTEXTOBJ TextObj;
5099     PFONTGDI FontGdi;
5100     FONTSIGNATURE fs;
5101     TT_OS2 *pOS2;
5102     FT_Face Face;
5103     CHARSETINFO csi;
5104     DWORD cp, fs0;
5105     USHORT usACP, usOEM;
5106 
5107     pdcattr = Dc->pdcattr;
5108     hFont = pdcattr->hlfntNew;
5109     TextObj = RealizeFontInit(hFont);
5110 
5111     if (!TextObj)
5112     {
5113         EngSetLastError(ERROR_INVALID_HANDLE);
5114         return Ret;
5115     }
5116     FontGdi = ObjToGDI(TextObj->Font, FONT);
5117     Face = FontGdi->SharedFace->Face;
5118     TEXTOBJ_UnlockText(TextObj);
5119 
5120     memset(&fs, 0, sizeof(FONTSIGNATURE));
5121     IntLockFreeType();
5122     pOS2 = FT_Get_Sfnt_Table(Face, ft_sfnt_os2);
5123     if (NULL != pOS2)
5124     {
5125         fs.fsCsb[0] = pOS2->ulCodePageRange1;
5126         fs.fsCsb[1] = pOS2->ulCodePageRange2;
5127         fs.fsUsb[0] = pOS2->ulUnicodeRange1;
5128         fs.fsUsb[1] = pOS2->ulUnicodeRange2;
5129         fs.fsUsb[2] = pOS2->ulUnicodeRange3;
5130         fs.fsUsb[3] = pOS2->ulUnicodeRange4;
5131         if (pOS2->version == 0)
5132         {
5133             FT_UInt dummy;
5134 
5135             if (FT_Get_First_Char( Face, &dummy ) < 0x100)
5136                 fs.fsCsb[0] |= FS_LATIN1;
5137             else
5138                 fs.fsCsb[0] |= FS_SYMBOL;
5139         }
5140     }
5141     pOS2 = NULL;
5142     IntUnLockFreeType();
5143     DPRINT("Csb 1=%x  0=%x\n", fs.fsCsb[1],fs.fsCsb[0]);
5144     if (fs.fsCsb[0] == 0)
5145     { /* Let's see if we can find any interesting cmaps */
5146         for (i = 0; i < Face->num_charmaps; i++)
5147         {
5148             switch (Face->charmaps[i]->encoding)
5149             {
5150             case FT_ENCODING_UNICODE:
5151             case FT_ENCODING_APPLE_ROMAN:
5152                 fs.fsCsb[0] |= FS_LATIN1;
5153                 break;
5154             case FT_ENCODING_MS_SYMBOL:
5155                 fs.fsCsb[0] |= FS_SYMBOL;
5156                 break;
5157             default:
5158                 break;
5159             }
5160         }
5161     }
5162     if (lpSig)
5163     {
5164         RtlCopyMemory(lpSig, &fs, sizeof(FONTSIGNATURE));
5165     }
5166 
5167     RtlGetDefaultCodePage(&usACP, &usOEM);
5168     cp = usACP;
5169 
5170     if (IntTranslateCharsetInfo(&cp, &csi, TCI_SRCCODEPAGE))
5171         if (csi.fs.fsCsb[0] & fs.fsCsb[0])
5172         {
5173             DPRINT("Hit 1\n");
5174             Ret = csi.ciCharset;
5175             goto Exit;
5176         }
5177 
5178     for (i = 0; i < MAXTCIINDEX; i++)
5179     {
5180         fs0 = 1L << i;
5181         if (fs.fsCsb[0] & fs0)
5182         {
5183             if (IntTranslateCharsetInfo(&fs0, &csi, TCI_SRCFONTSIG))
5184             {
5185                 // *cp = csi.ciACP;
5186                 DPRINT("Hit 2\n");
5187                 Ret = csi.ciCharset;
5188                 goto Exit;
5189             }
5190             else
5191                 DPRINT1("TCI failing on %x\n", fs0);
5192         }
5193     }
5194 Exit:
5195     DPRINT("CharSet %u CodePage %u\n", csi.ciCharset, csi.ciACP);
5196     return (MAKELONG(csi.ciACP, csi.ciCharset));
5197 }
5198 
5199 
5200 DWORD
5201 FASTCALL
ftGetFontUnicodeRanges(PFONTGDI Font,PGLYPHSET glyphset)5202 ftGetFontUnicodeRanges(PFONTGDI Font, PGLYPHSET glyphset)
5203 {
5204     DWORD size = 0;
5205     DWORD num_ranges = 0;
5206     FT_Face face = Font->SharedFace->Face;
5207 
5208     if (face->charmap == NULL)
5209     {
5210         DPRINT1("FIXME: No charmap selected! This is a BUG!\n");
5211         return 0;
5212     }
5213 
5214     if (face->charmap->encoding == FT_ENCODING_UNICODE)
5215     {
5216         FT_UInt glyph_code = 0;
5217         FT_ULong char_code, char_code_prev;
5218 
5219         char_code_prev = char_code = FT_Get_First_Char(face, &glyph_code);
5220 
5221         DPRINT("Face encoding FT_ENCODING_UNICODE, number of glyphs %ld, first glyph %u, first char %04lx\n",
5222                face->num_glyphs, glyph_code, char_code);
5223 
5224         if (!glyph_code) return 0;
5225 
5226         if (glyphset)
5227         {
5228             glyphset->ranges[0].wcLow = (USHORT)char_code;
5229             glyphset->ranges[0].cGlyphs = 0;
5230             glyphset->cGlyphsSupported = 0;
5231         }
5232 
5233         num_ranges = 1;
5234         while (glyph_code)
5235         {
5236             if (char_code < char_code_prev)
5237             {
5238                 DPRINT1("Expected increasing char code from FT_Get_Next_Char\n");
5239                 return 0;
5240             }
5241             if (char_code - char_code_prev > 1)
5242             {
5243                 num_ranges++;
5244                 if (glyphset)
5245                 {
5246                     glyphset->ranges[num_ranges - 1].wcLow = (USHORT)char_code;
5247                     glyphset->ranges[num_ranges - 1].cGlyphs = 1;
5248                     glyphset->cGlyphsSupported++;
5249                 }
5250             }
5251             else if (glyphset)
5252             {
5253                 glyphset->ranges[num_ranges - 1].cGlyphs++;
5254                 glyphset->cGlyphsSupported++;
5255             }
5256             char_code_prev = char_code;
5257             char_code = FT_Get_Next_Char(face, char_code, &glyph_code);
5258         }
5259     }
5260     else
5261         DPRINT1("Encoding %i not supported\n", face->charmap->encoding);
5262 
5263     size = sizeof(GLYPHSET) + sizeof(WCRANGE) * (num_ranges - 1);
5264     if (glyphset)
5265     {
5266         glyphset->cbThis = size;
5267         glyphset->cRanges = num_ranges;
5268         glyphset->flAccel = 0;
5269     }
5270     return size;
5271 }
5272 
5273 
5274 BOOL
5275 FASTCALL
ftGdiGetTextMetricsW(HDC hDC,PTMW_INTERNAL ptmwi)5276 ftGdiGetTextMetricsW(
5277     HDC hDC,
5278     PTMW_INTERNAL ptmwi)
5279 {
5280     PDC dc;
5281     PDC_ATTR pdcattr;
5282     PTEXTOBJ TextObj;
5283     PFONTGDI FontGDI;
5284     FT_Face Face;
5285     TT_OS2 *pOS2;
5286     TT_HoriHeader *pHori;
5287     FT_WinFNT_HeaderRec Win;
5288     ULONG Error;
5289     NTSTATUS Status = STATUS_SUCCESS;
5290     LOGFONTW *plf;
5291 
5292     if (!ptmwi)
5293     {
5294         EngSetLastError(ERROR_INVALID_PARAMETER);
5295         return FALSE;
5296     }
5297     RtlZeroMemory(ptmwi, sizeof(TMW_INTERNAL));
5298 
5299     if (!(dc = DC_LockDc(hDC)))
5300     {
5301         EngSetLastError(ERROR_INVALID_HANDLE);
5302         return FALSE;
5303     }
5304     pdcattr = dc->pdcattr;
5305     TextObj = RealizeFontInit(pdcattr->hlfntNew);
5306     if (NULL != TextObj)
5307     {
5308         plf = &TextObj->logfont.elfEnumLogfontEx.elfLogFont;
5309         FontGDI = ObjToGDI(TextObj->Font, FONT);
5310 
5311         Face = FontGDI->SharedFace->Face;
5312 
5313         // NOTE: GetTextMetrics simply ignores lfEscapement and XFORM.
5314         IntLockFreeType();
5315         Error = IntRequestFontSize(dc, FontGDI, plf->lfWidth, plf->lfHeight);
5316         FT_Set_Transform(Face, NULL, NULL);
5317 
5318         IntUnLockFreeType();
5319 
5320         if (0 != Error)
5321         {
5322             DPRINT1("Error in setting pixel sizes: %u\n", Error);
5323             Status = STATUS_UNSUCCESSFUL;
5324         }
5325         else
5326         {
5327             Status = STATUS_SUCCESS;
5328 
5329             IntLockFreeType();
5330 
5331             Error = FT_Get_WinFNT_Header(Face, &Win);
5332             pOS2 = FT_Get_Sfnt_Table(Face, ft_sfnt_os2);
5333             pHori = FT_Get_Sfnt_Table(Face, ft_sfnt_hhea);
5334 
5335             if (!pOS2 && Error)
5336             {
5337                 DPRINT1("Can't find OS/2 table - not TT font?\n");
5338                 Status = STATUS_INTERNAL_ERROR;
5339             }
5340 
5341             if (!pHori && Error)
5342             {
5343                 DPRINT1("Can't find HHEA table - not TT font?\n");
5344                 Status = STATUS_INTERNAL_ERROR;
5345             }
5346 
5347             if (NT_SUCCESS(Status))
5348             {
5349                 FillTM(&ptmwi->TextMetric, FontGDI, pOS2, pHori, (Error ? NULL : &Win));
5350 
5351                 /* FIXME: Fill Diff member */
5352             }
5353 
5354             IntUnLockFreeType();
5355         }
5356         TEXTOBJ_UnlockText(TextObj);
5357     }
5358     else
5359     {
5360         Status = STATUS_INVALID_HANDLE;
5361     }
5362     DC_UnlockDc(dc);
5363 
5364     if (!NT_SUCCESS(Status))
5365     {
5366         SetLastNtError(Status);
5367         return FALSE;
5368     }
5369     return TRUE;
5370 }
5371 
5372 DWORD
5373 FASTCALL
ftGdiGetFontData(PFONTGDI FontGdi,DWORD Table,DWORD Offset,PVOID Buffer,DWORD Size)5374 ftGdiGetFontData(
5375     PFONTGDI FontGdi,
5376     DWORD Table,
5377     DWORD Offset,
5378     PVOID Buffer,
5379     DWORD Size)
5380 {
5381     DWORD Result = GDI_ERROR;
5382     FT_Face Face = FontGdi->SharedFace->Face;
5383 
5384     IntLockFreeType();
5385 
5386     if (FT_IS_SFNT(Face))
5387     {
5388         if (Table)
5389             Table = Table >> 24 | Table << 24 | (Table >> 8 & 0xFF00) |
5390                     (Table << 8 & 0xFF0000);
5391 
5392         if (!Buffer) Size = 0;
5393 
5394         if (Buffer && Size)
5395         {
5396             FT_Error Error;
5397             FT_ULong Needed = 0;
5398 
5399             Error = FT_Load_Sfnt_Table(Face, Table, Offset, NULL, &Needed);
5400 
5401             if ( !Error && Needed < Size) Size = Needed;
5402         }
5403         if (!FT_Load_Sfnt_Table(Face, Table, Offset, Buffer, &Size))
5404             Result = Size;
5405     }
5406 
5407     IntUnLockFreeType();
5408 
5409     return Result;
5410 }
5411 
5412 #define GOT_PENALTY(name, value) Penalty += (value)
5413 
5414 // NOTE: See Table 1. of https://learn.microsoft.com/en-us/previous-versions/ms969909(v=msdn.10)
5415 static UINT
GetFontPenalty(const LOGFONTW * LogFont,const OUTLINETEXTMETRICW * Otm,const char * style_name)5416 GetFontPenalty(const LOGFONTW *               LogFont,
5417                const OUTLINETEXTMETRICW *     Otm,
5418                const char *             style_name)
5419 {
5420     ULONG   Penalty = 0;
5421     BYTE    Byte;
5422     LONG    Long;
5423     BOOL    fNeedScaling = FALSE;
5424     const BYTE UserCharSet = CharSetFromLangID(gusLanguageID);
5425     const TEXTMETRICW * TM = &Otm->otmTextMetrics;
5426     WCHAR* ActualNameW;
5427 
5428     ASSERT(Otm);
5429     ASSERT(LogFont);
5430 
5431     /* FIXME: IntSizeSynth Penalty 20 */
5432     /* FIXME: SmallPenalty Penalty 1 */
5433     /* FIXME: FaceNameSubst Penalty 500 */
5434 
5435     Byte = LogFont->lfCharSet;
5436 
5437     if (Byte != TM->tmCharSet)
5438     {
5439         if (Byte != DEFAULT_CHARSET && Byte != ANSI_CHARSET)
5440         {
5441             /* CharSet Penalty 65000 */
5442             /* Requested charset does not match the candidate's. */
5443             GOT_PENALTY("CharSet", 65000);
5444         }
5445         else
5446         {
5447             if (UserCharSet != TM->tmCharSet)
5448             {
5449                 /* UNDOCUMENTED: Not user language */
5450                 GOT_PENALTY("UNDOCUMENTED:NotUserLanguage", 100);
5451 
5452                 if (ANSI_CHARSET != TM->tmCharSet)
5453                 {
5454                     /* UNDOCUMENTED: Not ANSI charset */
5455                     GOT_PENALTY("UNDOCUMENTED:NotAnsiCharSet", 100);
5456                 }
5457             }
5458         }
5459     }
5460 
5461     Byte = LogFont->lfOutPrecision;
5462     switch (Byte)
5463     {
5464         case OUT_DEFAULT_PRECIS:
5465             /* nothing to do */
5466             break;
5467         case OUT_DEVICE_PRECIS:
5468             if (!(TM->tmPitchAndFamily & TMPF_DEVICE) ||
5469                 !(TM->tmPitchAndFamily & (TMPF_VECTOR | TMPF_TRUETYPE)))
5470             {
5471                 /* OutputPrecision Penalty 19000 */
5472                 /* Requested OUT_STROKE_PRECIS, but the device can't do it
5473                    or the candidate is not a vector font. */
5474                 GOT_PENALTY("OutputPrecision", 19000);
5475             }
5476             break;
5477         default:
5478             if (TM->tmPitchAndFamily & (TMPF_VECTOR | TMPF_TRUETYPE))
5479             {
5480                 /* OutputPrecision Penalty 19000 */
5481                 /* Or OUT_STROKE_PRECIS not requested, and the candidate
5482                    is a vector font that requires GDI support. */
5483                 GOT_PENALTY("OutputPrecision", 19000);
5484             }
5485             break;
5486     }
5487 
5488     Byte = (LogFont->lfPitchAndFamily & 0x0F);
5489     if (Byte == DEFAULT_PITCH)
5490         Byte = VARIABLE_PITCH;
5491     if (Byte == FIXED_PITCH)
5492     {
5493         if (TM->tmPitchAndFamily & _TMPF_VARIABLE_PITCH)
5494         {
5495             /* FixedPitch Penalty 15000 */
5496             /* Requested a fixed pitch font, but the candidate is a
5497                variable pitch font. */
5498             GOT_PENALTY("FixedPitch", 15000);
5499         }
5500     }
5501     if (Byte == VARIABLE_PITCH)
5502     {
5503         if (!(TM->tmPitchAndFamily & _TMPF_VARIABLE_PITCH))
5504         {
5505             /* PitchVariable Penalty 350 */
5506             /* Requested a variable pitch font, but the candidate is not a
5507                variable pitch font. */
5508             GOT_PENALTY("PitchVariable", 350);
5509         }
5510     }
5511 
5512     Byte = (LogFont->lfPitchAndFamily & 0x0F);
5513     if (Byte == DEFAULT_PITCH)
5514     {
5515         if (!(TM->tmPitchAndFamily & _TMPF_VARIABLE_PITCH))
5516         {
5517             /* DefaultPitchFixed Penalty 1 */
5518             /* Requested DEFAULT_PITCH, but the candidate is fixed pitch. */
5519             GOT_PENALTY("DefaultPitchFixed", 1);
5520         }
5521     }
5522 
5523     ActualNameW = (WCHAR*)((ULONG_PTR)Otm + (ULONG_PTR)Otm->otmpFamilyName);
5524 
5525     if (LogFont->lfFaceName[0] != UNICODE_NULL)
5526     {
5527         BOOL Found = FALSE;
5528 
5529         /* localized family name */
5530         if (!Found)
5531         {
5532             Found = (_wcsicmp(LogFont->lfFaceName, ActualNameW) == 0);
5533         }
5534         /* localized full name */
5535         if (!Found)
5536         {
5537             ActualNameW = (WCHAR*)((ULONG_PTR)Otm + (ULONG_PTR)Otm->otmpFaceName);
5538             Found = (_wcsicmp(LogFont->lfFaceName, ActualNameW) == 0);
5539         }
5540         if (!Found)
5541         {
5542             /* FaceName Penalty 10000 */
5543             /* Requested a face name, but the candidate's face name
5544                does not match. */
5545             GOT_PENALTY("FaceName", 10000);
5546         }
5547     }
5548 
5549     Byte = (LogFont->lfPitchAndFamily & 0xF0);
5550     if (Byte != FF_DONTCARE)
5551     {
5552         if (Byte != (TM->tmPitchAndFamily & 0xF0))
5553         {
5554             /* Family Penalty 9000 */
5555             /* Requested a family, but the candidate's family is different. */
5556             GOT_PENALTY("Family", 9000);
5557         }
5558     }
5559 
5560     if ((TM->tmPitchAndFamily & 0xF0) == FF_DONTCARE)
5561     {
5562         /* FamilyUnknown Penalty 8000 */
5563         /* Requested a family, but the candidate has no family. */
5564         GOT_PENALTY("FamilyUnknown", 8000);
5565     }
5566 
5567     /* Is the candidate a non-vector font? */
5568     if (!(TM->tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_VECTOR)))
5569     {
5570         /* Is lfHeight specified? */
5571         if (LogFont->lfHeight != 0)
5572         {
5573             if (labs(LogFont->lfHeight) < TM->tmHeight)
5574             {
5575                 /* HeightBigger Penalty 600 */
5576                 /* The candidate is a nonvector font and is bigger than the
5577                    requested height. */
5578                 GOT_PENALTY("HeightBigger", 600);
5579                 /* HeightBiggerDifference Penalty 150 */
5580                 /* The candidate is a raster font and is larger than the
5581                    requested height. Penalty * height difference */
5582                 GOT_PENALTY("HeightBiggerDifference", 150 * labs(TM->tmHeight - labs(LogFont->lfHeight)));
5583 
5584                 fNeedScaling = TRUE;
5585             }
5586             if (TM->tmHeight < labs(LogFont->lfHeight))
5587             {
5588                 /* HeightSmaller Penalty 150 */
5589                 /* The candidate is a raster font and is smaller than the
5590                    requested height. Penalty * height difference */
5591                 GOT_PENALTY("HeightSmaller", 150 * labs(TM->tmHeight - labs(LogFont->lfHeight)));
5592 
5593                 fNeedScaling = TRUE;
5594             }
5595         }
5596     }
5597 
5598     switch (LogFont->lfPitchAndFamily & 0xF0)
5599     {
5600         case FF_ROMAN: case FF_MODERN: case FF_SWISS:
5601             switch (TM->tmPitchAndFamily & 0xF0)
5602             {
5603                 case FF_DECORATIVE: case FF_SCRIPT:
5604                     /* FamilyUnlikely Penalty 50 */
5605                     /* Requested a roman/modern/swiss family, but the
5606                        candidate is decorative/script. */
5607                     GOT_PENALTY("FamilyUnlikely", 50);
5608                     break;
5609                 default:
5610                     break;
5611             }
5612             break;
5613         case FF_DECORATIVE: case FF_SCRIPT:
5614             switch (TM->tmPitchAndFamily & 0xF0)
5615             {
5616                 case FF_ROMAN: case FF_MODERN: case FF_SWISS:
5617                     /* FamilyUnlikely Penalty 50 */
5618                     /* Or requested decorative/script, and the candidate is
5619                        roman/modern/swiss. */
5620                     GOT_PENALTY("FamilyUnlikely", 50);
5621                     break;
5622                 default:
5623                     break;
5624             }
5625         default:
5626             break;
5627     }
5628 
5629     if (LogFont->lfWidth != 0)
5630     {
5631         if (LogFont->lfWidth != TM->tmAveCharWidth)
5632         {
5633             /* Width Penalty 50 */
5634             /* Requested a nonzero width, but the candidate's width
5635                doesn't match. Penalty * width difference */
5636             GOT_PENALTY("Width", 50 * labs(LogFont->lfWidth - TM->tmAveCharWidth));
5637 
5638             if (!(TM->tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_VECTOR)))
5639                 fNeedScaling = TRUE;
5640         }
5641     }
5642 
5643     if (fNeedScaling)
5644     {
5645         /* SizeSynth Penalty 50 */
5646         /* The candidate is a raster font that needs scaling by GDI. */
5647         GOT_PENALTY("SizeSynth", 50);
5648     }
5649 
5650     if (!LogFont->lfItalic && TM->tmItalic)
5651     {
5652         /* Italic Penalty 4 */
5653         /* Requested font and candidate font do not agree on italic status,
5654            and the desired result cannot be simulated. */
5655         /* Adjusted to 40 to satisfy (Oblique Penalty > Book Penalty). */
5656         GOT_PENALTY("Italic", 40);
5657     }
5658     else if (LogFont->lfItalic && !TM->tmItalic)
5659     {
5660         /* ItalicSim Penalty 1 */
5661         /* Requested italic font but the candidate is not italic,
5662            although italics can be simulated. */
5663         GOT_PENALTY("ItalicSim", 1);
5664     }
5665 
5666     if (LogFont->lfOutPrecision == OUT_TT_PRECIS)
5667     {
5668         if (!(TM->tmPitchAndFamily & TMPF_TRUETYPE))
5669         {
5670             /* NotTrueType Penalty 4 */
5671             /* Requested OUT_TT_PRECIS, but the candidate is not a
5672                TrueType font. */
5673             GOT_PENALTY("NotTrueType", 4);
5674         }
5675     }
5676 
5677     Long = LogFont->lfWeight;
5678     if (LogFont->lfWeight == FW_DONTCARE)
5679         Long = FW_NORMAL;
5680     if (Long != TM->tmWeight)
5681     {
5682         /* Weight Penalty 3 */
5683         /* The candidate's weight does not match the requested weight.
5684            Penalty * (weight difference/10) */
5685         GOT_PENALTY("Weight", 3 * (labs(Long - TM->tmWeight) / 10));
5686     }
5687 
5688     if (!LogFont->lfUnderline && TM->tmUnderlined)
5689     {
5690         /* Underline Penalty 3 */
5691         /* Requested font has no underline, but the candidate is
5692            underlined. */
5693         GOT_PENALTY("Underline", 3);
5694     }
5695 
5696     if (!LogFont->lfStrikeOut && TM->tmStruckOut)
5697     {
5698         /* StrikeOut Penalty 3 */
5699         /* Requested font has no strike-out, but the candidate is
5700            struck out. */
5701         GOT_PENALTY("StrikeOut", 3);
5702     }
5703 
5704     /* Is the candidate a non-vector font? */
5705     if (!(TM->tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_VECTOR)))
5706     {
5707         if (LogFont->lfHeight != 0 && TM->tmHeight < LogFont->lfHeight)
5708         {
5709             /* VectorHeightSmaller Penalty 2 */
5710             /* Candidate is a vector font that is smaller than the
5711                requested height. Penalty * height difference */
5712             GOT_PENALTY("VectorHeightSmaller", 2 * labs(TM->tmHeight - LogFont->lfHeight));
5713         }
5714         if (LogFont->lfHeight != 0 && TM->tmHeight > LogFont->lfHeight)
5715         {
5716             /* VectorHeightBigger Penalty 1 */
5717             /* Candidate is a vector font that is bigger than the
5718                requested height. Penalty * height difference */
5719             GOT_PENALTY("VectorHeightBigger", 1 * labs(TM->tmHeight - LogFont->lfHeight));
5720         }
5721     }
5722 
5723     if (!(TM->tmPitchAndFamily & TMPF_DEVICE))
5724     {
5725         /* DeviceFavor Penalty 2 */
5726         /* Extra penalty for all nondevice fonts. */
5727         GOT_PENALTY("DeviceFavor", 2);
5728     }
5729 
5730     if (TM->tmAveCharWidth >= 5 && TM->tmHeight >= 5)
5731     {
5732         if (TM->tmAveCharWidth / TM->tmHeight >= 3)
5733         {
5734             /* Aspect Penalty 30 */
5735             /* The aspect rate is >= 3. It seems like a bad font. */
5736             GOT_PENALTY("Aspect", ((TM->tmAveCharWidth / TM->tmHeight) - 2) * 30);
5737         }
5738         else if (TM->tmHeight / TM->tmAveCharWidth >= 3)
5739         {
5740             /* Aspect Penalty 30 */
5741             /* The aspect rate is >= 3. It seems like a bad font. */
5742             GOT_PENALTY("Aspect", ((TM->tmHeight / TM->tmAveCharWidth) - 2) * 30);
5743         }
5744     }
5745 
5746     if (Penalty < 200)
5747     {
5748         DPRINT("WARNING: Penalty:%ld < 200: RequestedNameW:%ls, "
5749             "ActualNameW:%ls, lfCharSet:%d, lfWeight:%ld, "
5750             "tmCharSet:%d, tmWeight:%ld\n",
5751             Penalty, LogFont->lfFaceName, ActualNameW,
5752             LogFont->lfCharSet, LogFont->lfWeight,
5753             TM->tmCharSet, TM->tmWeight);
5754     }
5755 
5756     return Penalty;     /* success */
5757 }
5758 
5759 #undef GOT_PENALTY
5760 
5761 static __inline VOID
FindBestFontFromList(FONTOBJ ** FontObj,ULONG * MatchPenalty,const LOGFONTW * LogFont,const PLIST_ENTRY Head)5762 FindBestFontFromList(FONTOBJ **FontObj, ULONG *MatchPenalty,
5763                      const LOGFONTW *LogFont,
5764                      const PLIST_ENTRY Head)
5765 {
5766     ULONG Penalty;
5767     PLIST_ENTRY Entry;
5768     PFONT_ENTRY CurrentEntry;
5769     FONTGDI *FontGDI;
5770     OUTLINETEXTMETRICW *Otm = NULL;
5771     UINT OtmSize, OldOtmSize = 0;
5772     FT_Face Face;
5773 
5774     ASSERT(FontObj);
5775     ASSERT(MatchPenalty);
5776     ASSERT(LogFont);
5777     ASSERT(Head);
5778 
5779     /* Start with a pretty big buffer */
5780     OldOtmSize = 0x200;
5781     Otm = ExAllocatePoolWithTag(PagedPool, OldOtmSize, GDITAG_TEXT);
5782 
5783     /* get the FontObj of lowest penalty */
5784     for (Entry = Head->Flink; Entry != Head; Entry = Entry->Flink)
5785     {
5786         CurrentEntry = CONTAINING_RECORD(Entry, FONT_ENTRY, ListEntry);
5787 
5788         FontGDI = CurrentEntry->Font;
5789         ASSERT(FontGDI);
5790         Face = FontGDI->SharedFace->Face;
5791 
5792         /* get text metrics */
5793         ASSERT_FREETYPE_LOCK_HELD();
5794         OtmSize = IntGetOutlineTextMetrics(FontGDI, 0, NULL, TRUE);
5795         if (OtmSize > OldOtmSize)
5796         {
5797             if (Otm)
5798                 ExFreePoolWithTag(Otm, GDITAG_TEXT);
5799             Otm = ExAllocatePoolWithTag(PagedPool, OtmSize, GDITAG_TEXT);
5800         }
5801 
5802         /* update FontObj if lowest penalty */
5803         if (Otm)
5804         {
5805             ASSERT_FREETYPE_LOCK_HELD();
5806             IntRequestFontSize(NULL, FontGDI, LogFont->lfWidth, LogFont->lfHeight);
5807 
5808             ASSERT_FREETYPE_LOCK_HELD();
5809             OtmSize = IntGetOutlineTextMetrics(FontGDI, OtmSize, Otm, TRUE);
5810             if (!OtmSize)
5811                 continue;
5812 
5813             OldOtmSize = OtmSize;
5814 
5815             Penalty = GetFontPenalty(LogFont, Otm, Face->style_name);
5816             if (*MatchPenalty == MAXULONG || Penalty < *MatchPenalty)
5817             {
5818                 *FontObj = GDIToObj(FontGDI, FONT);
5819                 *MatchPenalty = Penalty;
5820             }
5821         }
5822     }
5823 
5824     if (Otm)
5825         ExFreePoolWithTag(Otm, GDITAG_TEXT);
5826 }
5827 
5828 static
5829 VOID
5830 FASTCALL
IntFontType(PFONTGDI Font)5831 IntFontType(PFONTGDI Font)
5832 {
5833     PS_FontInfoRec psfInfo;
5834     FT_ULong tmp_size = 0;
5835     FT_Face Face = Font->SharedFace->Face;
5836 
5837     ASSERT_FREETYPE_LOCK_NOT_HELD();
5838     IntLockFreeType();
5839 
5840     if (FT_HAS_MULTIPLE_MASTERS(Face))
5841         Font->FontObj.flFontType |= FO_MULTIPLEMASTER;
5842     if (FT_HAS_VERTICAL(Face))
5843         Font->FontObj.flFontType |= FO_VERT_FACE;
5844     if (!FT_IS_SCALABLE(Face))
5845         Font->FontObj.flFontType |= FO_TYPE_RASTER;
5846     if (FT_IS_SFNT(Face))
5847     {
5848         Font->FontObj.flFontType |= FO_TYPE_TRUETYPE;
5849         if (FT_Get_Sfnt_Table(Face, ft_sfnt_post))
5850             Font->FontObj.flFontType |= FO_POSTSCRIPT;
5851     }
5852     if (!FT_Get_PS_Font_Info(Face, &psfInfo ))
5853     {
5854         Font->FontObj.flFontType |= FO_POSTSCRIPT;
5855     }
5856     /* Check for the presence of the 'CFF ' table to check if the font is Type1 */
5857     if (!FT_Load_Sfnt_Table(Face, TTAG_CFF, 0, NULL, &tmp_size))
5858     {
5859         Font->FontObj.flFontType |= (FO_CFF|FO_POSTSCRIPT);
5860     }
5861 
5862     IntUnLockFreeType();
5863 }
5864 
5865 static BOOL
MatchFontName(PSHARED_FACE SharedFace,PUNICODE_STRING Name1,FT_UShort NameID,FT_UShort LangID)5866 MatchFontName(PSHARED_FACE SharedFace, PUNICODE_STRING Name1, FT_UShort NameID, FT_UShort LangID)
5867 {
5868     NTSTATUS Status;
5869     UNICODE_STRING Name2;
5870 
5871     RtlInitUnicodeString(&Name2, NULL);
5872     Status = IntGetFontLocalizedName(&Name2, SharedFace, NameID, LangID);
5873 
5874     if (NT_SUCCESS(Status))
5875     {
5876         if (RtlCompareUnicodeString(Name1, &Name2, TRUE) == 0)
5877         {
5878             RtlFreeUnicodeString(&Name2);
5879             return TRUE;
5880         }
5881 
5882         RtlFreeUnicodeString(&Name2);
5883     }
5884 
5885     return FALSE;
5886 }
5887 
5888 static BOOL
MatchFontNames(PSHARED_FACE SharedFace,LPCWSTR lfFaceName)5889 MatchFontNames(PSHARED_FACE SharedFace, LPCWSTR lfFaceName)
5890 {
5891     UNICODE_STRING Name1;
5892 
5893     if (lfFaceName[0] == UNICODE_NULL)
5894         return FALSE;
5895 
5896     RtlInitUnicodeString(&Name1, lfFaceName);
5897 
5898     if (MatchFontName(SharedFace, &Name1, TT_NAME_ID_FONT_FAMILY, LANG_ENGLISH) ||
5899         MatchFontName(SharedFace, &Name1, TT_NAME_ID_FULL_NAME, LANG_ENGLISH))
5900     {
5901         return TRUE;
5902     }
5903     if (PRIMARYLANGID(gusLanguageID) != LANG_ENGLISH)
5904     {
5905         if (MatchFontName(SharedFace, &Name1, TT_NAME_ID_FONT_FAMILY, gusLanguageID) ||
5906             MatchFontName(SharedFace, &Name1, TT_NAME_ID_FULL_NAME, gusLanguageID))
5907         {
5908             return TRUE;
5909         }
5910     }
5911     return FALSE;
5912 }
5913 
5914 NTSTATUS
5915 FASTCALL
TextIntRealizeFont(HFONT FontHandle,PTEXTOBJ pTextObj)5916 TextIntRealizeFont(HFONT FontHandle, PTEXTOBJ pTextObj)
5917 {
5918     NTSTATUS Status = STATUS_SUCCESS;
5919     PTEXTOBJ TextObj;
5920     PPROCESSINFO Win32Process;
5921     ULONG MatchPenalty;
5922     LOGFONTW *pLogFont;
5923     LOGFONTW SubstitutedLogFont;
5924 
5925     if (!pTextObj)
5926     {
5927         TextObj = TEXTOBJ_LockText(FontHandle);
5928         if (NULL == TextObj)
5929         {
5930             return STATUS_INVALID_HANDLE;
5931         }
5932 
5933         if (TextObj->fl & TEXTOBJECT_INIT)
5934         {
5935             TEXTOBJ_UnlockText(TextObj);
5936             return STATUS_SUCCESS;
5937         }
5938     }
5939     else
5940     {
5941         TextObj = pTextObj;
5942     }
5943 
5944     pLogFont = &TextObj->logfont.elfEnumLogfontEx.elfLogFont;
5945 
5946     /* substitute */
5947     SubstitutedLogFont = *pLogFont;
5948     SubstituteFontRecurse(&SubstitutedLogFont);
5949     DPRINT("Font '%S,%u' is substituted by '%S,%u'.\n",
5950            pLogFont->lfFaceName, pLogFont->lfCharSet,
5951            SubstitutedLogFont.lfFaceName, SubstitutedLogFont.lfCharSet);
5952 
5953     MatchPenalty = 0xFFFFFFFF;
5954     TextObj->Font = NULL;
5955 
5956     Win32Process = PsGetCurrentProcessWin32Process();
5957 
5958     /* Search private fonts */
5959     IntLockFreeType();
5960     IntLockProcessPrivateFonts(Win32Process);
5961     FindBestFontFromList(&TextObj->Font, &MatchPenalty, &SubstitutedLogFont,
5962                          &Win32Process->PrivateFontListHead);
5963     IntUnLockProcessPrivateFonts(Win32Process);
5964 
5965     /* Search system fonts */
5966     FindBestFontFromList(&TextObj->Font, &MatchPenalty, &SubstitutedLogFont,
5967                          &g_FontListHead);
5968     IntUnLockFreeType();
5969 
5970     if (NULL == TextObj->Font)
5971     {
5972         DPRINT1("Request font %S not found, no fonts loaded at all\n",
5973                 pLogFont->lfFaceName);
5974         Status = STATUS_NOT_FOUND;
5975     }
5976     else
5977     {
5978         UNICODE_STRING Name;
5979         PFONTGDI FontGdi = ObjToGDI(TextObj->Font, FONT);
5980         PSHARED_FACE SharedFace = FontGdi->SharedFace;
5981 
5982         TextObj->TextFace[0] = UNICODE_NULL;
5983         IntLockFreeType();
5984         if (MatchFontNames(SharedFace, SubstitutedLogFont.lfFaceName))
5985         {
5986             IntUnLockFreeType();
5987             RtlStringCchCopyW(TextObj->TextFace, _countof(TextObj->TextFace), pLogFont->lfFaceName);
5988         }
5989         else
5990         {
5991             RtlInitUnicodeString(&Name, NULL);
5992             Status = IntGetFontLocalizedName(&Name, SharedFace, TT_NAME_ID_FONT_FAMILY, gusLanguageID);
5993             IntUnLockFreeType();
5994             if (NT_SUCCESS(Status))
5995             {
5996                 /* truncated copy */
5997                 IntUnicodeStringToBuffer(TextObj->TextFace, sizeof(TextObj->TextFace), &Name);
5998                 RtlFreeUnicodeString(&Name);
5999             }
6000         }
6001 
6002         // Need hdev, when freetype is loaded need to create DEVOBJ for
6003         // Consumer and Producer.
6004         TextObj->Font->iUniq = 1; // Now it can be cached.
6005         IntFontType(FontGdi);
6006         FontGdi->flType = TextObj->Font->flFontType;
6007         FontGdi->RequestUnderline = pLogFont->lfUnderline ? 0xFF : 0;
6008         FontGdi->RequestStrikeOut = pLogFont->lfStrikeOut ? 0xFF : 0;
6009         FontGdi->RequestItalic = pLogFont->lfItalic ? 0xFF : 0;
6010         if (pLogFont->lfWeight != FW_DONTCARE)
6011             FontGdi->RequestWeight = pLogFont->lfWeight;
6012         else
6013             FontGdi->RequestWeight = FW_NORMAL;
6014 
6015         TextObj->fl |= TEXTOBJECT_INIT;
6016         Status = STATUS_SUCCESS;
6017     }
6018 
6019     if (!pTextObj) TEXTOBJ_UnlockText(TextObj);
6020 
6021     ASSERT((NT_SUCCESS(Status) ^ (NULL == TextObj->Font)) != 0);
6022 
6023     return Status;
6024 }
6025 
6026 static
6027 BOOL
6028 FASTCALL
IntGetFullFileName(POBJECT_NAME_INFORMATION NameInfo,ULONG Size,PUNICODE_STRING FileName)6029 IntGetFullFileName(
6030     POBJECT_NAME_INFORMATION NameInfo,
6031     ULONG Size,
6032     PUNICODE_STRING FileName)
6033 {
6034     NTSTATUS Status;
6035     OBJECT_ATTRIBUTES ObjectAttributes;
6036     HANDLE hFile;
6037     IO_STATUS_BLOCK IoStatusBlock;
6038     ULONG Desired;
6039 
6040     InitializeObjectAttributes(&ObjectAttributes,
6041                                FileName,
6042                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
6043                                NULL,
6044                                NULL);
6045 
6046     Status = ZwOpenFile(
6047                  &hFile,
6048                  0, // FILE_READ_ATTRIBUTES,
6049                  &ObjectAttributes,
6050                  &IoStatusBlock,
6051                  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
6052                  0);
6053 
6054     if (!NT_SUCCESS(Status))
6055     {
6056         DPRINT("ZwOpenFile() failed (Status = 0x%lx)\n", Status);
6057         return FALSE;
6058     }
6059 
6060     Status = ZwQueryObject(hFile, ObjectNameInformation, NameInfo, Size, &Desired);
6061     ZwClose(hFile);
6062     if (!NT_SUCCESS(Status))
6063     {
6064         DPRINT("ZwQueryObject() failed (Status = %lx)\n", Status);
6065         return FALSE;
6066     }
6067 
6068     return TRUE;
6069 }
6070 
6071 static BOOL
EqualFamilyInfo(const FONTFAMILYINFO * pInfo1,const FONTFAMILYINFO * pInfo2)6072 EqualFamilyInfo(const FONTFAMILYINFO *pInfo1, const FONTFAMILYINFO *pInfo2)
6073 {
6074     const ENUMLOGFONTEXW *pLog1 = &pInfo1->EnumLogFontEx;
6075     const ENUMLOGFONTEXW *pLog2 = &pInfo2->EnumLogFontEx;
6076     const LOGFONTW *plf1 = &pLog1->elfLogFont;
6077     const LOGFONTW *plf2 = &pLog2->elfLogFont;
6078 
6079     if (_wcsicmp(plf1->lfFaceName, plf2->lfFaceName) != 0)
6080     {
6081         return FALSE;
6082     }
6083 
6084     if (_wcsicmp(pLog1->elfStyle, pLog2->elfStyle) != 0)
6085     {
6086         return FALSE;
6087     }
6088 
6089     return TRUE;
6090 }
6091 
6092 static VOID
IntAddNameFromFamInfo(LPWSTR psz,FONTFAMILYINFO * FamInfo)6093 IntAddNameFromFamInfo(LPWSTR psz, FONTFAMILYINFO *FamInfo)
6094 {
6095     wcscat(psz, FamInfo->EnumLogFontEx.elfLogFont.lfFaceName);
6096     if (FamInfo->EnumLogFontEx.elfStyle[0] &&
6097         _wcsicmp(FamInfo->EnumLogFontEx.elfStyle, L"Regular") != 0)
6098     {
6099         wcscat(psz, L" ");
6100         wcscat(psz, FamInfo->EnumLogFontEx.elfStyle);
6101     }
6102 }
6103 
6104 BOOL
6105 FASTCALL
IntGdiGetFontResourceInfo(PUNICODE_STRING FileName,PVOID pBuffer,DWORD * pdwBytes,DWORD dwType)6106 IntGdiGetFontResourceInfo(
6107     PUNICODE_STRING FileName,
6108     PVOID pBuffer,
6109     DWORD *pdwBytes,
6110     DWORD dwType)
6111 {
6112     UNICODE_STRING EntryFileName;
6113     POBJECT_NAME_INFORMATION NameInfo1 = NULL, NameInfo2 = NULL;
6114     PLIST_ENTRY ListEntry;
6115     PFONT_ENTRY FontEntry;
6116     ULONG Size, i, Count;
6117     LPBYTE pbBuffer;
6118     BOOL IsEqual;
6119     FONTFAMILYINFO *FamInfo;
6120     const ULONG MaxFamInfo = 64;
6121     const ULONG MAX_FAM_INFO_BYTES = sizeof(FONTFAMILYINFO) * MaxFamInfo;
6122     BOOL bSuccess;
6123     const ULONG NAMEINFO_SIZE = sizeof(OBJECT_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR);
6124 
6125     DPRINT("IntGdiGetFontResourceInfo: dwType == %lu\n", dwType);
6126 
6127     do
6128     {
6129         /* Create buffer for full path name */
6130         NameInfo1 = ExAllocatePoolWithTag(PagedPool, NAMEINFO_SIZE, TAG_FINF);
6131         if (!NameInfo1)
6132             break;
6133 
6134         /* Get the full path name */
6135         if (!IntGetFullFileName(NameInfo1, NAMEINFO_SIZE, FileName))
6136             break;
6137 
6138         /* Create a buffer for the entries' names */
6139         NameInfo2 = ExAllocatePoolWithTag(PagedPool, NAMEINFO_SIZE, TAG_FINF);
6140         if (!NameInfo2)
6141             break;
6142 
6143         FamInfo = ExAllocatePoolWithTag(PagedPool, MAX_FAM_INFO_BYTES, TAG_FINF);
6144     } while (0);
6145 
6146     if (!NameInfo1 || !NameInfo2 || !FamInfo)
6147     {
6148         if (NameInfo2)
6149             ExFreePoolWithTag(NameInfo2, TAG_FINF);
6150 
6151         if (NameInfo1)
6152             ExFreePoolWithTag(NameInfo1, TAG_FINF);
6153 
6154         EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
6155         return FALSE;
6156     }
6157 
6158     Count = 0;
6159 
6160     /* Try to find the pathname in the global font list */
6161     IntLockFreeType();
6162     for (ListEntry = g_FontListHead.Flink; ListEntry != &g_FontListHead;
6163          ListEntry = ListEntry->Flink)
6164     {
6165         FontEntry = CONTAINING_RECORD(ListEntry, FONT_ENTRY, ListEntry);
6166         if (FontEntry->Font->Filename == NULL)
6167             continue;
6168 
6169         RtlInitUnicodeString(&EntryFileName , FontEntry->Font->Filename);
6170         if (!IntGetFullFileName(NameInfo2, NAMEINFO_SIZE, &EntryFileName))
6171             continue;
6172 
6173         if (!RtlEqualUnicodeString(&NameInfo1->Name, &NameInfo2->Name, FALSE))
6174             continue;
6175 
6176         IsEqual = FALSE;
6177         FontFamilyFillInfo(&FamInfo[Count], FontEntry->FaceName.Buffer,
6178                            NULL, FontEntry->Font);
6179         for (i = 0; i < Count; ++i)
6180         {
6181             if (EqualFamilyInfo(&FamInfo[i], &FamInfo[Count]))
6182             {
6183                 IsEqual = TRUE;
6184                 break;
6185             }
6186         }
6187         if (!IsEqual)
6188         {
6189             /* Found */
6190             ++Count;
6191             if (Count >= MaxFamInfo)
6192                 break;
6193         }
6194     }
6195     IntUnLockFreeType();
6196 
6197     /* Free the buffers */
6198     ExFreePoolWithTag(NameInfo1, TAG_FINF);
6199     ExFreePoolWithTag(NameInfo2, TAG_FINF);
6200 
6201     if (Count == 0 && dwType != 5)
6202     {
6203         /* Font could not be found in system table
6204            dwType == 5 will still handle this */
6205         ExFreePoolWithTag(FamInfo, TAG_FINF);
6206         return FALSE;
6207     }
6208 
6209     bSuccess = FALSE;
6210     switch (dwType)
6211     {
6212     case 0: /* FIXME: Returns 1 or 2, don't know what this is atm */
6213         Size = sizeof(DWORD);
6214         if (*pdwBytes == 0)
6215         {
6216             *pdwBytes = Size;
6217             bSuccess = TRUE;
6218         }
6219         else if (pBuffer)
6220         {
6221             if (*pdwBytes >= Size)
6222             {
6223                 *(DWORD*)pBuffer = Count;
6224             }
6225             *pdwBytes = Size;
6226             bSuccess = TRUE;
6227         }
6228         break;
6229 
6230     case 1: /* copy the font title */
6231         /* calculate the required size */
6232         Size = 0;
6233         for (i = 0; i < Count; ++i)
6234         {
6235             if (i > 0)
6236                 Size += 3;  /* " & " */
6237             Size += wcslen(FamInfo[i].EnumLogFontEx.elfLogFont.lfFaceName);
6238             if (FamInfo[i].EnumLogFontEx.elfStyle[0] &&
6239                 _wcsicmp(FamInfo[i].EnumLogFontEx.elfStyle, L"Regular") != 0)
6240             {
6241                 Size += 1 + wcslen(FamInfo[i].EnumLogFontEx.elfStyle);
6242             }
6243         }
6244         Size += 2;  /* "\0\0" */
6245         Size *= sizeof(WCHAR);
6246 
6247         if (*pdwBytes == 0)
6248         {
6249             *pdwBytes = Size;
6250             bSuccess = TRUE;
6251         }
6252         else if (pBuffer)
6253         {
6254             if (*pdwBytes >= Size)
6255             {
6256                 /* store font title to buffer */
6257                 WCHAR *psz = pBuffer;
6258                 *psz = 0;
6259                 for (i = 0; i < Count; ++i)
6260                 {
6261                     if (i > 0)
6262                         wcscat(psz, L" & ");
6263                     IntAddNameFromFamInfo(psz, &FamInfo[i]);
6264                 }
6265                 psz[wcslen(psz) + 1] = UNICODE_NULL;
6266                 *pdwBytes = Size;
6267                 bSuccess = TRUE;
6268             }
6269             else
6270             {
6271                 *pdwBytes = 1024;       /* this is confirmed value */
6272             }
6273         }
6274         break;
6275 
6276     case 2: /* Copy an array of LOGFONTW */
6277         Size = Count * sizeof(LOGFONTW);
6278         if (*pdwBytes == 0)
6279         {
6280             *pdwBytes = Size;
6281             bSuccess = TRUE;
6282         }
6283         else if (pBuffer)
6284         {
6285             if (*pdwBytes >= Size)
6286             {
6287                 pbBuffer = (LPBYTE)pBuffer;
6288                 for (i = 0; i < Count; ++i)
6289                 {
6290                     FamInfo[i].EnumLogFontEx.elfLogFont.lfWidth = 0;
6291                     RtlCopyMemory(pbBuffer, &FamInfo[i].EnumLogFontEx.elfLogFont, sizeof(LOGFONTW));
6292                     pbBuffer += sizeof(LOGFONTW);
6293                 }
6294             }
6295             *pdwBytes = Size;
6296             bSuccess = TRUE;
6297         }
6298         else
6299         {
6300             *pdwBytes = 1024;       /* this is confirmed value */
6301         }
6302         break;
6303 
6304     case 3:
6305         Size = sizeof(DWORD);
6306         if (*pdwBytes == 0)
6307         {
6308             *pdwBytes = Size;
6309             bSuccess = TRUE;
6310         }
6311         else if (pBuffer)
6312         {
6313             if (*pdwBytes >= Size)
6314             {
6315                 /* FIXME: What exactly is copied here? */
6316                 *(DWORD*)pBuffer = 1;
6317             }
6318             *pdwBytes = Size;
6319             bSuccess = TRUE;
6320         }
6321         break;
6322 
6323     case 4:     /* full file path */
6324         if (FileName->Length >= 4 * sizeof(WCHAR))
6325         {
6326             /* The beginning of FileName is \??\ */
6327             LPWSTR pch = FileName->Buffer + 4;
6328             DWORD Length = FileName->Length - 4 * sizeof(WCHAR);
6329 
6330             Size = Length + sizeof(WCHAR);
6331             if (*pdwBytes == 0)
6332             {
6333                 *pdwBytes = Size;
6334                 bSuccess = TRUE;
6335             }
6336             else if (pBuffer)
6337             {
6338                 if (*pdwBytes >= Size)
6339                 {
6340                     RtlCopyMemory(pBuffer, pch, Size);
6341                 }
6342                 *pdwBytes = Size;
6343                 bSuccess = TRUE;
6344             }
6345         }
6346         break;
6347 
6348     case 5: /* Looks like a BOOL that is copied, TRUE, if the font was not found */
6349         Size = sizeof(BOOL);
6350         if (*pdwBytes == 0)
6351         {
6352             *pdwBytes = Size;
6353             bSuccess = TRUE;
6354         }
6355         else if (pBuffer)
6356         {
6357             if (*pdwBytes >= Size)
6358             {
6359                 *(BOOL*)pBuffer = Count == 0;
6360             }
6361             *pdwBytes = Size;
6362             bSuccess = TRUE;
6363         }
6364         break;
6365     }
6366     ExFreePoolWithTag(FamInfo, TAG_FINF);
6367 
6368     return bSuccess;
6369 }
6370 
6371 
6372 BOOL
6373 FASTCALL
ftGdiRealizationInfo(PFONTGDI Font,PREALIZATION_INFO Info)6374 ftGdiRealizationInfo(PFONTGDI Font, PREALIZATION_INFO Info)
6375 {
6376     if (FT_HAS_FIXED_SIZES(Font->SharedFace->Face))
6377         Info->iTechnology = RI_TECH_BITMAP;
6378     else
6379     {
6380         if (FT_IS_SCALABLE(Font->SharedFace->Face))
6381             Info->iTechnology = RI_TECH_SCALABLE;
6382         else
6383             Info->iTechnology = RI_TECH_FIXED;
6384     }
6385     Info->iUniq = Font->FontObj.iUniq;
6386     Info->dwUnknown = -1;
6387     return TRUE;
6388 }
6389 
6390 
6391 DWORD
6392 FASTCALL
ftGdiGetKerningPairs(PFONTGDI Font,DWORD cPairs,LPKERNINGPAIR pKerningPair)6393 ftGdiGetKerningPairs( PFONTGDI Font,
6394                       DWORD cPairs,
6395                       LPKERNINGPAIR pKerningPair)
6396 {
6397     DWORD Count = 0;
6398     INT i = 0;
6399     FT_Face face = Font->SharedFace->Face;
6400 
6401     if (FT_HAS_KERNING(face) && face->charmap->encoding == FT_ENCODING_UNICODE)
6402     {
6403         FT_UInt previous_index = 0, glyph_index = 0;
6404         FT_ULong char_code, char_previous;
6405         FT_Vector delta;
6406 
6407         char_previous = char_code = FT_Get_First_Char(face, &glyph_index);
6408 
6409         IntLockFreeType();
6410 
6411         while (glyph_index)
6412         {
6413             if (previous_index && glyph_index)
6414             {
6415                 FT_Get_Kerning(face, previous_index, glyph_index, FT_KERNING_DEFAULT, &delta);
6416 
6417                 if (pKerningPair && cPairs)
6418                 {
6419                     pKerningPair[i].wFirst      = char_previous;
6420                     pKerningPair[i].wSecond     = char_code;
6421                     pKerningPair[i].iKernAmount = delta.x;
6422                     i++;
6423                     if (i == cPairs) break;
6424                 }
6425                 Count++;
6426             }
6427             previous_index = glyph_index;
6428             char_previous = char_code;
6429             char_code = FT_Get_Next_Char(face, char_code, &glyph_index);
6430         }
6431         IntUnLockFreeType();
6432     }
6433     return Count;
6434 }
6435 
6436 
6437 ///////////////////////////////////////////////////////////////////////////
6438 //
6439 // Functions needing sorting.
6440 //
6441 ///////////////////////////////////////////////////////////////////////////
6442 
6443 LONG FASTCALL
IntGetFontFamilyInfo(HDC Dc,const LOGFONTW * SafeLogFont,PFONTFAMILYINFO SafeInfo,LONG InfoCount)6444 IntGetFontFamilyInfo(HDC Dc,
6445                      const LOGFONTW *SafeLogFont,
6446                      PFONTFAMILYINFO SafeInfo,
6447                      LONG InfoCount)
6448 {
6449     LONG AvailCount = 0;
6450     PPROCESSINFO Win32Process;
6451 
6452     /* Enumerate font families in the global list */
6453     IntLockFreeType();
6454     if (!GetFontFamilyInfoForList(SafeLogFont, SafeInfo, NULL, &AvailCount,
6455                                   InfoCount, &g_FontListHead))
6456     {
6457         IntUnLockFreeType();
6458         return -1;
6459     }
6460 
6461     /* Enumerate font families in the process local list */
6462     Win32Process = PsGetCurrentProcessWin32Process();
6463     IntLockProcessPrivateFonts(Win32Process);
6464     if (!GetFontFamilyInfoForList(SafeLogFont, SafeInfo, NULL, &AvailCount, InfoCount,
6465                                   &Win32Process->PrivateFontListHead))
6466     {
6467         IntUnLockProcessPrivateFonts(Win32Process);
6468         IntUnLockFreeType();
6469         return -1;
6470     }
6471     IntUnLockProcessPrivateFonts(Win32Process);
6472     IntUnLockFreeType();
6473 
6474     /* Enumerate font families in the registry */
6475     if (!GetFontFamilyInfoForSubstitutes(SafeLogFont, SafeInfo, &AvailCount, InfoCount))
6476     {
6477         return -1;
6478     }
6479 
6480     return AvailCount;
6481 }
6482 
6483 LONG NTAPI
NtGdiGetFontFamilyInfo(HDC Dc,const LOGFONTW * UnsafeLogFont,PFONTFAMILYINFO UnsafeInfo,LPLONG UnsafeInfoCount)6484 NtGdiGetFontFamilyInfo(HDC Dc,
6485                        const LOGFONTW *UnsafeLogFont,
6486                        PFONTFAMILYINFO UnsafeInfo,
6487                        LPLONG UnsafeInfoCount)
6488 {
6489     NTSTATUS Status;
6490     LOGFONTW LogFont;
6491     PFONTFAMILYINFO Info;
6492     LONG GotCount, AvailCount, SafeInfoCount;
6493     ULONG DataSize;
6494 
6495     if (UnsafeLogFont == NULL || UnsafeInfo == NULL || UnsafeInfoCount == NULL)
6496     {
6497         EngSetLastError(ERROR_INVALID_PARAMETER);
6498         return -1;
6499     }
6500 
6501     Status = MmCopyFromCaller(&SafeInfoCount, UnsafeInfoCount, sizeof(SafeInfoCount));
6502     if (!NT_SUCCESS(Status))
6503     {
6504         EngSetLastError(ERROR_INVALID_PARAMETER);
6505         return -1;
6506     }
6507     GotCount = 0;
6508     Status = MmCopyToCaller(UnsafeInfoCount, &GotCount, sizeof(*UnsafeInfoCount));
6509     if (!NT_SUCCESS(Status))
6510     {
6511         EngSetLastError(ERROR_INVALID_PARAMETER);
6512         return -1;
6513     }
6514     Status = MmCopyFromCaller(&LogFont, UnsafeLogFont, sizeof(LOGFONTW));
6515     if (!NT_SUCCESS(Status))
6516     {
6517         EngSetLastError(ERROR_INVALID_PARAMETER);
6518         return -1;
6519     }
6520     if (SafeInfoCount <= 0)
6521     {
6522         EngSetLastError(ERROR_INVALID_PARAMETER);
6523         return -1;
6524     }
6525 
6526     /* Allocate space for a safe copy */
6527     Status = RtlULongMult(SafeInfoCount, sizeof(FONTFAMILYINFO), &DataSize);
6528     if (!NT_SUCCESS(Status) || DataSize > LONG_MAX)
6529     {
6530         DPRINT1("Overflowed.\n");
6531         EngSetLastError(ERROR_INVALID_PARAMETER);
6532         return -1;
6533     }
6534     Info = ExAllocatePoolWithTag(PagedPool, DataSize, GDITAG_TEXT);
6535     if (Info == NULL)
6536     {
6537         EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
6538         return -1;
6539     }
6540 
6541     /* Retrieve the information */
6542     AvailCount = IntGetFontFamilyInfo(Dc, &LogFont, Info, SafeInfoCount);
6543     GotCount = min(AvailCount, SafeInfoCount);
6544     SafeInfoCount = AvailCount;
6545 
6546     /* Return data to caller */
6547     if (GotCount > 0)
6548     {
6549         Status = RtlULongMult(GotCount, sizeof(FONTFAMILYINFO), &DataSize);
6550         if (!NT_SUCCESS(Status) || DataSize > LONG_MAX)
6551         {
6552             DPRINT1("Overflowed.\n");
6553             ExFreePoolWithTag(Info, GDITAG_TEXT);
6554             EngSetLastError(ERROR_INVALID_PARAMETER);
6555             return -1;
6556         }
6557         Status = MmCopyToCaller(UnsafeInfo, Info, DataSize);
6558         if (!NT_SUCCESS(Status))
6559         {
6560             ExFreePoolWithTag(Info, GDITAG_TEXT);
6561             EngSetLastError(ERROR_INVALID_PARAMETER);
6562             return -1;
6563         }
6564         Status = MmCopyToCaller(UnsafeInfoCount, &SafeInfoCount, sizeof(*UnsafeInfoCount));
6565         if (!NT_SUCCESS(Status))
6566         {
6567             ExFreePoolWithTag(Info, GDITAG_TEXT);
6568             EngSetLastError(ERROR_INVALID_PARAMETER);
6569             return -1;
6570         }
6571     }
6572 
6573     ExFreePoolWithTag(Info, GDITAG_TEXT);
6574 
6575     return GotCount;
6576 }
6577 
6578 static inline
6579 LONG
ScaleLong(LONG lValue,PFLOATOBJ pef)6580 ScaleLong(LONG lValue, PFLOATOBJ pef)
6581 {
6582     FLOATOBJ efTemp;
6583 
6584     /* Check if we have scaling different from 1 */
6585     if (!FLOATOBJ_Equal(pef, (PFLOATOBJ)&gef1))
6586     {
6587         /* Need to multiply */
6588         FLOATOBJ_SetLong(&efTemp, lValue);
6589         FLOATOBJ_Mul(&efTemp, pef);
6590         lValue = FLOATOBJ_GetLong(&efTemp);
6591     }
6592 
6593     return lValue;
6594 }
6595 
6596 /*
6597  * Calculate X and Y disposition of the text.
6598  * NOTE: The disposition can be negative.
6599  */
6600 static BOOL
IntGetTextDisposition(OUT LONGLONG * pX64,OUT LONGLONG * pY64,IN LPCWSTR String,IN INT Count,IN OPTIONAL LPINT Dx,IN OUT PFONT_CACHE_ENTRY Cache,IN UINT fuOptions,IN BOOL bNoTransform,IN OUT PFONTLINK_CHAIN pChain)6601 IntGetTextDisposition(
6602     OUT LONGLONG *pX64,
6603     OUT LONGLONG *pY64,
6604     IN LPCWSTR String,
6605     IN INT Count,
6606     IN OPTIONAL LPINT Dx,
6607     IN OUT PFONT_CACHE_ENTRY Cache,
6608     IN UINT fuOptions,
6609     IN BOOL bNoTransform,
6610     IN OUT PFONTLINK_CHAIN pChain)
6611 {
6612     LONGLONG X64 = 0, Y64 = 0;
6613     INT i, glyph_index;
6614     FT_BitmapGlyph realglyph;
6615     FT_Face face = Cache->Hashed.Face;
6616     BOOL use_kerning = FT_HAS_KERNING(face);
6617     ULONG previous = 0;
6618     FT_Vector delta, vec;
6619     DWORD ch0, ch1;
6620 
6621     ASSERT_FREETYPE_LOCK_HELD();
6622 
6623     for (i = 0; i < Count; ++i)
6624     {
6625         ch0 = *String++;
6626         if (IS_HIGH_SURROGATE(ch0))
6627         {
6628             ++i;
6629             if (i >= Count)
6630                 return TRUE;
6631 
6632             ch1 = *String++;
6633             if (IS_LOW_SURROGATE(ch1))
6634                 ch0 = Utf32FromSurrogatePair(ch0, ch1);
6635         }
6636 
6637         glyph_index = FontLink_Chain_FindGlyph(pChain, Cache, &face, ch0,
6638                                                (fuOptions & ETO_GLYPH_INDEX));
6639         Cache->Hashed.GlyphIndex = glyph_index;
6640 
6641         realglyph = IntGetRealGlyph(Cache);
6642         if (!realglyph)
6643             return FALSE;
6644 
6645         /* Retrieve kerning distance */
6646         if (use_kerning && previous && glyph_index)
6647         {
6648             FT_Get_Kerning(face, previous, glyph_index, 0, &delta);
6649             X64 += delta.x;
6650             Y64 -= delta.y;
6651         }
6652 
6653         if (NULL == Dx)
6654         {
6655             X64 += realglyph->root.advance.x >> 10;
6656             Y64 -= realglyph->root.advance.y >> 10;
6657         }
6658         else if (fuOptions & ETO_PDY)
6659         {
6660             vec.x = (Dx[2 * i + 0] << 6);
6661             vec.y = (Dx[2 * i + 1] << 6);
6662             if (!bNoTransform)
6663                 FT_Vector_Transform(&vec, &Cache->Hashed.matTransform);
6664             X64 += vec.x;
6665             Y64 -= vec.y;
6666         }
6667         else
6668         {
6669             vec.x = (Dx[i] << 6);
6670             vec.y = 0;
6671             if (!bNoTransform)
6672                 FT_Vector_Transform(&vec, &Cache->Hashed.matTransform);
6673             X64 += vec.x;
6674             Y64 -= vec.y;
6675         }
6676 
6677         previous = glyph_index;
6678     }
6679 
6680     *pX64 = X64;
6681     *pY64 = Y64;
6682     return TRUE;
6683 }
6684 
6685 VOID APIENTRY
IntEngFillPolygon(IN OUT PDC dc,IN POINTL * pPoints,IN UINT cPoints,IN BRUSHOBJ * BrushObj)6686 IntEngFillPolygon(
6687     IN OUT PDC dc,
6688     IN POINTL *pPoints,
6689     IN UINT cPoints,
6690     IN BRUSHOBJ *BrushObj)
6691 {
6692     SURFACE *psurf = dc->dclevel.pSurface;
6693     RECT Rect;
6694     UINT i;
6695     INT x, y;
6696 
6697     ASSERT_DC_PREPARED(dc);
6698     ASSERT(psurf != NULL);
6699 
6700     Rect.left = Rect.right = pPoints[0].x;
6701     Rect.top = Rect.bottom = pPoints[0].y;
6702     for (i = 1; i < cPoints; ++i)
6703     {
6704         x = pPoints[i].x;
6705         if (x < Rect.left)
6706             Rect.left = x;
6707         else if (Rect.right < x)
6708             Rect.right = x;
6709 
6710         y = pPoints[i].y;
6711         if (y < Rect.top)
6712             Rect.top = y;
6713         else if (Rect.bottom < y)
6714             Rect.bottom = y;
6715     }
6716 
6717     IntFillPolygon(dc, dc->dclevel.pSurface, BrushObj, pPoints, cPoints, Rect, &PointZero);
6718 }
6719 
6720 VOID
6721 FASTCALL
IntEngFillBox(IN OUT PDC dc,IN INT X,IN INT Y,IN INT Width,IN INT Height,IN BRUSHOBJ * BrushObj)6722 IntEngFillBox(
6723     IN OUT PDC dc,
6724     IN INT X,
6725     IN INT Y,
6726     IN INT Width,
6727     IN INT Height,
6728     IN BRUSHOBJ *BrushObj)
6729 {
6730     RECTL DestRect;
6731     SURFACE *psurf = dc->dclevel.pSurface;
6732 
6733     ASSERT_DC_PREPARED(dc);
6734     ASSERT(psurf != NULL);
6735 
6736     if (Width < 0)
6737     {
6738         X += Width;
6739         Width = -Width;
6740     }
6741 
6742     if (Height < 0)
6743     {
6744         Y += Height;
6745         Height = -Height;
6746     }
6747 
6748     DestRect.left = X;
6749     DestRect.right = X + Width;
6750     DestRect.top = Y;
6751     DestRect.bottom = Y + Height;
6752 
6753     IntEngBitBlt(&psurf->SurfObj,
6754                  NULL,
6755                  NULL,
6756                  (CLIPOBJ *)&dc->co,
6757                  NULL,
6758                  &DestRect,
6759                  NULL,
6760                  NULL,
6761                  BrushObj,
6762                  &PointZero,
6763                  ROP4_FROM_INDEX(R3_OPINDEX_PATCOPY));
6764 }
6765 
6766 
6767 BOOL
6768 APIENTRY
IntExtTextOutW(IN PDC dc,IN INT XStart,IN INT YStart,IN UINT fuOptions,IN OPTIONAL PRECTL lprc,IN LPCWSTR String,IN INT Count,IN OPTIONAL LPINT Dx,IN DWORD dwCodePage)6769 IntExtTextOutW(
6770     IN PDC dc,
6771     IN INT XStart,
6772     IN INT YStart,
6773     IN UINT fuOptions,
6774     IN OPTIONAL PRECTL lprc,
6775     IN LPCWSTR String,
6776     IN INT Count,
6777     IN OPTIONAL LPINT Dx,
6778     IN DWORD dwCodePage)
6779 {
6780     /*
6781      * FIXME:
6782      * Call EngTextOut, which does the real work (calling DrvTextOut where
6783      * appropriate)
6784      */
6785 
6786     PDC_ATTR pdcattr;
6787     SURFOBJ *SurfObj, *SourceGlyphSurf;
6788     SURFACE *psurf;
6789     INT glyph_index, i;
6790     FT_Face face;
6791     FT_BitmapGlyph realglyph;
6792     LONGLONG X64, Y64, RealXStart64, RealYStart64, DeltaX64, DeltaY64;
6793     ULONG previous;
6794     RECTL DestRect, MaskRect;
6795     HBITMAP HSourceGlyph;
6796     SIZEL bitSize;
6797     FONTOBJ *FontObj;
6798     PFONTGDI FontGDI;
6799     PTEXTOBJ TextObj = NULL;
6800     EXLATEOBJ exloRGB2Dst, exloDst2RGB;
6801     POINT Start;
6802     PMATRIX pmxWorldToDevice;
6803     FT_Vector delta, vecAscent64, vecDescent64, vec;
6804     LOGFONTW *plf;
6805     BOOL use_kerning, bResult, DoBreak;
6806     FONT_CACHE_ENTRY Cache;
6807     FT_Matrix mat;
6808     BOOL bNoTransform;
6809     DWORD ch0, ch1;
6810     const DWORD del = 0x7f, nbsp = 0xa0; // DEL is ASCII DELETE and nbsp is a non-breaking space
6811     FONTLINK_CHAIN Chain;
6812     SIZE spaceWidth;
6813 
6814     /* Check if String is valid */
6815     if (Count > 0xFFFF || (Count > 0 && String == NULL))
6816     {
6817         EngSetLastError(ERROR_INVALID_PARAMETER);
6818         return FALSE;
6819     }
6820 
6821     if (PATH_IsPathOpen(dc->dclevel))
6822     {
6823         return PATH_ExtTextOut(dc,
6824                                XStart, YStart,
6825                                fuOptions,
6826                                lprc,
6827                                String, Count,
6828                                Dx);
6829     }
6830 
6831     DC_vPrepareDCsForBlit(dc, NULL, NULL, NULL);
6832 
6833     if (!dc->dclevel.pSurface)
6834     {
6835         /* Memory DC with no surface selected */
6836         bResult = TRUE;
6837         goto Cleanup;
6838     }
6839 
6840     pdcattr = dc->pdcattr;
6841     if (pdcattr->flTextAlign & TA_UPDATECP)
6842     {
6843         Start.x = pdcattr->ptlCurrent.x;
6844         Start.y = pdcattr->ptlCurrent.y;
6845     }
6846     else
6847     {
6848         Start.x = XStart;
6849         Start.y = YStart;
6850     }
6851 
6852     IntLPtoDP(dc, &Start, 1);
6853     RealXStart64 = ((LONGLONG)Start.x + dc->ptlDCOrig.x) << 6;
6854     RealYStart64 = ((LONGLONG)Start.y + dc->ptlDCOrig.y) << 6;
6855 
6856     MaskRect.left = 0;
6857     MaskRect.top = 0;
6858 
6859     psurf = dc->dclevel.pSurface;
6860     SurfObj = &psurf->SurfObj;
6861 
6862     if (pdcattr->iGraphicsMode == GM_ADVANCED)
6863         pmxWorldToDevice = DC_pmxWorldToDevice(dc);
6864     else
6865         pmxWorldToDevice = (PMATRIX)&gmxWorldToDeviceDefault;
6866 
6867     if (pdcattr->ulDirty_ & DIRTY_BACKGROUND)
6868         DC_vUpdateBackgroundBrush(dc);
6869 
6870     if (lprc && (fuOptions & (ETO_CLIPPED | ETO_OPAQUE)))
6871     {
6872         IntLPtoDP(dc, (POINT*)lprc, 2);
6873         lprc->left   += dc->ptlDCOrig.x;
6874         lprc->top    += dc->ptlDCOrig.y;
6875         lprc->right  += dc->ptlDCOrig.x;
6876         lprc->bottom += dc->ptlDCOrig.y;
6877     }
6878 
6879     if (lprc && (fuOptions & ETO_OPAQUE))
6880     {
6881         IntEngFillBox(dc,
6882                       lprc->left, lprc->top,
6883                       lprc->right - lprc->left, lprc->bottom - lprc->top,
6884                       &dc->eboBackground.BrushObject);
6885         fuOptions &= ~ETO_OPAQUE;
6886     }
6887     else
6888     {
6889         if (pdcattr->jBkMode == OPAQUE)
6890         {
6891             fuOptions |= ETO_OPAQUE;
6892         }
6893     }
6894 
6895     TextObj = RealizeFontInit(pdcattr->hlfntNew);
6896     if (TextObj == NULL)
6897     {
6898         bResult = FALSE;
6899         goto Cleanup;
6900     }
6901 
6902     FontObj = TextObj->Font;
6903     ASSERT(FontObj);
6904     FontGDI = ObjToGDI(FontObj, FONT);
6905     ASSERT(FontGDI);
6906 
6907     IntLockFreeType();
6908     Cache.Hashed.Face = face = FontGDI->SharedFace->Face;
6909 
6910     plf = &TextObj->logfont.elfEnumLogfontEx.elfLogFont;
6911     Cache.Hashed.lfHeight = plf->lfHeight;
6912     Cache.Hashed.lfWidth = plf->lfWidth;
6913     Cache.Hashed.Aspect.Emu.Bold = EMUBOLD_NEEDED(FontGDI->OriginalWeight, plf->lfWeight);
6914     Cache.Hashed.Aspect.Emu.Italic = (plf->lfItalic && !FontGDI->OriginalItalic);
6915 
6916     if (IntIsFontRenderingEnabled())
6917         Cache.Hashed.Aspect.RenderMode = (BYTE)IntGetFontRenderMode(plf);
6918     else
6919         Cache.Hashed.Aspect.RenderMode = (BYTE)FT_RENDER_MODE_MONO;
6920 
6921     if (!TextIntUpdateSize(dc, TextObj, FontGDI, FALSE))
6922     {
6923         IntUnLockFreeType();
6924         bResult = FALSE;
6925         goto Cleanup;
6926     }
6927 
6928     FontLink_Chain_Init(&Chain, TextObj, face);
6929 
6930     /* Apply lfEscapement */
6931     if (FT_IS_SCALABLE(face) && plf->lfEscapement != 0)
6932         IntEscapeMatrix(&Cache.Hashed.matTransform, plf->lfEscapement);
6933     else
6934         Cache.Hashed.matTransform = identityMat;
6935 
6936     /* Apply the world transformation */
6937     IntMatrixFromMx(&mat, pmxWorldToDevice);
6938     FT_Matrix_Multiply(&mat, &Cache.Hashed.matTransform);
6939     FT_Set_Transform(face, &Cache.Hashed.matTransform, NULL);
6940 
6941     /* Is there no transformation? */
6942     bNoTransform = ((mat.xy == 0) && (mat.yx == 0) &&
6943                     (mat.xx == (1 << 16)) && (mat.yy == (1 << 16)));
6944 
6945     /* Calculate the ascent point and the descent point */
6946     vecAscent64.x = 0;
6947     vecAscent64.y = (FontGDI->tmAscent << 6);
6948     FT_Vector_Transform(&vecAscent64, &Cache.Hashed.matTransform);
6949     vecDescent64.x = 0;
6950     vecDescent64.y = -(FontGDI->tmDescent << 6);
6951     FT_Vector_Transform(&vecDescent64, &Cache.Hashed.matTransform);
6952 
6953     /* Process the vertical alignment and fix the real starting point. */
6954 #define VALIGN_MASK  (TA_TOP | TA_BASELINE | TA_BOTTOM)
6955     if ((pdcattr->flTextAlign & VALIGN_MASK) == TA_BASELINE)
6956     {
6957         NOTHING;
6958     }
6959     else if ((pdcattr->flTextAlign & VALIGN_MASK) == TA_BOTTOM)
6960     {
6961         RealXStart64 -= vecDescent64.x;
6962         RealYStart64 += vecDescent64.y;
6963     }
6964     else /* TA_TOP */
6965     {
6966         RealXStart64 -= vecAscent64.x;
6967         RealYStart64 += vecAscent64.y;
6968     }
6969 #undef VALIGN_MASK
6970 
6971     use_kerning = FT_HAS_KERNING(face);
6972 
6973     /* Calculate the text width if necessary */
6974     if ((fuOptions & ETO_OPAQUE) || (pdcattr->flTextAlign & (TA_CENTER | TA_RIGHT)))
6975     {
6976         if (!IntGetTextDisposition(&DeltaX64, &DeltaY64, String, Count, Dx, &Cache,
6977                                    fuOptions, bNoTransform, &Chain))
6978         {
6979             FontLink_Chain_Finish(&Chain);
6980             IntUnLockFreeType();
6981             bResult = FALSE;
6982             goto Cleanup;
6983         }
6984 
6985         /* Adjust the horizontal position by horizontal alignment */
6986         if ((pdcattr->flTextAlign & TA_CENTER) == TA_CENTER)
6987         {
6988             RealXStart64 -= DeltaX64 / 2;
6989             RealYStart64 -= DeltaY64 / 2;
6990         }
6991         else if ((pdcattr->flTextAlign & TA_RIGHT) == TA_RIGHT)
6992         {
6993             RealXStart64 -= DeltaX64;
6994             RealYStart64 -= DeltaY64;
6995         }
6996 
6997         /* Fill background */
6998         if (fuOptions & ETO_OPAQUE)
6999         {
7000             INT X0 = (RealXStart64 + vecAscent64.x + 32) >> 6;
7001             INT Y0 = (RealYStart64 - vecAscent64.y + 32) >> 6;
7002             INT DX = (DeltaX64 >> 6);
7003             if (Cache.Hashed.matTransform.xy == 0 && Cache.Hashed.matTransform.yx == 0)
7004             {
7005                 INT CY = (vecAscent64.y - vecDescent64.y + 32) >> 6;
7006                 IntEngFillBox(dc, X0, Y0, DX, CY, &dc->eboBackground.BrushObject);
7007             }
7008             else
7009             {
7010                 INT DY = (DeltaY64 >> 6);
7011                 INT X1 = ((RealXStart64 + vecDescent64.x + 32) >> 6);
7012                 INT Y1 = ((RealYStart64 - vecDescent64.y + 32) >> 6);
7013                 POINT Points[4] =
7014                 {
7015                     { X0,       Y0      },
7016                     { X0 + DX,  Y0 + DY },
7017                     { X1 + DX,  Y1 + DY },
7018                     { X1,       Y1      },
7019                 };
7020                 IntEngFillPolygon(dc, Points, 4, &dc->eboBackground.BrushObject);
7021             }
7022         }
7023     }
7024 
7025     EXLATEOBJ_vInitialize(&exloRGB2Dst, &gpalRGB, psurf->ppal, 0, 0, 0);
7026     EXLATEOBJ_vInitialize(&exloDst2RGB, psurf->ppal, &gpalRGB, 0, 0, 0);
7027 
7028     if (pdcattr->ulDirty_ & DIRTY_TEXT)
7029         DC_vUpdateTextBrush(dc);
7030 
7031     /*
7032      * The main rendering loop.
7033      */
7034     X64 = RealXStart64;
7035     Y64 = RealYStart64;
7036     previous = 0;
7037     DoBreak = FALSE;
7038     bResult = TRUE; /* Assume success */
7039     for (i = 0; i < Count; ++i)
7040     {
7041         ch0 = *String++;
7042         if (IS_HIGH_SURROGATE(ch0))
7043         {
7044             ++i;
7045             if (i >= Count)
7046                 break;
7047 
7048             ch1 = *String++;
7049             if (IS_LOW_SURROGATE(ch1))
7050                 ch0 = Utf32FromSurrogatePair(ch0, ch1);
7051         }
7052 
7053         glyph_index = FontLink_Chain_FindGlyph(&Chain, &Cache, &face, ch0,
7054                                                (fuOptions & ETO_GLYPH_INDEX));
7055         Cache.Hashed.GlyphIndex = glyph_index;
7056 
7057         realglyph = IntGetRealGlyph(&Cache);
7058         if (!realglyph)
7059         {
7060             bResult = FALSE;
7061             break;
7062         }
7063 
7064         /* retrieve kerning distance and move pen position */
7065         if (use_kerning && previous && glyph_index && NULL == Dx)
7066         {
7067             FT_Get_Kerning(face, previous, glyph_index, 0, &delta);
7068             X64 += delta.x;
7069             Y64 -= delta.y;
7070         }
7071 
7072         DPRINT("X64, Y64: %I64d, %I64d\n", X64, Y64);
7073         DPRINT("Advance: %d, %d\n", realglyph->root.advance.x, realglyph->root.advance.y);
7074 
7075         bitSize.cx = realglyph->bitmap.width;
7076         bitSize.cy = realglyph->bitmap.rows;
7077 
7078         /* Do chars > space & not DEL & not nbsp have a bitSize.cx of zero? */
7079         if (ch0 > L' ' && ch0 != del && ch0 != nbsp && bitSize.cx == 0)
7080             DPRINT1("WARNING: WChar 0x%04x has a bitSize.cx of zero\n", ch0);
7081 
7082         /* Don't ignore spaces or non-breaking spaces when computing offset.
7083          * This completes the fix of CORE-11787. */
7084         if ((pdcattr->flTextAlign & TA_UPDATECP) && bitSize.cx == 0 &&
7085             (ch0 == L' ' || ch0 == nbsp)) // Space chars needing x-dim widths
7086         {
7087             IntUnLockFreeType();
7088             /* Get the width of the space character */
7089             TextIntGetTextExtentPoint(dc, TextObj, L" ", 1, 0, NULL, 0, &spaceWidth, 0);
7090             IntLockFreeType();
7091             bitSize.cx = spaceWidth.cx;
7092             realglyph->left = 0;
7093         }
7094 
7095         MaskRect.right = realglyph->bitmap.width;
7096         MaskRect.bottom = realglyph->bitmap.rows;
7097 
7098         DestRect.left   = ((X64 + 32) >> 6) + realglyph->left;
7099         DestRect.right  = DestRect.left + bitSize.cx;
7100         DestRect.top    = ((Y64 + 32) >> 6) - realglyph->top;
7101         DestRect.bottom = DestRect.top + bitSize.cy;
7102 
7103         /* Check if the bitmap has any pixels */
7104         if ((bitSize.cx != 0) && (bitSize.cy != 0))
7105         {
7106             /*
7107              * We should create the bitmap out of the loop at the biggest possible
7108              * glyph size. Then use memset with 0 to clear it and sourcerect to
7109              * limit the work of the transbitblt.
7110              */
7111             HSourceGlyph = EngCreateBitmap(bitSize, realglyph->bitmap.pitch,
7112                                            BMF_8BPP, BMF_TOPDOWN,
7113                                            realglyph->bitmap.buffer);
7114             if (!HSourceGlyph)
7115             {
7116                 DPRINT1("WARNING: EngCreateBitmap() failed!\n");
7117                 bResult = FALSE;
7118                 break;
7119             }
7120 
7121             SourceGlyphSurf = EngLockSurface((HSURF)HSourceGlyph);
7122             if (!SourceGlyphSurf)
7123             {
7124                 EngDeleteSurface((HSURF)HSourceGlyph);
7125                 DPRINT1("WARNING: EngLockSurface() failed!\n");
7126                 bResult = FALSE;
7127                 break;
7128             }
7129 
7130             /*
7131              * Use the font data as a mask to paint onto the DCs surface using a
7132              * brush.
7133              */
7134             if (lprc && (fuOptions & ETO_CLIPPED))
7135             {
7136                 // We do the check '>=' instead of '>' to possibly save an iteration
7137                 // through this loop, since it's breaking after the drawing is done,
7138                 // and x is always incremented.
7139                 if (DestRect.right >= lprc->right)
7140                 {
7141                     DestRect.right = lprc->right;
7142                     DoBreak = TRUE;
7143                 }
7144 
7145                 if (DestRect.bottom >= lprc->bottom)
7146                 {
7147                     DestRect.bottom = lprc->bottom;
7148                 }
7149             }
7150 
7151             if (!IntEngMaskBlt(SurfObj,
7152                                SourceGlyphSurf,
7153                                (CLIPOBJ *)&dc->co,
7154                                &exloRGB2Dst.xlo,
7155                                &exloDst2RGB.xlo,
7156                                &DestRect,
7157                                (PPOINTL)&MaskRect,
7158                                &dc->eboText.BrushObject,
7159                                &PointZero))
7160             {
7161                 DPRINT1("Failed to MaskBlt a glyph!\n");
7162             }
7163 
7164             EngUnlockSurface(SourceGlyphSurf);
7165             EngDeleteSurface((HSURF)HSourceGlyph);
7166         }
7167 
7168         if (DoBreak)
7169             break;
7170 
7171         if (NULL == Dx)
7172         {
7173             X64 += realglyph->root.advance.x >> 10;
7174             Y64 -= realglyph->root.advance.y >> 10;
7175         }
7176         else if (fuOptions & ETO_PDY)
7177         {
7178             vec.x = (Dx[2 * i + 0] << 6);
7179             vec.y = (Dx[2 * i + 1] << 6);
7180             if (!bNoTransform)
7181                 FT_Vector_Transform(&vec, &Cache.Hashed.matTransform);
7182             X64 += vec.x;
7183             Y64 -= vec.y;
7184         }
7185         else
7186         {
7187             vec.x = (Dx[i] << 6);
7188             vec.y = 0;
7189             if (!bNoTransform)
7190                 FT_Vector_Transform(&vec, &Cache.Hashed.matTransform);
7191             X64 += vec.x;
7192             Y64 -= vec.y;
7193         }
7194 
7195         DPRINT("New X64, New Y64: %I64d, %I64d\n", X64, Y64);
7196 
7197         previous = glyph_index;
7198     }
7199     /* Don't update position if String == NULL. Fixes CORE-19721. */
7200     if ((pdcattr->flTextAlign & TA_UPDATECP) && String)
7201         pdcattr->ptlCurrent.x = DestRect.right - dc->ptlDCOrig.x;
7202 
7203     if (plf->lfUnderline || plf->lfStrikeOut) /* Underline or strike-out? */
7204     {
7205         /* Calculate the position and the thickness */
7206         INT underline_position, thickness;
7207         FT_Vector vecA64, vecB64;
7208 
7209         DeltaX64 = X64 - RealXStart64;
7210         DeltaY64 = Y64 - RealYStart64;
7211 
7212         if (!face->units_per_EM)
7213         {
7214             underline_position = 0;
7215             thickness = 1;
7216         }
7217         else
7218         {
7219             underline_position =
7220                 face->underline_position * face->size->metrics.y_ppem / face->units_per_EM;
7221             thickness =
7222                 face->underline_thickness * face->size->metrics.y_ppem / face->units_per_EM;
7223             if (thickness <= 0)
7224                 thickness = 1;
7225         }
7226 
7227         if (plf->lfUnderline) /* Draw underline */
7228         {
7229             vecA64.x = 0;
7230             vecA64.y = (-underline_position - thickness / 2) << 6;
7231             vecB64.x = 0;
7232             vecB64.y = vecA64.y + (thickness << 6);
7233             FT_Vector_Transform(&vecA64, &Cache.Hashed.matTransform);
7234             FT_Vector_Transform(&vecB64, &Cache.Hashed.matTransform);
7235             {
7236                 INT X0 = (RealXStart64 - vecA64.x + 32) >> 6;
7237                 INT Y0 = (RealYStart64 + vecA64.y + 32) >> 6;
7238                 INT DX = (DeltaX64 >> 6);
7239                 if (Cache.Hashed.matTransform.xy == 0 && Cache.Hashed.matTransform.yx == 0)
7240                 {
7241                     INT CY = (vecB64.y - vecA64.y + 32) >> 6;
7242                     IntEngFillBox(dc, X0, Y0, DX, CY, &dc->eboText.BrushObject);
7243                 }
7244                 else
7245                 {
7246                     INT DY = (DeltaY64 >> 6);
7247                     INT X1 = X0 + ((vecA64.x - vecB64.x + 32) >> 6);
7248                     INT Y1 = Y0 + ((vecB64.y - vecA64.y + 32) >> 6);
7249                     POINT Points[4] =
7250                     {
7251                         { X0,       Y0      },
7252                         { X0 + DX,  Y0 + DY },
7253                         { X1 + DX,  Y1 + DY },
7254                         { X1,       Y1      },
7255                     };
7256                     IntEngFillPolygon(dc, Points, 4, &dc->eboText.BrushObject);
7257                 }
7258             }
7259         }
7260 
7261         if (plf->lfStrikeOut) /* Draw strike-out */
7262         {
7263             vecA64.x = 0;
7264             vecA64.y = -(FontGDI->tmAscent << 6) / 3;
7265             vecB64.x = 0;
7266             vecB64.y = vecA64.y + (thickness << 6);
7267             FT_Vector_Transform(&vecA64, &Cache.Hashed.matTransform);
7268             FT_Vector_Transform(&vecB64, &Cache.Hashed.matTransform);
7269             {
7270                 INT X0 = (RealXStart64 - vecA64.x + 32) >> 6;
7271                 INT Y0 = (RealYStart64 + vecA64.y + 32) >> 6;
7272                 INT DX = (DeltaX64 >> 6);
7273                 if (Cache.Hashed.matTransform.xy == 0 && Cache.Hashed.matTransform.yx == 0)
7274                 {
7275                     INT CY = (vecB64.y - vecA64.y + 32) >> 6;
7276                     IntEngFillBox(dc, X0, Y0, DX, CY, &dc->eboText.BrushObject);
7277                 }
7278                 else
7279                 {
7280                     INT DY = (DeltaY64 >> 6);
7281                     INT X1 = X0 + ((vecA64.x - vecB64.x + 32) >> 6);
7282                     INT Y1 = Y0 + ((vecB64.y - vecA64.y + 32) >> 6);
7283                     POINT Points[4] =
7284                     {
7285                         { X0,       Y0      },
7286                         { X0 + DX,  Y0 + DY },
7287                         { X1 + DX,  Y1 + DY },
7288                         { X1,       Y1      },
7289                     };
7290                     IntEngFillPolygon(dc, Points, 4, &dc->eboText.BrushObject);
7291                 }
7292             }
7293         }
7294     }
7295 
7296     FontLink_Chain_Finish(&Chain);
7297 
7298     IntUnLockFreeType();
7299 
7300     EXLATEOBJ_vCleanup(&exloRGB2Dst);
7301     EXLATEOBJ_vCleanup(&exloDst2RGB);
7302 
7303 Cleanup:
7304     DC_vFinishBlit(dc, NULL);
7305 
7306     if (TextObj != NULL)
7307         TEXTOBJ_UnlockText(TextObj);
7308 
7309     return bResult;
7310 }
7311 
7312 
7313 BOOL
7314 APIENTRY
GreExtTextOutW(IN HDC hDC,IN INT XStart,IN INT YStart,IN UINT fuOptions,IN OPTIONAL PRECTL lprc,IN LPCWSTR String,IN INT Count,IN OPTIONAL LPINT Dx,IN DWORD dwCodePage)7315 GreExtTextOutW(
7316     IN HDC hDC,
7317     IN INT XStart,
7318     IN INT YStart,
7319     IN UINT fuOptions,
7320     IN OPTIONAL PRECTL lprc,
7321     IN LPCWSTR String,
7322     IN INT Count,
7323     IN OPTIONAL LPINT Dx,
7324     IN DWORD dwCodePage)
7325 {
7326     BOOL bResult;
7327     DC *dc;
7328 
7329     // TODO: Write test-cases to exactly match real Windows in different
7330     // bad parameters (e.g. does Windows check the DC or the RECT first?).
7331     dc = DC_LockDc(hDC);
7332     if (!dc)
7333     {
7334         EngSetLastError(ERROR_INVALID_HANDLE);
7335         return FALSE;
7336     }
7337 
7338     bResult = IntExtTextOutW( dc,
7339                               XStart,
7340                               YStart,
7341                               fuOptions,
7342                               lprc,
7343                               String,
7344                               Count,
7345                               Dx,
7346                               dwCodePage );
7347 
7348     DC_UnlockDc(dc);
7349 
7350     return bResult;
7351 }
7352 
7353 #define STACK_TEXT_BUFFER_SIZE 512
7354 
7355 BOOL
7356 APIENTRY
NtGdiExtTextOutW(IN HDC hDC,IN INT XStart,IN INT YStart,IN UINT fuOptions,IN OPTIONAL LPRECT UnsafeRect,IN LPWSTR UnsafeString,IN INT Count,IN OPTIONAL LPINT UnsafeDx,IN DWORD dwCodePage)7357 NtGdiExtTextOutW(
7358     IN HDC hDC,
7359     IN INT XStart,
7360     IN INT YStart,
7361     IN UINT fuOptions,
7362     IN OPTIONAL LPRECT UnsafeRect,
7363     IN LPWSTR UnsafeString,
7364     IN INT Count,
7365     IN OPTIONAL LPINT UnsafeDx,
7366     IN DWORD dwCodePage)
7367 {
7368     BOOL Result = FALSE;
7369     NTSTATUS Status = STATUS_SUCCESS;
7370     RECTL SafeRect;
7371     BYTE LocalBuffer[STACK_TEXT_BUFFER_SIZE];
7372     PVOID Buffer = LocalBuffer;
7373     LPCWSTR SafeString = NULL;
7374     LPINT SafeDx = NULL;
7375     ULONG BufSize, StringSize, DxSize = 0;
7376 
7377     /* Check if String is valid */
7378     if ((Count > 0xFFFF) || (Count > 0 && UnsafeString == NULL))
7379     {
7380         EngSetLastError(ERROR_INVALID_PARAMETER);
7381         return FALSE;
7382     }
7383 
7384     if (Count > 0)
7385     {
7386         /* Calculate buffer size for string and Dx values */
7387         BufSize = StringSize = Count * sizeof(WCHAR);
7388         if (UnsafeDx)
7389         {
7390             /* If ETO_PDY is specified, we have pairs of INTs */
7391             DxSize = (Count * sizeof(INT)) * ((fuOptions & ETO_PDY) ? 2 : 1);
7392             BufSize += DxSize;
7393         }
7394 
7395         /* Check if our local buffer is large enough */
7396         if (BufSize > sizeof(LocalBuffer))
7397         {
7398             /* It's not, allocate a temp buffer */
7399             Buffer = ExAllocatePoolWithTag(PagedPool, BufSize, GDITAG_TEXT);
7400             if (!Buffer)
7401             {
7402                 return FALSE;
7403             }
7404         }
7405 
7406         /* Probe and copy user mode data to the buffer */
7407         _SEH2_TRY
7408         {
7409             /* Put the Dx before the String to assure alignment of 4 */
7410             SafeString = (LPCWSTR)(((ULONG_PTR)Buffer) + DxSize);
7411 
7412             /* Probe and copy the string */
7413             ProbeForRead(UnsafeString, StringSize, 1);
7414             RtlCopyMemory((PVOID)SafeString, UnsafeString, StringSize);
7415 
7416             /* If we have Dx values... */
7417             if (UnsafeDx)
7418             {
7419                 /* ... probe and copy them */
7420                 SafeDx = Buffer;
7421                 ProbeForRead(UnsafeDx, DxSize, 1);
7422                 RtlCopyMemory(SafeDx, UnsafeDx, DxSize);
7423             }
7424         }
7425         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
7426         {
7427             Status = _SEH2_GetExceptionCode();
7428         }
7429         _SEH2_END
7430         if (!NT_SUCCESS(Status))
7431         {
7432             goto cleanup;
7433         }
7434     }
7435 
7436     /* If we have a rect, copy it */
7437     if (UnsafeRect)
7438     {
7439         _SEH2_TRY
7440         {
7441             ProbeForRead(UnsafeRect, sizeof(RECT), 1);
7442             SafeRect = *UnsafeRect;
7443         }
7444         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
7445         {
7446             Status = _SEH2_GetExceptionCode();
7447         }
7448         _SEH2_END
7449         if (!NT_SUCCESS(Status))
7450         {
7451             goto cleanup;
7452         }
7453     }
7454 
7455     /* Finally call the internal routine */
7456     Result = GreExtTextOutW(hDC,
7457                             XStart,
7458                             YStart,
7459                             fuOptions,
7460                             &SafeRect,
7461                             SafeString,
7462                             Count,
7463                             SafeDx,
7464                             dwCodePage);
7465 
7466 cleanup:
7467     /* If we allocated a buffer, free it */
7468     if (Buffer != LocalBuffer)
7469     {
7470         ExFreePoolWithTag(Buffer, GDITAG_TEXT);
7471     }
7472 
7473     return Result;
7474 }
7475 
7476 
7477 /*
7478 * @implemented
7479 */
7480 BOOL
7481 APIENTRY
NtGdiGetCharABCWidthsW(IN HDC hDC,IN UINT FirstChar,IN ULONG Count,IN OPTIONAL PWCHAR UnSafepwch,IN FLONG fl,OUT PVOID Buffer)7482 NtGdiGetCharABCWidthsW(
7483     IN HDC hDC,
7484     IN UINT FirstChar,
7485     IN ULONG Count,
7486     IN OPTIONAL PWCHAR UnSafepwch,
7487     IN FLONG fl,
7488     OUT PVOID Buffer)
7489 {
7490     LPABC SafeBuff;
7491     LPABCFLOAT SafeBuffF = NULL;
7492     PDC dc;
7493     PDC_ATTR pdcattr;
7494     PTEXTOBJ TextObj;
7495     PFONTGDI FontGDI;
7496     FT_Face face;
7497     FT_CharMap charmap, found = NULL;
7498     UINT i, glyph_index, BufferSize;
7499     HFONT hFont = 0;
7500     NTSTATUS Status = STATUS_SUCCESS;
7501     PWCHAR Safepwch = NULL;
7502     LOGFONTW *plf;
7503 
7504     if (!Buffer)
7505     {
7506         EngSetLastError(ERROR_INVALID_PARAMETER);
7507         return FALSE;
7508     }
7509 
7510     if (UnSafepwch)
7511     {
7512         UINT pwchSize = Count * sizeof(WCHAR);
7513         Safepwch = ExAllocatePoolWithTag(PagedPool, pwchSize, GDITAG_TEXT);
7514 
7515         if(!Safepwch)
7516         {
7517             EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
7518             return FALSE;
7519         }
7520 
7521         _SEH2_TRY
7522         {
7523             ProbeForRead(UnSafepwch, pwchSize, 1);
7524             RtlCopyMemory(Safepwch, UnSafepwch, pwchSize);
7525         }
7526         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
7527         {
7528             Status = _SEH2_GetExceptionCode();
7529         }
7530         _SEH2_END;
7531     }
7532 
7533     if (!NT_SUCCESS(Status))
7534     {
7535         if(Safepwch)
7536             ExFreePoolWithTag(Safepwch , GDITAG_TEXT);
7537 
7538         SetLastNtError(Status);
7539         return FALSE;
7540     }
7541 
7542     BufferSize = Count * sizeof(ABC); // Same size!
7543     SafeBuff = ExAllocatePoolWithTag(PagedPool, BufferSize, GDITAG_TEXT);
7544     if (!fl) SafeBuffF = (LPABCFLOAT) SafeBuff;
7545     if (SafeBuff == NULL)
7546     {
7547 
7548         if(Safepwch)
7549             ExFreePoolWithTag(Safepwch , GDITAG_TEXT);
7550 
7551         EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
7552         return FALSE;
7553     }
7554 
7555     dc = DC_LockDc(hDC);
7556     if (dc == NULL)
7557     {
7558         ExFreePoolWithTag(SafeBuff, GDITAG_TEXT);
7559 
7560         if(Safepwch)
7561             ExFreePoolWithTag(Safepwch , GDITAG_TEXT);
7562 
7563         EngSetLastError(ERROR_INVALID_HANDLE);
7564         return FALSE;
7565     }
7566     pdcattr = dc->pdcattr;
7567     hFont = pdcattr->hlfntNew;
7568     TextObj = RealizeFontInit(hFont);
7569 
7570     DC_UnlockDc(dc);
7571 
7572     if (TextObj == NULL)
7573     {
7574         ExFreePoolWithTag(SafeBuff, GDITAG_TEXT);
7575 
7576         if(Safepwch)
7577             ExFreePoolWithTag(Safepwch , GDITAG_TEXT);
7578 
7579         EngSetLastError(ERROR_INVALID_HANDLE);
7580         return FALSE;
7581     }
7582 
7583     FontGDI = ObjToGDI(TextObj->Font, FONT);
7584 
7585     face = FontGDI->SharedFace->Face;
7586     if (face->charmap == NULL)
7587     {
7588         for (i = 0; i < (UINT)face->num_charmaps; i++)
7589         {
7590             charmap = face->charmaps[i];
7591             if (charmap->encoding != 0)
7592             {
7593                 found = charmap;
7594                 break;
7595             }
7596         }
7597 
7598         if (!found)
7599         {
7600             DPRINT1("WARNING: Could not find desired charmap!\n");
7601             ExFreePoolWithTag(SafeBuff, GDITAG_TEXT);
7602 
7603             if(Safepwch)
7604                 ExFreePoolWithTag(Safepwch , GDITAG_TEXT);
7605 
7606             EngSetLastError(ERROR_INVALID_HANDLE);
7607             return FALSE;
7608         }
7609 
7610         IntLockFreeType();
7611         FT_Set_Charmap(face, found);
7612         IntUnLockFreeType();
7613     }
7614 
7615     plf = &TextObj->logfont.elfEnumLogfontEx.elfLogFont;
7616 
7617     // NOTE: GetCharABCWidths simply ignores lfEscapement and XFORM.
7618     IntLockFreeType();
7619     IntRequestFontSize(dc, FontGDI, plf->lfWidth, plf->lfHeight);
7620     FT_Set_Transform(face, NULL, NULL);
7621 
7622     for (i = FirstChar; i < FirstChar+Count; i++)
7623     {
7624         int adv, lsb, bbx, left, right;
7625 
7626         if (Safepwch)
7627         {
7628             glyph_index = get_glyph_index_flagged(face, Safepwch[i - FirstChar], (fl & GCABCW_INDICES));
7629         }
7630         else
7631         {
7632             glyph_index = get_glyph_index_flagged(face, i, (fl & GCABCW_INDICES));
7633         }
7634         FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
7635 
7636         left = (INT)face->glyph->metrics.horiBearingX  & -64;
7637         right = (INT)((face->glyph->metrics.horiBearingX + face->glyph->metrics.width) + 63) & -64;
7638         adv  = (face->glyph->advance.x + 32) >> 6;
7639 
7640 //      int test = (INT)(face->glyph->metrics.horiAdvance + 63) >> 6;
7641 //      DPRINT1("Advance Wine %d and Advance Ros %d\n",test, adv ); /* It's the same! */
7642 
7643         lsb = left >> 6;
7644         bbx = (right - left) >> 6;
7645         /*
7646               DPRINT1("lsb %d and bbx %d\n", lsb, bbx );
7647          */
7648         if (!fl)
7649         {
7650             SafeBuffF[i - FirstChar].abcfA = (FLOAT) lsb;
7651             SafeBuffF[i - FirstChar].abcfB = (FLOAT) bbx;
7652             SafeBuffF[i - FirstChar].abcfC = (FLOAT) (adv - lsb - bbx);
7653         }
7654         else
7655         {
7656             SafeBuff[i - FirstChar].abcA = lsb;
7657             SafeBuff[i - FirstChar].abcB = bbx;
7658             SafeBuff[i - FirstChar].abcC = adv - lsb - bbx;
7659         }
7660     }
7661     IntUnLockFreeType();
7662     TEXTOBJ_UnlockText(TextObj);
7663     Status = MmCopyToCaller(Buffer, SafeBuff, BufferSize);
7664 
7665     ExFreePoolWithTag(SafeBuff, GDITAG_TEXT);
7666 
7667     if(Safepwch)
7668         ExFreePoolWithTag(Safepwch , GDITAG_TEXT);
7669 
7670     if (!NT_SUCCESS(Status))
7671     {
7672         SetLastNtError(Status);
7673         return FALSE;
7674     }
7675 
7676     DPRINT("NtGdiGetCharABCWidths Worked!\n");
7677     return TRUE;
7678 }
7679 
7680 /*
7681 * @implemented
7682 */
7683 BOOL
7684 APIENTRY
NtGdiGetCharWidthW(IN HDC hDC,IN UINT FirstChar,IN UINT Count,IN OPTIONAL PWCHAR UnSafepwc,IN FLONG fl,OUT PVOID Buffer)7685 NtGdiGetCharWidthW(
7686     IN HDC hDC,
7687     IN UINT FirstChar,
7688     IN UINT Count,
7689     IN OPTIONAL PWCHAR UnSafepwc,
7690     IN FLONG fl,
7691     OUT PVOID Buffer)
7692 {
7693     NTSTATUS Status = STATUS_SUCCESS;
7694     LPINT SafeBuff;
7695     PFLOAT SafeBuffF = NULL;
7696     PDC dc;
7697     PDC_ATTR pdcattr;
7698     PTEXTOBJ TextObj;
7699     PFONTGDI FontGDI;
7700     FT_Face face;
7701     FT_CharMap charmap, found = NULL;
7702     UINT i, glyph_index, BufferSize;
7703     HFONT hFont = 0;
7704     PWCHAR Safepwc = NULL;
7705     LOGFONTW *plf;
7706 
7707     if (UnSafepwc)
7708     {
7709         UINT pwcSize = Count * sizeof(WCHAR);
7710         Safepwc = ExAllocatePoolWithTag(PagedPool, pwcSize, GDITAG_TEXT);
7711 
7712         if(!Safepwc)
7713         {
7714             EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
7715             return FALSE;
7716         }
7717         _SEH2_TRY
7718         {
7719             ProbeForRead(UnSafepwc, pwcSize, 1);
7720             RtlCopyMemory(Safepwc, UnSafepwc, pwcSize);
7721         }
7722         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
7723         {
7724             Status = _SEH2_GetExceptionCode();
7725         }
7726         _SEH2_END;
7727     }
7728 
7729     if (!NT_SUCCESS(Status))
7730     {
7731         SetLastNtError(Status);
7732         return FALSE;
7733     }
7734 
7735     BufferSize = Count * sizeof(INT); // Same size!
7736     SafeBuff = ExAllocatePoolWithTag(PagedPool, BufferSize, GDITAG_TEXT);
7737     if (!fl) SafeBuffF = (PFLOAT) SafeBuff;
7738     if (SafeBuff == NULL)
7739     {
7740         if(Safepwc)
7741             ExFreePoolWithTag(Safepwc, GDITAG_TEXT);
7742 
7743         EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
7744         return FALSE;
7745     }
7746 
7747     dc = DC_LockDc(hDC);
7748     if (dc == NULL)
7749     {
7750         if(Safepwc)
7751             ExFreePoolWithTag(Safepwc, GDITAG_TEXT);
7752 
7753         ExFreePoolWithTag(SafeBuff, GDITAG_TEXT);
7754         EngSetLastError(ERROR_INVALID_HANDLE);
7755         return FALSE;
7756     }
7757     pdcattr = dc->pdcattr;
7758     hFont = pdcattr->hlfntNew;
7759     TextObj = RealizeFontInit(hFont);
7760     DC_UnlockDc(dc);
7761 
7762     if (TextObj == NULL)
7763     {
7764         if(Safepwc)
7765             ExFreePoolWithTag(Safepwc, GDITAG_TEXT);
7766 
7767         ExFreePoolWithTag(SafeBuff, GDITAG_TEXT);
7768         EngSetLastError(ERROR_INVALID_HANDLE);
7769         return FALSE;
7770     }
7771 
7772     FontGDI = ObjToGDI(TextObj->Font, FONT);
7773 
7774     face = FontGDI->SharedFace->Face;
7775     if (face->charmap == NULL)
7776     {
7777         for (i = 0; i < (UINT)face->num_charmaps; i++)
7778         {
7779             charmap = face->charmaps[i];
7780             if (charmap->encoding != 0)
7781             {
7782                 found = charmap;
7783                 break;
7784             }
7785         }
7786 
7787         if (!found)
7788         {
7789             DPRINT1("WARNING: Could not find desired charmap!\n");
7790 
7791             if(Safepwc)
7792                 ExFreePoolWithTag(Safepwc, GDITAG_TEXT);
7793 
7794             ExFreePoolWithTag(SafeBuff, GDITAG_TEXT);
7795             EngSetLastError(ERROR_INVALID_HANDLE);
7796             return FALSE;
7797         }
7798 
7799         IntLockFreeType();
7800         FT_Set_Charmap(face, found);
7801         IntUnLockFreeType();
7802     }
7803 
7804     plf = &TextObj->logfont.elfEnumLogfontEx.elfLogFont;
7805 
7806     // NOTE: GetCharWidth simply ignores lfEscapement and XFORM.
7807     IntLockFreeType();
7808     IntRequestFontSize(dc, FontGDI, plf->lfWidth, plf->lfHeight);
7809     FT_Set_Transform(face, NULL, NULL);
7810 
7811     for (i = FirstChar; i < FirstChar+Count; i++)
7812     {
7813         if (Safepwc)
7814         {
7815             glyph_index = get_glyph_index_flagged(face, Safepwc[i - FirstChar], (fl & GCW_INDICES));
7816         }
7817         else
7818         {
7819             glyph_index = get_glyph_index_flagged(face, i, (fl & GCW_INDICES));
7820         }
7821         FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
7822         if (!fl)
7823             SafeBuffF[i - FirstChar] = (FLOAT) ((face->glyph->advance.x + 32) >> 6);
7824         else
7825             SafeBuff[i - FirstChar] = (face->glyph->advance.x + 32) >> 6;
7826     }
7827     IntUnLockFreeType();
7828     TEXTOBJ_UnlockText(TextObj);
7829     MmCopyToCaller(Buffer, SafeBuff, BufferSize);
7830 
7831     if(Safepwc)
7832         ExFreePoolWithTag(Safepwc, GDITAG_TEXT);
7833 
7834     ExFreePoolWithTag(SafeBuff, GDITAG_TEXT);
7835     return TRUE;
7836 }
7837 
7838 
7839 /*
7840 * @implemented
7841 */
7842 // TODO: Move this code into NtGdiGetGlyphIndicesWInternal and wrap
7843 // NtGdiGetGlyphIndicesW around NtGdiGetGlyphIndicesWInternal instead.
7844 // NOTE: See also GreGetGlyphIndicesW.
7845 __kernel_entry
7846 W32KAPI
7847 DWORD
7848 APIENTRY
NtGdiGetGlyphIndicesW(_In_ HDC hdc,_In_reads_opt_ (cwc)LPCWSTR pwc,_In_ INT cwc,_Out_writes_opt_ (cwc)LPWORD pgi,_In_ DWORD iMode)7849 NtGdiGetGlyphIndicesW(
7850     _In_ HDC hdc,
7851     _In_reads_opt_(cwc) LPCWSTR pwc,
7852     _In_ INT cwc,
7853     _Out_writes_opt_(cwc) LPWORD pgi,
7854     _In_ DWORD iMode)
7855 {
7856     PDC dc;
7857     PDC_ATTR pdcattr;
7858     PTEXTOBJ TextObj;
7859     PFONTGDI FontGDI;
7860     HFONT hFont = NULL;
7861     NTSTATUS Status = STATUS_SUCCESS;
7862     OUTLINETEXTMETRICW *potm;
7863     INT i;
7864     WCHAR DefChar = 0xffff;
7865     PWSTR Buffer = NULL;
7866     ULONG Size, pwcSize;
7867     PWSTR Safepwc = NULL;
7868     LPCWSTR UnSafepwc = pwc;
7869     LPWORD UnSafepgi = pgi;
7870     FT_Face Face;
7871     TT_OS2 *pOS2;
7872 
7873     if (cwc < 0)
7874     {
7875         DPRINT1("cwc < 0\n");
7876         return GDI_ERROR;
7877     }
7878 
7879     if (!UnSafepwc && !UnSafepgi && cwc > 0)
7880     {
7881         DPRINT1("!UnSafepwc && !UnSafepgi && cwc > 0\n");
7882         return GDI_ERROR;
7883     }
7884 
7885     if (!UnSafepwc != !UnSafepgi)
7886     {
7887         DPRINT1("UnSafepwc == %p, UnSafepgi = %p\n", UnSafepwc, UnSafepgi);
7888         return GDI_ERROR;
7889     }
7890 
7891     /* Get FontGDI */
7892     dc = DC_LockDc(hdc);
7893     if (!dc)
7894     {
7895         DPRINT1("!DC_LockDC\n");
7896         return GDI_ERROR;
7897     }
7898     pdcattr = dc->pdcattr;
7899     hFont = pdcattr->hlfntNew;
7900     TextObj = RealizeFontInit(hFont);
7901     DC_UnlockDc(dc);
7902     if (!TextObj)
7903     {
7904         DPRINT1("!TextObj\n");
7905         return GDI_ERROR;
7906     }
7907     FontGDI = ObjToGDI(TextObj->Font, FONT);
7908     TEXTOBJ_UnlockText(TextObj);
7909 
7910     if (cwc == 0)
7911     {
7912         if (!UnSafepwc && !UnSafepgi)
7913         {
7914             Face = FontGDI->SharedFace->Face;
7915             return Face->num_glyphs;
7916         }
7917         else
7918         {
7919             Status = STATUS_UNSUCCESSFUL;
7920             goto ErrorRet;
7921         }
7922     }
7923 
7924     Buffer = ExAllocatePoolWithTag(PagedPool, cwc * sizeof(WORD), GDITAG_TEXT);
7925     if (!Buffer)
7926     {
7927         DPRINT1("ExAllocatePoolWithTag\n");
7928         return GDI_ERROR;
7929     }
7930 
7931     /* Get DefChar */
7932     if (iMode & GGI_MARK_NONEXISTING_GLYPHS)
7933     {
7934         DefChar = 0xffff;
7935     }
7936     else
7937     {
7938         Face = FontGDI->SharedFace->Face;
7939         if (FT_IS_SFNT(Face))
7940         {
7941             IntLockFreeType();
7942             pOS2 = FT_Get_Sfnt_Table(Face, ft_sfnt_os2);
7943             DefChar = (pOS2->usDefaultChar ? get_glyph_index(Face, pOS2->usDefaultChar) : 0);
7944             IntUnLockFreeType();
7945         }
7946         else
7947         {
7948             ASSERT_FREETYPE_LOCK_NOT_HELD();
7949             Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL, FALSE);
7950             if (!Size)
7951             {
7952                 Status = STATUS_UNSUCCESSFUL;
7953                 DPRINT1("!Size\n");
7954                 goto ErrorRet;
7955             }
7956             potm = ExAllocatePoolWithTag(PagedPool, Size, GDITAG_TEXT);
7957             if (!potm)
7958             {
7959                 Status = STATUS_INSUFFICIENT_RESOURCES;
7960                 DPRINT1("!potm\n");
7961                 goto ErrorRet;
7962             }
7963             ASSERT_FREETYPE_LOCK_NOT_HELD();
7964             Size = IntGetOutlineTextMetrics(FontGDI, Size, potm, FALSE);
7965             if (Size)
7966                 DefChar = potm->otmTextMetrics.tmDefaultChar;
7967             ExFreePoolWithTag(potm, GDITAG_TEXT);
7968         }
7969     }
7970 
7971     /* Allocate for Safepwc */
7972     pwcSize = cwc * sizeof(WCHAR);
7973     Safepwc = ExAllocatePoolWithTag(PagedPool, pwcSize, GDITAG_TEXT);
7974     if (!Safepwc)
7975     {
7976         Status = STATUS_NO_MEMORY;
7977         DPRINT1("!Safepwc\n");
7978         goto ErrorRet;
7979     }
7980 
7981     _SEH2_TRY
7982     {
7983         ProbeForRead(UnSafepwc, pwcSize, 1);
7984         RtlCopyMemory(Safepwc, UnSafepwc, pwcSize);
7985     }
7986     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
7987     {
7988         Status = _SEH2_GetExceptionCode();
7989     }
7990     _SEH2_END;
7991 
7992     if (!NT_SUCCESS(Status))
7993     {
7994         DPRINT1("Status: %08lX\n", Status);
7995         goto ErrorRet;
7996     }
7997 
7998     /* Get glyph indeces */
7999     IntLockFreeType();
8000     for (i = 0; i < cwc; i++)
8001     {
8002         Buffer[i] = get_glyph_index(FontGDI->SharedFace->Face, Safepwc[i]);
8003         if (Buffer[i] == 0)
8004         {
8005             Buffer[i] = DefChar;
8006         }
8007     }
8008     IntUnLockFreeType();
8009 
8010     _SEH2_TRY
8011     {
8012         ProbeForWrite(UnSafepgi, cwc * sizeof(WORD), 1);
8013         RtlCopyMemory(UnSafepgi, Buffer, cwc * sizeof(WORD));
8014     }
8015     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
8016     {
8017         Status = _SEH2_GetExceptionCode();
8018     }
8019     _SEH2_END;
8020 
8021 ErrorRet:
8022     if (Buffer != NULL)
8023     {
8024         ExFreePoolWithTag(Buffer, GDITAG_TEXT);
8025     }
8026     if (Safepwc != NULL)
8027     {
8028         ExFreePoolWithTag(Safepwc, GDITAG_TEXT);
8029     }
8030 
8031     if (NT_SUCCESS(Status))
8032         return cwc;
8033 
8034     return GDI_ERROR;
8035 }
8036 
8037 /* EOF */
8038