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