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