[WIN32K]
[reactos.git] / reactos / subsystems / win32 / win32k / objects / freetype.c
1 /*
2 * PROJECT: ReactOS win32 kernel mode subsystem
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: subsystems/win32/win32k/objects/freetype.c
5 * PURPOSE: FreeType font engine interface
6 * PROGRAMMER: Copyright 2001 Huw D M Davies for CodeWeavers.
7 * Copyright 2006 Dmitry Timoshkov for CodeWeavers.
8 */
9
10 /** Includes ******************************************************************/
11
12 #include <win32k.h>
13
14 #define NDEBUG
15 #include <debug.h>
16
17 #ifndef FT_MAKE_TAG
18 #define FT_MAKE_TAG( ch0, ch1, ch2, ch3 ) \
19 ( ((DWORD)(BYTE)(ch0) << 24) | ((DWORD)(BYTE)(ch1) << 16) | \
20 ((DWORD)(BYTE)(ch2) << 8) | (DWORD)(BYTE)(ch3) )
21 #endif
22
23 FT_Library library;
24
25 typedef struct _FONT_ENTRY
26 {
27 LIST_ENTRY ListEntry;
28 FONTGDI *Font;
29 UNICODE_STRING FaceName;
30 BYTE NotEnum;
31 } FONT_ENTRY, *PFONT_ENTRY;
32
33 /* The FreeType library is not thread safe, so we have
34 to serialize access to it */
35 static PFAST_MUTEX FreeTypeLock;
36
37 static LIST_ENTRY FontListHead;
38 static PFAST_MUTEX FontListLock;
39 static BOOL RenderingEnabled = TRUE;
40
41 #define IntLockGlobalFonts \
42 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(FontListLock)
43
44 #define IntUnLockGlobalFonts \
45 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(FontListLock)
46
47 #define IntLockFreeType \
48 ExEnterCriticalRegionAndAcquireFastMutexUnsafe(FreeTypeLock)
49
50 #define IntUnLockFreeType \
51 ExReleaseFastMutexUnsafeAndLeaveCriticalRegion(FreeTypeLock)
52
53 #define MAX_FONT_CACHE 256
54
55 typedef struct _FONT_CACHE_ENTRY
56 {
57 LIST_ENTRY ListEntry;
58 int GlyphIndex;
59 FT_Face Face;
60 FT_BitmapGlyph BitmapGlyph;
61 int Height;
62 } FONT_CACHE_ENTRY, *PFONT_CACHE_ENTRY;
63 static LIST_ENTRY FontCacheListHead;
64 static UINT FontCacheNumEntries;
65
66 static PWCHAR ElfScripts[32] = /* These are in the order of the fsCsb[0] bits */
67 {
68 L"Western", /* 00 */
69 L"Central_European",
70 L"Cyrillic",
71 L"Greek",
72 L"Turkish",
73 L"Hebrew",
74 L"Arabic",
75 L"Baltic",
76 L"Vietnamese", /* 08 */
77 NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 15 */
78 L"Thai",
79 L"Japanese",
80 L"CHINESE_GB2312",
81 L"Hangul",
82 L"CHINESE_BIG5",
83 L"Hangul(Johab)",
84 NULL, NULL, /* 23 */
85 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
86 L"Symbol" /* 31 */
87 };
88
89 /*
90 * For TranslateCharsetInfo
91 */
92 #define CP_SYMBOL 42
93 #define MAXTCIINDEX 32
94 static const CHARSETINFO FontTci[MAXTCIINDEX] =
95 {
96 /* ANSI */
97 { ANSI_CHARSET, 1252, {{0,0,0,0},{FS_LATIN1,0}} },
98 { EASTEUROPE_CHARSET, 1250, {{0,0,0,0},{FS_LATIN2,0}} },
99 { RUSSIAN_CHARSET, 1251, {{0,0,0,0},{FS_CYRILLIC,0}} },
100 { GREEK_CHARSET, 1253, {{0,0,0,0},{FS_GREEK,0}} },
101 { TURKISH_CHARSET, 1254, {{0,0,0,0},{FS_TURKISH,0}} },
102 { HEBREW_CHARSET, 1255, {{0,0,0,0},{FS_HEBREW,0}} },
103 { ARABIC_CHARSET, 1256, {{0,0,0,0},{FS_ARABIC,0}} },
104 { BALTIC_CHARSET, 1257, {{0,0,0,0},{FS_BALTIC,0}} },
105 { VIETNAMESE_CHARSET, 1258, {{0,0,0,0},{FS_VIETNAMESE,0}} },
106 /* reserved by ANSI */
107 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
108 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
109 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
110 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
111 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
112 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
113 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
114 /* ANSI and OEM */
115 { THAI_CHARSET, 874, {{0,0,0,0},{FS_THAI,0}} },
116 { SHIFTJIS_CHARSET, 932, {{0,0,0,0},{FS_JISJAPAN,0}} },
117 { GB2312_CHARSET, 936, {{0,0,0,0},{FS_CHINESESIMP,0}} },
118 { HANGEUL_CHARSET, 949, {{0,0,0,0},{FS_WANSUNG,0}} },
119 { CHINESEBIG5_CHARSET, 950, {{0,0,0,0},{FS_CHINESETRAD,0}} },
120 { JOHAB_CHARSET, 1361, {{0,0,0,0},{FS_JOHAB,0}} },
121 /* Reserved for alternate ANSI and OEM */
122 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
123 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
124 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
125 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
126 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
127 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
128 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
129 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
130 /* Reserved for system */
131 { DEFAULT_CHARSET, 0, {{0,0,0,0},{FS_LATIN1,0}} },
132 { SYMBOL_CHARSET, CP_SYMBOL, {{0,0,0,0},{FS_SYMBOL,0}} }
133 };
134
135 BOOL FASTCALL
136 InitFontSupport(VOID)
137 {
138 ULONG ulError;
139
140 InitializeListHead(&FontListHead);
141 InitializeListHead(&FontCacheListHead);
142 FontCacheNumEntries = 0;
143 /* Fast Mutexes must be allocated from non paged pool */
144 FontListLock = ExAllocatePoolWithTag(NonPagedPool, sizeof(FAST_MUTEX), TAG_INTERNAL_SYNC);
145 ExInitializeFastMutex(FontListLock);
146 FreeTypeLock = ExAllocatePoolWithTag(NonPagedPool, sizeof(FAST_MUTEX), TAG_INTERNAL_SYNC);
147 ExInitializeFastMutex(FreeTypeLock);
148
149 ulError = FT_Init_FreeType(&library);
150 if (ulError)
151 {
152 DPRINT1("FT_Init_FreeType failed with error code 0x%x\n", ulError);
153 return FALSE;
154 }
155
156 IntLoadSystemFonts();
157
158 return TRUE;
159 }
160
161 /*
162 * IntLoadSystemFonts
163 *
164 * Search the system font directory and adds each font found.
165 */
166
167 VOID FASTCALL
168 IntLoadSystemFonts(VOID)
169 {
170 OBJECT_ATTRIBUTES ObjectAttributes;
171 UNICODE_STRING Directory, SearchPattern, FileName, TempString;
172 IO_STATUS_BLOCK Iosb;
173 HANDLE hDirectory;
174 BYTE *DirInfoBuffer;
175 PFILE_DIRECTORY_INFORMATION DirInfo;
176 BOOLEAN bRestartScan = TRUE;
177 NTSTATUS Status;
178
179 RtlInitUnicodeString(&Directory, L"\\SystemRoot\\Fonts\\");
180 /* FIXME: Add support for other font types */
181 RtlInitUnicodeString(&SearchPattern, L"*.ttf");
182
183 InitializeObjectAttributes(
184 &ObjectAttributes,
185 &Directory,
186 OBJ_CASE_INSENSITIVE,
187 NULL,
188 NULL);
189
190 Status = ZwOpenFile(
191 &hDirectory,
192 SYNCHRONIZE | FILE_LIST_DIRECTORY,
193 &ObjectAttributes,
194 &Iosb,
195 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
196 FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE);
197
198 if (NT_SUCCESS(Status))
199 {
200 DirInfoBuffer = ExAllocatePoolWithTag(PagedPool, 0x4000, TAG_FONT);
201 if (DirInfoBuffer == NULL)
202 {
203 ZwClose(hDirectory);
204 return;
205 }
206
207 FileName.Buffer = ExAllocatePoolWithTag(PagedPool, MAX_PATH * sizeof(WCHAR), TAG_FONT);
208 if (FileName.Buffer == NULL)
209 {
210 ExFreePoolWithTag(DirInfoBuffer, TAG_FONT);
211 ZwClose(hDirectory);
212 return;
213 }
214 FileName.Length = 0;
215 FileName.MaximumLength = MAX_PATH * sizeof(WCHAR);
216
217 while (1)
218 {
219 Status = ZwQueryDirectoryFile(
220 hDirectory,
221 NULL,
222 NULL,
223 NULL,
224 &Iosb,
225 DirInfoBuffer,
226 0x4000,
227 FileDirectoryInformation,
228 FALSE,
229 &SearchPattern,
230 bRestartScan);
231
232 if (!NT_SUCCESS(Status) || Status == STATUS_NO_MORE_FILES)
233 {
234 break;
235 }
236
237 DirInfo = (PFILE_DIRECTORY_INFORMATION)DirInfoBuffer;
238 while (1)
239 {
240 TempString.Buffer = DirInfo->FileName;
241 TempString.Length =
242 TempString.MaximumLength = DirInfo->FileNameLength;
243 RtlCopyUnicodeString(&FileName, &Directory);
244 RtlAppendUnicodeStringToString(&FileName, &TempString);
245 IntGdiAddFontResource(&FileName, 0);
246 if (DirInfo->NextEntryOffset == 0)
247 break;
248 DirInfo = (PFILE_DIRECTORY_INFORMATION)((ULONG_PTR)DirInfo + DirInfo->NextEntryOffset);
249 }
250
251 bRestartScan = FALSE;
252 }
253
254 ExFreePoolWithTag(FileName.Buffer, TAG_FONT);
255 ExFreePoolWithTag(DirInfoBuffer, TAG_FONT);
256 ZwClose(hDirectory);
257 }
258 }
259
260
261 /*
262 * IntGdiAddFontResource
263 *
264 * Adds the font resource from the specified file to the system.
265 */
266
267 INT FASTCALL
268 IntGdiAddFontResource(PUNICODE_STRING FileName, DWORD Characteristics)
269 {
270 FONTGDI *FontGDI;
271 NTSTATUS Status;
272 HANDLE FileHandle, KeyHandle;
273 OBJECT_ATTRIBUTES ObjectAttributes;
274 PVOID Buffer = NULL;
275 IO_STATUS_BLOCK Iosb;
276 INT Error;
277 FT_Face Face;
278 ANSI_STRING AnsiFaceName;
279 PFONT_ENTRY Entry;
280 PSECTION_OBJECT SectionObject;
281 ULONG ViewSize = 0;
282 LARGE_INTEGER SectionSize;
283 UNICODE_STRING FontRegPath = RTL_CONSTANT_STRING(L"\\REGISTRY\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts");
284
285 /* Open the font file */
286
287 InitializeObjectAttributes(&ObjectAttributes, FileName, 0, NULL, NULL);
288 Status = ZwOpenFile(
289 &FileHandle,
290 FILE_GENERIC_READ | SYNCHRONIZE,
291 &ObjectAttributes,
292 &Iosb,
293 FILE_SHARE_READ,
294 FILE_SYNCHRONOUS_IO_NONALERT);
295
296 if (!NT_SUCCESS(Status))
297 {
298 DPRINT("Could not load font file: %wZ\n", FileName);
299 return 0;
300 }
301
302 SectionSize.QuadPart = 0LL;
303 Status = MmCreateSection((PVOID)&SectionObject, SECTION_ALL_ACCESS,
304 NULL, &SectionSize, PAGE_READONLY,
305 SEC_COMMIT, FileHandle, NULL);
306 if (!NT_SUCCESS(Status))
307 {
308 DPRINT("Could not map file: %wZ\n", FileName);
309 ZwClose(FileHandle);
310 return 0;
311 }
312
313 ZwClose(FileHandle);
314
315 Status = MmMapViewInSystemSpace(SectionObject, &Buffer, &ViewSize);
316 if (!NT_SUCCESS(Status))
317 {
318 DPRINT("Could not map file: %wZ\n", FileName);
319 return Status;
320 }
321
322 IntLockFreeType;
323 Error = FT_New_Memory_Face(
324 library,
325 Buffer,
326 ViewSize,
327 0,
328 &Face);
329 IntUnLockFreeType;
330
331 if (Error)
332 {
333 if (Error == FT_Err_Unknown_File_Format)
334 DPRINT("Unknown font file format\n");
335 else
336 DPRINT("Error reading font file (error code: %u)\n", Error);
337 ObDereferenceObject(SectionObject);
338 return 0;
339 }
340
341 Entry = ExAllocatePoolWithTag(PagedPool, sizeof(FONT_ENTRY), TAG_FONT);
342 if (!Entry)
343 {
344 FT_Done_Face(Face);
345 ObDereferenceObject(SectionObject);
346 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
347 return 0;
348 }
349
350 FontGDI = EngAllocMem(FL_ZERO_MEMORY, sizeof(FONTGDI), GDITAG_RFONT);
351 if (FontGDI == NULL)
352 {
353 FT_Done_Face(Face);
354 ObDereferenceObject(SectionObject);
355 ExFreePoolWithTag(Entry, TAG_FONT);
356 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
357 return 0;
358 }
359
360 FontGDI->Filename = ExAllocatePoolWithTag(PagedPool, FileName->Length + sizeof(WCHAR), GDITAG_PFF);
361 if (FontGDI->Filename == NULL)
362 {
363 EngFreeMem(FontGDI);
364 FT_Done_Face(Face);
365 ObDereferenceObject(SectionObject);
366 ExFreePoolWithTag(Entry, TAG_FONT);
367 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
368 return 0;
369 }
370 RtlCopyMemory(FontGDI->Filename, FileName->Buffer, FileName->Length);
371 FontGDI->Filename[FileName->Length / sizeof(WCHAR)] = L'\0';
372 FontGDI->face = Face;
373
374 DPRINT("Font loaded: %s (%s)\n", Face->family_name, Face->style_name);
375 DPRINT("Num glyphs: %u\n", Face->num_glyphs);
376
377 /* Add this font resource to the font table */
378
379 Entry->Font = FontGDI;
380 Entry->NotEnum = (Characteristics & FR_NOT_ENUM);
381 RtlInitAnsiString(&AnsiFaceName, (LPSTR)Face->family_name);
382 RtlAnsiStringToUnicodeString(&Entry->FaceName, &AnsiFaceName, TRUE);
383
384 if (Characteristics & FR_PRIVATE)
385 {
386 PPROCESSINFO Win32Process = PsGetCurrentProcessWin32Process();
387 IntLockProcessPrivateFonts(Win32Process);
388 InsertTailList(&Win32Process->PrivateFontListHead, &Entry->ListEntry);
389 IntUnLockProcessPrivateFonts(Win32Process);
390 }
391 else
392 {
393 IntLockGlobalFonts;
394 InsertTailList(&FontListHead, &Entry->ListEntry);
395 InitializeObjectAttributes(&ObjectAttributes, &FontRegPath, OBJ_CASE_INSENSITIVE, NULL, NULL);
396 Status = ZwOpenKey(&KeyHandle, KEY_WRITE, &ObjectAttributes);
397 if (NT_SUCCESS(Status))
398 {
399 LPWSTR pName = wcsrchr(FileName->Buffer, L'\\');
400 if (pName)
401 {
402 pName++;
403 ZwSetValueKey(KeyHandle, &Entry->FaceName, 0, REG_SZ, pName, (wcslen(pName) + 1) * sizeof(WCHAR));
404 }
405 ZwClose(KeyHandle);
406 }
407 IntUnLockGlobalFonts;
408 }
409 return 1;
410 }
411
412 BOOL FASTCALL
413 IntIsFontRenderingEnabled(VOID)
414 {
415 BOOL Ret = RenderingEnabled;
416 HDC hDC;
417
418 hDC = IntGetScreenDC();
419 if (hDC)
420 Ret = (NtGdiGetDeviceCaps(hDC, BITSPIXEL) > 8) && RenderingEnabled;
421
422 return Ret;
423 }
424
425 VOID FASTCALL
426 IntEnableFontRendering(BOOL Enable)
427 {
428 RenderingEnabled = Enable;
429 }
430
431 FT_Render_Mode FASTCALL
432 IntGetFontRenderMode(LOGFONTW *logfont)
433 {
434 switch (logfont->lfQuality)
435 {
436 case NONANTIALIASED_QUALITY:
437 return FT_RENDER_MODE_MONO;
438 case DRAFT_QUALITY:
439 return FT_RENDER_MODE_LIGHT;
440 /* case CLEARTYPE_QUALITY:
441 return FT_RENDER_MODE_LCD; */
442 }
443 return FT_RENDER_MODE_NORMAL;
444 }
445
446
447 NTSTATUS FASTCALL
448 TextIntCreateFontIndirect(CONST LPLOGFONTW lf, HFONT *NewFont)
449 {
450 PTEXTOBJ TextObj;
451
452 TextObj = TEXTOBJ_AllocTextWithHandle();
453 if (!TextObj)
454 {
455 return STATUS_NO_MEMORY;
456 }
457
458 *NewFont = TextObj->BaseObject.hHmgr;
459 RtlCopyMemory(&TextObj->logfont.elfEnumLogfontEx.elfLogFont, lf, sizeof(LOGFONTW));
460 if (lf->lfEscapement != lf->lfOrientation)
461 {
462 /* This should really depend on whether GM_ADVANCED is set */
463 TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfOrientation =
464 TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfEscapement;
465 }
466 TEXTOBJ_UnlockText(TextObj);
467
468 return STATUS_SUCCESS;
469 }
470
471 /*************************************************************************
472 * TranslateCharsetInfo
473 *
474 * Fills a CHARSETINFO structure for a character set, code page, or
475 * font. This allows making the correspondance between different labelings
476 * (character set, Windows, ANSI, and OEM codepages, and Unicode ranges)
477 * of the same encoding.
478 *
479 * Only one codepage will be set in Cs->fs. If TCI_SRCFONTSIG is used,
480 * only one codepage should be set in *Src.
481 *
482 * RETURNS
483 * TRUE on success, FALSE on failure.
484 *
485 */
486 static BOOLEAN APIENTRY
487 IntTranslateCharsetInfo(PDWORD Src, /* [in]
488 if flags == TCI_SRCFONTSIG: pointer to fsCsb of a FONTSIGNATURE
489 if flags == TCI_SRCCHARSET: a character set value
490 if flags == TCI_SRCCODEPAGE: a code page value */
491 LPCHARSETINFO Cs, /* [out] structure to receive charset information */
492 DWORD Flags /* [in] determines interpretation of lpSrc */)
493 {
494 int Index = 0;
495
496 switch (Flags)
497 {
498 case TCI_SRCFONTSIG:
499 while (0 == (*Src >> Index & 0x0001) && Index < MAXTCIINDEX)
500 {
501 Index++;
502 }
503 break;
504 case TCI_SRCCODEPAGE:
505 while ( *Src != FontTci[Index].ciACP && Index < MAXTCIINDEX)
506 {
507 Index++;
508 }
509 break;
510 case TCI_SRCCHARSET:
511 while ( *Src != FontTci[Index].ciCharset && Index < MAXTCIINDEX)
512 {
513 Index++;
514 }
515 break;
516 default:
517 return FALSE;
518 }
519
520 if (Index >= MAXTCIINDEX || DEFAULT_CHARSET == FontTci[Index].ciCharset)
521 {
522 return FALSE;
523 }
524
525 RtlCopyMemory(Cs, &FontTci[Index], sizeof(CHARSETINFO));
526
527 return TRUE;
528 }
529
530
531 static BOOL face_has_symbol_charmap(FT_Face ft_face)
532 {
533 int i;
534
535 for(i = 0; i < ft_face->num_charmaps; i++)
536 {
537 if(ft_face->charmaps[i]->encoding == FT_ENCODING_MS_SYMBOL)
538 return TRUE;
539 }
540 return FALSE;
541 }
542
543 static void FASTCALL
544 FillTM(TEXTMETRICW *TM, PFONTGDI FontGDI, TT_OS2 *pOS2, TT_HoriHeader *pHori, FT_WinFNT_HeaderRec *pWin)
545 {
546 FT_Fixed XScale, YScale;
547 int Ascent, Descent;
548 FT_Face Face = FontGDI->face;
549
550 XScale = Face->size->metrics.x_scale;
551 YScale = Face->size->metrics.y_scale;
552
553 if (pWin)
554 {
555 TM->tmHeight = pWin->pixel_height;
556 TM->tmAscent = pWin->ascent;
557 TM->tmDescent = TM->tmHeight - TM->tmAscent;
558 TM->tmInternalLeading = pWin->internal_leading;
559 TM->tmExternalLeading = pWin->external_leading;
560 TM->tmAveCharWidth = pWin->avg_width;
561 TM->tmMaxCharWidth = pWin->max_width;
562 TM->tmWeight = pWin->weight;
563 TM->tmOverhang = 0;
564 TM->tmDigitizedAspectX = pWin->horizontal_resolution;
565 TM->tmDigitizedAspectY = pWin->vertical_resolution;
566 TM->tmFirstChar = pWin->first_char;
567 TM->tmLastChar = pWin->last_char;
568 TM->tmDefaultChar = pWin->default_char + pWin->first_char;
569 TM->tmBreakChar = pWin->break_char + pWin->first_char;
570 TM->tmItalic = pWin->italic;
571 TM->tmUnderlined = FontGDI->Underline;
572 TM->tmStruckOut = FontGDI->StrikeOut;
573 TM->tmPitchAndFamily = pWin->pitch_and_family;
574 TM->tmCharSet = pWin->charset;
575 return;
576 }
577
578 if (pOS2->usWinAscent + pOS2->usWinDescent == 0)
579 {
580 Ascent = pHori->Ascender;
581 Descent = -pHori->Descender;
582 }
583 else
584 {
585 Ascent = pOS2->usWinAscent;
586 Descent = pOS2->usWinDescent;
587 }
588
589 #if 0 /* This (Wine) code doesn't seem to work correctly for us, cmd issue */
590 TM->tmAscent = (FT_MulFix(Ascent, YScale) + 32) >> 6;
591 TM->tmDescent = (FT_MulFix(Descent, YScale) + 32) >> 6;
592 #else /* This (ros) code was previously affected by a FreeType bug, but it works now */
593 TM->tmAscent = (Face->size->metrics.ascender + 32) >> 6; /* Units above baseline */
594 TM->tmDescent = (32 - Face->size->metrics.descender) >> 6; /* Units below baseline */
595 #endif
596 TM->tmInternalLeading = (FT_MulFix(Ascent + Descent - Face->units_per_EM, YScale) + 32) >> 6;
597
598 TM->tmHeight = TM->tmAscent + TM->tmDescent;
599
600 /* MSDN says:
601 * el = MAX(0, LineGap - ((WinAscent + WinDescent) - (Ascender - Descender)))
602 */
603 TM->tmExternalLeading = max(0, (FT_MulFix(pHori->Line_Gap
604 - ((Ascent + Descent)
605 - (pHori->Ascender - pHori->Descender)),
606 YScale) + 32) >> 6);
607
608 TM->tmAveCharWidth = (FT_MulFix(pOS2->xAvgCharWidth, XScale) + 32) >> 6;
609 if (TM->tmAveCharWidth == 0)
610 {
611 TM->tmAveCharWidth = 1;
612 }
613
614 /* Correct forumla to get the maxcharwidth from unicode and ansi font */
615 TM->tmMaxCharWidth = (FT_MulFix(Face->max_advance_width, XScale) + 32) >> 6;
616
617 TM->tmWeight = pOS2->usWeightClass;
618 TM->tmOverhang = 0;
619 TM->tmDigitizedAspectX = 96;
620 TM->tmDigitizedAspectY = 96;
621 if (face_has_symbol_charmap(Face) || (pOS2->usFirstCharIndex >= 0xf000 && pOS2->usFirstCharIndex < 0xf100))
622 {
623 USHORT cpOEM, cpAnsi;
624
625 EngGetCurrentCodePage(&cpOEM, &cpAnsi);
626 TM->tmFirstChar = 0;
627 switch(cpAnsi)
628 {
629 case 1257: /* Baltic */
630 TM->tmLastChar = 0xf8fd;
631 break;
632 default:
633 TM->tmLastChar = 0xf0ff;
634 }
635 TM->tmBreakChar = 0x20;
636 TM->tmDefaultChar = 0x1f;
637 }
638 else
639 {
640 TM->tmFirstChar = pOS2->usFirstCharIndex; /* Should be the first char in the cmap */
641 TM->tmLastChar = pOS2->usLastCharIndex; /* Should be min(cmap_last, os2_last) */
642
643 if(pOS2->usFirstCharIndex <= 1)
644 TM->tmBreakChar = pOS2->usFirstCharIndex + 2;
645 else if (pOS2->usFirstCharIndex > 0xff)
646 TM->tmBreakChar = 0x20;
647 else
648 TM->tmBreakChar = pOS2->usFirstCharIndex;
649 TM->tmDefaultChar = TM->tmBreakChar - 1;
650 }
651 TM->tmItalic = (Face->style_flags & FT_STYLE_FLAG_ITALIC) ? 255 : 0;
652 TM->tmUnderlined = FontGDI->Underline;
653 TM->tmStruckOut = FontGDI->StrikeOut;
654
655 /* Yes TPMF_FIXED_PITCH is correct; braindead api */
656 if (! FT_IS_FIXED_WIDTH(Face))
657 {
658 TM->tmPitchAndFamily = TMPF_FIXED_PITCH;
659 }
660 else
661 {
662 TM->tmPitchAndFamily = 0;
663 }
664
665 switch (pOS2->panose[PAN_FAMILYTYPE_INDEX])
666 {
667 case PAN_FAMILY_SCRIPT:
668 TM->tmPitchAndFamily |= FF_SCRIPT;
669 break;
670 case PAN_FAMILY_DECORATIVE:
671 TM->tmPitchAndFamily |= FF_DECORATIVE;
672 break;
673
674 case PAN_ANY:
675 case PAN_NO_FIT:
676 case PAN_FAMILY_TEXT_DISPLAY:
677 case PAN_FAMILY_PICTORIAL: /* Symbol fonts get treated as if they were text */
678 /* Which is clearly not what the panose spec says. */
679 if (TM->tmPitchAndFamily == 0) /* Fixed */
680 {
681 TM->tmPitchAndFamily = FF_MODERN;
682 }
683 else
684 {
685 switch (pOS2->panose[PAN_SERIFSTYLE_INDEX])
686 {
687 case PAN_ANY:
688 case PAN_NO_FIT:
689 default:
690 TM->tmPitchAndFamily |= FF_DONTCARE;
691 break;
692
693 case PAN_SERIF_COVE:
694 case PAN_SERIF_OBTUSE_COVE:
695 case PAN_SERIF_SQUARE_COVE:
696 case PAN_SERIF_OBTUSE_SQUARE_COVE:
697 case PAN_SERIF_SQUARE:
698 case PAN_SERIF_THIN:
699 case PAN_SERIF_BONE:
700 case PAN_SERIF_EXAGGERATED:
701 case PAN_SERIF_TRIANGLE:
702 TM->tmPitchAndFamily |= FF_ROMAN;
703 break;
704
705 case PAN_SERIF_NORMAL_SANS:
706 case PAN_SERIF_OBTUSE_SANS:
707 case PAN_SERIF_PERP_SANS:
708 case PAN_SERIF_FLARED:
709 case PAN_SERIF_ROUNDED:
710 TM->tmPitchAndFamily |= FF_SWISS;
711 break;
712 }
713 }
714 break;
715 default:
716 TM->tmPitchAndFamily |= FF_DONTCARE;
717 }
718
719 if (FT_IS_SCALABLE(Face))
720 {
721 TM->tmPitchAndFamily |= TMPF_VECTOR;
722 }
723 if (FT_IS_SFNT(Face))
724 {
725 TM->tmPitchAndFamily |= TMPF_TRUETYPE;
726 }
727
728 TM->tmCharSet = DEFAULT_CHARSET;
729 }
730
731 /*************************************************************
732 * IntGetOutlineTextMetrics
733 *
734 */
735 INT FASTCALL
736 IntGetOutlineTextMetrics(PFONTGDI FontGDI,
737 UINT Size,
738 OUTLINETEXTMETRICW *Otm)
739 {
740 unsigned Needed;
741 TT_OS2 *pOS2;
742 TT_HoriHeader *pHori;
743 TT_Postscript *pPost;
744 FT_Fixed XScale, YScale;
745 ANSI_STRING FamilyNameA, StyleNameA;
746 UNICODE_STRING FamilyNameW, StyleNameW, Regular;
747 FT_WinFNT_HeaderRec Win;
748 FT_Error Error;
749 char *Cp;
750
751 Needed = sizeof(OUTLINETEXTMETRICW);
752
753 RtlInitAnsiString(&FamilyNameA, FontGDI->face->family_name);
754 RtlAnsiStringToUnicodeString(&FamilyNameW, &FamilyNameA, TRUE);
755
756 RtlInitAnsiString(&StyleNameA, FontGDI->face->style_name);
757 RtlAnsiStringToUnicodeString(&StyleNameW, &StyleNameA, TRUE);
758
759 /* These names should be read from the TT name table */
760
761 /* Length of otmpFamilyName */
762 Needed += FamilyNameW.Length + sizeof(WCHAR);
763
764 RtlInitUnicodeString(&Regular, L"regular");
765 /* Length of otmpFaceName */
766 if (0 == RtlCompareUnicodeString(&StyleNameW, &Regular, TRUE))
767 {
768 Needed += FamilyNameW.Length + sizeof(WCHAR); /* Just the family name */
769 }
770 else
771 {
772 Needed += FamilyNameW.Length + StyleNameW.Length + (sizeof(WCHAR) << 1); /* family + " " + style */
773 }
774
775 /* Length of otmpStyleName */
776 Needed += StyleNameW.Length + sizeof(WCHAR);
777
778 /* Length of otmpFullName */
779 Needed += FamilyNameW.Length + StyleNameW.Length + (sizeof(WCHAR) << 1);
780
781 if (Size < Needed)
782 {
783 RtlFreeUnicodeString(&FamilyNameW);
784 RtlFreeUnicodeString(&StyleNameW);
785 return Needed;
786 }
787
788 XScale = FontGDI->face->size->metrics.x_scale;
789 YScale = FontGDI->face->size->metrics.y_scale;
790
791 IntLockFreeType;
792 pOS2 = FT_Get_Sfnt_Table(FontGDI->face, ft_sfnt_os2);
793 if (NULL == pOS2)
794 {
795 IntUnLockFreeType;
796 DPRINT1("Can't find OS/2 table - not TT font?\n");
797 RtlFreeUnicodeString(&StyleNameW);
798 RtlFreeUnicodeString(&FamilyNameW);
799 return 0;
800 }
801
802 pHori = FT_Get_Sfnt_Table(FontGDI->face, ft_sfnt_hhea);
803 if (NULL == pHori)
804 {
805 IntUnLockFreeType;
806 DPRINT1("Can't find HHEA table - not TT font?\n");
807 RtlFreeUnicodeString(&StyleNameW);
808 RtlFreeUnicodeString(&FamilyNameW);
809 return 0;
810 }
811
812 pPost = FT_Get_Sfnt_Table(FontGDI->face, ft_sfnt_post); /* We can live with this failing */
813
814 Error = FT_Get_WinFNT_Header(FontGDI->face , &Win);
815
816 Otm->otmSize = Needed;
817
818 FillTM(&Otm->otmTextMetrics, FontGDI, pOS2, pHori, !Error ? &Win : 0);
819
820 Otm->otmFiller = 0;
821 RtlCopyMemory(&Otm->otmPanoseNumber, pOS2->panose, PANOSE_COUNT);
822 Otm->otmfsSelection = pOS2->fsSelection;
823 Otm->otmfsType = pOS2->fsType;
824 Otm->otmsCharSlopeRise = pHori->caret_Slope_Rise;
825 Otm->otmsCharSlopeRun = pHori->caret_Slope_Run;
826 Otm->otmItalicAngle = 0; /* POST table */
827 Otm->otmEMSquare = FontGDI->face->units_per_EM;
828 Otm->otmAscent = (FT_MulFix(pOS2->sTypoAscender, YScale) + 32) >> 6;
829 Otm->otmDescent = (FT_MulFix(pOS2->sTypoDescender, YScale) + 32) >> 6;
830 Otm->otmLineGap = (FT_MulFix(pOS2->sTypoLineGap, YScale) + 32) >> 6;
831 Otm->otmsCapEmHeight = (FT_MulFix(pOS2->sCapHeight, YScale) + 32) >> 6;
832 Otm->otmsXHeight = (FT_MulFix(pOS2->sxHeight, YScale) + 32) >> 6;
833 Otm->otmrcFontBox.left = (FT_MulFix(FontGDI->face->bbox.xMin, XScale) + 32) >> 6;
834 Otm->otmrcFontBox.right = (FT_MulFix(FontGDI->face->bbox.xMax, XScale) + 32) >> 6;
835 Otm->otmrcFontBox.top = (FT_MulFix(FontGDI->face->bbox.yMax, YScale) + 32) >> 6;
836 Otm->otmrcFontBox.bottom = (FT_MulFix(FontGDI->face->bbox.yMin, YScale) + 32) >> 6;
837 Otm->otmMacAscent = Otm->otmTextMetrics.tmAscent;
838 Otm->otmMacDescent = -Otm->otmTextMetrics.tmDescent;
839 Otm->otmMacLineGap = Otm->otmLineGap;
840 Otm->otmusMinimumPPEM = 0; /* TT Header */
841 Otm->otmptSubscriptSize.x = (FT_MulFix(pOS2->ySubscriptXSize, XScale) + 32) >> 6;
842 Otm->otmptSubscriptSize.y = (FT_MulFix(pOS2->ySubscriptYSize, YScale) + 32) >> 6;
843 Otm->otmptSubscriptOffset.x = (FT_MulFix(pOS2->ySubscriptXOffset, XScale) + 32) >> 6;
844 Otm->otmptSubscriptOffset.y = (FT_MulFix(pOS2->ySubscriptYOffset, YScale) + 32) >> 6;
845 Otm->otmptSuperscriptSize.x = (FT_MulFix(pOS2->ySuperscriptXSize, XScale) + 32) >> 6;
846 Otm->otmptSuperscriptSize.y = (FT_MulFix(pOS2->ySuperscriptYSize, YScale) + 32) >> 6;
847 Otm->otmptSuperscriptOffset.x = (FT_MulFix(pOS2->ySuperscriptXOffset, XScale) + 32) >> 6;
848 Otm->otmptSuperscriptOffset.y = (FT_MulFix(pOS2->ySuperscriptYOffset, YScale) + 32) >> 6;
849 Otm->otmsStrikeoutSize = (FT_MulFix(pOS2->yStrikeoutSize, YScale) + 32) >> 6;
850 Otm->otmsStrikeoutPosition = (FT_MulFix(pOS2->yStrikeoutPosition, YScale) + 32) >> 6;
851 if (!pPost)
852 {
853 Otm->otmsUnderscoreSize = 0;
854 Otm->otmsUnderscorePosition = 0;
855 }
856 else
857 {
858 Otm->otmsUnderscoreSize = (FT_MulFix(pPost->underlineThickness, YScale) + 32) >> 6;
859 Otm->otmsUnderscorePosition = (FT_MulFix(pPost->underlinePosition, YScale) + 32) >> 6;
860 }
861
862 IntUnLockFreeType;
863
864 /* otmp* members should clearly have type ptrdiff_t, but M$ knows best */
865 Cp = (char*) Otm + sizeof(OUTLINETEXTMETRICW);
866 Otm->otmpFamilyName = (LPSTR)(Cp - (char*) Otm);
867 wcscpy((WCHAR*) Cp, FamilyNameW.Buffer);
868 Cp += FamilyNameW.Length + sizeof(WCHAR);
869 Otm->otmpStyleName = (LPSTR)(Cp - (char*) Otm);
870 wcscpy((WCHAR*) Cp, StyleNameW.Buffer);
871 Cp += StyleNameW.Length + sizeof(WCHAR);
872 Otm->otmpFaceName = (LPSTR)(Cp - (char*) Otm);
873 wcscpy((WCHAR*) Cp, FamilyNameW.Buffer);
874 if (0 != RtlCompareUnicodeString(&StyleNameW, &Regular, TRUE))
875 {
876 wcscat((WCHAR*) Cp, L" ");
877 wcscat((WCHAR*) Cp, StyleNameW.Buffer);
878 Cp += FamilyNameW.Length + StyleNameW.Length + (sizeof(WCHAR) << 1);
879 }
880 else
881 {
882 Cp += FamilyNameW.Length + sizeof(WCHAR);
883 }
884 Otm->otmpFullName = (LPSTR)(Cp - (char*) Otm);
885 wcscpy((WCHAR*) Cp, FamilyNameW.Buffer);
886 wcscat((WCHAR*) Cp, L" ");
887 wcscat((WCHAR*) Cp, StyleNameW.Buffer);
888
889 RtlFreeUnicodeString(&StyleNameW);
890 RtlFreeUnicodeString(&FamilyNameW);
891
892 return Needed;
893 }
894
895 static PFONTGDI FASTCALL
896 FindFaceNameInList(PUNICODE_STRING FaceName, PLIST_ENTRY Head)
897 {
898 PLIST_ENTRY Entry;
899 PFONT_ENTRY CurrentEntry;
900 ANSI_STRING EntryFaceNameA;
901 UNICODE_STRING EntryFaceNameW;
902 FONTGDI *FontGDI;
903
904 Entry = Head->Flink;
905 while (Entry != Head)
906 {
907 CurrentEntry = (PFONT_ENTRY) CONTAINING_RECORD(Entry, FONT_ENTRY, ListEntry);
908
909 FontGDI = CurrentEntry->Font;
910 ASSERT(FontGDI);
911
912 RtlInitAnsiString(&EntryFaceNameA, FontGDI->face->family_name);
913 RtlAnsiStringToUnicodeString(&EntryFaceNameW, &EntryFaceNameA, TRUE);
914 if ((LF_FACESIZE - 1) * sizeof(WCHAR) < EntryFaceNameW.Length)
915 {
916 EntryFaceNameW.Length = (LF_FACESIZE - 1) * sizeof(WCHAR);
917 EntryFaceNameW.Buffer[LF_FACESIZE - 1] = L'\0';
918 }
919
920 if (0 == RtlCompareUnicodeString(FaceName, &EntryFaceNameW, TRUE))
921 {
922 RtlFreeUnicodeString(&EntryFaceNameW);
923 return FontGDI;
924 }
925
926 RtlFreeUnicodeString(&EntryFaceNameW);
927 Entry = Entry->Flink;
928 }
929
930 return NULL;
931 }
932
933 static PFONTGDI FASTCALL
934 FindFaceNameInLists(PUNICODE_STRING FaceName)
935 {
936 PPROCESSINFO Win32Process;
937 PFONTGDI Font;
938
939 /* Search the process local list */
940 Win32Process = PsGetCurrentProcessWin32Process();
941 IntLockProcessPrivateFonts(Win32Process);
942 Font = FindFaceNameInList(FaceName, &Win32Process->PrivateFontListHead);
943 IntUnLockProcessPrivateFonts(Win32Process);
944 if (NULL != Font)
945 {
946 return Font;
947 }
948
949 /* Search the global list */
950 IntLockGlobalFonts;
951 Font = FindFaceNameInList(FaceName, &FontListHead);
952 IntUnLockGlobalFonts;
953
954 return Font;
955 }
956
957 static void FASTCALL
958 FontFamilyFillInfo(PFONTFAMILYINFO Info, PCWSTR FaceName, PFONTGDI FontGDI)
959 {
960 ANSI_STRING StyleA;
961 UNICODE_STRING StyleW;
962 TT_OS2 *pOS2;
963 FONTSIGNATURE fs;
964 CHARSETINFO CharSetInfo;
965 unsigned i, Size;
966 OUTLINETEXTMETRICW *Otm;
967 LOGFONTW *Lf;
968 TEXTMETRICW *TM;
969 NEWTEXTMETRICW *Ntm;
970 DWORD fs0;
971
972 RtlZeroMemory(Info, sizeof(FONTFAMILYINFO));
973 Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL);
974 Otm = ExAllocatePoolWithTag(PagedPool, Size, GDITAG_TEXT);
975 if (!Otm)
976 {
977 return;
978 }
979 IntGetOutlineTextMetrics(FontGDI, Size, Otm);
980
981 Lf = &Info->EnumLogFontEx.elfLogFont;
982 TM = &Otm->otmTextMetrics;
983
984 Lf->lfHeight = TM->tmHeight;
985 Lf->lfWidth = TM->tmAveCharWidth;
986 Lf->lfWeight = TM->tmWeight;
987 Lf->lfItalic = TM->tmItalic;
988 Lf->lfPitchAndFamily = (TM->tmPitchAndFamily & 0xf1) + 1;
989 Lf->lfCharSet = TM->tmCharSet;
990 Lf->lfOutPrecision = OUT_OUTLINE_PRECIS;
991 Lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
992 Lf->lfQuality = PROOF_QUALITY;
993
994 Ntm = &Info->NewTextMetricEx.ntmTm;
995 Ntm->tmHeight = TM->tmHeight;
996 Ntm->tmAscent = TM->tmAscent;
997 Ntm->tmDescent = TM->tmDescent;
998 Ntm->tmInternalLeading = TM->tmInternalLeading;
999 Ntm->tmExternalLeading = TM->tmExternalLeading;
1000 Ntm->tmAveCharWidth = TM->tmAveCharWidth;
1001 Ntm->tmMaxCharWidth = TM->tmMaxCharWidth;
1002 Ntm->tmWeight = TM->tmWeight;
1003 Ntm->tmOverhang = TM->tmOverhang;
1004 Ntm->tmDigitizedAspectX = TM->tmDigitizedAspectX;
1005 Ntm->tmDigitizedAspectY = TM->tmDigitizedAspectY;
1006 Ntm->tmFirstChar = TM->tmFirstChar;
1007 Ntm->tmLastChar = TM->tmLastChar;
1008 Ntm->tmDefaultChar = TM->tmDefaultChar;
1009 Ntm->tmBreakChar = TM->tmBreakChar;
1010 Ntm->tmItalic = TM->tmItalic;
1011 Ntm->tmUnderlined = TM->tmUnderlined;
1012 Ntm->tmStruckOut = TM->tmStruckOut;
1013 Ntm->tmPitchAndFamily = TM->tmPitchAndFamily;
1014 Ntm->tmCharSet = TM->tmCharSet;
1015 Ntm->ntmFlags = TM->tmItalic ? NTM_ITALIC : 0;
1016
1017 if (550 < TM->tmWeight) Ntm->ntmFlags |= NTM_BOLD;
1018
1019 if (0 == Ntm->ntmFlags) Ntm->ntmFlags = NTM_REGULAR;
1020
1021 Ntm->ntmSizeEM = Otm->otmEMSquare;
1022 Ntm->ntmCellHeight = 0;
1023 Ntm->ntmAvgWidth = 0;
1024
1025 Info->FontType = (0 != (TM->tmPitchAndFamily & TMPF_TRUETYPE)
1026 ? TRUETYPE_FONTTYPE : 0);
1027
1028 if (0 == (TM->tmPitchAndFamily & TMPF_VECTOR))
1029 Info->FontType |= RASTER_FONTTYPE;
1030
1031 ExFreePoolWithTag(Otm, GDITAG_TEXT);
1032
1033 RtlStringCbCopyW(Info->EnumLogFontEx.elfLogFont.lfFaceName,
1034 sizeof(Info->EnumLogFontEx.elfLogFont.lfFaceName),
1035 FaceName);
1036 RtlStringCbCopyW(Info->EnumLogFontEx.elfFullName,
1037 sizeof(Info->EnumLogFontEx.elfFullName),
1038 FaceName);
1039 RtlInitAnsiString(&StyleA, FontGDI->face->style_name);
1040 StyleW.Buffer = Info->EnumLogFontEx.elfStyle;
1041 StyleW.MaximumLength = sizeof(Info->EnumLogFontEx.elfStyle);
1042 RtlAnsiStringToUnicodeString(&StyleW, &StyleA, FALSE);
1043
1044 Info->EnumLogFontEx.elfLogFont.lfCharSet = DEFAULT_CHARSET;
1045 Info->EnumLogFontEx.elfScript[0] = L'\0';
1046 IntLockFreeType;
1047 pOS2 = FT_Get_Sfnt_Table(FontGDI->face, ft_sfnt_os2);
1048 IntUnLockFreeType;
1049 if (NULL != pOS2)
1050 {
1051 fs.fsCsb[0] = pOS2->ulCodePageRange1;
1052 fs.fsCsb[1] = pOS2->ulCodePageRange2;
1053 fs.fsUsb[0] = pOS2->ulUnicodeRange1;
1054 fs.fsUsb[1] = pOS2->ulUnicodeRange2;
1055 fs.fsUsb[2] = pOS2->ulUnicodeRange3;
1056 fs.fsUsb[3] = pOS2->ulUnicodeRange4;
1057
1058 if (0 == pOS2->version)
1059 {
1060 FT_UInt Dummy;
1061
1062 if (FT_Get_First_Char(FontGDI->face, &Dummy) < 0x100)
1063 fs.fsCsb[0] |= FS_LATIN1;
1064 else
1065 fs.fsCsb[0] |= FS_SYMBOL;
1066 }
1067 if (fs.fsCsb[0] == 0)
1068 { /* Let's see if we can find any interesting cmaps */
1069 for (i = 0; i < FontGDI->face->num_charmaps; i++)
1070 {
1071 switch (FontGDI->face->charmaps[i]->encoding)
1072 {
1073 case FT_ENCODING_UNICODE:
1074 case FT_ENCODING_APPLE_ROMAN:
1075 fs.fsCsb[0] |= FS_LATIN1;
1076 break;
1077 case FT_ENCODING_MS_SYMBOL:
1078 fs.fsCsb[0] |= FS_SYMBOL;
1079 break;
1080 default:
1081 break;
1082 }
1083 }
1084 }
1085 for (i = 0; i < MAXTCIINDEX; i++)
1086 {
1087 fs0 = 1L << i;
1088 if (fs.fsCsb[0] & fs0)
1089 {
1090 if (!IntTranslateCharsetInfo(&fs0, &CharSetInfo, TCI_SRCFONTSIG))
1091 {
1092 CharSetInfo.ciCharset = DEFAULT_CHARSET;
1093 }
1094 if (DEFAULT_CHARSET != CharSetInfo.ciCharset)
1095 {
1096 Info->EnumLogFontEx.elfLogFont.lfCharSet = CharSetInfo.ciCharset;
1097 if (NULL != ElfScripts[i])
1098 wcscpy(Info->EnumLogFontEx.elfScript, ElfScripts[i]);
1099 else
1100 {
1101 DPRINT1("Unknown elfscript for bit %d\n", i);
1102 }
1103 }
1104 }
1105 }
1106 Info->NewTextMetricEx.ntmFontSig = fs;
1107 }
1108 }
1109
1110 static int FASTCALL
1111 FindFaceNameInInfo(PUNICODE_STRING FaceName, PFONTFAMILYINFO Info, DWORD InfoEntries)
1112 {
1113 DWORD i;
1114 UNICODE_STRING InfoFaceName;
1115
1116 for (i = 0; i < InfoEntries; i++)
1117 {
1118 RtlInitUnicodeString(&InfoFaceName, Info[i].EnumLogFontEx.elfLogFont.lfFaceName);
1119 if (0 == RtlCompareUnicodeString(&InfoFaceName, FaceName, TRUE))
1120 {
1121 return i;
1122 }
1123 }
1124
1125 return -1;
1126 }
1127
1128 static BOOLEAN FASTCALL
1129 FontFamilyInclude(LPLOGFONTW LogFont, PUNICODE_STRING FaceName,
1130 PFONTFAMILYINFO Info, DWORD InfoEntries)
1131 {
1132 UNICODE_STRING LogFontFaceName;
1133
1134 RtlInitUnicodeString(&LogFontFaceName, LogFont->lfFaceName);
1135 if (0 != LogFontFaceName.Length
1136 && 0 != RtlCompareUnicodeString(&LogFontFaceName, FaceName, TRUE))
1137 {
1138 return FALSE;
1139 }
1140
1141 return FindFaceNameInInfo(FaceName, Info, InfoEntries) < 0;
1142 }
1143
1144 static BOOLEAN FASTCALL
1145 GetFontFamilyInfoForList(LPLOGFONTW LogFont,
1146 PFONTFAMILYINFO Info,
1147 DWORD *Count,
1148 DWORD Size,
1149 PLIST_ENTRY Head)
1150 {
1151 PLIST_ENTRY Entry;
1152 PFONT_ENTRY CurrentEntry;
1153 ANSI_STRING EntryFaceNameA;
1154 UNICODE_STRING EntryFaceNameW;
1155 FONTGDI *FontGDI;
1156
1157 Entry = Head->Flink;
1158 while (Entry != Head)
1159 {
1160 CurrentEntry = (PFONT_ENTRY) CONTAINING_RECORD(Entry, FONT_ENTRY, ListEntry);
1161
1162 FontGDI = CurrentEntry->Font;
1163 ASSERT(FontGDI);
1164
1165 RtlInitAnsiString(&EntryFaceNameA, FontGDI->face->family_name);
1166 RtlAnsiStringToUnicodeString(&EntryFaceNameW, &EntryFaceNameA, TRUE);
1167 if ((LF_FACESIZE - 1) * sizeof(WCHAR) < EntryFaceNameW.Length)
1168 {
1169 EntryFaceNameW.Length = (LF_FACESIZE - 1) * sizeof(WCHAR);
1170 EntryFaceNameW.Buffer[LF_FACESIZE - 1] = L'\0';
1171 }
1172
1173 if (FontFamilyInclude(LogFont, &EntryFaceNameW, Info, min(*Count, Size)))
1174 {
1175 if (*Count < Size)
1176 {
1177 FontFamilyFillInfo(Info + *Count, EntryFaceNameW.Buffer, FontGDI);
1178 }
1179 (*Count)++;
1180 }
1181 RtlFreeUnicodeString(&EntryFaceNameW);
1182 Entry = Entry->Flink;
1183 }
1184
1185 return TRUE;
1186 }
1187
1188 typedef struct FontFamilyInfoCallbackContext
1189 {
1190 LPLOGFONTW LogFont;
1191 PFONTFAMILYINFO Info;
1192 DWORD Count;
1193 DWORD Size;
1194 } FONT_FAMILY_INFO_CALLBACK_CONTEXT, *PFONT_FAMILY_INFO_CALLBACK_CONTEXT;
1195
1196 static NTSTATUS APIENTRY
1197 FontFamilyInfoQueryRegistryCallback(IN PWSTR ValueName, IN ULONG ValueType,
1198 IN PVOID ValueData, IN ULONG ValueLength,
1199 IN PVOID Context, IN PVOID EntryContext)
1200 {
1201 PFONT_FAMILY_INFO_CALLBACK_CONTEXT InfoContext;
1202 UNICODE_STRING RegistryName, RegistryValue;
1203 int Existing;
1204 PFONTGDI FontGDI;
1205
1206 if (REG_SZ != ValueType)
1207 {
1208 return STATUS_SUCCESS;
1209 }
1210 InfoContext = (PFONT_FAMILY_INFO_CALLBACK_CONTEXT) Context;
1211 RtlInitUnicodeString(&RegistryName, ValueName);
1212
1213 /* Do we need to include this font family? */
1214 if (FontFamilyInclude(InfoContext->LogFont, &RegistryName, InfoContext->Info,
1215 min(InfoContext->Count, InfoContext->Size)))
1216 {
1217 RtlInitUnicodeString(&RegistryValue, (PCWSTR) ValueData);
1218 Existing = FindFaceNameInInfo(&RegistryValue, InfoContext->Info,
1219 min(InfoContext->Count, InfoContext->Size));
1220 if (0 <= Existing)
1221 {
1222 /* We already have the information about the "real" font. Just copy it */
1223 if (InfoContext->Count < InfoContext->Size)
1224 {
1225 InfoContext->Info[InfoContext->Count] = InfoContext->Info[Existing];
1226 RtlStringCbCopyNW(InfoContext->Info[InfoContext->Count].EnumLogFontEx.elfLogFont.lfFaceName,
1227 sizeof(InfoContext->Info[InfoContext->Count].EnumLogFontEx.elfLogFont.lfFaceName),
1228 RegistryName.Buffer,
1229 RegistryName.Length);
1230 }
1231 InfoContext->Count++;
1232 return STATUS_SUCCESS;
1233 }
1234
1235 /* Try to find information about the "real" font */
1236 FontGDI = FindFaceNameInLists(&RegistryValue);
1237 if (NULL == FontGDI)
1238 {
1239 /* "Real" font not found, discard this registry entry */
1240 return STATUS_SUCCESS;
1241 }
1242
1243 /* Return info about the "real" font but with the name of the alias */
1244 if (InfoContext->Count < InfoContext->Size)
1245 {
1246 FontFamilyFillInfo(InfoContext->Info + InfoContext->Count,
1247 RegistryName.Buffer, FontGDI);
1248 }
1249 InfoContext->Count++;
1250 return STATUS_SUCCESS;
1251 }
1252
1253 return STATUS_SUCCESS;
1254 }
1255
1256 static BOOLEAN FASTCALL
1257 GetFontFamilyInfoForSubstitutes(LPLOGFONTW LogFont,
1258 PFONTFAMILYINFO Info,
1259 DWORD *Count,
1260 DWORD Size)
1261 {
1262 RTL_QUERY_REGISTRY_TABLE QueryTable[2] = {{0}};
1263 FONT_FAMILY_INFO_CALLBACK_CONTEXT Context;
1264 NTSTATUS Status;
1265
1266 /* Enumerate font families found in HKLM\Software\Microsoft\Windows NT\CurrentVersion\SysFontSubstitutes
1267 The real work is done in the registry callback function */
1268 Context.LogFont = LogFont;
1269 Context.Info = Info;
1270 Context.Count = *Count;
1271 Context.Size = Size;
1272
1273 QueryTable[0].QueryRoutine = FontFamilyInfoQueryRegistryCallback;
1274 QueryTable[0].Flags = 0;
1275 QueryTable[0].Name = NULL;
1276 QueryTable[0].EntryContext = NULL;
1277 QueryTable[0].DefaultType = REG_NONE;
1278 QueryTable[0].DefaultData = NULL;
1279 QueryTable[0].DefaultLength = 0;
1280
1281 QueryTable[1].QueryRoutine = NULL;
1282 QueryTable[1].Name = NULL;
1283
1284 Status = RtlQueryRegistryValues(RTL_REGISTRY_WINDOWS_NT,
1285 L"SysFontSubstitutes",
1286 QueryTable,
1287 &Context,
1288 NULL);
1289 if (NT_SUCCESS(Status))
1290 {
1291 *Count = Context.Count;
1292 }
1293
1294 return NT_SUCCESS(Status) || STATUS_OBJECT_NAME_NOT_FOUND == Status;
1295 }
1296
1297 BOOL
1298 FASTCALL
1299 ftGdiGetRasterizerCaps(LPRASTERIZER_STATUS lprs)
1300 {
1301 if ( lprs )
1302 {
1303 lprs->nSize = sizeof(RASTERIZER_STATUS);
1304 lprs->wFlags = TT_AVAILABLE | TT_ENABLED;
1305 lprs->nLanguageID = gusLanguageID;
1306 return TRUE;
1307 }
1308 EngSetLastError(ERROR_INVALID_PARAMETER);
1309 return FALSE;
1310 }
1311
1312
1313 FT_BitmapGlyph APIENTRY
1314 ftGdiGlyphCacheGet(
1315 FT_Face Face,
1316 INT GlyphIndex,
1317 INT Height)
1318 {
1319 PLIST_ENTRY CurrentEntry;
1320 PFONT_CACHE_ENTRY FontEntry;
1321
1322 CurrentEntry = FontCacheListHead.Flink;
1323 while (CurrentEntry != &FontCacheListHead)
1324 {
1325 FontEntry = (PFONT_CACHE_ENTRY)CurrentEntry;
1326 if (FontEntry->Face == Face &&
1327 FontEntry->GlyphIndex == GlyphIndex &&
1328 FontEntry->Height == Height)
1329 break;
1330 CurrentEntry = CurrentEntry->Flink;
1331 }
1332
1333 if (CurrentEntry == &FontCacheListHead)
1334 {
1335 return NULL;
1336 }
1337
1338 RemoveEntryList(CurrentEntry);
1339 InsertHeadList(&FontCacheListHead, CurrentEntry);
1340 return FontEntry->BitmapGlyph;
1341 }
1342
1343 FT_BitmapGlyph APIENTRY
1344 ftGdiGlyphCacheSet(
1345 FT_Face Face,
1346 INT GlyphIndex,
1347 INT Height,
1348 FT_GlyphSlot GlyphSlot,
1349 FT_Render_Mode RenderMode)
1350 {
1351 FT_Glyph GlyphCopy;
1352 INT error;
1353 PFONT_CACHE_ENTRY NewEntry;
1354 FT_Bitmap AlignedBitmap;
1355 FT_BitmapGlyph BitmapGlyph;
1356
1357 error = FT_Get_Glyph(GlyphSlot, &GlyphCopy);
1358 if (error)
1359 {
1360 DPRINT1("Failure caching glyph.\n");
1361 return NULL;
1362 };
1363
1364 error = FT_Glyph_To_Bitmap(&GlyphCopy, RenderMode, 0, 1);
1365 if (error)
1366 {
1367 FT_Done_Glyph(GlyphCopy);
1368 DPRINT1("Failure rendering glyph.\n");
1369 return NULL;
1370 };
1371
1372 NewEntry = ExAllocatePoolWithTag(PagedPool, sizeof(FONT_CACHE_ENTRY), TAG_FONT);
1373 if (!NewEntry)
1374 {
1375 DPRINT1("Alloc failure caching glyph.\n");
1376 FT_Done_Glyph(GlyphCopy);
1377 return NULL;
1378 }
1379
1380 BitmapGlyph = (FT_BitmapGlyph)GlyphCopy;
1381 FT_Bitmap_New(&AlignedBitmap);
1382 if(FT_Bitmap_Convert(GlyphSlot->library, &BitmapGlyph->bitmap, &AlignedBitmap, 4))
1383 {
1384 DPRINT1("Conversion failed\n");
1385 ExFreePoolWithTag(NewEntry, TAG_FONT);
1386 FT_Done_Glyph((FT_Glyph)BitmapGlyph);
1387 return NULL;
1388 }
1389
1390 FT_Bitmap_Done(GlyphSlot->library, &BitmapGlyph->bitmap);
1391 BitmapGlyph->bitmap = AlignedBitmap;
1392
1393 NewEntry->GlyphIndex = GlyphIndex;
1394 NewEntry->Face = Face;
1395 NewEntry->BitmapGlyph = BitmapGlyph;
1396 NewEntry->Height = Height;
1397
1398 InsertHeadList(&FontCacheListHead, &NewEntry->ListEntry);
1399 if (FontCacheNumEntries++ > MAX_FONT_CACHE)
1400 {
1401 NewEntry = (PFONT_CACHE_ENTRY)FontCacheListHead.Blink;
1402 FT_Done_Glyph((FT_Glyph)NewEntry->BitmapGlyph);
1403 RemoveTailList(&FontCacheListHead);
1404 ExFreePool(NewEntry);
1405 FontCacheNumEntries--;
1406 }
1407
1408 return BitmapGlyph;
1409 }
1410
1411
1412 static
1413 void
1414 FTVectorToPOINTFX(FT_Vector *vec, POINTFX *pt)
1415 {
1416 pt->x.value = vec->x >> 6;
1417 pt->x.fract = (vec->x & 0x3f) << 10;
1418 pt->x.fract |= ((pt->x.fract >> 6) | (pt->x.fract >> 12));
1419 pt->y.value = vec->y >> 6;
1420 pt->y.fract = (vec->y & 0x3f) << 10;
1421 pt->y.fract |= ((pt->y.fract >> 6) | (pt->y.fract >> 12));
1422 return;
1423 }
1424
1425 /*
1426 This function builds an FT_Fixed from a float. It puts the integer part
1427 in the highest 16 bits and the decimal part in the lowest 16 bits of the FT_Fixed.
1428 It fails if the integer part of the float number is greater than SHORT_MAX.
1429 */
1430 static __inline FT_Fixed FT_FixedFromFloat(float f)
1431 {
1432 short value = f;
1433 unsigned short fract = (f - value) * 0xFFFF;
1434 return (FT_Fixed)((long)value << 16 | (unsigned long)fract);
1435 }
1436
1437 /*
1438 This function builds an FT_Fixed from a FIXED. It simply put f.value
1439 in the highest 16 bits and f.fract in the lowest 16 bits of the FT_Fixed.
1440 */
1441 static __inline FT_Fixed FT_FixedFromFIXED(FIXED f)
1442 {
1443 return (FT_Fixed)((long)f.value << 16 | (unsigned long)f.fract);
1444 }
1445
1446 /*
1447 * Based on WineEngGetGlyphOutline
1448 *
1449 */
1450 ULONG
1451 FASTCALL
1452 ftGdiGetGlyphOutline(
1453 PDC dc,
1454 WCHAR wch,
1455 UINT iFormat,
1456 LPGLYPHMETRICS pgm,
1457 ULONG cjBuf,
1458 PVOID pvBuf,
1459 LPMAT2 pmat2,
1460 BOOL bIgnoreRotation)
1461 {
1462 static const FT_Matrix identityMat = {(1 << 16), 0, 0, (1 << 16)};
1463 PDC_ATTR pdcattr;
1464 PTEXTOBJ TextObj;
1465 PFONTGDI FontGDI;
1466 HFONT hFont = 0;
1467 GLYPHMETRICS gm;
1468 ULONG Size;
1469 FT_Face ft_face;
1470 FT_UInt glyph_index;
1471 DWORD width, height, pitch, needed = 0;
1472 FT_Bitmap ft_bitmap;
1473 FT_Error error;
1474 INT left, right, top = 0, bottom = 0;
1475 FT_Angle angle = 0;
1476 FT_Int load_flags = FT_LOAD_DEFAULT | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
1477 FLOAT eM11, widthRatio = 1.0;
1478 FT_Matrix transMat = identityMat;
1479 BOOL needsTransform = FALSE;
1480 INT orientation;
1481 LONG aveWidth;
1482 INT adv, lsb, bbx; /* These three hold to widths of the unrotated chars */
1483 OUTLINETEXTMETRICW *potm;
1484 int n = 0;
1485 FT_CharMap found = 0, charmap;
1486 XFORM xForm;
1487
1488 DPRINT("%d, %08x, %p, %08lx, %p, %p\n", wch, iFormat, pgm,
1489 cjBuf, pvBuf, pmat2);
1490
1491 pdcattr = dc->pdcattr;
1492
1493 MatrixS2XForm(&xForm, &dc->dclevel.mxWorldToDevice);
1494 eM11 = xForm.eM11;
1495
1496 hFont = pdcattr->hlfntNew;
1497 TextObj = RealizeFontInit(hFont);
1498
1499 if (!TextObj)
1500 {
1501 EngSetLastError(ERROR_INVALID_HANDLE);
1502 return GDI_ERROR;
1503 }
1504 FontGDI = ObjToGDI(TextObj->Font, FONT);
1505 ft_face = FontGDI->face;
1506
1507 aveWidth = FT_IS_SCALABLE(ft_face) ? TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfWidth: 0;
1508 orientation = FT_IS_SCALABLE(ft_face) ? TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfOrientation: 0;
1509
1510 Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL);
1511 potm = ExAllocatePoolWithTag(PagedPool, Size, GDITAG_TEXT);
1512 if (!potm)
1513 {
1514 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1515 TEXTOBJ_UnlockText(TextObj);
1516 return GDI_ERROR;
1517 }
1518 IntGetOutlineTextMetrics(FontGDI, Size, potm);
1519
1520 IntLockFreeType;
1521
1522 /* During testing, I never saw this used. It is here just in case. */
1523 if (ft_face->charmap == NULL)
1524 {
1525 DPRINT("WARNING: No charmap selected!\n");
1526 DPRINT("This font face has %d charmaps\n", ft_face->num_charmaps);
1527
1528 for (n = 0; n < ft_face->num_charmaps; n++)
1529 {
1530 charmap = ft_face->charmaps[n];
1531 DPRINT("Found charmap encoding: %u\n", charmap->encoding);
1532 if (charmap->encoding != 0)
1533 {
1534 found = charmap;
1535 break;
1536 }
1537 }
1538 if (!found)
1539 {
1540 DPRINT1("WARNING: Could not find desired charmap!\n");
1541 }
1542 error = FT_Set_Charmap(ft_face, found);
1543 if (error)
1544 {
1545 DPRINT1("WARNING: Could not set the charmap!\n");
1546 }
1547 }
1548
1549 // FT_Set_Pixel_Sizes(ft_face,
1550 // TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfWidth,
1551 /* FIXME: Should set character height if neg */
1552 // (TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight == 0 ?
1553 // dc->ppdev->devinfo.lfDefaultFont.lfHeight : abs(TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight)));
1554
1555 TEXTOBJ_UnlockText(TextObj);
1556
1557 if (iFormat & GGO_GLYPH_INDEX)
1558 {
1559 glyph_index = wch;
1560 iFormat &= ~GGO_GLYPH_INDEX;
1561 }
1562 else glyph_index = FT_Get_Char_Index(ft_face, wch);
1563
1564 if (orientation || (iFormat != GGO_METRICS && iFormat != GGO_BITMAP) || aveWidth || pmat2)
1565 load_flags |= FT_LOAD_NO_BITMAP;
1566
1567 if (iFormat & GGO_UNHINTED)
1568 {
1569 load_flags |= FT_LOAD_NO_HINTING;
1570 iFormat &= ~GGO_UNHINTED;
1571 }
1572
1573 error = FT_Load_Glyph(ft_face, glyph_index, load_flags);
1574 if (error)
1575 {
1576 DPRINT1("WARNING: Failed to load and render glyph! [index: %u]\n", glyph_index);
1577 IntUnLockFreeType;
1578 if (potm) ExFreePoolWithTag(potm, GDITAG_TEXT);
1579 return GDI_ERROR;
1580 }
1581 IntUnLockFreeType;
1582
1583 if (aveWidth && potm)
1584 {
1585 widthRatio = (FLOAT)aveWidth * eM11 /
1586 (FLOAT) potm->otmTextMetrics.tmAveCharWidth;
1587 }
1588
1589 left = (INT)(ft_face->glyph->metrics.horiBearingX * widthRatio) & -64;
1590 right = (INT)((ft_face->glyph->metrics.horiBearingX +
1591 ft_face->glyph->metrics.width) * widthRatio + 63) & -64;
1592
1593 adv = (INT)((ft_face->glyph->metrics.horiAdvance * widthRatio) + 63) >> 6;
1594 lsb = left >> 6;
1595 bbx = (right - left) >> 6;
1596
1597 DPRINT("Advance = %d, lsb = %d, bbx = %d\n",adv, lsb, bbx);
1598
1599 IntLockFreeType;
1600
1601 /* Scaling transform */
1602 if (aveWidth)
1603 {
1604 FT_Matrix scaleMat;
1605 DPRINT("Scaling Trans!\n");
1606 scaleMat.xx = FT_FixedFromFloat(widthRatio);
1607 scaleMat.xy = 0;
1608 scaleMat.yx = 0;
1609 scaleMat.yy = (1 << 16);
1610 FT_Matrix_Multiply(&scaleMat, &transMat);
1611 needsTransform = TRUE;
1612 }
1613
1614 /* Slant transform */
1615 if (potm->otmTextMetrics.tmItalic)
1616 {
1617 FT_Matrix slantMat;
1618 DPRINT("Slant Trans!\n");
1619 slantMat.xx = (1 << 16);
1620 slantMat.xy = ((1 << 16) >> 2);
1621 slantMat.yx = 0;
1622 slantMat.yy = (1 << 16);
1623 FT_Matrix_Multiply(&slantMat, &transMat);
1624 needsTransform = TRUE;
1625 }
1626
1627 /* Rotation transform */
1628 if (orientation)
1629 {
1630 FT_Matrix rotationMat;
1631 FT_Vector vecAngle;
1632 DPRINT("Rotation Trans!\n");
1633 angle = FT_FixedFromFloat((float)orientation / 10.0);
1634 FT_Vector_Unit(&vecAngle, angle);
1635 rotationMat.xx = vecAngle.x;
1636 rotationMat.xy = -vecAngle.y;
1637 rotationMat.yx = -rotationMat.xy;
1638 rotationMat.yy = rotationMat.xx;
1639 FT_Matrix_Multiply(&rotationMat, &transMat);
1640 needsTransform = TRUE;
1641 }
1642
1643 /* Extra transformation specified by caller */
1644 if (pmat2)
1645 {
1646 FT_Matrix extraMat;
1647 DPRINT("MAT2 Matrix Trans!\n");
1648 extraMat.xx = FT_FixedFromFIXED(pmat2->eM11);
1649 extraMat.xy = FT_FixedFromFIXED(pmat2->eM21);
1650 extraMat.yx = FT_FixedFromFIXED(pmat2->eM12);
1651 extraMat.yy = FT_FixedFromFIXED(pmat2->eM22);
1652 FT_Matrix_Multiply(&extraMat, &transMat);
1653 needsTransform = TRUE;
1654 }
1655
1656 if (potm) ExFreePoolWithTag(potm, GDITAG_TEXT); /* It looks like we are finished with potm ATM. */
1657
1658 if (!needsTransform)
1659 {
1660 DPRINT("No Need to be Transformed!\n");
1661 top = (ft_face->glyph->metrics.horiBearingY + 63) & -64;
1662 bottom = (ft_face->glyph->metrics.horiBearingY -
1663 ft_face->glyph->metrics.height) & -64;
1664 gm.gmCellIncX = adv;
1665 gm.gmCellIncY = 0;
1666 }
1667 else
1668 {
1669 INT xc, yc;
1670 FT_Vector vec;
1671 for (xc = 0; xc < 2; xc++)
1672 {
1673 for (yc = 0; yc < 2; yc++)
1674 {
1675 vec.x = (ft_face->glyph->metrics.horiBearingX +
1676 xc * ft_face->glyph->metrics.width);
1677 vec.y = ft_face->glyph->metrics.horiBearingY -
1678 yc * ft_face->glyph->metrics.height;
1679 DPRINT("Vec %ld,%ld\n", vec.x, vec.y);
1680 FT_Vector_Transform(&vec, &transMat);
1681 if (xc == 0 && yc == 0)
1682 {
1683 left = right = vec.x;
1684 top = bottom = vec.y;
1685 }
1686 else
1687 {
1688 if (vec.x < left) left = vec.x;
1689 else if (vec.x > right) right = vec.x;
1690 if (vec.y < bottom) bottom = vec.y;
1691 else if (vec.y > top) top = vec.y;
1692 }
1693 }
1694 }
1695 left = left & -64;
1696 right = (right + 63) & -64;
1697 bottom = bottom & -64;
1698 top = (top + 63) & -64;
1699
1700 DPRINT("Transformed box: (%d,%d - %d,%d)\n", left, top, right, bottom);
1701 vec.x = ft_face->glyph->metrics.horiAdvance;
1702 vec.y = 0;
1703 FT_Vector_Transform(&vec, &transMat);
1704 gm.gmCellIncX = (vec.x+63) >> 6;
1705 gm.gmCellIncY = -((vec.y+63) >> 6);
1706 }
1707 gm.gmBlackBoxX = (right - left) >> 6;
1708 gm.gmBlackBoxY = (top - bottom) >> 6;
1709 gm.gmptGlyphOrigin.x = left >> 6;
1710 gm.gmptGlyphOrigin.y = top >> 6;
1711
1712 DPRINT("CX %d CY %d BBX %d BBY %d GOX %d GOY %d\n",
1713 gm.gmCellIncX, gm.gmCellIncY,
1714 gm.gmBlackBoxX, gm.gmBlackBoxY,
1715 gm.gmptGlyphOrigin.x, gm.gmptGlyphOrigin.y);
1716
1717 IntUnLockFreeType;
1718
1719 if (pgm) RtlCopyMemory(pgm, &gm, sizeof(GLYPHMETRICS));
1720
1721 if (iFormat == GGO_METRICS)
1722 {
1723 DPRINT("GGO_METRICS Exit!\n");
1724 return 1; /* FIXME */
1725 }
1726
1727 if (ft_face->glyph->format != ft_glyph_format_outline && iFormat != GGO_BITMAP)
1728 {
1729 DPRINT1("Loaded a bitmap\n");
1730 return GDI_ERROR;
1731 }
1732
1733 switch (iFormat)
1734 {
1735 case GGO_BITMAP:
1736 width = gm.gmBlackBoxX;
1737 height = gm.gmBlackBoxY;
1738 pitch = ((width + 31) >> 5) << 2;
1739 needed = pitch * height;
1740
1741 if (!pvBuf || !cjBuf) break;
1742
1743 switch (ft_face->glyph->format)
1744 {
1745 case ft_glyph_format_bitmap:
1746 {
1747 BYTE *src = ft_face->glyph->bitmap.buffer, *dst = pvBuf;
1748 INT w = (ft_face->glyph->bitmap.width + 7) >> 3;
1749 INT h = ft_face->glyph->bitmap.rows;
1750 while (h--)
1751 {
1752 RtlCopyMemory(dst, src, w);
1753 src += ft_face->glyph->bitmap.pitch;
1754 dst += pitch;
1755 }
1756 break;
1757 }
1758
1759 case ft_glyph_format_outline:
1760 ft_bitmap.width = width;
1761 ft_bitmap.rows = height;
1762 ft_bitmap.pitch = pitch;
1763 ft_bitmap.pixel_mode = ft_pixel_mode_mono;
1764 ft_bitmap.buffer = pvBuf;
1765
1766 IntLockFreeType;
1767 if (needsTransform)
1768 {
1769 FT_Outline_Transform(&ft_face->glyph->outline, &transMat);
1770 }
1771 FT_Outline_Translate(&ft_face->glyph->outline, -left, -bottom );
1772 /* Note: FreeType will only set 'black' bits for us. */
1773 RtlZeroMemory(pvBuf, needed);
1774 FT_Outline_Get_Bitmap(library, &ft_face->glyph->outline, &ft_bitmap);
1775 IntUnLockFreeType;
1776 break;
1777
1778 default:
1779 DPRINT1("Loaded glyph format %x\n", ft_face->glyph->format);
1780 return GDI_ERROR;
1781 }
1782 break;
1783
1784 case GGO_GRAY2_BITMAP:
1785 case GGO_GRAY4_BITMAP:
1786 case GGO_GRAY8_BITMAP:
1787 {
1788 unsigned int mult, row, col;
1789 BYTE *start, *ptr;
1790
1791 width = gm.gmBlackBoxX;
1792 height = gm.gmBlackBoxY;
1793 pitch = (width + 3) / 4 * 4;
1794 needed = pitch * height;
1795
1796 if (!pvBuf || !cjBuf) break;
1797
1798 switch (ft_face->glyph->format)
1799 {
1800 case ft_glyph_format_bitmap:
1801 {
1802 BYTE *src = ft_face->glyph->bitmap.buffer, *dst = pvBuf;
1803 INT h = ft_face->glyph->bitmap.rows;
1804 INT x;
1805 while (h--)
1806 {
1807 for (x = 0; x < pitch; x++)
1808 {
1809 if (x < ft_face->glyph->bitmap.width)
1810 dst[x] = (src[x / 8] & (1 << ( (7 - (x % 8))))) ? 0xff : 0;
1811 else
1812 dst[x] = 0;
1813 }
1814 src += ft_face->glyph->bitmap.pitch;
1815 dst += pitch;
1816 }
1817 return needed;
1818 }
1819 case ft_glyph_format_outline:
1820 {
1821 ft_bitmap.width = width;
1822 ft_bitmap.rows = height;
1823 ft_bitmap.pitch = pitch;
1824 ft_bitmap.pixel_mode = ft_pixel_mode_grays;
1825 ft_bitmap.buffer = pvBuf;
1826
1827 IntLockFreeType;
1828 if (needsTransform)
1829 {
1830 FT_Outline_Transform(&ft_face->glyph->outline, &transMat);
1831 }
1832 FT_Outline_Translate(&ft_face->glyph->outline, -left, -bottom );
1833 RtlZeroMemory(ft_bitmap.buffer, cjBuf);
1834 FT_Outline_Get_Bitmap(library, &ft_face->glyph->outline, &ft_bitmap);
1835 IntUnLockFreeType;
1836
1837 if (iFormat == GGO_GRAY2_BITMAP)
1838 mult = 4;
1839 else if (iFormat == GGO_GRAY4_BITMAP)
1840 mult = 16;
1841 else if (iFormat == GGO_GRAY8_BITMAP)
1842 mult = 64;
1843 else
1844 {
1845 return GDI_ERROR;
1846 }
1847 }
1848 default:
1849 DPRINT1("Loaded glyph format %x\n", ft_face->glyph->format);
1850 return GDI_ERROR;
1851 }
1852 start = pvBuf;
1853 for (row = 0; row < height; row++)
1854 {
1855 ptr = start;
1856 for (col = 0; col < width; col++, ptr++)
1857 {
1858 *ptr = (((int)*ptr) * mult + 128) / 256;
1859 }
1860 start += pitch;
1861 }
1862 break;
1863 }
1864
1865 case GGO_NATIVE:
1866 {
1867 int contour, point = 0, first_pt;
1868 FT_Outline *outline = &ft_face->glyph->outline;
1869 TTPOLYGONHEADER *pph;
1870 TTPOLYCURVE *ppc;
1871 DWORD pph_start, cpfx, type;
1872
1873 if (cjBuf == 0) pvBuf = NULL; /* This is okay, need cjBuf to allocate. */
1874
1875 IntLockFreeType;
1876 if (needsTransform && pvBuf) FT_Outline_Transform(outline, &transMat);
1877
1878 for (contour = 0; contour < outline->n_contours; contour++)
1879 {
1880 pph_start = needed;
1881 pph = (TTPOLYGONHEADER *)((char *)pvBuf + needed);
1882 first_pt = point;
1883 if (pvBuf)
1884 {
1885 pph->dwType = TT_POLYGON_TYPE;
1886 FTVectorToPOINTFX(&outline->points[point], &pph->pfxStart);
1887 }
1888 needed += sizeof(*pph);
1889 point++;
1890 while (point <= outline->contours[contour])
1891 {
1892 ppc = (TTPOLYCURVE *)((char *)pvBuf + needed);
1893 type = (outline->tags[point] & FT_Curve_Tag_On) ?
1894 TT_PRIM_LINE : TT_PRIM_QSPLINE;
1895 cpfx = 0;
1896 do
1897 {
1898 if (pvBuf)
1899 FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
1900 cpfx++;
1901 point++;
1902 }
1903 while (point <= outline->contours[contour] &&
1904 (outline->tags[point] & FT_Curve_Tag_On) ==
1905 (outline->tags[point-1] & FT_Curve_Tag_On));
1906
1907 /* At the end of a contour Windows adds the start point, but
1908 only for Beziers */
1909 if (point > outline->contours[contour] &&
1910 !(outline->tags[point-1] & FT_Curve_Tag_On))
1911 {
1912 if (pvBuf)
1913 FTVectorToPOINTFX(&outline->points[first_pt], &ppc->apfx[cpfx]);
1914 cpfx++;
1915 }
1916 else if (point <= outline->contours[contour] &&
1917 outline->tags[point] & FT_Curve_Tag_On)
1918 {
1919 /* Add closing pt for bezier */
1920 if (pvBuf)
1921 FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
1922 cpfx++;
1923 point++;
1924 }
1925 if (pvBuf)
1926 {
1927 ppc->wType = type;
1928 ppc->cpfx = cpfx;
1929 }
1930 needed += sizeof(*ppc) + (cpfx - 1) * sizeof(POINTFX);
1931 }
1932 if (pvBuf) pph->cb = needed - pph_start;
1933 }
1934 IntUnLockFreeType;
1935 break;
1936 }
1937 case GGO_BEZIER:
1938 {
1939 /* Convert the quadratic Beziers to cubic Beziers.
1940 The parametric eqn for a cubic Bezier is, from PLRM:
1941 r(t) = at^3 + bt^2 + ct + r0
1942 with the control points:
1943 r1 = r0 + c/3
1944 r2 = r1 + (c + b)/3
1945 r3 = r0 + c + b + a
1946
1947 A quadratic Beizer has the form:
1948 p(t) = (1-t)^2 p0 + 2(1-t)t p1 + t^2 p2
1949
1950 So equating powers of t leads to:
1951 r1 = 2/3 p1 + 1/3 p0
1952 r2 = 2/3 p1 + 1/3 p2
1953 and of course r0 = p0, r3 = p2
1954 */
1955
1956 int contour, point = 0, first_pt;
1957 FT_Outline *outline = &ft_face->glyph->outline;
1958 TTPOLYGONHEADER *pph;
1959 TTPOLYCURVE *ppc;
1960 DWORD pph_start, cpfx, type;
1961 FT_Vector cubic_control[4];
1962 if (cjBuf == 0) pvBuf = NULL;
1963
1964 if (needsTransform && pvBuf)
1965 {
1966 IntLockFreeType;
1967 FT_Outline_Transform(outline, &transMat);
1968 IntUnLockFreeType;
1969 }
1970
1971 for (contour = 0; contour < outline->n_contours; contour++)
1972 {
1973 pph_start = needed;
1974 pph = (TTPOLYGONHEADER *)((char *)pvBuf + needed);
1975 first_pt = point;
1976 if (pvBuf)
1977 {
1978 pph->dwType = TT_POLYGON_TYPE;
1979 FTVectorToPOINTFX(&outline->points[point], &pph->pfxStart);
1980 }
1981 needed += sizeof(*pph);
1982 point++;
1983 while (point <= outline->contours[contour])
1984 {
1985 ppc = (TTPOLYCURVE *)((char *)pvBuf + needed);
1986 type = (outline->tags[point] & FT_Curve_Tag_On) ?
1987 TT_PRIM_LINE : TT_PRIM_CSPLINE;
1988 cpfx = 0;
1989 do
1990 {
1991 if (type == TT_PRIM_LINE)
1992 {
1993 if (pvBuf)
1994 FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
1995 cpfx++;
1996 point++;
1997 }
1998 else
1999 {
2000 /* Unlike QSPLINEs, CSPLINEs always have their endpoint
2001 so cpfx = 3n */
2002
2003 /* FIXME: Possible optimization in endpoint calculation
2004 if there are two consecutive curves */
2005 cubic_control[0] = outline->points[point-1];
2006 if (!(outline->tags[point-1] & FT_Curve_Tag_On))
2007 {
2008 cubic_control[0].x += outline->points[point].x + 1;
2009 cubic_control[0].y += outline->points[point].y + 1;
2010 cubic_control[0].x >>= 1;
2011 cubic_control[0].y >>= 1;
2012 }
2013 if (point+1 > outline->contours[contour])
2014 cubic_control[3] = outline->points[first_pt];
2015 else
2016 {
2017 cubic_control[3] = outline->points[point+1];
2018 if (!(outline->tags[point+1] & FT_Curve_Tag_On))
2019 {
2020 cubic_control[3].x += outline->points[point].x + 1;
2021 cubic_control[3].y += outline->points[point].y + 1;
2022 cubic_control[3].x >>= 1;
2023 cubic_control[3].y >>= 1;
2024 }
2025 }
2026 /* r1 = 1/3 p0 + 2/3 p1
2027 r2 = 1/3 p2 + 2/3 p1 */
2028 cubic_control[1].x = (2 * outline->points[point].x + 1) / 3;
2029 cubic_control[1].y = (2 * outline->points[point].y + 1) / 3;
2030 cubic_control[2] = cubic_control[1];
2031 cubic_control[1].x += (cubic_control[0].x + 1) / 3;
2032 cubic_control[1].y += (cubic_control[0].y + 1) / 3;
2033 cubic_control[2].x += (cubic_control[3].x + 1) / 3;
2034 cubic_control[2].y += (cubic_control[3].y + 1) / 3;
2035 if (pvBuf)
2036 {
2037 FTVectorToPOINTFX(&cubic_control[1], &ppc->apfx[cpfx]);
2038 FTVectorToPOINTFX(&cubic_control[2], &ppc->apfx[cpfx+1]);
2039 FTVectorToPOINTFX(&cubic_control[3], &ppc->apfx[cpfx+2]);
2040 }
2041 cpfx += 3;
2042 point++;
2043 }
2044 }
2045 while (point <= outline->contours[contour] &&
2046 (outline->tags[point] & FT_Curve_Tag_On) ==
2047 (outline->tags[point-1] & FT_Curve_Tag_On));
2048 /* At the end of a contour Windows adds the start point,
2049 but only for Beziers and we've already done that. */
2050 if (point <= outline->contours[contour] &&
2051 outline->tags[point] & FT_Curve_Tag_On)
2052 {
2053 /* This is the closing pt of a bezier, but we've already
2054 added it, so just inc point and carry on */
2055 point++;
2056 }
2057 if (pvBuf)
2058 {
2059 ppc->wType = type;
2060 ppc->cpfx = cpfx;
2061 }
2062 needed += sizeof(*ppc) + (cpfx - 1) * sizeof(POINTFX);
2063 }
2064 if (pvBuf) pph->cb = needed - pph_start;
2065 }
2066 break;
2067 }
2068
2069 default:
2070 DPRINT1("Unsupported format %d\n", iFormat);
2071 return GDI_ERROR;
2072 }
2073
2074 DPRINT("ftGdiGetGlyphOutline END and needed %d\n", needed);
2075 return needed;
2076 }
2077
2078 BOOL
2079 FASTCALL
2080 TextIntGetTextExtentPoint(PDC dc,
2081 PTEXTOBJ TextObj,
2082 LPCWSTR String,
2083 INT Count,
2084 ULONG MaxExtent,
2085 LPINT Fit,
2086 LPINT Dx,
2087 LPSIZE Size,
2088 FLONG fl)
2089 {
2090 PFONTGDI FontGDI;
2091 FT_Face face;
2092 FT_GlyphSlot glyph;
2093 FT_BitmapGlyph realglyph;
2094 INT error, n, glyph_index, i, previous;
2095 ULONGLONG TotalWidth = 0;
2096 FT_CharMap charmap, found = NULL;
2097 BOOL use_kerning;
2098 FT_Render_Mode RenderMode;
2099 BOOLEAN Render;
2100
2101 FontGDI = ObjToGDI(TextObj->Font, FONT);
2102
2103 face = FontGDI->face;
2104 if (NULL != Fit)
2105 {
2106 *Fit = 0;
2107 }
2108
2109 IntLockFreeType;
2110 if (face->charmap == NULL)
2111 {
2112 DPRINT("WARNING: No charmap selected!\n");
2113 DPRINT("This font face has %d charmaps\n", face->num_charmaps);
2114
2115 for (n = 0; n < face->num_charmaps; n++)
2116 {
2117 charmap = face->charmaps[n];
2118 DPRINT("Found charmap encoding: %u\n", charmap->encoding);
2119 if (charmap->encoding != 0)
2120 {
2121 found = charmap;
2122 break;
2123 }
2124 }
2125
2126 if (! found)
2127 {
2128 DPRINT1("WARNING: Could not find desired charmap!\n");
2129 }
2130
2131 error = FT_Set_Charmap(face, found);
2132 if (error)
2133 {
2134 DPRINT1("WARNING: Could not set the charmap!\n");
2135 }
2136 }
2137
2138 Render = IntIsFontRenderingEnabled();
2139 if (Render)
2140 RenderMode = IntGetFontRenderMode(&TextObj->logfont.elfEnumLogfontEx.elfLogFont);
2141 else
2142 RenderMode = FT_RENDER_MODE_MONO;
2143
2144 error = FT_Set_Pixel_Sizes(face,
2145 TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfWidth,
2146 /* FIXME: Should set character height if neg */
2147 (TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight == 0 ?
2148 dc->ppdev->devinfo.lfDefaultFont.lfHeight : abs(TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight)));
2149 if (error)
2150 {
2151 DPRINT1("Error in setting pixel sizes: %u\n", error);
2152 }
2153
2154 use_kerning = FT_HAS_KERNING(face);
2155 previous = 0;
2156
2157 for (i = 0; i < Count; i++)
2158 {
2159 if (fl & GTEF_INDICES)
2160 glyph_index = *String;
2161 else
2162 glyph_index = FT_Get_Char_Index(face, *String);
2163
2164 if (!(realglyph = ftGdiGlyphCacheGet(face, glyph_index,
2165 TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight)))
2166 {
2167 error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
2168 if (error)
2169 {
2170 DPRINT1("WARNING: Failed to load and render glyph! [index: %u]\n", glyph_index);
2171 break;
2172 }
2173
2174 glyph = face->glyph;
2175 realglyph = ftGdiGlyphCacheSet(face, glyph_index,
2176 TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight, glyph, RenderMode);
2177 if (!realglyph)
2178 {
2179 DPRINT1("Failed to render glyph! [index: %u]\n", glyph_index);
2180 break;
2181 }
2182 }
2183
2184 /* Retrieve kerning distance */
2185 if (use_kerning && previous && glyph_index)
2186 {
2187 FT_Vector delta;
2188 FT_Get_Kerning(face, previous, glyph_index, 0, &delta);
2189 TotalWidth += delta.x;
2190 }
2191
2192 TotalWidth += realglyph->root.advance.x >> 10;
2193
2194 if (((TotalWidth + 32) >> 6) <= MaxExtent && NULL != Fit)
2195 {
2196 *Fit = i + 1;
2197 }
2198 if (NULL != Dx)
2199 {
2200 Dx[i] = (TotalWidth + 32) >> 6;
2201 }
2202
2203 previous = glyph_index;
2204 String++;
2205 }
2206 IntUnLockFreeType;
2207
2208 Size->cx = (TotalWidth + 32) >> 6;
2209 Size->cy = (TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight == 0 ?
2210 dc->ppdev->devinfo.lfDefaultFont.lfHeight : abs(TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight));
2211 Size->cy = EngMulDiv(Size->cy, dc->ppdev->gdiinfo.ulLogPixelsY, 72);
2212
2213 return TRUE;
2214 }
2215
2216
2217 INT
2218 FASTCALL
2219 ftGdiGetTextCharsetInfo(
2220 PDC Dc,
2221 LPFONTSIGNATURE lpSig,
2222 DWORD dwFlags)
2223 {
2224 PDC_ATTR pdcattr;
2225 UINT Ret = DEFAULT_CHARSET, i;
2226 HFONT hFont;
2227 PTEXTOBJ TextObj;
2228 PFONTGDI FontGdi;
2229 FONTSIGNATURE fs;
2230 TT_OS2 *pOS2;
2231 FT_Face Face;
2232 CHARSETINFO csi;
2233 DWORD cp, fs0;
2234 USHORT usACP, usOEM;
2235
2236 pdcattr = Dc->pdcattr;
2237 hFont = pdcattr->hlfntNew;
2238 TextObj = RealizeFontInit(hFont);
2239
2240 if (!TextObj)
2241 {
2242 EngSetLastError(ERROR_INVALID_HANDLE);
2243 return Ret;
2244 }
2245 FontGdi = ObjToGDI(TextObj->Font, FONT);
2246 Face = FontGdi->face;
2247 TEXTOBJ_UnlockText(TextObj);
2248
2249 IntLockFreeType;
2250 pOS2 = FT_Get_Sfnt_Table(Face, ft_sfnt_os2);
2251 IntUnLockFreeType;
2252 memset(&fs, 0, sizeof(FONTSIGNATURE));
2253 if (NULL != pOS2)
2254 {
2255 fs.fsCsb[0] = pOS2->ulCodePageRange1;
2256 fs.fsCsb[1] = pOS2->ulCodePageRange2;
2257 fs.fsUsb[0] = pOS2->ulUnicodeRange1;
2258 fs.fsUsb[1] = pOS2->ulUnicodeRange2;
2259 fs.fsUsb[2] = pOS2->ulUnicodeRange3;
2260 fs.fsUsb[3] = pOS2->ulUnicodeRange4;
2261 if (pOS2->version == 0)
2262 {
2263 FT_UInt dummy;
2264
2265 if (FT_Get_First_Char( Face, &dummy ) < 0x100)
2266 fs.fsCsb[0] |= FS_LATIN1;
2267 else
2268 fs.fsCsb[0] |= FS_SYMBOL;
2269 }
2270 }
2271 DPRINT("Csb 1=%x 0=%x\n", fs.fsCsb[1],fs.fsCsb[0]);
2272 if (fs.fsCsb[0] == 0)
2273 { /* Let's see if we can find any interesting cmaps */
2274 for (i = 0; i < Face->num_charmaps; i++)
2275 {
2276 switch (Face->charmaps[i]->encoding)
2277 {
2278 case FT_ENCODING_UNICODE:
2279 case FT_ENCODING_APPLE_ROMAN:
2280 fs.fsCsb[0] |= FS_LATIN1;
2281 break;
2282 case FT_ENCODING_MS_SYMBOL:
2283 fs.fsCsb[0] |= FS_SYMBOL;
2284 break;
2285 default:
2286 break;
2287 }
2288 }
2289 }
2290 if (lpSig)
2291 {
2292 RtlCopyMemory(lpSig, &fs, sizeof(FONTSIGNATURE));
2293 }
2294
2295 RtlGetDefaultCodePage(&usACP, &usOEM);
2296 cp = usACP;
2297
2298 if (IntTranslateCharsetInfo(&cp, &csi, TCI_SRCCODEPAGE))
2299 if (csi.fs.fsCsb[0] & fs.fsCsb[0])
2300 {
2301 DPRINT("Hit 1\n");
2302 Ret = csi.ciCharset;
2303 goto Exit;
2304 }
2305
2306 for (i = 0; i < MAXTCIINDEX; i++)
2307 {
2308 fs0 = 1L << i;
2309 if (fs.fsCsb[0] & fs0)
2310 {
2311 if (IntTranslateCharsetInfo(&fs0, &csi, TCI_SRCFONTSIG))
2312 {
2313 // *cp = csi.ciACP;
2314 DPRINT("Hit 2\n");
2315 Ret = csi.ciCharset;
2316 goto Exit;
2317 }
2318 else
2319 DPRINT1("TCI failing on %x\n", fs0);
2320 }
2321 }
2322 Exit:
2323 DPRINT("CharSet %d CodePage %d\n",csi.ciCharset, csi.ciACP);
2324 return (MAKELONG(csi.ciACP, csi.ciCharset));
2325 }
2326
2327
2328 DWORD
2329 FASTCALL
2330 ftGetFontUnicodeRanges(PFONTGDI Font, PGLYPHSET glyphset)
2331 {
2332 DWORD size = 0;
2333 DWORD num_ranges = 0;
2334 FT_Face face = Font->face;
2335
2336 if (face->charmap->encoding == FT_ENCODING_UNICODE)
2337 {
2338 FT_UInt glyph_code = 0;
2339 FT_ULong char_code, char_code_prev;
2340
2341 char_code_prev = char_code = FT_Get_First_Char(face, &glyph_code);
2342
2343 DPRINT("Face encoding FT_ENCODING_UNICODE, number of glyphs %ld, first glyph %u, first char %04lx\n",
2344 face->num_glyphs, glyph_code, char_code);
2345
2346 if (!glyph_code) return 0;
2347
2348 if (glyphset)
2349 {
2350 glyphset->ranges[0].wcLow = (USHORT)char_code;
2351 glyphset->ranges[0].cGlyphs = 0;
2352 glyphset->cGlyphsSupported = 0;
2353 }
2354
2355 num_ranges = 1;
2356 while (glyph_code)
2357 {
2358 if (char_code < char_code_prev)
2359 {
2360 DPRINT1("Expected increasing char code from FT_Get_Next_Char\n");
2361 return 0;
2362 }
2363 if (char_code - char_code_prev > 1)
2364 {
2365 num_ranges++;
2366 if (glyphset)
2367 {
2368 glyphset->ranges[num_ranges - 1].wcLow = (USHORT)char_code;
2369 glyphset->ranges[num_ranges - 1].cGlyphs = 1;
2370 glyphset->cGlyphsSupported++;
2371 }
2372 }
2373 else if (glyphset)
2374 {
2375 glyphset->ranges[num_ranges - 1].cGlyphs++;
2376 glyphset->cGlyphsSupported++;
2377 }
2378 char_code_prev = char_code;
2379 char_code = FT_Get_Next_Char(face, char_code, &glyph_code);
2380 }
2381 }
2382 else
2383 DPRINT1("Encoding %u not supported\n", face->charmap->encoding);
2384
2385 size = sizeof(GLYPHSET) + sizeof(WCRANGE) * (num_ranges - 1);
2386 if (glyphset)
2387 {
2388 glyphset->cbThis = size;
2389 glyphset->cRanges = num_ranges;
2390 }
2391 return size;
2392 }
2393
2394
2395 BOOL
2396 FASTCALL
2397 ftGdiGetTextMetricsW(
2398 HDC hDC,
2399 PTMW_INTERNAL ptmwi)
2400 {
2401 PDC dc;
2402 PDC_ATTR pdcattr;
2403 PTEXTOBJ TextObj;
2404 PFONTGDI FontGDI;
2405 FT_Face Face;
2406 TT_OS2 *pOS2;
2407 TT_HoriHeader *pHori;
2408 FT_WinFNT_HeaderRec Win;
2409 ULONG Error;
2410 NTSTATUS Status = STATUS_SUCCESS;
2411
2412 if (!ptmwi)
2413 {
2414 EngSetLastError(STATUS_INVALID_PARAMETER);
2415 return FALSE;
2416 }
2417
2418 if (!(dc = DC_LockDc(hDC)))
2419 {
2420 EngSetLastError(ERROR_INVALID_HANDLE);
2421 return FALSE;
2422 }
2423 pdcattr = dc->pdcattr;
2424 TextObj = RealizeFontInit(pdcattr->hlfntNew);
2425 if (NULL != TextObj)
2426 {
2427 FontGDI = ObjToGDI(TextObj->Font, FONT);
2428
2429 Face = FontGDI->face;
2430 IntLockFreeType;
2431 Error = FT_Set_Pixel_Sizes(Face,
2432 TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfWidth,
2433 /* FIXME: Should set character height if neg */
2434 (TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight == 0 ?
2435 dc->ppdev->devinfo.lfDefaultFont.lfHeight : abs(TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfHeight)));
2436 IntUnLockFreeType;
2437 if (0 != Error)
2438 {
2439 DPRINT1("Error in setting pixel sizes: %u\n", Error);
2440 Status = STATUS_UNSUCCESSFUL;
2441 }
2442 else
2443 {
2444 Status = STATUS_SUCCESS;
2445
2446 IntLockFreeType;
2447 pOS2 = FT_Get_Sfnt_Table(FontGDI->face, ft_sfnt_os2);
2448 if (NULL == pOS2)
2449 {
2450 DPRINT1("Can't find OS/2 table - not TT font?\n");
2451 Status = STATUS_INTERNAL_ERROR;
2452 }
2453
2454 pHori = FT_Get_Sfnt_Table(FontGDI->face, ft_sfnt_hhea);
2455 if (NULL == pHori)
2456 {
2457 DPRINT1("Can't find HHEA table - not TT font?\n");
2458 Status = STATUS_INTERNAL_ERROR;
2459 }
2460
2461 Error = FT_Get_WinFNT_Header(FontGDI->face , &Win);
2462
2463 IntUnLockFreeType;
2464
2465 if (NT_SUCCESS(Status))
2466 {
2467 FillTM(&ptmwi->TextMetric, FontGDI, pOS2, pHori, !Error ? &Win : 0);
2468
2469 /* FIXME: Fill Diff member */
2470 RtlZeroMemory(&ptmwi->Diff, sizeof(ptmwi->Diff));
2471 }
2472 }
2473 TEXTOBJ_UnlockText(TextObj);
2474 }
2475 else
2476 {
2477 Status = STATUS_INVALID_HANDLE;
2478 }
2479 DC_UnlockDc(dc);
2480
2481 if (!NT_SUCCESS(Status))
2482 {
2483 SetLastNtError(Status);
2484 return FALSE;
2485 }
2486 return TRUE;
2487 }
2488
2489
2490 DWORD
2491 FASTCALL
2492 ftGdiGetFontData(
2493 PFONTGDI FontGdi,
2494 DWORD Table,
2495 DWORD Offset,
2496 PVOID Buffer,
2497 DWORD Size)
2498 {
2499 DWORD Result = GDI_ERROR;
2500
2501 IntLockFreeType;
2502
2503 if (FT_IS_SFNT(FontGdi->face))
2504 {
2505 if (Table)
2506 Table = Table >> 24 | Table << 24 | (Table >> 8 & 0xFF00) |
2507 (Table << 8 & 0xFF0000);
2508
2509 if (!Buffer) Size = 0;
2510
2511 if (Buffer && Size)
2512 {
2513 FT_Error Error;
2514 FT_ULong Needed = 0;
2515
2516 Error = FT_Load_Sfnt_Table(FontGdi->face, Table, Offset, NULL, &Needed);
2517
2518 if ( !Error && Needed < Size) Size = Needed;
2519 }
2520 if (!FT_Load_Sfnt_Table(FontGdi->face, Table, Offset, Buffer, &Size))
2521 Result = Size;
2522 }
2523
2524 IntUnLockFreeType;
2525
2526 return Result;
2527 }
2528
2529 static UINT FASTCALL
2530 GetFontScore(LOGFONTW *LogFont, PUNICODE_STRING FaceName, PFONTGDI FontGDI)
2531 {
2532 ANSI_STRING EntryFaceNameA;
2533 UNICODE_STRING EntryFaceNameW;
2534 unsigned Size;
2535 OUTLINETEXTMETRICW *Otm;
2536 LONG WeightDiff;
2537 NTSTATUS Status;
2538 UINT Score = 1;
2539
2540 RtlInitAnsiString(&EntryFaceNameA, FontGDI->face->family_name);
2541 Status = RtlAnsiStringToUnicodeString(&EntryFaceNameW, &EntryFaceNameA, TRUE);
2542 if (NT_SUCCESS(Status))
2543 {
2544 if ((LF_FACESIZE - 1) * sizeof(WCHAR) < EntryFaceNameW.Length)
2545 {
2546 EntryFaceNameW.Length = (LF_FACESIZE - 1) * sizeof(WCHAR);
2547 EntryFaceNameW.Buffer[LF_FACESIZE - 1] = L'\0';
2548 }
2549 if (0 == RtlCompareUnicodeString(FaceName, &EntryFaceNameW, TRUE))
2550 {
2551 Score += 49;
2552 }
2553 RtlFreeUnicodeString(&EntryFaceNameW);
2554 }
2555
2556 Size = IntGetOutlineTextMetrics(FontGDI, 0, NULL);
2557 Otm = ExAllocatePoolWithTag(PagedPool, Size, GDITAG_TEXT);
2558 if (NULL == Otm)
2559 {
2560 return Score;
2561 }
2562 IntGetOutlineTextMetrics(FontGDI, Size, Otm);
2563
2564 if ((0 != LogFont->lfItalic && 0 != Otm->otmTextMetrics.tmItalic) ||
2565 (0 == LogFont->lfItalic && 0 == Otm->otmTextMetrics.tmItalic))
2566 {
2567 Score += 25;
2568 }
2569 if (LogFont->lfWeight != FW_DONTCARE)
2570 {
2571 if (LogFont->lfWeight < Otm->otmTextMetrics.tmWeight)
2572 {
2573 WeightDiff = Otm->otmTextMetrics.tmWeight - LogFont->lfWeight;
2574 }
2575 else
2576 {
2577 WeightDiff = LogFont->lfWeight - Otm->otmTextMetrics.tmWeight;
2578 }
2579 Score += (1000 - WeightDiff) / (1000 / 25);
2580 }
2581 else
2582 {
2583 Score += 25;
2584 }
2585
2586 ExFreePool(Otm);
2587
2588 return Score;
2589 }
2590
2591 static __inline VOID
2592 FindBestFontFromList(FONTOBJ **FontObj, UINT *MatchScore, LOGFONTW *LogFont,
2593 PUNICODE_STRING FaceName, PLIST_ENTRY Head)
2594 {
2595 PLIST_ENTRY Entry;
2596 PFONT_ENTRY CurrentEntry;
2597 FONTGDI *FontGDI;
2598 UINT Score;
2599 ASSERT(FontObj && MatchScore && LogFont && FaceName && Head);
2600 Entry = Head->Flink;
2601 while (Entry != Head)
2602 {
2603 CurrentEntry = (PFONT_ENTRY) CONTAINING_RECORD(Entry, FONT_ENTRY, ListEntry);
2604
2605 FontGDI = CurrentEntry->Font;
2606 ASSERT(FontGDI);
2607
2608 Score = GetFontScore(LogFont, FaceName, FontGDI);
2609 if (*MatchScore == 0 || *MatchScore < Score)
2610 {
2611 *FontObj = GDIToObj(FontGDI, FONT);
2612 *MatchScore = Score;
2613 }
2614 Entry = Entry->Flink;
2615 }
2616 }
2617
2618 static __inline BOOLEAN
2619 SubstituteFontFamilyKey(PUNICODE_STRING FaceName,
2620 LPCWSTR Key)
2621 {
2622 RTL_QUERY_REGISTRY_TABLE QueryTable[2] = {{0}};
2623 NTSTATUS Status;
2624 UNICODE_STRING Value;
2625
2626 RtlInitUnicodeString(&Value, NULL);
2627
2628 QueryTable[0].QueryRoutine = NULL;
2629 QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_NOEXPAND |
2630 RTL_QUERY_REGISTRY_REQUIRED;
2631 QueryTable[0].Name = FaceName->Buffer;
2632 QueryTable[0].EntryContext = &Value;
2633 QueryTable[0].DefaultType = REG_NONE;
2634 QueryTable[0].DefaultData = NULL;
2635 QueryTable[0].DefaultLength = 0;
2636
2637 QueryTable[1].QueryRoutine = NULL;
2638 QueryTable[1].Name = NULL;
2639
2640 Status = RtlQueryRegistryValues(RTL_REGISTRY_WINDOWS_NT,
2641 Key,
2642 QueryTable,
2643 NULL,
2644 NULL);
2645 if (NT_SUCCESS(Status))
2646 {
2647 RtlFreeUnicodeString(FaceName);
2648 *FaceName = Value;
2649 }
2650
2651 return NT_SUCCESS(Status);
2652 }
2653
2654 static __inline void
2655 SubstituteFontFamily(PUNICODE_STRING FaceName, UINT Level)
2656 {
2657 if (10 < Level) /* Enough is enough */
2658 {
2659 return;
2660 }
2661
2662 if (SubstituteFontFamilyKey(FaceName, L"SysFontSubstitutes") ||
2663 SubstituteFontFamilyKey(FaceName, L"FontSubstitutes"))
2664 {
2665 SubstituteFontFamily(FaceName, Level + 1);
2666 }
2667 }
2668
2669 static
2670 VOID
2671 FASTCALL
2672 IntFontType(PFONTGDI Font)
2673 {
2674 PS_FontInfoRec psfInfo;
2675 FT_ULong tmp_size = 0;
2676
2677 if (FT_HAS_MULTIPLE_MASTERS(Font->face))
2678 Font->FontObj.flFontType |= FO_MULTIPLEMASTER;
2679 if (FT_HAS_VERTICAL( Font->face ))
2680 Font->FontObj.flFontType |= FO_VERT_FACE;
2681 if (FT_IS_SCALABLE( Font->face ))
2682 Font->FontObj.flFontType |= FO_TYPE_RASTER;
2683 if (FT_IS_SFNT(Font->face))
2684 {
2685 Font->FontObj.flFontType |= FO_TYPE_TRUETYPE;
2686 if (FT_Get_Sfnt_Table(Font->face, ft_sfnt_post))
2687 Font->FontObj.flFontType |= FO_POSTSCRIPT;
2688 }
2689 if (!FT_Get_PS_Font_Info(Font->face, &psfInfo ))
2690 {
2691 Font->FontObj.flFontType |= FO_POSTSCRIPT;
2692 }
2693 /* Check for the presence of the 'CFF ' table to check if the font is Type1 */
2694 if (!FT_Load_Sfnt_Table(Font->face, FT_MAKE_TAG('C','F','F',' '), 0, NULL, &tmp_size))
2695 {
2696 Font->FontObj.flFontType |= (FO_CFF|FO_POSTSCRIPT);
2697 }
2698 }
2699
2700 NTSTATUS
2701 FASTCALL
2702 TextIntRealizeFont(HFONT FontHandle, PTEXTOBJ pTextObj)
2703 {
2704 NTSTATUS Status = STATUS_SUCCESS;
2705 PTEXTOBJ TextObj;
2706 UNICODE_STRING FaceName;
2707 PPROCESSINFO Win32Process;
2708 UINT MatchScore;
2709
2710 if (!pTextObj)
2711 {
2712 TextObj = TEXTOBJ_LockText(FontHandle);
2713 if (NULL == TextObj)
2714 {
2715 return STATUS_INVALID_HANDLE;
2716 }
2717
2718 if (TextObj->fl & TEXTOBJECT_INIT)
2719 {
2720 TEXTOBJ_UnlockText(TextObj);
2721 return STATUS_SUCCESS;
2722 }
2723 }
2724 else
2725 TextObj = pTextObj;
2726
2727 if (! RtlCreateUnicodeString(&FaceName, TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfFaceName))
2728 {
2729 if (!pTextObj) TEXTOBJ_UnlockText(TextObj);
2730 return STATUS_NO_MEMORY;
2731 }
2732 SubstituteFontFamily(&FaceName, 0);
2733 MatchScore = 0;
2734 TextObj->Font = NULL;
2735
2736 /* First search private fonts */
2737 Win32Process = PsGetCurrentProcessWin32Process();
2738 IntLockProcessPrivateFonts(Win32Process);
2739 FindBestFontFromList(&TextObj->Font, &MatchScore,
2740 &TextObj->logfont.elfEnumLogfontEx.elfLogFont, &FaceName,
2741 &Win32Process->PrivateFontListHead);
2742 IntUnLockProcessPrivateFonts(Win32Process);
2743
2744 /* Search system fonts */
2745 IntLockGlobalFonts;
2746 FindBestFontFromList(&TextObj->Font, &MatchScore,
2747 &TextObj->logfont.elfEnumLogfontEx.elfLogFont, &FaceName,
2748 &FontListHead);
2749 IntUnLockGlobalFonts;
2750 if (NULL == TextObj->Font)
2751 {
2752 DPRINT1("Requested font %S not found, no fonts loaded at all\n",
2753 TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfFaceName);
2754 Status = STATUS_NOT_FOUND;
2755 }
2756 else
2757 {
2758 PFONTGDI FontGdi = ObjToGDI(TextObj->Font, FONT);
2759 // Need hdev, when freetype is loaded need to create DEVOBJ for
2760 // Consumer and Producer.
2761 TextObj->Font->iUniq = 1; // Now it can be cached.
2762 IntFontType(FontGdi);
2763 FontGdi->flType = TextObj->Font->flFontType;
2764 FontGdi->Underline = TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfUnderline ? 0xff : 0;
2765 FontGdi->StrikeOut = TextObj->logfont.elfEnumLogfontEx.elfLogFont.lfStrikeOut ? 0xff : 0;
2766 TextObj->fl |= TEXTOBJECT_INIT;
2767 Status = STATUS_SUCCESS;
2768 }
2769
2770 RtlFreeUnicodeString(&FaceName);
2771 if (!pTextObj) TEXTOBJ_UnlockText(TextObj);
2772
2773 ASSERT((NT_SUCCESS(Status) ^ (NULL == TextObj->Font)) != 0);
2774
2775 return Status;
2776 }
2777
2778
2779 static
2780 BOOL
2781 FASTCALL
2782 IntGetFullFileName(
2783 POBJECT_NAME_INFORMATION NameInfo,
2784 ULONG Size,
2785 PUNICODE_STRING FileName)
2786 {
2787 NTSTATUS Status;
2788 OBJECT_ATTRIBUTES ObjectAttributes;
2789 HANDLE hFile;
2790 IO_STATUS_BLOCK IoStatusBlock;
2791 ULONG Desired;
2792
2793 InitializeObjectAttributes(&ObjectAttributes,
2794 FileName,
2795 OBJ_CASE_INSENSITIVE,
2796 NULL,
2797 NULL);
2798
2799 Status = ZwOpenFile(
2800 &hFile,
2801 0, // FILE_READ_ATTRIBUTES,
2802 &ObjectAttributes,
2803 &IoStatusBlock,
2804 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
2805 0);
2806
2807 if (!NT_SUCCESS(Status))
2808 {
2809 DPRINT("ZwOpenFile() failed (Status = 0x%lx)\n", Status);
2810 return FALSE;
2811 }
2812
2813 Status = ZwQueryObject(hFile, ObjectNameInformation, NameInfo, Size, &Desired);
2814 ZwClose(hFile);
2815 if (!NT_SUCCESS(Status))
2816 {
2817 DPRINT("ZwQueryObject() failed (Status = %lx)\n", Status);
2818 return FALSE;
2819 }
2820
2821 return TRUE;
2822 }
2823
2824 BOOL
2825 FASTCALL
2826 IntGdiGetFontResourceInfo(
2827 PUNICODE_STRING FileName,
2828 PVOID pBuffer,
2829 DWORD *pdwBytes,
2830 DWORD dwType)
2831 {
2832 UNICODE_STRING EntryFileName;
2833 POBJECT_NAME_INFORMATION NameInfo1, NameInfo2;
2834 PLIST_ENTRY ListEntry;
2835 PFONT_ENTRY FontEntry;
2836 FONTFAMILYINFO Info;
2837 ULONG Size;
2838 BOOL bFound = FALSE;
2839
2840 /* Create buffer for full path name */
2841 Size = sizeof(OBJECT_NAME_INFORMATION) + MAX_PATH * sizeof(WCHAR);
2842 NameInfo1 = ExAllocatePoolWithTag(PagedPool, Size, TAG_FINF);
2843 if (!NameInfo1)
2844 {
2845 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
2846 return FALSE;
2847 }
2848
2849 /* Get the full path name */
2850 if (!IntGetFullFileName(NameInfo1, Size, FileName))
2851 {
2852 ExFreePool(NameInfo1);
2853 return FALSE;
2854 }
2855
2856 /* Create a buffer for the entries' names */
2857 NameInfo2 = ExAllocatePoolWithTag(PagedPool, Size, TAG_FINF);
2858 if (!NameInfo2)
2859 {
2860 ExFreePool(NameInfo1);
2861 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
2862 return FALSE;
2863 }
2864
2865 /* Try to find the pathname in the global font list */
2866 IntLockGlobalFonts;
2867 for (ListEntry = FontListHead.Flink;
2868 ListEntry != &FontListHead;
2869 ListEntry = ListEntry->Flink)
2870 {
2871 FontEntry = CONTAINING_RECORD(ListEntry, FONT_ENTRY, ListEntry);
2872 if (FontEntry->Font->Filename != NULL)
2873 {
2874 RtlInitUnicodeString(&EntryFileName , FontEntry->Font->Filename);
2875 if (IntGetFullFileName(NameInfo2, Size, &EntryFileName))
2876 {
2877 if (RtlEqualUnicodeString(&NameInfo1->Name, &NameInfo2->Name, FALSE))
2878 {
2879 /* Found */
2880 FontFamilyFillInfo(&Info, FontEntry->FaceName.Buffer, FontEntry->Font);
2881 bFound = TRUE;
2882 break;
2883 }
2884 }
2885 }
2886 }
2887 IntUnLockGlobalFonts;
2888
2889 /* Free the buffers */
2890 ExFreePool(NameInfo1);
2891 ExFreePool(NameInfo2);
2892
2893 if (!bFound && dwType != 5)
2894 {
2895 /* Font could not be found in system table
2896 dwType == 5 will still handle this */
2897 return FALSE;
2898 }
2899
2900 switch (dwType)
2901 {
2902 case 0: /* FIXME: Returns 1 or 2, don't know what this is atm */
2903 *(DWORD*)pBuffer = 1;
2904 *pdwBytes = sizeof(DWORD);
2905 break;
2906
2907 case 1: /* Copy the full font name */
2908 Size = wcslen(Info.EnumLogFontEx.elfFullName) + 1;
2909 Size = min(Size , LF_FULLFACESIZE) * sizeof(WCHAR);
2910 RtlCopyMemory(pBuffer, Info.EnumLogFontEx.elfFullName, Size);
2911 // FIXME: Do we have to zeroterminate?
2912 *pdwBytes = Size;
2913 break;
2914
2915 case 2: /* Copy a LOGFONTW structure */
2916 Info.EnumLogFontEx.elfLogFont.lfWidth = 0;
2917 RtlCopyMemory(pBuffer, &Info.EnumLogFontEx.elfLogFont, sizeof(LOGFONTW));
2918 *pdwBytes = sizeof(LOGFONTW);
2919 break;
2920
2921 case 3: /* FIXME: What exactly is copied here? */
2922 *(DWORD*)pBuffer = 1;
2923 *pdwBytes = sizeof(DWORD*);
2924 break;
2925
2926 case 5: /* Looks like a BOOL that is copied, TRUE, if the font was not found */
2927 *(BOOL*)pBuffer = !bFound;
2928 *pdwBytes = sizeof(BOOL);
2929 break;
2930
2931 default:
2932 return FALSE;
2933 }
2934
2935 return TRUE;
2936 }
2937
2938
2939 BOOL
2940 FASTCALL
2941 ftGdiRealizationInfo(PFONTGDI Font, PREALIZATION_INFO Info)
2942 {
2943 if (FT_HAS_FIXED_SIZES(Font->face))
2944 Info->iTechnology = RI_TECH_BITMAP;
2945 else
2946 {
2947 if (FT_IS_SCALABLE(Font->face))
2948 Info->iTechnology = RI_TECH_SCALABLE;
2949 else
2950 Info->iTechnology = RI_TECH_FIXED;
2951 }
2952 Info->iUniq = Font->FontObj.iUniq;
2953 Info->dwUnknown = -1;
2954 return TRUE;
2955 }
2956
2957
2958 DWORD
2959 FASTCALL
2960 ftGdiGetKerningPairs( PFONTGDI Font,
2961 DWORD cPairs,
2962 LPKERNINGPAIR pKerningPair)
2963 {
2964 DWORD Count = 0;
2965 INT i = 0;
2966 FT_Face face = Font->face;
2967
2968 if (FT_HAS_KERNING(face) && face->charmap->encoding == FT_ENCODING_UNICODE)
2969 {
2970 FT_UInt previous_index = 0, glyph_index = 0;
2971 FT_ULong char_code, char_previous;
2972 FT_Vector delta;
2973
2974 char_previous = char_code = FT_Get_First_Char(face, &glyph_index);
2975
2976 IntLockFreeType;
2977