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