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"
40 #include "gdi_private.h"
41 #include "wine/unicode.h"
42 #include "wine/debug.h"
44 WINE_DEFAULT_DEBUG_CHANNEL(font);
48 #ifdef HAVE_FT2BUILD_H
51 #ifdef HAVE_FREETYPE_FREETYPE_H
52 #include <freetype/freetype.h>
54 #ifdef HAVE_FREETYPE_FTGLYPH_H
55 #include <freetype/ftglyph.h>
57 #ifdef HAVE_FREETYPE_TTTABLES_H
58 #include <freetype/tttables.h>
60 #ifdef HAVE_FREETYPE_FTSNAMES_H
61 #include <freetype/ftsnames.h>
63 # ifdef HAVE_FREETYPE_FTNAMES_H
64 # include <freetype/ftnames.h>
67 #ifdef HAVE_FREETYPE_TTNAMEID_H
68 #include <freetype/ttnameid.h>
70 #ifdef HAVE_FREETYPE_FTOUTLN_H
71 #include <freetype/ftoutln.h>
73 #ifdef HAVE_FREETYPE_INTERNAL_SFNT_H
74 #include <freetype/internal/sfnt.h>
76 #ifdef HAVE_FREETYPE_FTTRIGON_H
77 #include <freetype/fttrigon.h>
80 #ifndef SONAME_LIBFREETYPE
81 #define SONAME_LIBFREETYPE "libfreetype.so"
84 static FT_Library library = 0;
91 static FT_Version_t FT_Version;
93 static void *ft_handle = NULL;
95 #define MAKE_FUNCPTR(f) static typeof(f) * p##f = NULL
96 MAKE_FUNCPTR(FT_Vector_Unit);
97 MAKE_FUNCPTR(FT_Done_Face);
98 MAKE_FUNCPTR(FT_Get_Char_Index);
99 MAKE_FUNCPTR(FT_Get_Sfnt_Table);
100 MAKE_FUNCPTR(FT_Init_FreeType);
101 MAKE_FUNCPTR(FT_Load_Glyph);
102 MAKE_FUNCPTR(FT_Matrix_Multiply);
103 MAKE_FUNCPTR(FT_MulFix);
104 MAKE_FUNCPTR(FT_New_Face);
105 MAKE_FUNCPTR(FT_Outline_Get_Bitmap);
106 MAKE_FUNCPTR(FT_Outline_Transform);
107 MAKE_FUNCPTR(FT_Outline_Translate);
108 MAKE_FUNCPTR(FT_Select_Charmap);
109 MAKE_FUNCPTR(FT_Set_Pixel_Sizes);
110 MAKE_FUNCPTR(FT_Vector_Transform);
111 static void (*pFT_Library_Version)(FT_Library,FT_Int*,FT_Int*,FT_Int*);
112 static FT_Error (*pFT_Load_Sfnt_Table)(FT_Face,FT_ULong,FT_Long,FT_Byte*,FT_ULong*);
113 static FT_ULong (*pFT_Get_First_Char)(FT_Face,FT_UInt*);
115 #ifdef HAVE_FONTCONFIG_FONTCONFIG_H
116 #include <fontconfig/fontconfig.h>
117 MAKE_FUNCPTR(FcConfigGetCurrent);
118 MAKE_FUNCPTR(FcFontList);
119 MAKE_FUNCPTR(FcFontSetDestroy);
120 MAKE_FUNCPTR(FcInit);
121 MAKE_FUNCPTR(FcObjectSetAdd);
122 MAKE_FUNCPTR(FcObjectSetCreate);
123 MAKE_FUNCPTR(FcObjectSetDestroy);
124 MAKE_FUNCPTR(FcPatternCreate);
125 MAKE_FUNCPTR(FcPatternDestroy);
126 MAKE_FUNCPTR(FcPatternGet);
127 #ifndef SONAME_LIBFONTCONFIG
128 #define SONAME_LIBFONTCONFIG "libfontconfig.so"
135 #define GET_BE_WORD(ptr) MAKEWORD( ((BYTE *)(ptr))[1], ((BYTE *)(ptr))[0] )
137 typedef struct tagFace {
144 FT_Fixed font_version;
145 BOOL external; /* TRUE if we should manually add this font to the registry */
146 struct tagFace *next;
147 struct tagFamily *family;
150 typedef struct tagFamily {
153 struct tagFamily *next;
158 INT adv; /* These three hold to widths of the unrotated chars */
178 OUTLINETEXTMETRICW *potm;
180 struct tagGdiFont *next;
183 #define INIT_GM_SIZE 128
185 static GdiFont GdiFontList = NULL;
187 static Family *FontList = NULL;
189 static const WCHAR defSerif[] = {'T','i','m','e','s',' ','N','e','w',' ',
190 'R','o','m','a','n','\0'};
191 static const WCHAR defSans[] = {'A','r','i','a','l','\0'};
192 static const WCHAR defFixed[] = {'C','o','u','r','i','e','r',' ','N','e','w','\0'};
194 static const WCHAR defSystem[] = {'A','r','i','a','l','\0'};
195 static const WCHAR SystemW[] = {'S','y','s','t','e','m','\0'};
196 static const WCHAR MSSansSerifW[] = {'M','S',' ','S','a','n','s',' ',
197 'S','e','r','i','f','\0'};
198 static const WCHAR HelvW[] = {'H','e','l','v','\0'};
199 static const WCHAR RegularW[] = {'R','e','g','u','l','a','r','\0'};
201 static const WCHAR win9x_font_reg_key[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
202 'W','i','n','d','o','w','s','\\',
203 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
204 'F','o','n','t','s','\0'};
206 static const WCHAR winnt_font_reg_key[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
207 'W','i','n','d','o','w','s',' ','N','T','\\',
208 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
209 'F','o','n','t','s','\0'};
211 static const WCHAR external_fonts_reg_key[] = {'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\','W','i','n','e','\\',
212 'F','o','n','t','s','\\','E','x','t','e','r','n','a','l',' ','F','o','n','t','s','\0'};
214 static const WCHAR ArabicW[] = {'A','r','a','b','i','c','\0'};
215 static const WCHAR BalticW[] = {'B','a','l','t','i','c','\0'};
216 static const WCHAR CHINESE_BIG5W[] = {'C','H','I','N','E','S','E','_','B','I','G','5','\0'};
217 static const WCHAR CHINESE_GB2312W[] = {'C','H','I','N','E','S','E','_','G','B','2','3','1','2','\0'};
218 static const WCHAR Central_EuropeanW[] = {'C','e','n','t','r','a','l',' ',
219 'E','u','r','o','p','e','a','n','\0'};
220 static const WCHAR CyrillicW[] = {'C','y','r','i','l','l','i','c','\0'};
221 static const WCHAR GreekW[] = {'G','r','e','e','k','\0'};
222 static const WCHAR HangulW[] = {'H','a','n','g','u','l','\0'};
223 static const WCHAR Hangul_Johab_W[] = {'H','a','n','g','u','l','(','J','o','h','a','b',')','\0'};
224 static const WCHAR HebrewW[] = {'H','e','b','r','e','w','\0'};
225 static const WCHAR JapaneseW[] = {'J','a','p','a','n','e','s','e','\0'};
226 static const WCHAR SymbolW[] = {'S','y','m','b','o','l','\0'};
227 static const WCHAR ThaiW[] = {'T','h','a','i','\0'};
228 static const WCHAR TurkishW[] = {'T','u','r','k','i','s','h','\0'};
229 static const WCHAR VietnameseW[] = {'V','i','e','t','n','a','m','e','s','e','\0'};
230 static const WCHAR WesternW[] = {'W','e','s','t','e','r','n','\0'};
232 static const WCHAR *ElfScriptsW[32] = { /* these are in the order of the fsCsb[0] bits */
242 NULL, NULL, NULL, NULL, NULL, NULL, NULL, /*15*/
250 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
259 typedef struct tagFontSubst {
262 struct tagFontSubst *next;
265 static FontSubst *substlist = NULL;
266 static BOOL have_installed_roman_font = FALSE; /* CreateFontInstance will fail if this is still FALSE */
268 static const WCHAR font_mutex_nameW[] = {'_','_','W','I','N','E','_','F','O','N','T','_','M','U','T','E','X','_','_','\0'};
270 static inline BOOL is_win9x(void)
272 return GetVersion() & 0x80000000;
275 This function builds an FT_Fixed from a float. It puts the integer part
276 in the highest 16 bits and the decimal part in the lowest 16 bits of the FT_Fixed.
277 It fails if the integer part of the float number is greater than SHORT_MAX.
279 static inline FT_Fixed FT_FixedFromFloat(float f)
282 unsigned short fract = (f - value) * 0xFFFF;
283 return (FT_Fixed)((long)value << 16 | (unsigned long)fract);
287 This function builds an FT_Fixed from a FIXED. It simply put f.value
288 in the highest 16 bits and f.fract in the lowest 16 bits of the FT_Fixed.
290 static inline FT_Fixed FT_FixedFromFIXED(FIXED f)
292 return (FT_Fixed)((long)f.value << 16 | (unsigned long)f.fract);
295 static BOOL AddFontFileToList(const char *file, char *fake_family, BOOL external_font)
300 WCHAR *FamilyW, *StyleW;
302 Family *family = FontList;
303 Family **insert = &FontList;
304 Face **insertface, *next;
306 FT_Long face_index = 0, num_faces;
310 char *family_name = fake_family;
312 TRACE("Loading font file %s index %ld\n", debugstr_a(file), face_index);
313 if((err = pFT_New_Face(library, file, face_index, &ft_face)) != 0) {
314 WARN("Unable to load font file %s err = %x\n", debugstr_a(file), err);
318 if(!FT_IS_SFNT(ft_face)) { /* for now we'll skip everything but TT/OT */
319 pFT_Done_Face(ft_face);
322 if(!pFT_Get_Sfnt_Table(ft_face, ft_sfnt_os2) ||
323 !pFT_Get_Sfnt_Table(ft_face, ft_sfnt_hhea) ||
324 !(pHeader = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_head))) {
325 TRACE("Font file %s lacks either an OS2, HHEA or HEAD table.\n"
326 "Skipping this font.\n", debugstr_a(file));
327 pFT_Done_Face(ft_face);
331 if(!ft_face->family_name || !ft_face->style_name) {
332 TRACE("Font file %s lacks either a family or style name\n", debugstr_a(file));
333 pFT_Done_Face(ft_face);
338 family_name = ft_face->family_name;
340 len = MultiByteToWideChar(CP_ACP, 0, family_name, -1, NULL, 0);
341 FamilyW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
342 MultiByteToWideChar(CP_ACP, 0, family_name, -1, FamilyW, len);
345 if(!strcmpW(family->FamilyName, FamilyW))
347 insert = &family->next;
348 family = family->next;
351 family = *insert = HeapAlloc(GetProcessHeap(), 0, sizeof(*family));
352 family->FamilyName = FamilyW;
353 family->FirstFace = NULL;
356 HeapFree(GetProcessHeap(), 0, FamilyW);
359 len = MultiByteToWideChar(CP_ACP, 0, ft_face->style_name, -1, NULL, 0);
360 StyleW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
361 MultiByteToWideChar(CP_ACP, 0, ft_face->style_name, -1, StyleW, len);
364 for(insertface = &family->FirstFace; *insertface;
365 insertface = &(*insertface)->next) {
366 if(!strcmpW((*insertface)->StyleName, StyleW)) {
367 TRACE("Already loaded font %s %s original version is %lx, this version is %lx\n",
368 debugstr_w(family->FamilyName), debugstr_w(StyleW),
369 (*insertface)->font_version, pHeader->Font_Revision);
372 TRACE("This font is a replacement but the original really exists, so we'll skip the replacement\n");
373 HeapFree(GetProcessHeap(), 0, StyleW);
374 pFT_Done_Face(ft_face);
377 if(pHeader->Font_Revision <= (*insertface)->font_version) {
378 TRACE("Original font is newer so skipping this one\n");
379 HeapFree(GetProcessHeap(), 0, StyleW);
380 pFT_Done_Face(ft_face);
383 TRACE("Replacing original with this one\n");
384 next = (*insertface)->next;
385 HeapFree(GetProcessHeap(), 0, (*insertface)->file);
386 HeapFree(GetProcessHeap(), 0, (*insertface)->StyleName);
387 HeapFree(GetProcessHeap(), 0, *insertface);
392 *insertface = HeapAlloc(GetProcessHeap(), 0, sizeof(**insertface));
393 (*insertface)->StyleName = StyleW;
394 (*insertface)->file = HeapAlloc(GetProcessHeap(),0,strlen(file)+1);
395 strcpy((*insertface)->file, file);
396 (*insertface)->face_index = face_index;
397 (*insertface)->next = next;
398 (*insertface)->Italic = (ft_face->style_flags & FT_STYLE_FLAG_ITALIC) ? 1 : 0;
399 (*insertface)->Bold = (ft_face->style_flags & FT_STYLE_FLAG_BOLD) ? 1 : 0;
400 (*insertface)->font_version = pHeader->Font_Revision;
401 (*insertface)->family = family;
402 (*insertface)->external = external_font;
404 pOS2 = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_os2);
406 (*insertface)->fs.fsCsb[0] = pOS2->ulCodePageRange1;
407 (*insertface)->fs.fsCsb[1] = pOS2->ulCodePageRange2;
408 (*insertface)->fs.fsUsb[0] = pOS2->ulUnicodeRange1;
409 (*insertface)->fs.fsUsb[1] = pOS2->ulUnicodeRange2;
410 (*insertface)->fs.fsUsb[2] = pOS2->ulUnicodeRange3;
411 (*insertface)->fs.fsUsb[3] = pOS2->ulUnicodeRange4;
413 (*insertface)->fs.fsCsb[0] = (*insertface)->fs.fsCsb[1] = 0;
414 (*insertface)->fs.fsUsb[0] = 0;
415 (*insertface)->fs.fsUsb[1] = 0;
416 (*insertface)->fs.fsUsb[2] = 0;
417 (*insertface)->fs.fsUsb[3] = 0;
419 TRACE("fsCsb = %08lx %08lx/%08lx %08lx %08lx %08lx\n",
420 (*insertface)->fs.fsCsb[0], (*insertface)->fs.fsCsb[1],
421 (*insertface)->fs.fsUsb[0], (*insertface)->fs.fsUsb[1],
422 (*insertface)->fs.fsUsb[2], (*insertface)->fs.fsUsb[3]);
424 if(pOS2->version == 0) {
427 /* If the function is not there, we assume the font is ok */
428 if(!pFT_Get_First_Char || (pFT_Get_First_Char( ft_face, &dummy ) < 0x100))
429 (*insertface)->fs.fsCsb[0] |= 1;
431 (*insertface)->fs.fsCsb[0] |= 1L << 31;
434 if((*insertface)->fs.fsCsb[0] == 0) { /* let's see if we can find any interesting cmaps */
435 for(i = 0; i < ft_face->num_charmaps; i++) {
436 switch(ft_face->charmaps[i]->encoding) {
437 case ft_encoding_unicode:
438 case ft_encoding_apple_roman:
439 (*insertface)->fs.fsCsb[0] |= 1;
441 case ft_encoding_symbol:
442 (*insertface)->fs.fsCsb[0] |= 1L << 31;
450 if((*insertface)->fs.fsCsb[0] & ~(1L << 31))
451 have_installed_roman_font = TRUE;
453 num_faces = ft_face->num_faces;
454 pFT_Done_Face(ft_face);
455 TRACE("Added font %s %s\n", debugstr_w(family->FamilyName),
457 } while(num_faces > ++face_index);
461 static void DumpFontList(void)
466 for(family = FontList; family; family = family->next) {
467 TRACE("Family: %s\n", debugstr_w(family->FamilyName));
468 for(face = family->FirstFace; face; face = face->next) {
469 TRACE("\t%s\n", debugstr_w(face->StyleName));
475 static void DumpSubstList(void)
479 for(psub = substlist; psub; psub = psub->next)
480 if(psub->from.charset != -1 || psub->to.charset != -1)
481 TRACE("%s:%d -> %s:%d\n", debugstr_w(psub->from.name),
482 psub->from.charset, debugstr_w(psub->to.name), psub->to.charset);
484 TRACE("%s -> %s\n", debugstr_w(psub->from.name),
485 debugstr_w(psub->to.name));
489 static LPWSTR strdupW(LPWSTR p)
492 DWORD len = (strlenW(p) + 1) * sizeof(WCHAR);
493 ret = HeapAlloc(GetProcessHeap(), 0, len);
498 static void split_subst_info(NameCs *nc, LPSTR str)
500 CHAR *p = strrchr(str, ',');
505 nc->charset = strtol(p+1, NULL, 10);
508 len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
509 nc->name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
510 MultiByteToWideChar(CP_ACP, 0, str, -1, nc->name, len);
513 static void LoadSubstList(void)
515 FontSubst *psub, **ppsub;
517 DWORD valuelen, datalen, i = 0, type, dlen, vlen;
522 for(psub = substlist; psub;) {
524 HeapFree(GetProcessHeap(), 0, psub->to.name);
525 HeapFree(GetProcessHeap(), 0, psub->from.name);
528 HeapFree(GetProcessHeap(), 0, ptmp);
533 if(RegOpenKeyA(HKEY_LOCAL_MACHINE,
534 "Software\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes",
535 &hkey) == ERROR_SUCCESS) {
537 RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
538 &valuelen, &datalen, NULL, NULL);
540 valuelen++; /* returned value doesn't include room for '\0' */
541 value = HeapAlloc(GetProcessHeap(), 0, valuelen * sizeof(CHAR));
542 data = HeapAlloc(GetProcessHeap(), 0, datalen);
547 while(RegEnumValueA(hkey, i++, value, &vlen, NULL, &type, data,
548 &dlen) == ERROR_SUCCESS) {
549 TRACE("Got %s=%s\n", debugstr_a(value), debugstr_a(data));
551 *ppsub = HeapAlloc(GetProcessHeap(), 0, sizeof(**ppsub));
552 (*ppsub)->next = NULL;
553 split_subst_info(&((*ppsub)->from), value);
554 split_subst_info(&((*ppsub)->to), data);
556 /* Win 2000 doesn't allow mapping between different charsets
557 or mapping of DEFAULT_CHARSET */
558 if(((*ppsub)->to.charset != (*ppsub)->from.charset) ||
559 (*ppsub)->to.charset == DEFAULT_CHARSET) {
560 HeapFree(GetProcessHeap(), 0, (*ppsub)->to.name);
561 HeapFree(GetProcessHeap(), 0, (*ppsub)->from.name);
562 HeapFree(GetProcessHeap(), 0, *ppsub);
565 ppsub = &((*ppsub)->next);
567 /* reset dlen and vlen */
571 HeapFree(GetProcessHeap(), 0, data);
572 HeapFree(GetProcessHeap(), 0, value);
577 /***********************************************************
578 * The replacement list is a way to map an entire font
579 * family onto another family. For example adding
581 * [HKLM\Software\Wine\Wine\FontReplacements]
582 * "Wingdings"="Winedings"
584 * would enumerate the Winedings font both as Winedings and
585 * Wingdings. However if a real Wingdings font is present the
586 * replacement does not take place.
589 static void LoadReplaceList(void)
592 DWORD valuelen, datalen, i = 0, type, dlen, vlen;
597 WCHAR old_nameW[200];
599 if(RegOpenKeyA(HKEY_LOCAL_MACHINE,
600 "Software\\Wine\\Wine\\FontReplacements",
601 &hkey) == ERROR_SUCCESS) {
603 RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
604 &valuelen, &datalen, NULL, NULL);
606 valuelen++; /* returned value doesn't include room for '\0' */
607 value = HeapAlloc(GetProcessHeap(), 0, valuelen * sizeof(CHAR));
608 data = HeapAlloc(GetProcessHeap(), 0, datalen);
612 while(RegEnumValueA(hkey, i++, value, &vlen, NULL, &type, data,
613 &dlen) == ERROR_SUCCESS) {
614 TRACE("Got %s=%s\n", debugstr_a(value), debugstr_a(data));
615 /* "NewName"="Oldname" */
616 if(!MultiByteToWideChar(CP_ACP, 0, data, -1, old_nameW, sizeof(old_nameW)))
619 /* Find the old family and hence all of the font files
621 for(family = FontList; family; family = family->next) {
622 if(!strcmpiW(family->FamilyName, old_nameW)) {
623 for(face = family->FirstFace; face; face = face->next) {
624 TRACE("mapping %s %s to %s\n", debugstr_w(family->FamilyName),
625 debugstr_w(face->StyleName), value);
626 /* Now add a new entry with the new family name */
627 AddFontFileToList(face->file, value, face->external);
632 /* reset dlen and vlen */
636 HeapFree(GetProcessHeap(), 0, data);
637 HeapFree(GetProcessHeap(), 0, value);
643 static BOOL ReadFontDir(const char *dirname, BOOL external_fonts)
649 TRACE("Loading fonts from %s\n", debugstr_a(dirname));
651 dir = opendir(dirname);
653 ERR("Can't open directory %s\n", debugstr_a(dirname));
656 while((dent = readdir(dir)) != NULL) {
659 if(!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
662 TRACE("Found %s in %s\n", debugstr_a(dent->d_name), debugstr_a(dirname));
664 sprintf(path, "%s/%s", dirname, dent->d_name);
666 if(stat(path, &statbuf) == -1)
668 WARN("Can't stat %s\n", debugstr_a(path));
671 if(S_ISDIR(statbuf.st_mode))
672 ReadFontDir(path, external_fonts);
674 AddFontFileToList(path, NULL, external_fonts);
680 static void load_fontconfig_fonts(void)
682 #ifdef HAVE_FONTCONFIG_FONTCONFIG_H
683 void *fc_handle = NULL;
692 fc_handle = wine_dlopen(SONAME_LIBFONTCONFIG, RTLD_NOW, NULL, 0);
694 TRACE("Wine cannot find the fontconfig library (%s).\n",
695 SONAME_LIBFONTCONFIG);
698 #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;}
699 LOAD_FUNCPTR(FcConfigGetCurrent);
700 LOAD_FUNCPTR(FcFontList);
701 LOAD_FUNCPTR(FcFontSetDestroy);
702 LOAD_FUNCPTR(FcInit);
703 LOAD_FUNCPTR(FcObjectSetAdd);
704 LOAD_FUNCPTR(FcObjectSetCreate);
705 LOAD_FUNCPTR(FcObjectSetDestroy);
706 LOAD_FUNCPTR(FcPatternCreate);
707 LOAD_FUNCPTR(FcPatternDestroy);
708 LOAD_FUNCPTR(FcPatternGet);
711 if(!pFcInit()) return;
713 config = pFcConfigGetCurrent();
714 pat = pFcPatternCreate();
715 os = pFcObjectSetCreate();
716 pFcObjectSetAdd(os, FC_FILE);
717 fontset = pFcFontList(config, pat, os);
719 for(i = 0; i < fontset->nfont; i++) {
720 if(pFcPatternGet(fontset->fonts[i], FC_FILE, 0, &v) != FcResultMatch)
722 if(v.type != FcTypeString) continue;
723 TRACE("fontconfig: %s\n", v.u.s);
725 /* We're just interested in OT/TT fonts for now, so this hack just
726 picks up the standard extensions to save time loading every other
729 if(len < 4) continue;
730 ext = v.u.s + len - 3;
731 if(!strcasecmp(ext, "ttf") || !strcasecmp(ext, "ttc") || !strcasecmp(ext, "otf"))
732 AddFontFileToList(v.u.s, NULL, TRUE);
734 pFcFontSetDestroy(fontset);
735 pFcObjectSetDestroy(os);
736 pFcPatternDestroy(pat);
742 /*************************************************************
744 * This adds registry entries for any externally loaded fonts
745 * (fonts from fontconfig or FontDirs). It also deletes entries
746 * of no longer existing fonts.
749 void update_reg_entries(void)
751 HKEY winkey = 0, externalkey = 0;
754 DWORD dlen, vlen, datalen, valuelen, i, type, len, len_fam;
758 static const WCHAR TrueType[] = {' ','(','T','r','u','e','T','y','p','e',')','\0'};
759 static const WCHAR spaceW[] = {' ', '\0'};
762 if(RegCreateKeyExW(HKEY_LOCAL_MACHINE, is_win9x() ? win9x_font_reg_key : winnt_font_reg_key,
763 0, NULL, 0, KEY_ALL_ACCESS, NULL, &winkey, NULL) != ERROR_SUCCESS) {
764 ERR("Can't create Windows font reg key\n");
767 if(RegCreateKeyExW(HKEY_LOCAL_MACHINE, external_fonts_reg_key,
768 0, NULL, 0, KEY_ALL_ACCESS, NULL, &externalkey, NULL) != ERROR_SUCCESS) {
769 ERR("Can't create external font reg key\n");
773 /* Delete all external fonts added last time */
775 RegQueryInfoKeyW(externalkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
776 &valuelen, &datalen, NULL, NULL);
777 valuelen++; /* returned value doesn't include room for '\0' */
778 valueW = HeapAlloc(GetProcessHeap(), 0, valuelen * sizeof(WCHAR));
779 data = HeapAlloc(GetProcessHeap(), 0, datalen * sizeof(WCHAR));
781 dlen = datalen * sizeof(WCHAR);
784 while(RegEnumValueW(externalkey, i++, valueW, &vlen, NULL, &type, data,
785 &dlen) == ERROR_SUCCESS) {
787 RegDeleteValueW(winkey, valueW);
788 /* reset dlen and vlen */
792 HeapFree(GetProcessHeap(), 0, data);
793 HeapFree(GetProcessHeap(), 0, valueW);
795 /* Delete the old external fonts key */
796 RegCloseKey(externalkey);
798 RegDeleteKeyW(HKEY_LOCAL_MACHINE, external_fonts_reg_key);
800 if(RegCreateKeyExW(HKEY_LOCAL_MACHINE, external_fonts_reg_key,
801 0, NULL, 0, KEY_ALL_ACCESS, NULL, &externalkey, NULL) != ERROR_SUCCESS) {
802 ERR("Can't create external font reg key\n");
806 /* enumerate the fonts and add external ones to the two keys */
808 for(family = FontList; family; family = family->next) {
809 len_fam = strlenW(family->FamilyName) + sizeof(TrueType) / sizeof(WCHAR) + 1;
810 for(face = family->FirstFace; face; face = face->next) {
811 if(!face->external) continue;
813 if(strcmpiW(face->StyleName, RegularW))
814 len = len_fam + strlenW(face->StyleName) + 1;
815 valueW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
816 strcpyW(valueW, family->FamilyName);
818 strcatW(valueW, spaceW);
819 strcatW(valueW, face->StyleName);
821 strcatW(valueW, TrueType);
822 if((path = strrchr(face->file, '/')) == NULL)
826 len = MultiByteToWideChar(CP_ACP, 0, path, -1, NULL, 0);
828 file = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
829 MultiByteToWideChar(CP_ACP, 0, path, -1, file, len);
830 RegSetValueExW(winkey, valueW, 0, REG_SZ, (BYTE*)file, len * sizeof(WCHAR));
831 RegSetValueExW(externalkey, valueW, 0, REG_SZ, (BYTE*)file, len * sizeof(WCHAR));
833 HeapFree(GetProcessHeap(), 0, file);
834 HeapFree(GetProcessHeap(), 0, valueW);
839 RegCloseKey(externalkey);
846 /*************************************************************
847 * WineEngAddFontResourceEx
850 INT WineEngAddFontResourceEx(LPCWSTR file, DWORD flags, PVOID pdv)
852 if (ft_handle) /* do it only if we have freetype up and running */
857 FIXME("Ignoring flags %lx\n", flags);
859 if((unixname = wine_get_unix_file_name(file)))
861 AddFontFileToList(unixname, NULL, FALSE);
862 HeapFree(GetProcessHeap(), 0, unixname);
868 /*************************************************************
869 * WineEngRemoveFontResourceEx
872 BOOL WineEngRemoveFontResourceEx(LPCWSTR file, DWORD flags, PVOID pdv)
878 /*************************************************************
881 * Initialize FreeType library and create a list of available faces
883 BOOL WineEngInit(void)
885 static const WCHAR fontsW[] = {'\\','F','o','n','t','s','\0'};
887 DWORD valuelen, datalen, i = 0, type, dlen, vlen;
889 WCHAR windowsdir[MAX_PATH];
895 ft_handle = wine_dlopen(SONAME_LIBFREETYPE, RTLD_NOW, NULL, 0);
898 "Wine cannot find the FreeType font library. To enable Wine to\n"
899 "use TrueType fonts please install a version of FreeType greater than\n"
900 "or equal to 2.0.5.\n"
901 "http://www.freetype.org\n");
905 #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;}
907 LOAD_FUNCPTR(FT_Vector_Unit)
908 LOAD_FUNCPTR(FT_Done_Face)
909 LOAD_FUNCPTR(FT_Get_Char_Index)
910 LOAD_FUNCPTR(FT_Get_Sfnt_Table)
911 LOAD_FUNCPTR(FT_Init_FreeType)
912 LOAD_FUNCPTR(FT_Load_Glyph)
913 LOAD_FUNCPTR(FT_Matrix_Multiply)
914 LOAD_FUNCPTR(FT_MulFix)
915 LOAD_FUNCPTR(FT_New_Face)
916 LOAD_FUNCPTR(FT_Outline_Get_Bitmap)
917 LOAD_FUNCPTR(FT_Outline_Transform)
918 LOAD_FUNCPTR(FT_Outline_Translate)
919 LOAD_FUNCPTR(FT_Select_Charmap)
920 LOAD_FUNCPTR(FT_Set_Pixel_Sizes)
921 LOAD_FUNCPTR(FT_Vector_Transform)
924 /* Don't warn if this one is missing */
925 pFT_Library_Version = wine_dlsym(ft_handle, "FT_Library_Version", NULL, 0);
926 pFT_Load_Sfnt_Table = wine_dlsym(ft_handle, "FT_Load_Sfnt_Table", NULL, 0);
927 pFT_Get_First_Char = wine_dlsym(ft_handle, "FT_Get_First_Char", NULL, 0);
929 if(!wine_dlsym(ft_handle, "FT_Get_Postscript_Name", NULL, 0) &&
930 !wine_dlsym(ft_handle, "FT_Sqrt64", NULL, 0)) {
931 /* try to avoid 2.0.4: >= 2.0.5 has FT_Get_Postscript_Name and
932 <= 2.0.3 has FT_Sqrt64 */
936 if(pFT_Init_FreeType(&library) != 0) {
937 ERR("Can't init FreeType library\n");
938 wine_dlclose(ft_handle, NULL, 0);
942 FT_Version.major=FT_Version.minor=FT_Version.patch=-1;
943 if (pFT_Library_Version)
945 pFT_Library_Version(library,&FT_Version.major,&FT_Version.minor,&FT_Version.patch);
947 if (FT_Version.major<=0)
953 TRACE("FreeType version is %d.%d.%d\n",FT_Version.major,FT_Version.minor,FT_Version.patch);
955 if((font_mutex = CreateMutexW(NULL, FALSE, font_mutex_nameW)) == NULL) {
956 ERR("Failed to create font mutex\n");
959 WaitForSingleObject(font_mutex, INFINITE);
961 /* load in the fonts from %WINDOWSDIR%\\Fonts first of all */
962 GetWindowsDirectoryW(windowsdir, sizeof(windowsdir) / sizeof(WCHAR));
963 strcatW(windowsdir, fontsW);
964 if((unixname = wine_get_unix_file_name(windowsdir)))
966 ReadFontDir(unixname, FALSE);
967 HeapFree(GetProcessHeap(), 0, unixname);
970 /* now look under HKLM\Software\Microsoft\Windows[ NT]\CurrentVersion\Fonts
971 for any fonts not installed in %WINDOWSDIR%\Fonts. They will have their
972 full path as the entry */
973 if(RegOpenKeyW(HKEY_LOCAL_MACHINE,
974 is_win9x() ? win9x_font_reg_key : winnt_font_reg_key,
975 &hkey) == ERROR_SUCCESS) {
977 RegQueryInfoKeyW(hkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
978 &valuelen, &datalen, NULL, NULL);
980 valuelen++; /* returned value doesn't include room for '\0' */
981 valueW = HeapAlloc(GetProcessHeap(), 0, valuelen * sizeof(WCHAR));
982 data = HeapAlloc(GetProcessHeap(), 0, datalen * sizeof(WCHAR));
985 dlen = datalen * sizeof(WCHAR);
987 while(RegEnumValueW(hkey, i++, valueW, &vlen, NULL, &type, data,
988 &dlen) == ERROR_SUCCESS) {
989 if(((LPWSTR)data)[0] && ((LPWSTR)data)[1] == ':')
990 if((unixname = wine_get_unix_file_name((LPWSTR)data)))
992 AddFontFileToList(unixname, NULL, FALSE);
993 HeapFree(GetProcessHeap(), 0, unixname);
995 /* reset dlen and vlen */
1000 if (data) HeapFree(GetProcessHeap(), 0, data);
1001 if (valueW) HeapFree(GetProcessHeap(), 0, valueW);
1005 load_fontconfig_fonts();
1007 /* then look in any directories that we've specified in the config file */
1008 if(RegOpenKeyA(HKEY_LOCAL_MACHINE,
1009 "Software\\Wine\\Wine\\Config\\FontDirs",
1010 &hkey) == ERROR_SUCCESS) {
1012 RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1013 &valuelen, &datalen, NULL, NULL);
1015 valuelen++; /* returned value doesn't include room for '\0' */
1016 value = HeapAlloc(GetProcessHeap(), 0, valuelen);
1017 data = HeapAlloc(GetProcessHeap(), 0, datalen);
1022 while(RegEnumValueA(hkey, i++, value, &vlen, NULL, &type, data,
1023 &dlen) == ERROR_SUCCESS) {
1024 TRACE("Got %s=%s\n", value, (LPSTR)data);
1025 ReadFontDir((LPSTR)data, TRUE);
1026 /* reset dlen and vlen */
1030 HeapFree(GetProcessHeap(), 0, data);
1031 HeapFree(GetProcessHeap(), 0, value);
1039 update_reg_entries();
1041 ReleaseMutex(font_mutex);
1045 "Wine cannot find certain functions that it needs inside the FreeType\n"
1046 "font library. To enable Wine to use TrueType fonts please upgrade\n"
1047 "FreeType to at least version 2.0.5.\n"
1048 "http://www.freetype.org\n");
1049 wine_dlclose(ft_handle, NULL, 0);
1055 static LONG calc_ppem_for_height(FT_Face ft_face, LONG height)
1058 TT_HoriHeader *pHori;
1062 pOS2 = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_os2);
1063 pHori = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_hhea);
1065 if(height == 0) height = 16;
1067 /* Calc. height of EM square:
1069 * For +ve lfHeight we have
1070 * lfHeight = (winAscent + winDescent) * ppem / units_per_em
1071 * Re-arranging gives:
1072 * ppem = units_per_em * lfheight / (winAscent + winDescent)
1074 * For -ve lfHeight we have
1076 * [i.e. |lfHeight| = (winAscent + winDescent - il) * ppem / units_per_em
1077 * with il = winAscent + winDescent - units_per_em]
1082 if(pOS2->usWinAscent + pOS2->usWinDescent == 0)
1083 ppem = ft_face->units_per_EM * height /
1084 (pHori->Ascender - pHori->Descender);
1086 ppem = ft_face->units_per_EM * height /
1087 (pOS2->usWinAscent + pOS2->usWinDescent);
1095 static LONG load_VDMX(GdiFont, LONG);
1097 static FT_Face OpenFontFile(GdiFont font, char *file, FT_Long face_index, LONG height)
1103 err = pFT_New_Face(library, file, face_index, &ft_face);
1105 ERR("FT_New_Face rets %d\n", err);
1109 /* set it here, as load_VDMX needs it */
1110 font->ft_face = ft_face;
1112 /* load the VDMX table if we have one */
1113 ppem = load_VDMX(font, height);
1115 ppem = calc_ppem_for_height(ft_face, height);
1117 pFT_Set_Pixel_Sizes(ft_face, 0, ppem);
1123 static int get_nearest_charset(Face *face)
1125 /* Only get here if lfCharSet == DEFAULT_CHARSET or we couldn't find
1126 a single face with the requested charset. The idea is to check if
1127 the selected font supports the current ANSI codepage, if it does
1128 return the corresponding charset, else return the first charset */
1131 int acp = GetACP(), i;
1134 if(TranslateCharsetInfo((DWORD*)acp, &csi, TCI_SRCCODEPAGE))
1135 if(csi.fs.fsCsb[0] & face->fs.fsCsb[0])
1136 return csi.ciCharset;
1138 for(i = 0; i < 32; i++) {
1140 if(face->fs.fsCsb[0] & fs0) {
1141 if(TranslateCharsetInfo(&fs0, &csi, TCI_SRCFONTSIG))
1142 return csi.ciCharset;
1144 FIXME("TCI failing on %lx\n", fs0);
1148 FIXME("returning DEFAULT_CHARSET face->fs.fsCsb[0] = %08lx file = %s\n",
1149 face->fs.fsCsb[0], face->file);
1150 return DEFAULT_CHARSET;
1153 static GdiFont alloc_font(void)
1155 GdiFont ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*ret));
1156 ret->gmsize = INIT_GM_SIZE;
1157 ret->gm = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1158 ret->gmsize * sizeof(*ret->gm));
1161 ret->xform.eM11 = ret->xform.eM22 = 1.0;
1165 static void free_font(GdiFont font)
1167 if (font->ft_face) pFT_Done_Face(font->ft_face);
1168 if (font->potm) HeapFree(GetProcessHeap(), 0, font->potm);
1169 if (font->name) HeapFree(GetProcessHeap(), 0, font->name);
1170 HeapFree(GetProcessHeap(), 0, font->gm);
1171 HeapFree(GetProcessHeap(), 0, font);
1175 /*************************************************************
1178 * load the vdmx entry for the specified height
1181 #define MS_MAKE_TAG( _x1, _x2, _x3, _x4 ) \
1182 ( ( (FT_ULong)_x4 << 24 ) | \
1183 ( (FT_ULong)_x3 << 16 ) | \
1184 ( (FT_ULong)_x2 << 8 ) | \
1187 #define MS_VDMX_TAG MS_MAKE_TAG('V', 'D', 'M', 'X')
1197 static LONG load_VDMX(GdiFont font, LONG height)
1199 BYTE hdr[6], tmp[2], group[4];
1200 BYTE devXRatio, devYRatio;
1201 USHORT numRecs, numRatios;
1206 result = WineEngGetFontData(font, MS_VDMX_TAG, 0, hdr, 6);
1208 if(result == GDI_ERROR) /* no vdmx table present, use linear scaling */
1211 /* FIXME: need the real device aspect ratio */
1215 numRecs = GET_BE_WORD(&hdr[2]);
1216 numRatios = GET_BE_WORD(&hdr[4]);
1218 TRACE("numRecs = %d numRatios = %d\n", numRecs, numRatios);
1219 for(i = 0; i < numRatios; i++) {
1222 offset = (3 * 2) + (i * sizeof(Ratios));
1223 WineEngGetFontData(font, MS_VDMX_TAG, offset, &ratio, sizeof(Ratios));
1226 TRACE("Ratios[%d] %d %d : %d -> %d\n", i, ratio.bCharSet, ratio.xRatio, ratio.yStartRatio, ratio.yEndRatio);
1228 if(ratio.bCharSet != 1)
1231 if((ratio.xRatio == 0 &&
1232 ratio.yStartRatio == 0 &&
1233 ratio.yEndRatio == 0) ||
1234 (devXRatio == ratio.xRatio &&
1235 devYRatio >= ratio.yStartRatio &&
1236 devYRatio <= ratio.yEndRatio))
1238 offset = (3 * 2) + (numRatios * 4) + (i * 2);
1239 WineEngGetFontData(font, MS_VDMX_TAG, offset, tmp, 2);
1240 offset = GET_BE_WORD(tmp);
1246 FIXME("No suitable ratio found\n");
1250 if(WineEngGetFontData(font, MS_VDMX_TAG, offset, group, 4) != GDI_ERROR) {
1252 BYTE startsz, endsz;
1255 recs = GET_BE_WORD(group);
1259 TRACE("recs=%d startsz=%d endsz=%d\n", recs, startsz, endsz);
1261 vTable = HeapAlloc(GetProcessHeap(), 0, recs * 6);
1262 result = WineEngGetFontData(font, MS_VDMX_TAG, offset + 4, vTable, recs * 6);
1263 if(result == GDI_ERROR) {
1264 FIXME("Failed to retrieve vTable\n");
1269 for(i = 0; i < recs; i++) {
1270 SHORT yMax = GET_BE_WORD(&vTable[(i * 6) + 2]);
1271 SHORT yMin = GET_BE_WORD(&vTable[(i * 6) + 4]);
1272 ppem = GET_BE_WORD(&vTable[i * 6]);
1274 if(yMax + -yMin == height) {
1277 TRACE("ppem %ld found; height=%ld yMax=%d yMin=%d\n", ppem, height, font->yMax, font->yMin);
1280 if(yMax + -yMin > height) {
1283 goto end; /* failed */
1285 font->yMax = GET_BE_WORD(&vTable[(i * 6) + 2]);
1286 font->yMin = GET_BE_WORD(&vTable[(i * 6) + 4]);
1287 TRACE("ppem %ld found; height=%ld yMax=%d yMin=%d\n", ppem, height, font->yMax, font->yMin);
1293 TRACE("ppem not found for height %ld\n", height);
1297 if(ppem < startsz || ppem > endsz)
1300 for(i = 0; i < recs; i++) {
1302 yPelHeight = GET_BE_WORD(&vTable[i * 6]);
1304 if(yPelHeight > ppem)
1307 if(yPelHeight == ppem) {
1308 font->yMax = GET_BE_WORD(&vTable[(i * 6) + 2]);
1309 font->yMin = GET_BE_WORD(&vTable[(i * 6) + 4]);
1310 TRACE("ppem %ld found; yMax=%d yMin=%d\n", ppem, font->yMax, font->yMin);
1316 HeapFree(GetProcessHeap(), 0, vTable);
1323 /*************************************************************
1324 * WineEngCreateFontInstance
1327 GdiFont WineEngCreateFontInstance(DC *dc, HFONT hfont)
1331 Family *family = NULL;
1337 if (!GetObjectW( hfont, sizeof(lf), &lf )) return NULL;
1339 TRACE("%s, h=%ld, it=%d, weight=%ld, PandF=%02x, charset=%d orient %ld escapement %ld\n",
1340 debugstr_w(lf.lfFaceName), lf.lfHeight, lf.lfItalic,
1341 lf.lfWeight, lf.lfPitchAndFamily, lf.lfCharSet, lf.lfOrientation,
1344 /* check the cache first */
1345 for(ret = GdiFontList; ret; ret = ret->next) {
1346 if(ret->hfont == hfont && !memcmp(&ret->xform, &dc->xformWorld2Vport, offsetof(XFORM, eDx))) {
1347 TRACE("returning cached gdiFont(%p) for hFont %p\n", ret, hfont);
1352 if(!FontList || !have_installed_roman_font) /* No fonts installed */
1354 TRACE("No fonts installed\n");
1359 memcpy(&ret->xform, &dc->xformWorld2Vport, sizeof(XFORM));
1361 /* If lfFaceName is "Symbol" then Windows fixes up lfCharSet to
1362 SYMBOL_CHARSET so that Symbol gets picked irrespective of the
1363 original value lfCharSet. Note this is a special case for
1364 Symbol and doesn't happen at least for "Wingdings*" */
1366 if(!strcmpiW(lf.lfFaceName, SymbolW))
1367 lf.lfCharSet = SYMBOL_CHARSET;
1369 if(!TranslateCharsetInfo((DWORD*)(INT)lf.lfCharSet, &csi, TCI_SRCCHARSET)) {
1370 switch(lf.lfCharSet) {
1371 case DEFAULT_CHARSET:
1372 csi.fs.fsCsb[0] = 0;
1375 FIXME("Untranslated charset %d\n", lf.lfCharSet);
1376 csi.fs.fsCsb[0] = 0;
1381 if(lf.lfFaceName[0] != '\0') {
1383 for(psub = substlist; psub; psub = psub->next)
1384 if(!strcmpiW(lf.lfFaceName, psub->from.name) &&
1385 (psub->from.charset == -1 ||
1386 psub->from.charset == lf.lfCharSet))
1389 TRACE("substituting %s -> %s\n", debugstr_w(lf.lfFaceName),
1390 debugstr_w(psub->to.name));
1391 strcpyW(lf.lfFaceName, psub->to.name);
1394 /* We want a match on name and charset or just name if
1395 charset was DEFAULT_CHARSET. If the latter then
1396 we fixup the returned charset later in get_nearest_charset
1397 where we'll either use the charset of the current ansi codepage
1398 or if that's unavailable the first charset that the font supports.
1400 for(family = FontList; family; family = family->next) {
1401 if(!strcmpiW(family->FamilyName, lf.lfFaceName))
1402 if((csi.fs.fsCsb[0] & family->FirstFace->fs.fsCsb[0]) || !csi.fs.fsCsb[0])
1406 if(!family) { /* do other aliases here */
1407 if(!strcmpiW(lf.lfFaceName, SystemW))
1408 strcpyW(lf.lfFaceName, defSystem);
1409 else if(!strcmpiW(lf.lfFaceName, MSSansSerifW))
1410 strcpyW(lf.lfFaceName, defSans);
1411 else if(!strcmpiW(lf.lfFaceName, HelvW))
1412 strcpyW(lf.lfFaceName, defSans);
1416 for(family = FontList; family; family = family->next) {
1417 if(!strcmpiW(family->FamilyName, lf.lfFaceName))
1418 if((csi.fs.fsCsb[0] & family->FirstFace->fs.fsCsb[0]) || !csi.fs.fsCsb[0])
1426 /* If requested charset was DEFAULT_CHARSET then try using charset
1427 corresponding to the current ansi codepage */
1428 if(!csi.fs.fsCsb[0]) {
1430 if(!TranslateCharsetInfo((DWORD*)acp, &csi, TCI_SRCCODEPAGE)) {
1431 FIXME("TCI failed on codepage %d\n", acp);
1432 csi.fs.fsCsb[0] = 0;
1434 lf.lfCharSet = csi.ciCharset;
1437 /* Face families are in the top 4 bits of lfPitchAndFamily,
1438 so mask with 0xF0 before testing */
1440 if((lf.lfPitchAndFamily & FIXED_PITCH) ||
1441 (lf.lfPitchAndFamily & 0xF0) == FF_MODERN)
1442 strcpyW(lf.lfFaceName, defFixed);
1443 else if((lf.lfPitchAndFamily & 0xF0) == FF_ROMAN)
1444 strcpyW(lf.lfFaceName, defSerif);
1445 else if((lf.lfPitchAndFamily & 0xF0) == FF_SWISS)
1446 strcpyW(lf.lfFaceName, defSans);
1448 strcpyW(lf.lfFaceName, defSans);
1449 for(family = FontList; family; family = family->next) {
1450 if(!strcmpiW(family->FamilyName, lf.lfFaceName) &&
1451 (csi.fs.fsCsb[0] & family->FirstFace->fs.fsCsb[0]))
1457 for(family = FontList; family; family = family->next) {
1458 if(csi.fs.fsCsb[0] & family->FirstFace->fs.fsCsb[0])
1465 csi.fs.fsCsb[0] = 0;
1466 FIXME("just using first face for now\n");
1469 it = lf.lfItalic ? 1 : 0;
1470 bd = lf.lfWeight > 550 ? 1 : 0;
1472 for(face = family->FirstFace; face; face = face->next) {
1473 if(!(face->Italic ^ it) && !(face->Bold ^ bd))
1477 face = family->FirstFace;
1478 if(it && !face->Italic) ret->fake_italic = TRUE;
1479 if(bd && !face->Bold) ret->fake_bold = TRUE;
1482 memcpy(&ret->fs, &face->fs, sizeof(FONTSIGNATURE));
1485 ret->charset = lf.lfCharSet;
1487 ret->charset = get_nearest_charset(face);
1489 TRACE("Chosen: %s %s\n", debugstr_w(family->FamilyName),
1490 debugstr_w(face->StyleName));
1492 height = GDI_ROUND( (FLOAT)lf.lfHeight * dc->xformWorld2Vport.eM22 );
1493 ret->ft_face = OpenFontFile(ret, face->file, face->face_index,
1494 lf.lfHeight < 0 ? -abs(height) : abs(height));
1501 if (ret->charset == SYMBOL_CHARSET &&
1502 !pFT_Select_Charmap(ret->ft_face, ft_encoding_symbol)) {
1505 else if (!pFT_Select_Charmap(ret->ft_face, ft_encoding_unicode)) {
1509 pFT_Select_Charmap(ret->ft_face, ft_encoding_apple_roman);
1512 ret->orientation = lf.lfOrientation;
1513 ret->name = strdupW(family->FamilyName);
1515 TRACE("caching: gdiFont=%p hfont=%p\n", ret, hfont);
1517 ret->aveWidth= lf.lfWidth;
1518 ret->next = GdiFontList;
1524 static void DumpGdiFontList(void)
1528 TRACE("---------- gdiFont Cache ----------\n");
1529 for(gdiFont = GdiFontList; gdiFont; gdiFont = gdiFont->next) {
1531 GetObjectW( gdiFont->hfont, sizeof(lf), &lf );
1532 TRACE("gdiFont=%p hfont=%p (%s)\n",
1533 gdiFont, gdiFont->hfont, debugstr_w(lf.lfFaceName));
1537 /*************************************************************
1538 * WineEngDestroyFontInstance
1540 * free the gdiFont associated with this handle
1543 BOOL WineEngDestroyFontInstance(HFONT handle)
1546 GdiFont gdiPrev = NULL;
1549 TRACE("destroying hfont=%p\n", handle);
1553 gdiFont = GdiFontList;
1555 if(gdiFont->hfont == handle) {
1557 gdiPrev->next = gdiFont->next;
1559 gdiFont = gdiPrev->next;
1561 GdiFontList = gdiFont->next;
1563 gdiFont = GdiFontList;
1568 gdiFont = gdiFont->next;
1574 static void GetEnumStructs(Face *face, LPENUMLOGFONTEXW pelf,
1575 NEWTEXTMETRICEXW *pntm, LPDWORD ptype)
1577 OUTLINETEXTMETRICW *potm;
1579 GdiFont font = alloc_font();
1581 if (!(font->ft_face = OpenFontFile(font, face->file, face->face_index, 100)))
1587 font->name = strdupW(face->family->FamilyName);
1589 memset(&pelf->elfLogFont, 0, sizeof(LOGFONTW));
1591 size = WineEngGetOutlineTextMetrics(font, 0, NULL);
1592 potm = HeapAlloc(GetProcessHeap(), 0, size);
1593 WineEngGetOutlineTextMetrics(font, size, potm);
1595 #define TM potm->otmTextMetrics
1597 pntm->ntmTm.tmHeight = pelf->elfLogFont.lfHeight = TM.tmHeight;
1598 pntm->ntmTm.tmAscent = TM.tmAscent;
1599 pntm->ntmTm.tmDescent = TM.tmDescent;
1600 pntm->ntmTm.tmInternalLeading = TM.tmInternalLeading;
1601 pntm->ntmTm.tmExternalLeading = TM.tmExternalLeading;
1602 pntm->ntmTm.tmAveCharWidth = pelf->elfLogFont.lfWidth = TM.tmAveCharWidth;
1603 pntm->ntmTm.tmMaxCharWidth = TM.tmMaxCharWidth;
1604 pntm->ntmTm.tmWeight = pelf->elfLogFont.lfWeight = TM.tmWeight;
1605 pntm->ntmTm.tmOverhang = TM.tmOverhang;
1606 pntm->ntmTm.tmDigitizedAspectX = TM.tmDigitizedAspectX;
1607 pntm->ntmTm.tmDigitizedAspectY = TM.tmDigitizedAspectY;
1608 pntm->ntmTm.tmFirstChar = TM.tmFirstChar;
1609 pntm->ntmTm.tmLastChar = TM.tmLastChar;
1610 pntm->ntmTm.tmDefaultChar = TM.tmDefaultChar;
1611 pntm->ntmTm.tmBreakChar = TM.tmBreakChar;
1612 pntm->ntmTm.tmItalic = pelf->elfLogFont.lfItalic = TM.tmItalic;
1613 pntm->ntmTm.tmUnderlined = pelf->elfLogFont.lfUnderline = TM.tmUnderlined;
1614 pntm->ntmTm.tmStruckOut = pelf->elfLogFont.lfStrikeOut = TM.tmStruckOut;
1615 pntm->ntmTm.tmPitchAndFamily = TM.tmPitchAndFamily;
1616 pelf->elfLogFont.lfPitchAndFamily = (TM.tmPitchAndFamily & 0xf1) + 1;
1617 pntm->ntmTm.tmCharSet = pelf->elfLogFont.lfCharSet = TM.tmCharSet;
1618 pelf->elfLogFont.lfOutPrecision = OUT_STROKE_PRECIS;
1619 pelf->elfLogFont.lfClipPrecision = CLIP_STROKE_PRECIS;
1620 pelf->elfLogFont.lfQuality = DRAFT_QUALITY;
1622 pntm->ntmTm.ntmFlags = TM.tmItalic ? NTM_ITALIC : 0;
1623 if(TM.tmWeight > 550) pntm->ntmTm.ntmFlags |= NTM_BOLD;
1624 if(pntm->ntmTm.ntmFlags == 0) pntm->ntmTm.ntmFlags = NTM_REGULAR;
1626 pntm->ntmTm.ntmSizeEM = potm->otmEMSquare;
1627 pntm->ntmTm.ntmCellHeight = 0;
1628 pntm->ntmTm.ntmAvgWidth = 0;
1630 *ptype = TM.tmPitchAndFamily & TMPF_TRUETYPE ? TRUETYPE_FONTTYPE : 0;
1631 if(!(TM.tmPitchAndFamily & TMPF_VECTOR))
1632 *ptype |= RASTER_FONTTYPE;
1635 memset(&pntm->ntmFontSig, 0, sizeof(FONTSIGNATURE));
1637 strncpyW(pelf->elfLogFont.lfFaceName,
1638 (WCHAR*)((char*)potm + (ptrdiff_t)potm->otmpFamilyName),
1640 strncpyW(pelf->elfFullName,
1641 (WCHAR*)((char*)potm + (ptrdiff_t)potm->otmpFaceName),
1643 strncpyW(pelf->elfStyle,
1644 (WCHAR*)((char*)potm + (ptrdiff_t)potm->otmpStyleName),
1646 pelf->elfScript[0] = '\0'; /* This will get set in WineEngEnumFonts */
1648 HeapFree(GetProcessHeap(), 0, potm);
1653 /*************************************************************
1657 DWORD WineEngEnumFonts(LPLOGFONTW plf, FONTENUMPROCW proc, LPARAM lparam)
1662 NEWTEXTMETRICEXW ntm;
1663 DWORD type, ret = 1;
1669 TRACE("facename = %s charset %d\n", debugstr_w(plf->lfFaceName), plf->lfCharSet);
1671 if(plf->lfFaceName[0]) {
1673 for(psub = substlist; psub; psub = psub->next)
1674 if(!strcmpiW(plf->lfFaceName, psub->from.name) &&
1675 (psub->from.charset == -1 ||
1676 psub->from.charset == plf->lfCharSet))
1679 TRACE("substituting %s -> %s\n", debugstr_w(plf->lfFaceName),
1680 debugstr_w(psub->to.name));
1681 memcpy(&lf, plf, sizeof(lf));
1682 strcpyW(lf.lfFaceName, psub->to.name);
1685 for(family = FontList; family; family = family->next) {
1686 if(!strcmpiW(plf->lfFaceName, family->FamilyName)) {
1687 for(face = family->FirstFace; face; face = face->next) {
1688 GetEnumStructs(face, &elf, &ntm, &type);
1689 for(i = 0; i < 32; i++) {
1690 if(face->fs.fsCsb[0] & (1L << i)) {
1691 fs.fsCsb[0] = 1L << i;
1693 if(!TranslateCharsetInfo(fs.fsCsb, &csi,
1695 csi.ciCharset = DEFAULT_CHARSET;
1696 if(i == 31) csi.ciCharset = SYMBOL_CHARSET;
1697 if(csi.ciCharset != DEFAULT_CHARSET) {
1698 elf.elfLogFont.lfCharSet =
1699 ntm.ntmTm.tmCharSet = csi.ciCharset;
1701 strcpyW(elf.elfScript, ElfScriptsW[i]);
1703 FIXME("Unknown elfscript for bit %d\n", i);
1704 TRACE("enuming face %s full %s style %s charset %d type %ld script %s it %d weight %ld ntmflags %08lx\n",
1705 debugstr_w(elf.elfLogFont.lfFaceName),
1706 debugstr_w(elf.elfFullName), debugstr_w(elf.elfStyle),
1707 csi.ciCharset, type, debugstr_w(elf.elfScript),
1708 elf.elfLogFont.lfItalic, elf.elfLogFont.lfWeight,
1709 ntm.ntmTm.ntmFlags);
1710 ret = proc(&elf.elfLogFont, (TEXTMETRICW *)&ntm, type, lparam);
1719 for(family = FontList; family; family = family->next) {
1720 GetEnumStructs(family->FirstFace, &elf, &ntm, &type);
1721 for(i = 0; i < 32; i++) {
1722 if(family->FirstFace->fs.fsCsb[0] & (1L << i)) {
1723 fs.fsCsb[0] = 1L << i;
1725 if(!TranslateCharsetInfo(fs.fsCsb, &csi,
1727 csi.ciCharset = DEFAULT_CHARSET;
1728 if(i == 31) csi.ciCharset = SYMBOL_CHARSET;
1729 if(csi.ciCharset != DEFAULT_CHARSET) {
1730 elf.elfLogFont.lfCharSet = ntm.ntmTm.tmCharSet =
1733 strcpyW(elf.elfScript, ElfScriptsW[i]);
1735 FIXME("Unknown elfscript for bit %d\n", i);
1736 TRACE("enuming face %s full %s style %s charset = %d type %ld script %s it %d weight %ld ntmflags %08lx\n",
1737 debugstr_w(elf.elfLogFont.lfFaceName),
1738 debugstr_w(elf.elfFullName), debugstr_w(elf.elfStyle),
1739 csi.ciCharset, type, debugstr_w(elf.elfScript),
1740 elf.elfLogFont.lfItalic, elf.elfLogFont.lfWeight,
1741 ntm.ntmTm.ntmFlags);
1742 ret = proc(&elf.elfLogFont, (TEXTMETRICW *)&ntm, type, lparam);
1753 static void FTVectorToPOINTFX(FT_Vector *vec, POINTFX *pt)
1755 pt->x.value = vec->x >> 6;
1756 pt->x.fract = (vec->x & 0x3f) << 10;
1757 pt->x.fract |= ((pt->x.fract >> 6) | (pt->x.fract >> 12));
1758 pt->y.value = vec->y >> 6;
1759 pt->y.fract = (vec->y & 0x3f) << 10;
1760 pt->y.fract |= ((pt->y.fract >> 6) | (pt->y.fract >> 12));
1764 static FT_UInt get_glyph_index(GdiFont font, UINT glyph)
1766 if(font->charset == SYMBOL_CHARSET && glyph < 0x100)
1767 glyph = glyph + 0xf000;
1768 return pFT_Get_Char_Index(font->ft_face, glyph);
1771 /*************************************************************
1772 * WineEngGetGlyphIndices
1774 * FIXME: add support for GGI_MARK_NONEXISTING_GLYPHS
1776 DWORD WineEngGetGlyphIndices(GdiFont font, LPCWSTR lpstr, INT count,
1777 LPWORD pgi, DWORD flags)
1781 for(i = 0; i < count; i++)
1782 pgi[i] = get_glyph_index(font, lpstr[i]);
1787 /*************************************************************
1788 * WineEngGetGlyphOutline
1790 * Behaves in exactly the same way as the win32 api GetGlyphOutline
1791 * except that the first parameter is the HWINEENGFONT of the font in
1792 * question rather than an HDC.
1795 DWORD WineEngGetGlyphOutline(GdiFont font, UINT glyph, UINT format,
1796 LPGLYPHMETRICS lpgm, DWORD buflen, LPVOID buf,
1799 static const FT_Matrix identityMat = {(1 << 16), 0, 0, (1 << 16)};
1800 FT_Face ft_face = font->ft_face;
1801 FT_UInt glyph_index;
1802 DWORD width, height, pitch, needed = 0;
1803 FT_Bitmap ft_bitmap;
1805 INT left, right, top = 0, bottom = 0;
1807 FT_Int load_flags = FT_LOAD_DEFAULT | FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
1808 float widthRatio = 1.0;
1809 FT_Matrix transMat = identityMat;
1810 BOOL needsTransform = FALSE;
1813 TRACE("%p, %04x, %08x, %p, %08lx, %p, %p\n", font, glyph, format, lpgm,
1814 buflen, buf, lpmat);
1816 if(format & GGO_GLYPH_INDEX) {
1817 glyph_index = glyph;
1818 format &= ~GGO_GLYPH_INDEX;
1820 glyph_index = get_glyph_index(font, glyph);
1822 if(glyph_index >= font->gmsize) {
1823 font->gmsize = (glyph_index / INIT_GM_SIZE + 1) * INIT_GM_SIZE;
1824 font->gm = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, font->gm,
1825 font->gmsize * sizeof(*font->gm));
1827 if(format == GGO_METRICS && font->gm[glyph_index].init) {
1828 memcpy(lpgm, &font->gm[glyph_index].gm, sizeof(*lpgm));
1829 return 1; /* FIXME */
1833 if(font->orientation || (format != GGO_METRICS && format != GGO_BITMAP) || font->aveWidth || lpmat)
1834 load_flags |= FT_LOAD_NO_BITMAP;
1836 err = pFT_Load_Glyph(ft_face, glyph_index, load_flags);
1839 FIXME("FT_Load_Glyph on index %x returns %d\n", glyph_index, err);
1843 /* Scaling factor */
1844 if (font->aveWidth && font->potm) {
1845 widthRatio = (float)font->aveWidth * font->xform.eM11 / (float) font->potm->otmTextMetrics.tmAveCharWidth;
1848 left = (INT)(ft_face->glyph->metrics.horiBearingX * widthRatio) & -64;
1849 right = (INT)((ft_face->glyph->metrics.horiBearingX + ft_face->glyph->metrics.width) * widthRatio + 63) & -64;
1851 font->gm[glyph_index].adv = (INT)((ft_face->glyph->metrics.horiAdvance * widthRatio) + 63) >> 6;
1852 font->gm[glyph_index].lsb = left >> 6;
1853 font->gm[glyph_index].bbx = (right - left) >> 6;
1855 /* Scaling transform */
1856 if(font->aveWidth) {
1858 scaleMat.xx = FT_FixedFromFloat(widthRatio);
1861 scaleMat.yy = (1 << 16);
1863 pFT_Matrix_Multiply(&scaleMat, &transMat);
1864 needsTransform = TRUE;
1867 /* Rotation transform */
1868 if(font->orientation) {
1869 FT_Matrix rotationMat;
1871 angle = FT_FixedFromFloat((float)font->orientation / 10.0);
1872 pFT_Vector_Unit(&vecAngle, angle);
1873 rotationMat.xx = vecAngle.x;
1874 rotationMat.xy = -vecAngle.y;
1875 rotationMat.yx = -rotationMat.xy;
1876 rotationMat.yy = rotationMat.xx;
1878 pFT_Matrix_Multiply(&rotationMat, &transMat);
1879 needsTransform = TRUE;
1882 /* Extra transformation specified by caller */
1885 extraMat.xx = FT_FixedFromFIXED(lpmat->eM11);
1886 extraMat.xy = FT_FixedFromFIXED(lpmat->eM21);
1887 extraMat.yx = FT_FixedFromFIXED(lpmat->eM12);
1888 extraMat.yy = FT_FixedFromFIXED(lpmat->eM22);
1889 pFT_Matrix_Multiply(&extraMat, &transMat);
1890 needsTransform = TRUE;
1893 if(!needsTransform) {
1894 top = (ft_face->glyph->metrics.horiBearingY + 63) & -64;
1895 bottom = (ft_face->glyph->metrics.horiBearingY -
1896 ft_face->glyph->metrics.height) & -64;
1897 lpgm->gmCellIncX = font->gm[glyph_index].adv;
1898 lpgm->gmCellIncY = 0;
1902 for(xc = 0; xc < 2; xc++) {
1903 for(yc = 0; yc < 2; yc++) {
1904 vec.x = (ft_face->glyph->metrics.horiBearingX +
1905 xc * ft_face->glyph->metrics.width);
1906 vec.y = ft_face->glyph->metrics.horiBearingY -
1907 yc * ft_face->glyph->metrics.height;
1908 TRACE("Vec %ld,%ld\n", vec.x, vec.y);
1909 pFT_Vector_Transform(&vec, &transMat);
1910 if(xc == 0 && yc == 0) {
1911 left = right = vec.x;
1912 top = bottom = vec.y;
1914 if(vec.x < left) left = vec.x;
1915 else if(vec.x > right) right = vec.x;
1916 if(vec.y < bottom) bottom = vec.y;
1917 else if(vec.y > top) top = vec.y;
1922 right = (right + 63) & -64;
1923 bottom = bottom & -64;
1924 top = (top + 63) & -64;
1926 TRACE("transformed box: (%d,%d - %d,%d)\n", left, top, right, bottom);
1927 vec.x = ft_face->glyph->metrics.horiAdvance;
1929 pFT_Vector_Transform(&vec, &transMat);
1930 lpgm->gmCellIncX = (vec.x+63) >> 6;
1931 lpgm->gmCellIncY = -((vec.y+63) >> 6);
1933 lpgm->gmBlackBoxX = (right - left) >> 6;
1934 lpgm->gmBlackBoxY = (top - bottom) >> 6;
1935 lpgm->gmptGlyphOrigin.x = left >> 6;
1936 lpgm->gmptGlyphOrigin.y = top >> 6;
1938 memcpy(&font->gm[glyph_index].gm, lpgm, sizeof(*lpgm));
1939 font->gm[glyph_index].init = TRUE;
1941 if(format == GGO_METRICS)
1942 return 1; /* FIXME */
1944 if (buf && !buflen){
1948 if(ft_face->glyph->format != ft_glyph_format_outline && format != GGO_BITMAP) {
1949 FIXME("loaded a bitmap\n");
1955 width = lpgm->gmBlackBoxX;
1956 height = lpgm->gmBlackBoxY;
1957 pitch = ((width + 31) >> 5) << 2;
1958 needed = pitch * height;
1960 if(!buf || !buflen) break;
1962 switch(ft_face->glyph->format) {
1963 case ft_glyph_format_bitmap:
1965 BYTE *src = ft_face->glyph->bitmap.buffer, *dst = buf;
1966 INT w = (ft_face->glyph->bitmap.width + 7) >> 3;
1967 INT h = ft_face->glyph->bitmap.rows;
1969 memcpy(dst, src, w);
1970 src += ft_face->glyph->bitmap.pitch;
1976 case ft_glyph_format_outline:
1977 ft_bitmap.width = width;
1978 ft_bitmap.rows = height;
1979 ft_bitmap.pitch = pitch;
1980 ft_bitmap.pixel_mode = ft_pixel_mode_mono;
1981 ft_bitmap.buffer = buf;
1983 if(needsTransform) {
1984 pFT_Outline_Transform(&ft_face->glyph->outline, &transMat);
1987 pFT_Outline_Translate(&ft_face->glyph->outline, -left, -bottom );
1989 /* Note: FreeType will only set 'black' bits for us. */
1990 memset(buf, 0, needed);
1991 pFT_Outline_Get_Bitmap(library, &ft_face->glyph->outline, &ft_bitmap);
1995 FIXME("loaded glyph format %x\n", ft_face->glyph->format);
2000 case GGO_GRAY2_BITMAP:
2001 case GGO_GRAY4_BITMAP:
2002 case GGO_GRAY8_BITMAP:
2003 case WINE_GGO_GRAY16_BITMAP:
2008 width = lpgm->gmBlackBoxX;
2009 height = lpgm->gmBlackBoxY;
2010 pitch = (width + 3) / 4 * 4;
2011 needed = pitch * height;
2013 if(!buf || !buflen) break;
2014 ft_bitmap.width = width;
2015 ft_bitmap.rows = height;
2016 ft_bitmap.pitch = pitch;
2017 ft_bitmap.pixel_mode = ft_pixel_mode_grays;
2018 ft_bitmap.buffer = buf;
2020 if(needsTransform) {
2021 pFT_Outline_Transform(&ft_face->glyph->outline, &transMat);
2024 pFT_Outline_Translate(&ft_face->glyph->outline, -left, -bottom );
2026 pFT_Outline_Get_Bitmap(library, &ft_face->glyph->outline, &ft_bitmap);
2028 if(format == GGO_GRAY2_BITMAP)
2030 else if(format == GGO_GRAY4_BITMAP)
2032 else if(format == GGO_GRAY8_BITMAP)
2034 else if(format == WINE_GGO_GRAY16_BITMAP)
2042 for(row = 0; row < height; row++) {
2044 for(col = 0; col < width; col++, ptr++) {
2045 *ptr = (*(unsigned int*)ptr * mult + 128) / 256;
2054 int contour, point = 0, first_pt;
2055 FT_Outline *outline = &ft_face->glyph->outline;
2056 TTPOLYGONHEADER *pph;
2058 DWORD pph_start, cpfx, type;
2060 if(buflen == 0) buf = NULL;
2062 if (needsTransform && buf) {
2063 pFT_Outline_Transform(outline, &transMat);
2066 for(contour = 0; contour < outline->n_contours; contour++) {
2068 pph = (TTPOLYGONHEADER *)((char *)buf + needed);
2071 pph->dwType = TT_POLYGON_TYPE;
2072 FTVectorToPOINTFX(&outline->points[point], &pph->pfxStart);
2074 needed += sizeof(*pph);
2076 while(point <= outline->contours[contour]) {
2077 ppc = (TTPOLYCURVE *)((char *)buf + needed);
2078 type = (outline->tags[point] & FT_Curve_Tag_On) ?
2079 TT_PRIM_LINE : TT_PRIM_QSPLINE;
2083 FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
2086 } while(point <= outline->contours[contour] &&
2087 (outline->tags[point] & FT_Curve_Tag_On) ==
2088 (outline->tags[point-1] & FT_Curve_Tag_On));
2089 /* At the end of a contour Windows adds the start point, but
2091 if(point > outline->contours[contour] &&
2092 !(outline->tags[point-1] & FT_Curve_Tag_On)) {
2094 FTVectorToPOINTFX(&outline->points[first_pt], &ppc->apfx[cpfx]);
2096 } else if(point <= outline->contours[contour] &&
2097 outline->tags[point] & FT_Curve_Tag_On) {
2098 /* add closing pt for bezier */
2100 FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
2108 needed += sizeof(*ppc) + (cpfx - 1) * sizeof(POINTFX);
2111 pph->cb = needed - pph_start;
2117 /* Convert the quadratic Beziers to cubic Beziers.
2118 The parametric eqn for a cubic Bezier is, from PLRM:
2119 r(t) = at^3 + bt^2 + ct + r0
2120 with the control points:
2125 A quadratic Beizer has the form:
2126 p(t) = (1-t)^2 p0 + 2(1-t)t p1 + t^2 p2
2128 So equating powers of t leads to:
2129 r1 = 2/3 p1 + 1/3 p0
2130 r2 = 2/3 p1 + 1/3 p2
2131 and of course r0 = p0, r3 = p2
2134 int contour, point = 0, first_pt;
2135 FT_Outline *outline = &ft_face->glyph->outline;
2136 TTPOLYGONHEADER *pph;
2138 DWORD pph_start, cpfx, type;
2139 FT_Vector cubic_control[4];
2140 if(buflen == 0) buf = NULL;
2142 if (needsTransform && buf) {
2143 pFT_Outline_Transform(outline, &transMat);
2146 for(contour = 0; contour < outline->n_contours; contour++) {
2148 pph = (TTPOLYGONHEADER *)((char *)buf + needed);
2151 pph->dwType = TT_POLYGON_TYPE;
2152 FTVectorToPOINTFX(&outline->points[point], &pph->pfxStart);
2154 needed += sizeof(*pph);
2156 while(point <= outline->contours[contour]) {
2157 ppc = (TTPOLYCURVE *)((char *)buf + needed);
2158 type = (outline->tags[point] & FT_Curve_Tag_On) ?
2159 TT_PRIM_LINE : TT_PRIM_CSPLINE;
2162 if(type == TT_PRIM_LINE) {
2164 FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
2168 /* Unlike QSPLINEs, CSPLINEs always have their endpoint
2171 /* FIXME: Possible optimization in endpoint calculation
2172 if there are two consecutive curves */
2173 cubic_control[0] = outline->points[point-1];
2174 if(!(outline->tags[point-1] & FT_Curve_Tag_On)) {
2175 cubic_control[0].x += outline->points[point].x + 1;
2176 cubic_control[0].y += outline->points[point].y + 1;
2177 cubic_control[0].x >>= 1;
2178 cubic_control[0].y >>= 1;
2180 if(point+1 > outline->contours[contour])
2181 cubic_control[3] = outline->points[first_pt];
2183 cubic_control[3] = outline->points[point+1];
2184 if(!(outline->tags[point+1] & FT_Curve_Tag_On)) {
2185 cubic_control[3].x += outline->points[point].x + 1;
2186 cubic_control[3].y += outline->points[point].y + 1;
2187 cubic_control[3].x >>= 1;
2188 cubic_control[3].y >>= 1;
2191 /* r1 = 1/3 p0 + 2/3 p1
2192 r2 = 1/3 p2 + 2/3 p1 */
2193 cubic_control[1].x = (2 * outline->points[point].x + 1) / 3;
2194 cubic_control[1].y = (2 * outline->points[point].y + 1) / 3;
2195 cubic_control[2] = cubic_control[1];
2196 cubic_control[1].x += (cubic_control[0].x + 1) / 3;
2197 cubic_control[1].y += (cubic_control[0].y + 1) / 3;
2198 cubic_control[2].x += (cubic_control[3].x + 1) / 3;
2199 cubic_control[2].y += (cubic_control[3].y + 1) / 3;
2201 FTVectorToPOINTFX(&cubic_control[1], &ppc->apfx[cpfx]);
2202 FTVectorToPOINTFX(&cubic_control[2], &ppc->apfx[cpfx+1]);
2203 FTVectorToPOINTFX(&cubic_control[3], &ppc->apfx[cpfx+2]);
2208 } while(point <= outline->contours[contour] &&
2209 (outline->tags[point] & FT_Curve_Tag_On) ==
2210 (outline->tags[point-1] & FT_Curve_Tag_On));
2211 /* At the end of a contour Windows adds the start point,
2212 but only for Beziers and we've already done that.
2214 if(point <= outline->contours[contour] &&
2215 outline->tags[point] & FT_Curve_Tag_On) {
2216 /* This is the closing pt of a bezier, but we've already
2217 added it, so just inc point and carry on */
2224 needed += sizeof(*ppc) + (cpfx - 1) * sizeof(POINTFX);
2227 pph->cb = needed - pph_start;
2233 FIXME("Unsupported format %d\n", format);
2239 /*************************************************************
2240 * WineEngGetTextMetrics
2243 BOOL WineEngGetTextMetrics(GdiFont font, LPTEXTMETRICW ptm)
2246 if(!WineEngGetOutlineTextMetrics(font, 0, NULL))
2249 if(!font->potm) return FALSE;
2250 memcpy(ptm, &font->potm->otmTextMetrics, sizeof(*ptm));
2252 if (font->aveWidth) {
2253 ptm->tmAveCharWidth = font->aveWidth * font->xform.eM11;
2259 /*************************************************************
2260 * WineEngGetOutlineTextMetrics
2263 UINT WineEngGetOutlineTextMetrics(GdiFont font, UINT cbSize,
2264 OUTLINETEXTMETRICW *potm)
2266 FT_Face ft_face = font->ft_face;
2267 UINT needed, lenfam, lensty, ret;
2269 TT_HoriHeader *pHori;
2270 TT_Postscript *pPost;
2271 FT_Fixed x_scale, y_scale;
2272 WCHAR *family_nameW, *style_nameW;
2273 static const WCHAR spaceW[] = {' ', '\0'};
2275 INT ascent, descent;
2277 TRACE("font=%p\n", font);
2280 if(cbSize >= font->potm->otmSize)
2281 memcpy(potm, font->potm, font->potm->otmSize);
2282 return font->potm->otmSize;
2285 needed = sizeof(*potm);
2287 lenfam = (strlenW(font->name) + 1) * sizeof(WCHAR);
2288 family_nameW = strdupW(font->name);
2290 lensty = MultiByteToWideChar(CP_ACP, 0, ft_face->style_name, -1, NULL, 0)
2292 style_nameW = HeapAlloc(GetProcessHeap(), 0, lensty);
2293 MultiByteToWideChar(CP_ACP, 0, ft_face->style_name, -1,
2294 style_nameW, lensty);
2296 /* These names should be read from the TT name table */
2298 /* length of otmpFamilyName */
2301 /* length of otmpFaceName */
2302 if(!strcasecmp(ft_face->style_name, "regular")) {
2303 needed += lenfam; /* just the family name */
2305 needed += lenfam + lensty; /* family + " " + style */
2308 /* length of otmpStyleName */
2311 /* length of otmpFullName */
2312 needed += lenfam + lensty;
2315 x_scale = ft_face->size->metrics.x_scale;
2316 y_scale = ft_face->size->metrics.y_scale;
2318 pOS2 = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_os2);
2320 FIXME("Can't find OS/2 table - not TT font?\n");
2325 pHori = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_hhea);
2327 FIXME("Can't find HHEA table - not TT font?\n");
2332 pPost = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_post); /* we can live with this failing */
2334 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",
2335 pOS2->usWinAscent, pOS2->usWinDescent,
2336 pOS2->sTypoAscender, pOS2->sTypoDescender, pOS2->sTypoLineGap,
2337 ft_face->ascender, ft_face->descender, ft_face->height,
2338 pHori->Ascender, pHori->Descender, pHori->Line_Gap,
2339 ft_face->bbox.yMax, ft_face->bbox.yMin);
2341 font->potm = HeapAlloc(GetProcessHeap(), 0, needed);
2342 font->potm->otmSize = needed;
2344 #define TM font->potm->otmTextMetrics
2346 if(pOS2->usWinAscent + pOS2->usWinDescent == 0) {
2347 ascent = pHori->Ascender;
2348 descent = -pHori->Descender;
2350 ascent = pOS2->usWinAscent;
2351 descent = pOS2->usWinDescent;
2355 TM.tmAscent = font->yMax;
2356 TM.tmDescent = -font->yMin;
2357 TM.tmInternalLeading = (TM.tmAscent + TM.tmDescent) - ft_face->size->metrics.y_ppem;
2359 TM.tmAscent = (pFT_MulFix(ascent, y_scale) + 32) >> 6;
2360 TM.tmDescent = (pFT_MulFix(descent, y_scale) + 32) >> 6;
2361 TM.tmInternalLeading = (pFT_MulFix(ascent + descent
2362 - ft_face->units_per_EM, y_scale) + 32) >> 6;
2365 TM.tmHeight = TM.tmAscent + TM.tmDescent;
2368 el = MAX(0, LineGap - ((WinAscent + WinDescent) - (Ascender - Descender)))
2370 TM.tmExternalLeading = max(0, (pFT_MulFix(pHori->Line_Gap -
2371 ((ascent + descent) -
2372 (pHori->Ascender - pHori->Descender)), y_scale) + 32) >> 6);
2374 TM.tmAveCharWidth = (pFT_MulFix(pOS2->xAvgCharWidth, x_scale) + 32) >> 6;
2375 if (TM.tmAveCharWidth == 0) {
2376 TM.tmAveCharWidth = 1;
2378 TM.tmMaxCharWidth = (pFT_MulFix(ft_face->bbox.xMax - ft_face->bbox.xMin, x_scale) + 32) >> 6;
2379 TM.tmWeight = font->fake_bold ? FW_BOLD : pOS2->usWeightClass;
2381 TM.tmDigitizedAspectX = 300;
2382 TM.tmDigitizedAspectY = 300;
2383 TM.tmFirstChar = pOS2->usFirstCharIndex;
2384 TM.tmLastChar = pOS2->usLastCharIndex;
2385 TM.tmDefaultChar = pOS2->usDefaultChar;
2386 TM.tmBreakChar = pOS2->usBreakChar ? pOS2->usBreakChar : ' ';
2387 TM.tmItalic = font->fake_italic ? 255 : ((ft_face->style_flags & FT_STYLE_FLAG_ITALIC) ? 255 : 0);
2388 TM.tmUnderlined = 0; /* entry in OS2 table */
2389 TM.tmStruckOut = 0; /* entry in OS2 table */
2391 /* Yes TPMF_FIXED_PITCH is correct; braindead api */
2392 if(!FT_IS_FIXED_WIDTH(ft_face))
2393 TM.tmPitchAndFamily = TMPF_FIXED_PITCH;
2395 TM.tmPitchAndFamily = 0;
2397 switch(pOS2->panose[PAN_FAMILYTYPE_INDEX]) {
2398 case PAN_FAMILY_SCRIPT:
2399 TM.tmPitchAndFamily |= FF_SCRIPT;
2401 case PAN_FAMILY_DECORATIVE:
2402 case PAN_FAMILY_PICTORIAL:
2403 TM.tmPitchAndFamily |= FF_DECORATIVE;
2405 case PAN_FAMILY_TEXT_DISPLAY:
2406 if(TM.tmPitchAndFamily == 0) /* fixed */
2407 TM.tmPitchAndFamily = FF_MODERN;
2409 switch(pOS2->panose[PAN_SERIFSTYLE_INDEX]) {
2410 case PAN_SERIF_NORMAL_SANS:
2411 case PAN_SERIF_OBTUSE_SANS:
2412 case PAN_SERIF_PERP_SANS:
2413 TM.tmPitchAndFamily |= FF_SWISS;
2416 TM.tmPitchAndFamily |= FF_ROMAN;
2421 TM.tmPitchAndFamily |= FF_DONTCARE;
2424 if(FT_IS_SCALABLE(ft_face))
2425 TM.tmPitchAndFamily |= TMPF_VECTOR;
2426 if(FT_IS_SFNT(ft_face))
2427 TM.tmPitchAndFamily |= TMPF_TRUETYPE;
2429 TM.tmCharSet = font->charset;
2432 font->potm->otmFiller = 0;
2433 memcpy(&font->potm->otmPanoseNumber, pOS2->panose, PANOSE_COUNT);
2434 font->potm->otmfsSelection = pOS2->fsSelection;
2435 font->potm->otmfsType = pOS2->fsType;
2436 font->potm->otmsCharSlopeRise = pHori->caret_Slope_Rise;
2437 font->potm->otmsCharSlopeRun = pHori->caret_Slope_Run;
2438 font->potm->otmItalicAngle = 0; /* POST table */
2439 font->potm->otmEMSquare = ft_face->units_per_EM;
2440 font->potm->otmAscent = (pFT_MulFix(pOS2->sTypoAscender, y_scale) + 32) >> 6;
2441 font->potm->otmDescent = (pFT_MulFix(pOS2->sTypoDescender, y_scale) + 32) >> 6;
2442 font->potm->otmLineGap = (pFT_MulFix(pOS2->sTypoLineGap, y_scale) + 32) >> 6;
2443 font->potm->otmsCapEmHeight = (pFT_MulFix(pOS2->sCapHeight, y_scale) + 32) >> 6;
2444 font->potm->otmsXHeight = (pFT_MulFix(pOS2->sxHeight, y_scale) + 32) >> 6;
2445 font->potm->otmrcFontBox.left = (pFT_MulFix(ft_face->bbox.xMin, x_scale) + 32) >> 6;
2446 font->potm->otmrcFontBox.right = (pFT_MulFix(ft_face->bbox.xMax, x_scale) + 32) >> 6;
2447 font->potm->otmrcFontBox.top = (pFT_MulFix(ft_face->bbox.yMax, y_scale) + 32) >> 6;
2448 font->potm->otmrcFontBox.bottom = (pFT_MulFix(ft_face->bbox.yMin, y_scale) + 32) >> 6;
2449 font->potm->otmMacAscent = 0; /* where do these come from ? */
2450 font->potm->otmMacDescent = 0;
2451 font->potm->otmMacLineGap = 0;
2452 font->potm->otmusMinimumPPEM = 0; /* TT Header */
2453 font->potm->otmptSubscriptSize.x = (pFT_MulFix(pOS2->ySubscriptXSize, x_scale) + 32) >> 6;
2454 font->potm->otmptSubscriptSize.y = (pFT_MulFix(pOS2->ySubscriptYSize, y_scale) + 32) >> 6;
2455 font->potm->otmptSubscriptOffset.x = (pFT_MulFix(pOS2->ySubscriptXOffset, x_scale) + 32) >> 6;
2456 font->potm->otmptSubscriptOffset.y = (pFT_MulFix(pOS2->ySubscriptYOffset, y_scale) + 32) >> 6;
2457 font->potm->otmptSuperscriptSize.x = (pFT_MulFix(pOS2->ySuperscriptXSize, x_scale) + 32) >> 6;
2458 font->potm->otmptSuperscriptSize.y = (pFT_MulFix(pOS2->ySuperscriptYSize, y_scale) + 32) >> 6;
2459 font->potm->otmptSuperscriptOffset.x = (pFT_MulFix(pOS2->ySuperscriptXOffset, x_scale) + 32) >> 6;
2460 font->potm->otmptSuperscriptOffset.y = (pFT_MulFix(pOS2->ySuperscriptYOffset, y_scale) + 32) >> 6;
2461 font->potm->otmsStrikeoutSize = (pFT_MulFix(pOS2->yStrikeoutSize, y_scale) + 32) >> 6;
2462 font->potm->otmsStrikeoutPosition = (pFT_MulFix(pOS2->yStrikeoutPosition, y_scale) + 32) >> 6;
2464 font->potm->otmsUnderscoreSize = 0;
2465 font->potm->otmsUnderscorePosition = 0;
2467 font->potm->otmsUnderscoreSize = (pFT_MulFix(pPost->underlineThickness, y_scale) + 32) >> 6;
2468 font->potm->otmsUnderscorePosition = (pFT_MulFix(pPost->underlinePosition, y_scale) + 32) >> 6;
2471 /* otmp* members should clearly have type ptrdiff_t, but M$ knows best */
2472 cp = (char*)font->potm + sizeof(*font->potm);
2473 font->potm->otmpFamilyName = (LPSTR)(cp - (char*)font->potm);
2474 strcpyW((WCHAR*)cp, family_nameW);
2476 font->potm->otmpStyleName = (LPSTR)(cp - (char*)font->potm);
2477 strcpyW((WCHAR*)cp, style_nameW);
2479 font->potm->otmpFaceName = (LPSTR)(cp - (char*)font->potm);
2480 strcpyW((WCHAR*)cp, family_nameW);
2481 if(strcasecmp(ft_face->style_name, "regular")) {
2482 strcatW((WCHAR*)cp, spaceW);
2483 strcatW((WCHAR*)cp, style_nameW);
2484 cp += lenfam + lensty;
2487 font->potm->otmpFullName = (LPSTR)(cp - (char*)font->potm);
2488 strcpyW((WCHAR*)cp, family_nameW);
2489 strcatW((WCHAR*)cp, spaceW);
2490 strcatW((WCHAR*)cp, style_nameW);
2493 if(potm && needed <= cbSize)
2494 memcpy(potm, font->potm, font->potm->otmSize);
2497 HeapFree(GetProcessHeap(), 0, style_nameW);
2498 HeapFree(GetProcessHeap(), 0, family_nameW);
2504 /*************************************************************
2505 * WineEngGetCharWidth
2508 BOOL WineEngGetCharWidth(GdiFont font, UINT firstChar, UINT lastChar,
2513 FT_UInt glyph_index;
2515 TRACE("%p, %d, %d, %p\n", font, firstChar, lastChar, buffer);
2517 for(c = firstChar; c <= lastChar; c++) {
2518 glyph_index = get_glyph_index(font, c);
2519 WineEngGetGlyphOutline(font, glyph_index, GGO_METRICS | GGO_GLYPH_INDEX,
2520 &gm, 0, NULL, NULL);
2521 buffer[c - firstChar] = font->gm[glyph_index].adv;
2526 /*************************************************************
2527 * WineEngGetCharABCWidths
2530 BOOL WineEngGetCharABCWidths(GdiFont font, UINT firstChar, UINT lastChar,
2535 FT_UInt glyph_index;
2537 TRACE("%p, %d, %d, %p\n", font, firstChar, lastChar, buffer);
2539 for(c = firstChar; c <= lastChar; c++) {
2540 glyph_index = get_glyph_index(font, c);
2541 WineEngGetGlyphOutline(font, glyph_index, GGO_METRICS | GGO_GLYPH_INDEX,
2542 &gm, 0, NULL, NULL);
2543 buffer[c - firstChar].abcA = font->gm[glyph_index].lsb;
2544 buffer[c - firstChar].abcB = font->gm[glyph_index].bbx;
2545 buffer[c - firstChar].abcC = font->gm[glyph_index].adv - font->gm[glyph_index].lsb -
2546 font->gm[glyph_index].bbx;
2551 /*************************************************************
2552 * WineEngGetTextExtentPoint
2555 BOOL WineEngGetTextExtentPoint(GdiFont font, LPCWSTR wstr, INT count,
2561 FT_UInt glyph_index;
2563 TRACE("%p, %s, %d, %p\n", font, debugstr_wn(wstr, count), count,
2567 WineEngGetTextMetrics(font, &tm);
2568 size->cy = tm.tmHeight;
2570 for(idx = 0; idx < count; idx++) {
2571 glyph_index = get_glyph_index(font, wstr[idx]);
2572 WineEngGetGlyphOutline(font, glyph_index, GGO_METRICS | GGO_GLYPH_INDEX,
2573 &gm, 0, NULL, NULL);
2574 size->cx += font->gm[glyph_index].adv;
2576 TRACE("return %ld,%ld\n", size->cx, size->cy);
2580 /*************************************************************
2581 * WineEngGetTextExtentPointI
2584 BOOL WineEngGetTextExtentPointI(GdiFont font, const WORD *indices, INT count,
2591 TRACE("%p, %p, %d, %p\n", font, indices, count, size);
2594 WineEngGetTextMetrics(font, &tm);
2595 size->cy = tm.tmHeight;
2597 for(idx = 0; idx < count; idx++) {
2598 WineEngGetGlyphOutline(font, indices[idx],
2599 GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL,
2601 size->cx += font->gm[indices[idx]].adv;
2603 TRACE("return %ld,%ld\n", size->cx, size->cy);
2607 /*************************************************************
2608 * WineEngGetFontData
2611 DWORD WineEngGetFontData(GdiFont font, DWORD table, DWORD offset, LPVOID buf,
2614 FT_Face ft_face = font->ft_face;
2618 TRACE("font=%p, table=%08lx, offset=%08lx, buf=%p, cbData=%lx\n",
2619 font, table, offset, buf, cbData);
2621 if(!FT_IS_SFNT(ft_face))
2629 if(table) { /* MS tags differ in endidness from FT ones */
2630 table = table >> 24 | table << 24 |
2631 (table >> 8 & 0xff00) | (table << 8 & 0xff0000);
2634 /* If the FT_Load_Sfnt_Table function is there we'll use it */
2635 if(pFT_Load_Sfnt_Table)
2636 err = pFT_Load_Sfnt_Table(ft_face, table, offset, buf, &len);
2637 else { /* Do it the hard way */
2638 TT_Face tt_face = (TT_Face) ft_face;
2639 SFNT_Interface *sfnt;
2640 if (FT_Version.major==2 && FT_Version.minor==0)
2643 sfnt = *(SFNT_Interface**)((char*)tt_face + 528);
2647 /* A field was added in the middle of the structure in 2.1.x */
2648 sfnt = *(SFNT_Interface**)((char*)tt_face + 532);
2650 err = sfnt->load_any(tt_face, table, offset, buf, &len);
2653 TRACE("Can't find table %08lx.\n", table);
2659 /*************************************************************
2660 * WineEngGetTextFace
2663 INT WineEngGetTextFace(GdiFont font, INT count, LPWSTR str)
2666 lstrcpynW(str, font->name, count);
2667 return strlenW(font->name);
2669 return strlenW(font->name) + 1;
2672 UINT WineEngGetTextCharsetInfo(GdiFont font, LPFONTSIGNATURE fs, DWORD flags)
2674 if (fs) memcpy(fs, &font->fs, sizeof(FONTSIGNATURE));
2675 return font->charset;
2678 #else /* HAVE_FREETYPE */
2680 BOOL WineEngInit(void)
2684 GdiFont WineEngCreateFontInstance(DC *dc, HFONT hfont)
2688 BOOL WineEngDestroyFontInstance(HFONT hfont)
2693 DWORD WineEngEnumFonts(LPLOGFONTW plf, FONTENUMPROCW proc, LPARAM lparam)
2698 DWORD WineEngGetGlyphIndices(GdiFont font, LPCWSTR lpstr, INT count,
2699 LPWORD pgi, DWORD flags)
2704 DWORD WineEngGetGlyphOutline(GdiFont font, UINT glyph, UINT format,
2705 LPGLYPHMETRICS lpgm, DWORD buflen, LPVOID buf,
2708 ERR("called but we don't have FreeType\n");
2712 BOOL WineEngGetTextMetrics(GdiFont font, LPTEXTMETRICW ptm)
2714 ERR("called but we don't have FreeType\n");
2718 UINT WineEngGetOutlineTextMetrics(GdiFont font, UINT cbSize,
2719 OUTLINETEXTMETRICW *potm)
2721 ERR("called but we don't have FreeType\n");
2725 BOOL WineEngGetCharWidth(GdiFont font, UINT firstChar, UINT lastChar,
2728 ERR("called but we don't have FreeType\n");
2732 BOOL WineEngGetCharABCWidths(GdiFont font, UINT firstChar, UINT lastChar,
2735 ERR("called but we don't have FreeType\n");
2739 BOOL WineEngGetTextExtentPoint(GdiFont font, LPCWSTR wstr, INT count,
2742 ERR("called but we don't have FreeType\n");
2746 BOOL WineEngGetTextExtentPointI(GdiFont font, const WORD *indices, INT count,
2749 ERR("called but we don't have FreeType\n");
2753 DWORD WineEngGetFontData(GdiFont font, DWORD table, DWORD offset, LPVOID buf,
2756 ERR("called but we don't have FreeType\n");
2760 INT WineEngGetTextFace(GdiFont font, INT count, LPWSTR str)
2762 ERR("called but we don't have FreeType\n");
2766 INT WineEngAddFontResourceEx(LPCWSTR file, DWORD flags, PVOID pdv)
2772 INT WineEngRemoveFontResourceEx(LPCWSTR file, DWORD flags, PVOID pdv)
2778 UINT WineEngGetTextCharsetInfo(GdiFont font, LPFONTSIGNATURE fs, DWORD flags)
2781 return DEFAULT_CHARSET;
2784 #endif /* HAVE_FREETYPE */