898f58c765fb1e75560164fdb509e69b61e5b21e
[reactos.git] / reactos / win32ss / gdi / ntgdi / freetype.c
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 static const WORD gusEnglishUS = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
52
53 /* special font names */
54 static const UNICODE_STRING MarlettW = RTL_CONSTANT_STRING(L"Marlett");
55 static const UNICODE_STRING SystemW = RTL_CONSTANT_STRING(L"System");
56 static const UNICODE_STRING FixedSysW = RTL_CONSTANT_STRING(L"FixedSys");
57
58 /* registry */
59 static UNICODE_STRING FontRegPath =
60 RTL_CONSTANT_STRING(L"\\REGISTRY\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts");
61
62
63 /* The FreeType library is not thread safe, so we have
64 to serialize access to it */
65 static PFAST_MUTEX FreeTypeLock;
66
67 static LIST_ENTRY FontListHead;
68 static PFAST_MUTEX FontListLock;
69 static BOOL RenderingEnabled = TRUE;
70
71 #define IntLockGlobalFonts \
72 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(FontListLock)
73
74 #define IntUnLockGlobalFonts \
75 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(FontListLock)
76
77 #define ASSERT_GLOBALFONTS_LOCK_HELD() \
78 ASSERT(FontListLock->Owner == KeGetCurrentThread())
79
80 #define IntLockFreeType \
81 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(FreeTypeLock)
82
83 #define IntUnLockFreeType \
84 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(FreeTypeLock)
85
86 #define ASSERT_FREETYPE_LOCK_HELD() \
87 ASSERT(FreeTypeLock->Owner == KeGetCurrentThread())
88
89 #define ASSERT_FREETYPE_LOCK_NOT_HELD() \
90 ASSERT(FreeTypeLock->Owner != KeGetCurrentThread())
91
92 #define MAX_FONT_CACHE 256
93
94 static LIST_ENTRY FontCacheListHead;
95 static UINT FontCacheNumEntries;
96
97 static PWCHAR ElfScripts[32] = /* These are in the order of the fsCsb[0] bits */
98 {
99 L"Western", /* 00 */
100 L"Central_European",
101 L"Cyrillic",
102 L"Greek",
103 L"Turkish",
104 L"Hebrew",
105 L"Arabic",
106 L"Baltic",
107 L"Vietnamese", /* 08 */
108 NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 15 */
109 L"Thai",
110 L"Japanese",
111 L"CHINESE_GB2312",
112 L"Hangul",
113 L"CHINESE_BIG5",
114 L"Hangul(Johab)",
115 NULL, NULL, /* 23 */
116 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
117 L"Symbol" /* 31 */
118 };
119
120 /*
121 * For TranslateCharsetInfo
122 */
123 #define CP_SYMBOL 42
124 #define MAXTCIINDEX 32
125 static const CHARSETINFO FontTci[MAXTCIINDEX] =
126 {
127 /* ANSI */
128 { ANSI_CHARSET, 1252, {{0,0,0,0},{FS_LATIN1,0}} },
129 { EASTEUROPE_CHARSET, 1250, {{0,0,0,0},{FS_LATIN2,0}} },
130 { RUSSIAN_CHARSET, 1251, {{0,0,0,0},{FS_CYRILLIC,0}} },
131 { GREEK_CHARSET, 1253, {{0,0,0,0},{FS_GREEK,0}} },
132 { TURKISH_CHARSET, 1254, {{0,0,0,0},{FS_TURKISH,0}} },
133 { HEBREW_CHARSET, 1255, {{0,0,0,0},{FS_HEBREW,0}} },
134 { ARABIC_CHARSET, 1256, {{0,0,0,0},{FS_ARABIC,0}} },
135 { BALTIC_CHARSET, 1257, {{0,0,0,0},{FS_BALTIC,0}} },
136 { VIETNAMESE_CHARSET, 1258, {{0,0,0,0},{FS_VIETNAMESE,0}} },
137 /* reserved by ANSI */
138 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
139 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
140 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
141 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
142 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
143 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
144 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
145 /* ANSI and OEM */
146 { THAI_CHARSET, 874, {{0,0,0,0},{FS_THAI,0}} },
147 { SHIFTJIS_CHARSET, 932, {{0,0,0,0},{FS_JISJAPAN,0}} },
148 { GB2312_CHARSET, 936, {{0,0,0,0},{FS_CHINESESIMP,0}} },
149 { HANGEUL_CHARSET, 949, {{0,0,0,0},{FS_WANSUNG,0}} },
150 { CHINESEBIG5_CHARSET, 950, {{0,0,0,0},{FS_CHINESETRAD,0}} },
151 { JOHAB_CHARSET, 1361, {{0,0,0,0},{FS_JOHAB,0}} },
152 /* Reserved for alternate ANSI and OEM */
153 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
154 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
155 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
156 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
157 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
158 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
159 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
160 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
161 /* Reserved for system */
162 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
163 { SYMBOL_CHARSET, CP_SYMBOL, {{0,0,0,0},{FS_SYMBOL,0}} }
164 };
165
166 /* list head */
167 static RTL_STATIC_LIST_HEAD(FontSubstListHead);
168
169 static void
170 SharedMem_AddRef(PSHARED_MEM Ptr)
171 {
172 ASSERT_FREETYPE_LOCK_HELD();
173
174 ++Ptr->RefCount;
175 }
176
177 static void
178 SharedFaceCache_Init(PSHARED_FACE_CACHE Cache)
179 {
180 Cache->OutlineRequiredSize = 0;
181 RtlInitUnicodeString(&Cache->FontFamily, NULL);
182 RtlInitUnicodeString(&Cache->FullName, NULL);
183 }
184
185 static PSHARED_FACE
186 SharedFace_Create(FT_Face Face, PSHARED_MEM Memory)
187 {
188 PSHARED_FACE Ptr;
189 Ptr = ExAllocatePoolWithTag(PagedPool, sizeof(SHARED_FACE), TAG_FONT);
190 if (Ptr)
191 {
192 Ptr->Face = Face;
193 Ptr->RefCount = 1;
194 Ptr->Memory = Memory;
195 SharedFaceCache_Init(&Ptr->EnglishUS);
196 SharedFaceCache_Init(&Ptr->UserLanguage);
197
198 SharedMem_AddRef(Memory);
199 DPRINT("Creating SharedFace for %s\n", Face->family_name);
200 }
201 return Ptr;
202 }
203
204 static PSHARED_MEM
205 SharedMem_Create(PBYTE Buffer, ULONG BufferSize, BOOL IsMapping)
206 {
207 PSHARED_MEM Ptr;
208 Ptr = ExAllocatePoolWithTag(PagedPool, sizeof(SHARED_MEM), TAG_FONT);
209 if (Ptr)
210 {
211 Ptr->Buffer = Buffer;
212 Ptr->BufferSize = BufferSize;
213 Ptr->RefCount = 1;
214 Ptr->IsMapping = IsMapping;
215 DPRINT("Creating SharedMem for %p (%i, %p)\n", Buffer, IsMapping, Ptr);
216 }
217 return Ptr;
218 }
219
220 static void
221 SharedFace_AddRef(PSHARED_FACE Ptr)
222 {
223 ASSERT_FREETYPE_LOCK_HELD();
224
225 ++Ptr->RefCount;
226 }
227
228 static void
229 RemoveCachedEntry(PFONT_CACHE_ENTRY Entry)
230 {
231 ASSERT_FREETYPE_LOCK_HELD();
232
233 FT_Done_Glyph((FT_Glyph)Entry->BitmapGlyph);
234 RemoveEntryList(&Entry->ListEntry);
235 ExFreePoolWithTag(Entry, TAG_FONT);
236 FontCacheNumEntries--;
237 ASSERT(FontCacheNumEntries <= MAX_FONT_CACHE);
238 }
239
240 static void
241 RemoveCacheEntries(FT_Face Face)
242 {
243 PLIST_ENTRY CurrentEntry;
244 PFONT_CACHE_ENTRY FontEntry;
245
246 ASSERT_FREETYPE_LOCK_HELD();
247
248 CurrentEntry = FontCacheListHead.Flink;
249 while (CurrentEntry != &FontCacheListHead)
250 {
251 FontEntry = CONTAINING_RECORD(CurrentEntry, FONT_CACHE_ENTRY, ListEntry);
252 CurrentEntry = CurrentEntry->Flink;
253
254 if (FontEntry->Face == Face)
255 {
256 RemoveCachedEntry(FontEntry);
257 }
258 }
259 }
260
261 static void SharedMem_Release(PSHARED_MEM Ptr)
262 {
263 ASSERT_FREETYPE_LOCK_HELD();
264 ASSERT(Ptr->RefCount > 0);
265
266 if (Ptr->RefCount <= 0)
267 return;
268
269 --Ptr->RefCount;
270 if (Ptr->RefCount == 0)
271 {
272 DPRINT("Releasing SharedMem for %p (%i, %p)\n", Ptr->Buffer, Ptr->IsMapping, Ptr);
273 if (Ptr->IsMapping)
274 MmUnmapViewInSystemSpace(Ptr->Buffer);
275 else
276 ExFreePoolWithTag(Ptr->Buffer, TAG_FONT);
277 ExFreePoolWithTag(Ptr, TAG_FONT);
278 }
279 }
280
281 static void
282 SharedFaceCache_Release(PSHARED_FACE_CACHE Cache)
283 {
284 RtlFreeUnicodeString(&Cache->FontFamily);
285 RtlFreeUnicodeString(&Cache->FullName);
286 }
287
288 static void
289 SharedFace_Release(PSHARED_FACE Ptr)
290 {
291 IntLockFreeType;
292 ASSERT(Ptr->RefCount > 0);
293
294 if (Ptr->RefCount <= 0)
295 return;
296
297 --Ptr->RefCount;
298 if (Ptr->RefCount == 0)
299 {
300 DPRINT("Releasing SharedFace for %s\n", Ptr->Face->family_name);
301 RemoveCacheEntries(Ptr->Face);
302 FT_Done_Face(Ptr->Face);
303 SharedMem_Release(Ptr->Memory);
304 SharedFaceCache_Release(&Ptr->EnglishUS);
305 SharedFaceCache_Release(&Ptr->UserLanguage);
306 ExFreePoolWithTag(Ptr, TAG_FONT);
307 }
308 IntUnLockFreeType;
309 }
310
311
312 /*
313 * IntLoadFontSubstList --- loads the list of font substitutes
314 */
315 BOOL FASTCALL
316 IntLoadFontSubstList(PLIST_ENTRY pHead)
317 {
318 NTSTATUS Status;
319 HANDLE KeyHandle;
320 OBJECT_ATTRIBUTES ObjectAttributes;
321 KEY_FULL_INFORMATION KeyFullInfo;
322 ULONG i, Length;
323 UNICODE_STRING FromW, ToW;
324 BYTE InfoBuffer[128];
325 PKEY_VALUE_FULL_INFORMATION pInfo;
326 BYTE CharSets[FONTSUBST_FROM_AND_TO];
327 LPWSTR pch;
328 PFONTSUBST_ENTRY pEntry;
329
330 /* the FontSubstitutes registry key */
331 static UNICODE_STRING FontSubstKey =
332 RTL_CONSTANT_STRING(L"\\Registry\\Machine\\Software\\"
333 L"Microsoft\\Windows NT\\CurrentVersion\\"
334 L"FontSubstitutes");
335
336 /* open registry key */
337 InitializeObjectAttributes(&ObjectAttributes, &FontSubstKey,
338 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
339 NULL, NULL);
340 Status = ZwOpenKey(&KeyHandle, KEY_READ, &ObjectAttributes);
341 if (!NT_SUCCESS(Status))
342 {
343 DPRINT("ZwOpenKey failed: 0x%08X\n", Status);
344 return FALSE; /* failure */
345 }
346
347 /* query count of values */
348 Status = ZwQueryKey(KeyHandle, KeyFullInformation,
349 &KeyFullInfo, sizeof(KeyFullInfo), &Length);
350 if (!NT_SUCCESS(Status))
351 {
352 DPRINT("ZwQueryKey failed: 0x%08X\n", Status);
353 ZwClose(KeyHandle);
354 return FALSE; /* failure */
355 }
356
357 /* for each value */
358 for (i = 0; i < KeyFullInfo.Values; ++i)
359 {
360 /* get value name */
361 Status = ZwEnumerateValueKey(KeyHandle, i, KeyValueFullInformation,
362 InfoBuffer, sizeof(InfoBuffer), &Length);
363 if (!NT_SUCCESS(Status))
364 {
365 DPRINT("ZwEnumerateValueKey failed: 0x%08X\n", Status);
366 break; /* failure */
367 }
368
369 /* create FromW string */
370 pInfo = (PKEY_VALUE_FULL_INFORMATION)InfoBuffer;
371 Length = pInfo->NameLength / sizeof(WCHAR);
372 pInfo->Name[Length] = UNICODE_NULL; /* truncate */
373 Status = RtlCreateUnicodeString(&FromW, pInfo->Name);
374 if (!NT_SUCCESS(Status))
375 {
376 DPRINT("RtlCreateUnicodeString failed: 0x%08X\n", Status);
377 break; /* failure */
378 }
379
380 /* query value */
381 Status = ZwQueryValueKey(KeyHandle, &FromW, KeyValueFullInformation,
382 InfoBuffer, sizeof(InfoBuffer), &Length);
383 pInfo = (PKEY_VALUE_FULL_INFORMATION)InfoBuffer;
384 if (!NT_SUCCESS(Status) || !pInfo->DataLength)
385 {
386 DPRINT("ZwQueryValueKey failed: 0x%08X\n", Status);
387 RtlFreeUnicodeString(&FromW);
388 break; /* failure */
389 }
390
391 /* create ToW string */
392 pch = (LPWSTR)((PUCHAR)pInfo + pInfo->DataOffset);
393 Length = pInfo->DataLength / sizeof(WCHAR);
394 pch[Length] = UNICODE_NULL; /* truncate */
395 Status = RtlCreateUnicodeString(&ToW, pch);
396 if (!NT_SUCCESS(Status))
397 {
398 DPRINT("RtlCreateUnicodeString failed: 0x%08X\n", Status);
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 ULONG 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 ULONG 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 = ++Win32Process->PrivateMemFontHandleCount;
1229 InsertTailList(&Win32Process->PrivateMemFontListHead, &EntryCollection->ListEntry);
1230 IntUnLockProcessPrivateFonts(Win32Process);
1231 Ret = (HANDLE)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 == (UINT)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 case NONANTIALIASED_QUALITY:
1398 return FT_RENDER_MODE_MONO;
1399 case DRAFT_QUALITY:
1400 return FT_RENDER_MODE_LIGHT;
1401 /* case CLEARTYPE_QUALITY:
1402 return FT_RENDER_MODE_LCD; */
1403 }
1404 return FT_RENDER_MODE_NORMAL;
1405 }
1406
1407
1408 NTSTATUS FASTCALL
1409 TextIntCreateFontIndirect(CONST LPLOGFONTW lf, HFONT *NewFont)
1410 {
1411 PLFONT plfont;
1412 LOGFONTW *plf;
1413
1414 plfont = LFONT_AllocFontWithHandle();
1415 if (!plfont)
1416 {
1417 return STATUS_NO_MEMORY;
1418 }
1419
1420 ExInitializePushLock(&plfont->lock);
1421 *NewFont = plfont->BaseObject.hHmgr;
1422 plf = &plfont->logfont.elfEnumLogfontEx.elfLogFont;
1423 RtlCopyMemory(plf, lf, sizeof(LOGFONTW));
1424 if (lf->lfEscapement != lf->lfOrientation)
1425 {
1426 /* This should really depend on whether GM_ADVANCED is set */
1427 plf->lfOrientation = plf->lfEscapement;
1428 }
1429 LFONT_UnlockFont(plfont);
1430
1431 return STATUS_SUCCESS;
1432 }
1433
1434 /*************************************************************************
1435 * TranslateCharsetInfo
1436 *
1437 * Fills a CHARSETINFO structure for a character set, code page, or
1438 * font. This allows making the correspondance between different labelings
1439 * (character set, Windows, ANSI, and OEM codepages, and Unicode ranges)
1440 * of the same encoding.
1441 *
1442 * Only one codepage will be set in Cs->fs. If TCI_SRCFONTSIG is used,
1443 * only one codepage should be set in *Src.
1444 *
1445 * RETURNS
1446 * TRUE on success, FALSE on failure.
1447 *
1448 */
1449 static BOOLEAN APIENTRY
1450 IntTranslateCharsetInfo(PDWORD Src, /* [in]
1451 if flags == TCI_SRCFONTSIG: pointer to fsCsb of a FONTSIGNATURE
1452 if flags == TCI_SRCCHARSET: a character set value
1453 if flags == TCI_SRCCODEPAGE: a code page value */
1454 LPCHARSETINFO Cs, /* [out] structure to receive charset information */
1455 DWORD Flags /* [in] determines interpretation of lpSrc */)
1456 {
1457 int Index = 0;
1458
1459 switch (Flags)
1460 {
1461 case TCI_SRCFONTSIG:
1462 while (Index < MAXTCIINDEX && 0 == (*Src >> Index & 0x0001))
1463 {
1464 Index++;
1465 }
1466 break;
1467 case TCI_SRCCODEPAGE:
1468 while (Index < MAXTCIINDEX && *Src != FontTci[Index].ciACP)
1469 {
1470 Index++;
1471 }
1472 break;
1473 case TCI_SRCCHARSET:
1474 while (Index < MAXTCIINDEX && *Src != FontTci[Index].ciCharset)
1475 {
1476 Index++;
1477 }
1478 break;
1479 default:
1480 return FALSE;
1481 }
1482
1483 if (Index >= MAXTCIINDEX || DEFAULT_CHARSET == FontTci[Index].ciCharset)
1484 {
1485 return FALSE;
1486 }
1487
1488 RtlCopyMemory(Cs, &FontTci[Index], sizeof(CHARSETINFO));
1489
1490 return TRUE;
1491 }
1492
1493
1494 static BOOL face_has_symbol_charmap(FT_Face ft_face)
1495 {
1496 int i;
1497
1498 for(i = 0; i < ft_face->num_charmaps; i++)
1499 {
1500 if(ft_face->charmaps[i]->encoding == FT_ENCODING_MS_SYMBOL)
1501 return TRUE;
1502 }
1503 return FALSE;
1504 }
1505
1506
1507 static void FASTCALL
1508 FillTMEx(TEXTMETRICW *TM, PFONTGDI FontGDI,
1509 TT_OS2 *pOS2, TT_HoriHeader *pHori,
1510 FT_WinFNT_HeaderRec *pFNT, BOOL RealFont)
1511 {
1512 FT_Fixed XScale, YScale;
1513 int Ascent, Descent;
1514 FT_Face Face = FontGDI->SharedFace->Face;
1515
1516 XScale = Face->size->metrics.x_scale;
1517 YScale = Face->size->metrics.y_scale;
1518
1519 if (pFNT)
1520 {
1521 TM->tmHeight = pFNT->pixel_height;
1522 TM->tmAscent = pFNT->ascent;
1523 TM->tmDescent = TM->tmHeight - TM->tmAscent;
1524 TM->tmInternalLeading = pFNT->internal_leading;
1525 TM->tmExternalLeading = pFNT->external_leading;
1526 TM->tmAveCharWidth = pFNT->avg_width;
1527 TM->tmMaxCharWidth = pFNT->max_width;
1528 TM->tmOverhang = 0;
1529 TM->tmDigitizedAspectX = pFNT->horizontal_resolution;
1530 TM->tmDigitizedAspectY = pFNT->vertical_resolution;
1531 TM->tmFirstChar = pFNT->first_char;
1532 TM->tmLastChar = pFNT->last_char;
1533 TM->tmDefaultChar = pFNT->default_char + pFNT->first_char;
1534 TM->tmBreakChar = pFNT->break_char + pFNT->first_char;
1535 TM->tmPitchAndFamily = pFNT->pitch_and_family;
1536 if (RealFont)
1537 {
1538 TM->tmWeight = FontGDI->OriginalWeight;
1539 TM->tmItalic = FontGDI->OriginalItalic;
1540 TM->tmUnderlined = pFNT->underline;
1541 TM->tmStruckOut = pFNT->strike_out;
1542 TM->tmCharSet = pFNT->charset;
1543 }
1544 else
1545 {
1546 TM->tmWeight = FontGDI->RequestWeight;
1547 TM->tmItalic = FontGDI->RequestItalic;
1548 TM->tmUnderlined = FontGDI->RequestUnderline;
1549 TM->tmStruckOut = FontGDI->RequestStrikeOut;
1550 TM->tmCharSet = FontGDI->CharSet;
1551 }
1552 return;
1553 }
1554
1555 if (pOS2->usWinAscent + pOS2->usWinDescent == 0)
1556 {
1557 Ascent = pHori->Ascender;
1558 Descent = -pHori->Descender;
1559 }
1560 else
1561 {
1562 Ascent = pOS2->usWinAscent;
1563 Descent = pOS2->usWinDescent;
1564 }
1565
1566 #if 0 /* This (Wine) code doesn't seem to work correctly for us, cmd issue */
1567 TM->tmAscent = (FT_MulFix(Ascent, YScale) + 32) >> 6;
1568 TM->tmDescent = (FT_MulFix(Descent, YScale) + 32) >> 6;
1569 #else /* This (ros) code was previously affected by a FreeType bug, but it works now */
1570 TM->tmAscent = (Face->size->metrics.ascender + 32) >> 6; /* Units above baseline */
1571 TM->tmDescent = (32 - Face->size->metrics.descender) >> 6; /* Units below baseline */
1572 #endif
1573 TM->tmInternalLeading = (FT_MulFix(Ascent + Descent - Face->units_per_EM, YScale) + 32) >> 6;
1574
1575 TM->tmHeight = TM->tmAscent + TM->tmDescent;
1576
1577 /* MSDN says:
1578 * el = MAX(0, LineGap - ((WinAscent + WinDescent) - (Ascender - Descender)))
1579 */
1580 TM->tmExternalLeading = max(0, (FT_MulFix(pHori->Line_Gap
1581 - ((Ascent + Descent)
1582 - (pHori->Ascender - pHori->Descender)),
1583 YScale) + 32) >> 6);
1584
1585 TM->tmAveCharWidth = (FT_MulFix(pOS2->xAvgCharWidth, XScale) + 32) >> 6;
1586 if (TM->tmAveCharWidth == 0)
1587 {
1588 TM->tmAveCharWidth = 1;
1589 }
1590
1591 /* Correct forumla to get the maxcharwidth from unicode and ansi font */
1592 TM->tmMaxCharWidth = (FT_MulFix(Face->max_advance_width, XScale) + 32) >> 6;
1593
1594 if (RealFont)
1595 {
1596 TM->tmWeight = FontGDI->OriginalWeight;
1597 }
1598 else
1599 {
1600 if (FontGDI->OriginalWeight != FW_DONTCARE &&
1601 FontGDI->OriginalWeight != FW_NORMAL)
1602 {
1603 TM->tmWeight = FontGDI->OriginalWeight;
1604 }
1605 else
1606 {
1607 TM->tmWeight = FontGDI->RequestWeight;
1608 }
1609 }
1610
1611 TM->tmOverhang = 0;
1612 TM->tmDigitizedAspectX = 96;
1613 TM->tmDigitizedAspectY = 96;
1614 if (face_has_symbol_charmap(Face) ||
1615 (pOS2->usFirstCharIndex >= 0xf000 && pOS2->usFirstCharIndex < 0xf100))
1616 {
1617 USHORT cpOEM, cpAnsi;
1618
1619 EngGetCurrentCodePage(&cpOEM, &cpAnsi);
1620 TM->tmFirstChar = 0;
1621 switch(cpAnsi)
1622 {
1623 case 1257: /* Baltic */
1624 TM->tmLastChar = 0xf8fd;
1625 break;
1626 default:
1627 TM->tmLastChar = 0xf0ff;
1628 }
1629 TM->tmBreakChar = 0x20;
1630 TM->tmDefaultChar = 0x1f;
1631 }
1632 else
1633 {
1634 TM->tmFirstChar = pOS2->usFirstCharIndex; /* Should be the first char in the cmap */
1635 TM->tmLastChar = pOS2->usLastCharIndex; /* Should be min(cmap_last, os2_last) */
1636
1637 if(pOS2->usFirstCharIndex <= 1)
1638 TM->tmBreakChar = pOS2->usFirstCharIndex + 2;
1639 else if (pOS2->usFirstCharIndex > 0xff)
1640 TM->tmBreakChar = 0x20;
1641 else
1642 TM->tmBreakChar = pOS2->usFirstCharIndex;
1643 TM->tmDefaultChar = TM->tmBreakChar - 1;
1644 }
1645
1646 if (RealFont)
1647 {
1648 TM->tmItalic = FontGDI->OriginalItalic;
1649 TM->tmUnderlined = FALSE;
1650 TM->tmStruckOut = FALSE;
1651 }
1652 else
1653 {
1654 if (FontGDI->OriginalItalic || FontGDI->RequestItalic)
1655 {
1656 TM->tmItalic = 0xFF;
1657 }
1658 else
1659 {
1660 TM->tmItalic = 0;
1661 }
1662 TM->tmUnderlined = (FontGDI->RequestUnderline ? 0xFF : 0);
1663 TM->tmStruckOut = (FontGDI->RequestStrikeOut ? 0xFF : 0);
1664 }
1665
1666 if (!FT_IS_FIXED_WIDTH(Face))
1667 {
1668 switch (pOS2->panose[PAN_PROPORTION_INDEX])
1669 {
1670 case PAN_PROP_MONOSPACED:
1671 TM->tmPitchAndFamily = 0;
1672 break;
1673 default:
1674 TM->tmPitchAndFamily = _TMPF_VARIABLE_PITCH;
1675 break;
1676 }
1677 }
1678 else
1679 {
1680 TM->tmPitchAndFamily = 0;
1681 }
1682
1683 switch (pOS2->panose[PAN_FAMILYTYPE_INDEX])
1684 {
1685 case PAN_FAMILY_SCRIPT:
1686 TM->tmPitchAndFamily |= FF_SCRIPT;
1687 break;
1688 case PAN_FAMILY_DECORATIVE:
1689 TM->tmPitchAndFamily |= FF_DECORATIVE;
1690 break;
1691
1692 case PAN_ANY:
1693 case PAN_NO_FIT:
1694 case PAN_FAMILY_TEXT_DISPLAY:
1695 case PAN_FAMILY_PICTORIAL: /* Symbol fonts get treated as if they were text */
1696 /* Which is clearly not what the panose spec says. */
1697 if (TM->tmPitchAndFamily == 0) /* Fixed */
1698 {
1699 TM->tmPitchAndFamily = FF_MODERN;
1700 }
1701 else
1702 {
1703 switch (pOS2->panose[PAN_SERIFSTYLE_INDEX])
1704 {
1705 case PAN_ANY:
1706 case PAN_NO_FIT:
1707 default:
1708 TM->tmPitchAndFamily |= FF_DONTCARE;
1709 break;
1710
1711 case PAN_SERIF_COVE:
1712 case PAN_SERIF_OBTUSE_COVE:
1713 case PAN_SERIF_SQUARE_COVE:
1714 case PAN_SERIF_OBTUSE_SQUARE_COVE:
1715 case PAN_SERIF_SQUARE:
1716 case PAN_SERIF_THIN:
1717 case PAN_SERIF_BONE:
1718 case PAN_SERIF_EXAGGERATED:
1719 case PAN_SERIF_TRIANGLE:
1720 TM->tmPitchAndFamily |= FF_ROMAN;
1721 break;
1722
1723 case PAN_SERIF_NORMAL_SANS:
1724 case PAN_SERIF_OBTUSE_SANS:
1725 case PAN_SERIF_PERP_SANS:
1726 case PAN_SERIF_FLARED:
1727 case PAN_SERIF_ROUNDED:
1728 TM->tmPitchAndFamily |= FF_SWISS;
1729 break;
1730 }
1731 }
1732 break;
1733 default:
1734 TM->tmPitchAndFamily |= FF_DONTCARE;
1735 }
1736
1737 if (FT_IS_SCALABLE(Face))
1738 {
1739 TM->tmPitchAndFamily |= TMPF_VECTOR;
1740 }
1741 if (FT_IS_SFNT(Face))
1742 {
1743 TM->tmPitchAndFamily |= TMPF_TRUETYPE;
1744 }
1745
1746 TM->tmCharSet = FontGDI->CharSet;
1747 }
1748
1749 static void FASTCALL
1750 FillTM(TEXTMETRICW *TM, PFONTGDI FontGDI,
1751 TT_OS2 *pOS2, TT_HoriHeader *pHori,
1752 FT_WinFNT_HeaderRec *pFNT)
1753 {
1754 FillTMEx(TM, FontGDI, pOS2, pHori, pFNT, FALSE);
1755 }
1756
1757 static NTSTATUS
1758 IntGetFontLocalizedName(PUNICODE_STRING pNameW, PSHARED_FACE SharedFace,
1759 FT_UShort NameID, FT_UShort LangID);
1760
1761 /*************************************************************
1762 * IntGetOutlineTextMetrics
1763 *
1764 */
1765 INT FASTCALL
1766 IntGetOutlineTextMetrics(PFONTGDI FontGDI,
1767 UINT Size,
1768 OUTLINETEXTMETRICW *Otm)
1769 {
1770 TT_OS2 *pOS2;
1771 TT_HoriHeader *pHori;
1772 TT_Postscript *pPost;
1773 FT_Fixed XScale, YScale;
1774 FT_WinFNT_HeaderRec Win;
1775 FT_Error Error;
1776 char *Cp;
1777 UNICODE_STRING FamilyNameW, FaceNameW, StyleNameW, FullNameW;
1778 PSHARED_FACE SharedFace = FontGDI->SharedFace;
1779 PSHARED_FACE_CACHE Cache = (PRIMARYLANGID(gusLanguageID) == LANG_ENGLISH) ? &SharedFace->EnglishUS : &SharedFace->UserLanguage;
1780 FT_Face Face = SharedFace->Face;
1781
1782 if (Cache->OutlineRequiredSize && Size < Cache->OutlineRequiredSize)
1783 {
1784 return Cache->OutlineRequiredSize;
1785 }
1786
1787 /* family name */
1788 RtlInitUnicodeString(&FamilyNameW, NULL);
1789 IntGetFontLocalizedName(&FamilyNameW, SharedFace, TT_NAME_ID_FONT_FAMILY, gusLanguageID);
1790
1791 /* face name */
1792 RtlInitUnicodeString(&FaceNameW, NULL);
1793 IntGetFontLocalizedName(&FaceNameW, SharedFace, TT_NAME_ID_FULL_NAME, gusLanguageID);
1794
1795 /* style name */
1796 RtlInitUnicodeString(&StyleNameW, NULL);
1797 IntGetFontLocalizedName(&StyleNameW, SharedFace, TT_NAME_ID_FONT_SUBFAMILY, gusLanguageID);
1798
1799 /* unique name (full name) */
1800 RtlInitUnicodeString(&FullNameW, NULL);
1801 IntGetFontLocalizedName(&FullNameW, SharedFace, TT_NAME_ID_UNIQUE_ID, gusLanguageID);
1802
1803 if (!Cache->OutlineRequiredSize)
1804 {
1805 UINT Needed;
1806 Needed = sizeof(OUTLINETEXTMETRICW);
1807 Needed += FamilyNameW.Length + sizeof(WCHAR);
1808 Needed += FaceNameW.Length + sizeof(WCHAR);
1809 Needed += StyleNameW.Length + sizeof(WCHAR);
1810 Needed += FullNameW.Length + sizeof(WCHAR);
1811
1812 Cache->OutlineRequiredSize = Needed;
1813 }
1814
1815 if (Size < Cache->OutlineRequiredSize)
1816 {
1817 RtlFreeUnicodeString(&FamilyNameW);
1818 RtlFreeUnicodeString(&FaceNameW);
1819 RtlFreeUnicodeString(&StyleNameW);
1820 RtlFreeUnicodeString(&FullNameW);
1821 return Cache->OutlineRequiredSize;
1822 }
1823
1824 XScale = Face->size->metrics.x_scale;
1825 YScale = Face->size->metrics.y_scale;
1826
1827 IntLockFreeType;
1828 pOS2 = FT_Get_Sfnt_Table(Face, ft_sfnt_os2);
1829 if (NULL == pOS2)
1830 {
1831 IntUnLockFreeType;
1832 DPRINT1("Can't find OS/2 table - not TT font?\n");
1833 RtlFreeUnicodeString(&FamilyNameW);
1834 RtlFreeUnicodeString(&FaceNameW);
1835 RtlFreeUnicodeString(&StyleNameW);
1836 RtlFreeUnicodeString(&FullNameW);
1837 return 0;
1838 }
1839
1840 pHori = FT_Get_Sfnt_Table(Face, ft_sfnt_hhea);
1841 if (NULL == pHori)
1842 {
1843 IntUnLockFreeType;
1844 DPRINT1("Can't find HHEA table - not TT font?\n");
1845 RtlFreeUnicodeString(&FamilyNameW);
1846 RtlFreeUnicodeString(&FaceNameW);
1847 RtlFreeUnicodeString(&StyleNameW);
1848 RtlFreeUnicodeString(&FullNameW);
1849 return 0;
1850 }
1851
1852 pPost = FT_Get_Sfnt_Table(Face, ft_sfnt_post); /* We can live with this failing */
1853
1854 Error = FT_Get_WinFNT_Header(Face , &Win);
1855
1856 Otm->otmSize = Cache->OutlineRequiredSize;
1857
1858 FillTM(&Otm->otmTextMetrics, FontGDI, pOS2, pHori, !Error ? &Win : 0);
1859
1860 Otm->otmFiller = 0;
1861 RtlCopyMemory(&Otm->otmPanoseNumber, pOS2->panose, PANOSE_COUNT);
1862 Otm->otmfsSelection = pOS2->fsSelection;
1863 Otm->otmfsType = pOS2->fsType;
1864 Otm->otmsCharSlopeRise = pHori->caret_Slope_Rise;
1865 Otm->otmsCharSlopeRun = pHori->caret_Slope_Run;
1866 Otm->otmItalicAngle = 0; /* POST table */
1867 Otm->otmEMSquare = Face->units_per_EM;
1868 Otm->otmAscent = (FT_MulFix(pOS2->sTypoAscender, YScale) + 32) >> 6;
1869 Otm->otmDescent = (FT_MulFix(pOS2->sTypoDescender, YScale) + 32) >> 6;
1870 Otm->otmLineGap = (FT_MulFix(pOS2->sTypoLineGap, YScale) + 32) >> 6;
1871 Otm->otmsCapEmHeight = (FT_MulFix(pOS2->sCapHeight, YScale) + 32) >> 6;
1872 Otm->otmsXHeight = (FT_MulFix(pOS2->sxHeight, YScale) + 32) >> 6;
1873 Otm->otmrcFontBox.left = (FT_MulFix(Face->bbox.xMin, XScale) + 32) >> 6;
1874 Otm->otmrcFontBox.right = (FT_MulFix(Face->bbox.xMax, XScale) + 32) >> 6;
1875 Otm->otmrcFontBox.top = (FT_MulFix(Face->bbox.yMax, YScale) + 32) >> 6;
1876 Otm->otmrcFontBox.bottom = (FT_MulFix(Face->bbox.yMin, YScale) + 32) >> 6;
1877 Otm->otmMacAscent = Otm->otmTextMetrics.tmAscent;
1878 Otm->otmMacDescent = -Otm->otmTextMetrics.tmDescent;
1879 Otm->otmMacLineGap = Otm->otmLineGap;
1880 Otm->otmusMinimumPPEM = 0; /* TT Header */
1881 Otm->otmptSubscriptSize.x = (FT_MulFix(pOS2->ySubscriptXSize, XScale) + 32) >> 6;
1882 Otm->otmptSubscriptSize.y = (FT_MulFix(pOS2->ySubscriptYSize, YScale) + 32) >> 6;
1883 Otm->otmptSubscriptOffset.x = (FT_MulFix(pOS2->ySubscriptXOffset, XScale) + 32) >> 6;
1884 Otm->otmptSubscriptOffset.y = (FT_MulFix(pOS2->ySubscriptYOffset, YScale) + 32) >> 6;
1885 Otm->otmptSuperscriptSize.x = (FT_MulFix(pOS2->ySuperscriptXSize, XScale) + 32) >> 6;
1886 Otm->otmptSuperscriptSize.y = (FT_MulFix(pOS2->ySuperscriptYSize, YScale) + 32) >> 6;
1887 Otm->otmptSuperscriptOffset.x = (FT_MulFix(pOS2->ySuperscriptXOffset, XScale) + 32) >> 6;
1888 Otm->otmptSuperscriptOffset.y = (FT_MulFix(pOS2->ySuperscriptYOffset, YScale) + 32) >> 6;
1889 Otm->otmsStrikeoutSize = (FT_MulFix(pOS2->yStrikeoutSize, YScale) + 32) >> 6;
1890 Otm->otmsStrikeoutPosition = (FT_MulFix(pOS2->yStrikeoutPosition, YScale) + 32) >> 6;
1891 if (!pPost)
1892 {
1893 Otm->otmsUnderscoreSize = 0;
1894 Otm->otmsUnderscorePosition = 0;
1895 }
1896 else
1897 {
1898 Otm->otmsUnderscoreSize = (FT_MulFix(pPost->underlineThickness, YScale) + 32) >> 6;
1899 Otm->otmsUnderscorePosition = (FT_MulFix(pPost->underlinePosition, YScale) + 32) >> 6;
1900 }
1901
1902 IntUnLockFreeType;
1903
1904 Cp = (char*) Otm + sizeof(OUTLINETEXTMETRICW);
1905
1906 /* family name */
1907 Otm->otmpFamilyName = (LPSTR)(Cp - (char*) Otm);
1908 wcscpy((WCHAR*) Cp, FamilyNameW.Buffer);
1909 Cp += FamilyNameW.Length + sizeof(WCHAR);
1910
1911 /* face name */
1912 Otm->otmpFaceName = (LPSTR)(Cp - (char*) Otm);
1913 wcscpy((WCHAR*) Cp, FaceNameW.Buffer);
1914 Cp += FaceNameW.Length + sizeof(WCHAR);
1915
1916 /* style name */
1917 Otm->otmpStyleName = (LPSTR)(Cp - (char*) Otm);
1918 wcscpy((WCHAR*) Cp, StyleNameW.Buffer);
1919 Cp += StyleNameW.Length + sizeof(WCHAR);
1920
1921 /* unique name (full name) */
1922 Otm->otmpFullName = (LPSTR)(Cp - (char*) Otm);
1923 wcscpy((WCHAR*) Cp, FullNameW.Buffer);
1924 Cp += FullNameW.Length + sizeof(WCHAR);
1925
1926 ASSERT(Cp - (char*)Otm == Cache->OutlineRequiredSize);
1927
1928 RtlFreeUnicodeString(&FamilyNameW);
1929 RtlFreeUnicodeString(&FaceNameW);
1930 RtlFreeUnicodeString(&StyleNameW);
1931 RtlFreeUnicodeString(&FullNameW);
1932
1933 return Cache->OutlineRequiredSize;
1934 }
1935
1936 static PFONTGDI FASTCALL
1937 FindFaceNameInList(PUNICODE_STRING FaceName, PLIST_ENTRY Head)
1938 {
1939 PLIST_ENTRY Entry;
1940 PFONT_ENTRY CurrentEntry;
1941 ANSI_STRING EntryFaceNameA;
1942 UNICODE_STRING EntryFaceNameW;
1943 FONTGDI *FontGDI;
1944 NTSTATUS status;
1945
1946 Entry = Head->Flink;
1947 while (Entry != Head)
1948 {
1949 CurrentEntry = CONTAINING_RECORD(Entry, FONT_ENTRY, ListEntry);
1950
1951 FontGDI = CurrentEntry->Font;
1952 ASSERT(FontGDI);
1953
1954 RtlInitAnsiString(&EntryFaceNameA, FontGDI->SharedFace->Face->family_name);
1955 status = RtlAnsiStringToUnicodeString(&EntryFaceNameW, &EntryFaceNameA, TRUE);
1956 if (!NT_SUCCESS(status))
1957 {
1958 break;
1959 }
1960
1961 if ((LF_FACESIZE - 1) * sizeof(WCHAR) < EntryFaceNameW.Length)
1962 {
1963 EntryFaceNameW.Length = (LF_FACESIZE - 1) * sizeof(WCHAR);
1964 EntryFaceNameW.Buffer[LF_FACESIZE - 1] = L'\0';
1965 }
1966
1967 if (RtlEqualUnicodeString(FaceName, &EntryFaceNameW, TRUE))
1968 {
1969 RtlFreeUnicodeString(&EntryFaceNameW);
1970 return FontGDI;
1971 }
1972
1973 RtlFreeUnicodeString(&EntryFaceNameW);
1974 Entry = Entry->Flink;
1975 }
1976
1977 return NULL;
1978 }
1979
1980 static PFONTGDI FASTCALL
1981 FindFaceNameInLists(PUNICODE_STRING FaceName)
1982 {
1983 PPROCESSINFO Win32Process;
1984 PFONTGDI Font;
1985
1986 /* Search the process local list.
1987 We do not have to search the 'Mem' list, since those fonts are linked in the PrivateFontListHead */
1988 Win32Process = PsGetCurrentProcessWin32Process();
1989 IntLockProcessPrivateFonts(Win32Process);
1990 Font = FindFaceNameInList(FaceName, &Win32Process->PrivateFontListHead);
1991 IntUnLockProcessPrivateFonts(Win32Process);
1992 if (NULL != Font)
1993 {
1994 return Font;
1995 }
1996
1997 /* Search the global list */
1998 IntLockGlobalFonts;
1999 Font = FindFaceNameInList(FaceName, &FontListHead);
2000 IntUnLockGlobalFonts;
2001
2002 return Font;
2003 }
2004
2005 /* See https://msdn.microsoft.com/en-us/library/bb165625(v=vs.90).aspx */
2006 static BYTE
2007 CharSetFromLangID(LANGID LangID)
2008 {
2009 /* FIXME: Add more and fix if wrong */
2010 switch (PRIMARYLANGID(LangID))
2011 {
2012 case LANG_CHINESE:
2013 switch (SUBLANGID(LangID))
2014 {
2015 case SUBLANG_CHINESE_TRADITIONAL:
2016 return CHINESEBIG5_CHARSET;
2017 case SUBLANG_CHINESE_SIMPLIFIED:
2018 default:
2019 break;
2020 }
2021 return GB2312_CHARSET;
2022
2023 case LANG_CZECH: case LANG_HUNGARIAN: case LANG_POLISH:
2024 case LANG_SLOVAK: case LANG_SLOVENIAN: case LANG_ROMANIAN:
2025 return EASTEUROPE_CHARSET;
2026
2027 case LANG_RUSSIAN: case LANG_BULGARIAN: case LANG_MACEDONIAN:
2028 case LANG_SERBIAN: case LANG_UKRAINIAN:
2029 return RUSSIAN_CHARSET;
2030
2031 case LANG_ARABIC: return ARABIC_CHARSET;
2032 case LANG_GREEK: return GREEK_CHARSET;
2033 case LANG_HEBREW: return HEBREW_CHARSET;
2034 case LANG_JAPANESE: return SHIFTJIS_CHARSET;
2035 case LANG_KOREAN: return JOHAB_CHARSET;
2036 case LANG_TURKISH: return TURKISH_CHARSET;
2037 case LANG_THAI: return THAI_CHARSET;
2038 case LANG_LATVIAN: return BALTIC_CHARSET;
2039 case LANG_VIETNAMESE: return VIETNAMESE_CHARSET;
2040
2041 case LANG_ENGLISH: case LANG_BASQUE: case LANG_CATALAN:
2042 case LANG_DANISH: case LANG_DUTCH: case LANG_FINNISH:
2043 case LANG_FRENCH: case LANG_GERMAN: case LANG_ITALIAN:
2044 case LANG_NORWEGIAN: case LANG_PORTUGUESE: case LANG_SPANISH:
2045 case LANG_SWEDISH: default:
2046 return ANSI_CHARSET;
2047 }
2048 }
2049
2050 static void
2051 SwapEndian(LPVOID pvData, DWORD Size)
2052 {
2053 BYTE b, *pb = pvData;
2054 Size /= 2;
2055 while (Size-- > 0)
2056 {
2057 b = pb[0];
2058 pb[0] = pb[1];
2059 pb[1] = b;
2060 ++pb; ++pb;
2061 }
2062 }
2063
2064 static NTSTATUS
2065 DuplicateUnicodeString(PUNICODE_STRING Source, PUNICODE_STRING Destination)
2066 {
2067 NTSTATUS Status = STATUS_NO_MEMORY;
2068 UNICODE_STRING Tmp;
2069
2070 Tmp.Buffer = ExAllocatePoolWithTag(PagedPool, Source->MaximumLength, TAG_USTR);
2071 if (Tmp.Buffer)
2072 {
2073 Tmp.MaximumLength = Source->MaximumLength;
2074 Tmp.Length = 0;
2075 RtlCopyUnicodeString(&Tmp, Source);
2076
2077 Destination->MaximumLength = Tmp.MaximumLength;
2078 Destination->Length = Tmp.Length;
2079 Destination->Buffer = Tmp.Buffer;
2080
2081 Status = STATUS_SUCCESS;
2082 }
2083
2084 return Status;
2085 }
2086
2087 static NTSTATUS
2088 IntGetFontLocalizedName(PUNICODE_STRING pNameW, PSHARED_FACE SharedFace,
2089 FT_UShort NameID, FT_UShort LangID)
2090 {
2091 FT_SfntName Name;
2092 INT i, Count, BestIndex, Score, BestScore;
2093 WCHAR Buf[LF_FULLFACESIZE];
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 (sizeof(Buf) < Name.string_len + sizeof(UNICODE_NULL))
2153 {
2154 continue; /* name too long */
2155 }
2156
2157 if (Name.language_id == LangID)
2158 {
2159 Score = 30;
2160 BestIndex = i;
2161 break; /* best match */
2162 }
2163 else if (PRIMARYLANGID(Name.language_id) == PRIMARYLANGID(LangID))
2164 {
2165 Score = 20;
2166 }
2167 else if (PRIMARYLANGID(Name.language_id) == LANG_ENGLISH)
2168 {
2169 Score = 10;
2170 }
2171 else
2172 {
2173 Score = 0;
2174 }
2175
2176 if (Score > BestScore)
2177 {
2178 BestScore = Score;
2179 BestIndex = i;
2180 }
2181 }
2182
2183 if (BestIndex >= 0)
2184 {
2185 /* store the best name */
2186 Error = (Score == 30) ? 0 : FT_Get_Sfnt_Name(Face, BestIndex, &Name);
2187 if (!Error)
2188 {
2189 /* NOTE: Name.string is not null-terminated */
2190 RtlCopyMemory(Buf, Name.string, Name.string_len);
2191 Buf[Name.string_len / sizeof(WCHAR)] = UNICODE_NULL;
2192
2193 /* Convert UTF-16 big endian to little endian */
2194 SwapEndian(Buf, Name.string_len);
2195
2196 Status = RtlCreateUnicodeString(pNameW, Buf);
2197 }
2198 }
2199
2200 if (!NT_SUCCESS(Status))
2201 {
2202 /* defaulted */
2203 if (NameID == TT_NAME_ID_FONT_SUBFAMILY)
2204 {
2205 RtlInitAnsiString(&AnsiName, Face->style_name);
2206 Status = RtlAnsiStringToUnicodeString(pNameW, &AnsiName, TRUE);
2207 }
2208 else
2209 {
2210 RtlInitAnsiString(&AnsiName, Face->family_name);
2211 Status = RtlAnsiStringToUnicodeString(pNameW, &AnsiName, TRUE);
2212 }
2213 }
2214
2215 if (NT_SUCCESS(Status))
2216 {
2217 /* make cache */
2218 if (NameID == TT_NAME_ID_FONT_FAMILY)
2219 {
2220 ASSERT_FREETYPE_LOCK_NOT_HELD();
2221 IntLockFreeType;
2222 if (!Cache->FontFamily.Buffer)
2223 DuplicateUnicodeString(pNameW, &Cache->FontFamily);
2224 IntUnLockFreeType;
2225 }
2226 else if (NameID == TT_NAME_ID_FULL_NAME)
2227 {
2228 ASSERT_FREETYPE_LOCK_NOT_HELD();
2229 IntLockFreeType;
2230 if (!Cache->FullName.Buffer)
2231 DuplicateUnicodeString(pNameW, &Cache->FullName);
2232 IntUnLockFreeType;
2233 }
2234 }
2235
2236 return Status;
2237 }
2238
2239 static void FASTCALL
2240 FontFamilyFillInfo(PFONTFAMILYINFO Info, LPCWSTR FaceName,
2241 LPCWSTR FullName, PFONTGDI FontGDI)
2242 {
2243 ANSI_STRING StyleA;
2244 UNICODE_STRING StyleW;
2245 TT_OS2 *pOS2;
2246 FONTSIGNATURE fs;
2247 CHARSETINFO CharSetInfo;
2248 unsigned i, Size;
2249 OUTLINETEXTMETRICW *Otm;
2250 LOGFONTW *Lf;
2251 TEXTMETRICW *TM;
2252 NEWTEXTMETRICW *Ntm;
2253 DWORD fs0;
2254 NTSTATUS status;
2255 PSHARED_FACE SharedFace = FontGDI->SharedFace;
2256 FT_Face Face = SharedFace->Face;
2257 UNICODE_STRING NameW;
2258
2259 RtlInitUnicodeString(&NameW, NULL);
2260 RtlZeroMemory(Info, sizeof(FONTFAMILYINFO));
2261 Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL);
2262 Otm = ExAllocatePoolWithTag(PagedPool, Size, GDITAG_TEXT);
2263 if (!Otm)
2264 {
2265 return;
2266 }
2267 Size = IntGetOutlineTextMetrics(FontGDI, Size, Otm);
2268 if (!Size)
2269 {
2270 ExFreePoolWithTag(Otm, GDITAG_TEXT);
2271 return;
2272 }
2273
2274 Lf = &Info->EnumLogFontEx.elfLogFont;
2275 TM = &Otm->otmTextMetrics;
2276
2277 Lf->lfHeight = TM->tmHeight;
2278 Lf->lfWidth = TM->tmAveCharWidth;
2279 Lf->lfWeight = TM->tmWeight;
2280 Lf->lfItalic = TM->tmItalic;
2281 Lf->lfPitchAndFamily = (TM->tmPitchAndFamily & 0xf1) + 1;
2282 Lf->lfCharSet = TM->tmCharSet;
2283 Lf->lfOutPrecision = OUT_OUTLINE_PRECIS;
2284 Lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
2285 Lf->lfQuality = PROOF_QUALITY;
2286
2287 Ntm = &Info->NewTextMetricEx.ntmTm;
2288 Ntm->tmHeight = TM->tmHeight;
2289 Ntm->tmAscent = TM->tmAscent;
2290 Ntm->tmDescent = TM->tmDescent;
2291 Ntm->tmInternalLeading = TM->tmInternalLeading;
2292 Ntm->tmExternalLeading = TM->tmExternalLeading;
2293 Ntm->tmAveCharWidth = TM->tmAveCharWidth;
2294 Ntm->tmMaxCharWidth = TM->tmMaxCharWidth;
2295 Ntm->tmWeight = TM->tmWeight;
2296 Ntm->tmOverhang = TM->tmOverhang;
2297 Ntm->tmDigitizedAspectX = TM->tmDigitizedAspectX;
2298 Ntm->tmDigitizedAspectY = TM->tmDigitizedAspectY;
2299 Ntm->tmFirstChar = TM->tmFirstChar;
2300 Ntm->tmLastChar = TM->tmLastChar;
2301 Ntm->tmDefaultChar = TM->tmDefaultChar;
2302 Ntm->tmBreakChar = TM->tmBreakChar;
2303 Ntm->tmItalic = TM->tmItalic;
2304 Ntm->tmUnderlined = TM->tmUnderlined;
2305 Ntm->tmStruckOut = TM->tmStruckOut;
2306 Ntm->tmPitchAndFamily = TM->tmPitchAndFamily;
2307 Ntm->tmCharSet = TM->tmCharSet;
2308 Ntm->ntmFlags = TM->tmItalic ? NTM_ITALIC : 0;
2309
2310 if (550 < TM->tmWeight) Ntm->ntmFlags |= NTM_BOLD;
2311
2312 if (0 == Ntm->ntmFlags) Ntm->ntmFlags = NTM_REGULAR;
2313
2314 Ntm->ntmSizeEM = Otm->otmEMSquare;
2315 Ntm->ntmCellHeight = Otm->otmEMSquare;
2316 Ntm->ntmAvgWidth = 0;
2317
2318 Info->FontType = (0 != (TM->tmPitchAndFamily & TMPF_TRUETYPE)
2319 ? TRUETYPE_FONTTYPE : 0);
2320
2321 if (0 == (TM->tmPitchAndFamily & TMPF_VECTOR))
2322 Info->FontType |= RASTER_FONTTYPE;
2323
2324
2325 /* face name */
2326 if (!FaceName)
2327 FaceName = (WCHAR*)((ULONG_PTR)Otm + (ULONG_PTR)Otm->otmpFamilyName);
2328
2329 RtlStringCbCopyW(Lf->lfFaceName, sizeof(Lf->lfFaceName), FaceName);
2330
2331 /* full name */
2332 if (!FullName)
2333 FullName = (WCHAR*)((ULONG_PTR) Otm + (ULONG_PTR)Otm->otmpFaceName);
2334
2335 RtlStringCbCopyW(Info->EnumLogFontEx.elfFullName,
2336 sizeof(Info->EnumLogFontEx.elfFullName),
2337 FullName);
2338
2339 ExFreePoolWithTag(Otm, GDITAG_TEXT);
2340
2341 RtlInitAnsiString(&StyleA, Face->style_name);
2342 StyleW.Buffer = Info->EnumLogFontEx.elfStyle;
2343 StyleW.MaximumLength = sizeof(Info->EnumLogFontEx.elfStyle);
2344 status = RtlAnsiStringToUnicodeString(&StyleW, &StyleA, FALSE);
2345 if (!NT_SUCCESS(status))
2346 {
2347 return;
2348 }
2349 Info->EnumLogFontEx.elfScript[0] = UNICODE_NULL;
2350
2351 IntLockFreeType;
2352 pOS2 = FT_Get_Sfnt_Table(Face, ft_sfnt_os2);
2353
2354 if (!pOS2)
2355 {
2356 IntUnLockFreeType;
2357 return;
2358 }
2359
2360 fs.fsCsb[0] = pOS2->ulCodePageRange1;
2361 fs.fsCsb[1] = pOS2->ulCodePageRange2;
2362 fs.fsUsb[0] = pOS2->ulUnicodeRange1;
2363 fs.fsUsb[1] = pOS2->ulUnicodeRange2;
2364 fs.fsUsb[2] = pOS2->ulUnicodeRange3;
2365 fs.fsUsb[3] = pOS2->ulUnicodeRange4;
2366
2367 if (0 == pOS2->version)
2368 {
2369 FT_UInt Dummy;
2370
2371 if (FT_Get_First_Char(Face, &Dummy) < 0x100)
2372 fs.fsCsb[0] |= FS_LATIN1;
2373 else
2374 fs.fsCsb[0] |= FS_SYMBOL;
2375 }
2376 IntUnLockFreeType;
2377
2378 if (fs.fsCsb[0] == 0)
2379 {
2380 /* Let's see if we can find any interesting cmaps */
2381 for (i = 0; i < (UINT)Face->num_charmaps; i++)
2382 {
2383 switch (Face->charmaps[i]->encoding)
2384 {
2385 case FT_ENCODING_UNICODE:
2386 case FT_ENCODING_APPLE_ROMAN:
2387 fs.fsCsb[0] |= FS_LATIN1;
2388 break;
2389 case FT_ENCODING_MS_SYMBOL:
2390 fs.fsCsb[0] |= FS_SYMBOL;
2391 break;
2392 default:
2393 break;
2394 }
2395 }
2396 }
2397
2398 for (i = 0; i < MAXTCIINDEX; i++)
2399 {
2400 fs0 = 1L << i;
2401 if (fs.fsCsb[0] & fs0)
2402 {
2403 if (!IntTranslateCharsetInfo(&fs0, &CharSetInfo, TCI_SRCFONTSIG))
2404 {
2405 CharSetInfo.ciCharset = DEFAULT_CHARSET;
2406 }
2407 if (DEFAULT_CHARSET != CharSetInfo.ciCharset)
2408 {
2409 if (ElfScripts[i])
2410 wcscpy(Info->EnumLogFontEx.elfScript, ElfScripts[i]);
2411 else
2412 {
2413 DPRINT1("Unknown elfscript for bit %u\n", i);
2414 }
2415 }
2416 }
2417 }
2418 Info->NewTextMetricEx.ntmFontSig = fs;
2419 }
2420
2421 static int FASTCALL
2422 FindFaceNameInInfo(PUNICODE_STRING FaceName, PFONTFAMILYINFO Info, DWORD InfoEntries)
2423 {
2424 DWORD i;
2425 UNICODE_STRING InfoFaceName;
2426
2427 for (i = 0; i < InfoEntries; i++)
2428 {
2429 RtlInitUnicodeString(&InfoFaceName, Info[i].EnumLogFontEx.elfLogFont.lfFaceName);
2430 if (RtlEqualUnicodeString(&InfoFaceName, FaceName, TRUE))
2431 {
2432 return i;
2433 }
2434 }
2435
2436 return -1;
2437 }
2438
2439 static BOOLEAN FASTCALL
2440 FontFamilyInclude(LPLOGFONTW LogFont, PUNICODE_STRING FaceName,
2441 PFONTFAMILYINFO Info, DWORD InfoEntries)
2442 {
2443 UNICODE_STRING LogFontFaceName;
2444
2445 RtlInitUnicodeString(&LogFontFaceName, LogFont->lfFaceName);
2446 if (0 != LogFontFaceName.Length &&
2447 !RtlEqualUnicodeString(&LogFontFaceName, FaceName, TRUE))
2448 {
2449 return FALSE;
2450 }
2451
2452 return FindFaceNameInInfo(FaceName, Info, InfoEntries) < 0;
2453 }
2454
2455 static BOOLEAN FASTCALL
2456 GetFontFamilyInfoForList(LPLOGFONTW LogFont,
2457 PFONTFAMILYINFO Info,
2458 DWORD *Count,
2459 DWORD Size,
2460 PLIST_ENTRY Head)
2461 {
2462 PLIST_ENTRY Entry;
2463 PFONT_ENTRY CurrentEntry;
2464 ANSI_STRING EntryFaceNameA;
2465 UNICODE_STRING EntryFaceNameW;
2466 FONTGDI *FontGDI;
2467 NTSTATUS status;
2468
2469 Entry = Head->Flink;
2470 while (Entry != Head)
2471 {
2472 CurrentEntry = (PFONT_ENTRY) CONTAINING_RECORD(Entry, FONT_ENTRY, ListEntry);
2473
2474 FontGDI = CurrentEntry->Font;
2475 ASSERT(FontGDI);
2476
2477 RtlInitAnsiString(&EntryFaceNameA, FontGDI->SharedFace->Face->family_name);
2478 status = RtlAnsiStringToUnicodeString(&EntryFaceNameW, &EntryFaceNameA, TRUE);
2479 if (!NT_SUCCESS(status))
2480 {
2481 return FALSE;
2482 }
2483
2484 if ((LF_FACESIZE - 1) * sizeof(WCHAR) < EntryFaceNameW.Length)
2485 {
2486 EntryFaceNameW.Length = (LF_FACESIZE - 1) * sizeof(WCHAR);
2487 EntryFaceNameW.Buffer[LF_FACESIZE - 1] = L'\0';
2488 }
2489
2490 if (FontFamilyInclude(LogFont, &EntryFaceNameW, Info, min(*Count, Size)))
2491 {
2492 if (*Count < Size)
2493 {
2494 FontFamilyFillInfo(Info + *Count, EntryFaceNameW.Buffer,
2495 NULL, FontGDI);
2496 }
2497 (*Count)++;
2498 }
2499 RtlFreeUnicodeString(&EntryFaceNameW);
2500 Entry = Entry->Flink;
2501 }
2502
2503 return TRUE;
2504 }
2505
2506 static BOOLEAN FASTCALL
2507 GetFontFamilyInfoForSubstitutes(LPLOGFONTW LogFont,
2508 PFONTFAMILYINFO Info,
2509 DWORD *pCount,
2510 DWORD MaxCount)
2511 {
2512 PLIST_ENTRY pEntry, pHead = &FontSubstListHead;
2513 PFONTSUBST_ENTRY pCurrentEntry;
2514 PUNICODE_STRING pFromW;
2515 FONTGDI *FontGDI;
2516 LOGFONTW lf = *LogFont;
2517 UNICODE_STRING NameW;
2518
2519 for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink)
2520 {
2521 pCurrentEntry = CONTAINING_RECORD(pEntry, FONTSUBST_ENTRY, ListEntry);
2522
2523 pFromW = &pCurrentEntry->FontNames[FONTSUBST_FROM];
2524 if (LogFont->lfFaceName[0] != UNICODE_NULL)
2525 {
2526 if (!FontFamilyInclude(LogFont, pFromW, Info, min(*pCount, MaxCount)))
2527 continue; /* mismatch */
2528 }
2529
2530 RtlStringCchCopyW(lf.lfFaceName, LF_FACESIZE, pFromW->Buffer);
2531 SubstituteFontRecurse(&lf);
2532
2533 RtlInitUnicodeString(&NameW, lf.lfFaceName);
2534 FontGDI = FindFaceNameInLists(&NameW);
2535 if (FontGDI == NULL)
2536 {
2537 continue; /* no real font */
2538 }
2539
2540 if (*pCount < MaxCount)
2541 {
2542 FontFamilyFillInfo(&Info[*pCount], pFromW->Buffer, NULL, FontGDI);
2543 }
2544 (*pCount)++;
2545 }
2546
2547 return TRUE;
2548 }
2549
2550 BOOL
2551 FASTCALL
2552 ftGdiGetRasterizerCaps(LPRASTERIZER_STATUS lprs)
2553 {
2554 if ( lprs )
2555 {
2556 lprs->nSize = sizeof(RASTERIZER_STATUS);
2557 lprs->wFlags = TT_AVAILABLE | TT_ENABLED;
2558 lprs->nLanguageID = gusLanguageID;
2559 return TRUE;
2560 }
2561 EngSetLastError(ERROR_INVALID_PARAMETER);
2562 return FALSE;
2563 }
2564
2565 static
2566 BOOL
2567 SameScaleMatrix(
2568 PMATRIX pmx1,
2569 PMATRIX pmx2)
2570 {
2571 return (FLOATOBJ_Equal(&pmx1->efM11, &pmx2->efM11) &&
2572 FLOATOBJ_Equal(&pmx1->efM12, &pmx2->efM12) &&
2573 FLOATOBJ_Equal(&pmx1->efM21, &pmx2->efM21) &&
2574 FLOATOBJ_Equal(&pmx1->efM22, &pmx2->efM22));
2575 }
2576
2577 FT_BitmapGlyph APIENTRY
2578 ftGdiGlyphCacheGet(
2579 FT_Face Face,
2580 INT GlyphIndex,
2581 INT Height,
2582 PMATRIX pmx)
2583 {
2584 PLIST_ENTRY CurrentEntry;
2585 PFONT_CACHE_ENTRY FontEntry;
2586
2587 ASSERT_FREETYPE_LOCK_HELD();
2588
2589 CurrentEntry = FontCacheListHead.Flink;
2590 while (CurrentEntry != &FontCacheListHead)
2591 {
2592 FontEntry = CONTAINING_RECORD(CurrentEntry, FONT_CACHE_ENTRY, ListEntry);
2593 if ((FontEntry->Face == Face) &&
2594 (FontEntry->GlyphIndex == GlyphIndex) &&
2595 (FontEntry->Height == Height) &&
2596 (SameScaleMatrix(&FontEntry->mxWorldToDevice, pmx)))
2597 break;
2598 CurrentEntry = CurrentEntry->Flink;
2599 }
2600
2601 if (CurrentEntry == &FontCacheListHead)
2602 {
2603 return NULL;
2604 }
2605
2606 RemoveEntryList(CurrentEntry);
2607 InsertHeadList(&FontCacheListHead, CurrentEntry);
2608 return FontEntry->BitmapGlyph;
2609 }
2610
2611 /* no cache */
2612 FT_BitmapGlyph APIENTRY
2613 ftGdiGlyphSet(
2614 FT_Face Face,
2615 FT_GlyphSlot GlyphSlot,
2616 FT_Render_Mode RenderMode)
2617 {
2618 FT_Glyph Glyph;
2619 INT error;
2620 FT_Bitmap AlignedBitmap;
2621 FT_BitmapGlyph BitmapGlyph;
2622
2623 error = FT_Get_Glyph(GlyphSlot, &Glyph);
2624 if (error)
2625 {
2626 DPRINT1("Failure getting glyph.\n");
2627 return NULL;
2628 }
2629
2630 error = FT_Glyph_To_Bitmap(&Glyph, RenderMode, 0, 1);
2631 if (error)
2632 {
2633 FT_Done_Glyph(Glyph);
2634 DPRINT1("Failure rendering glyph.\n");
2635 return NULL;
2636 }
2637
2638 BitmapGlyph = (FT_BitmapGlyph)Glyph;
2639 FT_Bitmap_New(&AlignedBitmap);
2640 if (FT_Bitmap_Convert(GlyphSlot->library, &BitmapGlyph->bitmap, &AlignedBitmap, 4))
2641 {
2642 DPRINT1("Conversion failed\n");
2643 FT_Done_Glyph((FT_Glyph)BitmapGlyph);
2644 return NULL;
2645 }
2646
2647 FT_Bitmap_Done(GlyphSlot->library, &BitmapGlyph->bitmap);
2648 BitmapGlyph->bitmap = AlignedBitmap;
2649
2650 return BitmapGlyph;
2651 }
2652
2653 FT_BitmapGlyph APIENTRY
2654 ftGdiGlyphCacheSet(
2655 FT_Face Face,
2656 INT GlyphIndex,
2657 INT Height,
2658 PMATRIX pmx,
2659 FT_GlyphSlot GlyphSlot,
2660 FT_Render_Mode RenderMode)
2661 {
2662 FT_Glyph GlyphCopy;
2663 INT error;
2664 PFONT_CACHE_ENTRY NewEntry;
2665 FT_Bitmap AlignedBitmap;
2666 FT_BitmapGlyph BitmapGlyph;
2667
2668 ASSERT_FREETYPE_LOCK_HELD();
2669
2670 error = FT_Get_Glyph(GlyphSlot, &GlyphCopy);
2671 if (error)
2672 {
2673 DPRINT1("Failure caching glyph.\n");
2674 return NULL;
2675 };
2676
2677 error = FT_Glyph_To_Bitmap(&GlyphCopy, RenderMode, 0, 1);
2678 if (error)
2679 {
2680 FT_Done_Glyph(GlyphCopy);
2681 DPRINT1("Failure rendering glyph.\n");
2682 return NULL;
2683 };
2684
2685 NewEntry = ExAllocatePoolWithTag(PagedPool, sizeof(FONT_CACHE_ENTRY), TAG_FONT);
2686 if (!NewEntry)
2687 {
2688 DPRINT1("Alloc failure caching glyph.\n");
2689 FT_Done_Glyph(GlyphCopy);
2690 return NULL;
2691 }
2692
2693 BitmapGlyph = (FT_BitmapGlyph)GlyphCopy;
2694 FT_Bitmap_New(&AlignedBitmap);
2695 if(FT_Bitmap_Convert(GlyphSlot->library, &BitmapGlyph->bitmap, &AlignedBitmap, 4))
2696 {
2697 DPRINT1("Conversion failed\n");
2698 ExFreePoolWithTag(NewEntry, TAG_FONT);
2699 FT_Bitmap_Done(GlyphSlot->library, &AlignedBitmap);
2700 FT_Done_Glyph((FT_Glyph)BitmapGlyph);
2701 return NULL;
2702 }
2703
2704 FT_Bitmap_Done(GlyphSlot->library, &BitmapGlyph->bitmap);
2705 BitmapGlyph->bitmap = AlignedBitmap;
2706
2707 NewEntry->GlyphIndex = GlyphIndex;
2708 NewEntry->Face = Face;
2709 NewEntry->BitmapGlyph = BitmapGlyph;
2710 NewEntry->Height = Height;
2711 NewEntry->mxWorldToDevice = *pmx;
2712
2713 InsertHeadList(&FontCacheListHead, &NewEntry->ListEntry);
2714 if (++FontCacheNumEntries > MAX_FONT_CACHE)
2715 {
2716 NewEntry = CONTAINING_RECORD(FontCacheListHead.Blink, FONT_CACHE_ENTRY, ListEntry);
2717 RemoveCachedEntry(NewEntry);
2718 }
2719
2720 return BitmapGlyph;
2721 }
2722
2723
2724 static void FTVectorToPOINTFX(FT_Vector *vec, POINTFX *pt)
2725 {
2726 pt->x.value = vec->x >> 6;
2727 pt->x.fract = (vec->x & 0x3f) << 10;
2728 pt->x.fract |= ((pt->x.fract >> 6) | (pt->x.fract >> 12));
2729 pt->y.value = vec->y >> 6;
2730 pt->y.fract = (vec->y & 0x3f) << 10;
2731 pt->y.fract |= ((pt->y.fract >> 6) | (pt->y.fract >> 12));
2732 }
2733
2734 /*
2735 This function builds an FT_Fixed from a float. It puts the integer part
2736 in the highest 16 bits and the decimal part in the lowest 16 bits of the FT_Fixed.
2737 It fails if the integer part of the float number is greater than SHORT_MAX.
2738 */
2739 static __inline FT_Fixed FT_FixedFromFloat(float f)
2740 {
2741 short value = f;
2742 unsigned short fract = (f - value) * 0xFFFF;
2743 return (FT_Fixed)((long)value << 16 | (unsigned long)fract);
2744 }
2745
2746 /*
2747 This function builds an FT_Fixed from a FIXED. It simply put f.value
2748 in the highest 16 bits and f.fract in the lowest 16 bits of the FT_Fixed.
2749 */
2750 static __inline FT_Fixed FT_FixedFromFIXED(FIXED f)
2751 {
2752 return (FT_Fixed)((long)f.value << 16 | (unsigned long)f.fract);
2753 }
2754
2755 static unsigned int get_native_glyph_outline(FT_Outline *outline, unsigned int buflen, char *buf)
2756 {
2757 TTPOLYGONHEADER *pph;
2758 TTPOLYCURVE *ppc;
2759 int needed = 0, point = 0, contour, first_pt;
2760 unsigned int pph_start, cpfx;
2761 DWORD type;
2762
2763 for (contour = 0; contour < outline->n_contours; contour++)
2764 {
2765 /* Ignore contours containing one point */
2766 if (point == outline->contours[contour])
2767 {
2768 point++;
2769 continue;
2770 }
2771
2772 pph_start = needed;
2773 pph = (TTPOLYGONHEADER *)(buf + needed);
2774 first_pt = point;
2775 if (buf)
2776 {
2777 pph->dwType = TT_POLYGON_TYPE;
2778 FTVectorToPOINTFX(&outline->points[point], &pph->pfxStart);
2779 }
2780 needed += sizeof(*pph);
2781 point++;
2782 while (point <= outline->contours[contour])
2783 {
2784 ppc = (TTPOLYCURVE *)(buf + needed);
2785 type = outline->tags[point] & FT_Curve_Tag_On ?
2786 TT_PRIM_LINE : TT_PRIM_QSPLINE;
2787 cpfx = 0;
2788 do
2789 {
2790 if (buf)
2791 FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
2792 cpfx++;
2793 point++;
2794 } while (point <= outline->contours[contour] &&
2795 (outline->tags[point] & FT_Curve_Tag_On) ==
2796 (outline->tags[point-1] & FT_Curve_Tag_On));
2797 /* At the end of a contour Windows adds the start point, but
2798 only for Beziers */
2799 if (point > outline->contours[contour] &&
2800 !(outline->tags[point-1] & FT_Curve_Tag_On))
2801 {
2802 if (buf)
2803 FTVectorToPOINTFX(&outline->points[first_pt], &ppc->apfx[cpfx]);
2804 cpfx++;
2805 }
2806 else if (point <= outline->contours[contour] &&
2807 outline->tags[point] & FT_Curve_Tag_On)
2808 {
2809 /* add closing pt for bezier */
2810 if (buf)
2811 FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
2812 cpfx++;
2813 point++;
2814 }
2815 if (buf)
2816 {
2817 ppc->wType = type;
2818 ppc->cpfx = cpfx;
2819 }
2820 needed += sizeof(*ppc) + (cpfx - 1) * sizeof(POINTFX);
2821 }
2822 if (buf)
2823 pph->cb = needed - pph_start;
2824 }
2825 return needed;
2826 }
2827
2828 static unsigned int get_bezier_glyph_outline(FT_Outline *outline, unsigned int buflen, char *buf)
2829 {
2830 /* Convert the quadratic Beziers to cubic Beziers.
2831 The parametric eqn for a cubic Bezier is, from PLRM:
2832 r(t) = at^3 + bt^2 + ct + r0
2833 with the control points:
2834 r1 = r0 + c/3
2835 r2 = r1 + (c + b)/3
2836 r3 = r0 + c + b + a
2837
2838 A quadratic Bezier has the form:
2839 p(t) = (1-t)^2 p0 + 2(1-t)t p1 + t^2 p2
2840
2841 So equating powers of t leads to:
2842 r1 = 2/3 p1 + 1/3 p0
2843 r2 = 2/3 p1 + 1/3 p2
2844 and of course r0 = p0, r3 = p2
2845 */
2846 int contour, point = 0, first_pt;
2847 TTPOLYGONHEADER *pph;
2848 TTPOLYCURVE *ppc;
2849 DWORD pph_start, cpfx, type;
2850 FT_Vector cubic_control[4];
2851 unsigned int needed = 0;
2852
2853 for (contour = 0; contour < outline->n_contours; contour++)
2854 {
2855 pph_start = needed;
2856 pph = (TTPOLYGONHEADER *)(buf + needed);
2857 first_pt = point;
2858 if (buf)
2859 {
2860 pph->dwType = TT_POLYGON_TYPE;
2861 FTVectorToPOINTFX(&outline->points[point], &pph->pfxStart);
2862 }
2863 needed += sizeof(*pph);
2864 point++;
2865 while (point <= outline->contours[contour])
2866 {
2867 ppc = (TTPOLYCURVE *)(buf + needed);
2868 type = outline->tags[point] & FT_Curve_Tag_On ?
2869 TT_PRIM_LINE : TT_PRIM_CSPLINE;
2870 cpfx = 0;
2871 do
2872 {
2873 if (type == TT_PRIM_LINE)
2874 {
2875 if (buf)
2876 FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
2877 cpfx++;
2878 point++;
2879 }
2880 else
2881 {
2882 /* Unlike QSPLINEs, CSPLINEs always have their endpoint
2883 so cpfx = 3n */
2884
2885 /* FIXME: Possible optimization in endpoint calculation
2886 if there are two consecutive curves */
2887 cubic_control[0] = outline->points[point-1];
2888 if (!(outline->tags[point-1] & FT_Curve_Tag_On))
2889 {
2890 cubic_control[0].x += outline->points[point].x + 1;
2891 cubic_control[0].y += outline->points[point].y + 1;
2892 cubic_control[0].x >>= 1;
2893 cubic_control[0].y >>= 1;
2894 }
2895 if (point+1 > outline->contours[contour])
2896 cubic_control[3] = outline->points[first_pt];
2897 else
2898 {
2899 cubic_control[3] = outline->points[point+1];
2900 if (!(outline->tags[point+1] & FT_Curve_Tag_On))
2901 {
2902 cubic_control[3].x += outline->points[point].x + 1;
2903 cubic_control[3].y += outline->points[point].y + 1;
2904 cubic_control[3].x >>= 1;
2905 cubic_control[3].y >>= 1;
2906 }
2907 }
2908 /* r1 = 1/3 p0 + 2/3 p1
2909 r2 = 1/3 p2 + 2/3 p1 */
2910 cubic_control[1].x = (2 * outline->points[point].x + 1) / 3;
2911 cubic_control[1].y = (2 * outline->points[point].y + 1) / 3;
2912 cubic_control[2] = cubic_control[1];
2913 cubic_control[1].x += (cubic_control[0].x + 1) / 3;
2914 cubic_control[1].y += (cubic_control[0].y + 1) / 3;
2915 cubic_control[2].x += (cubic_control[3].x + 1) / 3;
2916 cubic_control[2].y += (cubic_control[3].y + 1) / 3;
2917 if (buf)
2918 {
2919 FTVectorToPOINTFX(&cubic_control[1], &ppc->apfx[cpfx]);
2920 FTVectorToPOINTFX(&cubic_control[2], &ppc->apfx[cpfx+1]);
2921 FTVectorToPOINTFX(&cubic_control[3], &ppc->apfx[cpfx+2]);
2922 }
2923 cpfx += 3;
2924 point++;
2925 }
2926 } while (point <= outline->contours[contour] &&
2927 (outline->tags[point] & FT_Curve_Tag_On) ==
2928 (outline->tags[point-1] & FT_Curve_Tag_On));
2929 /* At the end of a contour Windows adds the start point,
2930 but only for Beziers and we've already done that.
2931 */
2932 if (point <= outline->contours[contour] &&
2933 outline->tags[point] & FT_Curve_Tag_On)
2934 {
2935 /* This is the closing pt of a bezier, but we've already
2936 added it, so just inc point and carry on */
2937 point++;
2938 }
2939 if (buf)
2940 {
2941 ppc->wType = type;
2942 ppc->cpfx = cpfx;
2943 }
2944 needed += sizeof(*ppc) + (cpfx - 1) * sizeof(POINTFX);
2945 }
2946 if (buf)
2947 pph->cb = needed - pph_start;
2948 }
2949 return needed;
2950 }
2951
2952 static INT
2953 IntRequestFontSize(PDC dc, FT_Face face, LONG Width, LONG Height)
2954 {
2955 FT_Size_RequestRec req;
2956
2957 if (Width < 0)
2958 Width = -Width;
2959
2960 if (Height < 0)
2961 {
2962 Height = -Height;
2963 }
2964 if (Height == 0)
2965 {
2966 Height = dc->ppdev->devinfo.lfDefaultFont.lfHeight;
2967 }
2968 if (Height == 0)
2969 {
2970 Height = Width;
2971 }
2972
2973 if (Height < 1)
2974 Height = 1;
2975
2976 if (Width > 0xFFFFU)
2977 Width = 0xFFFFU;
2978 if (Height > 0xFFFFU)
2979 Height = 0xFFFFU;
2980
2981 req.type = FT_SIZE_REQUEST_TYPE_NOMINAL;
2982 req.width = (FT_Long)(Width << 6);
2983 req.height = (FT_Long)(Height << 6);
2984 req.horiResolution = 0;
2985 req.vertResolution = 0;
2986 return FT_Request_Size(face, &req);
2987 }
2988
2989 BOOL
2990 FASTCALL
2991 TextIntUpdateSize(PDC dc,
2992 PTEXTOBJ TextObj,
2993 PFONTGDI FontGDI,
2994 BOOL bDoLock)
2995 {
2996 FT_Face face;
2997 INT error, n;
2998 FT_CharMap charmap, found;
2999 LOGFONTW *plf;
3000
3001 if (bDoLock)
3002 IntLockFreeType;
3003
3004 face = FontGDI->SharedFace->Face;
3005 if (face->charmap == NULL)
3006 {
3007 DPRINT("WARNING: No charmap selected!\n");
3008 DPRINT("This font face has %d charmaps\n", face->num_charmaps);
3009
3010 found = NULL;
3011 for (n = 0; n < face->num_charmaps; n++)
3012 {
3013 charmap = face->charmaps[n];
3014 DPRINT("Found charmap encoding: %i\n", charmap->encoding);
3015 if (charmap->encoding != 0)
3016 {
3017 found = charmap;
3018 break;
3019 }
3020 }
3021 if (!found)
3022 {
3023 DPRINT1("WARNING: Could not find desired charmap!\n");
3024 }
3025 else
3026 {
3027 error = FT_Set_Charmap(face, found);
3028 if (error)
3029 {
3030 DPRINT1("WARNING: Could not set the charmap!\n");
3031 }
3032 }
3033 }
3034
3035 plf = &TextObj->logfont.elfEnumLogfontEx.elfLogFont;
3036
3037 error = IntRequestFontSize(dc, face, plf->lfWidth, plf->lfHeight);
3038
3039 if (bDoLock)
3040 IntUnLockFreeType;
3041
3042 if (error)
3043 {
3044 DPRINT1("Error in setting pixel sizes: %d\n", error);
3045 return FALSE;
3046 }
3047
3048 return TRUE;
3049 }
3050
3051
3052 /*
3053 * Based on WineEngGetGlyphOutline
3054 *
3055 */
3056 ULONG
3057 FASTCALL
3058 ftGdiGetGlyphOutline(
3059 PDC dc,
3060 WCHAR wch,
3061 UINT iFormat,
3062 LPGLYPHMETRICS pgm,
3063 ULONG cjBuf,
3064 PVOID pvBuf,
3065 LPMAT2 pmat2,
3066 BOOL bIgnoreRotation)
3067 {
3068 static const FT_Matrix identityMat = {(1 << 16), 0, 0, (1 << 16)};
3069 PDC_ATTR pdcattr;
3070 PTEXTOBJ TextObj;
3071 PFONTGDI FontGDI;
3072 HFONT hFont = 0;
3073 GLYPHMETRICS gm;
3074 ULONG Size;
3075 FT_Face ft_face;
3076 FT_UInt glyph_index;
3077 DWORD width, height, pitch, needed = 0;
3078 FT_Bitmap ft_bitmap;
3079 FT_Error error;
3080 INT left, right, top = 0, bottom = 0;
3081 FT_Angle angle = 0;
3082 FT_Int load_flags = FT_LOAD_DEFAULT | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
3083 FLOAT eM11, widthRatio = 1.0;
3084 FT_Matrix transMat = identityMat;
3085 BOOL needsTransform = FALSE;
3086 INT orientation;
3087 LONG aveWidth;
3088 INT adv, lsb, bbx; /* These three hold to widths of the unrotated chars */
3089 OUTLINETEXTMETRICW *potm;
3090 XFORM xForm;
3091 LOGFONTW *plf;
3092
3093 DPRINT("%u, %08x, %p, %08lx, %p, %p\n", wch, iFormat, pgm,
3094 cjBuf, pvBuf, pmat2);
3095
3096 pdcattr = dc->pdcattr;
3097
3098 MatrixS2XForm(&xForm, &dc->pdcattr->mxWorldToDevice);
3099 eM11 = xForm.eM11;
3100
3101 hFont = pdcattr->hlfntNew;
3102 TextObj = RealizeFontInit(hFont);
3103
3104 if (!TextObj)
3105 {
3106 EngSetLastError(ERROR_INVALID_HANDLE);
3107 return GDI_ERROR;
3108 }
3109 FontGDI = ObjToGDI(TextObj->Font, FONT);
3110 ft_face = FontGDI->SharedFace->Face;
3111
3112 plf = &TextObj->logfont.elfEnumLogfontEx.elfLogFont;
3113 aveWidth = FT_IS_SCALABLE(ft_face) ? abs(plf->lfWidth) : 0;
3114 orientation = FT_IS_SCALABLE(ft_face) ? plf->lfOrientation : 0;
3115
3116 Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL);
3117 potm = ExAllocatePoolWithTag(PagedPool, Size, GDITAG_TEXT);
3118 if (!potm)
3119 {
3120 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
3121 TEXTOBJ_UnlockText(TextObj);
3122 return GDI_ERROR;
3123 }
3124 Size = IntGetOutlineTextMetrics(FontGDI, Size, potm);
3125 if (!Size)
3126 {
3127 /* FIXME: last error? */
3128 ExFreePoolWithTag(potm, GDITAG_TEXT);
3129 TEXTOBJ_UnlockText(TextObj);
3130 return GDI_ERROR;
3131 }
3132
3133 IntLockFreeType;
3134 TextIntUpdateSize(dc, TextObj, FontGDI, FALSE);
3135 FtSetCoordinateTransform(ft_face, DC_pmxWorldToDevice(dc));
3136
3137 TEXTOBJ_UnlockText(TextObj);
3138
3139 if (iFormat & GGO_GLYPH_INDEX)
3140 {
3141 glyph_index = wch;
3142 iFormat &= ~GGO_GLYPH_INDEX;
3143 }
3144 else glyph_index = FT_Get_Char_Index(ft_face, wch);
3145
3146 if (orientation || (iFormat != GGO_METRICS && iFormat != GGO_BITMAP) || aveWidth || pmat2)
3147 load_flags |= FT_LOAD_NO_BITMAP;
3148
3149 if (iFormat & GGO_UNHINTED)
3150 {
3151 load_flags |= FT_LOAD_NO_HINTING;
3152 iFormat &= ~GGO_UNHINTED;
3153 }
3154
3155 error = FT_Load_Glyph(ft_face, glyph_index, load_flags);
3156 if (error)
3157 {
3158 DPRINT1("WARNING: Failed to load and render glyph! [index: %u]\n", glyph_index);
3159 IntUnLockFreeType;
3160 if (potm) ExFreePoolWithTag(potm, GDITAG_TEXT);
3161 return GDI_ERROR;
3162 }
3163 IntUnLockFreeType;
3164
3165 if (aveWidth && potm)
3166 {
3167 widthRatio = (FLOAT)aveWidth * eM11 /
3168 (FLOAT) potm->otmTextMetrics.tmAveCharWidth;
3169 }
3170
3171 left = (INT)(ft_face->glyph->metrics.horiBearingX * widthRatio) & -64;
3172 right = (INT)((ft_face->glyph->metrics.horiBearingX +
3173 ft_face->glyph->metrics.width) * widthRatio + 63) & -64;
3174
3175 adv = (INT)((ft_face->glyph->metrics.horiAdvance * widthRatio) + 63) >> 6;
3176 lsb = left >> 6;
3177 bbx = (right - left) >> 6;
3178
3179 DPRINT("Advance = %d, lsb = %d, bbx = %d\n",adv, lsb, bbx);
3180
3181 IntLockFreeType;
3182
3183 /* Scaling transform */
3184 /*if (aveWidth)*/
3185 {
3186
3187 FT_Matrix ftmatrix;
3188 FLOATOBJ efTemp;
3189
3190 PMATRIX pmx = DC_pmxWorldToDevice(dc);
3191
3192 /* Create a freetype matrix, by converting to 16.16 fixpoint format */
3193 efTemp = pmx->efM11;
3194 FLOATOBJ_MulLong(&efTemp, 0x00010000);
3195 ftmatrix.xx = FLOATOBJ_GetLong(&efTemp);
3196
3197 efTemp = pmx->efM12;
3198 FLOATOBJ_MulLong(&efTemp, 0x00010000);
3199 ftmatrix.xy = FLOATOBJ_GetLong(&efTemp);
3200
3201 efTemp = pmx->efM21;
3202 FLOATOBJ_MulLong(&efTemp, 0x00010000);
3203 ftmatrix.yx = FLOATOBJ_GetLong(&efTemp);
3204
3205 efTemp = pmx->efM22;
3206 FLOATOBJ_MulLong(&efTemp, 0x00010000);
3207 ftmatrix.yy = FLOATOBJ_GetLong(&efTemp);
3208
3209 FT_Matrix_Multiply(&ftmatrix, &transMat);
3210 needsTransform = TRUE;
3211 }
3212
3213 /* Rotation transform */
3214 if (orientation)
3215 {
3216 FT_Matrix rotationMat;
3217 FT_Vector vecAngle;
3218 DPRINT("Rotation Trans!\n");
3219 angle = FT_FixedFromFloat((float)orientation / 10.0);
3220 FT_Vector_Unit(&vecAngle, angle);
3221 rotationMat.xx = vecAngle.x;
3222 rotationMat.xy = -vecAngle.y;
3223 rotationMat.yx = -rotationMat.xy;
3224 rotationMat.yy = rotationMat.xx;
3225 FT_Matrix_Multiply(&rotationMat, &transMat);
3226 needsTransform = TRUE;
3227 }
3228
3229 /* Extra transformation specified by caller */
3230 if (pmat2)
3231 {
3232 FT_Matrix extraMat;
3233 DPRINT("MAT2 Matrix Trans!\n");
3234 extraMat.xx = FT_FixedFromFIXED(pmat2->eM11);
3235 extraMat.xy = FT_FixedFromFIXED(pmat2->eM21);
3236 extraMat.yx = FT_FixedFromFIXED(pmat2->eM12);
3237 extraMat.yy = FT_FixedFromFIXED(pmat2->eM22);
3238 FT_Matrix_Multiply(&extraMat, &transMat);
3239 needsTransform = TRUE;
3240 }
3241
3242 if (potm) ExFreePoolWithTag(potm, GDITAG_TEXT); /* It looks like we are finished with potm ATM. */
3243
3244 if (!needsTransform)
3245 {
3246 DPRINT("No Need to be Transformed!\n");
3247 top = (ft_face->glyph->metrics.horiBearingY + 63) & -64;
3248 bottom = (ft_face->glyph->metrics.horiBearingY -
3249 ft_face->glyph->metrics.height) & -64;
3250 gm.gmCellIncX = adv;
3251 gm.gmCellIncY = 0;
3252 }
3253 else
3254 {
3255 INT xc, yc;
3256 FT_Vector vec;
3257 for (xc = 0; xc < 2; xc++)
3258 {
3259 for (yc = 0; yc < 2; yc++)
3260 {
3261 vec.x = (ft_face->glyph->metrics.horiBearingX +
3262 xc * ft_face->glyph->metrics.width);
3263 vec.y = ft_face->glyph->metrics.horiBearingY -
3264 yc * ft_face->glyph->metrics.height;
3265 DPRINT("Vec %ld,%ld\n", vec.x, vec.y);
3266 FT_Vector_Transform(&vec, &transMat);
3267 if (xc == 0 && yc == 0)
3268 {
3269 left = right = vec.x;
3270 top = bottom = vec.y;
3271 }
3272 else
3273 {
3274 if (vec.x < left) left = vec.x;
3275 else if (vec.x > right) right = vec.x;
3276 if (vec.y < bottom) bottom = vec.y;
3277 else if (vec.y > top) top = vec.y;
3278 }
3279 }
3280 }
3281 left = left & -64;
3282 right = (right + 63) & -64;
3283 bottom = bottom & -64;
3284 top = (top + 63) & -64;
3285
3286 DPRINT("Transformed box: (%d,%d - %d,%d)\n", left, top, right, bottom);
3287 vec.x = ft_face->glyph->metrics.horiAdvance;
3288 vec.y = 0;
3289 FT_Vector_Transform(&vec, &transMat);
3290 gm.gmCellIncX = (vec.x+63) >> 6;
3291 gm.gmCellIncY = -((vec.y+63) >> 6);
3292 }
3293 gm.gmBlackBoxX = (right - left) >> 6;
3294 gm.gmBlackBoxY = (top - bottom) >> 6;
3295 gm.gmptGlyphOrigin.x = left >> 6;
3296 gm.gmptGlyphOrigin.y = top >> 6;
3297
3298 DPRINT("CX %d CY %d BBX %u BBY %u GOX %d GOY %d\n",
3299 gm.gmCellIncX, gm.gmCellIncY,
3300 gm.gmBlackBoxX, gm.gmBlackBoxY,
3301 gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y);
3302
3303 IntUnLockFreeType;
3304
3305
3306 if (iFormat == GGO_METRICS)
3307 {
3308 DPRINT("GGO_METRICS Exit!\n");
3309 *pgm = gm;
3310 return 1; /* FIXME */
3311 }
3312
3313 if (ft_face->glyph->format != ft_glyph_format_outline && iFormat != GGO_BITMAP)
3314 {
3315 DPRINT1("Loaded a bitmap\n");
3316 return GDI_ERROR;
3317 }
3318
3319 switch (iFormat)
3320 {
3321 case GGO_BITMAP:
3322 width = gm.gmBlackBoxX;
3323 height = gm.gmBlackBoxY;
3324 pitch = ((width + 31) >> 5) << 2;
3325 needed = pitch * height;
3326
3327 if (!pvBuf || !cjBuf) break;
3328 if (!needed) return GDI_ERROR; /* empty glyph */
3329 if (needed > cjBuf)
3330 return GDI_ERROR;
3331
3332 switch (ft_face->glyph->format)
3333 {
3334 case ft_glyph_format_bitmap:
3335 {
3336 BYTE *src = ft_face->glyph->bitmap.buffer, *dst = pvBuf;
3337 INT w = min( pitch, (ft_face->glyph->bitmap.width + 7) >> 3 );
3338 INT h = min( height, ft_face->glyph->bitmap.rows );
3339 while (h--)
3340 {
3341 RtlCopyMemory(dst, src, w);
3342 src += ft_face->glyph->bitmap.pitch;
3343 dst += pitch;
3344 }
3345 break;
3346 }
3347
3348 case ft_glyph_format_outline:
3349 ft_bitmap.width = width;
3350 ft_bitmap.rows = height;
3351 ft_bitmap.pitch = pitch;
3352 ft_bitmap.pixel_mode = FT_PIXEL_MODE_MONO;
3353 ft_bitmap.buffer = pvBuf;
3354
3355 IntLockFreeType;
3356 if (needsTransform)
3357 {
3358 FT_Outline_Transform(&ft_face->glyph->outline, &transMat);
3359 }
3360 FT_Outline_Translate(&ft_face->glyph->outline, -left, -bottom );
3361 /* Note: FreeType will only set 'black' bits for us. */
3362 RtlZeroMemory(pvBuf, needed);
3363 FT_Outline_Get_Bitmap(library, &ft_face->glyph->outline, &ft_bitmap);
3364 IntUnLockFreeType;
3365 break;
3366
3367 default:
3368 DPRINT1("Loaded glyph format %x\n", ft_face->glyph->format);
3369 return GDI_ERROR;
3370 }
3371 break;
3372
3373 case GGO_GRAY2_BITMAP:
3374 case GGO_GRAY4_BITMAP:
3375 case GGO_GRAY8_BITMAP:
3376 {
3377 unsigned int mult, row, col;
3378 BYTE *start, *ptr;
3379
3380 width = gm.gmBlackBoxX;
3381 height = gm.gmBlackBoxY;
3382 pitch = (width + 3) / 4 * 4;
3383 needed = pitch * height;
3384
3385 if (!pvBuf || !cjBuf) break;
3386 if (!needed) return GDI_ERROR; /* empty glyph */
3387 if (needed > cjBuf)
3388 return GDI_ERROR;
3389
3390 switch (ft_face->glyph->format)
3391 {
3392 case ft_glyph_format_bitmap:
3393 {
3394 BYTE *src = ft_face->glyph->bitmap.buffer, *dst = pvBuf;
3395 INT h = min( height, ft_face->glyph->bitmap.rows );
3396 INT x;
3397 while (h--)
3398 {
3399 for (x = 0; (UINT)x < pitch; x++)
3400 {
3401 if (x < ft_face->glyph->bitmap.width)
3402 dst[x] = (src[x / 8] & (1 << ( (7 - (x % 8))))) ? 0xff : 0;
3403 else
3404 dst[x] = 0;
3405 }
3406 src += ft_face->glyph->bitmap.pitch;
3407 dst += pitch;
3408 }
3409 break;
3410 }
3411 case ft_glyph_format_outline:
3412 {
3413 ft_bitmap.width = width;
3414 ft_bitmap.rows = height;
3415 ft_bitmap.pitch = pitch;
3416 ft_bitmap.pixel_mode = FT_PIXEL_MODE_GRAY;
3417 ft_bitmap.buffer = pvBuf;
3418
3419 IntLockFreeType;
3420 if (needsTransform)
3421 {
3422 FT_Outline_Transform(&ft_face->glyph->outline, &transMat);
3423 }
3424 FT_Outline_Translate(&ft_face->glyph->outline, -left, -bottom );
3425 RtlZeroMemory(ft_bitmap.buffer, cjBuf);
3426 FT_Outline_Get_Bitmap(library, &ft_face->glyph->outline, &ft_bitmap);
3427 IntUnLockFreeType;
3428
3429 if (iFormat == GGO_GRAY2_BITMAP)
3430 mult = 4;
3431 else if (iFormat == GGO_GRAY4_BITMAP)
3432 mult = 16;
3433 else if (iFormat == GGO_GRAY8_BITMAP)
3434 mult = 64;
3435 else
3436 {
3437 return GDI_ERROR;
3438 }
3439
3440 start = pvBuf;
3441 for (row = 0; row < height; row++)
3442 {
3443 ptr = start;
3444 for (col = 0; col < width; col++, ptr++)
3445 {
3446 *ptr = (((int)*ptr) * mult + 128) / 256;
3447 }
3448 start += pitch;
3449 }
3450
3451 break;
3452 }
3453 default:
3454 DPRINT1("Loaded glyph format %x\n", ft_face->glyph->format);
3455 return GDI_ERROR;
3456 }
3457 }
3458
3459 case GGO_NATIVE:
3460 {
3461 FT_Outline *outline = &ft_face->glyph->outline;
3462
3463 if (cjBuf == 0) pvBuf = NULL; /* This is okay, need cjBuf to allocate. */
3464
3465 IntLockFreeType;
3466 if (needsTransform && pvBuf) FT_Outline_Transform(outline, &transMat);
3467
3468 needed = get_native_glyph_outline(outline, cjBuf, NULL);
3469
3470 if (!pvBuf || !cjBuf)
3471 {
3472 IntUnLockFreeType;
3473 break;
3474 }
3475 if (needed > cjBuf)
3476 {
3477 IntUnLockFreeType;
3478 return GDI_ERROR;
3479 }
3480 get_native_glyph_outline(outline, cjBuf, pvBuf);
3481 IntUnLockFreeType;
3482 break;
3483 }
3484 case GGO_BEZIER:
3485 {
3486 FT_Outline *outline = &ft_face->glyph->outline;
3487 if (cjBuf == 0) pvBuf = NULL;
3488
3489 if (needsTransform && pvBuf)
3490 {
3491 IntLockFreeType;
3492 FT_Outline_Transform(outline, &transMat);
3493 IntUnLockFreeType;
3494 }
3495 needed = get_bezier_glyph_outline(outline, cjBuf, NULL);
3496
3497 if (!pvBuf || !cjBuf)
3498 break;
3499 if (needed > cjBuf)
3500 return GDI_ERROR;
3501
3502 get_bezier_glyph_outline(outline, cjBuf, pvBuf);
3503 break;
3504 }
3505
3506 default:
3507 DPRINT1("Unsupported format %u\n", iFormat);
3508 return GDI_ERROR;
3509 }
3510
3511 DPRINT("ftGdiGetGlyphOutline END and needed %lu\n", needed);
3512 *pgm = gm;
3513 return needed;
3514 }
3515
3516 BOOL
3517 FASTCALL
3518 TextIntGetTextExtentPoint(PDC dc,
3519 PTEXTOBJ TextObj,
3520 LPCWSTR String,
3521 INT Count,
3522 ULONG MaxExtent,
3523 LPINT Fit,
3524 LPINT Dx,
3525 LPSIZE Size,
3526 FLONG fl)
3527 {
3528 PFONTGDI FontGDI;
3529 FT_Face face;
3530 FT_GlyphSlot glyph;
3531 FT_BitmapGlyph realglyph;
3532 INT error, glyph_index, i, previous;
3533 ULONGLONG TotalWidth = 0;
3534 BOOL use_kerning;
3535 FT_Render_Mode RenderMode;
3536 BOOLEAN Render;
3537 PMATRIX pmxWorldToDevice;
3538 LOGFONTW *plf;
3539 BOOL EmuBold, EmuItalic;
3540
3541 FontGDI = ObjToGDI(TextObj->Font, FONT);
3542
3543 face = FontGDI->SharedFace->Face;
3544 if (NULL != Fit)
3545 {
3546 *Fit = 0;
3547 }
3548
3549 IntLockFreeType;
3550
3551 TextIntUpdateSize(dc, TextObj, FontGDI, FALSE);
3552
3553 plf = &TextObj->logfont.elfEnumLogfontEx.elfLogFont;
3554 EmuBold = (plf->lfWeight >= FW_BOLD && FontGDI->OriginalWeight <= FW_NORMAL);
3555 EmuItalic = (plf->lfItalic && !FontGDI->OriginalItalic);
3556
3557 Render = IntIsFontRenderingEnabled();
3558 if (Render)
3559 RenderMode = IntGetFontRenderMode(plf);
3560 else
3561 RenderMode = FT_RENDER_MODE_MONO;
3562
3563
3564 /* Get the DC's world-to-device transformation matrix */
3565 pmxWorldToDevice = DC_pmxWorldToDevice(dc);
3566 FtSetCoordinateTransform(face, pmxWorldToDevice);
3567
3568 use_kerning = FT_HAS_KERNING(face);
3569 previous = 0;
3570
3571 for (i = 0; i < Count; i++)
3572 {
3573 if (fl & GTEF_INDICES)
3574 glyph_index = *String;
3575 else
3576 glyph_index = FT_Get_Char_Index(face, *String);
3577
3578 if (EmuBold || EmuItalic)
3579 realglyph = NULL;
3580 else
3581 realglyph = ftGdiGlyphCacheGet(face, glyph_index,
3582 plf->lfHeight, pmxWorldToDevice);
3583
3584 if (EmuBold || EmuItalic || !realglyph)
3585 {
3586 error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
3587 if (error)
3588 {
3589 DPRINT1("WARNING: Failed to load and render glyph! [index: %d]\n", glyph_index);
3590 break;
3591 }
3592
3593 glyph = face->glyph;
3594 if (EmuBold || EmuItalic)
3595 {
3596 if (EmuBold)
3597 FT_GlyphSlot_Embolden(glyph);
3598 if (EmuItalic)
3599 FT_GlyphSlot_Oblique(glyph);
3600 realglyph = ftGdiGlyphSet(face, glyph, RenderMode);
3601 }
3602 else
3603 {
3604 realglyph = ftGdiGlyphCacheSet(face,
3605 glyph_index,
3606 plf->lfHeight,
3607 pmxWorldToDevice,
3608 glyph,
3609 RenderMode);
3610 }
3611
3612 if (!realglyph)
3613 {
3614 DPRINT1("Failed to render glyph! [index: %d]\n", glyph_index);
3615 break;
3616 }
3617 }
3618
3619 /* Retrieve kerning distance */
3620 if (use_kerning && previous && glyph_index)
3621 {
3622 FT_Vector delta;
3623 FT_Get_Kerning(face, previous, glyph_index, 0, &delta);
3624 TotalWidth += delta.x;
3625 }
3626
3627 TotalWidth += realglyph->root.advance.x >> 10;
3628
3629 if (((TotalWidth + 32) >> 6) <= MaxExtent && NULL != Fit)
3630 {