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