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