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