2 * FreeType font engine interface
4 * Copyright 2001 Huw D M Davies for CodeWeavers.
6 * This file contains the WineEng* functions.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include "wine/port.h"
28 #ifdef HAVE_SYS_STAT_H
29 # include <sys/stat.h>
42 #include "gdi_private.h"
43 #include "wine/unicode.h"
44 #include "wine/debug.h"
45 #include "wine/list.h"
47 WINE_DEFAULT_DEBUG_CHANNEL(font);
51 #ifdef HAVE_FT2BUILD_H
54 #ifdef HAVE_FREETYPE_FREETYPE_H
55 #include <freetype/freetype.h>
57 #ifdef HAVE_FREETYPE_FTGLYPH_H
58 #include <freetype/ftglyph.h>
60 #ifdef HAVE_FREETYPE_TTTABLES_H
61 #include <freetype/tttables.h>
63 #ifdef HAVE_FREETYPE_FTSNAMES_H
64 #include <freetype/ftsnames.h>
66 # ifdef HAVE_FREETYPE_FTNAMES_H
67 # include <freetype/ftnames.h>
70 #ifdef HAVE_FREETYPE_TTNAMEID_H
71 #include <freetype/ttnameid.h>
73 #ifdef HAVE_FREETYPE_FTOUTLN_H
74 #include <freetype/ftoutln.h>
76 #ifdef HAVE_FREETYPE_INTERNAL_SFNT_H
77 #include <freetype/internal/sfnt.h>
79 #ifdef HAVE_FREETYPE_FTTRIGON_H
80 #include <freetype/fttrigon.h>
82 #ifdef HAVE_FREETYPE_FTWINFNT_H
83 #include <freetype/ftwinfnt.h>
85 #ifdef HAVE_FREETYPE_FTMODAPI_H
86 #include <freetype/ftmodapi.h>
89 #ifndef SONAME_LIBFREETYPE
90 #define SONAME_LIBFREETYPE "libfreetype.so"
93 #ifndef FT_MODULE_DRIVER_HAS_HINTER
94 #define FT_MODULE_DRIVER_HAS_HINTER 0x400
97 static FT_Library library = 0;
104 static FT_Version_t FT_Version;
105 static DWORD FT_SimpleVersion;
107 static void *ft_handle = NULL;
109 #define MAKE_FUNCPTR(f) static typeof(f) * p##f = NULL
110 MAKE_FUNCPTR(FT_Vector_Unit);
111 MAKE_FUNCPTR(FT_Done_Face);
112 MAKE_FUNCPTR(FT_Get_Char_Index);
113 MAKE_FUNCPTR(FT_Get_Module);
114 MAKE_FUNCPTR(FT_Get_Sfnt_Table);
115 MAKE_FUNCPTR(FT_Init_FreeType);
116 MAKE_FUNCPTR(FT_Load_Glyph);
117 MAKE_FUNCPTR(FT_Matrix_Multiply);
118 MAKE_FUNCPTR(FT_MulFix);
119 MAKE_FUNCPTR(FT_New_Face);
120 MAKE_FUNCPTR(FT_Outline_Get_Bitmap);
121 MAKE_FUNCPTR(FT_Outline_Transform);
122 MAKE_FUNCPTR(FT_Outline_Translate);
123 MAKE_FUNCPTR(FT_Select_Charmap);
124 MAKE_FUNCPTR(FT_Set_Pixel_Sizes);
125 MAKE_FUNCPTR(FT_Vector_Transform);
126 static void (*pFT_Library_Version)(FT_Library,FT_Int*,FT_Int*,FT_Int*);
127 static FT_Error (*pFT_Load_Sfnt_Table)(FT_Face,FT_ULong,FT_Long,FT_Byte*,FT_ULong*);
128 static FT_ULong (*pFT_Get_First_Char)(FT_Face,FT_UInt*);
129 static FT_Error (*pFT_Module_Get_Flags)(FT_Module,FT_ULong*);
130 #ifdef HAVE_FREETYPE_FTWINFNT_H
131 MAKE_FUNCPTR(FT_Get_WinFNT_Header);
134 #ifdef HAVE_FONTCONFIG_FONTCONFIG_H
135 #include <fontconfig/fontconfig.h>
136 MAKE_FUNCPTR(FcConfigGetCurrent);
137 MAKE_FUNCPTR(FcFontList);
138 MAKE_FUNCPTR(FcFontSetDestroy);
139 MAKE_FUNCPTR(FcInit);
140 MAKE_FUNCPTR(FcObjectSetAdd);
141 MAKE_FUNCPTR(FcObjectSetCreate);
142 MAKE_FUNCPTR(FcObjectSetDestroy);
143 MAKE_FUNCPTR(FcPatternCreate);
144 MAKE_FUNCPTR(FcPatternDestroy);
145 MAKE_FUNCPTR(FcPatternGet);
146 #ifndef SONAME_LIBFONTCONFIG
147 #define SONAME_LIBFONTCONFIG "libfontconfig.so"
153 #ifndef ft_encoding_none
154 #define FT_ENCODING_NONE ft_encoding_none
156 #ifndef ft_encoding_ms_symbol
157 #define FT_ENCODING_MS_SYMBOL ft_encoding_symbol
159 #ifndef ft_encoding_unicode
160 #define FT_ENCODING_UNICODE ft_encoding_unicode
162 #ifndef ft_encoding_apple_roman
163 #define FT_ENCODING_APPLE_ROMAN ft_encoding_apple_roman
166 #define GET_BE_WORD(ptr) MAKEWORD( ((BYTE *)(ptr))[1], ((BYTE *)(ptr))[0] )
168 /* This is bascially a copy of FT_Bitmap_Size with an extra element added */
175 FT_Short internal_leading;
178 /* FT_Bitmap_Size gained 3 new elements between FreeType 2.1.4 and 2.1.5
179 So to let this compile on older versions of FreeType we'll define the
180 new structure here. */
182 FT_Short height, width;
183 FT_Pos size, x_ppem, y_ppem;
186 typedef struct tagFace {
194 FONTSIGNATURE fs_links;
195 FT_Fixed font_version;
197 Bitmap_Size size; /* set if face is a bitmap */
198 BOOL external; /* TRUE if we should manually add this font to the registry */
199 struct tagFamily *family;
202 typedef struct tagFamily {
210 INT adv; /* These three hold to widths of the unrotated chars */
227 typedef struct tagHFONTLIST {
252 struct list hfontlist;
257 OUTLINETEXTMETRICW *potm;
260 struct list child_fonts;
270 #define INIT_GM_SIZE 128
272 static struct list gdi_font_list = LIST_INIT(gdi_font_list);
273 static struct list unused_gdi_font_list = LIST_INIT(unused_gdi_font_list);
274 #define UNUSED_CACHE_SIZE 10
275 static struct list child_font_list = LIST_INIT(child_font_list);
276 static struct list system_links = LIST_INIT(system_links);
278 static struct list font_list = LIST_INIT(font_list);
280 static const WCHAR defSerif[] = {'T','i','m','e','s',' ','N','e','w',' ',
281 'R','o','m','a','n','\0'};
282 static const WCHAR defSans[] = {'A','r','i','a','l','\0'};
283 static const WCHAR defFixed[] = {'C','o','u','r','i','e','r',' ','N','e','w','\0'};
285 static const WCHAR defSystem[] = {'A','r','i','a','l','\0'};
286 static const WCHAR SystemW[] = {'S','y','s','t','e','m','\0'};
287 static const WCHAR MSSansSerifW[] = {'M','S',' ','S','a','n','s',' ',
288 'S','e','r','i','f','\0'};
289 static const WCHAR HelvW[] = {'H','e','l','v','\0'};
290 static const WCHAR RegularW[] = {'R','e','g','u','l','a','r','\0'};
292 static const WCHAR fontsW[] = {'\\','F','o','n','t','s','\0'};
293 static const WCHAR win9x_font_reg_key[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
294 'W','i','n','d','o','w','s','\\',
295 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
296 'F','o','n','t','s','\0'};
298 static const WCHAR winnt_font_reg_key[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
299 'W','i','n','d','o','w','s',' ','N','T','\\',
300 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
301 'F','o','n','t','s','\0'};
303 static const WCHAR system_fonts_reg_key[] = {'S','o','f','t','w','a','r','e','\\','F','o','n','t','s','\0'};
304 static const WCHAR FixedSys_Value[] = {'F','I','X','E','D','F','O','N','.','F','O','N','\0'};
305 static const WCHAR System_Value[] = {'F','O','N','T','S','.','F','O','N','\0'};
306 static const WCHAR OEMFont_Value[] = {'O','E','M','F','O','N','T','.','F','O','N','\0'};
308 static const WCHAR *SystemFontValues[4] = {
315 static const WCHAR external_fonts_reg_key[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\',
316 'F','o','n','t','s','\\','E','x','t','e','r','n','a','l',' ','F','o','n','t','s','\0'};
318 static const WCHAR ArabicW[] = {'A','r','a','b','i','c','\0'};
319 static const WCHAR BalticW[] = {'B','a','l','t','i','c','\0'};
320 static const WCHAR CHINESE_BIG5W[] = {'C','H','I','N','E','S','E','_','B','I','G','5','\0'};
321 static const WCHAR CHINESE_GB2312W[] = {'C','H','I','N','E','S','E','_','G','B','2','3','1','2','\0'};
322 static const WCHAR Central_EuropeanW[] = {'C','e','n','t','r','a','l',' ',
323 'E','u','r','o','p','e','a','n','\0'};
324 static const WCHAR CyrillicW[] = {'C','y','r','i','l','l','i','c','\0'};
325 static const WCHAR GreekW[] = {'G','r','e','e','k','\0'};
326 static const WCHAR HangulW[] = {'H','a','n','g','u','l','\0'};
327 static const WCHAR Hangul_Johab_W[] = {'H','a','n','g','u','l','(','J','o','h','a','b',')','\0'};
328 static const WCHAR HebrewW[] = {'H','e','b','r','e','w','\0'};
329 static const WCHAR JapaneseW[] = {'J','a','p','a','n','e','s','e','\0'};
330 static const WCHAR SymbolW[] = {'S','y','m','b','o','l','\0'};
331 static const WCHAR ThaiW[] = {'T','h','a','i','\0'};
332 static const WCHAR TurkishW[] = {'T','u','r','k','i','s','h','\0'};
333 static const WCHAR VietnameseW[] = {'V','i','e','t','n','a','m','e','s','e','\0'};
334 static const WCHAR WesternW[] = {'W','e','s','t','e','r','n','\0'};
335 static const WCHAR OEM_DOSW[] = {'O','E','M','/','D','O','S','\0'};
337 static const WCHAR *ElfScriptsW[32] = { /* these are in the order of the fsCsb[0] bits */
347 NULL, NULL, NULL, NULL, NULL, NULL, NULL, /*15*/
355 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
364 typedef struct tagFontSubst {
367 struct tagFontSubst *next;
370 static FontSubst *substlist = NULL;
371 static BOOL have_installed_roman_font = FALSE; /* CreateFontInstance will fail if this is still FALSE */
373 static const WCHAR font_mutex_nameW[] = {'_','_','W','I','N','E','_','F','O','N','T','_','M','U','T','E','X','_','_','\0'};
376 /****************************************
377 * Notes on .fon files
379 * The fonts System, FixedSys and Terminal are special. There are typically multiple
380 * versions installed for different resolutions and codepages. Windows stores which one to use
381 * in HKEY_CURRENT_CONFIG\\Software\\Fonts.
383 * FIXEDFON.FON FixedSys
385 * OEMFONT.FON Terminal
386 * LogPixels Current dpi set by the display control panel applet
387 * (HKLM\\Software\\Microsft\\Windows NT\\CurrentVersion\\FontDPI
388 * also has a LogPixels value that appears to mirror this)
390 * On my system these values have data: vgafix.fon, vgasys.fon, vga850.fon and 96 respectively
391 * (vgaoem.fon would be your oemfont.fon if you have a US setup).
392 * If the resolution is changed to be >= 109dpi then the fonts goto 8514fix, 8514sys and 8514oem
393 * (not sure what's happening to the oem codepage here). 109 is nicely halfway between 96 and 120dpi,
394 * so that makes sense.
396 * Additionally Windows also loads the fonts listed in the [386enh] section of system.ini (this doesn't appear
397 * to be mapped into the registry on Windows 2000 at least).
400 * ega80woa.fon=ega80850.fon
401 * ega40woa.fon=ega40850.fon
402 * cga80woa.fon=cga80850.fon
403 * cga40woa.fon=cga40850.fon
407 static inline BOOL is_win9x(void)
409 return GetVersion() & 0x80000000;
412 This function builds an FT_Fixed from a float. It puts the integer part
413 in the highest 16 bits and the decimal part in the lowest 16 bits of the FT_Fixed.
414 It fails if the integer part of the float number is greater than SHORT_MAX.
416 static inline FT_Fixed FT_FixedFromFloat(float f)
419 unsigned short fract = (f - value) * 0xFFFF;
420 return (FT_Fixed)((long)value << 16 | (unsigned long)fract);
424 This function builds an FT_Fixed from a FIXED. It simply put f.value
425 in the highest 16 bits and f.fract in the lowest 16 bits of the FT_Fixed.
427 static inline FT_Fixed FT_FixedFromFIXED(FIXED f)
429 return (FT_Fixed)((long)f.value << 16 | (unsigned long)f.fract);
432 #define ADDFONT_EXTERNAL_FONT 0x01
433 #define ADDFONT_FORCE_BITMAP 0x02
434 static BOOL AddFontFileToList(const char *file, char *fake_family, DWORD flags)
438 TT_Header *pHeader = NULL;
439 WCHAR *FamilyW, *StyleW;
443 struct list *family_elem_ptr, *face_elem_ptr;
445 FT_Long face_index = 0, num_faces;
446 #ifdef HAVE_FREETYPE_FTWINFNT_H
447 FT_WinFNT_HeaderRec winfnt_header;
449 int i, bitmap_num, internal_leading;
453 char *family_name = fake_family;
455 TRACE("Loading font file %s index %ld\n", debugstr_a(file), face_index);
456 if((err = pFT_New_Face(library, file, face_index, &ft_face)) != 0) {
457 WARN("Unable to load font file %s err = %x\n", debugstr_a(file), err);
461 if(!FT_IS_SFNT(ft_face) && (FT_IS_SCALABLE(ft_face) || !(flags & ADDFONT_FORCE_BITMAP))) { /* for now we'll accept TT/OT or bitmap fonts*/
462 WARN("Ignoring font %s\n", debugstr_a(file));
463 pFT_Done_Face(ft_face);
467 /* There are too many bugs in FreeType < 2.1.9 for bitmap font support */
468 if(!FT_IS_SCALABLE(ft_face) && FT_SimpleVersion < ((2 << 16) | (1 << 8) | (9 << 0))) {
469 WARN("FreeType version < 2.1.9, skipping bitmap font %s\n", debugstr_a(file));
470 pFT_Done_Face(ft_face);
474 if(FT_IS_SFNT(ft_face) && (!pFT_Get_Sfnt_Table(ft_face, ft_sfnt_os2) ||
475 !pFT_Get_Sfnt_Table(ft_face, ft_sfnt_hhea) ||
476 !(pHeader = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_head)))) {
477 TRACE("Font file %s lacks either an OS2, HHEA or HEAD table.\n"
478 "Skipping this font.\n", debugstr_a(file));
479 pFT_Done_Face(ft_face);
483 if(!ft_face->family_name || !ft_face->style_name) {
484 TRACE("Font file %s lacks either a family or style name\n", debugstr_a(file));
485 pFT_Done_Face(ft_face);
490 family_name = ft_face->family_name;
494 My_FT_Bitmap_Size *size = NULL;
496 if(!FT_IS_SCALABLE(ft_face))
497 size = (My_FT_Bitmap_Size *)ft_face->available_sizes + bitmap_num;
499 len = MultiByteToWideChar(CP_ACP, 0, family_name, -1, NULL, 0);
500 FamilyW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
501 MultiByteToWideChar(CP_ACP, 0, family_name, -1, FamilyW, len);
504 LIST_FOR_EACH(family_elem_ptr, &font_list) {
505 family = LIST_ENTRY(family_elem_ptr, Family, entry);
506 if(!strcmpW(family->FamilyName, FamilyW))
511 family = HeapAlloc(GetProcessHeap(), 0, sizeof(*family));
512 family->FamilyName = FamilyW;
513 list_init(&family->faces);
514 list_add_tail(&font_list, &family->entry);
516 HeapFree(GetProcessHeap(), 0, FamilyW);
519 len = MultiByteToWideChar(CP_ACP, 0, ft_face->style_name, -1, NULL, 0);
520 StyleW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
521 MultiByteToWideChar(CP_ACP, 0, ft_face->style_name, -1, StyleW, len);
523 internal_leading = 0;
524 memset(&fs, 0, sizeof(fs));
526 pOS2 = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_os2);
528 fs.fsCsb[0] = pOS2->ulCodePageRange1;
529 fs.fsCsb[1] = pOS2->ulCodePageRange2;
530 fs.fsUsb[0] = pOS2->ulUnicodeRange1;
531 fs.fsUsb[1] = pOS2->ulUnicodeRange2;
532 fs.fsUsb[2] = pOS2->ulUnicodeRange3;
533 fs.fsUsb[3] = pOS2->ulUnicodeRange4;
534 if(pOS2->version == 0) {
537 if(!pFT_Get_First_Char || (pFT_Get_First_Char( ft_face, &dummy ) < 0x100))
540 fs.fsCsb[0] |= 1L << 31;
543 #ifdef HAVE_FREETYPE_FTWINFNT_H
544 else if(pFT_Get_WinFNT_Header && !pFT_Get_WinFNT_Header(ft_face, &winfnt_header)) {
546 TRACE("pix_h %d charset %d dpi %dx%d pt %d\n", winfnt_header.pixel_height, winfnt_header.charset,
547 winfnt_header.vertical_resolution,winfnt_header.horizontal_resolution, winfnt_header.nominal_point_size);
548 if(TranslateCharsetInfo((DWORD*)(UINT)winfnt_header.charset, &csi, TCI_SRCCHARSET))
549 memcpy(&fs, &csi.fs, sizeof(csi.fs));
550 internal_leading = winfnt_header.internal_leading;
554 face_elem_ptr = list_head(&family->faces);
555 while(face_elem_ptr) {
556 face = LIST_ENTRY(face_elem_ptr, Face, entry);
557 face_elem_ptr = list_next(&family->faces, face_elem_ptr);
558 if(!strcmpW(face->StyleName, StyleW) &&
559 (FT_IS_SCALABLE(ft_face) || ((size->y_ppem == face->size.y_ppem) && !memcmp(&fs, &face->fs, sizeof(fs)) ))) {
560 TRACE("Already loaded font %s %s original version is %lx, this version is %lx\n",
561 debugstr_w(family->FamilyName), debugstr_w(StyleW),
562 face->font_version, pHeader ? pHeader->Font_Revision : 0);
565 TRACE("This font is a replacement but the original really exists, so we'll skip the replacement\n");
566 HeapFree(GetProcessHeap(), 0, StyleW);
567 pFT_Done_Face(ft_face);
570 if(!pHeader || pHeader->Font_Revision <= face->font_version) {
571 TRACE("Original font is newer so skipping this one\n");
572 HeapFree(GetProcessHeap(), 0, StyleW);
573 pFT_Done_Face(ft_face);
576 TRACE("Replacing original with this one\n");
577 list_remove(&face->entry);
578 HeapFree(GetProcessHeap(), 0, face->file);
579 HeapFree(GetProcessHeap(), 0, face->StyleName);
580 HeapFree(GetProcessHeap(), 0, face);
585 face = HeapAlloc(GetProcessHeap(), 0, sizeof(*face));
586 list_add_tail(&family->faces, &face->entry);
587 face->StyleName = StyleW;
588 face->file = HeapAlloc(GetProcessHeap(),0,strlen(file)+1);
589 strcpy(face->file, file);
590 face->face_index = face_index;
591 face->Italic = (ft_face->style_flags & FT_STYLE_FLAG_ITALIC) ? 1 : 0;
592 face->Bold = (ft_face->style_flags & FT_STYLE_FLAG_BOLD) ? 1 : 0;
593 face->font_version = pHeader ? pHeader->Font_Revision : 0;
594 face->family = family;
595 face->external = (flags & ADDFONT_EXTERNAL_FONT) ? TRUE : FALSE;
596 memcpy(&face->fs, &fs, sizeof(face->fs));
597 memset(&face->fs_links, 0, sizeof(face->fs_links));
599 if(FT_IS_SCALABLE(ft_face)) {
600 memset(&face->size, 0, sizeof(face->size));
601 face->scalable = TRUE;
603 TRACE("Adding bitmap size h %d w %d size %ld x_ppem %ld y_ppem %ld\n",
604 size->height, size->width, size->size >> 6,
605 size->x_ppem >> 6, size->y_ppem >> 6);
606 face->size.height = size->height;
607 face->size.width = size->width;
608 face->size.size = size->size;
609 face->size.x_ppem = size->x_ppem;
610 face->size.y_ppem = size->y_ppem;
611 face->size.internal_leading = internal_leading;
612 face->scalable = FALSE;
615 TRACE("fsCsb = %08lx %08lx/%08lx %08lx %08lx %08lx\n",
616 face->fs.fsCsb[0], face->fs.fsCsb[1],
617 face->fs.fsUsb[0], face->fs.fsUsb[1],
618 face->fs.fsUsb[2], face->fs.fsUsb[3]);
621 if(face->fs.fsCsb[0] == 0) { /* let's see if we can find any interesting cmaps */
622 for(i = 0; i < ft_face->num_charmaps; i++) {
623 switch(ft_face->charmaps[i]->encoding) {
624 case FT_ENCODING_UNICODE:
625 case FT_ENCODING_APPLE_ROMAN:
626 face->fs.fsCsb[0] |= 1;
628 case FT_ENCODING_MS_SYMBOL:
629 face->fs.fsCsb[0] |= 1L << 31;
637 if(face->fs.fsCsb[0] & ~(1L << 31))
638 have_installed_roman_font = TRUE;
639 } while(!FT_IS_SCALABLE(ft_face) && ++bitmap_num < ft_face->num_fixed_sizes);
641 num_faces = ft_face->num_faces;
642 pFT_Done_Face(ft_face);
643 TRACE("Added font %s %s\n", debugstr_w(family->FamilyName),
645 } while(num_faces > ++face_index);
649 static void DumpFontList(void)
653 struct list *family_elem_ptr, *face_elem_ptr;
655 LIST_FOR_EACH(family_elem_ptr, &font_list) {
656 family = LIST_ENTRY(family_elem_ptr, Family, entry);
657 TRACE("Family: %s\n", debugstr_w(family->FamilyName));
658 LIST_FOR_EACH(face_elem_ptr, &family->faces) {
659 face = LIST_ENTRY(face_elem_ptr, Face, entry);
660 TRACE("\t%s\t%08lx", debugstr_w(face->StyleName), face->fs.fsCsb[0]);
662 TRACE(" %ld", face->size.y_ppem >> 6);
669 static Face *find_face_from_filename(const WCHAR *name)
674 DWORD len = WideCharToMultiByte(CP_UNIXCP, 0, name, -1, NULL, 0, NULL, NULL);
675 char *nameA = HeapAlloc(GetProcessHeap(), 0, len);
678 WideCharToMultiByte(CP_UNIXCP, 0, name, -1, nameA, len, NULL, NULL);
679 TRACE("looking for %s\n", debugstr_a(nameA));
681 LIST_FOR_EACH_ENTRY(family, &font_list, Family, entry)
683 LIST_FOR_EACH_ENTRY(face, &family->faces, Face, entry)
685 file = strrchr(face->file, '/');
690 if(!strcmp(file, nameA))
695 HeapFree(GetProcessHeap(), 0, nameA);
699 static Family *find_family_from_name(const WCHAR *name)
703 LIST_FOR_EACH_ENTRY(family, &font_list, Family, entry)
705 if(!strcmpiW(family->FamilyName, name))
712 static void DumpSubstList(void)
716 for(psub = substlist; psub; psub = psub->next)
717 if(psub->from.charset != -1 || psub->to.charset != -1)
718 TRACE("%s:%d -> %s:%d\n", debugstr_w(psub->from.name),
719 psub->from.charset, debugstr_w(psub->to.name), psub->to.charset);
721 TRACE("%s -> %s\n", debugstr_w(psub->from.name),
722 debugstr_w(psub->to.name));
726 static LPWSTR strdupW(LPCWSTR p)
729 DWORD len = (strlenW(p) + 1) * sizeof(WCHAR);
730 ret = HeapAlloc(GetProcessHeap(), 0, len);
735 static LPSTR strdupA(LPCSTR p)
738 DWORD len = (strlen(p) + 1);
739 ret = HeapAlloc(GetProcessHeap(), 0, len);
744 static void split_subst_info(NameCs *nc, LPSTR str)
746 CHAR *p = strrchr(str, ',');
751 nc->charset = strtol(p+1, NULL, 10);
754 len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
755 nc->name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
756 MultiByteToWideChar(CP_ACP, 0, str, -1, nc->name, len);
759 static void LoadSubstList(void)
761 FontSubst *psub, **ppsub;
763 DWORD valuelen, datalen, i = 0, type, dlen, vlen;
768 for(psub = substlist; psub;) {
770 HeapFree(GetProcessHeap(), 0, psub->to.name);
771 HeapFree(GetProcessHeap(), 0, psub->from.name);
774 HeapFree(GetProcessHeap(), 0, ptmp);
779 if(RegOpenKeyA(HKEY_LOCAL_MACHINE,
780 "Software\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes",
781 &hkey) == ERROR_SUCCESS) {
783 RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
784 &valuelen, &datalen, NULL, NULL);
786 valuelen++; /* returned value doesn't include room for '\0' */
787 value = HeapAlloc(GetProcessHeap(), 0, valuelen * sizeof(CHAR));
788 data = HeapAlloc(GetProcessHeap(), 0, datalen);
793 while(RegEnumValueA(hkey, i++, value, &vlen, NULL, &type, data,
794 &dlen) == ERROR_SUCCESS) {
795 TRACE("Got %s=%s\n", debugstr_a(value), debugstr_a(data));
797 *ppsub = HeapAlloc(GetProcessHeap(), 0, sizeof(**ppsub));
798 (*ppsub)->next = NULL;
799 split_subst_info(&((*ppsub)->from), value);
800 split_subst_info(&((*ppsub)->to), data);
802 /* Win 2000 doesn't allow mapping between different charsets
803 or mapping of DEFAULT_CHARSET */
804 if(((*ppsub)->to.charset != (*ppsub)->from.charset) ||
805 (*ppsub)->to.charset == DEFAULT_CHARSET) {
806 HeapFree(GetProcessHeap(), 0, (*ppsub)->to.name);
807 HeapFree(GetProcessHeap(), 0, (*ppsub)->from.name);
808 HeapFree(GetProcessHeap(), 0, *ppsub);
811 ppsub = &((*ppsub)->next);
813 /* reset dlen and vlen */
817 HeapFree(GetProcessHeap(), 0, data);
818 HeapFree(GetProcessHeap(), 0, value);
823 /***********************************************************
824 * The replacement list is a way to map an entire font
825 * family onto another family. For example adding
827 * [HKCU\Software\Wine\Fonts\Replacements]
828 * "Wingdings"="Winedings"
830 * would enumerate the Winedings font both as Winedings and
831 * Wingdings. However if a real Wingdings font is present the
832 * replacement does not take place.
835 static void LoadReplaceList(void)
838 DWORD valuelen, datalen, i = 0, type, dlen, vlen;
843 struct list *family_elem_ptr, *face_elem_ptr;
844 WCHAR old_nameW[200];
846 /* @@ Wine registry key: HKCU\Software\Wine\Fonts\Replacements */
847 if(RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Fonts\\Replacements", &hkey) == ERROR_SUCCESS)
849 RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
850 &valuelen, &datalen, NULL, NULL);
852 valuelen++; /* returned value doesn't include room for '\0' */
853 value = HeapAlloc(GetProcessHeap(), 0, valuelen * sizeof(CHAR));
854 data = HeapAlloc(GetProcessHeap(), 0, datalen);
858 while(RegEnumValueA(hkey, i++, value, &vlen, NULL, &type, data,
859 &dlen) == ERROR_SUCCESS) {
860 TRACE("Got %s=%s\n", debugstr_a(value), debugstr_a(data));
861 /* "NewName"="Oldname" */
862 if(!MultiByteToWideChar(CP_ACP, 0, data, -1, old_nameW, sizeof(old_nameW)/sizeof(WCHAR)))
865 /* Find the old family and hence all of the font files
867 LIST_FOR_EACH(family_elem_ptr, &font_list) {
868 family = LIST_ENTRY(family_elem_ptr, Family, entry);
869 if(!strcmpiW(family->FamilyName, old_nameW)) {
870 LIST_FOR_EACH(face_elem_ptr, &family->faces) {
871 face = LIST_ENTRY(face_elem_ptr, Face, entry);
872 TRACE("mapping %s %s to %s\n", debugstr_w(family->FamilyName),
873 debugstr_w(face->StyleName), value);
874 /* Now add a new entry with the new family name */
875 AddFontFileToList(face->file, value, ADDFONT_FORCE_BITMAP | (face->external ? ADDFONT_EXTERNAL_FONT : 0));
880 /* reset dlen and vlen */
884 HeapFree(GetProcessHeap(), 0, data);
885 HeapFree(GetProcessHeap(), 0, value);
890 /*************************************************************
893 static BOOL init_system_links(void)
895 static const WCHAR system_link[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
896 'W','i','n','d','o','w','s',' ','N','T','\\',
897 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\','F','o','n','t','L','i','n','k','\\',
898 'S','y','s','t','e','m','L','i','n','k',0};
901 DWORD type, max_val, max_data, val_len, data_len, index;
904 SYSTEM_LINKS *font_link, *system_font_link;
905 CHILD_FONT *child_font;
906 static const WCHAR Tahoma[] = {'T','a','h','o','m','a',0};
907 static const WCHAR tahoma_ttf[] = {'t','a','h','o','m','a','.','t','t','f',0};
908 static const WCHAR System[] = {'S','y','s','t','e','m',0};
913 if(RegOpenKeyW(HKEY_LOCAL_MACHINE, system_link, &hkey) == ERROR_SUCCESS)
915 RegQueryInfoKeyW(hkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &max_val, &max_data, NULL, NULL);
916 value = HeapAlloc(GetProcessHeap(), 0, (max_val + 1) * sizeof(WCHAR));
917 data = HeapAlloc(GetProcessHeap(), 0, max_data);
918 val_len = max_val + 1;
921 while(RegEnumValueW(hkey, index++, value, &val_len, NULL, &type, (LPBYTE)data, &data_len) == ERROR_SUCCESS)
923 TRACE("%s:\n", debugstr_w(value));
925 memset(&fs, 0, sizeof(fs));
926 font_link = HeapAlloc(GetProcessHeap(), 0, sizeof(*font_link));
927 font_link->font_name = strdupW(value);
928 list_init(&font_link->links);
929 for(entry = data; (char*)entry < (char*)data + data_len && *entry != 0; entry = next)
933 CHILD_FONT *child_font;
935 TRACE("\t%s\n", debugstr_w(entry));
937 next = entry + strlenW(entry) + 1;
939 face_name = strchrW(entry, ',');
944 FIXME("don't yet handle ttc's correctly in linking. Assuming index 0\n");
946 while(isspaceW(*face_name))
951 face = find_face_from_filename(entry);
954 TRACE("Unable to find file %s\n", debugstr_w(entry));
958 child_font = HeapAlloc(GetProcessHeap(), 0, sizeof(*child_font));
959 child_font->file_name = strdupA(face->file);
960 child_font->index = index;
961 child_font->font = NULL;
962 fs.fsCsb[0] |= face->fs.fsCsb[0];
963 fs.fsCsb[1] |= face->fs.fsCsb[1];
964 list_add_tail(&font_link->links, &child_font->entry);
966 family = find_family_from_name(font_link->font_name);
969 LIST_FOR_EACH_ENTRY(face, &family->faces, Face, entry)
971 memcpy(&face->fs_links, &fs, sizeof(fs));
974 list_add_tail(&system_links, &font_link->entry);
975 val_len = max_val + 1;
979 HeapFree(GetProcessHeap(), 0, value);
980 HeapFree(GetProcessHeap(), 0, data);
984 /* Explicitly add an entry for the system font, this links to Tahoma and any links
987 system_font_link = HeapAlloc(GetProcessHeap(), 0, sizeof(*system_font_link));
988 system_font_link->font_name = strdupW(System);
989 list_init(&system_font_link->links);
991 face = find_face_from_filename(tahoma_ttf);
994 child_font = HeapAlloc(GetProcessHeap(), 0, sizeof(*child_font));
995 child_font->file_name = strdupA(face->file);
996 child_font->index = 0;
997 child_font->font = NULL;
998 list_add_tail(&system_font_link->links, &child_font->entry);
1000 LIST_FOR_EACH_ENTRY(font_link, &system_links, SYSTEM_LINKS, entry)
1002 if(!strcmpiW(font_link->font_name, Tahoma))
1004 CHILD_FONT *font_link_entry;
1005 LIST_FOR_EACH_ENTRY(font_link_entry, &font_link->links, CHILD_FONT, entry)
1007 CHILD_FONT *new_child;
1008 new_child = HeapAlloc(GetProcessHeap(), 0, sizeof(*new_child));
1009 new_child->file_name = strdupA(font_link_entry->file_name);
1010 new_child->index = font_link_entry->index;
1011 new_child->font = NULL;
1012 list_add_tail(&system_font_link->links, &new_child->entry);
1017 list_add_tail(&system_links, &system_font_link->entry);
1021 static BOOL ReadFontDir(const char *dirname, BOOL external_fonts)
1024 struct dirent *dent;
1025 char path[MAX_PATH];
1027 TRACE("Loading fonts from %s\n", debugstr_a(dirname));
1029 dir = opendir(dirname);
1031 ERR("Can't open directory %s\n", debugstr_a(dirname));
1034 while((dent = readdir(dir)) != NULL) {
1035 struct stat statbuf;
1037 if(!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
1040 TRACE("Found %s in %s\n", debugstr_a(dent->d_name), debugstr_a(dirname));
1042 sprintf(path, "%s/%s", dirname, dent->d_name);
1044 if(stat(path, &statbuf) == -1)
1046 WARN("Can't stat %s\n", debugstr_a(path));
1049 if(S_ISDIR(statbuf.st_mode))
1050 ReadFontDir(path, external_fonts);
1052 AddFontFileToList(path, NULL, external_fonts ? ADDFONT_EXTERNAL_FONT : 0);
1058 static void load_fontconfig_fonts(void)
1060 #ifdef HAVE_FONTCONFIG_FONTCONFIG_H
1061 void *fc_handle = NULL;
1068 const char *file, *ext;
1070 fc_handle = wine_dlopen(SONAME_LIBFONTCONFIG, RTLD_NOW, NULL, 0);
1072 TRACE("Wine cannot find the fontconfig library (%s).\n",
1073 SONAME_LIBFONTCONFIG);
1076 #define LOAD_FUNCPTR(f) if((p##f = wine_dlsym(fc_handle, #f, NULL, 0)) == NULL){WARN("Can't find symbol %s\n", #f); goto sym_not_found;}
1077 LOAD_FUNCPTR(FcConfigGetCurrent);
1078 LOAD_FUNCPTR(FcFontList);
1079 LOAD_FUNCPTR(FcFontSetDestroy);
1080 LOAD_FUNCPTR(FcInit);
1081 LOAD_FUNCPTR(FcObjectSetAdd);
1082 LOAD_FUNCPTR(FcObjectSetCreate);
1083 LOAD_FUNCPTR(FcObjectSetDestroy);
1084 LOAD_FUNCPTR(FcPatternCreate);
1085 LOAD_FUNCPTR(FcPatternDestroy);
1086 LOAD_FUNCPTR(FcPatternGet);
1089 if(!pFcInit()) return;
1091 config = pFcConfigGetCurrent();
1092 pat = pFcPatternCreate();
1093 os = pFcObjectSetCreate();
1094 pFcObjectSetAdd(os, FC_FILE);
1095 fontset = pFcFontList(config, pat, os);
1096 if(!fontset) return;
1097 for(i = 0; i < fontset->nfont; i++) {
1098 if(pFcPatternGet(fontset->fonts[i], FC_FILE, 0, &v) != FcResultMatch)
1100 if(v.type != FcTypeString) continue;
1101 file = (LPCSTR) v.u.s;
1102 TRACE("fontconfig: %s\n", file);
1104 /* We're just interested in OT/TT fonts for now, so this hack just
1105 picks up the standard extensions to save time loading every other
1107 len = strlen( file );
1108 if(len < 4) continue;
1109 ext = &file[ len - 3 ];
1110 if(!strcasecmp(ext, "ttf") || !strcasecmp(ext, "ttc") || !strcasecmp(ext, "otf"))
1111 AddFontFileToList(file, NULL, ADDFONT_EXTERNAL_FONT);
1113 pFcFontSetDestroy(fontset);
1114 pFcObjectSetDestroy(os);
1115 pFcPatternDestroy(pat);
1122 static void load_system_fonts(void)
1125 WCHAR data[MAX_PATH], windowsdir[MAX_PATH], pathW[MAX_PATH];
1126 const WCHAR **value;
1128 static const WCHAR fmtW[] = {'%','s','\\','%','s','\0'};
1131 if(RegOpenKeyW(HKEY_CURRENT_CONFIG, system_fonts_reg_key, &hkey) == ERROR_SUCCESS) {
1132 GetWindowsDirectoryW(windowsdir, sizeof(windowsdir) / sizeof(WCHAR));
1133 strcatW(windowsdir, fontsW);
1134 for(value = SystemFontValues; *value; value++) {
1135 dlen = sizeof(data);
1136 if(RegQueryValueExW(hkey, *value, 0, &type, (void*)data, &dlen) == ERROR_SUCCESS &&
1138 sprintfW(pathW, fmtW, windowsdir, data);
1139 if((unixname = wine_get_unix_file_name(pathW))) {
1140 AddFontFileToList(unixname, NULL, ADDFONT_FORCE_BITMAP);
1141 HeapFree(GetProcessHeap(), 0, unixname);
1149 /*************************************************************
1151 * This adds registry entries for any externally loaded fonts
1152 * (fonts from fontconfig or FontDirs). It also deletes entries
1153 * of no longer existing fonts.
1156 static void update_reg_entries(void)
1158 HKEY winkey = 0, externalkey = 0;
1161 DWORD dlen, vlen, datalen, valuelen, i, type, len, len_fam;
1164 struct list *family_elem_ptr, *face_elem_ptr;
1166 static const WCHAR TrueType[] = {' ','(','T','r','u','e','T','y','p','e',')','\0'};
1167 static const WCHAR spaceW[] = {' ', '\0'};
1170 if(RegCreateKeyExW(HKEY_LOCAL_MACHINE, is_win9x() ? win9x_font_reg_key : winnt_font_reg_key,
1171 0, NULL, 0, KEY_ALL_ACCESS, NULL, &winkey, NULL) != ERROR_SUCCESS) {
1172 ERR("Can't create Windows font reg key\n");
1175 /* @@ Wine registry key: HKCU\Software\Wine\Fonts\ExternalFonts */
1176 if(RegCreateKeyW(HKEY_CURRENT_USER, external_fonts_reg_key, &externalkey) != ERROR_SUCCESS) {
1177 ERR("Can't create external font reg key\n");
1181 /* Delete all external fonts added last time */
1183 RegQueryInfoKeyW(externalkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1184 &valuelen, &datalen, NULL, NULL);
1185 valuelen++; /* returned value doesn't include room for '\0' */
1186 valueW = HeapAlloc(GetProcessHeap(), 0, valuelen * sizeof(WCHAR));
1187 data = HeapAlloc(GetProcessHeap(), 0, datalen * sizeof(WCHAR));
1189 dlen = datalen * sizeof(WCHAR);
1192 while(RegEnumValueW(externalkey, i++, valueW, &vlen, NULL, &type, data,
1193 &dlen) == ERROR_SUCCESS) {
1195 RegDeleteValueW(winkey, valueW);
1196 /* reset dlen and vlen */
1200 HeapFree(GetProcessHeap(), 0, data);
1201 HeapFree(GetProcessHeap(), 0, valueW);
1203 /* Delete the old external fonts key */
1204 RegCloseKey(externalkey);
1206 RegDeleteKeyW(HKEY_CURRENT_USER, external_fonts_reg_key);
1208 /* @@ Wine registry key: HKCU\Software\Wine\Fonts\ExternalFonts */
1209 if(RegCreateKeyExW(HKEY_CURRENT_USER, external_fonts_reg_key,
1210 0, NULL, 0, KEY_ALL_ACCESS, NULL, &externalkey, NULL) != ERROR_SUCCESS) {
1211 ERR("Can't create external font reg key\n");
1215 /* enumerate the fonts and add external ones to the two keys */
1217 LIST_FOR_EACH(family_elem_ptr, &font_list) {
1218 family = LIST_ENTRY(family_elem_ptr, Family, entry);
1219 len_fam = strlenW(family->FamilyName) + sizeof(TrueType) / sizeof(WCHAR) + 1;
1220 LIST_FOR_EACH(face_elem_ptr, &family->faces) {
1221 face = LIST_ENTRY(face_elem_ptr, Face, entry);
1222 if(!face->external) continue;
1224 if(strcmpiW(face->StyleName, RegularW))
1225 len = len_fam + strlenW(face->StyleName) + 1;
1226 valueW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1227 strcpyW(valueW, family->FamilyName);
1228 if(len != len_fam) {
1229 strcatW(valueW, spaceW);
1230 strcatW(valueW, face->StyleName);
1232 strcatW(valueW, TrueType);
1233 if((path = strrchr(face->file, '/')) == NULL)
1237 len = MultiByteToWideChar(CP_ACP, 0, path, -1, NULL, 0);
1239 file = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1240 MultiByteToWideChar(CP_ACP, 0, path, -1, file, len);
1241 RegSetValueExW(winkey, valueW, 0, REG_SZ, (BYTE*)file, len * sizeof(WCHAR));
1242 RegSetValueExW(externalkey, valueW, 0, REG_SZ, (BYTE*)file, len * sizeof(WCHAR));
1244 HeapFree(GetProcessHeap(), 0, file);
1245 HeapFree(GetProcessHeap(), 0, valueW);
1250 RegCloseKey(externalkey);
1252 RegCloseKey(winkey);
1257 /*************************************************************
1258 * WineEngAddFontResourceEx
1261 INT WineEngAddFontResourceEx(LPCWSTR file, DWORD flags, PVOID pdv)
1263 if (ft_handle) /* do it only if we have freetype up and running */
1268 FIXME("Ignoring flags %lx\n", flags);
1270 if((unixname = wine_get_unix_file_name(file)))
1272 AddFontFileToList(unixname, NULL, ADDFONT_FORCE_BITMAP);
1273 HeapFree(GetProcessHeap(), 0, unixname);
1279 /*************************************************************
1280 * WineEngRemoveFontResourceEx
1283 BOOL WineEngRemoveFontResourceEx(LPCWSTR file, DWORD flags, PVOID pdv)
1289 static const struct nls_update_font_list
1291 UINT ansi_cp, oem_cp;
1292 const char *oem, *fixed, *system;
1293 const char *courier, *serif, *small, *sserif;
1294 } nls_update_font_list[] =
1296 /* Latin 1 (United States) */
1297 { 1252, 437, "vgaoem.fon", "vgafix.fon", "vgasys.fon",
1298 "coure.fon", "serife.fon", "smalle.fon", "sserife.fon",
1300 /* Latin 1 (Multilingual) */
1301 { 1252, 850, "vga850.fon", "vgafix.fon", "vgasys.fon",
1302 "coure.fon", "serife.fon", "smalle.fon", "sserife.fon",
1304 /* Eastern Europe */
1305 { 1250, 852, "vga852.fon", "vgafixe.fon", "vgasyse.fon",
1306 "couree.fon", "serifee.fon", "smallee.fon", "sserifee.fon",
1309 { 1251, 866, "vga866.fon", "vgafixr.fon", "vgasysr.fon",
1310 "courer.fon", "serifer.fon", "smaller.fon", "sserifer.fon",
1313 { 1253, 737, "vga869.fon", "vgafixg.fon", "vgasysg.fon",
1314 "coureg.fon", "serifeg.fon", "smalleg.fon", "sserifeg.fon",
1317 { 1254, 857, "vga857.fon", "vgafixt.fon", "vgasyst.fon",
1318 "couret.fon", "serifet.fon", "smallet.fon", "sserifet.fon",
1321 { 1255, 862, "vgaoem.fon", "vgaf1255.fon", "vgas1255.fon",
1322 "coue1255.fon", "sere1255.fon", "smae1255.fon", "ssee1255.fon",
1325 { 1256, 720, "vgaoem.fon", "vgaf1256.fon", "vgas1256.fon",
1326 "coue1256.fon", "sere1256.fon", "smae1256.fon", "ssee1256.fon",
1329 { 1257, 775, "vga775.fon", "vgaf1257.fon", "vgas1257.fon",
1330 "coue1257.fon", "sere1257.fon", "smae1257.fon", "ssee1257.fon",
1333 { 1258, 1258, "vga850.fon", "vgafix.fon", "vgasys.fon",
1334 "coure.fon", "serife.fon", "smalle.fon", "sserife.fon",
1337 { 874, 874, "vga850.fon", "vgaf874.fon", "vgas874.fon",
1338 "coure.fon", "serife.fon", "smalle.fon", "ssee874.fon",
1341 { 932, 932, "vga932.fon", "jvgafix.fon", "jvgasys.fon",
1342 "coure.fon", "serife.fon", "jsmalle.fon", "sserife.fon",
1344 /* Chinese Simplified */
1345 { 936, 936, "vga936.fon", "svgafix.fon", "svgasys.fon",
1346 "coure.fon", "serife.fon", "smalle.fon", "sserife.fon",
1349 { 949, 949, "vga949.fon", "hvgafix.fon", "hvgasys.fon",
1350 "coure.fon", "serife.fon", "smalle.fon", "sserife.fon",
1352 /* Chinese Traditional */
1353 { 950, 950, "vga950.fon", "cvgafix.fon", "cvgasys.fon",
1354 "coure.fon", "serife.fon", "smalle.fon", "sserife.fon",
1358 inline static HKEY create_fonts_NT_registry_key(void)
1362 RegCreateKeyExW(HKEY_LOCAL_MACHINE, winnt_font_reg_key, 0, NULL,
1363 0, KEY_ALL_ACCESS, NULL, &hkey, NULL);
1367 inline static HKEY create_fonts_9x_registry_key(void)
1371 RegCreateKeyExW(HKEY_LOCAL_MACHINE, win9x_font_reg_key, 0, NULL,
1372 0, KEY_ALL_ACCESS, NULL, &hkey, NULL);
1376 inline static HKEY create_config_fonts_registry_key(void)
1380 RegCreateKeyExW(HKEY_CURRENT_CONFIG, system_fonts_reg_key, 0, NULL,
1381 0, KEY_ALL_ACCESS, NULL, &hkey, NULL);
1385 static void add_font_list(HKEY hkey, const struct nls_update_font_list *fl)
1387 RegSetValueExA(hkey, "Courier", 0, REG_SZ, (const BYTE *)fl->courier, strlen(fl->courier)+1);
1388 RegSetValueExA(hkey, "MS Serif", 0, REG_SZ, (const BYTE *)fl->serif, strlen(fl->serif)+1);
1389 RegSetValueExA(hkey, "MS Sans Serif", 0, REG_SZ, (const BYTE *)fl->sserif, strlen(fl->sserif)+1);
1390 RegSetValueExA(hkey, "Small Fonts", 0, REG_SZ, (const BYTE *)fl->small, strlen(fl->small)+1);
1393 static void update_font_info(void)
1398 UINT i, ansi_cp = 0, oem_cp = 0;
1399 LCID lcid = GetUserDefaultLCID();
1401 if (RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Fonts", &hkey) != ERROR_SUCCESS)
1405 if (RegQueryValueExA(hkey, "Locale", 0, &type, (BYTE *)buf, &len) == ERROR_SUCCESS && type == REG_SZ)
1407 if (strtoul(buf, NULL, 16 ) == lcid) /* already set correctly */
1412 TRACE("updating registry, locale changed %s -> %08lx\n", debugstr_a(buf), lcid);
1414 else TRACE("updating registry, locale changed none -> %08lx\n", lcid);
1416 sprintf(buf, "%08lx", lcid);
1417 RegSetValueExA(hkey, "Locale", 0, REG_SZ, (const BYTE *)buf, strlen(buf)+1);
1420 GetLocaleInfoW(lcid, LOCALE_IDEFAULTANSICODEPAGE|LOCALE_RETURN_NUMBER|LOCALE_NOUSEROVERRIDE,
1421 (WCHAR *)&ansi_cp, sizeof(ansi_cp)/sizeof(WCHAR));
1422 GetLocaleInfoW(lcid, LOCALE_IDEFAULTCODEPAGE|LOCALE_RETURN_NUMBER|LOCALE_NOUSEROVERRIDE,
1423 (WCHAR *)&oem_cp, sizeof(oem_cp)/sizeof(WCHAR));
1425 for (i = 0; i < sizeof(nls_update_font_list)/sizeof(nls_update_font_list[0]); i++)
1427 if (nls_update_font_list[i].ansi_cp == ansi_cp &&
1428 nls_update_font_list[i].oem_cp == oem_cp)
1432 hkey = create_config_fonts_registry_key();
1433 RegSetValueExA(hkey, "OEMFONT.FON", 0, REG_SZ, (const BYTE *)nls_update_font_list[i].oem, strlen(nls_update_font_list[i].oem)+1);
1434 RegSetValueExA(hkey, "FIXED.FON", 0, REG_SZ, (const BYTE *)nls_update_font_list[i].fixed, strlen(nls_update_font_list[i].fixed)+1);
1435 RegSetValueExA(hkey, "FONTS.FON", 0, REG_SZ, (const BYTE *)nls_update_font_list[i].system, strlen(nls_update_font_list[i].system)+1);
1438 hkey = create_fonts_NT_registry_key();
1439 add_font_list(hkey, &nls_update_font_list[i]);
1442 hkey = create_fonts_9x_registry_key();
1443 add_font_list(hkey, &nls_update_font_list[i]);
1449 FIXME("there is no font defaults for lcid %04lx/ansi_cp %u", lcid, ansi_cp);
1452 /*************************************************************
1455 * Initialize FreeType library and create a list of available faces
1457 BOOL WineEngInit(void)
1459 static const WCHAR dot_fonW[] = {'.','f','o','n','\0'};
1460 static const WCHAR pathW[] = {'P','a','t','h',0};
1462 DWORD valuelen, datalen, i = 0, type, dlen, vlen;
1464 WCHAR windowsdir[MAX_PATH];
1470 /* update locale dependent font info in registry */
1473 ft_handle = wine_dlopen(SONAME_LIBFREETYPE, RTLD_NOW, NULL, 0);
1476 "Wine cannot find the FreeType font library. To enable Wine to\n"
1477 "use TrueType fonts please install a version of FreeType greater than\n"
1478 "or equal to 2.0.5.\n"
1479 "http://www.freetype.org\n");
1483 #define LOAD_FUNCPTR(f) if((p##f = wine_dlsym(ft_handle, #f, NULL, 0)) == NULL){WARN("Can't find symbol %s\n", #f); goto sym_not_found;}
1485 LOAD_FUNCPTR(FT_Vector_Unit)
1486 LOAD_FUNCPTR(FT_Done_Face)
1487 LOAD_FUNCPTR(FT_Get_Char_Index)
1488 LOAD_FUNCPTR(FT_Get_Module)
1489 LOAD_FUNCPTR(FT_Get_Sfnt_Table)
1490 LOAD_FUNCPTR(FT_Init_FreeType)
1491 LOAD_FUNCPTR(FT_Load_Glyph)
1492 LOAD_FUNCPTR(FT_Matrix_Multiply)
1493 LOAD_FUNCPTR(FT_MulFix)
1494 LOAD_FUNCPTR(FT_New_Face)
1495 LOAD_FUNCPTR(FT_Outline_Get_Bitmap)
1496 LOAD_FUNCPTR(FT_Outline_Transform)
1497 LOAD_FUNCPTR(FT_Outline_Translate)
1498 LOAD_FUNCPTR(FT_Select_Charmap)
1499 LOAD_FUNCPTR(FT_Set_Pixel_Sizes)
1500 LOAD_FUNCPTR(FT_Vector_Transform)
1503 /* Don't warn if this one is missing */
1504 pFT_Library_Version = wine_dlsym(ft_handle, "FT_Library_Version", NULL, 0);
1505 pFT_Load_Sfnt_Table = wine_dlsym(ft_handle, "FT_Load_Sfnt_Table", NULL, 0);
1506 pFT_Get_First_Char = wine_dlsym(ft_handle, "FT_Get_First_Char", NULL, 0);
1507 pFT_Module_Get_Flags = wine_dlsym(ft_handle, "FT_Module_Get_Flags", NULL, 0);
1508 #ifdef HAVE_FREETYPE_FTWINFNT_H
1509 pFT_Get_WinFNT_Header = wine_dlsym(ft_handle, "FT_Get_WinFNT_Header", NULL, 0);
1511 if(!wine_dlsym(ft_handle, "FT_Get_Postscript_Name", NULL, 0) &&
1512 !wine_dlsym(ft_handle, "FT_Sqrt64", NULL, 0)) {
1513 /* try to avoid 2.0.4: >= 2.0.5 has FT_Get_Postscript_Name and
1514 <= 2.0.3 has FT_Sqrt64 */
1518 if(pFT_Init_FreeType(&library) != 0) {
1519 ERR("Can't init FreeType library\n");
1520 wine_dlclose(ft_handle, NULL, 0);
1524 FT_Version.major=FT_Version.minor=FT_Version.patch=-1;
1525 if (pFT_Library_Version)
1527 pFT_Library_Version(library,&FT_Version.major,&FT_Version.minor,&FT_Version.patch);
1529 if (FT_Version.major<=0)
1535 TRACE("FreeType version is %d.%d.%d\n",FT_Version.major,FT_Version.minor,FT_Version.patch);
1536 FT_SimpleVersion = ((FT_Version.major << 16) & 0xff0000) |
1537 ((FT_Version.minor << 8) & 0x00ff00) |
1538 ((FT_Version.patch ) & 0x0000ff);
1540 if((font_mutex = CreateMutexW(NULL, FALSE, font_mutex_nameW)) == NULL) {
1541 ERR("Failed to create font mutex\n");
1544 WaitForSingleObject(font_mutex, INFINITE);
1546 /* load the system fonts */
1547 load_system_fonts();
1549 /* load in the fonts from %WINDOWSDIR%\\Fonts first of all */
1550 GetWindowsDirectoryW(windowsdir, sizeof(windowsdir) / sizeof(WCHAR));
1551 strcatW(windowsdir, fontsW);
1552 if((unixname = wine_get_unix_file_name(windowsdir)))
1554 ReadFontDir(unixname, FALSE);
1555 HeapFree(GetProcessHeap(), 0, unixname);
1558 /* now look under HKLM\Software\Microsoft\Windows[ NT]\CurrentVersion\Fonts
1559 for any fonts not installed in %WINDOWSDIR%\Fonts. They will have their
1560 full path as the entry. Also look for any .fon fonts, since ReadFontDir
1562 if(RegOpenKeyW(HKEY_LOCAL_MACHINE,
1563 is_win9x() ? win9x_font_reg_key : winnt_font_reg_key,
1564 &hkey) == ERROR_SUCCESS) {
1566 RegQueryInfoKeyW(hkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1567 &valuelen, &datalen, NULL, NULL);
1569 valuelen++; /* returned value doesn't include room for '\0' */
1570 valueW = HeapAlloc(GetProcessHeap(), 0, valuelen * sizeof(WCHAR));
1571 data = HeapAlloc(GetProcessHeap(), 0, datalen * sizeof(WCHAR));
1574 dlen = datalen * sizeof(WCHAR);
1576 while(RegEnumValueW(hkey, i++, valueW, &vlen, NULL, &type, data,
1577 &dlen) == ERROR_SUCCESS) {
1578 if(((LPWSTR)data)[0] && ((LPWSTR)data)[1] == ':')
1580 if((unixname = wine_get_unix_file_name((LPWSTR)data)))
1582 AddFontFileToList(unixname, NULL, ADDFONT_FORCE_BITMAP);
1583 HeapFree(GetProcessHeap(), 0, unixname);
1586 else if(dlen / 2 >= 6 && !strcmpiW(((LPWSTR)data) + dlen / 2 - 5, dot_fonW))
1588 WCHAR pathW[MAX_PATH];
1589 static const WCHAR fmtW[] = {'%','s','\\','%','s','\0'};
1590 sprintfW(pathW, fmtW, windowsdir, data);
1591 if((unixname = wine_get_unix_file_name(pathW)))
1593 AddFontFileToList(unixname, NULL, ADDFONT_FORCE_BITMAP);
1594 HeapFree(GetProcessHeap(), 0, unixname);
1597 /* reset dlen and vlen */
1602 HeapFree(GetProcessHeap(), 0, data);
1603 HeapFree(GetProcessHeap(), 0, valueW);
1607 load_fontconfig_fonts();
1609 /* then look in any directories that we've specified in the config file */
1610 /* @@ Wine registry key: HKCU\Software\Wine\Fonts */
1611 if(RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Fonts", &hkey) == ERROR_SUCCESS)
1617 if (RegQueryValueExW( hkey, pathW, NULL, NULL, NULL, &len ) == ERROR_SUCCESS)
1619 len += sizeof(WCHAR);
1620 valueW = HeapAlloc( GetProcessHeap(), 0, len );
1621 if (RegQueryValueExW( hkey, pathW, NULL, NULL, (LPBYTE)valueW, &len ) == ERROR_SUCCESS)
1623 len = WideCharToMultiByte( CP_UNIXCP, 0, valueW, -1, NULL, 0, NULL, NULL );
1624 valueA = HeapAlloc( GetProcessHeap(), 0, len );
1625 WideCharToMultiByte( CP_UNIXCP, 0, valueW, -1, valueA, len, NULL, NULL );
1626 TRACE( "got font path %s\n", debugstr_a(valueA) );
1630 LPSTR next = strchr( ptr, ':' );
1631 if (next) *next++ = 0;
1632 ReadFontDir( ptr, TRUE );
1635 HeapFree( GetProcessHeap(), 0, valueA );
1637 HeapFree( GetProcessHeap(), 0, valueW );
1646 update_reg_entries();
1648 init_system_links();
1650 ReleaseMutex(font_mutex);
1654 "Wine cannot find certain functions that it needs inside the FreeType\n"
1655 "font library. To enable Wine to use TrueType fonts please upgrade\n"
1656 "FreeType to at least version 2.0.5.\n"
1657 "http://www.freetype.org\n");
1658 wine_dlclose(ft_handle, NULL, 0);
1664 static LONG calc_ppem_for_height(FT_Face ft_face, LONG height)
1667 TT_HoriHeader *pHori;
1671 pOS2 = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_os2);
1672 pHori = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_hhea);
1674 if(height == 0) height = 16;
1676 /* Calc. height of EM square:
1678 * For +ve lfHeight we have
1679 * lfHeight = (winAscent + winDescent) * ppem / units_per_em
1680 * Re-arranging gives:
1681 * ppem = units_per_em * lfheight / (winAscent + winDescent)
1683 * For -ve lfHeight we have
1685 * [i.e. |lfHeight| = (winAscent + winDescent - il) * ppem / units_per_em
1686 * with il = winAscent + winDescent - units_per_em]
1691 if(pOS2->usWinAscent + pOS2->usWinDescent == 0)
1692 ppem = ft_face->units_per_EM * height /
1693 (pHori->Ascender - pHori->Descender);
1695 ppem = ft_face->units_per_EM * height /
1696 (pOS2->usWinAscent + pOS2->usWinDescent);
1704 static LONG load_VDMX(GdiFont, LONG);
1706 static FT_Face OpenFontFile(GdiFont font, char *file, FT_Long face_index, LONG width, LONG height)
1711 TRACE("%s, %ld, %ld x %ld\n", debugstr_a(file), face_index, width, height);
1712 err = pFT_New_Face(library, file, face_index, &ft_face);
1714 ERR("FT_New_Face rets %d\n", err);
1718 /* set it here, as load_VDMX needs it */
1719 font->ft_face = ft_face;
1721 if(FT_IS_SCALABLE(ft_face)) {
1722 /* load the VDMX table if we have one */
1723 font->ppem = load_VDMX(font, height);
1725 font->ppem = calc_ppem_for_height(ft_face, height);
1727 if((err = pFT_Set_Pixel_Sizes(ft_face, 0, font->ppem)) != 0)
1728 WARN("FT_Set_Pixel_Sizes %d, %ld rets %x\n", 0, font->ppem, err);
1730 font->ppem = height;
1731 if((err = pFT_Set_Pixel_Sizes(ft_face, width, height)) != 0)
1732 WARN("FT_Set_Pixel_Sizes %ld, %ld rets %x\n", width, height, err);
1738 static int get_nearest_charset(Face *face, int *cp)
1740 /* Only get here if lfCharSet == DEFAULT_CHARSET or we couldn't find
1741 a single face with the requested charset. The idea is to check if
1742 the selected font supports the current ANSI codepage, if it does
1743 return the corresponding charset, else return the first charset */
1746 int acp = GetACP(), i;
1750 if(TranslateCharsetInfo((DWORD*)acp, &csi, TCI_SRCCODEPAGE))
1751 if(csi.fs.fsCsb[0] & face->fs.fsCsb[0])
1752 return csi.ciCharset;
1754 for(i = 0; i < 32; i++) {
1756 if(face->fs.fsCsb[0] & fs0) {
1757 if(TranslateCharsetInfo(&fs0, &csi, TCI_SRCFONTSIG)) {
1759 return csi.ciCharset;
1762 FIXME("TCI failing on %lx\n", fs0);
1766 FIXME("returning DEFAULT_CHARSET face->fs.fsCsb[0] = %08lx file = %s\n",
1767 face->fs.fsCsb[0], face->file);
1769 return DEFAULT_CHARSET;
1772 static GdiFont alloc_font(void)
1774 GdiFont ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*ret));
1775 ret->gmsize = INIT_GM_SIZE;
1776 ret->gm = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1777 ret->gmsize * sizeof(*ret->gm));
1779 ret->font_desc.matrix.eM11 = ret->font_desc.matrix.eM22 = 1.0;
1780 list_init(&ret->hfontlist);
1781 list_init(&ret->child_fonts);
1785 static void free_font(GdiFont font)
1787 struct list *cursor, *cursor2;
1789 LIST_FOR_EACH_SAFE(cursor, cursor2, &font->child_fonts)
1791 CHILD_FONT *child = LIST_ENTRY(cursor, CHILD_FONT, entry);
1792 struct list *first_hfont;
1793 HFONTLIST *hfontlist;
1794 list_remove(cursor);
1797 first_hfont = list_head(&child->font->hfontlist);
1798 hfontlist = LIST_ENTRY(first_hfont, HFONTLIST, entry);
1799 DeleteObject(hfontlist->hfont);
1800 HeapFree(GetProcessHeap(), 0, hfontlist);
1801 free_font(child->font);
1803 HeapFree(GetProcessHeap(), 0, child->file_name);
1804 HeapFree(GetProcessHeap(), 0, child);
1807 if (font->ft_face) pFT_Done_Face(font->ft_face);
1808 HeapFree(GetProcessHeap(), 0, font->potm);
1809 HeapFree(GetProcessHeap(), 0, font->name);
1810 HeapFree(GetProcessHeap(), 0, font->gm);
1811 HeapFree(GetProcessHeap(), 0, font);
1815 /*************************************************************
1818 * load the vdmx entry for the specified height
1821 #define MS_MAKE_TAG( _x1, _x2, _x3, _x4 ) \
1822 ( ( (FT_ULong)_x4 << 24 ) | \
1823 ( (FT_ULong)_x3 << 16 ) | \
1824 ( (FT_ULong)_x2 << 8 ) | \
1827 #define MS_VDMX_TAG MS_MAKE_TAG('V', 'D', 'M', 'X')
1837 static LONG load_VDMX(GdiFont font, LONG height)
1839 BYTE hdr[6], tmp[2], group[4];
1840 BYTE devXRatio, devYRatio;
1841 USHORT numRecs, numRatios;
1842 DWORD result, offset = -1;
1846 /* For documentation on VDMX records, see
1847 * http://www.microsoft.com/OpenType/OTSpec/vdmx.htm
1850 result = WineEngGetFontData(font, MS_VDMX_TAG, 0, hdr, 6);
1852 if(result == GDI_ERROR) /* no vdmx table present, use linear scaling */
1855 /* FIXME: need the real device aspect ratio */
1859 numRecs = GET_BE_WORD(&hdr[2]);
1860 numRatios = GET_BE_WORD(&hdr[4]);
1862 TRACE("numRecs = %d numRatios = %d\n", numRecs, numRatios);
1863 for(i = 0; i < numRatios; i++) {
1866 offset = (3 * 2) + (i * sizeof(Ratios));
1867 WineEngGetFontData(font, MS_VDMX_TAG, offset, &ratio, sizeof(Ratios));
1870 TRACE("Ratios[%d] %d %d : %d -> %d\n", i, ratio.bCharSet, ratio.xRatio, ratio.yStartRatio, ratio.yEndRatio);
1872 if((ratio.xRatio == 0 &&
1873 ratio.yStartRatio == 0 &&
1874 ratio.yEndRatio == 0) ||
1875 (devXRatio == ratio.xRatio &&
1876 devYRatio >= ratio.yStartRatio &&
1877 devYRatio <= ratio.yEndRatio))
1879 offset = (3 * 2) + (numRatios * 4) + (i * 2);
1880 WineEngGetFontData(font, MS_VDMX_TAG, offset, tmp, 2);
1881 offset = GET_BE_WORD(tmp);
1887 FIXME("No suitable ratio found\n");
1891 if(WineEngGetFontData(font, MS_VDMX_TAG, offset, group, 4) != GDI_ERROR) {
1893 BYTE startsz, endsz;
1896 recs = GET_BE_WORD(group);
1900 TRACE("recs=%d startsz=%d endsz=%d\n", recs, startsz, endsz);
1902 vTable = HeapAlloc(GetProcessHeap(), 0, recs * 6);
1903 result = WineEngGetFontData(font, MS_VDMX_TAG, offset + 4, vTable, recs * 6);
1904 if(result == GDI_ERROR) {
1905 FIXME("Failed to retrieve vTable\n");
1910 for(i = 0; i < recs; i++) {
1911 SHORT yMax = GET_BE_WORD(&vTable[(i * 6) + 2]);
1912 SHORT yMin = GET_BE_WORD(&vTable[(i * 6) + 4]);
1913 ppem = GET_BE_WORD(&vTable[i * 6]);
1915 if(yMax + -yMin == height) {
1918 TRACE("ppem %ld found; height=%ld yMax=%d yMin=%d\n", ppem, height, font->yMax, font->yMin);
1921 if(yMax + -yMin > height) {
1924 goto end; /* failed */
1926 font->yMax = GET_BE_WORD(&vTable[(i * 6) + 2]);
1927 font->yMin = GET_BE_WORD(&vTable[(i * 6) + 4]);
1928 TRACE("ppem %ld found; height=%ld yMax=%d yMin=%d\n", ppem, height, font->yMax, font->yMin);
1934 TRACE("ppem not found for height %ld\n", height);
1938 if(ppem < startsz || ppem > endsz)
1941 for(i = 0; i < recs; i++) {
1943 yPelHeight = GET_BE_WORD(&vTable[i * 6]);
1945 if(yPelHeight > ppem)
1948 if(yPelHeight == ppem) {
1949 font->yMax = GET_BE_WORD(&vTable[(i * 6) + 2]);
1950 font->yMin = GET_BE_WORD(&vTable[(i * 6) + 4]);
1951 TRACE("ppem %ld found; yMax=%d yMin=%d\n", ppem, font->yMax, font->yMin);
1957 HeapFree(GetProcessHeap(), 0, vTable);
1963 static BOOL fontcmp(GdiFont font, FONT_DESC *fd)
1965 if(font->font_desc.hash != fd->hash) return TRUE;
1966 if(memcmp(&font->font_desc.matrix, &fd->matrix, sizeof(fd->matrix))) return TRUE;
1967 if(memcmp(&font->font_desc.lf, &fd->lf, offsetof(LOGFONTW, lfFaceName))) return TRUE;
1968 return strcmpiW(font->font_desc.lf.lfFaceName, fd->lf.lfFaceName);
1971 static void calc_hash(FONT_DESC *pfd)
1973 DWORD hash = 0, *ptr, two_chars;
1977 for(i = 0, ptr = (DWORD*)&pfd->matrix; i < sizeof(FMAT2)/sizeof(DWORD); i++, ptr++)
1979 for(i = 0, ptr = (DWORD*)&pfd->lf; i < 7; i++, ptr++)
1981 for(i = 0, ptr = (DWORD*)&pfd->lf.lfFaceName; i < LF_FACESIZE/2; i++, ptr++) {
1983 pwc = (WCHAR *)&two_chars;
1985 *pwc = toupperW(*pwc);
1987 *pwc = toupperW(*pwc);
1995 static GdiFont find_in_cache(HFONT hfont, LOGFONTW *plf, XFORM *pxf, BOOL can_use_bitmap)
2000 struct list *font_elem_ptr, *hfontlist_elem_ptr;
2002 memcpy(&fd.lf, plf, sizeof(LOGFONTW));
2003 memcpy(&fd.matrix, pxf, sizeof(FMAT2));
2006 /* try the in-use list */
2007 LIST_FOR_EACH(font_elem_ptr, &gdi_font_list) {
2008 ret = LIST_ENTRY(font_elem_ptr, struct tagGdiFont, entry);
2009 if(!fontcmp(ret, &fd)) {
2010 if(!can_use_bitmap && !FT_IS_SCALABLE(ret->ft_face)) continue;
2011 LIST_FOR_EACH(hfontlist_elem_ptr, &ret->hfontlist) {
2012 hflist = LIST_ENTRY(hfontlist_elem_ptr, struct tagHFONTLIST, entry);
2013 if(hflist->hfont == hfont)
2016 hflist = HeapAlloc(GetProcessHeap(), 0, sizeof(*hflist));
2017 hflist->hfont = hfont;
2018 list_add_head(&ret->hfontlist, &hflist->entry);
2023 /* then the unused list */
2024 font_elem_ptr = list_head(&unused_gdi_font_list);
2025 while(font_elem_ptr) {
2026 ret = LIST_ENTRY(font_elem_ptr, struct tagGdiFont, entry);
2027 font_elem_ptr = list_next(&unused_gdi_font_list, font_elem_ptr);
2028 if(!fontcmp(ret, &fd)) {
2029 if(!can_use_bitmap && !FT_IS_SCALABLE(ret->ft_face)) continue;
2030 assert(list_empty(&ret->hfontlist));
2031 TRACE("Found %p in unused list\n", ret);
2032 list_remove(&ret->entry);
2033 list_add_head(&gdi_font_list, &ret->entry);
2034 hflist = HeapAlloc(GetProcessHeap(), 0, sizeof(*hflist));
2035 hflist->hfont = hfont;
2036 list_add_head(&ret->hfontlist, &hflist->entry);
2044 /*************************************************************
2045 * create_child_font_list
2047 static BOOL create_child_font_list(GdiFont font)
2050 SYSTEM_LINKS *font_link;
2051 CHILD_FONT *font_link_entry, *new_child;
2053 LIST_FOR_EACH_ENTRY(font_link, &system_links, SYSTEM_LINKS, entry)
2055 if(!strcmpW(font_link->font_name, font->name))
2057 TRACE("found entry in system list\n");
2058 LIST_FOR_EACH_ENTRY(font_link_entry, &font_link->links, CHILD_FONT, entry)
2060 new_child = HeapAlloc(GetProcessHeap(), 0, sizeof(*new_child));
2061 new_child->file_name = strdupA(font_link_entry->file_name);
2062 new_child->index = font_link_entry->index;
2063 new_child->font = NULL;
2064 list_add_tail(&font->child_fonts, &new_child->entry);
2065 TRACE("font %s %d\n", debugstr_a(new_child->file_name), new_child->index);
2075 /*************************************************************
2076 * WineEngCreateFontInstance
2079 GdiFont WineEngCreateFontInstance(DC *dc, HFONT hfont)
2083 Family *family, *last_resort_family;
2084 struct list *family_elem_ptr, *face_elem_ptr;
2085 INT height, width = 0;
2086 signed int diff = 0, newdiff;
2087 BOOL bd, it, can_use_bitmap;
2092 LIST_FOR_EACH_ENTRY(ret, &child_font_list, struct tagGdiFont, entry)
2094 struct list *first_hfont = list_head(&ret->hfontlist);
2095 hflist = LIST_ENTRY(first_hfont, HFONTLIST, entry);
2096 if(hflist->hfont == hfont)
2100 if (!GetObjectW( hfont, sizeof(lf), &lf )) return NULL;
2101 can_use_bitmap = GetDeviceCaps(dc->hSelf, TEXTCAPS) & TC_RA_ABLE;
2103 TRACE("%s, h=%ld, it=%d, weight=%ld, PandF=%02x, charset=%d orient %ld escapement %ld\n",
2104 debugstr_w(lf.lfFaceName), lf.lfHeight, lf.lfItalic,
2105 lf.lfWeight, lf.lfPitchAndFamily, lf.lfCharSet, lf.lfOrientation,
2108 /* check the cache first */
2109 if((ret = find_in_cache(hfont, &lf, &dc->xformWorld2Vport, can_use_bitmap)) != NULL) {
2110 TRACE("returning cached gdiFont(%p) for hFont %p\n", ret, hfont);
2114 TRACE("not in cache\n");
2115 if(list_empty(&font_list)) /* No fonts installed */
2117 TRACE("No fonts installed\n");
2120 if(!have_installed_roman_font)
2122 TRACE("No roman font installed\n");
2128 memcpy(&ret->font_desc.matrix, &dc->xformWorld2Vport, sizeof(FMAT2));
2129 memcpy(&ret->font_desc.lf, &lf, sizeof(LOGFONTW));
2130 calc_hash(&ret->font_desc);
2131 hflist = HeapAlloc(GetProcessHeap(), 0, sizeof(*hflist));
2132 hflist->hfont = hfont;
2133 list_add_head(&ret->hfontlist, &hflist->entry);
2136 /* If lfFaceName is "Symbol" then Windows fixes up lfCharSet to
2137 SYMBOL_CHARSET so that Symbol gets picked irrespective of the
2138 original value lfCharSet. Note this is a special case for
2139 Symbol and doesn't happen at least for "Wingdings*" */
2141 if(!strcmpiW(lf.lfFaceName, SymbolW))
2142 lf.lfCharSet = SYMBOL_CHARSET;
2144 if(!TranslateCharsetInfo((DWORD*)(INT)lf.lfCharSet, &csi, TCI_SRCCHARSET)) {
2145 switch(lf.lfCharSet) {
2146 case DEFAULT_CHARSET:
2147 csi.fs.fsCsb[0] = 0;
2150 FIXME("Untranslated charset %d\n", lf.lfCharSet);
2151 csi.fs.fsCsb[0] = 0;
2157 if(lf.lfFaceName[0] != '\0') {
2159 for(psub = substlist; psub; psub = psub->next)
2160 if(!strcmpiW(lf.lfFaceName, psub->from.name) &&
2161 (psub->from.charset == -1 ||
2162 psub->from.charset == lf.lfCharSet))
2165 TRACE("substituting %s -> %s\n", debugstr_w(lf.lfFaceName),
2166 debugstr_w(psub->to.name));
2167 strcpyW(lf.lfFaceName, psub->to.name);
2170 /* We want a match on name and charset or just name if
2171 charset was DEFAULT_CHARSET. If the latter then
2172 we fixup the returned charset later in get_nearest_charset
2173 where we'll either use the charset of the current ansi codepage
2174 or if that's unavailable the first charset that the font supports.
2176 LIST_FOR_EACH(family_elem_ptr, &font_list) {
2177 family = LIST_ENTRY(family_elem_ptr, Family, entry);
2178 if(!strcmpiW(family->FamilyName, lf.lfFaceName)) {
2179 LIST_FOR_EACH(face_elem_ptr, &family->faces) {
2180 face = LIST_ENTRY(face_elem_ptr, Face, entry);
2181 if((csi.fs.fsCsb[0] & (face->fs.fsCsb[0] | face->fs_links.fsCsb[0])) || !csi.fs.fsCsb[0])
2182 if(face->scalable || can_use_bitmap)
2189 /* If requested charset was DEFAULT_CHARSET then try using charset
2190 corresponding to the current ansi codepage */
2191 if(!csi.fs.fsCsb[0]) {
2193 if(!TranslateCharsetInfo((DWORD*)acp, &csi, TCI_SRCCODEPAGE)) {
2194 FIXME("TCI failed on codepage %d\n", acp);
2195 csi.fs.fsCsb[0] = 0;
2197 lf.lfCharSet = csi.ciCharset;
2200 /* Face families are in the top 4 bits of lfPitchAndFamily,
2201 so mask with 0xF0 before testing */
2203 if((lf.lfPitchAndFamily & FIXED_PITCH) ||
2204 (lf.lfPitchAndFamily & 0xF0) == FF_MODERN)
2205 strcpyW(lf.lfFaceName, defFixed);
2206 else if((lf.lfPitchAndFamily & 0xF0) == FF_ROMAN)
2207 strcpyW(lf.lfFaceName, defSerif);
2208 else if((lf.lfPitchAndFamily & 0xF0) == FF_SWISS)
2209 strcpyW(lf.lfFaceName, defSans);
2211 strcpyW(lf.lfFaceName, defSans);
2212 LIST_FOR_EACH(family_elem_ptr, &font_list) {
2213 family = LIST_ENTRY(family_elem_ptr, Family, entry);
2214 if(!strcmpiW(family->FamilyName, lf.lfFaceName)) {
2215 LIST_FOR_EACH(face_elem_ptr, &family->faces) {
2216 face = LIST_ENTRY(face_elem_ptr, Face, entry);
2217 if(csi.fs.fsCsb[0] & (face->fs.fsCsb[0] | face->fs_links.fsCsb[0]))
2218 if(face->scalable || can_use_bitmap)
2224 last_resort_family = NULL;
2225 LIST_FOR_EACH(family_elem_ptr, &font_list) {
2226 family = LIST_ENTRY(family_elem_ptr, Family, entry);
2227 LIST_FOR_EACH(face_elem_ptr, &family->faces) {
2228 face = LIST_ENTRY(face_elem_ptr, Face, entry);
2229 if(csi.fs.fsCsb[0] & (face->fs.fsCsb[0] | face->fs_links.fsCsb[0])) {
2232 if(can_use_bitmap && !last_resort_family)
2233 last_resort_family = family;
2238 if(last_resort_family) {
2239 family = last_resort_family;
2240 csi.fs.fsCsb[0] = 0;
2244 LIST_FOR_EACH(family_elem_ptr, &font_list) {
2245 family = LIST_ENTRY(family_elem_ptr, Family, entry);
2246 LIST_FOR_EACH(face_elem_ptr, &family->faces) {
2247 face = LIST_ENTRY(face_elem_ptr, Face, entry);
2248 if(face->scalable) {
2249 csi.fs.fsCsb[0] = 0;
2250 FIXME("just using first face for now\n");
2253 if(can_use_bitmap && !last_resort_family)
2254 last_resort_family = family;
2257 if(!last_resort_family) {
2258 FIXME("can't find a single appropriate font - bailing\n");
2263 WARN("could only find a bitmap font - this will probably look awful!\n");
2264 family = last_resort_family;
2265 csi.fs.fsCsb[0] = 0;
2268 it = lf.lfItalic ? 1 : 0;
2269 bd = lf.lfWeight > 550 ? 1 : 0;
2271 height = GDI_ROUND( (FLOAT)lf.lfHeight * dc->xformWorld2Vport.eM22 );
2272 height = lf.lfHeight < 0 ? -abs(height) : abs(height);
2275 LIST_FOR_EACH(face_elem_ptr, &family->faces) {
2276 face = LIST_ENTRY(face_elem_ptr, Face, entry);
2277 if(!(face->Italic ^ it) && !(face->Bold ^ bd) &&
2278 ((csi.fs.fsCsb[0] & (face->fs.fsCsb[0] | face->fs_links.fsCsb[0])) || !csi.fs.fsCsb[0])) {
2282 newdiff = height - (signed int)(face->size.y_ppem >> 6);
2284 newdiff = -height - ((signed int)(face->size.y_ppem >> 6) - face->size.internal_leading);
2285 if(!best || (diff > 0 && newdiff < diff && newdiff >= 0) ||
2286 (diff < 0 && newdiff > diff)) {
2287 TRACE("%ld is better for %d diff was %d\n", face->size.y_ppem >> 6, height, diff);
2300 LIST_FOR_EACH(face_elem_ptr, &family->faces) {
2301 face = LIST_ENTRY(face_elem_ptr, Face, entry);
2302 if((csi.fs.fsCsb[0] & (face->fs.fsCsb[0] | face->fs_links.fsCsb[0])) || !csi.fs.fsCsb[0]) {
2306 newdiff = height - (signed int)(face->size.y_ppem >> 6);
2308 newdiff = -height - ((signed int)(face->size.y_ppem >> 6) - face->size.internal_leading);
2309 if(!best || (diff > 0 && newdiff < diff && newdiff >= 0) ||
2310 (diff < 0 && newdiff > diff)) {
2311 TRACE("%ld is better for %d diff was %d\n", face->size.y_ppem >> 6, height, diff);
2322 if(it && !face->Italic) ret->fake_italic = TRUE;
2323 if(bd && !face->Bold) ret->fake_bold = TRUE;
2326 memcpy(&ret->fs, &face->fs, sizeof(FONTSIGNATURE));
2328 if(csi.fs.fsCsb[0]) {
2329 ret->charset = lf.lfCharSet;
2330 ret->codepage = csi.ciACP;
2333 ret->charset = get_nearest_charset(face, &ret->codepage);
2335 TRACE("Chosen: %s %s (%s:%ld)\n", debugstr_w(family->FamilyName),
2336 debugstr_w(face->StyleName), face->file, face->face_index);
2338 if(!face->scalable) {
2339 width = face->size.x_ppem >> 6;
2340 height = face->size.y_ppem >> 6;
2342 ret->ft_face = OpenFontFile(ret, face->file, face->face_index, width, height);
2350 if (ret->charset == SYMBOL_CHARSET &&
2351 !pFT_Select_Charmap(ret->ft_face, FT_ENCODING_MS_SYMBOL)) {
2354 else if (!pFT_Select_Charmap(ret->ft_face, FT_ENCODING_UNICODE)) {
2358 pFT_Select_Charmap(ret->ft_face, FT_ENCODING_APPLE_ROMAN);
2361 ret->orientation = FT_IS_SCALABLE(ret->ft_face) ? lf.lfOrientation : 0;
2362 ret->name = strdupW(family->FamilyName);
2363 ret->underline = lf.lfUnderline ? 0xff : 0;
2364 ret->strikeout = lf.lfStrikeOut ? 0xff : 0;
2365 create_child_font_list(ret);
2367 TRACE("caching: gdiFont=%p hfont=%p\n", ret, hfont);
2369 ret->aveWidth = FT_IS_SCALABLE(ret->ft_face) ? lf.lfWidth : 0;
2370 list_add_head(&gdi_font_list, &ret->entry);
2374 static void dump_gdi_font_list(void)
2377 struct list *elem_ptr;
2379 TRACE("---------- gdiFont Cache ----------\n");
2380 LIST_FOR_EACH(elem_ptr, &gdi_font_list) {
2381 gdiFont = LIST_ENTRY(elem_ptr, struct tagGdiFont, entry);
2382 TRACE("gdiFont=%p %s %ld\n",
2383 gdiFont, debugstr_w(gdiFont->font_desc.lf.lfFaceName), gdiFont->font_desc.lf.lfHeight);
2386 TRACE("---------- Unused gdiFont Cache ----------\n");
2387 LIST_FOR_EACH(elem_ptr, &unused_gdi_font_list) {
2388 gdiFont = LIST_ENTRY(elem_ptr, struct tagGdiFont, entry);
2389 TRACE("gdiFont=%p %s %ld\n",
2390 gdiFont, debugstr_w(gdiFont->font_desc.lf.lfFaceName), gdiFont->font_desc.lf.lfHeight);
2394 /*************************************************************
2395 * WineEngDestroyFontInstance
2397 * free the gdiFont associated with this handle
2400 BOOL WineEngDestroyFontInstance(HFONT handle)
2405 struct list *font_elem_ptr, *hfontlist_elem_ptr;
2408 LIST_FOR_EACH_ENTRY(gdiFont, &child_font_list, struct tagGdiFont, entry)
2410 struct list *first_hfont = list_head(&gdiFont->hfontlist);
2411 hflist = LIST_ENTRY(first_hfont, HFONTLIST, entry);
2412 if(hflist->hfont == handle)
2414 TRACE("removing child font %p from child list\n", gdiFont);
2415 list_remove(&gdiFont->entry);
2420 TRACE("destroying hfont=%p\n", handle);
2422 dump_gdi_font_list();
2424 font_elem_ptr = list_head(&gdi_font_list);
2425 while(font_elem_ptr) {
2426 gdiFont = LIST_ENTRY(font_elem_ptr, struct tagGdiFont, entry);
2427 font_elem_ptr = list_next(&gdi_font_list, font_elem_ptr);
2429 hfontlist_elem_ptr = list_head(&gdiFont->hfontlist);
2430 while(hfontlist_elem_ptr) {
2431 hflist = LIST_ENTRY(hfontlist_elem_ptr, struct tagHFONTLIST, entry);
2432 hfontlist_elem_ptr = list_next(&gdiFont->hfontlist, hfontlist_elem_ptr);
2433 if(hflist->hfont == handle) {
2434 list_remove(&hflist->entry);
2435 HeapFree(GetProcessHeap(), 0, hflist);
2439 if(list_empty(&gdiFont->hfontlist)) {
2440 TRACE("Moving to Unused list\n");
2441 list_remove(&gdiFont->entry);
2442 list_add_head(&unused_gdi_font_list, &gdiFont->entry);
2447 font_elem_ptr = list_head(&unused_gdi_font_list);
2448 while(font_elem_ptr && i++ < UNUSED_CACHE_SIZE)
2449 font_elem_ptr = list_next(&unused_gdi_font_list, font_elem_ptr);
2450 while(font_elem_ptr) {
2451 gdiFont = LIST_ENTRY(font_elem_ptr, struct tagGdiFont, entry);
2452 font_elem_ptr = list_next(&unused_gdi_font_list, font_elem_ptr);
2453 TRACE("freeing %p\n", gdiFont);
2454 list_remove(&gdiFont->entry);
2460 static void GetEnumStructs(Face *face, LPENUMLOGFONTEXW pelf,
2461 NEWTEXTMETRICEXW *pntm, LPDWORD ptype)
2463 OUTLINETEXTMETRICW *potm = NULL;
2465 TEXTMETRICW tm, *ptm;
2466 GdiFont font = alloc_font();
2469 if(face->scalable) {
2473 height = face->size.y_ppem >> 6;
2474 width = face->size.x_ppem >> 6;
2477 if (!(font->ft_face = OpenFontFile(font, face->file, face->face_index, width, height)))
2483 font->name = strdupW(face->family->FamilyName);
2485 memset(&pelf->elfLogFont, 0, sizeof(LOGFONTW));
2487 size = WineEngGetOutlineTextMetrics(font, 0, NULL);
2489 potm = HeapAlloc(GetProcessHeap(), 0, size);
2490 WineEngGetOutlineTextMetrics(font, size, potm);
2491 ptm = (TEXTMETRICW*)&potm->otmTextMetrics;
2493 WineEngGetTextMetrics(font, &tm);
2497 pntm->ntmTm.tmHeight = pelf->elfLogFont.lfHeight = ptm->tmHeight;
2498 pntm->ntmTm.tmAscent = ptm->tmAscent;
2499 pntm->ntmTm.tmDescent = ptm->tmDescent;
2500 pntm->ntmTm.tmInternalLeading = ptm->tmInternalLeading;
2501 pntm->ntmTm.tmExternalLeading = ptm->tmExternalLeading;
2502 pntm->ntmTm.tmAveCharWidth = pelf->elfLogFont.lfWidth = ptm->tmAveCharWidth;
2503 pntm->ntmTm.tmMaxCharWidth = ptm->tmMaxCharWidth;
2504 pntm->ntmTm.tmWeight = pelf->elfLogFont.lfWeight = ptm->tmWeight;
2505 pntm->ntmTm.tmOverhang = ptm->tmOverhang;
2506 pntm->ntmTm.tmDigitizedAspectX = ptm->tmDigitizedAspectX;
2507 pntm->ntmTm.tmDigitizedAspectY = ptm->tmDigitizedAspectY;
2508 pntm->ntmTm.tmFirstChar = ptm->tmFirstChar;
2509 pntm->ntmTm.tmLastChar = ptm->tmLastChar;
2510 pntm->ntmTm.tmDefaultChar = ptm->tmDefaultChar;
2511 pntm->ntmTm.tmBreakChar = ptm->tmBreakChar;
2512 pntm->ntmTm.tmItalic = pelf->elfLogFont.lfItalic = ptm->tmItalic;
2513 pntm->ntmTm.tmUnderlined = pelf->elfLogFont.lfUnderline = ptm->tmUnderlined;
2514 pntm->ntmTm.tmStruckOut = pelf->elfLogFont.lfStrikeOut = ptm->tmStruckOut;
2515 pntm->ntmTm.tmPitchAndFamily = ptm->tmPitchAndFamily;
2516 pelf->elfLogFont.lfPitchAndFamily = (ptm->tmPitchAndFamily & 0xf1) + 1;
2517 pntm->ntmTm.tmCharSet = pelf->elfLogFont.lfCharSet = ptm->tmCharSet;
2518 pelf->elfLogFont.lfOutPrecision = OUT_STROKE_PRECIS;
2519 pelf->elfLogFont.lfClipPrecision = CLIP_STROKE_PRECIS;
2520 pelf->elfLogFont.lfQuality = DRAFT_QUALITY;
2522 *ptype = ptm->tmPitchAndFamily & TMPF_TRUETYPE ? TRUETYPE_FONTTYPE : 0;
2523 if(!(ptm->tmPitchAndFamily & TMPF_VECTOR))
2524 *ptype |= RASTER_FONTTYPE;
2526 pntm->ntmTm.ntmFlags = ptm->tmItalic ? NTM_ITALIC : 0;
2527 if(ptm->tmWeight > 550) pntm->ntmTm.ntmFlags |= NTM_BOLD;
2528 if(pntm->ntmTm.ntmFlags == 0) pntm->ntmTm.ntmFlags = NTM_REGULAR;
2530 pntm->ntmTm.ntmCellHeight = pntm->ntmTm.tmHeight;
2531 pntm->ntmTm.ntmAvgWidth = pntm->ntmTm.tmAveCharWidth;
2532 memset(&pntm->ntmFontSig, 0, sizeof(FONTSIGNATURE));
2535 pntm->ntmTm.ntmSizeEM = potm->otmEMSquare;
2537 lstrcpynW(pelf->elfLogFont.lfFaceName,
2538 (WCHAR*)((char*)potm + (ptrdiff_t)potm->otmpFamilyName),
2540 lstrcpynW(pelf->elfFullName,
2541 (WCHAR*)((char*)potm + (ptrdiff_t)potm->otmpFaceName),
2543 lstrcpynW(pelf->elfStyle,
2544 (WCHAR*)((char*)potm + (ptrdiff_t)potm->otmpStyleName),
2547 HeapFree(GetProcessHeap(), 0, potm);
2549 pntm->ntmTm.ntmSizeEM = pntm->ntmTm.tmHeight - pntm->ntmTm.tmInternalLeading;
2551 lstrcpynW(pelf->elfLogFont.lfFaceName, face->family->FamilyName, LF_FACESIZE);
2552 lstrcpynW(pelf->elfFullName, face->family->FamilyName, LF_FACESIZE);
2553 pelf->elfStyle[0] = '\0';
2556 pelf->elfScript[0] = '\0'; /* This will get set in WineEngEnumFonts */
2561 /*************************************************************
2565 DWORD WineEngEnumFonts(LPLOGFONTW plf, FONTENUMPROCW proc, LPARAM lparam)
2569 struct list *family_elem_ptr, *face_elem_ptr;
2571 NEWTEXTMETRICEXW ntm;
2572 DWORD type, ret = 1;
2578 TRACE("facename = %s charset %d\n", debugstr_w(plf->lfFaceName), plf->lfCharSet);
2580 if(plf->lfFaceName[0]) {
2582 for(psub = substlist; psub; psub = psub->next)
2583 if(!strcmpiW(plf->lfFaceName, psub->from.name) &&
2584 (psub->from.charset == -1 ||
2585 psub->from.charset == plf->lfCharSet))
2588 TRACE("substituting %s -> %s\n", debugstr_w(plf->lfFaceName),
2589 debugstr_w(psub->to.name));
2590 memcpy(&lf, plf, sizeof(lf));
2591 strcpyW(lf.lfFaceName, psub->to.name);
2595 LIST_FOR_EACH(family_elem_ptr, &font_list) {
2596 family = LIST_ENTRY(family_elem_ptr, Family, entry);
2597 if(!strcmpiW(plf->lfFaceName, family->FamilyName)) {
2598 LIST_FOR_EACH(face_elem_ptr, &family->faces) {
2599 face = LIST_ENTRY(face_elem_ptr, Face, entry);
2600 GetEnumStructs(face, &elf, &ntm, &type);
2601 for(i = 0; i < 32; i++) {
2602 if(!face->scalable && face->fs.fsCsb[0] == 0) { /* OEM bitmap */
2603 elf.elfLogFont.lfCharSet = ntm.ntmTm.tmCharSet = OEM_CHARSET;
2604 strcpyW(elf.elfScript, OEM_DOSW);
2605 i = 32; /* break out of loop */
2606 } else if(!(face->fs.fsCsb[0] & (1L << i)))
2609 fs.fsCsb[0] = 1L << i;
2611 if(!TranslateCharsetInfo(fs.fsCsb, &csi,
2613 csi.ciCharset = DEFAULT_CHARSET;
2614 if(i == 31) csi.ciCharset = SYMBOL_CHARSET;
2615 if(csi.ciCharset != DEFAULT_CHARSET) {
2616 elf.elfLogFont.lfCharSet =
2617 ntm.ntmTm.tmCharSet = csi.ciCharset;
2619 strcpyW(elf.elfScript, ElfScriptsW[i]);
2621 FIXME("Unknown elfscript for bit %d\n", i);
2624 TRACE("enuming face %s full %s style %s charset %d type %ld script %s it %d weight %ld ntmflags %08lx\n",
2625 debugstr_w(elf.elfLogFont.lfFaceName),
2626 debugstr_w(elf.elfFullName), debugstr_w(elf.elfStyle),
2627 csi.ciCharset, type, debugstr_w(elf.elfScript),
2628 elf.elfLogFont.lfItalic, elf.elfLogFont.lfWeight,
2629 ntm.ntmTm.ntmFlags);
2630 ret = proc(&elf.elfLogFont, (TEXTMETRICW *)&ntm, type, lparam);
2637 LIST_FOR_EACH(family_elem_ptr, &font_list) {
2638 family = LIST_ENTRY(family_elem_ptr, Family, entry);
2639 face_elem_ptr = list_head(&family->faces);
2640 face = LIST_ENTRY(face_elem_ptr, Face, entry);
2641 GetEnumStructs(face, &elf, &ntm, &type);
2642 for(i = 0; i < 32; i++) {
2643 if(!face->scalable && face->fs.fsCsb[0] == 0) { /* OEM bitmap */
2644 elf.elfLogFont.lfCharSet = ntm.ntmTm.tmCharSet = OEM_CHARSET;
2645 strcpyW(elf.elfScript, OEM_DOSW);
2646 i = 32; /* break out of loop */
2647 } else if(!(face->fs.fsCsb[0] & (1L << i)))
2650 fs.fsCsb[0] = 1L << i;
2652 if(!TranslateCharsetInfo(fs.fsCsb, &csi,
2654 csi.ciCharset = DEFAULT_CHARSET;
2655 if(i == 31) csi.ciCharset = SYMBOL_CHARSET;
2656 if(csi.ciCharset != DEFAULT_CHARSET) {
2657 elf.elfLogFont.lfCharSet = ntm.ntmTm.tmCharSet =
2660 strcpyW(elf.elfScript, ElfScriptsW[i]);
2662 FIXME("Unknown elfscript for bit %d\n", i);
2665 TRACE("enuming face %s full %s style %s charset = %d type %ld script %s it %d weight %ld ntmflags %08lx\n",
2666 debugstr_w(elf.elfLogFont.lfFaceName),
2667 debugstr_w(elf.elfFullName), debugstr_w(elf.elfStyle),
2668 csi.ciCharset, type, debugstr_w(elf.elfScript),
2669 elf.elfLogFont.lfItalic, elf.elfLogFont.lfWeight,
2670 ntm.ntmTm.ntmFlags);
2671 ret = proc(&elf.elfLogFont, (TEXTMETRICW *)&ntm, type, lparam);
2680 static void FTVectorToPOINTFX(FT_Vector *vec, POINTFX *pt)
2682 pt->x.value = vec->x >> 6;
2683 pt->x.fract = (vec->x & 0x3f) << 10;
2684 pt->x.fract |= ((pt->x.fract >> 6) | (pt->x.fract >> 12));
2685 pt->y.value = vec->y >> 6;
2686 pt->y.fract = (vec->y & 0x3f) << 10;
2687 pt->y.fract |= ((pt->y.fract >> 6) | (pt->y.fract >> 12));
2691 /***************************************************
2692 * According to the MSDN documentation on WideCharToMultiByte,
2693 * certain codepages cannot set the default_used parameter.
2694 * This returns TRUE if the codepage can set that parameter, false else
2695 * so that calls to WideCharToMultiByte don't fail with ERROR_INVALID_PARAMETER
2697 static BOOL codepage_sets_default_used(UINT codepage)
2710 static FT_UInt get_glyph_index(GdiFont font, UINT glyph)
2712 if(font->ft_face->charmap->encoding == FT_ENCODING_NONE) {
2713 WCHAR wc = (WCHAR)glyph;
2715 BOOL *default_used_pointer;
2718 default_used_pointer = NULL;
2719 default_used = FALSE;
2720 if (codepage_sets_default_used(font->codepage))
2721 default_used_pointer = &default_used;
2722 if(!WideCharToMultiByte(font->codepage, 0, &wc, 1, &buf, sizeof(buf), NULL, default_used_pointer) || default_used)
2725 ret = pFT_Get_Char_Index(font->ft_face, (unsigned char)buf);
2726 TRACE("%04x (%02x) -> ret %d def_used %d\n", glyph, buf, ret, default_used);
2730 if(font->charset == SYMBOL_CHARSET && glyph < 0x100)
2731 glyph = glyph + 0xf000;
2732 return pFT_Get_Char_Index(font->ft_face, glyph);
2735 /*************************************************************
2736 * WineEngGetGlyphIndices
2738 * FIXME: add support for GGI_MARK_NONEXISTING_GLYPHS
2740 DWORD WineEngGetGlyphIndices(GdiFont font, LPCWSTR lpstr, INT count,
2741 LPWORD pgi, DWORD flags)
2745 for(i = 0; i < count; i++)
2746 pgi[i] = get_glyph_index(font, lpstr[i]);
2751 /*************************************************************
2752 * WineEngGetGlyphOutline
2754 * Behaves in exactly the same way as the win32 api GetGlyphOutline
2755 * except that the first parameter is the HWINEENGFONT of the font in
2756 * question rather than an HDC.
2759 DWORD WineEngGetGlyphOutline(GdiFont font, UINT glyph, UINT format,
2760 LPGLYPHMETRICS lpgm, DWORD buflen, LPVOID buf,
2763 static const FT_Matrix identityMat = {(1 << 16), 0, 0, (1 << 16)};
2764 FT_Face ft_face = font->ft_face;
2765 FT_UInt glyph_index;
2766 DWORD width, height, pitch, needed = 0;
2767 FT_Bitmap ft_bitmap;
2769 INT left, right, top = 0, bottom = 0;
2771 FT_Int load_flags = FT_LOAD_DEFAULT | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
2772 float widthRatio = 1.0;
2773 FT_Matrix transMat = identityMat;
2774 BOOL needsTransform = FALSE;
2777 TRACE("%p, %04x, %08x, %p, %08lx, %p, %p\n", font, glyph, format, lpgm,
2778 buflen, buf, lpmat);
2780 if(format & GGO_GLYPH_INDEX) {
2781 glyph_index = glyph;
2782 format &= ~GGO_GLYPH_INDEX;
2784 glyph_index = get_glyph_index(font, glyph);
2786 if(glyph_index >= font->gmsize) {
2787 font->gmsize = (glyph_index / INIT_GM_SIZE + 1) * INIT_GM_SIZE;
2788 font->gm = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, font->gm,
2789 font->gmsize * sizeof(*font->gm));
2791 if(format == GGO_METRICS && font->gm[glyph_index].init) {
2792 memcpy(lpgm, &font->gm[glyph_index].gm, sizeof(*lpgm));
2793 return 1; /* FIXME */
2797 if(font->orientation || (format != GGO_METRICS && format != GGO_BITMAP) || font->aveWidth || lpmat)
2798 load_flags |= FT_LOAD_NO_BITMAP;
2800 err = pFT_Load_Glyph(ft_face, glyph_index, load_flags);
2803 FIXME("FT_Load_Glyph on index %x returns %d\n", glyph_index, err);
2807 /* Scaling factor */
2808 if (font->aveWidth && font->potm) {
2809 widthRatio = (float)font->aveWidth * font->font_desc.matrix.eM11 / (float) font->potm->otmTextMetrics.tmAveCharWidth;
2812 left = (INT)(ft_face->glyph->metrics.horiBearingX * widthRatio) & -64;
2813 right = (INT)((ft_face->glyph->metrics.horiBearingX + ft_face->glyph->metrics.width) * widthRatio + 63) & -64;
2815 font->gm[glyph_index].adv = (INT)((ft_face->glyph->metrics.horiAdvance * widthRatio) + 63) >> 6;
2816 font->gm[glyph_index].lsb = left >> 6;
2817 font->gm[glyph_index].bbx = (right - left) >> 6;
2819 /* Scaling transform */
2820 if(font->aveWidth) {
2822 scaleMat.xx = FT_FixedFromFloat(widthRatio);
2825 scaleMat.yy = (1 << 16);
2827 pFT_Matrix_Multiply(&scaleMat, &transMat);
2828 needsTransform = TRUE;
2831 /* Slant transform */
2832 if (font->fake_italic) {
2835 slantMat.xx = (1 << 16);
2836 slantMat.xy = ((1 << 16) >> 2);
2838 slantMat.yy = (1 << 16);
2839 pFT_Matrix_Multiply(&slantMat, &transMat);
2840 needsTransform = TRUE;
2843 /* Rotation transform */
2844 if(font->orientation) {
2845 FT_Matrix rotationMat;
2847 angle = FT_FixedFromFloat((float)font->orientation / 10.0);
2848 pFT_Vector_Unit(&vecAngle, angle);
2849 rotationMat.xx = vecAngle.x;
2850 rotationMat.xy = -vecAngle.y;
2851 rotationMat.yx = -rotationMat.xy;
2852 rotationMat.yy = rotationMat.xx;
2854 pFT_Matrix_Multiply(&rotationMat, &transMat);
2855 needsTransform = TRUE;
2858 /* Extra transformation specified by caller */
2861 extraMat.xx = FT_FixedFromFIXED(lpmat->eM11);
2862 extraMat.xy = FT_FixedFromFIXED(lpmat->eM21);
2863 extraMat.yx = FT_FixedFromFIXED(lpmat->eM12);
2864 extraMat.yy = FT_FixedFromFIXED(lpmat->eM22);
2865 pFT_Matrix_Multiply(&extraMat, &transMat);
2866 needsTransform = TRUE;
2869 if(!needsTransform) {
2870 top = (ft_face->glyph->metrics.horiBearingY + 63) & -64;
2871 bottom = (ft_face->glyph->metrics.horiBearingY -
2872 ft_face->glyph->metrics.height) & -64;
2873 lpgm->gmCellIncX = font->gm[glyph_index].adv;
2874 lpgm->gmCellIncY = 0;
2878 for(xc = 0; xc < 2; xc++) {
2879 for(yc = 0; yc < 2; yc++) {
2880 vec.x = (ft_face->glyph->metrics.horiBearingX +
2881 xc * ft_face->glyph->metrics.width);
2882 vec.y = ft_face->glyph->metrics.horiBearingY -
2883 yc * ft_face->glyph->metrics.height;
2884 TRACE("Vec %ld,%ld\n", vec.x, vec.y);
2885 pFT_Vector_Transform(&vec, &transMat);
2886 if(xc == 0 && yc == 0) {
2887 left = right = vec.x;
2888 top = bottom = vec.y;
2890 if(vec.x < left) left = vec.x;
2891 else if(vec.x > right) right = vec.x;
2892 if(vec.y < bottom) bottom = vec.y;
2893 else if(vec.y > top) top = vec.y;
2898 right = (right + 63) & -64;
2899 bottom = bottom & -64;
2900 top = (top + 63) & -64;
2902 TRACE("transformed box: (%d,%d - %d,%d)\n", left, top, right, bottom);
2903 vec.x = ft_face->glyph->metrics.horiAdvance;
2905 pFT_Vector_Transform(&vec, &transMat);
2906 lpgm->gmCellIncX = (vec.x+63) >> 6;
2907 lpgm->gmCellIncY = -((vec.y+63) >> 6);
2909 lpgm->gmBlackBoxX = (right - left) >> 6;
2910 lpgm->gmBlackBoxY = (top - bottom) >> 6;
2911 lpgm->gmptGlyphOrigin.x = left >> 6;
2912 lpgm->gmptGlyphOrigin.y = top >> 6;
2914 memcpy(&font->gm[glyph_index].gm, lpgm, sizeof(*lpgm));
2915 font->gm[glyph_index].init = TRUE;
2917 if(format == GGO_METRICS)
2918 return 1; /* FIXME */
2920 if(ft_face->glyph->format != ft_glyph_format_outline && format != GGO_BITMAP) {
2921 TRACE("loaded a bitmap\n");
2927 width = lpgm->gmBlackBoxX;
2928 height = lpgm->gmBlackBoxY;
2929 pitch = ((width + 31) >> 5) << 2;
2930 needed = pitch * height;
2932 if(!buf || !buflen) break;
2934 switch(ft_face->glyph->format) {
2935 case ft_glyph_format_bitmap:
2937 BYTE *src = ft_face->glyph->bitmap.buffer, *dst = buf;
2938 INT w = (ft_face->glyph->bitmap.width + 7) >> 3;
2939 INT h = ft_face->glyph->bitmap.rows;
2941 memcpy(dst, src, w);
2942 src += ft_face->glyph->bitmap.pitch;
2948 case ft_glyph_format_outline:
2949 ft_bitmap.width = width;
2950 ft_bitmap.rows = height;
2951 ft_bitmap.pitch = pitch;
2952 ft_bitmap.pixel_mode = ft_pixel_mode_mono;
2953 ft_bitmap.buffer = buf;
2955 if(needsTransform) {
2956 pFT_Outline_Transform(&ft_face->glyph->outline, &transMat);
2959 pFT_Outline_Translate(&ft_face->glyph->outline, -left, -bottom );
2961 /* Note: FreeType will only set 'black' bits for us. */
2962 memset(buf, 0, needed);
2963 pFT_Outline_Get_Bitmap(library, &ft_face->glyph->outline, &ft_bitmap);
2967 FIXME("loaded glyph format %x\n", ft_face->glyph->format);
2972 case GGO_GRAY2_BITMAP:
2973 case GGO_GRAY4_BITMAP:
2974 case GGO_GRAY8_BITMAP:
2975 case WINE_GGO_GRAY16_BITMAP:
2977 unsigned int mult, row, col;
2980 width = lpgm->gmBlackBoxX;
2981 height = lpgm->gmBlackBoxY;
2982 pitch = (width + 3) / 4 * 4;
2983 needed = pitch * height;
2985 if(!buf || !buflen) break;
2986 ft_bitmap.width = width;
2987 ft_bitmap.rows = height;
2988 ft_bitmap.pitch = pitch;
2989 ft_bitmap.pixel_mode = ft_pixel_mode_grays;
2990 ft_bitmap.buffer = buf;
2992 if(needsTransform) {
2993 pFT_Outline_Transform(&ft_face->glyph->outline, &transMat);
2996 pFT_Outline_Translate(&ft_face->glyph->outline, -left, -bottom );
2998 memset(ft_bitmap.buffer, 0, buflen);
3000 pFT_Outline_Get_Bitmap(library, &ft_face->glyph->outline, &ft_bitmap);
3002 if(format == GGO_GRAY2_BITMAP)
3004 else if(format == GGO_GRAY4_BITMAP)
3006 else if(format == GGO_GRAY8_BITMAP)
3008 else if(format == WINE_GGO_GRAY16_BITMAP)
3016 for(row = 0; row < height; row++) {
3018 for(col = 0; col < width; col++, ptr++) {
3019 *ptr = (((int)*ptr) * mult + 128) / 256;
3028 int contour, point = 0, first_pt;
3029 FT_Outline *outline = &ft_face->glyph->outline;
3030 TTPOLYGONHEADER *pph;
3032 DWORD pph_start, cpfx, type;
3034 if(buflen == 0) buf = NULL;
3036 if (needsTransform && buf) {
3037 pFT_Outline_Transform(outline, &transMat);
3040 for(contour = 0; contour < outline->n_contours; contour++) {
3042 pph = (TTPOLYGONHEADER *)((char *)buf + needed);
3045 pph->dwType = TT_POLYGON_TYPE;
3046 FTVectorToPOINTFX(&outline->points[point], &pph->pfxStart);
3048 needed += sizeof(*pph);
3050 while(point <= outline->contours[contour]) {
3051 ppc = (TTPOLYCURVE *)((char *)buf + needed);
3052 type = (outline->tags[point] & FT_Curve_Tag_On) ?
3053 TT_PRIM_LINE : TT_PRIM_QSPLINE;
3057 FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
3060 } while(point <= outline->contours[contour] &&
3061 (outline->tags[point] & FT_Curve_Tag_On) ==
3062 (outline->tags[point-1] & FT_Curve_Tag_On));
3063 /* At the end of a contour Windows adds the start point, but
3065 if(point > outline->contours[contour] &&
3066 !(outline->tags[point-1] & FT_Curve_Tag_On)) {
3068 FTVectorToPOINTFX(&outline->points[first_pt], &ppc->apfx[cpfx]);
3070 } else if(point <= outline->contours[contour] &&
3071 outline->tags[point] & FT_Curve_Tag_On) {
3072 /* add closing pt for bezier */
3074 FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
3082 needed += sizeof(*ppc) + (cpfx - 1) * sizeof(POINTFX);
3085 pph->cb = needed - pph_start;
3091 /* Convert the quadratic Beziers to cubic Beziers.
3092 The parametric eqn for a cubic Bezier is, from PLRM:
3093 r(t) = at^3 + bt^2 + ct + r0
3094 with the control points:
3099 A quadratic Beizer has the form:
3100 p(t) = (1-t)^2 p0 + 2(1-t)t p1 + t^2 p2
3102 So equating powers of t leads to:
3103 r1 = 2/3 p1 + 1/3 p0
3104 r2 = 2/3 p1 + 1/3 p2
3105 and of course r0 = p0, r3 = p2
3108 int contour, point = 0, first_pt;
3109 FT_Outline *outline = &ft_face->glyph->outline;
3110 TTPOLYGONHEADER *pph;
3112 DWORD pph_start, cpfx, type;
3113 FT_Vector cubic_control[4];
3114 if(buflen == 0) buf = NULL;
3116 if (needsTransform && buf) {
3117 pFT_Outline_Transform(outline, &transMat);
3120 for(contour = 0; contour < outline->n_contours; contour++) {
3122 pph = (TTPOLYGONHEADER *)((char *)buf + needed);
3125 pph->dwType = TT_POLYGON_TYPE;
3126 FTVectorToPOINTFX(&outline->points[point], &pph->pfxStart);
3128 needed += sizeof(*pph);
3130 while(point <= outline->contours[contour]) {
3131 ppc = (TTPOLYCURVE *)((char *)buf + needed);
3132 type = (outline->tags[point] & FT_Curve_Tag_On) ?
3133 TT_PRIM_LINE : TT_PRIM_CSPLINE;
3136 if(type == TT_PRIM_LINE) {
3138 FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
3142 /* Unlike QSPLINEs, CSPLINEs always have their endpoint
3145 /* FIXME: Possible optimization in endpoint calculation
3146 if there are two consecutive curves */
3147 cubic_control[0] = outline->points[point-1];
3148 if(!(outline->tags[point-1] & FT_Curve_Tag_On)) {
3149 cubic_control[0].x += outline->points[point].x + 1;
3150 cubic_control[0].y += outline->points[point].y + 1;
3151 cubic_control[0].x >>= 1;
3152 cubic_control[0].y >>= 1;
3154 if(point+1 > outline->contours[contour])
3155 cubic_control[3] = outline->points[first_pt];
3157 cubic_control[3] = outline->points[point+1];
3158 if(!(outline->tags[point+1] & FT_Curve_Tag_On)) {
3159 cubic_control[3].x += outline->points[point].x + 1;
3160 cubic_control[3].y += outline->points[point].y + 1;
3161 cubic_control[3].x >>= 1;
3162 cubic_control[3].y >>= 1;
3165 /* r1 = 1/3 p0 + 2/3 p1
3166 r2 = 1/3 p2 + 2/3 p1 */
3167 cubic_control[1].x = (2 * outline->points[point].x + 1) / 3;
3168 cubic_control[1].y = (2 * outline->points[point].y + 1) / 3;
3169 cubic_control[2] = cubic_control[1];
3170 cubic_control[1].x += (cubic_control[0].x + 1) / 3;
3171 cubic_control[1].y += (cubic_control[0].y + 1) / 3;
3172 cubic_control[2].x += (cubic_control[3].x + 1) / 3;
3173 cubic_control[2].y += (cubic_control[3].y + 1) / 3;
3175 FTVectorToPOINTFX(&cubic_control[1], &ppc->apfx[cpfx]);
3176 FTVectorToPOINTFX(&cubic_control[2], &ppc->apfx[cpfx+1]);
3177 FTVectorToPOINTFX(&cubic_control[3], &ppc->apfx[cpfx+2]);
3182 } while(point <= outline->contours[contour] &&
3183 (outline->tags[point] & FT_Curve_Tag_On) ==
3184 (outline->tags[point-1] & FT_Curve_Tag_On));
3185 /* At the end of a contour Windows adds the start point,
3186 but only for Beziers and we've already done that.
3188 if(point <= outline->contours[contour] &&
3189 outline->tags[point] & FT_Curve_Tag_On) {
3190 /* This is the closing pt of a bezier, but we've already
3191 added it, so just inc point and carry on */
3198 needed += sizeof(*ppc) + (cpfx - 1) * sizeof(POINTFX);
3201 pph->cb = needed - pph_start;
3207 FIXME("Unsupported format %d\n", format);
3213 static BOOL get_bitmap_text_metrics(GdiFont font)
3215 FT_Face ft_face = font->ft_face;
3216 #ifdef HAVE_FREETYPE_FTWINFNT_H
3217 FT_WinFNT_HeaderRec winfnt_header;
3219 const DWORD size = offsetof(OUTLINETEXTMETRICW, otmFiller);
3220 font->potm = HeapAlloc(GetProcessHeap(), 0, size);
3221 font->potm->otmSize = size;
3223 #define TM font->potm->otmTextMetrics
3224 #ifdef HAVE_FREETYPE_FTWINFNT_H
3225 if(pFT_Get_WinFNT_Header && !pFT_Get_WinFNT_Header(ft_face, &winfnt_header))
3227 TM.tmHeight = winfnt_header.pixel_height;
3228 TM.tmAscent = winfnt_header.ascent;
3229 TM.tmDescent = TM.tmHeight - TM.tmAscent;
3230 TM.tmInternalLeading = winfnt_header.internal_leading;
3231 TM.tmExternalLeading = winfnt_header.external_leading;
3232 TM.tmAveCharWidth = winfnt_header.avg_width;
3233 TM.tmMaxCharWidth = winfnt_header.max_width;
3234 TM.tmWeight = winfnt_header.weight;
3236 TM.tmDigitizedAspectX = winfnt_header.horizontal_resolution;
3237 TM.tmDigitizedAspectY = winfnt_header.vertical_resolution;
3238 TM.tmFirstChar = winfnt_header.first_char;
3239 TM.tmLastChar = winfnt_header.last_char;
3240 TM.tmDefaultChar = winfnt_header.default_char + winfnt_header.first_char;
3241 TM.tmBreakChar = winfnt_header.break_char + winfnt_header.first_char;
3242 TM.tmItalic = winfnt_header.italic;
3243 TM.tmUnderlined = font->underline;
3244 TM.tmStruckOut = font->strikeout;
3245 TM.tmPitchAndFamily = winfnt_header.pitch_and_family;
3246 TM.tmCharSet = winfnt_header.charset;
3251 TM.tmAscent = ft_face->size->metrics.ascender >> 6;
3252 TM.tmDescent = -ft_face->size->metrics.descender >> 6;
3253 TM.tmHeight = TM.tmAscent + TM.tmDescent;
3254 TM.tmInternalLeading = TM.tmHeight - ft_face->size->metrics.y_ppem;
3255 TM.tmExternalLeading = (ft_face->size->metrics.height >> 6) - TM.tmHeight;
3256 TM.tmMaxCharWidth = ft_face->size->metrics.max_advance >> 6;
3257 TM.tmAveCharWidth = TM.tmMaxCharWidth * 2 / 3; /* FIXME */
3258 TM.tmWeight = ft_face->style_flags & FT_STYLE_FLAG_BOLD ? FW_BOLD : FW_NORMAL;
3260 TM.tmDigitizedAspectX = 96; /* FIXME */
3261 TM.tmDigitizedAspectY = 96; /* FIXME */
3263 TM.tmLastChar = 255;
3264 TM.tmDefaultChar = 32;
3265 TM.tmBreakChar = 32;
3266 TM.tmItalic = ft_face->style_flags & FT_STYLE_FLAG_ITALIC ? 1 : 0;
3267 TM.tmUnderlined = font->underline;
3268 TM.tmStruckOut = font->strikeout;
3269 /* NB inverted meaning of TMPF_FIXED_PITCH */
3270 TM.tmPitchAndFamily = ft_face->face_flags & FT_FACE_FLAG_FIXED_WIDTH ? 0 : TMPF_FIXED_PITCH;
3271 TM.tmCharSet = font->charset;
3278 /*************************************************************
3279 * WineEngGetTextMetrics
3282 BOOL WineEngGetTextMetrics(GdiFont font, LPTEXTMETRICW ptm)
3285 if(!WineEngGetOutlineTextMetrics(font, 0, NULL))
3286 if(!get_bitmap_text_metrics(font))
3289 if(!font->potm) return FALSE;
3290 memcpy(ptm, &font->potm->otmTextMetrics, sizeof(*ptm));
3292 if (font->aveWidth) {
3293 ptm->tmAveCharWidth = font->aveWidth * font->font_desc.matrix.eM11;
3299 /*************************************************************
3300 * WineEngGetOutlineTextMetrics
3303 UINT WineEngGetOutlineTextMetrics(GdiFont font, UINT cbSize,
3304 OUTLINETEXTMETRICW *potm)
3306 FT_Face ft_face = font->ft_face;
3307 UINT needed, lenfam, lensty, ret;
3309 TT_HoriHeader *pHori;
3310 TT_Postscript *pPost;
3311 FT_Fixed x_scale, y_scale;
3312 WCHAR *family_nameW, *style_nameW;
3313 static const WCHAR spaceW[] = {' ', '\0'};
3315 INT ascent, descent;
3317 TRACE("font=%p\n", font);
3319 if(!FT_IS_SCALABLE(ft_face))
3323 if(cbSize >= font->potm->otmSize)
3324 memcpy(potm, font->potm, font->potm->otmSize);
3325 return font->potm->otmSize;
3329 needed = sizeof(*potm);
3331 lenfam = (strlenW(font->name) + 1) * sizeof(WCHAR);
3332 family_nameW = strdupW(font->name);
3334 lensty = MultiByteToWideChar(CP_ACP, 0, ft_face->style_name, -1, NULL, 0)
3336 style_nameW = HeapAlloc(GetProcessHeap(), 0, lensty);
3337 MultiByteToWideChar(CP_ACP, 0, ft_face->style_name, -1,
3338 style_nameW, lensty/sizeof(WCHAR));
3340 /* These names should be read from the TT name table */
3342 /* length of otmpFamilyName */
3345 /* length of otmpFaceName */
3346 if(!strcasecmp(ft_face->style_name, "regular")) {
3347 needed += lenfam; /* just the family name */
3349 needed += lenfam + lensty; /* family + " " + style */
3352 /* length of otmpStyleName */
3355 /* length of otmpFullName */
3356 needed += lenfam + lensty;
3359 x_scale = ft_face->size->metrics.x_scale;
3360 y_scale = ft_face->size->metrics.y_scale;
3362 pOS2 = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_os2);
3364 FIXME("Can't find OS/2 table - not TT font?\n");
3369 pHori = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_hhea);
3371 FIXME("Can't find HHEA table - not TT font?\n");
3376 pPost = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_post); /* we can live with this failing */
3378 TRACE("OS/2 winA = %d winD = %d typoA = %d typoD = %d typoLG = %d FT_Face a = %d, d = %d, h = %d: HORZ a = %d, d = %d lg = %d maxY = %ld minY = %ld\n",
3379 pOS2->usWinAscent, pOS2->usWinDescent,
3380 pOS2->sTypoAscender, pOS2->sTypoDescender, pOS2->sTypoLineGap,
3381 ft_face->ascender, ft_face->descender, ft_face->height,
3382 pHori->Ascender, pHori->Descender, pHori->Line_Gap,
3383 ft_face->bbox.yMax, ft_face->bbox.yMin);
3385 font->potm = HeapAlloc(GetProcessHeap(), 0, needed);
3386 font->potm->otmSize = needed;
3388 #define TM font->potm->otmTextMetrics
3390 if(pOS2->usWinAscent + pOS2->usWinDescent == 0) {
3391 ascent = pHori->Ascender;
3392 descent = -pHori->Descender;
3394 ascent = pOS2->usWinAscent;
3395 descent = pOS2->usWinDescent;
3399 TM.tmAscent = font->yMax;
3400 TM.tmDescent = -font->yMin;
3401 TM.tmInternalLeading = (TM.tmAscent + TM.tmDescent) - ft_face->size->metrics.y_ppem;
3403 TM.tmAscent = (pFT_MulFix(ascent, y_scale) + 32) >> 6;
3404 TM.tmDescent = (pFT_MulFix(descent, y_scale) + 32) >> 6;
3405 TM.tmInternalLeading = (pFT_MulFix(ascent + descent
3406 - ft_face->units_per_EM, y_scale) + 32) >> 6;
3409 TM.tmHeight = TM.tmAscent + TM.tmDescent;
3412 el = MAX(0, LineGap - ((WinAscent + WinDescent) - (Ascender - Descender)))
3414 TM.tmExternalLeading = max(0, (pFT_MulFix(pHori->Line_Gap -
3415 ((ascent + descent) -
3416 (pHori->Ascender - pHori->Descender)), y_scale) + 32) >> 6);
3418 TM.tmAveCharWidth = (pFT_MulFix(pOS2->xAvgCharWidth, x_scale) + 32) >> 6;
3419 if (TM.tmAveCharWidth == 0) {
3420 TM.tmAveCharWidth = 1;
3422 TM.tmMaxCharWidth = (pFT_MulFix(ft_face->bbox.xMax - ft_face->bbox.xMin, x_scale) + 32) >> 6;
3423 TM.tmWeight = font->fake_bold ? FW_BOLD : pOS2->usWeightClass;
3425 TM.tmDigitizedAspectX = 300;
3426 TM.tmDigitizedAspectY = 300;
3427 TM.tmFirstChar = pOS2->usFirstCharIndex;
3428 TM.tmLastChar = pOS2->usLastCharIndex;
3429 TM.tmDefaultChar = pOS2->usDefaultChar;
3430 TM.tmBreakChar = pOS2->usBreakChar ? pOS2->usBreakChar : ' ';
3431 TM.tmItalic = font->fake_italic ? 255 : ((ft_face->style_flags & FT_STYLE_FLAG_ITALIC) ? 255 : 0);
3432 TM.tmUnderlined = font->underline;
3433 TM.tmStruckOut = font->strikeout;
3435 /* Yes TPMF_FIXED_PITCH is correct; braindead api */
3436 if(!FT_IS_FIXED_WIDTH(ft_face) &&
3437 (pOS2->version == 0xFFFFU ||
3438 pOS2->panose[PAN_PROPORTION_INDEX] != PAN_PROP_MONOSPACED))
3439 TM.tmPitchAndFamily = TMPF_FIXED_PITCH;
3441 TM.tmPitchAndFamily = 0;
3443 switch(pOS2->panose[PAN_FAMILYTYPE_INDEX]) {
3444 case PAN_FAMILY_SCRIPT:
3445 TM.tmPitchAndFamily |= FF_SCRIPT;
3447 case PAN_FAMILY_DECORATIVE:
3448 case PAN_FAMILY_PICTORIAL:
3449 TM.tmPitchAndFamily |= FF_DECORATIVE;
3451 case PAN_FAMILY_TEXT_DISPLAY:
3452 if(TM.tmPitchAndFamily == 0) /* fixed */
3453 TM.tmPitchAndFamily = FF_MODERN;
3455 switch(pOS2->panose[PAN_SERIFSTYLE_INDEX]) {
3456 case PAN_SERIF_NORMAL_SANS:
3457 case PAN_SERIF_OBTUSE_SANS:
3458 case PAN_SERIF_PERP_SANS:
3459 TM.tmPitchAndFamily |= FF_SWISS;
3462 TM.tmPitchAndFamily |= FF_ROMAN;
3467 TM.tmPitchAndFamily |= FF_DONTCARE;
3470 if(FT_IS_SCALABLE(ft_face))
3471 TM.tmPitchAndFamily |= TMPF_VECTOR;
3472 if(FT_IS_SFNT(ft_face))
3473 TM.tmPitchAndFamily |= TMPF_TRUETYPE;
3475 TM.tmCharSet = font->charset;
3478 font->potm->otmFiller = 0;
3479 memcpy(&font->potm->otmPanoseNumber, pOS2->panose, PANOSE_COUNT);
3480 font->potm->otmfsSelection = pOS2->fsSelection;
3481 font->potm->otmfsType = pOS2->fsType;
3482 font->potm->otmsCharSlopeRise = pHori->caret_Slope_Rise;
3483 font->potm->otmsCharSlopeRun = pHori->caret_Slope_Run;
3484 font->potm->otmItalicAngle = 0; /* POST table */
3485 font->potm->otmEMSquare = ft_face->units_per_EM;
3486 font->potm->otmAscent = (pFT_MulFix(pOS2->sTypoAscender, y_scale) + 32) >> 6;
3487 font->potm->otmDescent = (pFT_MulFix(pOS2->sTypoDescender, y_scale) + 32) >> 6;
3488 font->potm->otmLineGap = (pFT_MulFix(pOS2->sTypoLineGap, y_scale) + 32) >> 6;
3489 font->potm->otmsCapEmHeight = (pFT_MulFix(pOS2->sCapHeight, y_scale) + 32) >> 6;
3490 font->potm->otmsXHeight = (pFT_MulFix(pOS2->sxHeight, y_scale) + 32) >> 6;
3491 font->potm->otmrcFontBox.left = (pFT_MulFix(ft_face->bbox.xMin, x_scale) + 32) >> 6;
3492 font->potm->otmrcFontBox.right = (pFT_MulFix(ft_face->bbox.xMax, x_scale) + 32) >> 6;
3493 font->potm->otmrcFontBox.top = (pFT_MulFix(ft_face->bbox.yMax, y_scale) + 32) >> 6;
3494 font->potm->otmrcFontBox.bottom = (pFT_MulFix(ft_face->bbox.yMin, y_scale) + 32) >> 6;
3495 font->potm->otmMacAscent = 0; /* where do these come from ? */
3496 font->potm->otmMacDescent = 0;
3497 font->potm->otmMacLineGap = 0;
3498 font->potm->otmusMinimumPPEM = 0; /* TT Header */
3499 font->potm->otmptSubscriptSize.x = (pFT_MulFix(pOS2->ySubscriptXSize, x_scale) + 32) >> 6;
3500 font->potm->otmptSubscriptSize.y = (pFT_MulFix(pOS2->ySubscriptYSize, y_scale) + 32) >> 6;
3501 font->potm->otmptSubscriptOffset.x = (pFT_MulFix(pOS2->ySubscriptXOffset, x_scale) + 32) >> 6;
3502 font->potm->otmptSubscriptOffset.y = (pFT_MulFix(pOS2->ySubscriptYOffset, y_scale) + 32) >> 6;
3503 font->potm->otmptSuperscriptSize.x = (pFT_MulFix(pOS2->ySuperscriptXSize, x_scale) + 32) >> 6;
3504 font->potm->otmptSuperscriptSize.y = (pFT_MulFix(pOS2->ySuperscriptYSize, y_scale) + 32) >> 6;
3505 font->potm->otmptSuperscriptOffset.x = (pFT_MulFix(pOS2->ySuperscriptXOffset, x_scale) + 32) >> 6;
3506 font->potm->otmptSuperscriptOffset.y = (pFT_MulFix(pOS2->ySuperscriptYOffset, y_scale) + 32) >> 6;
3507 font->potm->otmsStrikeoutSize = (pFT_MulFix(pOS2->yStrikeoutSize, y_scale) + 32) >> 6;
3508 font->potm->otmsStrikeoutPosition = (pFT_MulFix(pOS2->yStrikeoutPosition, y_scale) + 32) >> 6;
3510 font->potm->otmsUnderscoreSize = 0;
3511 font->potm->otmsUnderscorePosition = 0;
3513 font->potm->otmsUnderscoreSize = (pFT_MulFix(pPost->underlineThickness, y_scale) + 32) >> 6;
3514 font->potm->otmsUnderscorePosition = (pFT_MulFix(pPost->underlinePosition, y_scale) + 32) >> 6;
3517 /* otmp* members should clearly have type ptrdiff_t, but M$ knows best */
3518 cp = (char*)font->potm + sizeof(*font->potm);
3519 font->potm->otmpFamilyName = (LPSTR)(cp - (char*)font->potm);
3520 strcpyW((WCHAR*)cp, family_nameW);
3522 font->potm->otmpStyleName = (LPSTR)(cp - (char*)font->potm);
3523 strcpyW((WCHAR*)cp, style_nameW);
3525 font->potm->otmpFaceName = (LPSTR)(cp - (char*)font->potm);
3526 strcpyW((WCHAR*)cp, family_nameW);
3527 if(strcasecmp(ft_face->style_name, "regular")) {
3528 strcatW((WCHAR*)cp, spaceW);
3529 strcatW((WCHAR*)cp, style_nameW);
3530 cp += lenfam + lensty;
3533 font->potm->otmpFullName = (LPSTR)(cp - (char*)font->potm);
3534 strcpyW((WCHAR*)cp, family_nameW);
3535 strcatW((WCHAR*)cp, spaceW);
3536 strcatW((WCHAR*)cp, style_nameW);
3539 if(potm && needed <= cbSize)
3540 memcpy(potm, font->potm, font->potm->otmSize);
3543 HeapFree(GetProcessHeap(), 0, style_nameW);
3544 HeapFree(GetProcessHeap(), 0, family_nameW);
3549 static BOOL load_child_font(GdiFont font, CHILD_FONT *child)
3551 HFONTLIST *hfontlist;
3552 child->font = alloc_font();
3553 child->font->ft_face = OpenFontFile(child->font, child->file_name, child->index, 0, -font->ppem);
3554 if(!child->font->ft_face)
3556 free_font(child->font);
3561 child->font->orientation = font->orientation;
3562 hfontlist = HeapAlloc(GetProcessHeap(), 0, sizeof(*hfontlist));
3563 hfontlist->hfont = CreateFontIndirectW(&font->font_desc.lf);
3564 list_add_head(&child->font->hfontlist, &hfontlist->entry);
3565 child->font->base_font = font;
3566 list_add_head(&child_font_list, &child->font->entry);
3567 TRACE("created child font hfont %p for base %p child %p\n", hfontlist->hfont, font, child->font);
3571 static BOOL get_glyph_index_linked(GdiFont font, UINT c, GdiFont *linked_font, FT_UInt *glyph)
3574 CHILD_FONT *child_font;
3577 font = font->base_font;
3579 *linked_font = font;
3581 if((*glyph = get_glyph_index(font, c)))
3584 LIST_FOR_EACH_ENTRY(child_font, &font->child_fonts, CHILD_FONT, entry)
3586 if(!child_font->font)
3587 if(!load_child_font(font, child_font))
3590 if(!child_font->font->ft_face)
3592 g = get_glyph_index(child_font->font, c);
3596 *linked_font = child_font->font;
3603 /*************************************************************
3604 * WineEngGetCharWidth
3607 BOOL WineEngGetCharWidth(GdiFont font, UINT firstChar, UINT lastChar,
3612 FT_UInt glyph_index;
3613 GdiFont linked_font;
3615 TRACE("%p, %d, %d, %p\n", font, firstChar, lastChar, buffer);
3617 for(c = firstChar; c <= lastChar; c++) {
3618 get_glyph_index_linked(font, c, &linked_font, &glyph_index);
3619 WineEngGetGlyphOutline(linked_font, glyph_index, GGO_METRICS | GGO_GLYPH_INDEX,
3620 &gm, 0, NULL, NULL);
3621 buffer[c - firstChar] = linked_font->gm[glyph_index].adv;
3626 /*************************************************************
3627 * WineEngGetCharABCWidths
3630 BOOL WineEngGetCharABCWidths(GdiFont font, UINT firstChar, UINT lastChar,
3635 FT_UInt glyph_index;
3636 GdiFont linked_font;
3638 TRACE("%p, %d, %d, %p\n", font, firstChar, lastChar, buffer);
3640 if(!FT_IS_SCALABLE(font->ft_face))
3643 for(c = firstChar; c <= lastChar; c++) {
3644 get_glyph_index_linked(font, c, &linked_font, &glyph_index);
3645 WineEngGetGlyphOutline(linked_font, glyph_index, GGO_METRICS | GGO_GLYPH_INDEX,
3646 &gm, 0, NULL, NULL);
3647 buffer[c - firstChar].abcA = linked_font->gm[glyph_index].lsb;
3648 buffer[c - firstChar].abcB = linked_font->gm[glyph_index].bbx;
3649 buffer[c - firstChar].abcC = linked_font->gm[glyph_index].adv - linked_font->gm[glyph_index].lsb -
3650 linked_font->gm[glyph_index].bbx;
3655 /*************************************************************
3656 * WineEngGetTextExtentPoint
3659 BOOL WineEngGetTextExtentPoint(GdiFont font, LPCWSTR wstr, INT count,
3665 FT_UInt glyph_index;
3666 GdiFont linked_font;
3668 TRACE("%p, %s, %d, %p\n", font, debugstr_wn(wstr, count), count,
3672 WineEngGetTextMetrics(font, &tm);
3673 size->cy = tm.tmHeight;
3675 for(idx = 0; idx < count; idx++) {
3676 get_glyph_index_linked(font, wstr[idx], &linked_font, &glyph_index);
3677 WineEngGetGlyphOutline(linked_font, glyph_index, GGO_METRICS | GGO_GLYPH_INDEX,
3678 &gm, 0, NULL, NULL);
3679 size->cx += linked_font->gm[glyph_index].adv;
3681 TRACE("return %ld,%ld\n", size->cx, size->cy);
3685 /*************************************************************
3686 * WineEngGetTextExtentPointI
3689 BOOL WineEngGetTextExtentPointI(GdiFont font, const WORD *indices, INT count,
3696 TRACE("%p, %p, %d, %p\n", font, indices, count, size);
3699 WineEngGetTextMetrics(font, &tm);
3700 size->cy = tm.tmHeight;
3702 for(idx = 0; idx < count; idx++) {
3703 WineEngGetGlyphOutline(font, indices[idx],
3704 GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL,
3706 size->cx += font->gm[indices[idx]].adv;
3708 TRACE("return %ld,%ld\n", size->cx, size->cy);
3712 /*************************************************************
3713 * WineEngGetFontData
3716 DWORD WineEngGetFontData(GdiFont font, DWORD table, DWORD offset, LPVOID buf,
3719 FT_Face ft_face = font->ft_face;
3723 TRACE("font=%p, table=%08lx, offset=%08lx, buf=%p, cbData=%lx\n",
3724 font, table, offset, buf, cbData);
3726 if(!FT_IS_SFNT(ft_face))
3734 if(table) { /* MS tags differ in endidness from FT ones */
3735 table = table >> 24 | table << 24 |
3736 (table >> 8 & 0xff00) | (table << 8 & 0xff0000);
3739 /* If the FT_Load_Sfnt_Table function is there we'll use it */
3740 if(pFT_Load_Sfnt_Table) {
3741 /* make sure value of len is the value freetype says it needs */
3744 err = pFT_Load_Sfnt_Table(ft_face, table, offset, NULL, &needed);
3745 if( !err && needed < len) len = needed;
3747 err = pFT_Load_Sfnt_Table(ft_face, table, offset, buf, &len);
3749 #ifdef HAVE_FREETYPE_INTERNAL_SFNT_H
3750 else { /* Do it the hard way */
3751 TT_Face tt_face = (TT_Face) ft_face;
3752 SFNT_Interface *sfnt;
3753 if (FT_Version.major==2 && FT_Version.minor==0)
3756 sfnt = *(SFNT_Interface**)((char*)tt_face + 528);
3760 /* A field was added in the middle of the structure in 2.1.x */
3761 sfnt = *(SFNT_Interface**)((char*)tt_face + 532);
3763 /* make sure value of len is the value freetype says it needs */
3766 err = sfnt->load_any(tt_face, table, offset, NULL, &needed);
3767 if( !err && needed < len) len = needed;
3769 err = sfnt->load_any(tt_face, table, offset, buf, &len);
3775 MESSAGE("This version of Wine was compiled with freetype headers later than 2.2.0\n"
3776 "but is being run with a freetype library without the FT_Load_Sfnt_Table function.\n"
3777 "Please upgrade your freetype library.\n");
3780 err = FT_Err_Unimplemented_Feature;
3784 TRACE("Can't find table %08lx.\n", table);
3790 /*************************************************************
3791 * WineEngGetTextFace
3794 INT WineEngGetTextFace(GdiFont font, INT count, LPWSTR str)
3797 lstrcpynW(str, font->name, count);
3798 return strlenW(font->name);
3800 return strlenW(font->name) + 1;
3803 UINT WineEngGetTextCharsetInfo(GdiFont font, LPFONTSIGNATURE fs, DWORD flags)
3805 if (fs) memcpy(fs, &font->fs, sizeof(FONTSIGNATURE));
3806 return font->charset;
3809 BOOL WineEngGetLinkedHFont(DC *dc, WCHAR c, HFONT *new_hfont, UINT *glyph)
3811 GdiFont font = dc->gdiFont, linked_font;
3812 struct list *first_hfont;
3815 ret = get_glyph_index_linked(font, c, &linked_font, glyph);
3816 TRACE("get_glyph_index_linked glyph %d font %p\n", *glyph, linked_font);
3817 if(font == linked_font)
3818 *new_hfont = dc->hFont;
3821 first_hfont = list_head(&linked_font->hfontlist);
3822 *new_hfont = LIST_ENTRY(first_hfont, struct tagHFONTLIST, entry)->hfont;
3829 /*************************************************************
3832 BOOL WINAPI FontIsLinked(HDC hdc)
3834 DC *dc = DC_GetDCPtr(hdc);
3837 if(!dc) return FALSE;
3838 if(dc->gdiFont && !list_empty(&dc->gdiFont->child_fonts))
3840 GDI_ReleaseObj(hdc);
3841 TRACE("returning %d\n", ret);
3845 static BOOL is_hinting_enabled(void)
3847 FT_Module mod = pFT_Get_Module(library, "truetype");
3849 /* Use the >= 2.2.0 function if available */
3850 if(pFT_Module_Get_Flags)
3853 pFT_Module_Get_Flags(mod, &flags);
3854 return flags & FT_MODULE_DRIVER_HAS_HINTER;
3857 /* otherwise if we've been compiled with < 2.2.0 headers
3858 use the internal macro */
3859 #ifdef FT_DRIVER_HAS_HINTER
3860 if(mod && FT_DRIVER_HAS_HINTER(mod))
3867 /*************************************************************************
3868 * GetRasterizerCaps (GDI32.@)
3870 BOOL WINAPI GetRasterizerCaps( LPRASTERIZER_STATUS lprs, UINT cbNumBytes)
3872 static int hinting = -1;
3875 hinting = is_hinting_enabled();
3877 lprs->nSize = sizeof(RASTERIZER_STATUS);
3878 lprs->wFlags = TT_AVAILABLE | TT_ENABLED | (hinting ? WINE_TT_HINTER_ENABLED : 0);
3879 lprs->nLanguageID = 0;
3884 #else /* HAVE_FREETYPE */
3886 BOOL WineEngInit(void)
3890 GdiFont WineEngCreateFontInstance(DC *dc, HFONT hfont)
3894 BOOL WineEngDestroyFontInstance(HFONT hfont)
3899 DWORD WineEngEnumFonts(LPLOGFONTW plf, FONTENUMPROCW proc, LPARAM lparam)
3904 DWORD WineEngGetGlyphIndices(GdiFont font, LPCWSTR lpstr, INT count,
3905 LPWORD pgi, DWORD flags)
3910 DWORD WineEngGetGlyphOutline(GdiFont font, UINT glyph, UINT format,
3911 LPGLYPHMETRICS lpgm, DWORD buflen, LPVOID buf,
3914 ERR("called but we don't have FreeType\n");
3918 BOOL WineEngGetTextMetrics(GdiFont font, LPTEXTMETRICW ptm)
3920 ERR("called but we don't have FreeType\n");
3924 UINT WineEngGetOutlineTextMetrics(GdiFont font, UINT cbSize,
3925 OUTLINETEXTMETRICW *potm)
3927 ERR("called but we don't have FreeType\n");
3931 BOOL WineEngGetCharWidth(GdiFont font, UINT firstChar, UINT lastChar,
3934 ERR("called but we don't have FreeType\n");
3938 BOOL WineEngGetCharABCWidths(GdiFont font, UINT firstChar, UINT lastChar,
3941 ERR("called but we don't have FreeType\n");
3945 BOOL WineEngGetTextExtentPoint(GdiFont font, LPCWSTR wstr, INT count,
3948 ERR("called but we don't have FreeType\n");
3952 BOOL WineEngGetTextExtentPointI(GdiFont font, const WORD *indices, INT count,
3955 ERR("called but we don't have FreeType\n");
3959 DWORD WineEngGetFontData(GdiFont font, DWORD table, DWORD offset, LPVOID buf,
3962 ERR("called but we don't have FreeType\n");
3966 INT WineEngGetTextFace(GdiFont font, INT count, LPWSTR str)
3968 ERR("called but we don't have FreeType\n");
3972 INT WineEngAddFontResourceEx(LPCWSTR file, DWORD flags, PVOID pdv)
3978 INT WineEngRemoveFontResourceEx(LPCWSTR file, DWORD flags, PVOID pdv)
3984 UINT WineEngGetTextCharsetInfo(GdiFont font, LPFONTSIGNATURE fs, DWORD flags)
3987 return DEFAULT_CHARSET;
3990 BOOL WineEngGetLinkedHFont(DC *dc, WCHAR c, HFONT *new_hfont, UINT *glyph)
3995 BOOL WINAPI FontIsLinked(HDC hdc)
4000 /*************************************************************************
4001 * GetRasterizerCaps (GDI32.@)
4003 BOOL WINAPI GetRasterizerCaps( LPRASTERIZER_STATUS lprs, UINT cbNumBytes)
4005 lprs->nSize = sizeof(RASTERIZER_STATUS);
4007 lprs->nLanguageID = 0;
4011 #endif /* HAVE_FREETYPE */