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>
86 #ifndef SONAME_LIBFREETYPE
87 #define SONAME_LIBFREETYPE "libfreetype.so"
90 static FT_Library library = 0;
97 static FT_Version_t FT_Version;
98 static DWORD FT_SimpleVersion;
100 static void *ft_handle = NULL;
102 #define MAKE_FUNCPTR(f) static typeof(f) * p##f = NULL
103 MAKE_FUNCPTR(FT_Vector_Unit);
104 MAKE_FUNCPTR(FT_Done_Face);
105 MAKE_FUNCPTR(FT_Get_Char_Index);
106 MAKE_FUNCPTR(FT_Get_Sfnt_Table);
107 MAKE_FUNCPTR(FT_Init_FreeType);
108 MAKE_FUNCPTR(FT_Load_Glyph);
109 MAKE_FUNCPTR(FT_Matrix_Multiply);
110 MAKE_FUNCPTR(FT_MulFix);
111 MAKE_FUNCPTR(FT_New_Face);
112 MAKE_FUNCPTR(FT_Outline_Get_Bitmap);
113 MAKE_FUNCPTR(FT_Outline_Transform);
114 MAKE_FUNCPTR(FT_Outline_Translate);
115 MAKE_FUNCPTR(FT_Select_Charmap);
116 MAKE_FUNCPTR(FT_Set_Pixel_Sizes);
117 MAKE_FUNCPTR(FT_Vector_Transform);
118 static void (*pFT_Library_Version)(FT_Library,FT_Int*,FT_Int*,FT_Int*);
119 static FT_Error (*pFT_Load_Sfnt_Table)(FT_Face,FT_ULong,FT_Long,FT_Byte*,FT_ULong*);
120 static FT_ULong (*pFT_Get_First_Char)(FT_Face,FT_UInt*);
121 #ifdef HAVE_FREETYPE_FTWINFNT_H
122 MAKE_FUNCPTR(FT_Get_WinFNT_Header);
125 #ifdef HAVE_FONTCONFIG_FONTCONFIG_H
126 #include <fontconfig/fontconfig.h>
127 MAKE_FUNCPTR(FcConfigGetCurrent);
128 MAKE_FUNCPTR(FcFontList);
129 MAKE_FUNCPTR(FcFontSetDestroy);
130 MAKE_FUNCPTR(FcInit);
131 MAKE_FUNCPTR(FcObjectSetAdd);
132 MAKE_FUNCPTR(FcObjectSetCreate);
133 MAKE_FUNCPTR(FcObjectSetDestroy);
134 MAKE_FUNCPTR(FcPatternCreate);
135 MAKE_FUNCPTR(FcPatternDestroy);
136 MAKE_FUNCPTR(FcPatternGet);
137 #ifndef SONAME_LIBFONTCONFIG
138 #define SONAME_LIBFONTCONFIG "libfontconfig.so"
144 #ifndef ft_encoding_none
145 #define FT_ENCODING_NONE ft_encoding_none
147 #ifndef ft_encoding_ms_symbol
148 #define FT_ENCODING_MS_SYMBOL ft_encoding_symbol
150 #ifndef ft_encoding_unicode
151 #define FT_ENCODING_UNICODE ft_encoding_unicode
153 #ifndef ft_encoding_apple_roman
154 #define FT_ENCODING_APPLE_ROMAN ft_encoding_apple_roman
157 #define GET_BE_WORD(ptr) MAKEWORD( ((BYTE *)(ptr))[1], ((BYTE *)(ptr))[0] )
159 /* This is bascially a copy of FT_Bitmap_Size with an extra element added */
166 FT_Short internal_leading;
169 /* FT_Bitmap_Size gained 3 new elements between FreeType 2.1.4 and 2.1.5
170 So to let this compile on older versions of FreeType we'll define the
171 new structure here. */
173 FT_Short height, width;
174 FT_Pos size, x_ppem, y_ppem;
177 typedef struct tagFace {
185 FT_Fixed font_version;
187 Bitmap_Size size; /* set if face is a bitmap */
188 BOOL external; /* TRUE if we should manually add this font to the registry */
189 struct tagFamily *family;
192 typedef struct tagFamily {
200 INT adv; /* These three hold to widths of the unrotated chars */
217 typedef struct tagHFONTLIST {
235 struct list hfontlist;
240 OUTLINETEXTMETRICW *potm;
244 #define INIT_GM_SIZE 128
246 static struct list gdi_font_list = LIST_INIT(gdi_font_list);
247 static struct list unused_gdi_font_list = LIST_INIT(unused_gdi_font_list);
248 #define UNUSED_CACHE_SIZE 10
250 static struct list font_list = LIST_INIT(font_list);
252 static const WCHAR defSerif[] = {'T','i','m','e','s',' ','N','e','w',' ',
253 'R','o','m','a','n','\0'};
254 static const WCHAR defSans[] = {'A','r','i','a','l','\0'};
255 static const WCHAR defFixed[] = {'C','o','u','r','i','e','r',' ','N','e','w','\0'};
257 static const WCHAR defSystem[] = {'A','r','i','a','l','\0'};
258 static const WCHAR SystemW[] = {'S','y','s','t','e','m','\0'};
259 static const WCHAR MSSansSerifW[] = {'M','S',' ','S','a','n','s',' ',
260 'S','e','r','i','f','\0'};
261 static const WCHAR HelvW[] = {'H','e','l','v','\0'};
262 static const WCHAR RegularW[] = {'R','e','g','u','l','a','r','\0'};
264 static const WCHAR fontsW[] = {'\\','F','o','n','t','s','\0'};
265 static const WCHAR win9x_font_reg_key[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
266 'W','i','n','d','o','w','s','\\',
267 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
268 'F','o','n','t','s','\0'};
270 static const WCHAR winnt_font_reg_key[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
271 'W','i','n','d','o','w','s',' ','N','T','\\',
272 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
273 'F','o','n','t','s','\0'};
275 static const WCHAR system_fonts_reg_key[] = {'S','o','f','t','w','a','r','e','\\','F','o','n','t','s','\0'};
276 static const WCHAR FixedSys_Value[] = {'F','I','X','E','D','F','O','N','.','F','O','N','\0'};
277 static const WCHAR System_Value[] = {'F','O','N','T','S','.','F','O','N','\0'};
278 static const WCHAR OEMFont_Value[] = {'O','E','M','F','O','N','T','.','F','O','N','\0'};
280 static const WCHAR *SystemFontValues[4] = {
287 static const WCHAR external_fonts_reg_key[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\',
288 'F','o','n','t','s','\\','E','x','t','e','r','n','a','l',' ','F','o','n','t','s','\0'};
290 static const WCHAR ArabicW[] = {'A','r','a','b','i','c','\0'};
291 static const WCHAR BalticW[] = {'B','a','l','t','i','c','\0'};
292 static const WCHAR CHINESE_BIG5W[] = {'C','H','I','N','E','S','E','_','B','I','G','5','\0'};
293 static const WCHAR CHINESE_GB2312W[] = {'C','H','I','N','E','S','E','_','G','B','2','3','1','2','\0'};
294 static const WCHAR Central_EuropeanW[] = {'C','e','n','t','r','a','l',' ',
295 'E','u','r','o','p','e','a','n','\0'};
296 static const WCHAR CyrillicW[] = {'C','y','r','i','l','l','i','c','\0'};
297 static const WCHAR GreekW[] = {'G','r','e','e','k','\0'};
298 static const WCHAR HangulW[] = {'H','a','n','g','u','l','\0'};
299 static const WCHAR Hangul_Johab_W[] = {'H','a','n','g','u','l','(','J','o','h','a','b',')','\0'};
300 static const WCHAR HebrewW[] = {'H','e','b','r','e','w','\0'};
301 static const WCHAR JapaneseW[] = {'J','a','p','a','n','e','s','e','\0'};
302 static const WCHAR SymbolW[] = {'S','y','m','b','o','l','\0'};
303 static const WCHAR ThaiW[] = {'T','h','a','i','\0'};
304 static const WCHAR TurkishW[] = {'T','u','r','k','i','s','h','\0'};
305 static const WCHAR VietnameseW[] = {'V','i','e','t','n','a','m','e','s','e','\0'};
306 static const WCHAR WesternW[] = {'W','e','s','t','e','r','n','\0'};
307 static const WCHAR OEM_DOSW[] = {'O','E','M','/','D','O','S','\0'};
309 static const WCHAR *ElfScriptsW[32] = { /* these are in the order of the fsCsb[0] bits */
319 NULL, NULL, NULL, NULL, NULL, NULL, NULL, /*15*/
327 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
336 typedef struct tagFontSubst {
339 struct tagFontSubst *next;
342 static FontSubst *substlist = NULL;
343 static BOOL have_installed_roman_font = FALSE; /* CreateFontInstance will fail if this is still FALSE */
345 static const WCHAR font_mutex_nameW[] = {'_','_','W','I','N','E','_','F','O','N','T','_','M','U','T','E','X','_','_','\0'};
348 /****************************************
349 * Notes on .fon files
351 * The fonts System, FixedSys and Terminal are special. There are typically multiple
352 * versions installed for different resolutions and codepages. Windows stores which one to use
353 * in HKEY_CURRENT_CONFIG\\Software\\Fonts.
355 * FIXEDFON.FON FixedSys
357 * OEMFONT.FON Terminal
358 * LogPixels Current dpi set by the display control panel applet
359 * (HKLM\\Software\\Microsft\\Windows NT\\CurrentVersion\\FontDPI
360 * also has a LogPixels value that appears to mirror this)
362 * On my system these values have data: vgafix.fon, vgasys.fon, vga850.fon and 96 respectively
363 * (vgaoem.fon would be your oemfont.fon if you have a US setup).
364 * If the resolution is changed to be >= 109dpi then the fonts goto 8514fix, 8514sys and 8514oem
365 * (not sure what's happening to the oem codepage here). 109 is nicely halfway between 96 and 120dpi,
366 * so that makes sense.
368 * Additionally Windows also loads the fonts listed in the [386enh] section of system.ini (this doesn't appear
369 * to be mapped into the registry on Windows 2000 at least).
372 * ega80woa.fon=ega80850.fon
373 * ega40woa.fon=ega40850.fon
374 * cga80woa.fon=cga80850.fon
375 * cga40woa.fon=cga40850.fon
379 static inline BOOL is_win9x(void)
381 return GetVersion() & 0x80000000;
384 This function builds an FT_Fixed from a float. It puts the integer part
385 in the highest 16 bits and the decimal part in the lowest 16 bits of the FT_Fixed.
386 It fails if the integer part of the float number is greater than SHORT_MAX.
388 static inline FT_Fixed FT_FixedFromFloat(float f)
391 unsigned short fract = (f - value) * 0xFFFF;
392 return (FT_Fixed)((long)value << 16 | (unsigned long)fract);
396 This function builds an FT_Fixed from a FIXED. It simply put f.value
397 in the highest 16 bits and f.fract in the lowest 16 bits of the FT_Fixed.
399 static inline FT_Fixed FT_FixedFromFIXED(FIXED f)
401 return (FT_Fixed)((long)f.value << 16 | (unsigned long)f.fract);
404 #define ADDFONT_EXTERNAL_FONT 0x01
405 #define ADDFONT_FORCE_BITMAP 0x02
406 static BOOL AddFontFileToList(const char *file, char *fake_family, DWORD flags)
410 TT_Header *pHeader = NULL;
411 WCHAR *FamilyW, *StyleW;
415 struct list *family_elem_ptr, *face_elem_ptr;
417 FT_Long face_index = 0, num_faces;
418 #ifdef HAVE_FREETYPE_FTWINFNT_H
419 FT_WinFNT_HeaderRec winfnt_header;
424 char *family_name = fake_family;
426 TRACE("Loading font file %s index %ld\n", debugstr_a(file), face_index);
427 if((err = pFT_New_Face(library, file, face_index, &ft_face)) != 0) {
428 WARN("Unable to load font file %s err = %x\n", debugstr_a(file), err);
432 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*/
433 pFT_Done_Face(ft_face);
437 /* There are too many bugs in FreeType < 2.1.9 for bitmap font support */
438 if(!FT_IS_SCALABLE(ft_face) && FT_SimpleVersion < ((2 << 16) | (1 << 8) | (9 << 0))) {
439 pFT_Done_Face(ft_face);
443 if(FT_IS_SFNT(ft_face) && (!pFT_Get_Sfnt_Table(ft_face, ft_sfnt_os2) ||
444 !pFT_Get_Sfnt_Table(ft_face, ft_sfnt_hhea) ||
445 !(pHeader = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_head)))) {
446 TRACE("Font file %s lacks either an OS2, HHEA or HEAD table.\n"
447 "Skipping this font.\n", debugstr_a(file));
448 pFT_Done_Face(ft_face);
452 if(!ft_face->family_name || !ft_face->style_name) {
453 TRACE("Font file %s lacks either a family or style name\n", debugstr_a(file));
454 pFT_Done_Face(ft_face);
459 family_name = ft_face->family_name;
463 My_FT_Bitmap_Size *size = NULL;
465 if(!FT_IS_SCALABLE(ft_face))
466 size = (My_FT_Bitmap_Size *)ft_face->available_sizes + bitmap_num;
468 len = MultiByteToWideChar(CP_ACP, 0, family_name, -1, NULL, 0);
469 FamilyW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
470 MultiByteToWideChar(CP_ACP, 0, family_name, -1, FamilyW, len);
473 LIST_FOR_EACH(family_elem_ptr, &font_list) {
474 family = LIST_ENTRY(family_elem_ptr, Family, entry);
475 if(!strcmpW(family->FamilyName, FamilyW))
480 family = HeapAlloc(GetProcessHeap(), 0, sizeof(*family));
481 family->FamilyName = FamilyW;
482 list_init(&family->faces);
483 list_add_tail(&font_list, &family->entry);
485 HeapFree(GetProcessHeap(), 0, FamilyW);
488 len = MultiByteToWideChar(CP_ACP, 0, ft_face->style_name, -1, NULL, 0);
489 StyleW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
490 MultiByteToWideChar(CP_ACP, 0, ft_face->style_name, -1, StyleW, len);
492 face_elem_ptr = list_head(&family->faces);
493 while(face_elem_ptr) {
494 face = LIST_ENTRY(face_elem_ptr, Face, entry);
495 face_elem_ptr = list_next(&family->faces, face_elem_ptr);
496 if(!strcmpW(face->StyleName, StyleW) &&
497 (FT_IS_SCALABLE(ft_face) || (size->y_ppem == face->size.y_ppem))) {
498 TRACE("Already loaded font %s %s original version is %lx, this version is %lx\n",
499 debugstr_w(family->FamilyName), debugstr_w(StyleW),
500 face->font_version, pHeader ? pHeader->Font_Revision : 0);
503 TRACE("This font is a replacement but the original really exists, so we'll skip the replacement\n");
504 HeapFree(GetProcessHeap(), 0, StyleW);
505 pFT_Done_Face(ft_face);
508 if(!pHeader || pHeader->Font_Revision <= face->font_version) {
509 TRACE("Original font is newer so skipping this one\n");
510 HeapFree(GetProcessHeap(), 0, StyleW);
511 pFT_Done_Face(ft_face);
514 TRACE("Replacing original with this one\n");
515 list_remove(&face->entry);
516 HeapFree(GetProcessHeap(), 0, face->file);
517 HeapFree(GetProcessHeap(), 0, face->StyleName);
518 HeapFree(GetProcessHeap(), 0, face);
523 face = HeapAlloc(GetProcessHeap(), 0, sizeof(*face));
524 list_add_tail(&family->faces, &face->entry);
525 face->StyleName = StyleW;
526 face->file = HeapAlloc(GetProcessHeap(),0,strlen(file)+1);
527 strcpy(face->file, file);
528 face->face_index = face_index;
529 face->Italic = (ft_face->style_flags & FT_STYLE_FLAG_ITALIC) ? 1 : 0;
530 face->Bold = (ft_face->style_flags & FT_STYLE_FLAG_BOLD) ? 1 : 0;
531 face->font_version = pHeader ? pHeader->Font_Revision : 0;
532 face->family = family;
533 face->external = (flags & ADDFONT_EXTERNAL_FONT) ? TRUE : FALSE;
535 if(FT_IS_SCALABLE(ft_face)) {
536 memset(&face->size, 0, sizeof(face->size));
537 face->scalable = TRUE;
539 TRACE("Adding bitmap size h %d w %d size %ld x_ppem %ld y_ppem %ld\n",
540 size->height, size->width, size->size >> 6,
541 size->x_ppem >> 6, size->y_ppem >> 6);
542 face->size.height = size->height;
543 face->size.width = size->width;
544 face->size.size = size->size;
545 face->size.x_ppem = size->x_ppem;
546 face->size.y_ppem = size->y_ppem;
547 face->size.internal_leading = 0;
548 face->scalable = FALSE;
551 memset(&face->fs, 0, sizeof(face->fs));
553 pOS2 = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_os2);
555 face->fs.fsCsb[0] = pOS2->ulCodePageRange1;
556 face->fs.fsCsb[1] = pOS2->ulCodePageRange2;
557 face->fs.fsUsb[0] = pOS2->ulUnicodeRange1;
558 face->fs.fsUsb[1] = pOS2->ulUnicodeRange2;
559 face->fs.fsUsb[2] = pOS2->ulUnicodeRange3;
560 face->fs.fsUsb[3] = pOS2->ulUnicodeRange4;
561 if(pOS2->version == 0) {
564 if(!pFT_Get_First_Char || (pFT_Get_First_Char( ft_face, &dummy ) < 0x100))
565 face->fs.fsCsb[0] |= 1;
567 face->fs.fsCsb[0] |= 1L << 31;
570 #ifdef HAVE_FREETYPE_FTWINFNT_H
571 else if(pFT_Get_WinFNT_Header && !pFT_Get_WinFNT_Header(ft_face, &winfnt_header)) {
573 TRACE("pix_h %d charset %d dpi %dx%d pt %d\n", winfnt_header.pixel_height, winfnt_header.charset,
574 winfnt_header.vertical_resolution,winfnt_header.horizontal_resolution, winfnt_header.nominal_point_size);
575 if(TranslateCharsetInfo((DWORD*)(UINT)winfnt_header.charset, &csi, TCI_SRCCHARSET))
576 memcpy(&face->fs, &csi.fs, sizeof(csi.fs));
577 face->size.internal_leading = winfnt_header.internal_leading;
580 TRACE("fsCsb = %08lx %08lx/%08lx %08lx %08lx %08lx\n",
581 face->fs.fsCsb[0], face->fs.fsCsb[1],
582 face->fs.fsUsb[0], face->fs.fsUsb[1],
583 face->fs.fsUsb[2], face->fs.fsUsb[3]);
586 if(face->fs.fsCsb[0] == 0) { /* let's see if we can find any interesting cmaps */
587 for(i = 0; i < ft_face->num_charmaps; i++) {
588 switch(ft_face->charmaps[i]->encoding) {
589 case FT_ENCODING_UNICODE:
590 case FT_ENCODING_APPLE_ROMAN:
591 face->fs.fsCsb[0] |= 1;
593 case FT_ENCODING_MS_SYMBOL:
594 face->fs.fsCsb[0] |= 1L << 31;
602 if(face->fs.fsCsb[0] & ~(1L << 31))
603 have_installed_roman_font = TRUE;
604 } while(!FT_IS_SCALABLE(ft_face) && ++bitmap_num < ft_face->num_fixed_sizes);
606 num_faces = ft_face->num_faces;
607 pFT_Done_Face(ft_face);
608 TRACE("Added font %s %s\n", debugstr_w(family->FamilyName),
610 } while(num_faces > ++face_index);
614 static void DumpFontList(void)
618 struct list *family_elem_ptr, *face_elem_ptr;
620 LIST_FOR_EACH(family_elem_ptr, &font_list) {
621 family = LIST_ENTRY(family_elem_ptr, Family, entry);
622 TRACE("Family: %s\n", debugstr_w(family->FamilyName));
623 LIST_FOR_EACH(face_elem_ptr, &family->faces) {
624 face = LIST_ENTRY(face_elem_ptr, Face, entry);
625 TRACE("\t%s", debugstr_w(face->StyleName));
627 TRACE(" %ld", face->size.y_ppem >> 6);
634 static void DumpSubstList(void)
638 for(psub = substlist; psub; psub = psub->next)
639 if(psub->from.charset != -1 || psub->to.charset != -1)
640 TRACE("%s:%d -> %s:%d\n", debugstr_w(psub->from.name),
641 psub->from.charset, debugstr_w(psub->to.name), psub->to.charset);
643 TRACE("%s -> %s\n", debugstr_w(psub->from.name),
644 debugstr_w(psub->to.name));
648 static LPWSTR strdupW(LPWSTR p)
651 DWORD len = (strlenW(p) + 1) * sizeof(WCHAR);
652 ret = HeapAlloc(GetProcessHeap(), 0, len);
657 static void split_subst_info(NameCs *nc, LPSTR str)
659 CHAR *p = strrchr(str, ',');
664 nc->charset = strtol(p+1, NULL, 10);
667 len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
668 nc->name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
669 MultiByteToWideChar(CP_ACP, 0, str, -1, nc->name, len);
672 static void LoadSubstList(void)
674 FontSubst *psub, **ppsub;
676 DWORD valuelen, datalen, i = 0, type, dlen, vlen;
681 for(psub = substlist; psub;) {
683 HeapFree(GetProcessHeap(), 0, psub->to.name);
684 HeapFree(GetProcessHeap(), 0, psub->from.name);
687 HeapFree(GetProcessHeap(), 0, ptmp);
692 if(RegOpenKeyA(HKEY_LOCAL_MACHINE,
693 "Software\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes",
694 &hkey) == ERROR_SUCCESS) {
696 RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
697 &valuelen, &datalen, NULL, NULL);
699 valuelen++; /* returned value doesn't include room for '\0' */
700 value = HeapAlloc(GetProcessHeap(), 0, valuelen * sizeof(CHAR));
701 data = HeapAlloc(GetProcessHeap(), 0, datalen);
706 while(RegEnumValueA(hkey, i++, value, &vlen, NULL, &type, data,
707 &dlen) == ERROR_SUCCESS) {
708 TRACE("Got %s=%s\n", debugstr_a(value), debugstr_a(data));
710 *ppsub = HeapAlloc(GetProcessHeap(), 0, sizeof(**ppsub));
711 (*ppsub)->next = NULL;
712 split_subst_info(&((*ppsub)->from), value);
713 split_subst_info(&((*ppsub)->to), data);
715 /* Win 2000 doesn't allow mapping between different charsets
716 or mapping of DEFAULT_CHARSET */
717 if(((*ppsub)->to.charset != (*ppsub)->from.charset) ||
718 (*ppsub)->to.charset == DEFAULT_CHARSET) {
719 HeapFree(GetProcessHeap(), 0, (*ppsub)->to.name);
720 HeapFree(GetProcessHeap(), 0, (*ppsub)->from.name);
721 HeapFree(GetProcessHeap(), 0, *ppsub);
724 ppsub = &((*ppsub)->next);
726 /* reset dlen and vlen */
730 HeapFree(GetProcessHeap(), 0, data);
731 HeapFree(GetProcessHeap(), 0, value);
736 /***********************************************************
737 * The replacement list is a way to map an entire font
738 * family onto another family. For example adding
740 * [HKCU\Software\Wine\Fonts\Replacements]
741 * "Wingdings"="Winedings"
743 * would enumerate the Winedings font both as Winedings and
744 * Wingdings. However if a real Wingdings font is present the
745 * replacement does not take place.
748 static void LoadReplaceList(void)
751 DWORD valuelen, datalen, i = 0, type, dlen, vlen;
756 struct list *family_elem_ptr, *face_elem_ptr;
757 WCHAR old_nameW[200];
759 /* @@ Wine registry key: HKCU\Software\Wine\Fonts\Replacements */
760 if(RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Fonts\\Replacements", &hkey) == ERROR_SUCCESS)
762 RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
763 &valuelen, &datalen, NULL, NULL);
765 valuelen++; /* returned value doesn't include room for '\0' */
766 value = HeapAlloc(GetProcessHeap(), 0, valuelen * sizeof(CHAR));
767 data = HeapAlloc(GetProcessHeap(), 0, datalen);
771 while(RegEnumValueA(hkey, i++, value, &vlen, NULL, &type, data,
772 &dlen) == ERROR_SUCCESS) {
773 TRACE("Got %s=%s\n", debugstr_a(value), debugstr_a(data));
774 /* "NewName"="Oldname" */
775 if(!MultiByteToWideChar(CP_ACP, 0, data, -1, old_nameW, sizeof(old_nameW)))
778 /* Find the old family and hence all of the font files
780 LIST_FOR_EACH(family_elem_ptr, &font_list) {
781 family = LIST_ENTRY(family_elem_ptr, Family, entry);
782 if(!strcmpiW(family->FamilyName, old_nameW)) {
783 LIST_FOR_EACH(face_elem_ptr, &family->faces) {
784 face = LIST_ENTRY(face_elem_ptr, Face, entry);
785 TRACE("mapping %s %s to %s\n", debugstr_w(family->FamilyName),
786 debugstr_w(face->StyleName), value);
787 /* Now add a new entry with the new family name */
788 AddFontFileToList(face->file, value, ADDFONT_FORCE_BITMAP | (face->external ? ADDFONT_EXTERNAL_FONT : 0));
793 /* reset dlen and vlen */
797 HeapFree(GetProcessHeap(), 0, data);
798 HeapFree(GetProcessHeap(), 0, value);
804 static BOOL ReadFontDir(const char *dirname, BOOL external_fonts)
810 TRACE("Loading fonts from %s\n", debugstr_a(dirname));
812 dir = opendir(dirname);
814 ERR("Can't open directory %s\n", debugstr_a(dirname));
817 while((dent = readdir(dir)) != NULL) {
820 if(!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
823 TRACE("Found %s in %s\n", debugstr_a(dent->d_name), debugstr_a(dirname));
825 sprintf(path, "%s/%s", dirname, dent->d_name);
827 if(stat(path, &statbuf) == -1)
829 WARN("Can't stat %s\n", debugstr_a(path));
832 if(S_ISDIR(statbuf.st_mode))
833 ReadFontDir(path, external_fonts);
835 AddFontFileToList(path, NULL, external_fonts ? ADDFONT_EXTERNAL_FONT : 0);
841 static void load_fontconfig_fonts(void)
843 #ifdef HAVE_FONTCONFIG_FONTCONFIG_H
844 void *fc_handle = NULL;
851 const char *file, *ext;
853 fc_handle = wine_dlopen(SONAME_LIBFONTCONFIG, RTLD_NOW, NULL, 0);
855 TRACE("Wine cannot find the fontconfig library (%s).\n",
856 SONAME_LIBFONTCONFIG);
859 #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;}
860 LOAD_FUNCPTR(FcConfigGetCurrent);
861 LOAD_FUNCPTR(FcFontList);
862 LOAD_FUNCPTR(FcFontSetDestroy);
863 LOAD_FUNCPTR(FcInit);
864 LOAD_FUNCPTR(FcObjectSetAdd);
865 LOAD_FUNCPTR(FcObjectSetCreate);
866 LOAD_FUNCPTR(FcObjectSetDestroy);
867 LOAD_FUNCPTR(FcPatternCreate);
868 LOAD_FUNCPTR(FcPatternDestroy);
869 LOAD_FUNCPTR(FcPatternGet);
872 if(!pFcInit()) return;
874 config = pFcConfigGetCurrent();
875 pat = pFcPatternCreate();
876 os = pFcObjectSetCreate();
877 pFcObjectSetAdd(os, FC_FILE);
878 fontset = pFcFontList(config, pat, os);
880 for(i = 0; i < fontset->nfont; i++) {
881 if(pFcPatternGet(fontset->fonts[i], FC_FILE, 0, &v) != FcResultMatch)
883 if(v.type != FcTypeString) continue;
884 file = (LPCSTR) v.u.s;
885 TRACE("fontconfig: %s\n", file);
887 /* We're just interested in OT/TT fonts for now, so this hack just
888 picks up the standard extensions to save time loading every other
890 len = strlen( file );
891 if(len < 4) continue;
892 ext = &file[ len - 3 ];
893 if(!strcasecmp(ext, "ttf") || !strcasecmp(ext, "ttc") || !strcasecmp(ext, "otf"))
894 AddFontFileToList(file, NULL, ADDFONT_EXTERNAL_FONT);
896 pFcFontSetDestroy(fontset);
897 pFcObjectSetDestroy(os);
898 pFcPatternDestroy(pat);
905 static void load_system_fonts(void)
908 WCHAR data[MAX_PATH], windowsdir[MAX_PATH], pathW[MAX_PATH];
911 static const WCHAR fmtW[] = {'%','s','\\','%','s','\0'};
914 if(RegOpenKeyW(HKEY_CURRENT_CONFIG, system_fonts_reg_key, &hkey) == ERROR_SUCCESS) {
915 GetWindowsDirectoryW(windowsdir, sizeof(windowsdir) / sizeof(WCHAR));
916 strcatW(windowsdir, fontsW);
917 for(value = SystemFontValues; *value; value++) {
919 if(RegQueryValueExW(hkey, *value, 0, &type, (void*)data, &dlen) == ERROR_SUCCESS &&
921 sprintfW(pathW, fmtW, windowsdir, data);
922 if((unixname = wine_get_unix_file_name(pathW))) {
923 AddFontFileToList(unixname, NULL, ADDFONT_FORCE_BITMAP);
924 HeapFree(GetProcessHeap(), 0, unixname);
932 /*************************************************************
934 * This adds registry entries for any externally loaded fonts
935 * (fonts from fontconfig or FontDirs). It also deletes entries
936 * of no longer existing fonts.
939 static void update_reg_entries(void)
941 HKEY winkey = 0, externalkey = 0;
944 DWORD dlen, vlen, datalen, valuelen, i, type, len, len_fam;
947 struct list *family_elem_ptr, *face_elem_ptr;
949 static const WCHAR TrueType[] = {' ','(','T','r','u','e','T','y','p','e',')','\0'};
950 static const WCHAR spaceW[] = {' ', '\0'};
953 if(RegCreateKeyExW(HKEY_LOCAL_MACHINE, is_win9x() ? win9x_font_reg_key : winnt_font_reg_key,
954 0, NULL, 0, KEY_ALL_ACCESS, NULL, &winkey, NULL) != ERROR_SUCCESS) {
955 ERR("Can't create Windows font reg key\n");
958 /* @@ Wine registry key: HKCU\Software\Wine\Fonts\ExternalFonts */
959 if(RegCreateKeyW(HKEY_CURRENT_USER, external_fonts_reg_key, &externalkey) != ERROR_SUCCESS) {
960 ERR("Can't create external font reg key\n");
964 /* Delete all external fonts added last time */
966 RegQueryInfoKeyW(externalkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
967 &valuelen, &datalen, NULL, NULL);
968 valuelen++; /* returned value doesn't include room for '\0' */
969 valueW = HeapAlloc(GetProcessHeap(), 0, valuelen * sizeof(WCHAR));
970 data = HeapAlloc(GetProcessHeap(), 0, datalen * sizeof(WCHAR));
972 dlen = datalen * sizeof(WCHAR);
975 while(RegEnumValueW(externalkey, i++, valueW, &vlen, NULL, &type, data,
976 &dlen) == ERROR_SUCCESS) {
978 RegDeleteValueW(winkey, valueW);
979 /* reset dlen and vlen */
983 HeapFree(GetProcessHeap(), 0, data);
984 HeapFree(GetProcessHeap(), 0, valueW);
986 /* Delete the old external fonts key */
987 RegCloseKey(externalkey);
989 RegDeleteKeyW(HKEY_CURRENT_USER, external_fonts_reg_key);
991 if(RegCreateKeyExW(HKEY_CURRENT_USER, external_fonts_reg_key,
992 0, NULL, 0, KEY_ALL_ACCESS, NULL, &externalkey, NULL) != ERROR_SUCCESS) {
993 ERR("Can't create external font reg key\n");
997 /* enumerate the fonts and add external ones to the two keys */
999 LIST_FOR_EACH(family_elem_ptr, &font_list) {
1000 family = LIST_ENTRY(family_elem_ptr, Family, entry);
1001 len_fam = strlenW(family->FamilyName) + sizeof(TrueType) / sizeof(WCHAR) + 1;
1002 LIST_FOR_EACH(face_elem_ptr, &family->faces) {
1003 face = LIST_ENTRY(face_elem_ptr, Face, entry);
1004 if(!face->external) continue;
1006 if(strcmpiW(face->StyleName, RegularW))
1007 len = len_fam + strlenW(face->StyleName) + 1;
1008 valueW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1009 strcpyW(valueW, family->FamilyName);
1010 if(len != len_fam) {
1011 strcatW(valueW, spaceW);
1012 strcatW(valueW, face->StyleName);
1014 strcatW(valueW, TrueType);
1015 if((path = strrchr(face->file, '/')) == NULL)
1019 len = MultiByteToWideChar(CP_ACP, 0, path, -1, NULL, 0);
1021 file = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
1022 MultiByteToWideChar(CP_ACP, 0, path, -1, file, len);
1023 RegSetValueExW(winkey, valueW, 0, REG_SZ, (BYTE*)file, len * sizeof(WCHAR));
1024 RegSetValueExW(externalkey, valueW, 0, REG_SZ, (BYTE*)file, len * sizeof(WCHAR));
1026 HeapFree(GetProcessHeap(), 0, file);
1027 HeapFree(GetProcessHeap(), 0, valueW);
1032 RegCloseKey(externalkey);
1034 RegCloseKey(winkey);
1039 /*************************************************************
1040 * WineEngAddFontResourceEx
1043 INT WineEngAddFontResourceEx(LPCWSTR file, DWORD flags, PVOID pdv)
1045 if (ft_handle) /* do it only if we have freetype up and running */
1050 FIXME("Ignoring flags %lx\n", flags);
1052 if((unixname = wine_get_unix_file_name(file)))
1054 AddFontFileToList(unixname, NULL, ADDFONT_FORCE_BITMAP);
1055 HeapFree(GetProcessHeap(), 0, unixname);
1061 /*************************************************************
1062 * WineEngRemoveFontResourceEx
1065 BOOL WineEngRemoveFontResourceEx(LPCWSTR file, DWORD flags, PVOID pdv)
1071 /*************************************************************
1074 * Initialize FreeType library and create a list of available faces
1076 BOOL WineEngInit(void)
1078 static const WCHAR dot_fonW[] = {'.','f','o','n','\0'};
1079 static const WCHAR pathW[] = {'P','a','t','h',0};
1081 DWORD valuelen, datalen, i = 0, type, dlen, vlen;
1083 WCHAR windowsdir[MAX_PATH];
1089 ft_handle = wine_dlopen(SONAME_LIBFREETYPE, RTLD_NOW, NULL, 0);
1092 "Wine cannot find the FreeType font library. To enable Wine to\n"
1093 "use TrueType fonts please install a version of FreeType greater than\n"
1094 "or equal to 2.0.5.\n"
1095 "http://www.freetype.org\n");
1099 #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;}
1101 LOAD_FUNCPTR(FT_Vector_Unit)
1102 LOAD_FUNCPTR(FT_Done_Face)
1103 LOAD_FUNCPTR(FT_Get_Char_Index)
1104 LOAD_FUNCPTR(FT_Get_Sfnt_Table)
1105 LOAD_FUNCPTR(FT_Init_FreeType)
1106 LOAD_FUNCPTR(FT_Load_Glyph)
1107 LOAD_FUNCPTR(FT_Matrix_Multiply)
1108 LOAD_FUNCPTR(FT_MulFix)
1109 LOAD_FUNCPTR(FT_New_Face)
1110 LOAD_FUNCPTR(FT_Outline_Get_Bitmap)
1111 LOAD_FUNCPTR(FT_Outline_Transform)
1112 LOAD_FUNCPTR(FT_Outline_Translate)
1113 LOAD_FUNCPTR(FT_Select_Charmap)
1114 LOAD_FUNCPTR(FT_Set_Pixel_Sizes)
1115 LOAD_FUNCPTR(FT_Vector_Transform)
1118 /* Don't warn if this one is missing */
1119 pFT_Library_Version = wine_dlsym(ft_handle, "FT_Library_Version", NULL, 0);
1120 pFT_Load_Sfnt_Table = wine_dlsym(ft_handle, "FT_Load_Sfnt_Table", NULL, 0);
1121 pFT_Get_First_Char = wine_dlsym(ft_handle, "FT_Get_First_Char", NULL, 0);
1122 #ifdef HAVE_FREETYPE_FTWINFNT_H
1123 pFT_Get_WinFNT_Header = wine_dlsym(ft_handle, "FT_Get_WinFNT_Header", NULL, 0);
1125 if(!wine_dlsym(ft_handle, "FT_Get_Postscript_Name", NULL, 0) &&
1126 !wine_dlsym(ft_handle, "FT_Sqrt64", NULL, 0)) {
1127 /* try to avoid 2.0.4: >= 2.0.5 has FT_Get_Postscript_Name and
1128 <= 2.0.3 has FT_Sqrt64 */
1132 if(pFT_Init_FreeType(&library) != 0) {
1133 ERR("Can't init FreeType library\n");
1134 wine_dlclose(ft_handle, NULL, 0);
1138 FT_Version.major=FT_Version.minor=FT_Version.patch=-1;
1139 if (pFT_Library_Version)
1141 pFT_Library_Version(library,&FT_Version.major,&FT_Version.minor,&FT_Version.patch);
1143 if (FT_Version.major<=0)
1149 TRACE("FreeType version is %d.%d.%d\n",FT_Version.major,FT_Version.minor,FT_Version.patch);
1150 FT_SimpleVersion = ((FT_Version.major << 16) & 0xff0000) |
1151 ((FT_Version.minor << 8) & 0x00ff00) |
1152 ((FT_Version.patch ) & 0x0000ff);
1154 if((font_mutex = CreateMutexW(NULL, FALSE, font_mutex_nameW)) == NULL) {
1155 ERR("Failed to create font mutex\n");
1158 WaitForSingleObject(font_mutex, INFINITE);
1160 /* load the system fonts */
1161 load_system_fonts();
1163 /* load in the fonts from %WINDOWSDIR%\\Fonts first of all */
1164 GetWindowsDirectoryW(windowsdir, sizeof(windowsdir) / sizeof(WCHAR));
1165 strcatW(windowsdir, fontsW);
1166 if((unixname = wine_get_unix_file_name(windowsdir)))
1168 ReadFontDir(unixname, FALSE);
1169 HeapFree(GetProcessHeap(), 0, unixname);
1172 /* now look under HKLM\Software\Microsoft\Windows[ NT]\CurrentVersion\Fonts
1173 for any fonts not installed in %WINDOWSDIR%\Fonts. They will have their
1174 full path as the entry. Also look for any .fon fonts, since ReadFontDir
1176 if(RegOpenKeyW(HKEY_LOCAL_MACHINE,
1177 is_win9x() ? win9x_font_reg_key : winnt_font_reg_key,
1178 &hkey) == ERROR_SUCCESS) {
1180 RegQueryInfoKeyW(hkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1181 &valuelen, &datalen, NULL, NULL);
1183 valuelen++; /* returned value doesn't include room for '\0' */
1184 valueW = HeapAlloc(GetProcessHeap(), 0, valuelen * sizeof(WCHAR));
1185 data = HeapAlloc(GetProcessHeap(), 0, datalen * sizeof(WCHAR));
1188 dlen = datalen * sizeof(WCHAR);
1190 while(RegEnumValueW(hkey, i++, valueW, &vlen, NULL, &type, data,
1191 &dlen) == ERROR_SUCCESS) {
1192 if(((LPWSTR)data)[0] && ((LPWSTR)data)[1] == ':')
1194 if((unixname = wine_get_unix_file_name((LPWSTR)data)))
1196 AddFontFileToList(unixname, NULL, ADDFONT_FORCE_BITMAP);
1197 HeapFree(GetProcessHeap(), 0, unixname);
1200 else if(dlen / 2 >= 6 && !strcmpiW(((LPWSTR)data) + dlen / 2 - 5, dot_fonW))
1202 WCHAR pathW[MAX_PATH];
1203 static const WCHAR fmtW[] = {'%','s','\\','%','s','\0'};
1204 sprintfW(pathW, fmtW, windowsdir, data);
1205 if((unixname = wine_get_unix_file_name(pathW)))
1207 AddFontFileToList(unixname, NULL, ADDFONT_FORCE_BITMAP);
1208 HeapFree(GetProcessHeap(), 0, unixname);
1211 /* reset dlen and vlen */
1216 HeapFree(GetProcessHeap(), 0, data);
1217 HeapFree(GetProcessHeap(), 0, valueW);
1221 load_fontconfig_fonts();
1223 /* then look in any directories that we've specified in the config file */
1224 /* @@ Wine registry key: HKCU\Software\Wine\Fonts */
1225 if(RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Fonts", &hkey) == ERROR_SUCCESS)
1231 if (RegQueryValueExW( hkey, pathW, NULL, NULL, NULL, &len ) == ERROR_SUCCESS)
1233 len += sizeof(WCHAR);
1234 valueW = HeapAlloc( GetProcessHeap(), 0, len );
1235 if (RegQueryValueExW( hkey, pathW, NULL, NULL, (LPBYTE)valueW, &len ) == ERROR_SUCCESS)
1237 len = WideCharToMultiByte( CP_UNIXCP, 0, valueW, -1, NULL, 0, NULL, NULL );
1238 valueA = HeapAlloc( GetProcessHeap(), 0, len );
1239 WideCharToMultiByte( CP_UNIXCP, 0, valueW, -1, valueA, len, NULL, NULL );
1240 TRACE( "got font path %s\n", debugstr_a(valueA) );
1244 LPSTR next = strchr( ptr, ':' );
1245 if (next) *next++ = 0;
1246 ReadFontDir( ptr, TRUE );
1249 HeapFree( GetProcessHeap(), 0, valueA );
1251 HeapFree( GetProcessHeap(), 0, valueW );
1260 update_reg_entries();
1262 ReleaseMutex(font_mutex);
1266 "Wine cannot find certain functions that it needs inside the FreeType\n"
1267 "font library. To enable Wine to use TrueType fonts please upgrade\n"
1268 "FreeType to at least version 2.0.5.\n"
1269 "http://www.freetype.org\n");
1270 wine_dlclose(ft_handle, NULL, 0);
1276 static LONG calc_ppem_for_height(FT_Face ft_face, LONG height)
1279 TT_HoriHeader *pHori;
1283 pOS2 = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_os2);
1284 pHori = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_hhea);
1286 if(height == 0) height = 16;
1288 /* Calc. height of EM square:
1290 * For +ve lfHeight we have
1291 * lfHeight = (winAscent + winDescent) * ppem / units_per_em
1292 * Re-arranging gives:
1293 * ppem = units_per_em * lfheight / (winAscent + winDescent)
1295 * For -ve lfHeight we have
1297 * [i.e. |lfHeight| = (winAscent + winDescent - il) * ppem / units_per_em
1298 * with il = winAscent + winDescent - units_per_em]
1303 if(pOS2->usWinAscent + pOS2->usWinDescent == 0)
1304 ppem = ft_face->units_per_EM * height /
1305 (pHori->Ascender - pHori->Descender);
1307 ppem = ft_face->units_per_EM * height /
1308 (pOS2->usWinAscent + pOS2->usWinDescent);
1316 static LONG load_VDMX(GdiFont, LONG);
1318 static FT_Face OpenFontFile(GdiFont font, char *file, FT_Long face_index, LONG width, LONG height)
1324 err = pFT_New_Face(library, file, face_index, &ft_face);
1326 ERR("FT_New_Face rets %d\n", err);
1330 /* set it here, as load_VDMX needs it */
1331 font->ft_face = ft_face;
1333 if(FT_IS_SCALABLE(ft_face)) {
1334 /* load the VDMX table if we have one */
1335 ppem = load_VDMX(font, height);
1337 ppem = calc_ppem_for_height(ft_face, height);
1339 if((err = pFT_Set_Pixel_Sizes(ft_face, 0, ppem)) != 0)
1340 WARN("FT_Set_Pixel_Sizes %d, %ld rets %x\n", 0, ppem, err);
1342 if((err = pFT_Set_Pixel_Sizes(ft_face, width, height)) != 0)
1343 WARN("FT_Set_Pixel_Sizes %ld, %ld rets %x\n", width, height, err);
1349 static int get_nearest_charset(Face *face, int *cp)
1351 /* Only get here if lfCharSet == DEFAULT_CHARSET or we couldn't find
1352 a single face with the requested charset. The idea is to check if
1353 the selected font supports the current ANSI codepage, if it does
1354 return the corresponding charset, else return the first charset */
1357 int acp = GetACP(), i;
1361 if(TranslateCharsetInfo((DWORD*)acp, &csi, TCI_SRCCODEPAGE))
1362 if(csi.fs.fsCsb[0] & face->fs.fsCsb[0])
1363 return csi.ciCharset;
1365 for(i = 0; i < 32; i++) {
1367 if(face->fs.fsCsb[0] & fs0) {
1368 if(TranslateCharsetInfo(&fs0, &csi, TCI_SRCFONTSIG)) {
1370 return csi.ciCharset;
1373 FIXME("TCI failing on %lx\n", fs0);
1377 FIXME("returning DEFAULT_CHARSET face->fs.fsCsb[0] = %08lx file = %s\n",
1378 face->fs.fsCsb[0], face->file);
1380 return DEFAULT_CHARSET;
1383 static GdiFont alloc_font(void)
1385 GdiFont ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*ret));
1386 ret->gmsize = INIT_GM_SIZE;
1387 ret->gm = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1388 ret->gmsize * sizeof(*ret->gm));
1390 ret->font_desc.matrix.eM11 = ret->font_desc.matrix.eM22 = 1.0;
1391 list_init(&ret->hfontlist);
1395 static void free_font(GdiFont font)
1397 if (font->ft_face) pFT_Done_Face(font->ft_face);
1398 HeapFree(GetProcessHeap(), 0, font->potm);
1399 HeapFree(GetProcessHeap(), 0, font->name);
1400 HeapFree(GetProcessHeap(), 0, font->gm);
1401 HeapFree(GetProcessHeap(), 0, font);
1405 /*************************************************************
1408 * load the vdmx entry for the specified height
1411 #define MS_MAKE_TAG( _x1, _x2, _x3, _x4 ) \
1412 ( ( (FT_ULong)_x4 << 24 ) | \
1413 ( (FT_ULong)_x3 << 16 ) | \
1414 ( (FT_ULong)_x2 << 8 ) | \
1417 #define MS_VDMX_TAG MS_MAKE_TAG('V', 'D', 'M', 'X')
1427 static LONG load_VDMX(GdiFont font, LONG height)
1429 BYTE hdr[6], tmp[2], group[4];
1430 BYTE devXRatio, devYRatio;
1431 USHORT numRecs, numRatios;
1432 DWORD result, offset = -1;
1436 /* For documentation on VDMX records, see
1437 * http://www.microsoft.com/OpenType/OTSpec/vdmx.htm
1440 result = WineEngGetFontData(font, MS_VDMX_TAG, 0, hdr, 6);
1442 if(result == GDI_ERROR) /* no vdmx table present, use linear scaling */
1445 /* FIXME: need the real device aspect ratio */
1449 numRecs = GET_BE_WORD(&hdr[2]);
1450 numRatios = GET_BE_WORD(&hdr[4]);
1452 TRACE("numRecs = %d numRatios = %d\n", numRecs, numRatios);
1453 for(i = 0; i < numRatios; i++) {
1456 offset = (3 * 2) + (i * sizeof(Ratios));
1457 WineEngGetFontData(font, MS_VDMX_TAG, offset, &ratio, sizeof(Ratios));
1460 TRACE("Ratios[%d] %d %d : %d -> %d\n", i, ratio.bCharSet, ratio.xRatio, ratio.yStartRatio, ratio.yEndRatio);
1462 if((ratio.xRatio == 0 &&
1463 ratio.yStartRatio == 0 &&
1464 ratio.yEndRatio == 0) ||
1465 (devXRatio == ratio.xRatio &&
1466 devYRatio >= ratio.yStartRatio &&
1467 devYRatio <= ratio.yEndRatio))
1469 offset = (3 * 2) + (numRatios * 4) + (i * 2);
1470 WineEngGetFontData(font, MS_VDMX_TAG, offset, tmp, 2);
1471 offset = GET_BE_WORD(tmp);
1477 FIXME("No suitable ratio found\n");
1481 if(WineEngGetFontData(font, MS_VDMX_TAG, offset, group, 4) != GDI_ERROR) {
1483 BYTE startsz, endsz;
1486 recs = GET_BE_WORD(group);
1490 TRACE("recs=%d startsz=%d endsz=%d\n", recs, startsz, endsz);
1492 vTable = HeapAlloc(GetProcessHeap(), 0, recs * 6);
1493 result = WineEngGetFontData(font, MS_VDMX_TAG, offset + 4, vTable, recs * 6);
1494 if(result == GDI_ERROR) {
1495 FIXME("Failed to retrieve vTable\n");
1500 for(i = 0; i < recs; i++) {
1501 SHORT yMax = GET_BE_WORD(&vTable[(i * 6) + 2]);
1502 SHORT yMin = GET_BE_WORD(&vTable[(i * 6) + 4]);
1503 ppem = GET_BE_WORD(&vTable[i * 6]);
1505 if(yMax + -yMin == height) {
1508 TRACE("ppem %ld found; height=%ld yMax=%d yMin=%d\n", ppem, height, font->yMax, font->yMin);
1511 if(yMax + -yMin > height) {
1514 goto end; /* failed */
1516 font->yMax = GET_BE_WORD(&vTable[(i * 6) + 2]);
1517 font->yMin = GET_BE_WORD(&vTable[(i * 6) + 4]);
1518 TRACE("ppem %ld found; height=%ld yMax=%d yMin=%d\n", ppem, height, font->yMax, font->yMin);
1524 TRACE("ppem not found for height %ld\n", height);
1528 if(ppem < startsz || ppem > endsz)
1531 for(i = 0; i < recs; i++) {
1533 yPelHeight = GET_BE_WORD(&vTable[i * 6]);
1535 if(yPelHeight > ppem)
1538 if(yPelHeight == ppem) {
1539 font->yMax = GET_BE_WORD(&vTable[(i * 6) + 2]);
1540 font->yMin = GET_BE_WORD(&vTable[(i * 6) + 4]);
1541 TRACE("ppem %ld found; yMax=%d yMin=%d\n", ppem, font->yMax, font->yMin);
1547 HeapFree(GetProcessHeap(), 0, vTable);
1553 static BOOL fontcmp(GdiFont font, FONT_DESC *fd)
1555 if(font->font_desc.hash != fd->hash) return TRUE;
1556 if(memcmp(&font->font_desc.matrix, &fd->matrix, sizeof(fd->matrix))) return TRUE;
1557 if(memcmp(&font->font_desc.lf, &fd->lf, offsetof(LOGFONTW, lfFaceName))) return TRUE;
1558 return strcmpiW(font->font_desc.lf.lfFaceName, fd->lf.lfFaceName);
1561 static void calc_hash(FONT_DESC *pfd)
1563 DWORD hash = 0, *ptr, two_chars;
1567 for(i = 0, ptr = (DWORD*)&pfd->matrix; i < sizeof(FMAT2)/sizeof(DWORD); i++, ptr++)
1569 for(i = 0, ptr = (DWORD*)&pfd->lf; i < 7; i++, ptr++)
1571 for(i = 0, ptr = (DWORD*)&pfd->lf.lfFaceName; i < LF_FACESIZE/2; i++, ptr++) {
1573 pwc = (WCHAR *)&two_chars;
1575 *pwc = toupperW(*pwc);
1577 *pwc = toupperW(*pwc);
1585 static GdiFont find_in_cache(HFONT hfont, LOGFONTW *plf, XFORM *pxf, BOOL can_use_bitmap)
1590 struct list *font_elem_ptr, *hfontlist_elem_ptr;
1592 memcpy(&fd.lf, plf, sizeof(LOGFONTW));
1593 memcpy(&fd.matrix, pxf, sizeof(FMAT2));
1596 /* try the in-use list */
1597 LIST_FOR_EACH(font_elem_ptr, &gdi_font_list) {
1598 ret = LIST_ENTRY(font_elem_ptr, struct tagGdiFont, entry);
1599 if(!fontcmp(ret, &fd)) {
1600 if(!can_use_bitmap && !FT_IS_SCALABLE(ret->ft_face)) continue;
1601 LIST_FOR_EACH(hfontlist_elem_ptr, &ret->hfontlist) {
1602 hflist = LIST_ENTRY(hfontlist_elem_ptr, struct tagHFONTLIST, entry);
1603 if(hflist->hfont == hfont)
1606 hflist = HeapAlloc(GetProcessHeap(), 0, sizeof(*hflist));
1607 hflist->hfont = hfont;
1608 list_add_head(&ret->hfontlist, &hflist->entry);
1613 /* then the unused list */
1614 font_elem_ptr = list_head(&unused_gdi_font_list);
1615 while(font_elem_ptr) {
1616 ret = LIST_ENTRY(font_elem_ptr, struct tagGdiFont, entry);
1617 font_elem_ptr = list_next(&unused_gdi_font_list, font_elem_ptr);
1618 if(!fontcmp(ret, &fd)) {
1619 if(!can_use_bitmap && !FT_IS_SCALABLE(ret->ft_face)) continue;
1620 assert(list_empty(&ret->hfontlist));
1621 TRACE("Found %p in unused list\n", ret);
1622 list_remove(&ret->entry);
1623 list_add_head(&gdi_font_list, &ret->entry);
1624 hflist = HeapAlloc(GetProcessHeap(), 0, sizeof(*hflist));
1625 hflist->hfont = hfont;
1626 list_add_head(&ret->hfontlist, &hflist->entry);
1633 /*************************************************************
1634 * WineEngCreateFontInstance
1637 GdiFont WineEngCreateFontInstance(DC *dc, HFONT hfont)
1642 struct list *family_elem_ptr, *face_elem_ptr;
1643 INT height, width = 0;
1644 signed int diff = 0, newdiff;
1645 BOOL bd, it, can_use_bitmap;
1650 if (!GetObjectW( hfont, sizeof(lf), &lf )) return NULL;
1651 can_use_bitmap = GetDeviceCaps(dc->hSelf, TEXTCAPS) & TC_RA_ABLE;
1653 TRACE("%s, h=%ld, it=%d, weight=%ld, PandF=%02x, charset=%d orient %ld escapement %ld\n",
1654 debugstr_w(lf.lfFaceName), lf.lfHeight, lf.lfItalic,
1655 lf.lfWeight, lf.lfPitchAndFamily, lf.lfCharSet, lf.lfOrientation,
1658 /* check the cache first */
1659 if((ret = find_in_cache(hfont, &lf, &dc->xformWorld2Vport, can_use_bitmap)) != NULL) {
1660 TRACE("returning cached gdiFont(%p) for hFont %p\n", ret, hfont);
1664 TRACE("not in cache\n");
1665 if(list_empty(&font_list) || !have_installed_roman_font) /* No fonts installed */
1667 TRACE("No fonts installed\n");
1673 memcpy(&ret->font_desc.matrix, &dc->xformWorld2Vport, sizeof(FMAT2));
1674 memcpy(&ret->font_desc.lf, &lf, sizeof(LOGFONTW));
1675 calc_hash(&ret->font_desc);
1676 hflist = HeapAlloc(GetProcessHeap(), 0, sizeof(*hflist));
1677 hflist->hfont = hfont;
1678 list_add_head(&ret->hfontlist, &hflist->entry);
1681 /* If lfFaceName is "Symbol" then Windows fixes up lfCharSet to
1682 SYMBOL_CHARSET so that Symbol gets picked irrespective of the
1683 original value lfCharSet. Note this is a special case for
1684 Symbol and doesn't happen at least for "Wingdings*" */
1686 if(!strcmpiW(lf.lfFaceName, SymbolW))
1687 lf.lfCharSet = SYMBOL_CHARSET;
1689 if(!TranslateCharsetInfo((DWORD*)(INT)lf.lfCharSet, &csi, TCI_SRCCHARSET)) {
1690 switch(lf.lfCharSet) {
1691 case DEFAULT_CHARSET:
1692 csi.fs.fsCsb[0] = 0;
1695 FIXME("Untranslated charset %d\n", lf.lfCharSet);
1696 csi.fs.fsCsb[0] = 0;
1702 if(lf.lfFaceName[0] != '\0') {
1704 for(psub = substlist; psub; psub = psub->next)
1705 if(!strcmpiW(lf.lfFaceName, psub->from.name) &&
1706 (psub->from.charset == -1 ||
1707 psub->from.charset == lf.lfCharSet))
1710 TRACE("substituting %s -> %s\n", debugstr_w(lf.lfFaceName),
1711 debugstr_w(psub->to.name));
1712 strcpyW(lf.lfFaceName, psub->to.name);
1715 /* We want a match on name and charset or just name if
1716 charset was DEFAULT_CHARSET. If the latter then
1717 we fixup the returned charset later in get_nearest_charset
1718 where we'll either use the charset of the current ansi codepage
1719 or if that's unavailable the first charset that the font supports.
1721 LIST_FOR_EACH(family_elem_ptr, &font_list) {
1722 family = LIST_ENTRY(family_elem_ptr, Family, entry);
1723 if(!strcmpiW(family->FamilyName, lf.lfFaceName)) {
1724 face_elem_ptr = list_head(&family->faces);
1725 face = LIST_ENTRY(face_elem_ptr, Face, entry);
1726 if((csi.fs.fsCsb[0] & face->fs.fsCsb[0]) || !csi.fs.fsCsb[0])
1727 if(face->scalable || can_use_bitmap)
1735 /* If requested charset was DEFAULT_CHARSET then try using charset
1736 corresponding to the current ansi codepage */
1737 if(!csi.fs.fsCsb[0]) {
1739 if(!TranslateCharsetInfo((DWORD*)acp, &csi, TCI_SRCCODEPAGE)) {
1740 FIXME("TCI failed on codepage %d\n", acp);
1741 csi.fs.fsCsb[0] = 0;
1743 lf.lfCharSet = csi.ciCharset;
1746 /* Face families are in the top 4 bits of lfPitchAndFamily,
1747 so mask with 0xF0 before testing */
1749 if((lf.lfPitchAndFamily & FIXED_PITCH) ||
1750 (lf.lfPitchAndFamily & 0xF0) == FF_MODERN)
1751 strcpyW(lf.lfFaceName, defFixed);
1752 else if((lf.lfPitchAndFamily & 0xF0) == FF_ROMAN)
1753 strcpyW(lf.lfFaceName, defSerif);
1754 else if((lf.lfPitchAndFamily & 0xF0) == FF_SWISS)
1755 strcpyW(lf.lfFaceName, defSans);
1757 strcpyW(lf.lfFaceName, defSans);
1758 LIST_FOR_EACH(family_elem_ptr, &font_list) {
1759 family = LIST_ENTRY(family_elem_ptr, Family, entry);
1760 if(!strcmpiW(family->FamilyName, lf.lfFaceName)) {
1761 face_elem_ptr = list_head(&family->faces);
1762 face = LIST_ENTRY(face_elem_ptr, Face, entry);
1763 if(csi.fs.fsCsb[0] & face->fs.fsCsb[0])
1764 if(face->scalable || can_use_bitmap)
1772 LIST_FOR_EACH(family_elem_ptr, &font_list) {
1773 family = LIST_ENTRY(family_elem_ptr, Family, entry);
1774 face_elem_ptr = list_head(&family->faces);
1775 face = LIST_ENTRY(face_elem_ptr, Face, entry);
1776 if(csi.fs.fsCsb[0] & face->fs.fsCsb[0])
1777 if(face->scalable || can_use_bitmap)
1784 LIST_FOR_EACH(family_elem_ptr, &font_list) {
1785 family = LIST_ENTRY(family_elem_ptr, Family, entry);
1786 face_elem_ptr = list_head(&family->faces);
1787 face = LIST_ENTRY(face_elem_ptr, Face, entry);
1788 if(face->scalable || can_use_bitmap) {
1789 csi.fs.fsCsb[0] = 0;
1790 FIXME("just using first face for now\n");
1796 FIXME("can't find a single appropriate font - bailing\n");
1802 it = lf.lfItalic ? 1 : 0;
1803 bd = lf.lfWeight > 550 ? 1 : 0;
1805 height = GDI_ROUND( (FLOAT)lf.lfHeight * dc->xformWorld2Vport.eM22 );
1806 height = lf.lfHeight < 0 ? -abs(height) : abs(height);
1809 LIST_FOR_EACH(face_elem_ptr, &family->faces) {
1810 face = LIST_ENTRY(face_elem_ptr, Face, entry);
1811 if(!(face->Italic ^ it) && !(face->Bold ^ bd)) {
1815 newdiff = height - (signed int)(face->size.y_ppem >> 6);
1817 newdiff = -height - ((signed int)(face->size.y_ppem >> 6) - face->size.internal_leading);
1818 if(!best || (diff > 0 && newdiff < diff && newdiff >= 0) ||
1819 (diff < 0 && newdiff > diff)) {
1820 TRACE("%ld is better for %d diff was %d\n", face->size.y_ppem >> 6, height, diff);
1833 LIST_FOR_EACH(face_elem_ptr, &family->faces) {
1834 face = LIST_ENTRY(face_elem_ptr, Face, entry);
1838 newdiff = height - (signed int)(face->size.y_ppem >> 6);
1840 newdiff = -height - ((signed int)(face->size.y_ppem >> 6) - face->size.internal_leading);
1841 if(!best || (diff > 0 && newdiff < diff && newdiff >= 0) ||
1842 (diff < 0 && newdiff > diff)) {
1843 TRACE("%ld is better for %d diff was %d\n", face->size.y_ppem >> 6, height, diff);
1853 if(it && !face->Italic) ret->fake_italic = TRUE;
1854 if(bd && !face->Bold) ret->fake_bold = TRUE;
1857 memcpy(&ret->fs, &face->fs, sizeof(FONTSIGNATURE));
1859 if(csi.fs.fsCsb[0]) {
1860 ret->charset = lf.lfCharSet;
1861 ret->codepage = csi.ciACP;
1864 ret->charset = get_nearest_charset(face, &ret->codepage);
1866 TRACE("Chosen: %s %s\n", debugstr_w(family->FamilyName),
1867 debugstr_w(face->StyleName));
1869 if(!face->scalable) {
1870 width = face->size.x_ppem >> 6;
1871 height = face->size.y_ppem >> 6;
1873 ret->ft_face = OpenFontFile(ret, face->file, face->face_index, width, height);
1881 if (ret->charset == SYMBOL_CHARSET &&
1882 !pFT_Select_Charmap(ret->ft_face, FT_ENCODING_MS_SYMBOL)) {
1885 else if (!pFT_Select_Charmap(ret->ft_face, FT_ENCODING_UNICODE)) {
1889 pFT_Select_Charmap(ret->ft_face, FT_ENCODING_APPLE_ROMAN);
1892 ret->orientation = FT_IS_SCALABLE(ret->ft_face) ? lf.lfOrientation : 0;
1893 ret->name = strdupW(family->FamilyName);
1894 ret->underline = lf.lfUnderline ? 0xff : 0;
1895 ret->strikeout = lf.lfStrikeOut ? 0xff : 0;
1897 TRACE("caching: gdiFont=%p hfont=%p\n", ret, hfont);
1899 ret->aveWidth = FT_IS_SCALABLE(ret->ft_face) ? lf.lfWidth : 0;
1900 list_add_head(&gdi_font_list, &ret->entry);
1904 static void dump_gdi_font_list(void)
1907 struct list *elem_ptr;
1909 TRACE("---------- gdiFont Cache ----------\n");
1910 LIST_FOR_EACH(elem_ptr, &gdi_font_list) {
1911 gdiFont = LIST_ENTRY(elem_ptr, struct tagGdiFont, entry);
1912 TRACE("gdiFont=%p %s %ld\n",
1913 gdiFont, debugstr_w(gdiFont->font_desc.lf.lfFaceName), gdiFont->font_desc.lf.lfHeight);
1916 TRACE("---------- Unused gdiFont Cache ----------\n");
1917 LIST_FOR_EACH(elem_ptr, &unused_gdi_font_list) {
1918 gdiFont = LIST_ENTRY(elem_ptr, struct tagGdiFont, entry);
1919 TRACE("gdiFont=%p %s %ld\n",
1920 gdiFont, debugstr_w(gdiFont->font_desc.lf.lfFaceName), gdiFont->font_desc.lf.lfHeight);
1924 /*************************************************************
1925 * WineEngDestroyFontInstance
1927 * free the gdiFont associated with this handle
1930 BOOL WineEngDestroyFontInstance(HFONT handle)
1935 struct list *font_elem_ptr, *hfontlist_elem_ptr;
1938 TRACE("destroying hfont=%p\n", handle);
1940 dump_gdi_font_list();
1942 font_elem_ptr = list_head(&gdi_font_list);
1943 while(font_elem_ptr) {
1944 gdiFont = LIST_ENTRY(font_elem_ptr, struct tagGdiFont, entry);
1945 font_elem_ptr = list_next(&gdi_font_list, font_elem_ptr);
1947 hfontlist_elem_ptr = list_head(&gdiFont->hfontlist);
1948 while(hfontlist_elem_ptr) {
1949 hflist = LIST_ENTRY(hfontlist_elem_ptr, struct tagHFONTLIST, entry);
1950 hfontlist_elem_ptr = list_next(&gdiFont->hfontlist, hfontlist_elem_ptr);
1951 if(hflist->hfont == handle) {
1952 list_remove(&hflist->entry);
1953 HeapFree(GetProcessHeap(), 0, hflist);
1957 if(list_empty(&gdiFont->hfontlist)) {
1958 TRACE("Moving to Unused list\n");
1959 list_remove(&gdiFont->entry);
1960 list_add_head(&unused_gdi_font_list, &gdiFont->entry);
1965 font_elem_ptr = list_head(&unused_gdi_font_list);
1966 while(font_elem_ptr && i++ < UNUSED_CACHE_SIZE)
1967 font_elem_ptr = list_next(&unused_gdi_font_list, font_elem_ptr);
1968 while(font_elem_ptr) {
1969 gdiFont = LIST_ENTRY(font_elem_ptr, struct tagGdiFont, entry);
1970 font_elem_ptr = list_next(&unused_gdi_font_list, font_elem_ptr);
1971 TRACE("freeing %p\n", gdiFont);
1972 list_remove(&gdiFont->entry);
1978 static void GetEnumStructs(Face *face, LPENUMLOGFONTEXW pelf,
1979 NEWTEXTMETRICEXW *pntm, LPDWORD ptype)
1981 OUTLINETEXTMETRICW *potm = NULL;
1983 TEXTMETRICW tm, *ptm;
1984 GdiFont font = alloc_font();
1987 if(face->scalable) {
1991 height = face->size.y_ppem >> 6;
1992 width = face->size.x_ppem >> 6;
1995 if (!(font->ft_face = OpenFontFile(font, face->file, face->face_index, width, height)))
2001 font->name = strdupW(face->family->FamilyName);
2003 memset(&pelf->elfLogFont, 0, sizeof(LOGFONTW));
2005 size = WineEngGetOutlineTextMetrics(font, 0, NULL);
2007 potm = HeapAlloc(GetProcessHeap(), 0, size);
2008 WineEngGetOutlineTextMetrics(font, size, potm);
2009 ptm = (TEXTMETRICW*)&potm->otmTextMetrics;
2011 WineEngGetTextMetrics(font, &tm);
2015 pntm->ntmTm.tmHeight = pelf->elfLogFont.lfHeight = ptm->tmHeight;
2016 pntm->ntmTm.tmAscent = ptm->tmAscent;
2017 pntm->ntmTm.tmDescent = ptm->tmDescent;
2018 pntm->ntmTm.tmInternalLeading = ptm->tmInternalLeading;
2019 pntm->ntmTm.tmExternalLeading = ptm->tmExternalLeading;
2020 pntm->ntmTm.tmAveCharWidth = pelf->elfLogFont.lfWidth = ptm->tmAveCharWidth;
2021 pntm->ntmTm.tmMaxCharWidth = ptm->tmMaxCharWidth;
2022 pntm->ntmTm.tmWeight = pelf->elfLogFont.lfWeight = ptm->tmWeight;
2023 pntm->ntmTm.tmOverhang = ptm->tmOverhang;
2024 pntm->ntmTm.tmDigitizedAspectX = ptm->tmDigitizedAspectX;
2025 pntm->ntmTm.tmDigitizedAspectY = ptm->tmDigitizedAspectY;
2026 pntm->ntmTm.tmFirstChar = ptm->tmFirstChar;
2027 pntm->ntmTm.tmLastChar = ptm->tmLastChar;
2028 pntm->ntmTm.tmDefaultChar = ptm->tmDefaultChar;
2029 pntm->ntmTm.tmBreakChar = ptm->tmBreakChar;
2030 pntm->ntmTm.tmItalic = pelf->elfLogFont.lfItalic = ptm->tmItalic;
2031 pntm->ntmTm.tmUnderlined = pelf->elfLogFont.lfUnderline = ptm->tmUnderlined;
2032 pntm->ntmTm.tmStruckOut = pelf->elfLogFont.lfStrikeOut = ptm->tmStruckOut;
2033 pntm->ntmTm.tmPitchAndFamily = ptm->tmPitchAndFamily;
2034 pelf->elfLogFont.lfPitchAndFamily = (ptm->tmPitchAndFamily & 0xf1) + 1;
2035 pntm->ntmTm.tmCharSet = pelf->elfLogFont.lfCharSet = ptm->tmCharSet;
2036 pelf->elfLogFont.lfOutPrecision = OUT_STROKE_PRECIS;
2037 pelf->elfLogFont.lfClipPrecision = CLIP_STROKE_PRECIS;
2038 pelf->elfLogFont.lfQuality = DRAFT_QUALITY;
2040 *ptype = ptm->tmPitchAndFamily & TMPF_TRUETYPE ? TRUETYPE_FONTTYPE : 0;
2041 if(!(ptm->tmPitchAndFamily & TMPF_VECTOR))
2042 *ptype |= RASTER_FONTTYPE;
2044 pntm->ntmTm.ntmFlags = ptm->tmItalic ? NTM_ITALIC : 0;
2045 if(ptm->tmWeight > 550) pntm->ntmTm.ntmFlags |= NTM_BOLD;
2046 if(pntm->ntmTm.ntmFlags == 0) pntm->ntmTm.ntmFlags = NTM_REGULAR;
2048 pntm->ntmTm.ntmCellHeight = pntm->ntmTm.tmHeight;
2049 pntm->ntmTm.ntmAvgWidth = pntm->ntmTm.tmAveCharWidth;
2050 memset(&pntm->ntmFontSig, 0, sizeof(FONTSIGNATURE));
2053 pntm->ntmTm.ntmSizeEM = potm->otmEMSquare;
2055 lstrcpynW(pelf->elfLogFont.lfFaceName,
2056 (WCHAR*)((char*)potm + (ptrdiff_t)potm->otmpFamilyName),
2058 lstrcpynW(pelf->elfFullName,
2059 (WCHAR*)((char*)potm + (ptrdiff_t)potm->otmpFaceName),
2061 lstrcpynW(pelf->elfStyle,
2062 (WCHAR*)((char*)potm + (ptrdiff_t)potm->otmpStyleName),
2065 HeapFree(GetProcessHeap(), 0, potm);
2067 pntm->ntmTm.ntmSizeEM = pntm->ntmTm.tmHeight - pntm->ntmTm.tmInternalLeading;
2069 lstrcpynW(pelf->elfLogFont.lfFaceName, face->family->FamilyName, LF_FACESIZE);
2070 lstrcpynW(pelf->elfFullName, face->family->FamilyName, LF_FACESIZE);
2071 pelf->elfStyle[0] = '\0';
2074 pelf->elfScript[0] = '\0'; /* This will get set in WineEngEnumFonts */
2079 /*************************************************************
2083 DWORD WineEngEnumFonts(LPLOGFONTW plf, FONTENUMPROCW proc, LPARAM lparam)
2087 struct list *family_elem_ptr, *face_elem_ptr;
2089 NEWTEXTMETRICEXW ntm;
2090 DWORD type, ret = 1;
2096 TRACE("facename = %s charset %d\n", debugstr_w(plf->lfFaceName), plf->lfCharSet);
2098 if(plf->lfFaceName[0]) {
2100 for(psub = substlist; psub; psub = psub->next)
2101 if(!strcmpiW(plf->lfFaceName, psub->from.name) &&
2102 (psub->from.charset == -1 ||
2103 psub->from.charset == plf->lfCharSet))
2106 TRACE("substituting %s -> %s\n", debugstr_w(plf->lfFaceName),
2107 debugstr_w(psub->to.name));
2108 memcpy(&lf, plf, sizeof(lf));
2109 strcpyW(lf.lfFaceName, psub->to.name);
2113 LIST_FOR_EACH(family_elem_ptr, &font_list) {
2114 family = LIST_ENTRY(family_elem_ptr, Family, entry);
2115 if(!strcmpiW(plf->lfFaceName, family->FamilyName)) {
2116 LIST_FOR_EACH(face_elem_ptr, &family->faces) {
2117 face = LIST_ENTRY(face_elem_ptr, Face, entry);
2118 GetEnumStructs(face, &elf, &ntm, &type);
2119 for(i = 0; i < 32; i++) {
2120 if(!face->scalable && face->fs.fsCsb[0] == 0) { /* OEM bitmap */
2121 elf.elfLogFont.lfCharSet = ntm.ntmTm.tmCharSet = OEM_CHARSET;
2122 strcpyW(elf.elfScript, OEM_DOSW);
2123 i = 32; /* break out of loop */
2124 } else if(!(face->fs.fsCsb[0] & (1L << i)))
2127 fs.fsCsb[0] = 1L << i;
2129 if(!TranslateCharsetInfo(fs.fsCsb, &csi,
2131 csi.ciCharset = DEFAULT_CHARSET;
2132 if(i == 31) csi.ciCharset = SYMBOL_CHARSET;
2133 if(csi.ciCharset != DEFAULT_CHARSET) {
2134 elf.elfLogFont.lfCharSet =
2135 ntm.ntmTm.tmCharSet = csi.ciCharset;
2137 strcpyW(elf.elfScript, ElfScriptsW[i]);
2139 FIXME("Unknown elfscript for bit %d\n", i);
2142 TRACE("enuming face %s full %s style %s charset %d type %ld script %s it %d weight %ld ntmflags %08lx\n",
2143 debugstr_w(elf.elfLogFont.lfFaceName),
2144 debugstr_w(elf.elfFullName), debugstr_w(elf.elfStyle),
2145 csi.ciCharset, type, debugstr_w(elf.elfScript),
2146 elf.elfLogFont.lfItalic, elf.elfLogFont.lfWeight,
2147 ntm.ntmTm.ntmFlags);
2148 ret = proc(&elf.elfLogFont, (TEXTMETRICW *)&ntm, type, lparam);
2155 LIST_FOR_EACH(family_elem_ptr, &font_list) {
2156 family = LIST_ENTRY(family_elem_ptr, Family, entry);
2157 face_elem_ptr = list_head(&family->faces);
2158 face = LIST_ENTRY(face_elem_ptr, Face, entry);
2159 GetEnumStructs(face, &elf, &ntm, &type);
2160 for(i = 0; i < 32; i++) {
2161 if(!face->scalable && face->fs.fsCsb[0] == 0) { /* OEM bitmap */
2162 elf.elfLogFont.lfCharSet = ntm.ntmTm.tmCharSet = OEM_CHARSET;
2163 strcpyW(elf.elfScript, OEM_DOSW);
2164 i = 32; /* break out of loop */
2165 } else if(!(face->fs.fsCsb[0] & (1L << i)))
2168 fs.fsCsb[0] = 1L << i;
2170 if(!TranslateCharsetInfo(fs.fsCsb, &csi,
2172 csi.ciCharset = DEFAULT_CHARSET;
2173 if(i == 31) csi.ciCharset = SYMBOL_CHARSET;
2174 if(csi.ciCharset != DEFAULT_CHARSET) {
2175 elf.elfLogFont.lfCharSet = ntm.ntmTm.tmCharSet =
2178 strcpyW(elf.elfScript, ElfScriptsW[i]);
2180 FIXME("Unknown elfscript for bit %d\n", i);
2183 TRACE("enuming face %s full %s style %s charset = %d type %ld script %s it %d weight %ld ntmflags %08lx\n",
2184 debugstr_w(elf.elfLogFont.lfFaceName),
2185 debugstr_w(elf.elfFullName), debugstr_w(elf.elfStyle),
2186 csi.ciCharset, type, debugstr_w(elf.elfScript),
2187 elf.elfLogFont.lfItalic, elf.elfLogFont.lfWeight,
2188 ntm.ntmTm.ntmFlags);
2189 ret = proc(&elf.elfLogFont, (TEXTMETRICW *)&ntm, type, lparam);
2198 static void FTVectorToPOINTFX(FT_Vector *vec, POINTFX *pt)
2200 pt->x.value = vec->x >> 6;
2201 pt->x.fract = (vec->x & 0x3f) << 10;
2202 pt->x.fract |= ((pt->x.fract >> 6) | (pt->x.fract >> 12));
2203 pt->y.value = vec->y >> 6;
2204 pt->y.fract = (vec->y & 0x3f) << 10;
2205 pt->y.fract |= ((pt->y.fract >> 6) | (pt->y.fract >> 12));
2209 static FT_UInt get_glyph_index(GdiFont font, UINT glyph)
2211 if(font->ft_face->charmap->encoding == FT_ENCODING_NONE) {
2212 WCHAR wc = (WCHAR)glyph;
2214 WideCharToMultiByte(font->codepage, 0, &wc, 1, &buf, sizeof(buf), 0, 0);
2215 return pFT_Get_Char_Index(font->ft_face, buf);
2218 if(font->charset == SYMBOL_CHARSET && glyph < 0x100)
2219 glyph = glyph + 0xf000;
2220 return pFT_Get_Char_Index(font->ft_face, glyph);
2223 /*************************************************************
2224 * WineEngGetGlyphIndices
2226 * FIXME: add support for GGI_MARK_NONEXISTING_GLYPHS
2228 DWORD WineEngGetGlyphIndices(GdiFont font, LPCWSTR lpstr, INT count,
2229 LPWORD pgi, DWORD flags)
2233 for(i = 0; i < count; i++)
2234 pgi[i] = get_glyph_index(font, lpstr[i]);
2239 /*************************************************************
2240 * WineEngGetGlyphOutline
2242 * Behaves in exactly the same way as the win32 api GetGlyphOutline
2243 * except that the first parameter is the HWINEENGFONT of the font in
2244 * question rather than an HDC.
2247 DWORD WineEngGetGlyphOutline(GdiFont font, UINT glyph, UINT format,
2248 LPGLYPHMETRICS lpgm, DWORD buflen, LPVOID buf,
2251 static const FT_Matrix identityMat = {(1 << 16), 0, 0, (1 << 16)};
2252 FT_Face ft_face = font->ft_face;
2253 FT_UInt glyph_index;
2254 DWORD width, height, pitch, needed = 0;
2255 FT_Bitmap ft_bitmap;
2257 INT left, right, top = 0, bottom = 0;
2259 FT_Int load_flags = FT_LOAD_DEFAULT | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
2260 float widthRatio = 1.0;
2261 FT_Matrix transMat = identityMat;
2262 BOOL needsTransform = FALSE;
2265 TRACE("%p, %04x, %08x, %p, %08lx, %p, %p\n", font, glyph, format, lpgm,
2266 buflen, buf, lpmat);
2268 if(format & GGO_GLYPH_INDEX) {
2269 glyph_index = glyph;
2270 format &= ~GGO_GLYPH_INDEX;
2272 glyph_index = get_glyph_index(font, glyph);
2274 if(glyph_index >= font->gmsize) {
2275 font->gmsize = (glyph_index / INIT_GM_SIZE + 1) * INIT_GM_SIZE;
2276 font->gm = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, font->gm,
2277 font->gmsize * sizeof(*font->gm));
2279 if(format == GGO_METRICS && font->gm[glyph_index].init) {
2280 memcpy(lpgm, &font->gm[glyph_index].gm, sizeof(*lpgm));
2281 return 1; /* FIXME */
2285 if(font->orientation || (format != GGO_METRICS && format != GGO_BITMAP) || font->aveWidth || lpmat)
2286 load_flags |= FT_LOAD_NO_BITMAP;
2288 err = pFT_Load_Glyph(ft_face, glyph_index, load_flags);
2291 FIXME("FT_Load_Glyph on index %x returns %d\n", glyph_index, err);
2295 /* Scaling factor */
2296 if (font->aveWidth && font->potm) {
2297 widthRatio = (float)font->aveWidth * font->font_desc.matrix.eM11 / (float) font->potm->otmTextMetrics.tmAveCharWidth;
2300 left = (INT)(ft_face->glyph->metrics.horiBearingX * widthRatio) & -64;
2301 right = (INT)((ft_face->glyph->metrics.horiBearingX + ft_face->glyph->metrics.width) * widthRatio + 63) & -64;
2303 font->gm[glyph_index].adv = (INT)((ft_face->glyph->metrics.horiAdvance * widthRatio) + 63) >> 6;
2304 font->gm[glyph_index].lsb = left >> 6;
2305 font->gm[glyph_index].bbx = (right - left) >> 6;
2307 /* Scaling transform */
2308 if(font->aveWidth) {
2310 scaleMat.xx = FT_FixedFromFloat(widthRatio);
2313 scaleMat.yy = (1 << 16);
2315 pFT_Matrix_Multiply(&scaleMat, &transMat);
2316 needsTransform = TRUE;
2319 /* Rotation transform */
2320 if(font->orientation) {
2321 FT_Matrix rotationMat;
2323 angle = FT_FixedFromFloat((float)font->orientation / 10.0);
2324 pFT_Vector_Unit(&vecAngle, angle);
2325 rotationMat.xx = vecAngle.x;
2326 rotationMat.xy = -vecAngle.y;
2327 rotationMat.yx = -rotationMat.xy;
2328 rotationMat.yy = rotationMat.xx;
2330 pFT_Matrix_Multiply(&rotationMat, &transMat);
2331 needsTransform = TRUE;
2334 /* Extra transformation specified by caller */
2337 extraMat.xx = FT_FixedFromFIXED(lpmat->eM11);
2338 extraMat.xy = FT_FixedFromFIXED(lpmat->eM21);
2339 extraMat.yx = FT_FixedFromFIXED(lpmat->eM12);
2340 extraMat.yy = FT_FixedFromFIXED(lpmat->eM22);
2341 pFT_Matrix_Multiply(&extraMat, &transMat);
2342 needsTransform = TRUE;
2345 if(!needsTransform) {
2346 top = (ft_face->glyph->metrics.horiBearingY + 63) & -64;
2347 bottom = (ft_face->glyph->metrics.horiBearingY -
2348 ft_face->glyph->metrics.height) & -64;
2349 lpgm->gmCellIncX = font->gm[glyph_index].adv;
2350 lpgm->gmCellIncY = 0;
2354 for(xc = 0; xc < 2; xc++) {
2355 for(yc = 0; yc < 2; yc++) {
2356 vec.x = (ft_face->glyph->metrics.horiBearingX +
2357 xc * ft_face->glyph->metrics.width);
2358 vec.y = ft_face->glyph->metrics.horiBearingY -
2359 yc * ft_face->glyph->metrics.height;
2360 TRACE("Vec %ld,%ld\n", vec.x, vec.y);
2361 pFT_Vector_Transform(&vec, &transMat);
2362 if(xc == 0 && yc == 0) {
2363 left = right = vec.x;
2364 top = bottom = vec.y;
2366 if(vec.x < left) left = vec.x;
2367 else if(vec.x > right) right = vec.x;
2368 if(vec.y < bottom) bottom = vec.y;
2369 else if(vec.y > top) top = vec.y;
2374 right = (right + 63) & -64;
2375 bottom = bottom & -64;
2376 top = (top + 63) & -64;
2378 TRACE("transformed box: (%d,%d - %d,%d)\n", left, top, right, bottom);
2379 vec.x = ft_face->glyph->metrics.horiAdvance;
2381 pFT_Vector_Transform(&vec, &transMat);
2382 lpgm->gmCellIncX = (vec.x+63) >> 6;
2383 lpgm->gmCellIncY = -((vec.y+63) >> 6);
2385 lpgm->gmBlackBoxX = (right - left) >> 6;
2386 lpgm->gmBlackBoxY = (top - bottom) >> 6;
2387 lpgm->gmptGlyphOrigin.x = left >> 6;
2388 lpgm->gmptGlyphOrigin.y = top >> 6;
2390 memcpy(&font->gm[glyph_index].gm, lpgm, sizeof(*lpgm));
2391 font->gm[glyph_index].init = TRUE;
2393 if(format == GGO_METRICS)
2394 return 1; /* FIXME */
2396 if(ft_face->glyph->format != ft_glyph_format_outline && format != GGO_BITMAP) {
2397 TRACE("loaded a bitmap\n");
2403 width = lpgm->gmBlackBoxX;
2404 height = lpgm->gmBlackBoxY;
2405 pitch = ((width + 31) >> 5) << 2;
2406 needed = pitch * height;
2408 if(!buf || !buflen) break;
2410 switch(ft_face->glyph->format) {
2411 case ft_glyph_format_bitmap:
2413 BYTE *src = ft_face->glyph->bitmap.buffer, *dst = buf;
2414 INT w = (ft_face->glyph->bitmap.width + 7) >> 3;
2415 INT h = ft_face->glyph->bitmap.rows;
2417 memcpy(dst, src, w);
2418 src += ft_face->glyph->bitmap.pitch;
2424 case ft_glyph_format_outline:
2425 ft_bitmap.width = width;
2426 ft_bitmap.rows = height;
2427 ft_bitmap.pitch = pitch;
2428 ft_bitmap.pixel_mode = ft_pixel_mode_mono;
2429 ft_bitmap.buffer = buf;
2431 if(needsTransform) {
2432 pFT_Outline_Transform(&ft_face->glyph->outline, &transMat);
2435 pFT_Outline_Translate(&ft_face->glyph->outline, -left, -bottom );
2437 /* Note: FreeType will only set 'black' bits for us. */
2438 memset(buf, 0, needed);
2439 pFT_Outline_Get_Bitmap(library, &ft_face->glyph->outline, &ft_bitmap);
2443 FIXME("loaded glyph format %x\n", ft_face->glyph->format);
2448 case GGO_GRAY2_BITMAP:
2449 case GGO_GRAY4_BITMAP:
2450 case GGO_GRAY8_BITMAP:
2451 case WINE_GGO_GRAY16_BITMAP:
2453 unsigned int mult, row, col;
2456 width = lpgm->gmBlackBoxX;
2457 height = lpgm->gmBlackBoxY;
2458 pitch = (width + 3) / 4 * 4;
2459 needed = pitch * height;
2461 if(!buf || !buflen) break;
2462 ft_bitmap.width = width;
2463 ft_bitmap.rows = height;
2464 ft_bitmap.pitch = pitch;
2465 ft_bitmap.pixel_mode = ft_pixel_mode_grays;
2466 ft_bitmap.buffer = buf;
2468 if(needsTransform) {
2469 pFT_Outline_Transform(&ft_face->glyph->outline, &transMat);
2472 pFT_Outline_Translate(&ft_face->glyph->outline, -left, -bottom );
2474 memset(ft_bitmap.buffer, 0, buflen);
2476 pFT_Outline_Get_Bitmap(library, &ft_face->glyph->outline, &ft_bitmap);
2478 if(format == GGO_GRAY2_BITMAP)
2480 else if(format == GGO_GRAY4_BITMAP)
2482 else if(format == GGO_GRAY8_BITMAP)
2484 else if(format == WINE_GGO_GRAY16_BITMAP)
2492 for(row = 0; row < height; row++) {
2494 for(col = 0; col < width; col++, ptr++) {
2495 *ptr = (((int)*ptr) * mult + 128) / 256;
2504 int contour, point = 0, first_pt;
2505 FT_Outline *outline = &ft_face->glyph->outline;
2506 TTPOLYGONHEADER *pph;
2508 DWORD pph_start, cpfx, type;
2510 if(buflen == 0) buf = NULL;
2512 if (needsTransform && buf) {
2513 pFT_Outline_Transform(outline, &transMat);
2516 for(contour = 0; contour < outline->n_contours; contour++) {
2518 pph = (TTPOLYGONHEADER *)((char *)buf + needed);
2521 pph->dwType = TT_POLYGON_TYPE;
2522 FTVectorToPOINTFX(&outline->points[point], &pph->pfxStart);
2524 needed += sizeof(*pph);
2526 while(point <= outline->contours[contour]) {
2527 ppc = (TTPOLYCURVE *)((char *)buf + needed);
2528 type = (outline->tags[point] & FT_Curve_Tag_On) ?
2529 TT_PRIM_LINE : TT_PRIM_QSPLINE;
2533 FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
2536 } while(point <= outline->contours[contour] &&
2537 (outline->tags[point] & FT_Curve_Tag_On) ==
2538 (outline->tags[point-1] & FT_Curve_Tag_On));
2539 /* At the end of a contour Windows adds the start point, but
2541 if(point > outline->contours[contour] &&
2542 !(outline->tags[point-1] & FT_Curve_Tag_On)) {
2544 FTVectorToPOINTFX(&outline->points[first_pt], &ppc->apfx[cpfx]);
2546 } else if(point <= outline->contours[contour] &&
2547 outline->tags[point] & FT_Curve_Tag_On) {
2548 /* add closing pt for bezier */
2550 FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
2558 needed += sizeof(*ppc) + (cpfx - 1) * sizeof(POINTFX);
2561 pph->cb = needed - pph_start;
2567 /* Convert the quadratic Beziers to cubic Beziers.
2568 The parametric eqn for a cubic Bezier is, from PLRM:
2569 r(t) = at^3 + bt^2 + ct + r0
2570 with the control points:
2575 A quadratic Beizer has the form:
2576 p(t) = (1-t)^2 p0 + 2(1-t)t p1 + t^2 p2
2578 So equating powers of t leads to:
2579 r1 = 2/3 p1 + 1/3 p0
2580 r2 = 2/3 p1 + 1/3 p2
2581 and of course r0 = p0, r3 = p2
2584 int contour, point = 0, first_pt;
2585 FT_Outline *outline = &ft_face->glyph->outline;
2586 TTPOLYGONHEADER *pph;
2588 DWORD pph_start, cpfx, type;
2589 FT_Vector cubic_control[4];
2590 if(buflen == 0) buf = NULL;
2592 if (needsTransform && buf) {
2593 pFT_Outline_Transform(outline, &transMat);
2596 for(contour = 0; contour < outline->n_contours; contour++) {
2598 pph = (TTPOLYGONHEADER *)((char *)buf + needed);
2601 pph->dwType = TT_POLYGON_TYPE;
2602 FTVectorToPOINTFX(&outline->points[point], &pph->pfxStart);
2604 needed += sizeof(*pph);
2606 while(point <= outline->contours[contour]) {
2607 ppc = (TTPOLYCURVE *)((char *)buf + needed);
2608 type = (outline->tags[point] & FT_Curve_Tag_On) ?
2609 TT_PRIM_LINE : TT_PRIM_CSPLINE;
2612 if(type == TT_PRIM_LINE) {
2614 FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
2618 /* Unlike QSPLINEs, CSPLINEs always have their endpoint
2621 /* FIXME: Possible optimization in endpoint calculation
2622 if there are two consecutive curves */
2623 cubic_control[0] = outline->points[point-1];
2624 if(!(outline->tags[point-1] & FT_Curve_Tag_On)) {
2625 cubic_control[0].x += outline->points[point].x + 1;
2626 cubic_control[0].y += outline->points[point].y + 1;
2627 cubic_control[0].x >>= 1;
2628 cubic_control[0].y >>= 1;
2630 if(point+1 > outline->contours[contour])
2631 cubic_control[3] = outline->points[first_pt];
2633 cubic_control[3] = outline->points[point+1];
2634 if(!(outline->tags[point+1] & FT_Curve_Tag_On)) {
2635 cubic_control[3].x += outline->points[point].x + 1;
2636 cubic_control[3].y += outline->points[point].y + 1;
2637 cubic_control[3].x >>= 1;
2638 cubic_control[3].y >>= 1;
2641 /* r1 = 1/3 p0 + 2/3 p1
2642 r2 = 1/3 p2 + 2/3 p1 */
2643 cubic_control[1].x = (2 * outline->points[point].x + 1) / 3;
2644 cubic_control[1].y = (2 * outline->points[point].y + 1) / 3;
2645 cubic_control[2] = cubic_control[1];
2646 cubic_control[1].x += (cubic_control[0].x + 1) / 3;
2647 cubic_control[1].y += (cubic_control[0].y + 1) / 3;
2648 cubic_control[2].x += (cubic_control[3].x + 1) / 3;
2649 cubic_control[2].y += (cubic_control[3].y + 1) / 3;
2651 FTVectorToPOINTFX(&cubic_control[1], &ppc->apfx[cpfx]);
2652 FTVectorToPOINTFX(&cubic_control[2], &ppc->apfx[cpfx+1]);
2653 FTVectorToPOINTFX(&cubic_control[3], &ppc->apfx[cpfx+2]);
2658 } while(point <= outline->contours[contour] &&
2659 (outline->tags[point] & FT_Curve_Tag_On) ==
2660 (outline->tags[point-1] & FT_Curve_Tag_On));
2661 /* At the end of a contour Windows adds the start point,
2662 but only for Beziers and we've already done that.
2664 if(point <= outline->contours[contour] &&
2665 outline->tags[point] & FT_Curve_Tag_On) {
2666 /* This is the closing pt of a bezier, but we've already
2667 added it, so just inc point and carry on */
2674 needed += sizeof(*ppc) + (cpfx - 1) * sizeof(POINTFX);
2677 pph->cb = needed - pph_start;
2683 FIXME("Unsupported format %d\n", format);
2689 static BOOL get_bitmap_text_metrics(GdiFont font)
2691 FT_Face ft_face = font->ft_face;
2692 #ifdef HAVE_FREETYPE_FTWINFNT_H
2693 FT_WinFNT_HeaderRec winfnt_header;
2695 const DWORD size = offsetof(OUTLINETEXTMETRICW, otmFiller);
2696 font->potm = HeapAlloc(GetProcessHeap(), 0, size);
2697 font->potm->otmSize = size;
2699 #define TM font->potm->otmTextMetrics
2700 #ifdef HAVE_FREETYPE_FTWINFNT_H
2701 if(pFT_Get_WinFNT_Header && !pFT_Get_WinFNT_Header(ft_face, &winfnt_header))
2703 TM.tmHeight = winfnt_header.pixel_height;
2704 TM.tmAscent = winfnt_header.ascent;
2705 TM.tmDescent = TM.tmHeight - TM.tmAscent;
2706 TM.tmInternalLeading = winfnt_header.internal_leading;
2707 TM.tmExternalLeading = winfnt_header.external_leading;
2708 TM.tmAveCharWidth = winfnt_header.avg_width;
2709 TM.tmMaxCharWidth = winfnt_header.max_width;
2710 TM.tmWeight = winfnt_header.weight;
2712 TM.tmDigitizedAspectX = winfnt_header.horizontal_resolution;
2713 TM.tmDigitizedAspectY = winfnt_header.vertical_resolution;
2714 TM.tmFirstChar = winfnt_header.first_char;
2715 TM.tmLastChar = winfnt_header.last_char;
2716 TM.tmDefaultChar = winfnt_header.default_char + winfnt_header.first_char;
2717 TM.tmBreakChar = winfnt_header.break_char + winfnt_header.first_char;
2718 TM.tmItalic = winfnt_header.italic;
2719 TM.tmUnderlined = font->underline;
2720 TM.tmStruckOut = font->strikeout;
2721 TM.tmPitchAndFamily = winfnt_header.pitch_and_family;
2722 TM.tmCharSet = winfnt_header.charset;
2727 TM.tmAscent = ft_face->size->metrics.ascender >> 6;
2728 TM.tmDescent = -ft_face->size->metrics.descender >> 6;
2729 TM.tmHeight = TM.tmAscent + TM.tmDescent;
2730 TM.tmInternalLeading = TM.tmHeight - ft_face->size->metrics.y_ppem;
2731 TM.tmExternalLeading = (ft_face->size->metrics.height >> 6) - TM.tmHeight;
2732 TM.tmMaxCharWidth = ft_face->size->metrics.max_advance >> 6;
2733 TM.tmAveCharWidth = TM.tmMaxCharWidth * 2 / 3; /* FIXME */
2734 TM.tmWeight = ft_face->style_flags & FT_STYLE_FLAG_BOLD ? FW_BOLD : FW_NORMAL;
2736 TM.tmDigitizedAspectX = 96; /* FIXME */
2737 TM.tmDigitizedAspectY = 96; /* FIXME */
2739 TM.tmLastChar = 255;
2740 TM.tmDefaultChar = 32;
2741 TM.tmBreakChar = 32;
2742 TM.tmItalic = ft_face->style_flags & FT_STYLE_FLAG_ITALIC ? 1 : 0;
2743 TM.tmUnderlined = font->underline;
2744 TM.tmStruckOut = font->strikeout;
2745 /* NB inverted meaning of TMPF_FIXED_PITCH */
2746 TM.tmPitchAndFamily = ft_face->face_flags & FT_FACE_FLAG_FIXED_WIDTH ? 0 : TMPF_FIXED_PITCH;
2747 TM.tmCharSet = font->charset;
2754 /*************************************************************
2755 * WineEngGetTextMetrics
2758 BOOL WineEngGetTextMetrics(GdiFont font, LPTEXTMETRICW ptm)
2761 if(!WineEngGetOutlineTextMetrics(font, 0, NULL))
2762 if(!get_bitmap_text_metrics(font))
2765 if(!font->potm) return FALSE;
2766 memcpy(ptm, &font->potm->otmTextMetrics, sizeof(*ptm));
2768 if (font->aveWidth) {
2769 ptm->tmAveCharWidth = font->aveWidth * font->font_desc.matrix.eM11;
2775 /*************************************************************
2776 * WineEngGetOutlineTextMetrics
2779 UINT WineEngGetOutlineTextMetrics(GdiFont font, UINT cbSize,
2780 OUTLINETEXTMETRICW *potm)
2782 FT_Face ft_face = font->ft_face;
2783 UINT needed, lenfam, lensty, ret;
2785 TT_HoriHeader *pHori;
2786 TT_Postscript *pPost;
2787 FT_Fixed x_scale, y_scale;
2788 WCHAR *family_nameW, *style_nameW;
2789 static const WCHAR spaceW[] = {' ', '\0'};
2791 INT ascent, descent;
2793 TRACE("font=%p\n", font);
2795 if(!FT_IS_SCALABLE(ft_face))
2799 if(cbSize >= font->potm->otmSize)
2800 memcpy(potm, font->potm, font->potm->otmSize);
2801 return font->potm->otmSize;
2805 needed = sizeof(*potm);
2807 lenfam = (strlenW(font->name) + 1) * sizeof(WCHAR);
2808 family_nameW = strdupW(font->name);
2810 lensty = MultiByteToWideChar(CP_ACP, 0, ft_face->style_name, -1, NULL, 0)
2812 style_nameW = HeapAlloc(GetProcessHeap(), 0, lensty);
2813 MultiByteToWideChar(CP_ACP, 0, ft_face->style_name, -1,
2814 style_nameW, lensty);
2816 /* These names should be read from the TT name table */
2818 /* length of otmpFamilyName */
2821 /* length of otmpFaceName */
2822 if(!strcasecmp(ft_face->style_name, "regular")) {
2823 needed += lenfam; /* just the family name */
2825 needed += lenfam + lensty; /* family + " " + style */
2828 /* length of otmpStyleName */
2831 /* length of otmpFullName */
2832 needed += lenfam + lensty;
2835 x_scale = ft_face->size->metrics.x_scale;
2836 y_scale = ft_face->size->metrics.y_scale;
2838 pOS2 = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_os2);
2840 FIXME("Can't find OS/2 table - not TT font?\n");
2845 pHori = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_hhea);
2847 FIXME("Can't find HHEA table - not TT font?\n");
2852 pPost = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_post); /* we can live with this failing */
2854 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",
2855 pOS2->usWinAscent, pOS2->usWinDescent,
2856 pOS2->sTypoAscender, pOS2->sTypoDescender, pOS2->sTypoLineGap,
2857 ft_face->ascender, ft_face->descender, ft_face->height,
2858 pHori->Ascender, pHori->Descender, pHori->Line_Gap,
2859 ft_face->bbox.yMax, ft_face->bbox.yMin);
2861 font->potm = HeapAlloc(GetProcessHeap(), 0, needed);
2862 font->potm->otmSize = needed;
2864 #define TM font->potm->otmTextMetrics
2866 if(pOS2->usWinAscent + pOS2->usWinDescent == 0) {
2867 ascent = pHori->Ascender;
2868 descent = -pHori->Descender;
2870 ascent = pOS2->usWinAscent;
2871 descent = pOS2->usWinDescent;
2875 TM.tmAscent = font->yMax;
2876 TM.tmDescent = -font->yMin;
2877 TM.tmInternalLeading = (TM.tmAscent + TM.tmDescent) - ft_face->size->metrics.y_ppem;
2879 TM.tmAscent = (pFT_MulFix(ascent, y_scale) + 32) >> 6;
2880 TM.tmDescent = (pFT_MulFix(descent, y_scale) + 32) >> 6;
2881 TM.tmInternalLeading = (pFT_MulFix(ascent + descent
2882 - ft_face->units_per_EM, y_scale) + 32) >> 6;
2885 TM.tmHeight = TM.tmAscent + TM.tmDescent;
2888 el = MAX(0, LineGap - ((WinAscent + WinDescent) - (Ascender - Descender)))
2890 TM.tmExternalLeading = max(0, (pFT_MulFix(pHori->Line_Gap -
2891 ((ascent + descent) -
2892 (pHori->Ascender - pHori->Descender)), y_scale) + 32) >> 6);
2894 TM.tmAveCharWidth = (pFT_MulFix(pOS2->xAvgCharWidth, x_scale) + 32) >> 6;
2895 if (TM.tmAveCharWidth == 0) {
2896 TM.tmAveCharWidth = 1;
2898 TM.tmMaxCharWidth = (pFT_MulFix(ft_face->bbox.xMax - ft_face->bbox.xMin, x_scale) + 32) >> 6;
2899 TM.tmWeight = font->fake_bold ? FW_BOLD : pOS2->usWeightClass;
2901 TM.tmDigitizedAspectX = 300;
2902 TM.tmDigitizedAspectY = 300;
2903 TM.tmFirstChar = pOS2->usFirstCharIndex;
2904 TM.tmLastChar = pOS2->usLastCharIndex;
2905 TM.tmDefaultChar = pOS2->usDefaultChar;
2906 TM.tmBreakChar = pOS2->usBreakChar ? pOS2->usBreakChar : ' ';
2907 TM.tmItalic = font->fake_italic ? 255 : ((ft_face->style_flags & FT_STYLE_FLAG_ITALIC) ? 255 : 0);
2908 TM.tmUnderlined = font->underline;
2909 TM.tmStruckOut = font->strikeout;
2911 /* Yes TPMF_FIXED_PITCH is correct; braindead api */
2912 if(!FT_IS_FIXED_WIDTH(ft_face) &&
2913 (pOS2->version == 0xFFFFU ||
2914 pOS2->panose[PAN_PROPORTION_INDEX] != PAN_PROP_MONOSPACED))
2915 TM.tmPitchAndFamily = TMPF_FIXED_PITCH;
2917 TM.tmPitchAndFamily = 0;
2919 switch(pOS2->panose[PAN_FAMILYTYPE_INDEX]) {
2920 case PAN_FAMILY_SCRIPT:
2921 TM.tmPitchAndFamily |= FF_SCRIPT;
2923 case PAN_FAMILY_DECORATIVE:
2924 case PAN_FAMILY_PICTORIAL:
2925 TM.tmPitchAndFamily |= FF_DECORATIVE;
2927 case PAN_FAMILY_TEXT_DISPLAY:
2928 if(TM.tmPitchAndFamily == 0) /* fixed */
2929 TM.tmPitchAndFamily = FF_MODERN;
2931 switch(pOS2->panose[PAN_SERIFSTYLE_INDEX]) {
2932 case PAN_SERIF_NORMAL_SANS:
2933 case PAN_SERIF_OBTUSE_SANS:
2934 case PAN_SERIF_PERP_SANS:
2935 TM.tmPitchAndFamily |= FF_SWISS;
2938 TM.tmPitchAndFamily |= FF_ROMAN;
2943 TM.tmPitchAndFamily |= FF_DONTCARE;
2946 if(FT_IS_SCALABLE(ft_face))
2947 TM.tmPitchAndFamily |= TMPF_VECTOR;
2948 if(FT_IS_SFNT(ft_face))
2949 TM.tmPitchAndFamily |= TMPF_TRUETYPE;
2951 TM.tmCharSet = font->charset;
2954 font->potm->otmFiller = 0;
2955 memcpy(&font->potm->otmPanoseNumber, pOS2->panose, PANOSE_COUNT);
2956 font->potm->otmfsSelection = pOS2->fsSelection;
2957 font->potm->otmfsType = pOS2->fsType;
2958 font->potm->otmsCharSlopeRise = pHori->caret_Slope_Rise;
2959 font->potm->otmsCharSlopeRun = pHori->caret_Slope_Run;
2960 font->potm->otmItalicAngle = 0; /* POST table */
2961 font->potm->otmEMSquare = ft_face->units_per_EM;
2962 font->potm->otmAscent = (pFT_MulFix(pOS2->sTypoAscender, y_scale) + 32) >> 6;
2963 font->potm->otmDescent = (pFT_MulFix(pOS2->sTypoDescender, y_scale) + 32) >> 6;
2964 font->potm->otmLineGap = (pFT_MulFix(pOS2->sTypoLineGap, y_scale) + 32) >> 6;
2965 font->potm->otmsCapEmHeight = (pFT_MulFix(pOS2->sCapHeight, y_scale) + 32) >> 6;
2966 font->potm->otmsXHeight = (pFT_MulFix(pOS2->sxHeight, y_scale) + 32) >> 6;
2967 font->potm->otmrcFontBox.left = (pFT_MulFix(ft_face->bbox.xMin, x_scale) + 32) >> 6;
2968 font->potm->otmrcFontBox.right = (pFT_MulFix(ft_face->bbox.xMax, x_scale) + 32) >> 6;
2969 font->potm->otmrcFontBox.top = (pFT_MulFix(ft_face->bbox.yMax, y_scale) + 32) >> 6;
2970 font->potm->otmrcFontBox.bottom = (pFT_MulFix(ft_face->bbox.yMin, y_scale) + 32) >> 6;
2971 font->potm->otmMacAscent = 0; /* where do these come from ? */
2972 font->potm->otmMacDescent = 0;
2973 font->potm->otmMacLineGap = 0;
2974 font->potm->otmusMinimumPPEM = 0; /* TT Header */
2975 font->potm->otmptSubscriptSize.x = (pFT_MulFix(pOS2->ySubscriptXSize, x_scale) + 32) >> 6;
2976 font->potm->otmptSubscriptSize.y = (pFT_MulFix(pOS2->ySubscriptYSize, y_scale) + 32) >> 6;
2977 font->potm->otmptSubscriptOffset.x = (pFT_MulFix(pOS2->ySubscriptXOffset, x_scale) + 32) >> 6;
2978 font->potm->otmptSubscriptOffset.y = (pFT_MulFix(pOS2->ySubscriptYOffset, y_scale) + 32) >> 6;
2979 font->potm->otmptSuperscriptSize.x = (pFT_MulFix(pOS2->ySuperscriptXSize, x_scale) + 32) >> 6;
2980 font->potm->otmptSuperscriptSize.y = (pFT_MulFix(pOS2->ySuperscriptYSize, y_scale) + 32) >> 6;
2981 font->potm->otmptSuperscriptOffset.x = (pFT_MulFix(pOS2->ySuperscriptXOffset, x_scale) + 32) >> 6;
2982 font->potm->otmptSuperscriptOffset.y = (pFT_MulFix(pOS2->ySuperscriptYOffset, y_scale) + 32) >> 6;
2983 font->potm->otmsStrikeoutSize = (pFT_MulFix(pOS2->yStrikeoutSize, y_scale) + 32) >> 6;
2984 font->potm->otmsStrikeoutPosition = (pFT_MulFix(pOS2->yStrikeoutPosition, y_scale) + 32) >> 6;
2986 font->potm->otmsUnderscoreSize = 0;
2987 font->potm->otmsUnderscorePosition = 0;
2989 font->potm->otmsUnderscoreSize = (pFT_MulFix(pPost->underlineThickness, y_scale) + 32) >> 6;
2990 font->potm->otmsUnderscorePosition = (pFT_MulFix(pPost->underlinePosition, y_scale) + 32) >> 6;
2993 /* otmp* members should clearly have type ptrdiff_t, but M$ knows best */
2994 cp = (char*)font->potm + sizeof(*font->potm);
2995 font->potm->otmpFamilyName = (LPSTR)(cp - (char*)font->potm);
2996 strcpyW((WCHAR*)cp, family_nameW);
2998 font->potm->otmpStyleName = (LPSTR)(cp - (char*)font->potm);
2999 strcpyW((WCHAR*)cp, style_nameW);
3001 font->potm->otmpFaceName = (LPSTR)(cp - (char*)font->potm);
3002 strcpyW((WCHAR*)cp, family_nameW);
3003 if(strcasecmp(ft_face->style_name, "regular")) {
3004 strcatW((WCHAR*)cp, spaceW);
3005 strcatW((WCHAR*)cp, style_nameW);
3006 cp += lenfam + lensty;
3009 font->potm->otmpFullName = (LPSTR)(cp - (char*)font->potm);
3010 strcpyW((WCHAR*)cp, family_nameW);
3011 strcatW((WCHAR*)cp, spaceW);
3012 strcatW((WCHAR*)cp, style_nameW);
3015 if(potm && needed <= cbSize)
3016 memcpy(potm, font->potm, font->potm->otmSize);
3019 HeapFree(GetProcessHeap(), 0, style_nameW);
3020 HeapFree(GetProcessHeap(), 0, family_nameW);
3026 /*************************************************************
3027 * WineEngGetCharWidth
3030 BOOL WineEngGetCharWidth(GdiFont font, UINT firstChar, UINT lastChar,
3035 FT_UInt glyph_index;
3037 TRACE("%p, %d, %d, %p\n", font, firstChar, lastChar, buffer);
3039 for(c = firstChar; c <= lastChar; c++) {
3040 glyph_index = get_glyph_index(font, c);
3041 WineEngGetGlyphOutline(font, glyph_index, GGO_METRICS | GGO_GLYPH_INDEX,
3042 &gm, 0, NULL, NULL);
3043 buffer[c - firstChar] = font->gm[glyph_index].adv;
3048 /*************************************************************
3049 * WineEngGetCharABCWidths
3052 BOOL WineEngGetCharABCWidths(GdiFont font, UINT firstChar, UINT lastChar,
3057 FT_UInt glyph_index;
3059 TRACE("%p, %d, %d, %p\n", font, firstChar, lastChar, buffer);
3061 if(!FT_IS_SCALABLE(font->ft_face))
3064 for(c = firstChar; c <= lastChar; c++) {
3065 glyph_index = get_glyph_index(font, c);
3066 WineEngGetGlyphOutline(font, glyph_index, GGO_METRICS | GGO_GLYPH_INDEX,
3067 &gm, 0, NULL, NULL);
3068 buffer[c - firstChar].abcA = font->gm[glyph_index].lsb;
3069 buffer[c - firstChar].abcB = font->gm[glyph_index].bbx;
3070 buffer[c - firstChar].abcC = font->gm[glyph_index].adv - font->gm[glyph_index].lsb -
3071 font->gm[glyph_index].bbx;
3076 /*************************************************************
3077 * WineEngGetTextExtentPoint
3080 BOOL WineEngGetTextExtentPoint(GdiFont font, LPCWSTR wstr, INT count,
3086 FT_UInt glyph_index;
3088 TRACE("%p, %s, %d, %p\n", font, debugstr_wn(wstr, count), count,
3092 WineEngGetTextMetrics(font, &tm);
3093 size->cy = tm.tmHeight;
3095 for(idx = 0; idx < count; idx++) {
3096 glyph_index = get_glyph_index(font, wstr[idx]);
3097 WineEngGetGlyphOutline(font, glyph_index, GGO_METRICS | GGO_GLYPH_INDEX,
3098 &gm, 0, NULL, NULL);
3099 size->cx += font->gm[glyph_index].adv;
3101 TRACE("return %ld,%ld\n", size->cx, size->cy);
3105 /*************************************************************
3106 * WineEngGetTextExtentPointI
3109 BOOL WineEngGetTextExtentPointI(GdiFont font, const WORD *indices, INT count,
3116 TRACE("%p, %p, %d, %p\n", font, indices, count, size);
3119 WineEngGetTextMetrics(font, &tm);
3120 size->cy = tm.tmHeight;
3122 for(idx = 0; idx < count; idx++) {
3123 WineEngGetGlyphOutline(font, indices[idx],
3124 GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL,
3126 size->cx += font->gm[indices[idx]].adv;
3128 TRACE("return %ld,%ld\n", size->cx, size->cy);
3132 /*************************************************************
3133 * WineEngGetFontData
3136 DWORD WineEngGetFontData(GdiFont font, DWORD table, DWORD offset, LPVOID buf,
3139 FT_Face ft_face = font->ft_face;
3143 TRACE("font=%p, table=%08lx, offset=%08lx, buf=%p, cbData=%lx\n",
3144 font, table, offset, buf, cbData);
3146 if(!FT_IS_SFNT(ft_face))
3154 if(table) { /* MS tags differ in endidness from FT ones */
3155 table = table >> 24 | table << 24 |
3156 (table >> 8 & 0xff00) | (table << 8 & 0xff0000);
3159 /* If the FT_Load_Sfnt_Table function is there we'll use it */
3160 if(pFT_Load_Sfnt_Table)
3161 err = pFT_Load_Sfnt_Table(ft_face, table, offset, buf, &len);
3162 else { /* Do it the hard way */
3163 TT_Face tt_face = (TT_Face) ft_face;
3164 SFNT_Interface *sfnt;
3165 if (FT_Version.major==2 && FT_Version.minor==0)
3168 sfnt = *(SFNT_Interface**)((char*)tt_face + 528);
3172 /* A field was added in the middle of the structure in 2.1.x */
3173 sfnt = *(SFNT_Interface**)((char*)tt_face + 532);
3175 err = sfnt->load_any(tt_face, table, offset, buf, &len);
3178 TRACE("Can't find table %08lx.\n", table);
3184 /*************************************************************
3185 * WineEngGetTextFace
3188 INT WineEngGetTextFace(GdiFont font, INT count, LPWSTR str)
3191 lstrcpynW(str, font->name, count);
3192 return strlenW(font->name);
3194 return strlenW(font->name) + 1;
3197 UINT WineEngGetTextCharsetInfo(GdiFont font, LPFONTSIGNATURE fs, DWORD flags)
3199 if (fs) memcpy(fs, &font->fs, sizeof(FONTSIGNATURE));
3200 return font->charset;
3203 #else /* HAVE_FREETYPE */
3205 BOOL WineEngInit(void)
3209 GdiFont WineEngCreateFontInstance(DC *dc, HFONT hfont)
3213 BOOL WineEngDestroyFontInstance(HFONT hfont)
3218 DWORD WineEngEnumFonts(LPLOGFONTW plf, FONTENUMPROCW proc, LPARAM lparam)
3223 DWORD WineEngGetGlyphIndices(GdiFont font, LPCWSTR lpstr, INT count,
3224 LPWORD pgi, DWORD flags)
3229 DWORD WineEngGetGlyphOutline(GdiFont font, UINT glyph, UINT format,
3230 LPGLYPHMETRICS lpgm, DWORD buflen, LPVOID buf,
3233 ERR("called but we don't have FreeType\n");
3237 BOOL WineEngGetTextMetrics(GdiFont font, LPTEXTMETRICW ptm)
3239 ERR("called but we don't have FreeType\n");
3243 UINT WineEngGetOutlineTextMetrics(GdiFont font, UINT cbSize,
3244 OUTLINETEXTMETRICW *potm)
3246 ERR("called but we don't have FreeType\n");
3250 BOOL WineEngGetCharWidth(GdiFont font, UINT firstChar, UINT lastChar,
3253 ERR("called but we don't have FreeType\n");
3257 BOOL WineEngGetCharABCWidths(GdiFont font, UINT firstChar, UINT lastChar,
3260 ERR("called but we don't have FreeType\n");
3264 BOOL WineEngGetTextExtentPoint(GdiFont font, LPCWSTR wstr, INT count,
3267 ERR("called but we don't have FreeType\n");
3271 BOOL WineEngGetTextExtentPointI(GdiFont font, const WORD *indices, INT count,
3274 ERR("called but we don't have FreeType\n");
3278 DWORD WineEngGetFontData(GdiFont font, DWORD table, DWORD offset, LPVOID buf,
3281 ERR("called but we don't have FreeType\n");
3285 INT WineEngGetTextFace(GdiFont font, INT count, LPWSTR str)
3287 ERR("called but we don't have FreeType\n");
3291 INT WineEngAddFontResourceEx(LPCWSTR file, DWORD flags, PVOID pdv)
3297 INT WineEngRemoveFontResourceEx(LPCWSTR file, DWORD flags, PVOID pdv)
3303 UINT WineEngGetTextCharsetInfo(GdiFont font, LPFONTSIGNATURE fs, DWORD flags)
3306 return DEFAULT_CHARSET;
3309 #endif /* HAVE_FREETYPE */