ReactOS 0.4.16-dev-597-gdbf7844
freetype.c
Go to the documentation of this file.
1/*
2 * PROJECT: ReactOS win32 kernel mode subsystem
3 * LICENSE: GPL - See COPYING in the top level directory
4 * PURPOSE: FreeType font engine interface
5 * PROGRAMMERS: Copyright 2001 Huw D M Davies for CodeWeavers.
6 * Copyright 2006 Dmitry Timoshkov for CodeWeavers.
7 * Copyright 2016-2024 Katayama Hirofumi MZ.
8 */
9
12#include <win32k.h>
13
14#include FT_GLYPH_H
15#include FT_TYPE1_TABLES_H
16#include FT_TRUETYPE_TABLES_H
17#include FT_TRUETYPE_TAGS_H
18#include FT_TRIGONOMETRY_H
19#include FT_BITMAP_H
20#include FT_OUTLINE_H
21#include FT_WINFONTS_H
22#include FT_SFNT_NAMES_H
23#include FT_SYNTHESIS_H
24#include FT_TRUETYPE_IDS_H
25
26#ifndef FT_INTERNAL_INTERNAL_H
27 #define FT_INTERNAL_INTERNAL_H <freetype/internal/internal.h>
28 #include FT_INTERNAL_INTERNAL_H
29#endif
30#include FT_INTERNAL_TRUETYPE_TYPES_H
31
32#include <gdi/eng/floatobj.h>
33#include "font.h"
34
35#define NDEBUG
36#include <debug.h>
37// DBG_DEFAULT_CHANNEL(GdiFont); // TODO: Re-enable when using TRACE/ERR...
38
39typedef struct _FONTLINK
40{
41 LIST_ENTRY ListEntry; //< Entry in the FONTLINK_CHAIN::FontLinkList
46
47typedef struct _FONTLINK_CHAIN
48{
49 LIST_ENTRY FontLinkList; //< List of FONTLINK's
55
56typedef struct _FONTLINK_CACHE
57{
62
63#define FONTLINK_DEFAULT_CHAR 0x30FB // U+30FB (KATAKANA MIDDLE DOT)
64
71
72#define MAX_FONTLINK_CACHE 128
73static RTL_STATIC_LIST_HEAD(g_FontLinkCache); // The list of FONTLINK_CACHE
75
76static SIZE_T
78{
79 SIZE_T ret = 0, cch;
80 const WCHAR *pch = pszz;
81 while (*pch)
82 {
83 cch = wcslen(pch) + 1;
84 ret += cch;
85 pch += cch;
86 }
87 ++ret;
88 return ret * sizeof(WCHAR);
89}
90
91static inline NTSTATUS
93{
95 HKEY hKey;
96 DWORD cbData, dwValue;
97
98 // Set the default values
100
101 // Open the registry key
103 L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\FontLink",
104 &hKey);
105 if (!NT_SUCCESS(Status))
106 return Status;
107
108 cbData = sizeof(dwValue);
109 Status = RegQueryValue(hKey, L"FontLinkDefaultChar", REG_DWORD, &dwValue, &cbData);
110 if (NT_SUCCESS(Status) && cbData == sizeof(dwValue))
111 s_chFontLinkDefaultChar = dwValue;
112
113 ZwClose(hKey); // Close the registry key
114 return STATUS_SUCCESS;
115}
116
117static inline NTSTATUS
119{
121 HKEY hKey;
122 DWORD cbData;
123 WCHAR szValue[MAX_PATH];
124
125 // Set the default values
127
128 // Open the registry key
130 L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\FontAssoc\\Associated DefaultFonts",
131 &hKey);
132 if (!NT_SUCCESS(Status))
133 return Status;
134
135 cbData = sizeof(szValue);
136 Status = RegQueryValue(hKey, L"AssocSystemFont", REG_SZ, szValue, &cbData);
137 if (NT_SUCCESS(Status))
138 {
139 szValue[_countof(szValue) - 1] = UNICODE_NULL; // Avoid buffer overrun
141 }
142
143 cbData = sizeof(szValue);
144 Status = RegQueryValue(hKey, L"FontPackage", REG_SZ, szValue, &cbData);
145 if (NT_SUCCESS(Status))
146 {
147 szValue[_countof(szValue) - 1] = UNICODE_NULL; // Avoid buffer overrun
149 }
150
151 ZwClose(hKey); // Close the registry key
152 return STATUS_SUCCESS;
153}
154
155static inline NTSTATUS
157{
159 HKEY hKey;
160 DWORD cbData;
161 WCHAR szValue[8];
162
163 // Set the default values
165
166 // Open the registry key
168 L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\FontAssoc\\Associated Charset",
169 &hKey);
170 if (!NT_SUCCESS(Status))
171 return Status;
172
173 cbData = sizeof(szValue);
174 Status = RegQueryValue(hKey, L"ANSI(00)", REG_SZ, szValue, &cbData);
175 if (NT_SUCCESS(Status))
176 {
177 szValue[_countof(szValue) - 1] = UNICODE_NULL; // Avoid buffer overrun
178 s_fFontLinkUseAnsi = !_wcsicmp(szValue, L"YES");
179 }
180
181 cbData = sizeof(szValue);
182 Status = RegQueryValue(hKey, L"OEM(FF)", REG_SZ, szValue, &cbData);
183 if (NT_SUCCESS(Status))
184 {
185 szValue[_countof(szValue) - 1] = UNICODE_NULL; // Avoid buffer overrun
186 s_fFontLinkUseOem = !_wcsicmp(szValue, L"YES");
187 }
188
189 cbData = sizeof(szValue);
190 Status = RegQueryValue(hKey, L"SYMBOL(02)", REG_SZ, szValue, &cbData);
191 if (NT_SUCCESS(Status))
192 {
193 szValue[_countof(szValue) - 1] = UNICODE_NULL; // Avoid buffer overrun
194 s_fFontLinkUseSymbol = !_wcsicmp(szValue, L"YES");
195 }
196
197 ZwClose(hKey); // Close the registry key
198 return STATUS_SUCCESS;
199}
200
201static inline VOID
203{
204 ASSERT(pLink);
206}
207
208static inline BOOL
210{
211 return pChain->LogFont.lfFaceName[0];
212}
213
214static VOID
217{
219 PFONTLINK pLink;
220
221 if (!FontLink_Chain_IsPopulated(pChain)) // The chain is not populated yet
222 return;
223
224 if (pChain->pszzFontLink)
225 ExFreePoolWithTag(pChain->pszzFontLink, TAG_FONT);
226
227 while (!IsListEmpty(&pChain->FontLinkList))
228 {
229 Entry = RemoveHeadList(&pChain->FontLinkList);
230 pLink = CONTAINING_RECORD(Entry, FONTLINK, ListEntry);
231 FontLink_Destroy(pLink);
232 }
233}
234
235static inline VOID
237 _In_ PFONTLINK_CACHE pCache)
238{
240
241 /* Add the new cache entry to the top of the cache list */
243 InsertHeadList(&g_FontLinkCache, &pCache->ListEntry);
244
245 /* If there are too many cache entries in the list, remove the oldest one at the bottom */
247 {
248 ASSERT(!IsListEmpty(&g_FontLinkCache));
249 Entry = RemoveTailList(&g_FontLinkCache);
251 pCache = CONTAINING_RECORD(Entry, FONTLINK_CACHE, ListEntry);
252 FontLink_Chain_Free(&pCache->Chain);
254 }
255}
256
257static inline VOID
259 _Inout_ PLIST_ENTRY pNewHead,
260 _Inout_ PLIST_ENTRY pOldHead)
261{
263
264 ASSERT(pNewHead != pOldHead);
265
266 InitializeListHead(pNewHead);
267 while (!IsListEmpty(pOldHead))
268 {
269 Entry = RemoveTailList(pOldHead);
270 InsertHeadList(pNewHead, Entry);
271 }
272}
273
276static inline VOID
279{
280 PFONTLINK_CACHE pCache;
281
282 if (!FontLink_Chain_IsPopulated(pChain))
283 return; // The chain is not populated yet
284
286 if (!pCache)
287 return; // Out of memory
288
289 pCache->LogFont = pChain->LogFont;
290 pCache->Chain = *pChain;
291 IntRebaseList(&pCache->Chain.FontLinkList, &pChain->FontLinkList);
292
293 FontLink_AddCache(pCache);
294}
295
296static inline PFONTLINK_CACHE
298 _In_ const LOGFONTW* pLogFont)
299{
301 PFONTLINK_CACHE pLinkCache;
302 for (Entry = g_FontLinkCache.Flink; Entry != &g_FontLinkCache; Entry = Entry->Flink)
303 {
304 pLinkCache = CONTAINING_RECORD(Entry, FONTLINK_CACHE, ListEntry);
305 if (RtlEqualMemory(&pLinkCache->LogFont, pLogFont, sizeof(LOGFONTW)))
306 return pLinkCache;
307 }
308 return NULL;
309}
310
311static inline VOID
313{
315 PFONTLINK_CACHE pLinkCache;
316
317 while (!IsListEmpty(&g_FontLinkCache))
318 {
319 Entry = RemoveHeadList(&g_FontLinkCache);
320 pLinkCache = CONTAINING_RECORD(Entry, FONTLINK_CACHE, ListEntry);
321 FontLink_Chain_Free(&pLinkCache->Chain);
322 ExFreePoolWithTag(pLinkCache, TAG_FONT);
323 }
324
326}
327
328/* The ranges of the surrogate pairs */
329#define HIGH_SURROGATE_MIN 0xD800U
330#define HIGH_SURROGATE_MAX 0xDBFFU
331#define LOW_SURROGATE_MIN 0xDC00U
332#define LOW_SURROGATE_MAX 0xDFFFU
333
334#define IS_HIGH_SURROGATE(ch0) (HIGH_SURROGATE_MIN <= (ch0) && (ch0) <= HIGH_SURROGATE_MAX)
335#define IS_LOW_SURROGATE(ch1) (LOW_SURROGATE_MIN <= (ch1) && (ch1) <= LOW_SURROGATE_MAX)
336
337static inline DWORD
339{
340 return ((ch0 - HIGH_SURROGATE_MIN) << 10) + (ch1 - LOW_SURROGATE_MIN) + 0x10000;
341}
342
343/* TPMF_FIXED_PITCH is confusing; brain-dead api */
344#ifndef _TMPF_VARIABLE_PITCH
345 #define _TMPF_VARIABLE_PITCH TMPF_FIXED_PITCH
346#endif
347
348/* Is bold emulation necessary? */
349#define EMUBOLD_NEEDED(original, request) \
350 (((request) != FW_DONTCARE) && ((request) - (original) >= FW_BOLD - FW_MEDIUM))
351
353extern const MATRIX gmxWorldToPageDefault;
354static const FT_Matrix identityMat = {(1 << 16), 0, 0, (1 << 16)};
355static POINTL PointZero = { 0, 0 };
356
357/* HACK!! Fix XFORMOBJ then use 1:16 / 16:1 */
358#define gmxWorldToDeviceDefault gmxWorldToPageDefault
359
361
362/* registry */
364 RTL_CONSTANT_STRING(L"\\REGISTRY\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts");
365
366
367/* The FreeType library is not thread safe, so we have
368 to serialize access to it */
370
371static RTL_STATIC_LIST_HEAD(g_FontListHead);
373
374#define ASSERT_FREETYPE_LOCK_HELD() \
375 ASSERT(g_FreeTypeLock->Owner == KeGetCurrentThread())
376
377#define ASSERT_FREETYPE_LOCK_NOT_HELD() \
378 ASSERT(g_FreeTypeLock->Owner != KeGetCurrentThread())
379
380#define IntLockFreeType() \
381do { \
382 ASSERT_FREETYPE_LOCK_NOT_HELD(); \
383 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(g_FreeTypeLock); \
384} while (0)
385
386#define IntUnLockFreeType() \
387do { \
388 ASSERT_FREETYPE_LOCK_HELD(); \
389 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(g_FreeTypeLock); \
390} while(0)
391
392#define MAX_FONT_CACHE 256
393
394static RTL_STATIC_LIST_HEAD(g_FontCacheListHead);
396
397static PWCHAR g_ElfScripts[32] = /* These are in the order of the fsCsb[0] bits */
398{
399 L"Western", /* 00 */
400 L"Central_European",
401 L"Cyrillic",
402 L"Greek",
403 L"Turkish",
404 L"Hebrew",
405 L"Arabic",
406 L"Baltic",
407 L"Vietnamese", /* 08 */
408 NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 15 */
409 L"Thai",
410 L"Japanese",
411 L"CHINESE_GB2312",
412 L"Hangul",
413 L"CHINESE_BIG5",
414 L"Hangul(Johab)",
415 NULL, NULL, /* 23 */
417 L"Symbol" /* 31 */
418};
419
420/*
421 * For TranslateCharsetInfo
422 */
423#define CP_SYMBOL 42
424#define MAXTCIINDEX 32
426{
427 /* ANSI */
428 { ANSI_CHARSET, 1252, {{0,0,0,0},{FS_LATIN1,0}} },
429 { EASTEUROPE_CHARSET, 1250, {{0,0,0,0},{FS_LATIN2,0}} },
430 { RUSSIAN_CHARSET, 1251, {{0,0,0,0},{FS_CYRILLIC,0}} },
431 { GREEK_CHARSET, 1253, {{0,0,0,0},{FS_GREEK,0}} },
432 { TURKISH_CHARSET, 1254, {{0,0,0,0},{FS_TURKISH,0}} },
433 { HEBREW_CHARSET, 1255, {{0,0,0,0},{FS_HEBREW,0}} },
434 { ARABIC_CHARSET, 1256, {{0,0,0,0},{FS_ARABIC,0}} },
435 { BALTIC_CHARSET, 1257, {{0,0,0,0},{FS_BALTIC,0}} },
436 { VIETNAMESE_CHARSET, 1258, {{0,0,0,0},{FS_VIETNAMESE,0}} },
437 /* reserved by ANSI */
438 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
439 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
440 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
441 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
442 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
443 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
444 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
445 /* ANSI and OEM */
446 { THAI_CHARSET, 874, {{0,0,0,0},{FS_THAI,0}} },
447 { SHIFTJIS_CHARSET, 932, {{0,0,0,0},{FS_JISJAPAN,0}} },
448 { GB2312_CHARSET, 936, {{0,0,0,0},{FS_CHINESESIMP,0}} },
449 { HANGEUL_CHARSET, 949, {{0,0,0,0},{FS_WANSUNG,0}} },
450 { CHINESEBIG5_CHARSET, 950, {{0,0,0,0},{FS_CHINESETRAD,0}} },
451 { JOHAB_CHARSET, 1361, {{0,0,0,0},{FS_JOHAB,0}} },
452 /* Reserved for alternate ANSI and OEM */
453 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
454 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
455 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
456 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
457 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
458 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
459 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
460 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
461 /* Reserved for system */
462 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
463 { SYMBOL_CHARSET, CP_SYMBOL, {{0,0,0,0},{FS_SYMBOL,0}} }
464};
465
466#ifndef CP_OEMCP
467 #define CP_OEMCP 1
468 #define CP_MACCP 2
469#endif
470
471/* Get charset from specified codepage.
472 g_FontTci is used also in TranslateCharsetInfo. */
474{
475 UINT i;
476
477 if (uCodePage == CP_OEMCP)
478 return OEM_CHARSET;
479
480 if (uCodePage == CP_MACCP)
481 return MAC_CHARSET;
482
483 for (i = 0; i < MAXTCIINDEX; ++i)
484 {
485 if (g_FontTci[i].ciACP == 0)
486 continue;
487
488 if (g_FontTci[i].ciACP == uCodePage)
489 return g_FontTci[i].ciCharset;
490 }
491
492 return DEFAULT_CHARSET;
493}
494
495static __inline VOID
496FindBestFontFromList(FONTOBJ **FontObj, ULONG *MatchPenalty,
497 const LOGFONTW *LogFont,
498 const PLIST_ENTRY Head);
499
500static BOOL
502
503static BOOL
505 _Inout_ PFONTLINK pFontLink)
506{
507 FONTOBJ *pFontObj;
508 ULONG MatchPenalty;
509 UNICODE_STRING FaceName;
510 PPROCESSINFO Win32Process;
511 PFONTGDI pFontGDI;
512
514
515 if (pFontLink->bIgnore)
516 return FALSE;
517
518 if (pFontLink->SharedFace)
519 return TRUE;
520
521 MatchPenalty = MAXULONG;
522 pFontObj = NULL;
523
524 // Search private fonts
525 Win32Process = PsGetCurrentProcessWin32Process();
526 FindBestFontFromList(&pFontObj, &MatchPenalty, &pFontLink->LogFont,
527 &Win32Process->PrivateFontListHead);
528
529 // Search system fonts
530 FindBestFontFromList(&pFontObj, &MatchPenalty, &pFontLink->LogFont,
531 &g_FontListHead);
532
533 if (!pFontObj) // Not found?
534 {
535 pFontLink->bIgnore = TRUE;
536 return FALSE;
537 }
538
539 pFontGDI = ObjToGDI(pFontObj, FONT);
540 pFontLink->SharedFace = pFontGDI->SharedFace;
541
542 // FontLink uses family name
543 RtlInitUnicodeString(&FaceName, pFontLink->LogFont.lfFaceName);
544 if (!MatchFontName(pFontLink->SharedFace, &FaceName, TT_NAME_ID_FONT_FAMILY, LANG_ENGLISH) &&
545 !MatchFontName(pFontLink->SharedFace, &FaceName, TT_NAME_ID_FONT_FAMILY, gusLanguageID))
546 {
547 pFontLink->bIgnore = TRUE;
548 return FALSE;
549 }
550
551 return TRUE;
552}
553
554/* list head */
555static RTL_STATIC_LIST_HEAD(g_FontSubstListHead);
556
557static void
559{
561
562 ++Ptr->RefCount;
563}
564
565static void
567{
568 Cache->OutlineRequiredSize = 0;
569 RtlInitUnicodeString(&Cache->FontFamily, NULL);
570 RtlInitUnicodeString(&Cache->FullName, NULL);
571}
572
573static PSHARED_FACE
575{
578 if (Ptr)
579 {
580 Ptr->Face = Face;
581 Ptr->RefCount = 1;
582 Ptr->Memory = Memory;
583 SharedFaceCache_Init(&Ptr->EnglishUS);
584 SharedFaceCache_Init(&Ptr->UserLanguage);
585
587 DPRINT("Creating SharedFace for %s\n", Face->family_name ? Face->family_name : "<NULL>");
588 }
589 return Ptr;
590}
591
592static PSHARED_MEM
594{
597 if (Ptr)
598 {
599 Ptr->Buffer = Buffer;
600 Ptr->BufferSize = BufferSize;
601 Ptr->RefCount = 1;
602 Ptr->IsMapping = IsMapping;
603 DPRINT("Creating SharedMem for %p (%i, %p)\n", Buffer, IsMapping, Ptr);
604 }
605 return Ptr;
606}
607
608static void
610{
612
613 ++Ptr->RefCount;
614}
615
616static void
618{
620
621 FT_Done_Glyph((FT_Glyph)Entry->BitmapGlyph);
622 RemoveEntryList(&Entry->ListEntry);
626}
627
628static void
630{
631 PLIST_ENTRY CurrentEntry, NextEntry;
632 PFONT_CACHE_ENTRY FontEntry;
633
635
636 for (CurrentEntry = g_FontCacheListHead.Flink;
637 CurrentEntry != &g_FontCacheListHead;
638 CurrentEntry = NextEntry)
639 {
640 FontEntry = CONTAINING_RECORD(CurrentEntry, FONT_CACHE_ENTRY, ListEntry);
641 NextEntry = CurrentEntry->Flink;
642
643 if (FontEntry->Hashed.Face == Face)
644 {
645 RemoveCachedEntry(FontEntry);
646 }
647 }
648}
649
651{
653 ASSERT(Ptr->RefCount > 0);
654
655 if (Ptr->RefCount <= 0)
656 return;
657
658 --Ptr->RefCount;
659 if (Ptr->RefCount == 0)
660 {
661 DPRINT("Releasing SharedMem for %p (%i, %p)\n", Ptr->Buffer, Ptr->IsMapping, Ptr);
662 if (Ptr->IsMapping)
664 else
667 }
668}
669
670static void
672{
673 RtlFreeUnicodeString(&Cache->FontFamily);
674 RtlFreeUnicodeString(&Cache->FullName);
675}
676
677static void
679{
681 ASSERT(Ptr->RefCount > 0);
682
683 if (Ptr->RefCount <= 0)
684 return;
685
686 --Ptr->RefCount;
687 if (Ptr->RefCount == 0)
688 {
689 DPRINT("Releasing SharedFace for %s\n", Ptr->Face->family_name ? Ptr->Face->family_name : "<NULL>");
690 RemoveCacheEntries(Ptr->Face);
691 FT_Done_Face(Ptr->Face);
692 SharedMem_Release(Ptr->Memory);
693 SharedFaceCache_Release(&Ptr->EnglishUS);
694 SharedFaceCache_Release(&Ptr->UserLanguage);
696 }
698}
699
700
701static VOID FASTCALL
703{
704 // PFONTGDI FontGDI = FontEntry->Font;
705 PSHARED_FACE SharedFace = FontGDI->SharedFace;
706
707 if (FontGDI->Filename)
709
710 if (FontEntry->StyleName.Buffer)
711 RtlFreeUnicodeString(&FontEntry->StyleName);
712
713 if (FontEntry->FaceName.Buffer)
714 RtlFreeUnicodeString(&FontEntry->FaceName);
715
716 EngFreeMem(FontGDI);
717 SharedFace_Release(SharedFace);
718 ExFreePoolWithTag(FontEntry, TAG_FONT);
719}
720
723{
724 CleanupFontEntryEx(FontEntry, FontEntry->Font);
725}
726
727
729{
730 pt->x.value = vec->x >> 6;
731 pt->x.fract = (vec->x & 0x3f) << 10;
732 pt->x.fract |= ((pt->x.fract >> 6) | (pt->x.fract >> 12));
733 pt->y.value = vec->y >> 6;
734 pt->y.fract = (vec->y & 0x3f) << 10;
735 pt->y.fract |= ((pt->y.fract >> 6) | (pt->y.fract >> 12));
736}
737
738/*
739 This function builds an FT_Fixed from a FIXED. It simply put f.value
740 in the highest 16 bits and f.fract in the lowest 16 bits of the FT_Fixed.
741*/
743{
744 return (FT_Fixed)((long)f.value << 16 | (unsigned long)f.fract);
745}
746
747
748#if DBG
749VOID DumpFontEntry(PFONT_ENTRY FontEntry)
750{
751 const char *family_name;
752 const char *style_name;
753 FT_Face Face;
754 PFONTGDI FontGDI = FontEntry->Font;
755
756 if (!FontGDI)
757 {
758 DPRINT("FontGDI NULL\n");
759 return;
760 }
761
762 Face = (FontGDI->SharedFace ? FontGDI->SharedFace->Face : NULL);
763 if (Face)
764 {
765 family_name = Face->family_name;
766 style_name = Face->style_name;
767 }
768 else
769 {
770 family_name = "<invalid>";
771 style_name = "<invalid>";
772 }
773
774 DPRINT("family_name '%s', style_name '%s', FaceName '%wZ', StyleName '%wZ', FontGDI %p, "
775 "FontObj %p, iUnique %lu, SharedFace %p, Face %p, CharSet %u, Filename '%S'\n",
777 style_name,
778 &FontEntry->FaceName,
779 &FontEntry->StyleName,
780 FontGDI,
781 &FontGDI->FontObj,
782 FontGDI->iUnique,
783 FontGDI->SharedFace,
784 Face,
785 FontGDI->CharSet,
786 FontGDI->Filename);
787}
788
789VOID DumpFontList(PLIST_ENTRY Head)
790{
792 PFONT_ENTRY CurrentEntry;
793
794 DPRINT("## DumpFontList(%p)\n", Head);
795
796 for (Entry = Head->Flink; Entry != Head; Entry = Entry->Flink)
797 {
798 CurrentEntry = CONTAINING_RECORD(Entry, FONT_ENTRY, ListEntry);
799 DumpFontEntry(CurrentEntry);
800 }
801}
802
803VOID DumpFontSubstEntry(PFONTSUBST_ENTRY pSubstEntry)
804{
805 DPRINT("%wZ,%u -> %wZ,%u\n",
806 &pSubstEntry->FontNames[FONTSUBST_FROM],
807 pSubstEntry->CharSets[FONTSUBST_FROM],
808 &pSubstEntry->FontNames[FONTSUBST_TO],
809 pSubstEntry->CharSets[FONTSUBST_TO]);
810}
811
812VOID DumpFontSubstList(VOID)
813{
814 PLIST_ENTRY pHead = &g_FontSubstListHead;
815 PLIST_ENTRY pListEntry;
816 PFONTSUBST_ENTRY pSubstEntry;
817
818 DPRINT("## DumpFontSubstList\n");
819
820 for (pListEntry = pHead->Flink;
821 pListEntry != pHead;
822 pListEntry = pListEntry->Flink)
823 {
824 pSubstEntry = CONTAINING_RECORD(pListEntry, FONTSUBST_ENTRY, ListEntry);
825 DumpFontSubstEntry(pSubstEntry);
826 }
827}
828
829VOID DumpPrivateFontList(BOOL bDoLock)
830{
832
833 if (!Win32Process)
834 return;
835
836 if (bDoLock)
837 {
839 IntLockProcessPrivateFonts(Win32Process);
840 }
841
842 DumpFontList(&Win32Process->PrivateFontListHead);
843
844 if (bDoLock)
845 {
846 IntUnLockProcessPrivateFonts(Win32Process);
848 }
849}
850
851VOID DumpGlobalFontList(BOOL bDoLock)
852{
853 if (bDoLock)
855
856 DumpFontList(&g_FontListHead);
857
858 if (bDoLock)
860}
861
862VOID DumpFontInfo(BOOL bDoLock)
863{
864 DumpGlobalFontList(bDoLock);
865 DumpPrivateFontList(bDoLock);
866 DumpFontSubstList();
867}
868#endif
869
870/*
871 * IntLoadFontSubstList --- loads the list of font substitutes
872 */
875{
879 KEY_FULL_INFORMATION KeyFullInfo;
880 ULONG i, Length;
881 UNICODE_STRING FromW, ToW;
882 BYTE InfoBuffer[128];
884 BYTE CharSets[FONTSUBST_FROM_AND_TO];
885 LPWSTR pch;
887
888 /* the FontSubstitutes registry key */
889 static UNICODE_STRING FontSubstKey =
890 RTL_CONSTANT_STRING(L"\\Registry\\Machine\\Software\\"
891 L"Microsoft\\Windows NT\\CurrentVersion\\"
892 L"FontSubstitutes");
893
894 /* open registry key */
897 NULL, NULL);
898 Status = ZwOpenKey(&KeyHandle, KEY_READ, &ObjectAttributes);
899 if (!NT_SUCCESS(Status))
900 {
901 DPRINT("ZwOpenKey failed: 0x%08X\n", Status);
902 return FALSE; /* failure */
903 }
904
905 /* query count of values */
906 Status = ZwQueryKey(KeyHandle, KeyFullInformation,
907 &KeyFullInfo, sizeof(KeyFullInfo), &Length);
908 if (!NT_SUCCESS(Status))
909 {
910 DPRINT("ZwQueryKey failed: 0x%08X\n", Status);
912 return FALSE; /* failure */
913 }
914
915 /* for each value */
916 for (i = 0; i < KeyFullInfo.Values; ++i)
917 {
918 /* get value name */
919 Status = ZwEnumerateValueKey(KeyHandle, i, KeyValueFullInformation,
920 InfoBuffer, sizeof(InfoBuffer), &Length);
921 if (!NT_SUCCESS(Status))
922 {
923 DPRINT("ZwEnumerateValueKey failed: 0x%08X\n", Status);
924 break; /* failure */
925 }
926
927 /* create FromW string */
928 pInfo = (PKEY_VALUE_FULL_INFORMATION)InfoBuffer;
929 Length = pInfo->NameLength / sizeof(WCHAR);
930 pInfo->Name[Length] = UNICODE_NULL; /* truncate */
931 if (!RtlCreateUnicodeString(&FromW, pInfo->Name))
932 {
934 DPRINT("RtlCreateUnicodeString failed\n");
935 break; /* failure */
936 }
937
938 /* query value */
939 Status = ZwQueryValueKey(KeyHandle, &FromW, KeyValueFullInformation,
940 InfoBuffer, sizeof(InfoBuffer), &Length);
941 pInfo = (PKEY_VALUE_FULL_INFORMATION)InfoBuffer;
942 if (!NT_SUCCESS(Status) || !pInfo->DataLength)
943 {
944 DPRINT("ZwQueryValueKey failed: 0x%08X\n", Status);
945 RtlFreeUnicodeString(&FromW);
946 break; /* failure */
947 }
948
949 /* create ToW string */
950 pch = (LPWSTR)((PUCHAR)pInfo + pInfo->DataOffset);
951 Length = pInfo->DataLength / sizeof(WCHAR);
952 pch[Length] = UNICODE_NULL; /* truncate */
953 if (!RtlCreateUnicodeString(&ToW, pch))
954 {
956 DPRINT("RtlCreateUnicodeString failed\n");
957 RtlFreeUnicodeString(&FromW);
958 break; /* failure */
959 }
960
961 /* does charset exist? (from) */
963 pch = wcsrchr(FromW.Buffer, L',');
964 if (pch)
965 {
966 /* truncate */
967 *pch = UNICODE_NULL;
968 FromW.Length = (pch - FromW.Buffer) * sizeof(WCHAR);
969 /* parse charset number */
970 CharSets[FONTSUBST_FROM] = (BYTE)_wtoi(pch + 1);
971 }
972
973 /* does charset exist? (to) */
974 CharSets[FONTSUBST_TO] = DEFAULT_CHARSET;
975 pch = wcsrchr(ToW.Buffer, L',');
976 if (pch)
977 {
978 /* truncate */
979 *pch = UNICODE_NULL;
980 ToW.Length = (pch - ToW.Buffer) * sizeof(WCHAR);
981 /* parse charset number */
982 CharSets[FONTSUBST_TO] = (BYTE)_wtoi(pch + 1);
983 }
984
985 /* is it identical? */
986 if (RtlEqualUnicodeString(&FromW, &ToW, TRUE) &&
987 CharSets[FONTSUBST_FROM] == CharSets[FONTSUBST_TO])
988 {
989 RtlFreeUnicodeString(&FromW);
991 continue;
992 }
993
994 /* allocate an entry */
996 if (pEntry == NULL)
997 {
998 DPRINT("ExAllocatePoolWithTag failed\n");
999 RtlFreeUnicodeString(&FromW);
1001 break; /* failure */
1002 }
1003
1004 /* store to *pEntry */
1005 pEntry->FontNames[FONTSUBST_FROM] = FromW;
1006 pEntry->FontNames[FONTSUBST_TO] = ToW;
1007 pEntry->CharSets[FONTSUBST_FROM] = CharSets[FONTSUBST_FROM];
1008 pEntry->CharSets[FONTSUBST_TO] = CharSets[FONTSUBST_TO];
1009
1010 /* insert pEntry to *pHead */
1011 InsertTailList(pHead, &pEntry->ListEntry);
1012 }
1013
1014 /* close now */
1016
1017 return NT_SUCCESS(Status);
1018}
1019
1022{
1023 ULONG ulError;
1024
1026
1028 if (g_FreeTypeLock == NULL)
1029 {
1030 return FALSE;
1031 }
1033
1035 if (ulError)
1036 {
1037 DPRINT1("FT_Init_FreeType failed with error code 0x%x\n", ulError);
1038 return FALSE;
1039 }
1040
1042 {
1043 DPRINT1("Fonts registry is empty.\n");
1044
1045 /* Load font(s) with writing registry */
1047 }
1048
1049 IntLoadFontSubstList(&g_FontSubstListHead);
1050
1051#if 0
1052 DumpFontInfo(TRUE);
1053#endif
1054
1058
1059 return TRUE;
1060}
1061
1064{
1065 PLIST_ENTRY pHead, pEntry;
1066 PFONT_CACHE_ENTRY pFontCache;
1067 PFONTSUBST_ENTRY pSubstEntry;
1068 PFONT_ENTRY pFontEntry;
1069
1070 // Cleanup the FontLink cache
1072
1073 // Free font cache list
1074 pHead = &g_FontCacheListHead;
1075 while (!IsListEmpty(pHead))
1076 {
1077 pEntry = RemoveHeadList(pHead);
1078 pFontCache = CONTAINING_RECORD(pEntry, FONT_CACHE_ENTRY, ListEntry);
1079 RemoveCachedEntry(pFontCache);
1080 }
1081
1082 // Free font subst list
1083 pHead = &g_FontSubstListHead;
1084 while (!IsListEmpty(pHead))
1085 {
1086 pEntry = RemoveHeadList(pHead);
1087 pSubstEntry = CONTAINING_RECORD(pEntry, FONTSUBST_ENTRY, ListEntry);
1088 ExFreePoolWithTag(pSubstEntry, TAG_FONT);
1089 }
1090
1091 // Free font list
1092 pHead = &g_FontListHead;
1093 while (!IsListEmpty(pHead))
1094 {
1095 pEntry = RemoveHeadList(pHead);
1096 pFontEntry = CONTAINING_RECORD(pEntry, FONT_ENTRY, ListEntry);
1097 CleanupFontEntry(pFontEntry);
1098 }
1099
1101 {
1104 }
1105
1108}
1109
1110static LONG IntNormalizeAngle(LONG nTenthsOfDegrees)
1111{
1112 nTenthsOfDegrees %= 360 * 10;
1113 if (nTenthsOfDegrees >= 0)
1114 return nTenthsOfDegrees;
1115 return nTenthsOfDegrees + 360 * 10;
1116}
1117
1118static VOID FASTCALL IntEscapeMatrix(FT_Matrix *pmat, LONG lfEscapement)
1119{
1120 FT_Vector vecAngle;
1121 /* Convert the angle in tenths of degrees into degrees as a 16.16 fixed-point value */
1122 FT_Angle angle = INT_TO_FIXED(lfEscapement) / 10;
1123 FT_Vector_Unit(&vecAngle, angle);
1124 pmat->xx = vecAngle.x;
1125 pmat->xy = -vecAngle.y;
1126 pmat->yx = -pmat->xy;
1127 pmat->yy = pmat->xx;
1128}
1129
1130static VOID FASTCALL
1132{
1133 FLOATOBJ ef;
1134
1135 /* Create a freetype matrix, by converting to 16.16 fixpoint format */
1136 ef = pmx->efM11;
1137 FLOATOBJ_MulLong(&ef, 0x00010000);
1138 pmat->xx = FLOATOBJ_GetLong(&ef);
1139
1140 ef = pmx->efM21;
1141 FLOATOBJ_MulLong(&ef, 0x00010000);
1142 pmat->xy = -FLOATOBJ_GetLong(&ef); /* (*1) See below */
1143
1144 ef = pmx->efM12;
1145 FLOATOBJ_MulLong(&ef, 0x00010000);
1146 pmat->yx = -FLOATOBJ_GetLong(&ef); /* (*1) See below */
1147
1148 ef = pmx->efM22;
1149 FLOATOBJ_MulLong(&ef, 0x00010000);
1150 pmat->yy = FLOATOBJ_GetLong(&ef);
1151
1152 // (*1): Y direction is mirrored as follows:
1153 //
1154 // [ M11 -M12 ] [ X ] [ M11*X + M12*Y ]
1155 // [ ] * [ ] == [ ]
1156 // [ -M21 M22 ] [ -Y ] [ -(M21*X + M22*Y) ].
1157}
1158
1159static BOOL
1161 PUNICODE_STRING pOutputName,
1162 PUNICODE_STRING pInputName,
1163 BYTE RequestedCharSet,
1164 BYTE CharSetMap[FONTSUBST_FROM_AND_TO])
1165{
1166 PLIST_ENTRY pListEntry;
1167 PFONTSUBST_ENTRY pSubstEntry;
1168 BYTE CharSets[FONTSUBST_FROM_AND_TO];
1169
1170 CharSetMap[FONTSUBST_FROM] = DEFAULT_CHARSET;
1171 CharSetMap[FONTSUBST_TO] = RequestedCharSet;
1172
1173 /* for each list entry */
1174 for (pListEntry = pHead->Flink;
1175 pListEntry != pHead;
1176 pListEntry = pListEntry->Flink)
1177 {
1178 pSubstEntry = CONTAINING_RECORD(pListEntry, FONTSUBST_ENTRY, ListEntry);
1179
1180 CharSets[FONTSUBST_FROM] = pSubstEntry->CharSets[FONTSUBST_FROM];
1181
1182 if (CharSets[FONTSUBST_FROM] != DEFAULT_CHARSET &&
1183 CharSets[FONTSUBST_FROM] != RequestedCharSet)
1184 {
1185 continue; /* not matched */
1186 }
1187
1188 /* does charset number exist? (to) */
1189 if (pSubstEntry->CharSets[FONTSUBST_TO] != DEFAULT_CHARSET)
1190 {
1191 CharSets[FONTSUBST_TO] = pSubstEntry->CharSets[FONTSUBST_TO];
1192 }
1193 else
1194 {
1195 CharSets[FONTSUBST_TO] = RequestedCharSet;
1196 }
1197
1198 /* does font name match? */
1200 pInputName, TRUE))
1201 {
1202 continue; /* not matched */
1203 }
1204
1205 /* update *pOutputName */
1206 *pOutputName = pSubstEntry->FontNames[FONTSUBST_TO];
1207
1208 if (CharSetMap[FONTSUBST_FROM] == DEFAULT_CHARSET)
1209 {
1210 /* update CharSetMap */
1211 CharSetMap[FONTSUBST_FROM] = CharSets[FONTSUBST_FROM];
1212 CharSetMap[FONTSUBST_TO] = CharSets[FONTSUBST_TO];
1213 }
1214 return TRUE; /* success */
1215 }
1216
1217 return FALSE;
1218}
1219
1220static VOID
1222{
1223 SIZE_T cbLength = pString->Length;
1224
1225 if (cbBuffer < sizeof(UNICODE_NULL))
1226 return;
1227
1228 if (cbLength > cbBuffer - sizeof(UNICODE_NULL))
1229 cbLength = cbBuffer - sizeof(UNICODE_NULL);
1230
1231 RtlCopyMemory(pszBuffer, pString->Buffer, cbLength);
1232 pszBuffer[cbLength / sizeof(WCHAR)] = UNICODE_NULL;
1233}
1234
1235static NTSTATUS
1237{
1239 UNICODE_STRING Tmp;
1240
1241 Tmp.Buffer = ExAllocatePoolWithTag(PagedPool, Source->MaximumLength, TAG_USTR);
1242 if (Tmp.Buffer)
1243 {
1244 Tmp.MaximumLength = Source->MaximumLength;
1245 Tmp.Length = 0;
1247
1249 Destination->Length = Tmp.Length;
1250 Destination->Buffer = Tmp.Buffer;
1251
1253 }
1254
1255 return Status;
1256}
1257
1258static BOOL
1260{
1261 UINT RecurseCount = 5;
1262 UNICODE_STRING OutputNameW = { 0 };
1263 BYTE CharSetMap[FONTSUBST_FROM_AND_TO];
1264 BOOL Found;
1265 UNICODE_STRING InputNameW;
1266
1267 if (pLogFont->lfFaceName[0] == UNICODE_NULL)
1268 return FALSE;
1269
1270 RtlInitUnicodeString(&InputNameW, pLogFont->lfFaceName);
1271
1272 while (RecurseCount-- > 0)
1273 {
1274 Found = SubstituteFontByList(&g_FontSubstListHead,
1275 &OutputNameW, &InputNameW,
1276 pLogFont->lfCharSet, CharSetMap);
1277 if (!Found)
1278 break;
1279
1280 IntUnicodeStringToBuffer(pLogFont->lfFaceName, sizeof(pLogFont->lfFaceName), &OutputNameW);
1281 RtlInitUnicodeString(&InputNameW, pLogFont->lfFaceName);
1282
1283 if (CharSetMap[FONTSUBST_FROM] == DEFAULT_CHARSET ||
1284 CharSetMap[FONTSUBST_FROM] == pLogFont->lfCharSet)
1285 {
1286 pLogFont->lfCharSet = CharSetMap[FONTSUBST_TO];
1287 }
1288 }
1289
1290 return TRUE; /* success */
1291}
1292
1293// Quickly initialize a FONTLINK_CHAIN
1294static inline VOID
1296 _Out_ PFONTLINK_CHAIN pChain,
1297 _Inout_ PTEXTOBJ pTextObj,
1299{
1300 RtlZeroMemory(pChain, sizeof(*pChain));
1301 pChain->pBaseTextObj = pTextObj;
1302 pChain->pDefFace = face;
1304}
1305
1306// The default FontLink data
1307static const WCHAR s_szzDefFontLink[] =
1308 L"tahoma.ttf,Tahoma\0"
1309 L"msgothic.ttc,MS UI Gothic\0"
1310 L"mingliu.ttc,PMingLiU\0"
1311 L"simsun.ttc,SimSun\0"
1312 L"gulim.ttc,Gulim\0"
1313 L"\0";
1314// The default fixed-pitch FontLink data
1316 L"cour.ttf,Courier New\0"
1317 L"msgothic.ttc,MS Gothic\0"
1318 L"mingliu.ttc,MingLiU\0"
1319 L"simsun.ttc,NSimSun\0"
1320 L"gulim.ttc,GulimChe\0"
1321 L"\0";
1322
1323static NTSTATUS
1325 _Inout_ PFONTLINK_CHAIN pChain,
1326 _Inout_ PLOGFONTW pLF)
1327{
1329 HKEY hKey;
1330 DWORD cbData;
1331 WCHAR szzFontLink[512];
1332 SIZE_T FontLinkSize;
1333 PZZWSTR pszzFontLink = NULL;
1334
1335 ASSERT(pLF->lfFaceName[0]);
1336
1337 // Open the registry key
1339 L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\FontLink\\SystemLink",
1340 &hKey);
1341 if (!NT_SUCCESS(Status))
1342 return Status;
1343
1344 // Load the FontLink entry
1345 cbData = sizeof(szzFontLink);
1346 Status = RegQueryValue(hKey, pLF->lfFaceName, REG_MULTI_SZ, szzFontLink, &cbData);
1347 if (!NT_SUCCESS(Status) &&
1349 {
1350 // Retry with substituted
1352 cbData = sizeof(szzFontLink);
1353 Status = RegQueryValue(hKey, pLF->lfFaceName, REG_MULTI_SZ, szzFontLink, &cbData);
1354 }
1355
1357 {
1358 // Buffer is too small. Retry with larger buffer
1359 if (cbData >= 2 * sizeof(WCHAR)) // Sanity check
1360 {
1361 FontLinkSize = cbData;
1362 pszzFontLink = ExAllocatePoolWithTag(PagedPool, FontLinkSize, TAG_FONT);
1363 if (!pszzFontLink)
1364 {
1365 ZwClose(hKey); // Close the registry key
1366 return STATUS_NO_MEMORY;
1367 }
1368 Status = RegQueryValue(hKey, pLF->lfFaceName, REG_MULTI_SZ, pszzFontLink, &cbData);
1369 if (!NT_SUCCESS(Status))
1370 {
1371 ExFreePoolWithTag(pszzFontLink, TAG_FONT);
1372 pszzFontLink = NULL;
1373 }
1374 }
1375 }
1376
1377 ZwClose(hKey); // Close the registry key
1378
1379 if (!NT_SUCCESS(Status)) // Failed to get registry value
1380 {
1381 // Use default value
1382 ASSERT(sizeof(szzFontLink) >= sizeof(s_szzDefFontLink));
1383 ASSERT(sizeof(szzFontLink) >= sizeof(s_szzDefFixedFontLink));
1384 if (!(pLF->lfPitchAndFamily & FIXED_PITCH))
1385 RtlCopyMemory(szzFontLink, s_szzDefFontLink, sizeof(s_szzDefFontLink));
1386 else
1388 }
1389
1390 if (pszzFontLink)
1391 {
1392 // Ensure double-NUL-terminated
1393 ASSERT(FontLinkSize / sizeof(WCHAR) >= 2);
1394 pszzFontLink[FontLinkSize / sizeof(WCHAR) - 1] = UNICODE_NULL;
1395 pszzFontLink[FontLinkSize / sizeof(WCHAR) - 2] = UNICODE_NULL;
1396 }
1397 else
1398 {
1399 // Ensure double-NUL-terminated
1400 szzFontLink[_countof(szzFontLink) - 1] = UNICODE_NULL;
1401 szzFontLink[_countof(szzFontLink) - 2] = UNICODE_NULL;
1402
1403 FontLinkSize = SZZ_GetSize(szzFontLink);
1404 pszzFontLink = ExAllocatePoolWithTag(PagedPool, FontLinkSize, TAG_FONT);
1405 if (!pszzFontLink)
1406 return STATUS_NO_MEMORY;
1407 RtlCopyMemory(pszzFontLink, szzFontLink, FontLinkSize);
1408 }
1409 pChain->pszzFontLink = pszzFontLink;
1410
1411 return STATUS_SUCCESS;
1412}
1413
1414static inline PFONTLINK
1416 PFONTLINK_CHAIN pChain,
1417 PLOGFONTW plf)
1418{
1419 PLIST_ENTRY Entry, Head = &pChain->FontLinkList;
1420 PFONTLINK pLink;
1421
1422 for (Entry = Head->Flink; Entry != Head; Entry = Entry->Flink)
1423 {
1424 pLink = CONTAINING_RECORD(Entry, FONTLINK, ListEntry);
1425 if (RtlEqualMemory(&pLink->LogFont, plf, sizeof(*plf)))
1426 return pLink;
1427 }
1428
1429 return NULL;
1430}
1431
1432static inline PFONTLINK
1434 _Inout_ PFONTLINK_CHAIN pChain,
1435 _In_ const LOGFONTW *plfBase,
1436 _In_ LPCWSTR pszLink)
1437{
1438 LPWSTR pch0, pch1;
1439 LOGFONTW lf;
1440 PFONTLINK pLink;
1441
1442 lf = *plfBase;
1443
1444 // pszLink: "<FontFileName>,<FaceName>[,...]"
1445 pch0 = wcschr(pszLink, L',');
1446 if (!pch0)
1447 {
1448 DPRINT1("%S\n", pszLink);
1449 return NULL; // Invalid FontLink data
1450 }
1451 ++pch0;
1452
1453 pch1 = wcschr(pch0, L',');
1454 if (pch1)
1455 RtlStringCchCopyNW(lf.lfFaceName, _countof(lf.lfFaceName), pch0, pch1 - pch0);
1456 else
1458
1460 DPRINT("lfFaceName: %S\n", lf.lfFaceName);
1461
1462 if (RtlEqualMemory(plfBase, &lf, sizeof(lf)) || FontLink_Chain_FindLink(pChain, &lf))
1463 return NULL; // Already exists
1464
1465 pLink = ExAllocatePoolZero(PagedPool, sizeof(FONTLINK), TAG_FONT);
1466 if (!pLink)
1467 return NULL; // Out of memory
1468
1469 pLink->LogFont = lf;
1470 return pLink;
1471}
1472
1473static NTSTATUS
1475 _Inout_ PFONTLINK_CHAIN pChain)
1476{
1478 PFONTLINK pLink;
1479 LOGFONTW lfBase;
1480 PTEXTOBJ pTextObj = pChain->pBaseTextObj;
1481 PFONTGDI pFontGDI;
1482 PWSTR pszLink;
1483 PFONTLINK_CACHE pLinkCache;
1484 WCHAR szEntry[MAX_PATH];
1485 BOOL bFixCharSet;
1486
1487 InitializeListHead(&pChain->FontLinkList);
1488
1489 lfBase = pTextObj->logfont.elfEnumLogfontEx.elfLogFont;
1490 pFontGDI = ObjToGDI(pTextObj->Font, FONT);
1491 lfBase.lfHeight = pFontGDI->lfHeight;
1492 lfBase.lfWidth = pFontGDI->lfWidth;
1493
1494 // Use pTextObj->TextFace if lfFaceName was empty
1495 if (!lfBase.lfFaceName[0])
1496 {
1497 ASSERT(pTextObj->TextFace[0]);
1498 RtlStringCchCopyW(lfBase.lfFaceName, _countof(lfBase.lfFaceName), pTextObj->TextFace);
1499 }
1500
1501 // Fix lfCharSet
1502 switch (lfBase.lfCharSet)
1503 {
1504 case ANSI_CHARSET: bFixCharSet = !s_fFontLinkUseAnsi; break;
1505 case OEM_CHARSET: bFixCharSet = !s_fFontLinkUseOem; break;
1506 case SYMBOL_CHARSET: bFixCharSet = !s_fFontLinkUseSymbol; break;
1507 default: bFixCharSet = TRUE; break;
1508 }
1509 if (bFixCharSet)
1510 lfBase.lfCharSet = DEFAULT_CHARSET;
1511
1512 // Use cache if any
1513 pLinkCache = FontLink_FindCache(&lfBase);
1514 if (pLinkCache)
1515 {
1516 RemoveEntryList(&pLinkCache->ListEntry);
1517 *pChain = pLinkCache->Chain;
1518 IntRebaseList(&pChain->FontLinkList, &pLinkCache->Chain.FontLinkList);
1519 ExFreePoolWithTag(pLinkCache, TAG_FONT);
1520 return STATUS_SUCCESS;
1521 }
1522
1523 pChain->LogFont = lfBase;
1524
1525 // Load FontLink entry from registry
1526 Status = FontLink_Chain_LoadReg(pChain, &pChain->LogFont);
1527 if (!NT_SUCCESS(Status))
1528 return Status;
1529
1530 pszLink = pChain->pszzFontLink;
1531 while (*pszLink)
1532 {
1533 DPRINT("pszLink: '%S'\n", pszLink);
1534 pLink = FontLink_Create(pChain, &lfBase, pszLink);
1535 if (pLink)
1536 InsertTailList(&pChain->FontLinkList, &pLink->ListEntry);
1537 pszLink += wcslen(pszLink) + 1;
1538 }
1539
1540 // Use default settings (if any)
1542 {
1544 RtlStringCchCatW(szEntry, _countof(szEntry), L",");
1546 DPRINT("szEntry: '%S'\n", szEntry);
1547 pLink = FontLink_Create(pChain, &lfBase, szEntry);
1548 if (pLink)
1549 InsertTailList(&pChain->FontLinkList, &pLink->ListEntry);
1550 }
1551
1553 return Status;
1554}
1555
1556/*
1557 * IntLoadSystemFonts
1558 *
1559 * Search the system font directory and adds each font found.
1560 */
1563{
1565 UNICODE_STRING Directory, FileName, TempString;
1567 HANDLE hDirectory;
1568 BYTE *DirInfoBuffer;
1570 BOOLEAN bRestartScan = TRUE;
1572 INT i;
1573 static UNICODE_STRING SearchPatterns[] =
1574 {
1575 RTL_CONSTANT_STRING(L"*.ttf"),
1576 RTL_CONSTANT_STRING(L"*.ttc"),
1577 RTL_CONSTANT_STRING(L"*.otf"),
1578 RTL_CONSTANT_STRING(L"*.otc"),
1579 RTL_CONSTANT_STRING(L"*.fon"),
1580 RTL_CONSTANT_STRING(L"*.fnt")
1581 };
1582 static UNICODE_STRING IgnoreFiles[] =
1583 {
1586 };
1587
1588 RtlInitUnicodeString(&Directory, L"\\SystemRoot\\Fonts\\");
1589
1592 &Directory,
1594 NULL,
1595 NULL);
1596
1598 &hDirectory,
1601 &Iosb,
1604
1605 if (NT_SUCCESS(Status))
1606 {
1607 for (i = 0; i < _countof(SearchPatterns); ++i)
1608 {
1609 DirInfoBuffer = ExAllocatePoolWithTag(PagedPool, 0x4000, TAG_FONT);
1610 if (DirInfoBuffer == NULL)
1611 {
1612 ZwClose(hDirectory);
1613 return;
1614 }
1615
1617 if (FileName.Buffer == NULL)
1618 {
1619 ExFreePoolWithTag(DirInfoBuffer, TAG_FONT);
1620 ZwClose(hDirectory);
1621 return;
1622 }
1623 FileName.Length = 0;
1624 FileName.MaximumLength = MAX_PATH * sizeof(WCHAR);
1625
1626 while (1)
1627 {
1628 Status = ZwQueryDirectoryFile(
1629 hDirectory,
1630 NULL,
1631 NULL,
1632 NULL,
1633 &Iosb,
1634 DirInfoBuffer,
1635 0x4000,
1637 FALSE,
1638 &SearchPatterns[i],
1639 bRestartScan);
1640
1642 {
1643 break;
1644 }
1645
1646 DirInfo = (PFILE_DIRECTORY_INFORMATION)DirInfoBuffer;
1647 while (1)
1648 {
1649 SIZE_T ign;
1650
1651 TempString.Buffer = DirInfo->FileName;
1652 TempString.Length = TempString.MaximumLength = DirInfo->FileNameLength;
1653
1654 /* Should we ignore this file? */
1655 for (ign = 0; ign < _countof(IgnoreFiles); ++ign)
1656 {
1657 /* Yes.. */
1658 if (RtlEqualUnicodeString(IgnoreFiles + ign, &TempString, FALSE))
1659 break;
1660 }
1661
1662 /* If we tried all Ignore patterns and there was no match, try to create a font */
1663 if (ign == _countof(IgnoreFiles))
1664 {
1668 {
1669 DPRINT1("ERR: Failed to load %wZ\n", &FileName);
1670 }
1671 }
1672
1673 if (DirInfo->NextEntryOffset == 0)
1674 break;
1675
1676 DirInfo = (PFILE_DIRECTORY_INFORMATION)((ULONG_PTR)DirInfo + DirInfo->NextEntryOffset);
1677 }
1678
1679 bRestartScan = FALSE;
1680 }
1681
1683 ExFreePoolWithTag(DirInfoBuffer, TAG_FONT);
1684 }
1685 ZwClose(hDirectory);
1686 }
1687}
1688
1689/* NOTE: If nIndex < 0 then return the number of charsets. */
1690UINT FASTCALL IntGetCharSet(INT nIndex, FT_ULong CodePageRange1)
1691{
1692 UINT BitIndex, CharSet;
1693 UINT nCount = 0;
1694
1695 if (CodePageRange1 == 0)
1696 {
1697 return (nIndex < 0) ? 1 : DEFAULT_CHARSET;
1698 }
1699
1700 for (BitIndex = 0; BitIndex < MAXTCIINDEX; ++BitIndex)
1701 {
1702 if (CodePageRange1 & (1 << BitIndex))
1703 {
1704 CharSet = g_FontTci[BitIndex].ciCharset;
1705 if ((nIndex >= 0) && (nCount == (UINT)nIndex))
1706 {
1707 return CharSet;
1708 }
1709 ++nCount;
1710 }
1711 }
1712
1713 return (nIndex < 0) ? nCount : ANSI_CHARSET;
1714}
1715
1716/* pixels to points */
1717#define PX2PT(pixels) FT_MulDiv((pixels), 72, 96)
1718
1719static INT FASTCALL
1721 PSHARED_FACE SharedFace, FT_Long FontIndex, INT CharSetIndex)
1722{
1725 FONT_ENTRY_MEM* PrivateEntry = NULL;
1726 FONTGDI * FontGDI;
1728 FT_Face Face;
1730 FT_WinFNT_HeaderRec WinFNT;
1731 INT FaceCount = 0, CharSetCount = 0;
1732 PUNICODE_STRING pFileName = pLoadFont->pFileName;
1733 DWORD Characteristics = pLoadFont->Characteristics;
1734 PUNICODE_STRING pValueName = &pLoadFont->RegValueName;
1735 TT_OS2 * pOS2;
1736 INT BitIndex;
1737 FT_UShort os2_version;
1738 FT_ULong os2_ulCodePageRange1;
1739 FT_UShort os2_usWeightClass;
1740
1741 ASSERT(SharedFace != NULL);
1742 ASSERT(FontIndex != -1);
1743
1745 Face = SharedFace->Face;
1746 SharedFace_AddRef(SharedFace);
1748
1749 /* allocate a FONT_ENTRY */
1751 if (!Entry)
1752 {
1753 SharedFace_Release(SharedFace);
1755 return 0; /* failure */
1756 }
1757
1758 /* allocate a FONTGDI */
1759 FontGDI = EngAllocMem(FL_ZERO_MEMORY, sizeof(FONTGDI), GDITAG_RFONT);
1760 if (!FontGDI)
1761 {
1762 SharedFace_Release(SharedFace);
1765 return 0; /* failure */
1766 }
1767
1768 /* set file name */
1769 if (pFileName)
1770 {
1772 pFileName->Length + sizeof(UNICODE_NULL),
1773 GDITAG_PFF);
1774 if (FontGDI->Filename == NULL)
1775 {
1776 EngFreeMem(FontGDI);
1777 SharedFace_Release(SharedFace);
1780 return 0; /* failure */
1781 }
1782
1783 RtlCopyMemory(FontGDI->Filename, pFileName->Buffer, pFileName->Length);
1784 FontGDI->Filename[pFileName->Length / sizeof(WCHAR)] = UNICODE_NULL;
1785 }
1786 else
1787 {
1788 FontGDI->Filename = NULL;
1789
1790 PrivateEntry = ExAllocatePoolWithTag(PagedPool, sizeof(FONT_ENTRY_MEM), TAG_FONT);
1791 if (!PrivateEntry)
1792 {
1793 if (FontGDI->Filename)
1795 EngFreeMem(FontGDI);
1796 SharedFace_Release(SharedFace);
1799 return 0; /* failure */
1800 }
1801
1802 PrivateEntry->Entry = Entry;
1803 if (pLoadFont->PrivateEntry)
1804 {
1805 InsertTailList(&pLoadFont->PrivateEntry->ListEntry, &PrivateEntry->ListEntry);
1806 }
1807 else
1808 {
1809 InitializeListHead(&PrivateEntry->ListEntry);
1810 pLoadFont->PrivateEntry = PrivateEntry;
1811 }
1812 }
1813
1814 /* set face */
1815 FontGDI->SharedFace = SharedFace;
1816 FontGDI->CharSet = ANSI_CHARSET;
1817 FontGDI->OriginalItalic = FALSE;
1818 FontGDI->RequestItalic = FALSE;
1819 FontGDI->OriginalWeight = FALSE;
1820 FontGDI->RequestWeight = FW_NORMAL;
1821
1823 pOS2 = (TT_OS2 *)FT_Get_Sfnt_Table(Face, FT_SFNT_OS2);
1824 if (pOS2)
1825 {
1826 FontGDI->OriginalItalic = !!(pOS2->fsSelection & 0x1);
1827 FontGDI->OriginalWeight = pOS2->usWeightClass;
1828 }
1829 else
1830 {
1831 Error = FT_Get_WinFNT_Header(Face, &WinFNT);
1832 if (!Error)
1833 {
1834 FontGDI->OriginalItalic = !!WinFNT.italic;
1835 FontGDI->OriginalWeight = WinFNT.weight;
1836 }
1837 }
1839
1842 if (NT_SUCCESS(Status))
1843 {
1844 if (Face->style_name && Face->style_name[0] &&
1845 strcmp(Face->style_name, "Regular") != 0)
1846 {
1849 if (!NT_SUCCESS(Status))
1850 {
1851 RtlFreeUnicodeString(&Entry->FaceName);
1852 }
1853 }
1854 else
1855 {
1856 RtlInitUnicodeString(&Entry->StyleName, NULL);
1857 }
1858 }
1859 if (!NT_SUCCESS(Status))
1860 {
1861 if (PrivateEntry)
1862 {
1863 if (pLoadFont->PrivateEntry == PrivateEntry)
1864 {
1865 pLoadFont->PrivateEntry = NULL;
1866 }
1867 else
1868 {
1869 RemoveEntryList(&PrivateEntry->ListEntry);
1870 }
1871 ExFreePoolWithTag(PrivateEntry, TAG_FONT);
1872 }
1873 if (FontGDI->Filename)
1875 EngFreeMem(FontGDI);
1876 SharedFace_Release(SharedFace);
1878 return 0;
1879 }
1880
1881 os2_version = 0;
1883 pOS2 = (TT_OS2 *)FT_Get_Sfnt_Table(Face, FT_SFNT_OS2);
1884 if (pOS2)
1885 {
1886 os2_version = pOS2->version;
1887 os2_ulCodePageRange1 = pOS2->ulCodePageRange1;
1888 os2_usWeightClass = pOS2->usWeightClass;
1889 }
1891
1892 if (pOS2 && os2_version >= 1)
1893 {
1894 /* get charset and weight from OS/2 header */
1895
1896 /* Make sure we do not use this pointer anymore */
1897 pOS2 = NULL;
1898
1899 for (BitIndex = 0; BitIndex < MAXTCIINDEX; ++BitIndex)
1900 {
1901 if (os2_ulCodePageRange1 & (1 << BitIndex))
1902 {
1903 if (g_FontTci[BitIndex].ciCharset == DEFAULT_CHARSET)
1904 continue;
1905
1906 if ((CharSetIndex == -1 && CharSetCount == 0) ||
1907 CharSetIndex == CharSetCount)
1908 {
1909 FontGDI->CharSet = g_FontTci[BitIndex].ciCharset;
1910 }
1911
1912 ++CharSetCount;
1913 }
1914 }
1915
1916 /* set actual weight */
1917 FontGDI->OriginalWeight = os2_usWeightClass;
1918 }
1919 else
1920 {
1921 /* get charset from WinFNT header */
1923 Error = FT_Get_WinFNT_Header(Face, &WinFNT);
1924 if (!Error)
1925 {
1926 FontGDI->CharSet = WinFNT.charset;
1927 }
1929 }
1930
1931 ++FaceCount;
1932 DPRINT("Font loaded: %s (%s)\n",
1933 Face->family_name ? Face->family_name : "<NULL>",
1934 Face->style_name ? Face->style_name : "<NULL>");
1935 DPRINT("Num glyphs: %d\n", Face->num_glyphs);
1936 DPRINT("CharSet: %d\n", FontGDI->CharSet);
1937
1938 /* Add this font resource to the font table */
1939 Entry->Font = FontGDI;
1940 Entry->NotEnum = (Characteristics & FR_NOT_ENUM);
1941
1943 if (Characteristics & FR_PRIVATE)
1944 {
1945 /* private font */
1947 IntLockProcessPrivateFonts(Win32Process);
1948 InsertTailList(&Win32Process->PrivateFontListHead, &Entry->ListEntry);
1949 IntUnLockProcessPrivateFonts(Win32Process);
1950 }
1951 else
1952 {
1953 /* global font */
1954 InsertTailList(&g_FontListHead, &Entry->ListEntry);
1955 }
1957
1958 if (CharSetIndex == -1)
1959 {
1960 INT i;
1961 USHORT NameLength = Entry->FaceName.Length;
1962
1963 if (Entry->StyleName.Length)
1964 NameLength += Entry->StyleName.Length + sizeof(WCHAR);
1965
1966 if (pLoadFont->RegValueName.Length == 0)
1967 {
1968 pValueName->Length = 0;
1969 pValueName->MaximumLength = NameLength + sizeof(WCHAR);
1971 pValueName->MaximumLength,
1972 TAG_USTR);
1973 pValueName->Buffer[0] = UNICODE_NULL;
1974 RtlAppendUnicodeStringToString(pValueName, &Entry->FaceName);
1975 }
1976 else
1977 {
1978 UNICODE_STRING NewString;
1979 USHORT Length = pValueName->Length + 3 * sizeof(WCHAR) + NameLength;
1980 NewString.Length = 0;
1981 NewString.MaximumLength = Length + sizeof(WCHAR);
1983 NewString.MaximumLength,
1984 TAG_USTR);
1985 NewString.Buffer[0] = UNICODE_NULL;
1986
1987 RtlAppendUnicodeStringToString(&NewString, pValueName);
1988 RtlAppendUnicodeToString(&NewString, L" & ");
1989 RtlAppendUnicodeStringToString(&NewString, &Entry->FaceName);
1990
1991 RtlFreeUnicodeString(pValueName);
1992 *pValueName = NewString;
1993 }
1994 if (Entry->StyleName.Length)
1995 {
1996 RtlAppendUnicodeToString(pValueName, L" ");
1997 RtlAppendUnicodeStringToString(pValueName, &Entry->StyleName);
1998 }
1999
2000 for (i = 1; i < CharSetCount; ++i)
2001 {
2002 /* Do not count charsets as loaded 'faces' */
2003 IntGdiLoadFontsFromMemory(pLoadFont, SharedFace, FontIndex, i);
2004 }
2005 }
2006
2007 return FaceCount; /* Number of loaded faces */
2008}
2009
2010static INT FASTCALL
2012{
2014 FT_Face Face;
2015 FT_Long iFace, num_faces;
2016 PSHARED_FACE SharedFace;
2017 INT FaceCount = 0;
2018
2020
2021 /* Load a face from memory */
2023 pLoadFont->Memory->Buffer, pLoadFont->Memory->BufferSize,
2024 ((FontIndex == -1) ? 0 : FontIndex), &Face);
2025 if (Error)
2026 {
2027 if (Error == FT_Err_Unknown_File_Format)
2028 DPRINT1("Unknown font file format\n");
2029 else
2030 DPRINT1("Error reading font (error code: %d)\n", Error);
2032 return 0; /* Failure */
2033 }
2034
2035 pLoadFont->IsTrueType = FT_IS_SFNT(Face);
2036 num_faces = Face->num_faces;
2037 SharedFace = SharedFace_Create(Face, pLoadFont->Memory);
2038
2040
2041 if (!SharedFace)
2042 {
2043 DPRINT1("SharedFace_Create failed\n");
2045 return 0; /* Failure */
2046 }
2047
2048 if (FontIndex == -1)
2049 {
2050 for (iFace = 1; iFace < num_faces; ++iFace)
2051 {
2053 }
2054 FontIndex = 0;
2055 }
2056
2057 FaceCount += IntGdiLoadFontsFromMemory(pLoadFont, SharedFace, FontIndex, -1);
2058 return FaceCount;
2059}
2060
2061static LPCWSTR FASTCALL
2063{
2064 switch (CharSet)
2065 {
2066 case ANSI_CHARSET: return L"ANSI";
2067 case DEFAULT_CHARSET: return L"Default";
2068 case SYMBOL_CHARSET: return L"Symbol";
2069 case SHIFTJIS_CHARSET: return L"Shift_JIS";
2070 case HANGUL_CHARSET: return L"Hangul";
2071 case GB2312_CHARSET: return L"GB 2312";
2072 case CHINESEBIG5_CHARSET: return L"Chinese Big5";
2073 case OEM_CHARSET: return L"OEM";
2074 case JOHAB_CHARSET: return L"Johab";
2075 case HEBREW_CHARSET: return L"Hebrew";
2076 case ARABIC_CHARSET: return L"Arabic";
2077 case GREEK_CHARSET: return L"Greek";
2078 case TURKISH_CHARSET: return L"Turkish";
2079 case VIETNAMESE_CHARSET: return L"Vietnamese";
2080 case THAI_CHARSET: return L"Thai";
2081 case EASTEUROPE_CHARSET: return L"Eastern European";
2082 case RUSSIAN_CHARSET: return L"Russian";
2083 case MAC_CHARSET: return L"Mac";
2084 case BALTIC_CHARSET: return L"Baltic";
2085 default: return L"Unknown";
2086 }
2087}
2088
2089/*
2090 * IntGdiAddFontResource
2091 *
2092 * Adds the font resource from the specified file to the system.
2093 */
2094
2097 DWORD dwFlags)
2098{
2101 PVOID Buffer = NULL;
2104 SIZE_T ViewSize = 0, Length;
2105 LARGE_INTEGER SectionSize;
2108 INT FontCount;
2110 UNICODE_STRING PathName;
2111 LPWSTR pszBuffer;
2113 static const UNICODE_STRING TrueTypePostfix = RTL_CONSTANT_STRING(L" (TrueType)");
2114 static const UNICODE_STRING DosPathPrefix = RTL_CONSTANT_STRING(L"\\??\\");
2115
2116 /* Build PathName */
2118 {
2119 Length = DosPathPrefix.Length + FileName->Length + sizeof(UNICODE_NULL);
2121 if (!pszBuffer)
2122 return 0; /* failure */
2123
2124 RtlInitEmptyUnicodeString(&PathName, pszBuffer, Length);
2125 RtlAppendUnicodeStringToString(&PathName, &DosPathPrefix);
2127 }
2128 else
2129 {
2131 if (!NT_SUCCESS(Status))
2132 return 0; /* failure */
2133 }
2134
2135 /* Open the font file */
2139 &FileHandle,
2142 &Iosb,
2145 if (!NT_SUCCESS(Status))
2146 {
2147 DPRINT1("Could not load font file: %wZ\n", &PathName);
2148 RtlFreeUnicodeString(&PathName);
2149 return 0;
2150 }
2151
2154 if (!NT_SUCCESS(Status))
2155 {
2156 DPRINT1("ObReferenceObjectByHandle failed.\n");
2158 RtlFreeUnicodeString(&PathName);
2159 return 0;
2160 }
2161
2162 SectionSize.QuadPart = 0LL;
2165 NULL, &SectionSize, PAGE_READONLY,
2167 if (!NT_SUCCESS(Status))
2168 {
2169 DPRINT1("Could not map file: %wZ\n", &PathName);
2172 RtlFreeUnicodeString(&PathName);
2173 return 0;
2174 }
2176
2178 if (!NT_SUCCESS(Status))
2179 {
2180 DPRINT1("Could not map file: %wZ\n", &PathName);
2183 RtlFreeUnicodeString(&PathName);
2184 return 0;
2185 }
2186
2187 RtlZeroMemory(&LoadFont, sizeof(LoadFont));
2188 LoadFont.pFileName = &PathName;
2190 LoadFont.Characteristics = Characteristics;
2191 RtlInitUnicodeString(&LoadFont.RegValueName, NULL);
2192 LoadFont.CharSet = DEFAULT_CHARSET;
2193 FontCount = IntGdiLoadFontByIndexFromMemory(&LoadFont, -1);
2194
2195 /* Release our copy */
2199
2201
2203
2204 /* Save the loaded font name into the registry */
2205 if (FontCount > 0 && (dwFlags & AFRX_WRITE_REGISTRY))
2206 {
2207 UNICODE_STRING NewString;
2208 SIZE_T Length;
2209 PWCHAR pszBuffer;
2210 LPCWSTR CharSetName;
2211 if (LoadFont.IsTrueType)
2212 {
2213 /* Append " (TrueType)" */
2214 Length = LoadFont.RegValueName.Length + TrueTypePostfix.Length + sizeof(UNICODE_NULL);
2216 if (pszBuffer)
2217 {
2218 RtlInitEmptyUnicodeString(&NewString, pszBuffer, Length);
2219 NewString.Buffer[0] = UNICODE_NULL;
2220 RtlAppendUnicodeStringToString(&NewString, &LoadFont.RegValueName);
2221 RtlAppendUnicodeStringToString(&NewString, &TrueTypePostfix);
2222 RtlFreeUnicodeString(&LoadFont.RegValueName);
2223 LoadFont.RegValueName = NewString;
2224 }
2225 else
2226 {
2227 // FIXME!
2228 }
2229 }
2230 else if (LoadFont.CharSet != DEFAULT_CHARSET)
2231 {
2232 /* Append " (CharSetName)" */
2233 CharSetName = NameFromCharSet(LoadFont.CharSet);
2234 Length = LoadFont.RegValueName.Length +
2235 (wcslen(CharSetName) + 3) * sizeof(WCHAR) +
2236 sizeof(UNICODE_NULL);
2237
2239 if (pszBuffer)
2240 {
2241 RtlInitEmptyUnicodeString(&NewString, pszBuffer, Length);
2242 NewString.Buffer[0] = UNICODE_NULL;
2243 RtlAppendUnicodeStringToString(&NewString, &LoadFont.RegValueName);
2244 RtlAppendUnicodeToString(&NewString, L" (");
2245 RtlAppendUnicodeToString(&NewString, CharSetName);
2246 RtlAppendUnicodeToString(&NewString, L")");
2247 RtlFreeUnicodeString(&LoadFont.RegValueName);
2248 LoadFont.RegValueName = NewString;
2249 }
2250 else
2251 {
2252 // FIXME!
2253 }
2254 }
2255
2258 NULL, NULL);
2259 Status = ZwOpenKey(&KeyHandle, KEY_WRITE, &ObjectAttributes);
2260 if (NT_SUCCESS(Status))
2261 {
2263 LPWSTR pFileName;
2264
2266 {
2267 pFileName = PathName.Buffer;
2268 }
2269 else
2270 {
2271 pFileName = wcsrchr(PathName.Buffer, L'\\');
2272 }
2273
2274 if (pFileName)
2275 {
2277 {
2278 pFileName++;
2279 }
2280 DataSize = (wcslen(pFileName) + 1) * sizeof(WCHAR);
2281 ZwSetValueKey(KeyHandle, &LoadFont.RegValueName, 0, REG_SZ,
2282 pFileName, DataSize);
2283 }
2285 }
2286 }
2287 RtlFreeUnicodeString(&LoadFont.RegValueName);
2288
2289 RtlFreeUnicodeString(&PathName);
2290 return FontCount;
2291}
2292
2295{
2296 return IntGdiAddFontResourceEx(FileName, Characteristics, 0);
2297}
2298
2299/* Borrowed from shlwapi!PathIsRelativeW */
2301{
2302 if (!lpszPath || !*lpszPath)
2303 return TRUE;
2304 if (*lpszPath == L'\\' || (*lpszPath && lpszPath[1] == L':'))
2305 return FALSE;
2306 return TRUE;
2307}
2308
2311{
2315 KEY_FULL_INFORMATION KeyFullInfo;
2316 ULONG i, Length;
2317 UNICODE_STRING FontTitleW, FileNameW;
2318 SIZE_T InfoSize;
2319 LPBYTE InfoBuffer;
2321 LPWSTR pchPath;
2323 INT nFontCount = 0;
2324 DWORD dwFlags;
2325
2326 /* open registry key */
2329 NULL, NULL);
2330 Status = ZwOpenKey(&KeyHandle, KEY_READ, &ObjectAttributes);
2331 if (!NT_SUCCESS(Status))
2332 {
2333 DPRINT1("ZwOpenKey failed: 0x%08X\n", Status);
2334 return FALSE; /* failure */
2335 }
2336
2337 /* query count of values */
2338 Status = ZwQueryKey(KeyHandle, KeyFullInformation,
2339 &KeyFullInfo, sizeof(KeyFullInfo), &Length);
2340 if (!NT_SUCCESS(Status))
2341 {
2342 DPRINT1("ZwQueryKey failed: 0x%08X\n", Status);
2344 return FALSE; /* failure */
2345 }
2346
2347 /* allocate buffer */
2348 InfoSize = (MAX_PATH + 256) * sizeof(WCHAR);
2349 InfoBuffer = ExAllocatePoolWithTag(PagedPool, InfoSize, TAG_FONT);
2350 if (!InfoBuffer)
2351 {
2352 DPRINT1("ExAllocatePoolWithTag failed\n");
2354 return FALSE;
2355 }
2356
2357 /* for each value */
2358 for (i = 0; i < KeyFullInfo.Values; ++i)
2359 {
2360 /* get value name */
2361 Status = ZwEnumerateValueKey(KeyHandle, i, KeyValueFullInformation,
2362 InfoBuffer, InfoSize, &Length);
2364 {
2365 /* too short buffer */
2366 ExFreePoolWithTag(InfoBuffer, TAG_FONT);
2367 InfoSize *= 2;
2368 InfoBuffer = ExAllocatePoolWithTag(PagedPool, InfoSize, TAG_FONT);
2369 if (!InfoBuffer)
2370 {
2371 DPRINT1("ExAllocatePoolWithTag failed\n");
2372 break;
2373 }
2374 /* try again */
2375 Status = ZwEnumerateValueKey(KeyHandle, i, KeyValueFullInformation,
2376 InfoBuffer, InfoSize, &Length);
2377 }
2378 if (!NT_SUCCESS(Status))
2379 {
2380 DPRINT1("ZwEnumerateValueKey failed: 0x%08X\n", Status);
2381 break; /* failure */
2382 }
2383
2384 /* create FontTitleW string */
2385 pInfo = (PKEY_VALUE_FULL_INFORMATION)InfoBuffer;
2386 Length = pInfo->NameLength / sizeof(WCHAR);
2387 pInfo->Name[Length] = UNICODE_NULL; /* truncate */
2388 if (!RtlCreateUnicodeString(&FontTitleW, pInfo->Name))
2389 {
2391 DPRINT1("RtlCreateUnicodeString failed\n");
2392 break; /* failure */
2393 }
2394
2395 /* query value */
2396 Status = ZwQueryValueKey(KeyHandle, &FontTitleW, KeyValueFullInformation,
2397 InfoBuffer, InfoSize, &Length);
2399 {
2400 /* too short buffer */
2401 ExFreePoolWithTag(InfoBuffer, TAG_FONT);
2402 InfoSize *= 2;
2403 InfoBuffer = ExAllocatePoolWithTag(PagedPool, InfoSize, TAG_FONT);
2404 if (!InfoBuffer)
2405 {
2406 DPRINT1("ExAllocatePoolWithTag failed\n");
2407 break;
2408 }
2409 /* try again */
2410 Status = ZwQueryValueKey(KeyHandle, &FontTitleW, KeyValueFullInformation,
2411 InfoBuffer, InfoSize, &Length);
2412 }
2413 pInfo = (PKEY_VALUE_FULL_INFORMATION)InfoBuffer;
2414 if (!NT_SUCCESS(Status) || !pInfo->DataLength)
2415 {
2416 DPRINT1("ZwQueryValueKey failed: 0x%08X\n", Status);
2417 RtlFreeUnicodeString(&FontTitleW);
2418 break; /* failure */
2419 }
2420
2421 /* Build pchPath */
2422 pchPath = (LPWSTR)((PUCHAR)pInfo + pInfo->DataOffset);
2423 Length = pInfo->DataLength / sizeof(WCHAR);
2424 pchPath[Length] = UNICODE_NULL; /* truncate */
2425
2426 /* Load font(s) without writing registry */
2427 if (PathIsRelativeW(pchPath))
2428 {
2429 dwFlags = 0;
2431 L"\\SystemRoot\\Fonts\\%s", pchPath);
2432 }
2433 else
2434 {
2436 Status = RtlStringCbCopyW(szPath, sizeof(szPath), pchPath);
2437 }
2438
2439 if (NT_SUCCESS(Status))
2440 {
2441 RtlCreateUnicodeString(&FileNameW, szPath);
2442 nFontCount += IntGdiAddFontResourceEx(&FileNameW, 0, dwFlags);
2443 RtlFreeUnicodeString(&FileNameW);
2444 }
2445
2446 RtlFreeUnicodeString(&FontTitleW);
2447 }
2448
2449 /* close now */
2451
2452 /* free memory block */
2453 if (InfoBuffer)
2454 {
2455 ExFreePoolWithTag(InfoBuffer, TAG_FONT);
2456 }
2457
2458 return (KeyFullInfo.Values != 0 && nFontCount != 0);
2459}
2460
2463{
2464 HANDLE Ret = NULL;
2466 PFONT_ENTRY_COLL_MEM EntryCollection;
2467 INT FaceCount;
2468
2470 if (!BufferCopy)
2471 {
2472 *pNumAdded = 0;
2473 return NULL;
2474 }
2475 RtlCopyMemory(BufferCopy, Buffer, dwSize);
2476
2477 RtlZeroMemory(&LoadFont, sizeof(LoadFont));
2478 LoadFont.Memory = SharedMem_Create(BufferCopy, dwSize, FALSE);
2479 LoadFont.Characteristics = FR_PRIVATE | FR_NOT_ENUM;
2480 RtlInitUnicodeString(&LoadFont.RegValueName, NULL);
2482
2483 RtlFreeUnicodeString(&LoadFont.RegValueName);
2484
2485 /* Release our copy */
2489
2490 if (FaceCount > 0)
2491 {
2492 EntryCollection = ExAllocatePoolWithTag(PagedPool, sizeof(FONT_ENTRY_COLL_MEM), TAG_FONT);
2493 if (EntryCollection)
2494 {
2496 EntryCollection->Entry = LoadFont.PrivateEntry;
2498 IntLockProcessPrivateFonts(Win32Process);
2499 EntryCollection->Handle = ULongToHandle(++Win32Process->PrivateMemFontHandleCount);
2500 InsertTailList(&Win32Process->PrivateMemFontListHead, &EntryCollection->ListEntry);
2501 IntUnLockProcessPrivateFonts(Win32Process);
2503 Ret = EntryCollection->Handle;
2504 }
2505 }
2506 *pNumAdded = FaceCount;
2507
2508 return Ret;
2509}
2510
2511// FIXME: Add RemoveFontResource
2512
2515{
2517 PFONT_ENTRY_MEM FontEntry;
2518
2519 while (!IsListEmpty(&Head->ListEntry))
2520 {
2521 Entry = RemoveHeadList(&Head->ListEntry);
2522 FontEntry = CONTAINING_RECORD(Entry, FONT_ENTRY_MEM, ListEntry);
2523
2524 CleanupFontEntry(FontEntry->Entry);
2525 ExFreePoolWithTag(FontEntry, TAG_FONT);
2526 }
2527
2528 CleanupFontEntry(Head->Entry);
2530}
2531
2532static VOID FASTCALL
2534{
2535 PFONT_ENTRY_MEM FontMemEntry = Collection->Entry;
2536 PLIST_ENTRY ListEntry;
2537 RemoveEntryList(&Collection->ListEntry);
2538
2539 do {
2540 /* Also unlink the FONT_ENTRY stuff from the PrivateFontListHead */
2541 RemoveEntryList(&FontMemEntry->Entry->ListEntry);
2542
2543 ListEntry = FontMemEntry->ListEntry.Flink;
2544 FontMemEntry = CONTAINING_RECORD(ListEntry, FONT_ENTRY_MEM, ListEntry);
2545
2546 } while (FontMemEntry != Collection->Entry);
2547}
2548
2551{
2553 PFONT_ENTRY_COLL_MEM CurrentEntry;
2554 PFONT_ENTRY_COLL_MEM EntryCollection = NULL;
2556
2558 IntLockProcessPrivateFonts(Win32Process);
2559 for (Entry = Win32Process->PrivateMemFontListHead.Flink;
2560 Entry != &Win32Process->PrivateMemFontListHead;
2561 Entry = Entry->Flink)
2562 {
2563 CurrentEntry = CONTAINING_RECORD(Entry, FONT_ENTRY_COLL_MEM, ListEntry);
2564
2565 if (CurrentEntry->Handle == hMMFont)
2566 {
2567 EntryCollection = CurrentEntry;
2568 UnlinkFontMemCollection(CurrentEntry);
2569 break;
2570 }
2571 }
2572 IntUnLockProcessPrivateFonts(Win32Process);
2574
2575 if (EntryCollection)
2576 {
2577 IntGdiCleanupMemEntry(EntryCollection->Entry);
2578 ExFreePoolWithTag(EntryCollection, TAG_FONT);
2579 return TRUE;
2580 }
2581 return FALSE;
2582}
2583
2584
2587{
2590 PFONT_ENTRY_COLL_MEM EntryCollection;
2591
2592 DPRINT("IntGdiCleanupPrivateFontsForProcess()\n");
2593 do {
2594 Entry = NULL;
2595 EntryCollection = NULL;
2596
2598 IntLockProcessPrivateFonts(Win32Process);
2599 if (!IsListEmpty(&Win32Process->PrivateMemFontListHead))
2600 {
2601 Entry = Win32Process->PrivateMemFontListHead.Flink;
2602 EntryCollection = CONTAINING_RECORD(Entry, FONT_ENTRY_COLL_MEM, ListEntry);
2603 UnlinkFontMemCollection(EntryCollection);
2604 }
2605 IntUnLockProcessPrivateFonts(Win32Process);
2607
2608 if (EntryCollection)
2609 {
2610 IntGdiCleanupMemEntry(EntryCollection->Entry);
2611 ExFreePoolWithTag(EntryCollection, TAG_FONT);
2612 }
2613 else
2614 {
2615 /* No Mem fonts anymore, see if we have any other private fonts left */
2616 Entry = NULL;
2618 IntLockProcessPrivateFonts(Win32Process);
2619 if (!IsListEmpty(&Win32Process->PrivateFontListHead))
2620 {
2621 Entry = RemoveHeadList(&Win32Process->PrivateFontListHead);
2622 }
2623 IntUnLockProcessPrivateFonts(Win32Process);
2625
2626 if (Entry)
2627 {
2629 }
2630 }
2631
2632 } while (Entry);
2633}
2634
2637{
2638 return (gpsi->BitsPixel > 8) && g_RenderingEnabled;
2639}
2640
2643{
2645}
2646
2649{
2650 switch (logfont->lfQuality)
2651 {
2653 break;
2655 return FT_RENDER_MODE_MONO;
2656 case DRAFT_QUALITY:
2657 return FT_RENDER_MODE_LIGHT;
2658 case CLEARTYPE_QUALITY:
2659 if (!gspv.bFontSmoothing)
2660 break;
2662 break;
2663 return FT_RENDER_MODE_LCD;
2664 }
2665 return FT_RENDER_MODE_NORMAL;
2666}
2667
2668
2671{
2672 PLFONT plfont;
2673 LOGFONTW *plf;
2674
2675 ASSERT(lf);
2676 plfont = LFONT_AllocFontWithHandle();
2677 if (!plfont)
2678 {
2679 return STATUS_NO_MEMORY;
2680 }
2681
2682 ExInitializePushLock(&plfont->lock);
2683 *NewFont = plfont->BaseObject.hHmgr;
2684 plf = &plfont->logfont.elfEnumLogfontEx.elfLogFont;
2685 RtlCopyMemory(plf, lf, sizeof(LOGFONTW));
2686 if (lf->lfEscapement != lf->lfOrientation)
2687 {
2688 /* This should really depend on whether GM_ADVANCED is set */
2689 plf->lfOrientation = plf->lfEscapement;
2690 }
2691 LFONT_UnlockFont(plfont);
2692
2693 return STATUS_SUCCESS;
2694}
2695
2696/*************************************************************************
2697 * TranslateCharsetInfo
2698 *
2699 * Fills a CHARSETINFO structure for a character set, code page, or
2700 * font. This allows making the correspondance between different labelings
2701 * (character set, Windows, ANSI, and OEM codepages, and Unicode ranges)
2702 * of the same encoding.
2703 *
2704 * Only one codepage will be set in Cs->fs. If TCI_SRCFONTSIG is used,
2705 * only one codepage should be set in *Src.
2706 *
2707 * RETURNS
2708 * TRUE on success, FALSE on failure.
2709 *
2710 */
2711static BOOLEAN
2713 if flags == TCI_SRCFONTSIG: pointer to fsCsb of a FONTSIGNATURE
2714 if flags == TCI_SRCCHARSET: a character set value
2715 if flags == TCI_SRCCODEPAGE: a code page value */
2716 LPCHARSETINFO Cs, /* [out] structure to receive charset information */
2717 DWORD Flags /* [in] determines interpretation of lpSrc */)
2718{
2719 int Index = 0;
2720
2721 switch (Flags)
2722 {
2723 case TCI_SRCFONTSIG:
2724 while (Index < MAXTCIINDEX && 0 == (*Src >> Index & 0x0001))
2725 {
2726 Index++;
2727 }
2728 break;
2729 case TCI_SRCCODEPAGE:
2730 while (Index < MAXTCIINDEX && *Src != g_FontTci[Index].ciACP)
2731 {
2732 Index++;
2733 }
2734 break;
2735 case TCI_SRCCHARSET:
2736 while (Index < MAXTCIINDEX && *Src != g_FontTci[Index].ciCharset)
2737 {
2738 Index++;
2739 }
2740 break;
2741 case TCI_SRCLOCALE:
2743 return FALSE;
2744 default:
2745 return FALSE;
2746 }
2747
2748 if (Index >= MAXTCIINDEX || DEFAULT_CHARSET == g_FontTci[Index].ciCharset)
2749 {
2750 return FALSE;
2751 }
2752
2753 RtlCopyMemory(Cs, &g_FontTci[Index], sizeof(CHARSETINFO));
2754
2755 return TRUE;
2756}
2757
2758
2760{
2761 int i;
2762
2763 for(i = 0; i < ft_face->num_charmaps; i++)
2764 {
2765 if (ft_face->charmaps[i]->platform_id == TT_PLATFORM_MICROSOFT &&
2766 ft_face->charmaps[i]->encoding == FT_ENCODING_MS_SYMBOL)
2767 {
2768 return TRUE;
2769 }
2770 }
2771 return FALSE;
2772}
2773
2774static void FASTCALL
2776 TT_OS2 *pOS2, TT_HoriHeader *pHori,
2777 FT_WinFNT_HeaderRec *pFNT)
2778{
2779 FT_Fixed XScale, YScale;
2780 int Ascent, Descent;
2781 FT_Face Face = FontGDI->SharedFace->Face;
2782
2784
2785 XScale = Face->size->metrics.x_scale;
2786 YScale = Face->size->metrics.y_scale;
2787
2788 if (pFNT)
2789 {
2790 TM->tmHeight = pFNT->pixel_height;
2791 TM->tmAscent = pFNT->ascent;
2792 TM->tmDescent = TM->tmHeight - TM->tmAscent;
2795 TM->tmAveCharWidth = pFNT->avg_width;
2796 TM->tmMaxCharWidth = pFNT->max_width;
2797 TM->tmOverhang = 0;
2800 TM->tmFirstChar = pFNT->first_char;
2801 TM->tmLastChar = pFNT->last_char;
2802 TM->tmDefaultChar = pFNT->default_char + pFNT->first_char;
2803 TM->tmBreakChar = pFNT->break_char + pFNT->first_char;
2805 TM->tmWeight = FontGDI->RequestWeight;
2806 TM->tmItalic = FontGDI->RequestItalic;
2807 TM->tmUnderlined = FontGDI->RequestUnderline;
2808 TM->tmStruckOut = FontGDI->RequestStrikeOut;
2809 TM->tmCharSet = FontGDI->CharSet;
2810 return;
2811 }
2812
2813 ASSERT(pOS2);
2814 if (!pOS2)
2815 return;
2816
2817 if ((FT_Short)pOS2->usWinAscent + (FT_Short)pOS2->usWinDescent == 0)
2818 {
2819 Ascent = pHori->Ascender;
2820 Descent = -pHori->Descender;
2821 }
2822 else
2823 {
2824 Ascent = (FT_Short)pOS2->usWinAscent;
2825 Descent = (FT_Short)pOS2->usWinDescent;
2826 }
2827
2828 TM->tmAscent = FontGDI->tmAscent;
2829 TM->tmDescent = FontGDI->tmDescent;
2830 TM->tmHeight = TM->tmAscent + TM->tmDescent;
2831 TM->tmInternalLeading = FontGDI->tmInternalLeading;
2832
2833 /* MSDN says:
2834 * el = MAX(0, LineGap - ((WinAscent + WinDescent) - (Ascender - Descender)))
2835 */
2836 TM->tmExternalLeading = max(0, (FT_MulFix(pHori->Line_Gap
2837 - ((Ascent + Descent)
2838 - (pHori->Ascender - pHori->Descender)),
2839 YScale) + 32) >> 6);
2840 if (FontGDI->lfWidth != 0)
2841 TM->tmAveCharWidth = FontGDI->lfWidth;
2842 else
2843 TM->tmAveCharWidth = (FT_MulFix(pOS2->xAvgCharWidth, XScale) + 32) >> 6;
2844
2845 if (TM->tmAveCharWidth == 0)
2846 TM->tmAveCharWidth = 1;
2847
2848 /* Correct forumla to get the maxcharwidth from unicode and ansi font */
2849 TM->tmMaxCharWidth = (FT_MulFix(Face->max_advance_width, XScale) + 32) >> 6;
2850
2851 if (FontGDI->OriginalWeight != FW_DONTCARE &&
2852 FontGDI->OriginalWeight != FW_NORMAL)
2853 {
2854 TM->tmWeight = FontGDI->OriginalWeight;
2855 }
2856 else
2857 {
2858 TM->tmWeight = FontGDI->RequestWeight;
2859 }
2860
2861 TM->tmOverhang = 0;
2862 TM->tmDigitizedAspectX = 96;
2863 TM->tmDigitizedAspectY = 96;
2864 if (face_has_symbol_charmap(Face) ||
2865 (pOS2->usFirstCharIndex >= 0xf000 && pOS2->usFirstCharIndex < 0xf100))
2866 {
2867 USHORT cpOEM, cpAnsi;
2868
2869 EngGetCurrentCodePage(&cpOEM, &cpAnsi);
2870 TM->tmFirstChar = 0;
2871 switch(cpAnsi)
2872 {
2873 case 1257: /* Baltic */
2874 TM->tmLastChar = 0xf8fd;
2875 break;
2876 default:
2877 TM->tmLastChar = 0xf0ff;
2878 }
2879 TM->tmBreakChar = 0x20;
2880 TM->tmDefaultChar = 0x1f;
2881 }
2882 else
2883 {
2884 TM->tmFirstChar = pOS2->usFirstCharIndex; /* Should be the first char in the cmap */
2885 TM->tmLastChar = pOS2->usLastCharIndex; /* Should be min(cmap_last, os2_last) */
2886
2887 if(pOS2->usFirstCharIndex <= 1)
2888 TM->tmBreakChar = pOS2->usFirstCharIndex + 2;
2889 else if (pOS2->usFirstCharIndex > 0xff)
2890 TM->tmBreakChar = 0x20;
2891 else
2892 TM->tmBreakChar = pOS2->usFirstCharIndex;
2893 TM->tmDefaultChar = TM->tmBreakChar - 1;
2894 }
2895
2896 if (FontGDI->OriginalItalic || FontGDI->RequestItalic)
2897 {
2898 TM->tmItalic = 0xFF;
2899 }
2900 else
2901 {
2902 TM->tmItalic = 0;
2903 }
2904 TM->tmUnderlined = (FontGDI->RequestUnderline ? 0xFF : 0);
2905 TM->tmStruckOut = (FontGDI->RequestStrikeOut ? 0xFF : 0);
2906
2907 if (!FT_IS_FIXED_WIDTH(Face))
2908 {
2909 switch (pOS2->panose[PAN_PROPORTION_INDEX])
2910 {
2912 TM->tmPitchAndFamily = 0;
2913 break;
2914 default:
2916 break;
2917 }
2918 }
2919 else
2920 {
2921 TM->tmPitchAndFamily = 0;
2922 }
2923
2924 switch (pOS2->panose[PAN_FAMILYTYPE_INDEX])
2925 {
2926 case PAN_FAMILY_SCRIPT:
2928 break;
2931 break;
2932
2933 case PAN_ANY:
2934 case PAN_NO_FIT:
2936 case PAN_FAMILY_PICTORIAL: /* Symbol fonts get treated as if they were text */
2937 /* Which is clearly not what the panose spec says. */
2938 if (TM->tmPitchAndFamily == 0) /* Fixed */
2939 {
2941 }
2942 else
2943 {
2944 switch (pOS2->panose[PAN_SERIFSTYLE_INDEX])
2945 {
2946 case PAN_ANY:
2947 case PAN_NO_FIT:
2948 default:
2950 break;
2951
2952 case PAN_SERIF_COVE:
2956 case PAN_SERIF_SQUARE:
2957 case PAN_SERIF_THIN:
2958 case PAN_SERIF_BONE:
2960 case PAN_SERIF_TRIANGLE:
2962 break;
2963
2967 case PAN_SERIF_FLARED:
2968 case PAN_SERIF_ROUNDED:
2970 break;
2971 }
2972 }
2973 break;
2974 default:
2976 }
2977
2978 if (FT_IS_SCALABLE(Face))
2979 {
2981 }
2982 if (FT_IS_SFNT(Face))
2983 {
2985 }
2986
2987 TM->tmCharSet = FontGDI->CharSet;
2988}
2989
2990static NTSTATUS
2992 FT_UShort NameID, FT_UShort LangID);
2993
2994typedef struct FONT_NAMES
2995{
2996 UNICODE_STRING FamilyNameW; /* family name (TT_NAME_ID_FONT_FAMILY) */
2997 UNICODE_STRING FaceNameW; /* face name (TT_NAME_ID_FULL_NAME) */
2998 UNICODE_STRING StyleNameW; /* style name (TT_NAME_ID_FONT_SUBFAMILY) */
2999 UNICODE_STRING FullNameW; /* unique name (TT_NAME_ID_UNIQUE_ID) */
3000 ULONG OtmSize; /* size of OUTLINETEXTMETRICW with extra data */
3002
3003static __inline void FASTCALL
3005{
3006 ULONG OtmSize;
3007
3008 RtlInitUnicodeString(&Names->FamilyNameW, NULL);
3009 RtlInitUnicodeString(&Names->FaceNameW, NULL);
3010 RtlInitUnicodeString(&Names->StyleNameW, NULL);
3011 RtlInitUnicodeString(&Names->FullNameW, NULL);
3012
3013 /* family name */
3015 /* face name */
3017 /* style name */
3019 /* unique name (full name) */
3021
3022 /* Calculate the size of OUTLINETEXTMETRICW with extra data */
3023 OtmSize = sizeof(OUTLINETEXTMETRICW) +
3024 Names->FamilyNameW.Length + sizeof(UNICODE_NULL) +
3025 Names->FaceNameW.Length + sizeof(UNICODE_NULL) +
3026 Names->StyleNameW.Length + sizeof(UNICODE_NULL) +
3027 Names->FullNameW.Length + sizeof(UNICODE_NULL);
3028 Names->OtmSize = OtmSize;
3029}
3030
3033{
3034 RtlCopyMemory(pb, pName->Buffer, pName->Length);
3035 *(WCHAR *)&pb[pName->Length] = UNICODE_NULL;
3036 return pName->Length + sizeof(UNICODE_NULL);
3037}
3038
3039static __inline BYTE *FASTCALL
3041{
3042 BYTE *pb = (BYTE *)Otm + sizeof(OUTLINETEXTMETRICW);
3043
3044 /* family name */
3045 Otm->otmpFamilyName = (LPSTR)(pb - (BYTE*) Otm);
3046 pb += IntStoreName(&Names->FamilyNameW, pb);
3047
3048 /* face name */
3049 Otm->otmpFaceName = (LPSTR)(pb - (BYTE*) Otm);
3050 pb += IntStoreName(&Names->FaceNameW, pb);
3051
3052 /* style name */
3053 Otm->otmpStyleName = (LPSTR)(pb - (BYTE*) Otm);
3054 pb += IntStoreName(&Names->StyleNameW, pb);
3055
3056 /* unique name (full name) */
3057 Otm->otmpFullName = (LPSTR)(pb - (BYTE*) Otm);
3058 pb += IntStoreName(&Names->FullNameW, pb);
3059
3060 return pb;
3061}
3062
3063static __inline void FASTCALL
3065{
3066 RtlFreeUnicodeString(&Names->FamilyNameW);
3067 RtlFreeUnicodeString(&Names->FaceNameW);
3068 RtlFreeUnicodeString(&Names->StyleNameW);
3069 RtlFreeUnicodeString(&Names->FullNameW);
3070}
3071
3072/*************************************************************
3073 * IntGetOutlineTextMetrics
3074 *
3075 */
3078 UINT Size,
3079 OUTLINETEXTMETRICW *Otm,
3080 BOOL bLocked)
3081{
3082 TT_OS2 *pOS2;
3083 TT_HoriHeader *pHori;
3084 TT_Postscript *pPost;
3085 FT_Fixed XScale, YScale;
3086 FT_WinFNT_HeaderRec WinFNT;
3088 BYTE *pb;
3089 FONT_NAMES FontNames;
3090 PSHARED_FACE SharedFace = FontGDI->SharedFace;
3092 FT_Face Face = SharedFace->Face;
3093
3094 if (bLocked)
3096 else
3098
3100 {
3101 Cache = &SharedFace->EnglishUS;
3102 }
3103 else
3104 {
3105 Cache = &SharedFace->UserLanguage;
3106 }
3107
3108 if (Size == 0 && Cache->OutlineRequiredSize > 0)
3109 {
3110 ASSERT(Otm == NULL);
3111 return Cache->OutlineRequiredSize;
3112 }
3113
3114 if (!bLocked)
3116
3117 IntInitFontNames(&FontNames, SharedFace);
3118 Cache->OutlineRequiredSize = FontNames.OtmSize;
3119
3120 if (Size == 0)
3121 {
3122 ASSERT(Otm == NULL);
3123 IntFreeFontNames(&FontNames);
3124 if (!bLocked)
3126 return Cache->OutlineRequiredSize;
3127 }
3128
3129 ASSERT(Otm != NULL);
3130
3131 if (Size < Cache->OutlineRequiredSize)
3132 {
3133 DPRINT1("Size %u < OutlineRequiredSize %u\n", Size,
3134 Cache->OutlineRequiredSize);
3135 IntFreeFontNames(&FontNames);
3136 if (!bLocked)
3138 return 0; /* failure */
3139 }
3140
3141 XScale = Face->size->metrics.x_scale;
3142 YScale = Face->size->metrics.y_scale;
3143
3144 pOS2 = FT_Get_Sfnt_Table(Face, FT_SFNT_OS2);
3145 pHori = FT_Get_Sfnt_Table(Face, FT_SFNT_HHEA);
3146 pPost = FT_Get_Sfnt_Table(Face, FT_SFNT_POST); /* We can live with this failing */
3147 Error = FT_Get_WinFNT_Header(Face, &WinFNT);
3148
3149 if (pOS2 == NULL && Error)
3150 {
3151 if (!bLocked)
3153 DPRINT1("Can't find OS/2 table - not TT font?\n");
3154 IntFreeFontNames(&FontNames);
3155 return 0;
3156 }
3157
3158 if (pHori == NULL && Error)
3159 {
3160 if (!bLocked)
3162 DPRINT1("Can't find HHEA table - not TT font?\n");
3163 IntFreeFontNames(&FontNames);
3164 return 0;
3165 }
3166
3167 Otm->otmSize = Cache->OutlineRequiredSize;
3168
3169 FillTM(&Otm->otmTextMetrics, FontGDI, pOS2, pHori, (Error ? NULL : &WinFNT));
3170
3171 if (!pOS2)
3172 goto skip_os2;
3173
3174 Otm->otmFiller = 0;
3176 Otm->otmfsSelection = pOS2->fsSelection;
3177 Otm->otmfsType = pOS2->fsType;
3178 Otm->otmsCharSlopeRise = pHori->caret_Slope_Rise;
3179 Otm->otmsCharSlopeRun = pHori->caret_Slope_Run;
3180 Otm->otmItalicAngle = 0; /* POST table */
3181 Otm->otmEMSquare = Face->units_per_EM;
3182
3183#define SCALE_X(value) ((FT_MulFix((value), XScale) + 32) >> 6)
3184#define SCALE_Y(value) ((FT_MulFix((value), YScale) + 32) >> 6)
3185
3186 Otm->otmAscent = SCALE_Y(pOS2->sTypoAscender);
3187 Otm->otmDescent = SCALE_Y(pOS2->sTypoDescender);
3188 Otm->otmLineGap = SCALE_Y(pOS2->sTypoLineGap);
3189 Otm->otmsCapEmHeight = SCALE_Y(pOS2->sCapHeight);
3190 Otm->otmsXHeight = SCALE_Y(pOS2->sxHeight);
3191 Otm->otmrcFontBox.left = SCALE_X(Face->bbox.xMin);
3192 Otm->otmrcFontBox.right = SCALE_X(Face->bbox.xMax);
3193 Otm->otmrcFontBox.top = SCALE_Y(Face->bbox.yMax);
3194 Otm->otmrcFontBox.bottom = SCALE_Y(Face->bbox.yMin);
3197 Otm->otmMacLineGap = Otm->otmLineGap;
3198 Otm->otmusMinimumPPEM = 0; /* TT Header */
3209
3210 if (!pPost)
3211 {
3212 Otm->otmsUnderscoreSize = 0;
3213 Otm->otmsUnderscorePosition = 0;
3214 }
3215 else
3216 {
3219 }
3220
3221#undef SCALE_X
3222#undef SCALE_Y
3223
3224skip_os2:
3225 if (!bLocked)
3227
3228 pb = IntStoreFontNames(&FontNames, Otm);
3229 ASSERT(pb - (BYTE*)Otm == Cache->OutlineRequiredSize);
3230
3231 IntFreeFontNames(&FontNames);
3232
3233 return Cache->OutlineRequiredSize;
3234}
3235
3236/* See https://msdn.microsoft.com/en-us/library/bb165625(v=vs.90).aspx */
3237static BYTE
3239{
3240 /* FIXME: Add more and fix if wrong */
3241 switch (PRIMARYLANGID(LangID))
3242 {
3243 case LANG_CHINESE:
3244 switch (SUBLANGID(LangID))
3245 {
3247 return CHINESEBIG5_CHARSET;
3249 default:
3250 break;
3251 }
3252 return GB2312_CHARSET;
3253
3254 case LANG_CZECH: case LANG_HUNGARIAN: case LANG_POLISH:
3255 case LANG_SLOVAK: case LANG_SLOVENIAN: case LANG_ROMANIAN:
3256 return EASTEUROPE_CHARSET;
3257
3259 case LANG_SERBIAN: case LANG_UKRAINIAN:
3260 return RUSSIAN_CHARSET;
3261
3262 case LANG_ARABIC: return ARABIC_CHARSET;
3263 case LANG_GREEK: return GREEK_CHARSET;
3264 case LANG_HEBREW: return HEBREW_CHARSET;
3265 case LANG_JAPANESE: return SHIFTJIS_CHARSET;
3266 case LANG_KOREAN: return JOHAB_CHARSET;
3267 case LANG_TURKISH: return TURKISH_CHARSET;
3268 case LANG_THAI: return THAI_CHARSET;
3269 case LANG_LATVIAN: return BALTIC_CHARSET;
3271
3272 case LANG_ENGLISH: case LANG_BASQUE: case LANG_CATALAN:
3273 case LANG_DANISH: case LANG_DUTCH: case LANG_FINNISH:
3274 case LANG_FRENCH: case LANG_GERMAN: case LANG_ITALIAN:
3276 case LANG_SWEDISH: default:
3277 return ANSI_CHARSET;
3278 }
3279}
3280
3281static void
3283{
3284 BYTE b, *pb = pvData;
3285 Size /= 2;
3286 while (Size-- > 0)
3287 {
3288 b = pb[0];
3289 pb[0] = pb[1];
3290 pb[1] = b;
3291 ++pb; ++pb;
3292 }
3293}
3294
3295static NTSTATUS
3297 FT_UShort NameID, FT_UShort LangID)
3298{
3300 INT i, Count, BestIndex, Score, BestScore;
3303 ANSI_STRING AnsiName;
3305 FT_Face Face = SharedFace->Face;
3306
3307 RtlFreeUnicodeString(pNameW);
3308
3309 /* select cache */
3311 {
3312 Cache = &SharedFace->EnglishUS;
3313 }
3314 else
3315 {
3316 Cache = &SharedFace->UserLanguage;
3317 }
3318
3319 /* use cache if available */
3320 if (NameID == TT_NAME_ID_FONT_FAMILY && Cache->FontFamily.Buffer)
3321 {
3322 return DuplicateUnicodeString(&Cache->FontFamily, pNameW);
3323 }
3324 if (NameID == TT_NAME_ID_FULL_NAME && Cache->FullName.Buffer)
3325 {
3326 return DuplicateUnicodeString(&Cache->FullName, pNameW);
3327 }
3328
3329 BestIndex = -1;
3330 BestScore = 0;
3331
3333 for (i = 0; i < Count; ++i)
3334 {
3335 Error = FT_Get_Sfnt_Name(Face, i, &Name);
3336 if (Error)
3337 {
3338 continue; /* failure */
3339 }
3340
3341 if (Name.name_id != NameID)
3342 {
3343 continue; /* mismatched */
3344 }
3345
3346 if (Name.platform_id != TT_PLATFORM_MICROSOFT ||
3347 (Name.encoding_id != TT_MS_ID_UNICODE_CS &&
3348 Name.encoding_id != TT_MS_ID_SYMBOL_CS))
3349 {
3350 continue; /* not Microsoft Unicode name */
3351 }
3352
3353 if (Name.string == NULL || Name.string_len == 0 ||
3354 (Name.string[0] == 0 && Name.string[1] == 0))
3355 {
3356 continue; /* invalid string */
3357 }
3358
3359 if (Name.language_id == LangID)
3360 {
3361 Score = 30;
3362 BestIndex = i;
3363 break; /* best match */
3364 }
3365 else if (PRIMARYLANGID(Name.language_id) == PRIMARYLANGID(LangID))
3366 {
3367 Score = 20;
3368 }
3369 else if (PRIMARYLANGID(Name.language_id) == LANG_ENGLISH)
3370 {
3371 Score = 10;
3372 }
3373 else
3374 {
3375 Score = 0;
3376 }
3377
3378 if (Score > BestScore)
3379 {
3380 BestScore = Score;
3381 BestIndex = i;
3382 }
3383 }
3384
3385 if (BestIndex >= 0)
3386 {
3387 /* store the best name */
3388 Error = (Score == 30) ? 0 : FT_Get_Sfnt_Name(Face, BestIndex, &Name);
3389 if (!Error)
3390 {
3391 /* NOTE: Name.string is not null-terminated */
3392 UNICODE_STRING Tmp;
3393 Tmp.Buffer = (PWCH)Name.string;
3394 Tmp.Length = Tmp.MaximumLength = Name.string_len;
3395
3396 pNameW->Length = 0;
3397 pNameW->MaximumLength = Name.string_len + sizeof(WCHAR);
3399
3400 if (pNameW->Buffer)
3401 {
3402 Status = RtlAppendUnicodeStringToString(pNameW, &Tmp);
3403 if (Status == STATUS_SUCCESS)
3404 {
3405 /* Convert UTF-16 big endian to little endian */
3406 SwapEndian(pNameW->Buffer, pNameW->Length);
3407 }
3408 }
3409 else
3410 {
3412 }
3413 }
3414 }
3415
3416 if (!NT_SUCCESS(Status))
3417 {
3418 /* defaulted */
3419 if (NameID == TT_NAME_ID_FONT_SUBFAMILY)
3420 {
3421 RtlInitAnsiString(&AnsiName, Face->style_name);
3422 Status = RtlAnsiStringToUnicodeString(pNameW, &AnsiName, TRUE);
3423 }
3424 else
3425 {
3426 RtlInitAnsiString(&AnsiName, Face->family_name);
3427 Status = RtlAnsiStringToUnicodeString(pNameW, &AnsiName, TRUE);
3428 }
3429 }
3430
3431 if (NT_SUCCESS(Status))
3432 {
3433 /* make cache */
3434 if (NameID == TT_NAME_ID_FONT_FAMILY)
3435 {
3437 if (!Cache->FontFamily.Buffer)
3438 DuplicateUnicodeString(pNameW, &Cache->FontFamily);
3439 }
3440 else if (NameID == TT_NAME_ID_FULL_NAME)
3441 {
3443 if (!Cache->FullName.Buffer)
3444 DuplicateUnicodeString(pNameW, &Cache->FullName);
3445 }
3446 }
3447
3448 return Status;
3449}
3450
3451static void FASTCALL
3453 LPCWSTR FullName, PFONTGDI FontGDI)
3454{
3455 ANSI_STRING StyleA;
3456 UNICODE_STRING StyleW;
3457 TT_OS2 *pOS2;
3459 CHARSETINFO CharSetInfo;
3460 unsigned i, Size;
3461 OUTLINETEXTMETRICW *Otm;
3462 LOGFONTW *Lf;
3463 TEXTMETRICW *TM;
3464 NEWTEXTMETRICW *Ntm;
3465 DWORD fs0;
3467 PSHARED_FACE SharedFace = FontGDI->SharedFace;
3468 FT_Face Face = SharedFace->Face;
3469 UNICODE_STRING NameW;
3470
3471 RtlInitUnicodeString(&NameW, NULL);
3474 Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL, TRUE);
3475 if (!Size)
3476 return;
3478 if (!Otm)
3479 return;
3481 Size = IntGetOutlineTextMetrics(FontGDI, Size, Otm, TRUE);
3482 if (!Size)
3483 {
3485 return;
3486 }
3487
3488 Lf = &Info->EnumLogFontEx.elfLogFont;
3489 TM = &Otm->otmTextMetrics;
3490
3491 Lf->lfHeight = TM->tmHeight;
3492 Lf->lfWidth = TM->tmAveCharWidth;
3493 Lf->lfWeight = TM->tmWeight;
3494 Lf->lfItalic = TM->tmItalic;
3495 Lf->lfPitchAndFamily = (TM->tmPitchAndFamily & 0xf1) + 1;
3496 Lf->lfCharSet = TM->tmCharSet;
3500
3501 Ntm = &Info->NewTextMetricEx.ntmTm;
3502 Ntm->tmHeight = TM->tmHeight;
3503 Ntm->tmAscent = TM->tmAscent;
3504 Ntm->tmDescent = TM->tmDescent;
3507 Ntm->tmAveCharWidth = TM->tmAveCharWidth;
3508 Ntm->tmMaxCharWidth = TM->tmMaxCharWidth;
3509 Ntm->tmWeight = TM->tmWeight;
3510 Ntm->tmOverhang = TM->tmOverhang;
3513 Ntm->tmFirstChar = TM->tmFirstChar;
3514 Ntm->tmLastChar = TM->tmLastChar;
3515 Ntm->tmDefaultChar = TM->tmDefaultChar;
3516 Ntm->tmBreakChar = TM->tmBreakChar;
3517 Ntm->tmItalic = TM->tmItalic;
3518 Ntm->tmUnderlined = TM->tmUnderlined;
3519 Ntm->tmStruckOut = TM->tmStruckOut;
3521 Ntm->tmCharSet = TM->tmCharSet;
3522 Ntm->ntmFlags = TM->tmItalic ? NTM_ITALIC : 0;
3523
3524 if (550 < TM->tmWeight) Ntm->ntmFlags |= NTM_BOLD;
3525
3526 if (0 == Ntm->ntmFlags) Ntm->ntmFlags = NTM_REGULAR;
3527
3528 Info->FontType = (0 != (TM->tmPitchAndFamily & TMPF_TRUETYPE)
3529 ? TRUETYPE_FONTTYPE : 0);
3530
3531 if (0 == (TM->tmPitchAndFamily & TMPF_VECTOR))
3532 Info->FontType |= RASTER_FONTTYPE;
3533
3534
3535 /* face name */
3536 if (!FaceName)
3537 FaceName = (WCHAR*)((ULONG_PTR)Otm + (ULONG_PTR)Otm->otmpFamilyName);
3538
3539 RtlStringCbCopyW(Lf->lfFaceName, sizeof(Lf->lfFaceName), FaceName);
3540
3541 /* full name */
3542 if (!FullName)
3543 FullName = (WCHAR*)((ULONG_PTR) Otm + (ULONG_PTR)Otm->otmpFaceName);
3544
3545 RtlStringCbCopyW(Info->EnumLogFontEx.elfFullName,
3546 sizeof(Info->EnumLogFontEx.elfFullName),
3547 FullName);
3548
3549 RtlInitAnsiString(&StyleA, Face->style_name);
3550 StyleW.Buffer = Info->EnumLogFontEx.elfStyle;
3551 StyleW.MaximumLength = sizeof(Info->EnumLogFontEx.elfStyle);
3552 status = RtlAnsiStringToUnicodeString(&StyleW, &StyleA, FALSE);
3553 if (!NT_SUCCESS(status))
3554 {
3556 return;
3557 }
3558 Info->EnumLogFontEx.elfScript[0] = UNICODE_NULL;
3559
3560 pOS2 = FT_Get_Sfnt_Table(Face, ft_sfnt_os2);
3561
3562 if (!pOS2)
3563 {
3565 return;
3566 }
3567
3568 Ntm->ntmSizeEM = Otm->otmEMSquare;
3570 Ntm->ntmAvgWidth = 0;
3571
3573
3574 fs.fsCsb[0] = pOS2->ulCodePageRange1;
3575 fs.fsCsb[1] = pOS2->ulCodePageRange2;
3576 fs.fsUsb[0] = pOS2->ulUnicodeRange1;
3577 fs.fsUsb[1] = pOS2->ulUnicodeRange2;
3578 fs.fsUsb[2] = pOS2->ulUnicodeRange3;
3579 fs.fsUsb[3] = pOS2->ulUnicodeRange4;
3580
3581 if (0 == pOS2->version)
3582 {
3583 FT_UInt Dummy;
3584
3585 if (FT_Get_First_Char(Face, &Dummy) < 0x100)
3586 fs.fsCsb[0] |= FS_LATIN1;
3587 else
3588 fs.fsCsb[0] |= FS_SYMBOL;
3589 }
3590
3591 if (fs.fsCsb[0] == 0)
3592 {
3593 /* Let's see if we can find any interesting cmaps */
3594 for (i = 0; i < (UINT)Face->num_charmaps; i++)
3595 {
3596 switch (Face->charmaps[i]->encoding)
3597 {
3598 case FT_ENCODING_UNICODE:
3599 case FT_ENCODING_APPLE_ROMAN:
3600 fs.fsCsb[0] |= FS_LATIN1;
3601 break;
3602 case FT_ENCODING_MS_SYMBOL:
3603 fs.fsCsb[0] |= FS_SYMBOL;
3604 break;
3605 default:
3606 break;
3607 }
3608 }
3609 }
3610
3611 for (i = 0; i < MAXTCIINDEX; i++)
3612 {
3613 fs0 = 1L << i;
3614 if (fs.fsCsb[0] & fs0)
3615 {
3616 if (!IntTranslateCharsetInfo(&fs0, &CharSetInfo, TCI_SRCFONTSIG))
3617 {
3618 CharSetInfo.ciCharset = DEFAULT_CHARSET;
3619 }
3620 if (DEFAULT_CHARSET != CharSetInfo.ciCharset)
3621 {
3622 if (g_ElfScripts[i])
3623 wcscpy(Info->EnumLogFontEx.elfScript, g_ElfScripts[i]);
3624 else
3625 {
3626 DPRINT1("Unknown elfscript for bit %u\n", i);
3627 }
3628 }
3629 }
3630 }
3631 Info->NewTextMetricEx.ntmFontSig = fs;
3632}
3633
3634static BOOLEAN FASTCALL
3637 LPCWSTR NominalName,
3638 LONG *pCount,
3639 LONG MaxCount,
3640 PLIST_ENTRY Head)
3641{
3643 PFONT_ENTRY CurrentEntry;
3644 FONTGDI *FontGDI;
3645 FONTFAMILYINFO InfoEntry;
3646 LONG Count = *pCount;
3647
3648 for (Entry = Head->Flink; Entry != Head; Entry = Entry->Flink)
3649 {
3650 CurrentEntry = CONTAINING_RECORD(Entry, FONT_ENTRY, ListEntry);
3651 FontGDI = CurrentEntry->Font;
3652 ASSERT(FontGDI);
3653
3654 if (LogFont->lfCharSet != DEFAULT_CHARSET &&
3655 LogFont->lfCharSet != FontGDI->CharSet)
3656 {
3657 continue; /* charset mismatch */
3658 }
3659
3660 /* get one info entry */
3661 FontFamilyFillInfo(&InfoEntry, NULL, NULL, FontGDI);
3662
3663 if (LogFont->lfFaceName[0] != UNICODE_NULL)
3664 {
3665 /* check name */
3666 if (_wcsnicmp(LogFont->lfFaceName,
3668 RTL_NUMBER_OF(LogFont->lfFaceName) - 1) != 0 &&
3669 _wcsnicmp(LogFont->lfFaceName,
3670 InfoEntry.EnumLogFontEx.elfFullName,
3671 RTL_NUMBER_OF(LogFont->lfFaceName) - 1) != 0)
3672 {
3673 continue;
3674 }
3675 }
3676
3677 if (NominalName)
3678 {
3679 /* store the nominal name */
3681 sizeof(InfoEntry.EnumLogFontEx.elfLogFont.lfFaceName),
3682 NominalName);
3683 }
3684
3685 /* store one entry to Info */
3686 if (0 <= Count && Count < MaxCount)
3687 {
3688 RtlCopyMemory(&Info[Count], &InfoEntry, sizeof(InfoEntry));
3689 }
3690 Count++;
3691 }
3692
3693 *pCount = Count;
3694
3695 return TRUE;
3696}
3697
3698static BOOLEAN FASTCALL
3701 LONG *pCount,
3702 LONG MaxCount)
3703{
3704 PLIST_ENTRY pEntry, pHead = &g_FontSubstListHead;
3705 PFONTSUBST_ENTRY pCurrentEntry;
3706 PUNICODE_STRING pFromW, pToW;
3707 LOGFONTW lf = *LogFont;
3709
3710 for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink)
3711 {
3712 pCurrentEntry = CONTAINING_RECORD(pEntry, FONTSUBST_ENTRY, ListEntry);
3713
3714 pFromW = &pCurrentEntry->FontNames[FONTSUBST_FROM];
3715 if (LogFont->lfFaceName[0] != UNICODE_NULL)
3716 {
3717 /* check name */
3718 if (_wcsicmp(LogFont->lfFaceName, pFromW->Buffer) != 0)
3719 continue; /* mismatch */
3720 }
3721
3722 pToW = &pCurrentEntry->FontNames[FONTSUBST_TO];
3723 if (RtlEqualUnicodeString(pFromW, pToW, TRUE) &&
3724 pCurrentEntry->CharSets[FONTSUBST_FROM] ==
3725 pCurrentEntry->CharSets[FONTSUBST_TO])
3726 {
3727 /* identical mapping */
3728 continue;
3729 }
3730
3731 /* substitute and get the real name */
3732 IntUnicodeStringToBuffer(lf.lfFaceName, sizeof(lf.lfFaceName), pFromW);
3734 if (LogFont->lfCharSet != DEFAULT_CHARSET && LogFont->lfCharSet != lf.lfCharSet)
3735 continue;
3736
3737 /* search in global fonts */
3739 GetFontFamilyInfoForList(&lf, Info, pFromW->Buffer, pCount, MaxCount, &g_FontListHead);
3740
3741 /* search in private fonts */
3742 IntLockProcessPrivateFonts(Win32Process);
3743 GetFontFamilyInfoForList(&lf, Info, pFromW->Buffer, pCount, MaxCount,
3744 &Win32Process->PrivateFontListHead);
3745 IntUnLockProcessPrivateFonts(Win32Process);
3747
3748 if (LogFont->lfFaceName[0] != UNICODE_NULL)
3749 {
3750 /* it's already matched to the exact name and charset if the name
3751 was specified at here, then so don't scan more for another name */
3752 break;
3753 }
3754 }
3755
3756 return TRUE;
3757}
3758
3759BOOL
3762{
3763 if ( lprs )
3764 {
3765 lprs->nSize = sizeof(RASTERIZER_STATUS);
3766 lprs->wFlags = TT_AVAILABLE | TT_ENABLED;
3767 lprs->nLanguageID = gusLanguageID;
3768 return TRUE;
3769 }
3771 return FALSE;
3772}
3773
3774static DWORD
3776{
3777 DWORD dwHash = cdw;
3778 const DWORD *pdw = pv;
3779
3780 while (cdw-- > 0)
3781 {
3782 dwHash *= 3;
3783 dwHash ^= *pdw++;
3784 }
3785
3786 return dwHash;
3787}
3788
3789static FT_BitmapGlyph
3791{
3792 PLIST_ENTRY CurrentEntry;
3793 PFONT_CACHE_ENTRY FontEntry;
3794 DWORD dwHash = pCache->dwHash;
3795
3797
3798 for (CurrentEntry = g_FontCacheListHead.Flink;
3799 CurrentEntry != &g_FontCacheListHead;
3800 CurrentEntry = CurrentEntry->Flink)
3801 {
3802 FontEntry = CONTAINING_RECORD(CurrentEntry, FONT_CACHE_ENTRY, ListEntry);
3803 if (FontEntry->dwHash == dwHash &&
3804 FontEntry->Hashed.GlyphIndex == pCache->Hashed.GlyphIndex &&
3805 FontEntry->Hashed.Face == pCache->Hashed.Face &&
3806 FontEntry->Hashed.lfHeight == pCache->Hashed.lfHeight &&
3807 FontEntry->Hashed.lfWidth == pCache->Hashed.lfWidth &&
3808 FontEntry->Hashed.AspectValue == pCache->Hashed.AspectValue &&
3809 memcmp(&FontEntry->Hashed.matTransform, &pCache->Hashed.matTransform,
3810 sizeof(FT_Matrix)) == 0)
3811 {
3812 break;
3813 }
3814 }
3815
3816 if (CurrentEntry == &g_FontCacheListHead)
3817 {
3818 return NULL;
3819 }
3820
3821 RemoveEntryList(CurrentEntry);
3822 InsertHeadList(&g_FontCacheListHead, CurrentEntry);
3823 return FontEntry->BitmapGlyph;
3824}
3825
3826static FT_BitmapGlyph
3829 IN FT_GlyphSlot GlyphSlot)
3830{
3831 FT_Glyph GlyphCopy;
3832 INT error;
3833 PFONT_CACHE_ENTRY NewEntry;
3834 FT_Bitmap AlignedBitmap;
3835 FT_BitmapGlyph BitmapGlyph;
3836
3838
3839 error = FT_Get_Glyph(GlyphSlot, &GlyphCopy);
3840 if (error)
3841 {
3842 DPRINT1("Failure caching glyph.\n");
3843 return NULL;
3844 };
3845
3846 error = FT_Glyph_To_Bitmap(&GlyphCopy, Cache->Hashed.Aspect.RenderMode, 0, 1);
3847 if (error)
3848 {
3849 FT_Done_Glyph(GlyphCopy);
3850 DPRINT1("Failure rendering glyph.\n");
3851 return NULL;
3852 };
3853
3855 if (!NewEntry)
3856 {
3857 DPRINT1("Alloc failure caching glyph.\n");
3858 FT_Done_Glyph(GlyphCopy);
3859 return NULL;
3860 }
3861
3862 BitmapGlyph = (FT_BitmapGlyph)GlyphCopy;
3863 FT_Bitmap_New(&AlignedBitmap);
3864 if (FT_Bitmap_Convert_ReactOS_Hack(GlyphSlot->library, &BitmapGlyph->bitmap,
3865 &AlignedBitmap, 4, TRUE))
3866 {
3867 DPRINT1("Conversion failed\n");
3868 ExFreePoolWithTag(NewEntry, TAG_FONT);
3869 FT_Bitmap_Done(GlyphSlot->library, &AlignedBitmap);
3870 FT_Done_Glyph((FT_Glyph)BitmapGlyph);
3871 return NULL;
3872 }
3873
3874 FT_Bitmap_Done(GlyphSlot->library, &BitmapGlyph->bitmap);
3875 BitmapGlyph->bitmap = AlignedBitmap;
3876
3877 NewEntry->BitmapGlyph = BitmapGlyph;
3878 NewEntry->dwHash = Cache->dwHash;
3879 NewEntry->Hashed = Cache->Hashed;
3880
3881 InsertHeadList(&g_FontCacheListHead, &NewEntry->ListEntry);
3883 {
3884 NewEntry = CONTAINING_RECORD(g_FontCacheListHead.Blink, FONT_CACHE_ENTRY, ListEntry);
3885 RemoveCachedEntry(NewEntry);
3886 }
3887
3888 return BitmapGlyph;
3889}
3890
3891
3892static unsigned int get_native_glyph_outline(FT_Outline *outline, unsigned int buflen, char *buf)
3893{
3894 TTPOLYGONHEADER *pph;
3895 TTPOLYCURVE *ppc;
3896 int needed = 0, point = 0, contour, first_pt;
3897 unsigned int pph_start, cpfx;
3898 DWORD type;
3899
3900 for (contour = 0; contour < outline->n_contours; contour++)
3901 {
3902 /* Ignore contours containing one point */
3903 if (point == outline->contours[contour])
3904 {
3905 point++;
3906 continue;
3907 }
3908
3909 pph_start = needed;
3910 pph = (TTPOLYGONHEADER *)(buf + needed);
3911 first_pt = point;
3912 if (buf)
3913 {
3914 pph->dwType = TT_POLYGON_TYPE;
3915 FTVectorToPOINTFX(&outline->points[point], &pph->pfxStart);
3916 }
3917 needed += sizeof(*pph);
3918 point++;
3919 while (point <= outline->contours[contour])
3920 {
3921 ppc = (TTPOLYCURVE *)(buf + needed);
3922 type = (outline->tags[point] & FT_Curve_Tag_On) ?
3924 cpfx = 0;
3925 do
3926 {
3927 if (buf)
3928 FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
3929 cpfx++;
3930 point++;
3931 } while (point <= outline->contours[contour] &&
3932 (outline->tags[point] & FT_Curve_Tag_On) ==
3933 (outline->tags[point-1] & FT_Curve_Tag_On));
3934 /* At the end of a contour Windows adds the start point, but
3935 only for Beziers */
3936 if (point > outline->contours[contour] &&
3937 !(outline->tags[point-1] & FT_Curve_Tag_On))
3938 {
3939 if (buf)
3940 FTVectorToPOINTFX(&outline->points[first_pt], &ppc->apfx[cpfx]);
3941 cpfx++;
3942 }
3943 else if (point <= outline->contours[contour] &&
3944 outline->tags[point] & FT_Curve_Tag_On)
3945 {
3946 /* add closing pt for bezier */
3947 if (buf)
3948 FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
3949 cpfx++;
3950 point++;
3951 }
3952 if (buf)
3953 {
3954 ppc->wType = type;
3955 ppc->cpfx = cpfx;
3956 }
3957 needed += sizeof(*ppc) + (cpfx - 1) * sizeof(POINTFX);
3958 }
3959 if (buf)
3960 pph->cb = needed - pph_start;
3961 }
3962 return needed;
3963}
3964
3965static unsigned int get_bezier_glyph_outline(FT_Outline *outline, unsigned int buflen, char *buf)
3966{
3967 /* Convert the quadratic Beziers to cubic Beziers.
3968 The parametric eqn for a cubic Bezier is, from PLRM:
3969 r(t) = at^3 + bt^2 + ct + r0
3970 with the control points:
3971 r1 = r0 + c/3
3972 r2 = r1 + (c + b)/3
3973 r3 = r0 + c + b + a
3974
3975 A quadratic Bezier has the form:
3976 p(t) = (1-t)^2 p0 + 2(1-t)t p1 + t^2 p2
3977
3978 So equating powers of t leads to:
3979 r1 = 2/3 p1 + 1/3 p0
3980 r2 = 2/3 p1 + 1/3 p2
3981 and of course r0 = p0, r3 = p2
3982 */
3983 int contour, point = 0, first_pt;
3984 TTPOLYGONHEADER *pph;
3985 TTPOLYCURVE *ppc;
3986 DWORD pph_start, cpfx, type;
3987 FT_Vector cubic_control[4];
3988 unsigned int needed = 0;
3989
3990 for (contour = 0; contour < outline->n_contours; contour++)
3991 {
3992 pph_start = needed;
3993 pph = (TTPOLYGONHEADER *)(buf + needed);
3994 first_pt = point;
3995 if (buf)
3996 {
3997 pph->dwType = TT_POLYGON_TYPE;
3998 FTVectorToPOINTFX(&outline->points[point], &pph->pfxStart);
3999 }
4000 needed += sizeof(*pph);
4001 point++;
4002 while (point <= outline->contours[contour])
4003 {
4004 ppc = (TTPOLYCURVE *)(buf + needed);
4005 type = (outline->tags[point] & FT_Curve_Tag_On) ?
4007 cpfx = 0;
4008 do
4009 {
4010 if (type == TT_PRIM_LINE)
4011 {
4012 if (buf)
4013 FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
4014 cpfx++;
4015 point++;
4016 }
4017 else
4018 {
4019 /* Unlike QSPLINEs, CSPLINEs always have their endpoint
4020 so cpfx = 3n */
4021
4022 /* FIXME: Possible optimization in endpoint calculation
4023 if there are two consecutive curves */
4024 cubic_control[0] = outline->points[point-1];
4025 if (!(outline->tags[point-1] & FT_Curve_Tag_On))
4026 {
4027 cubic_control[0].x += outline->points[point].x + 1;
4028 cubic_control[0].y += outline->points[point].y + 1;
4029 cubic_control[0].x >>= 1;
4030 cubic_control[0].y >>= 1;
4031 }
4032 if (point+1 > outline->contours[contour])
4033 cubic_control[3] = outline->points[first_pt];
4034 else
4035 {
4036 cubic_control[3] = outline->points[point+1];
4037 if (!(outline->tags[point+1] & FT_Curve_Tag_On))
4038 {
4039 cubic_control[3].x += outline->points[point].x + 1;
4040 cubic_control[3].y += outline->points[point].y + 1;
4041 cubic_control[3].x >>= 1;
4042 cubic_control[3].y >>= 1;
4043 }
4044 }
4045 /* r1 = 1/3 p0 + 2/3 p1
4046 r2 = 1/3 p2 + 2/3 p1 */
4047 cubic_control[1].x = (2 * outline->points[point].x + 1) / 3;
4048 cubic_control[1].y = (2 * outline->points[point].y + 1) / 3;
4049 cubic_control[2] = cubic_control[1];
4050 cubic_control[1].x += (cubic_control[0].x + 1) / 3;
4051 cubic_control[1].y += (cubic_control[0].y + 1) / 3;
4052 cubic_control[2].x += (cubic_control[3].x + 1) / 3;
4053 cubic_control[2].y += (cubic_control[3].y + 1) / 3;
4054 if (buf)
4055 {
4056 FTVectorToPOINTFX(&cubic_control[1], &ppc->apfx[cpfx]);
4057 FTVectorToPOINTFX(&cubic_control[2], &ppc->apfx[cpfx+1]);
4058 FTVectorToPOINTFX(&cubic_control[3], &ppc->apfx[cpfx+2]);
4059 }
4060 cpfx += 3;
4061 point++;
4062 }
4063 } while (point <= outline->contours[contour] &&
4064 (outline->tags[point] & FT_Curve_Tag_On) ==
4065 (outline->tags[point-1] & FT_Curve_Tag_On));
4066 /* At the end of a contour Windows adds the start point,
4067 but only for Beziers and we've already done that.
4068 */
4069 if (point <= outline->contours[contour] &&
4070 outline->tags[point] & FT_Curve_Tag_On)
4071 {
4072 /* This is the closing pt of a bezier, but we've already
4073 added it, so just inc point and carry on */
4074 point++;
4075 }
4076 if (buf)
4077 {
4078 ppc->wType = type;
4079 ppc->cpfx = cpfx;
4080 }
4081 needed += sizeof(*ppc) + (cpfx - 1) * sizeof(POINTFX);
4082 }
4083 if (buf)
4084 pph->cb = needed - pph_start;
4085 }
4086 return needed;
4087}
4088
4089static FT_Error
4090IntRequestFontSize(PDC dc, PFONTGDI FontGDI, LONG lfWidth, LONG lfHeight)
4091{
4094 FT_Face face = FontGDI->SharedFace->Face;
4095 TT_OS2 *pOS2;
4096 TT_HoriHeader *pHori;
4097 FT_WinFNT_HeaderRec WinFNT;
4098 LONG Ascent, Descent, Sum, EmHeight, Width64;
4099
4100 lfWidth = abs(lfWidth);
4101 if (lfHeight == 0)
4102 {
4103 if (lfWidth == 0)
4104 {
4105 DPRINT("lfHeight and lfWidth are zero.\n");
4106 lfHeight = -16;
4107 }
4108 else
4109 {
4110 lfHeight = lfWidth;
4111 }
4112 }
4113
4114 if (lfHeight == -1)
4115 lfHeight = -2;
4116
4117 if (FontGDI->Magic == FONTGDI_MAGIC &&
4118 FontGDI->lfHeight == lfHeight &&
4119 FontGDI->lfWidth == lfWidth)
4120 {
4121 return 0; /* Cached */
4122 }
4123
4127
4128 if (!pOS2 || !pHori)
4129 {
4130 error = FT_Get_WinFNT_Header(face, &WinFNT);
4131 if (error)
4132 {
4133 DPRINT1("%s: Failed to request font size.\n", face->family_name);
4134 ASSERT(FALSE);
4135 return error;
4136 }
4137
4138 FontGDI->tmHeight = WinFNT.pixel_height;
4139 FontGDI->tmAscent = WinFNT.ascent;
4140 FontGDI->tmDescent = FontGDI->tmHeight - FontGDI->tmAscent;
4141 FontGDI->tmInternalLeading = WinFNT.internal_leading;
4142 FontGDI->Magic = FONTGDI_MAGIC;
4143 FontGDI->lfHeight = lfHeight;
4144 FontGDI->lfWidth = lfWidth;
4145 return 0;
4146 }
4147
4148 /*
4149 * NOTE: We cast TT_OS2.usWinAscent and TT_OS2.usWinDescent to signed FT_Short.
4150 * Why? See: https://docs.microsoft.com/en-us/typography/opentype/spec/os2#uswindescent
4151 *
4152 * > usWinDescent is "usually" a positive value ...
4153 *
4154 * We can read it as "not always". See CORE-14994.
4155 * See also: https://docs.microsoft.com/en-us/typography/opentype/spec/os2#fsselection
4156 */
4157#define FM_SEL_USE_TYPO_METRICS 0x80
4158 if (lfHeight > 0)
4159 {
4160 /* case (A): lfHeight is positive */
4161 Sum = (FT_Short)pOS2->usWinAscent + (FT_Short)pOS2->usWinDescent;
4162 if (Sum == 0 || (pOS2->fsSelection & FM_SEL_USE_TYPO_METRICS))
4163 {
4164 Ascent = pHori->Ascender;
4165 Descent = -pHori->Descender;
4166 Sum = Ascent + Descent;
4167 }
4168 else
4169 {
4170 Ascent = (FT_Short)pOS2->usWinAscent;
4171 Descent = (FT_Short)pOS2->usWinDescent;
4172 }
4173
4174 FontGDI->tmAscent = FT_MulDiv(lfHeight, Ascent, Sum);
4175 FontGDI->tmDescent = FT_MulDiv(lfHeight, Descent, Sum);
4176 FontGDI->tmHeight = FontGDI->tmAscent + FontGDI->tmDescent;
4177 FontGDI->tmInternalLeading = FontGDI->tmHeight - FT_MulDiv(lfHeight, face->units_per_EM, Sum);
4178 }
4179 else if (lfHeight < 0)
4180 {
4181 /* case (B): lfHeight is negative */
4183 {
4184 FontGDI->tmAscent = FT_MulDiv(-lfHeight, pHori->Ascender, face->units_per_EM);
4185 FontGDI->tmDescent = FT_MulDiv(-lfHeight, -pHori->Descender, face->units_per_EM);
4186 }
4187 else
4188 {
4189 FontGDI->tmAscent = FT_MulDiv(-lfHeight, (FT_Short)pOS2->usWinAscent, face->units_per_EM);
4190 FontGDI->tmDescent = FT_MulDiv(-lfHeight, (FT_Short)pOS2->usWinDescent, face->units_per_EM);
4191 }
4192 FontGDI->tmHeight = FontGDI->tmAscent + FontGDI->tmDescent;
4193 FontGDI->tmInternalLeading = FontGDI->tmHeight + lfHeight;
4194 }
4195#undef FM_SEL_USE_TYPO_METRICS
4196
4197 FontGDI->Magic = FONTGDI_MAGIC;
4198 FontGDI->lfHeight = lfHeight;
4199 FontGDI->lfWidth = lfWidth;
4200
4201 EmHeight = FontGDI->tmHeight - FontGDI->tmInternalLeading;
4202 EmHeight = max(EmHeight, 1);
4203 EmHeight = min(EmHeight, USHORT_MAX);
4204
4205#if 1
4206 /* I think this is wrong implementation but its test result is better. */
4207 if (lfWidth != 0)
4208 Width64 = FT_MulDiv(lfWidth, face->units_per_EM, pOS2->xAvgCharWidth) << 6;
4209 else
4210 Width64 = 0;
4211#else
4212 /* I think this is correct implementation but it is mismatching to the
4213 other metric functions. The test result is bad. */
4214 if (lfWidth != 0)
4215 Width64 = (FT_MulDiv(lfWidth, 96 * 5, 72 * 3) << 6); /* ??? FIXME */
4216 else
4217 Width64 = 0;
4218#endif
4219
4221 req.width = Width64;
4222 req.height = (EmHeight << 6);
4223 req.horiResolution = 0;
4224 req.vertResolution = 0;
4225 return FT_Request_Size(face, &req);
4226}
4227
4228BOOL
4231 PTEXTOBJ TextObj,
4232 PFONTGDI FontGDI,
4233 BOOL bDoLock)
4234{
4235 FT_Face face;
4236 INT error, n;
4237 FT_CharMap charmap, found;
4238 LOGFONTW *plf;
4239
4240 if (bDoLock)
4242
4243 face = FontGDI->SharedFace->Face;
4244 if (face->charmap == NULL)
4245 {
4246 DPRINT("WARNING: No charmap selected!\n");
4247 DPRINT("This font face has %d charmaps\n", face->num_charmaps);
4248
4249 found = NULL;
4250 for (n = 0; n < face->num_charmaps; n++)
4251 {
4252 charmap = face->charmaps[n];
4253 if (charmap->encoding == FT_ENCODING_UNICODE)
4254 {
4255 found = charmap;
4256 break;
4257 }
4258 }
4259 if (!found)
4260 {
4261 for (n = 0; n < face->num_charmaps; n++)
4262 {
4263 charmap = face->charmaps[n];
4264 if (charmap->platform_id == TT_PLATFORM_APPLE_UNICODE)
4265 {
4266 found = charmap;
4267 break;
4268 }
4269 }
4270 }
4271 if (!found)
4272 {
4273 for (n = 0; n < face->num_charmaps; n++)
4274 {
4275 charmap = face->charmaps[n];
4276 if (charmap->encoding == FT_ENCODING_MS_SYMBOL)
4277 {
4278 found = charmap;
4279 break;
4280 }
4281 }
4282 }
4283 if (!found && face->num_charmaps > 0)
4284 {
4285 found = face->charmaps[0];
4286 }
4287 if (!found)
4288 {
4289 DPRINT1("WARNING: Could not find desired charmap!\n");
4290 }
4291 else
4292 {
4293 DPRINT("Found charmap encoding: %i\n", found->encoding);
4294 error = FT_Set_Charmap(face, found);
4295 if (error)
4296 {
4297 DPRINT1("WARNING: Could not set the charmap!\n");
4298 }
4299 }
4300 }
4301
4302 plf = &TextObj->logfont.elfEnumLogfontEx.elfLogFont;
4303
4304 error = IntRequestFontSize(dc, FontGDI, plf->lfWidth, plf->lfHeight);
4305
4306 if (bDoLock)
4308
4309 if (error)
4310 {
4311 DPRINT1("Error in setting pixel sizes: %d\n", error);
4312 return FALSE;
4313 }
4314
4315 return TRUE;
4316}
4317
4318static inline FT_UInt FASTCALL
4320{
4321 FT_UInt ret;
4322
4323 if (glyph < 0x100) glyph += 0xf000;
4324 /* there are a number of old pre-Unicode "broken" TTFs, which
4325 do have symbols at U+00XX instead of U+f0XX */
4326 if (!(ret = FT_Get_Char_Index(ft_face, glyph)))
4327 ret = FT_Get_Char_Index(ft_face, glyph - 0xf000);
4328
4329 return ret;
4330}
4331
4332static inline FT_UInt FASTCALL
4334{
4335 FT_UInt ret;
4336
4337 if (face_has_symbol_charmap(ft_face))
4338 {
4339 ret = get_glyph_index_symbol(ft_face, glyph);
4340 if (ret != 0)
4341 return ret;
4342 }
4343
4344 return FT_Get_Char_Index(ft_face, glyph);
4345}
4346
4347static inline FT_UInt FASTCALL
4349{
4350 return (fCodeAsIndex ? code :