Improved generation of font signature from freetype face charmaps.
[wine.git] / dlls / gdi / freetype.c
blob059c542fab84e1d6b73e869bd0af0d3b533d7224
1 /*
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 "config.h"
26 #include <stdarg.h>
27 #include <stdlib.h>
28 #include <sys/stat.h>
29 #include <string.h>
30 #include <dirent.h>
31 #include <stdio.h>
32 #include <assert.h>
34 #include "windef.h"
35 #include "winbase.h"
36 #include "winerror.h"
37 #include "winreg.h"
38 #include "wingdi.h"
39 #include "wine/unicode.h"
40 #include "wine/port.h"
41 #include "wine/debug.h"
42 #include "gdi.h"
44 WINE_DEFAULT_DEBUG_CHANNEL(font);
46 #ifdef HAVE_FREETYPE
48 #ifdef HAVE_FREETYPE_FREETYPE_H
49 #include <freetype/freetype.h>
50 #endif
51 #ifdef HAVE_FREETYPE_FTGLYPH_H
52 #include <freetype/ftglyph.h>
53 #endif
54 #ifdef HAVE_FREETYPE_TTTABLES_H
55 #include <freetype/tttables.h>
56 #endif
57 #ifdef HAVE_FREETYPE_FTSNAMES_H
58 #include <freetype/ftsnames.h>
59 #else
60 # ifdef HAVE_FREETYPE_FTNAMES_H
61 # include <freetype/ftnames.h>
62 # endif
63 #endif
64 #ifdef HAVE_FREETYPE_TTNAMEID_H
65 #include <freetype/ttnameid.h>
66 #endif
67 #ifdef HAVE_FREETYPE_FTOUTLN_H
68 #include <freetype/ftoutln.h>
69 #endif
70 #ifdef HAVE_FREETYPE_INTERNAL_SFNT_H
71 #include <freetype/internal/sfnt.h>
72 #endif
73 #ifdef HAVE_FREETYPE_FTTRIGON_H
74 #include <freetype/fttrigon.h>
75 #endif
77 #ifndef SONAME_LIBFREETYPE
78 #define SONAME_LIBFREETYPE "libfreetype.so"
79 #endif
81 static FT_Library library = 0;
82 typedef struct
84 FT_Int major;
85 FT_Int minor;
86 FT_Int patch;
87 } FT_Version_t;
88 static FT_Version_t FT_Version;
90 static void *ft_handle = NULL;
92 #define MAKE_FUNCPTR(f) static typeof(f) * p##f = NULL
93 MAKE_FUNCPTR(FT_Cos);
94 MAKE_FUNCPTR(FT_Done_Face);
95 MAKE_FUNCPTR(FT_Get_Char_Index);
96 MAKE_FUNCPTR(FT_Get_Sfnt_Table);
97 MAKE_FUNCPTR(FT_Init_FreeType);
98 MAKE_FUNCPTR(FT_Load_Glyph);
99 MAKE_FUNCPTR(FT_MulFix);
100 MAKE_FUNCPTR(FT_New_Face);
101 MAKE_FUNCPTR(FT_Outline_Get_Bitmap);
102 MAKE_FUNCPTR(FT_Outline_Transform);
103 MAKE_FUNCPTR(FT_Outline_Translate);
104 MAKE_FUNCPTR(FT_Select_Charmap);
105 MAKE_FUNCPTR(FT_Set_Pixel_Sizes);
106 MAKE_FUNCPTR(FT_Sin);
107 MAKE_FUNCPTR(FT_Vector_Rotate);
108 #undef MAKE_FUNCPTR
109 static void (*pFT_Library_Version)(FT_Library,FT_Int*,FT_Int*,FT_Int*);
110 static FT_Error (*pFT_Load_Sfnt_Table)(FT_Face,FT_ULong,FT_Long,FT_Byte*,FT_ULong*);
112 #define GET_BE_WORD(ptr) MAKEWORD( ((BYTE *)(ptr))[1], ((BYTE *)(ptr))[0] )
114 typedef struct tagFace {
115 WCHAR *StyleName;
116 char *file;
117 FT_Long face_index;
118 BOOL Italic;
119 BOOL Bold;
120 FONTSIGNATURE fs;
121 FT_Fixed font_version;
122 struct tagFace *next;
123 struct tagFamily *family;
124 } Face;
126 typedef struct tagFamily {
127 WCHAR *FamilyName;
128 Face *FirstFace;
129 struct tagFamily *next;
130 } Family;
132 typedef struct {
133 GLYPHMETRICS gm;
134 INT adv; /* These three hold to widths of the unrotated chars */
135 INT lsb;
136 INT bbx;
137 BOOL init;
138 } GM;
140 struct tagGdiFont {
141 FT_Face ft_face;
142 XFORM xform;
143 LPWSTR name;
144 int charset;
145 BOOL fake_italic;
146 BOOL fake_bold;
147 INT orientation;
148 GM *gm;
149 DWORD gmsize;
150 HFONT hfont;
151 SHORT yMax;
152 SHORT yMin;
153 OUTLINETEXTMETRICW *potm;
154 FONTSIGNATURE fs;
155 struct tagGdiFont *next;
158 #define INIT_GM_SIZE 128
160 static GdiFont GdiFontList = NULL;
162 static Family *FontList = NULL;
164 static WCHAR defSerif[] = {'T','i','m','e','s',' ','N','e','w',' ',
165 'R','o','m','a','n','\0'};
166 static WCHAR defSans[] = {'A','r','i','a','l','\0'};
167 static WCHAR defFixed[] = {'C','o','u','r','i','e','r',' ','N','e','w','\0'};
169 static WCHAR defSystem[] = {'A','r','i','a','l','\0'};
170 static WCHAR SystemW[] = {'S','y','s','t','e','m','\0'};
171 static WCHAR MSSansSerifW[] = {'M','S',' ','S','a','n','s',' ',
172 'S','e','r','i','f','\0'};
173 static WCHAR HelvW[] = {'H','e','l','v','\0'};
175 static WCHAR ArabicW[] = {'A','r','a','b','i','c','\0'};
176 static WCHAR BalticW[] = {'B','a','l','t','i','c','\0'};
177 static WCHAR CHINESE_BIG5W[] = {'C','H','I','N','E','S','E','_','B','I','G','5','\0'};
178 static WCHAR CHINESE_GB2312W[] = {'C','H','I','N','E','S','E','_','G','B','2','3','1','2','\0'};
179 static WCHAR Central_EuropeanW[] = {'C','e','n','t','r','a','l',' ',
180 'E','u','r','o','p','e','a','n','\0'};
181 static WCHAR CyrillicW[] = {'C','y','r','i','l','l','i','c','\0'};
182 static WCHAR GreekW[] = {'G','r','e','e','k','\0'};
183 static WCHAR HangulW[] = {'H','a','n','g','u','l','\0'};
184 static WCHAR Hangul_Johab_W[] = {'H','a','n','g','u','l','(','J','o','h','a','b',')','\0'};
185 static WCHAR HebrewW[] = {'H','e','b','r','e','w','\0'};
186 static WCHAR JapaneseW[] = {'J','a','p','a','n','e','s','e','\0'};
187 static WCHAR SymbolW[] = {'S','y','m','b','o','l','\0'};
188 static WCHAR ThaiW[] = {'T','h','a','i','\0'};
189 static WCHAR TurkishW[] = {'T','u','r','k','i','s','h','\0'};
190 static WCHAR VietnameseW[] = {'V','i','e','t','n','a','m','e','s','e','\0'};
191 static WCHAR WesternW[] = {'W','e','s','t','e','r','n','\0'};
193 static WCHAR *ElfScriptsW[32] = { /* these are in the order of the fsCsb[0] bits */
194 WesternW, /*00*/
195 Central_EuropeanW,
196 CyrillicW,
197 GreekW,
198 TurkishW,
199 HebrewW,
200 ArabicW,
201 BalticW,
202 VietnameseW, /*08*/
203 NULL, NULL, NULL, NULL, NULL, NULL, NULL, /*15*/
204 ThaiW,
205 JapaneseW,
206 CHINESE_GB2312W,
207 HangulW,
208 CHINESE_BIG5W,
209 Hangul_Johab_W,
210 NULL, NULL, /*23*/
211 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
212 SymbolW /*31*/
215 typedef struct {
216 WCHAR *name;
217 INT charset;
218 } NameCs;
220 typedef struct tagFontSubst {
221 NameCs from;
222 NameCs to;
223 struct tagFontSubst *next;
224 } FontSubst;
226 static FontSubst *substlist = NULL;
227 static BOOL have_installed_roman_font = FALSE; /* CreateFontInstance will fail if this is still FALSE */
229 static BOOL AddFontFileToList(char *file, char *fake_family)
231 FT_Face ft_face;
232 TT_OS2 *pOS2;
233 TT_Header *pHeader;
234 WCHAR *FamilyW, *StyleW;
235 DWORD len;
236 Family *family = FontList;
237 Family **insert = &FontList;
238 Face **insertface, *next;
239 FT_Error err;
240 FT_Long face_index = 0, num_faces;
241 int i;
243 do {
244 char *family_name = fake_family;
246 TRACE("Loading font file %s index %ld\n", debugstr_a(file), face_index);
247 if((err = pFT_New_Face(library, file, face_index, &ft_face)) != 0) {
248 WARN("Unable to load font file %s err = %x\n", debugstr_a(file), err);
249 return FALSE;
252 if(!FT_IS_SFNT(ft_face)) { /* for now we'll skip everything but TT/OT */
253 pFT_Done_Face(ft_face);
254 return FALSE;
256 if(!pFT_Get_Sfnt_Table(ft_face, ft_sfnt_os2) ||
257 !pFT_Get_Sfnt_Table(ft_face, ft_sfnt_hhea) ||
258 !(pHeader = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_head))) {
259 TRACE("Font file %s lacks either an OS2, HHEA or HEAD table.\n"
260 "Skipping this font.\n", debugstr_a(file));
261 pFT_Done_Face(ft_face);
262 return FALSE;
265 if(!ft_face->family_name || !ft_face->style_name) {
266 TRACE("Font file %s lacks either a family or style name\n", debugstr_a(file));
267 pFT_Done_Face(ft_face);
268 return FALSE;
271 if(!family_name)
272 family_name = ft_face->family_name;
274 len = MultiByteToWideChar(CP_ACP, 0, family_name, -1, NULL, 0);
275 FamilyW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
276 MultiByteToWideChar(CP_ACP, 0, family_name, -1, FamilyW, len);
278 while(family) {
279 if(!strcmpW(family->FamilyName, FamilyW))
280 break;
281 insert = &family->next;
282 family = family->next;
284 if(!family) {
285 family = *insert = HeapAlloc(GetProcessHeap(), 0, sizeof(*family));
286 family->FamilyName = FamilyW;
287 family->FirstFace = NULL;
288 family->next = NULL;
289 } else {
290 HeapFree(GetProcessHeap(), 0, FamilyW);
293 len = MultiByteToWideChar(CP_ACP, 0, ft_face->style_name, -1, NULL, 0);
294 StyleW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
295 MultiByteToWideChar(CP_ACP, 0, ft_face->style_name, -1, StyleW, len);
297 next = NULL;
298 for(insertface = &family->FirstFace; *insertface;
299 insertface = &(*insertface)->next) {
300 if(!strcmpW((*insertface)->StyleName, StyleW)) {
301 TRACE("Already loaded font %s %s original version is %lx, this version is %lx\n",
302 debugstr_w(family->FamilyName), debugstr_w(StyleW),
303 (*insertface)->font_version, pHeader->Font_Revision);
305 if(fake_family) {
306 TRACE("This font is a replacement but the original really exists, so we'll skip the replacement\n");
307 HeapFree(GetProcessHeap(), 0, StyleW);
308 pFT_Done_Face(ft_face);
309 return FALSE;
311 if(pHeader->Font_Revision <= (*insertface)->font_version) {
312 TRACE("Original font is newer so skipping this one\n");
313 HeapFree(GetProcessHeap(), 0, StyleW);
314 pFT_Done_Face(ft_face);
315 return FALSE;
316 } else {
317 TRACE("Replacing original with this one\n");
318 next = (*insertface)->next;
319 HeapFree(GetProcessHeap(), 0, (*insertface)->file);
320 HeapFree(GetProcessHeap(), 0, (*insertface)->StyleName);
321 HeapFree(GetProcessHeap(), 0, *insertface);
322 break;
326 *insertface = HeapAlloc(GetProcessHeap(), 0, sizeof(**insertface));
327 (*insertface)->StyleName = StyleW;
328 (*insertface)->file = HeapAlloc(GetProcessHeap(),0,strlen(file)+1);
329 strcpy((*insertface)->file, file);
330 (*insertface)->face_index = face_index;
331 (*insertface)->next = next;
332 (*insertface)->Italic = (ft_face->style_flags & FT_STYLE_FLAG_ITALIC) ? 1 : 0;
333 (*insertface)->Bold = (ft_face->style_flags & FT_STYLE_FLAG_BOLD) ? 1 : 0;
334 (*insertface)->font_version = pHeader->Font_Revision;
335 (*insertface)->family = family;
337 pOS2 = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_os2);
338 if(pOS2) {
339 (*insertface)->fs.fsCsb[0] = pOS2->ulCodePageRange1;
340 (*insertface)->fs.fsCsb[1] = pOS2->ulCodePageRange2;
341 (*insertface)->fs.fsUsb[0] = pOS2->ulUnicodeRange1;
342 (*insertface)->fs.fsUsb[1] = pOS2->ulUnicodeRange2;
343 (*insertface)->fs.fsUsb[2] = pOS2->ulUnicodeRange3;
344 (*insertface)->fs.fsUsb[3] = pOS2->ulUnicodeRange4;
345 } else {
346 (*insertface)->fs.fsCsb[0] = (*insertface)->fs.fsCsb[1] = 0;
347 (*insertface)->fs.fsUsb[0] = 0;
348 (*insertface)->fs.fsUsb[1] = 0;
349 (*insertface)->fs.fsUsb[2] = 0;
350 (*insertface)->fs.fsUsb[3] = 0;
352 TRACE("fsCsb = %08lx %08lx/%08lx %08lx %08lx %08lx\n",
353 (*insertface)->fs.fsCsb[0], (*insertface)->fs.fsCsb[1],
354 (*insertface)->fs.fsUsb[0], (*insertface)->fs.fsUsb[1],
355 (*insertface)->fs.fsUsb[2], (*insertface)->fs.fsUsb[3]);
357 if((*insertface)->fs.fsCsb[0] == 0) { /* let's see if we can find any interesting cmaps */
358 for(i = 0; i < ft_face->num_charmaps; i++) {
359 switch(ft_face->charmaps[i]->encoding) {
360 case ft_encoding_unicode:
361 case ft_encoding_apple_roman:
362 (*insertface)->fs.fsCsb[0] |= 1;
363 break;
364 case ft_encoding_symbol:
365 (*insertface)->fs.fsCsb[0] |= 1L << 31;
366 break;
367 default:
368 break;
373 if((*insertface)->fs.fsCsb[0] & ~(1L << 31))
374 have_installed_roman_font = TRUE;
376 num_faces = ft_face->num_faces;
377 pFT_Done_Face(ft_face);
378 TRACE("Added font %s %s\n", debugstr_w(family->FamilyName),
379 debugstr_w(StyleW));
380 } while(num_faces > ++face_index);
381 return TRUE;
384 static void DumpFontList(void)
386 Family *family;
387 Face *face;
389 for(family = FontList; family; family = family->next) {
390 TRACE("Family: %s\n", debugstr_w(family->FamilyName));
391 for(face = family->FirstFace; face; face = face->next) {
392 TRACE("\t%s\n", debugstr_w(face->StyleName));
395 return;
398 static void DumpSubstList(void)
400 FontSubst *psub;
402 for(psub = substlist; psub; psub = psub->next)
403 if(psub->from.charset != -1 || psub->to.charset != -1)
404 TRACE("%s:%d -> %s:%d\n", debugstr_w(psub->from.name),
405 psub->from.charset, debugstr_w(psub->to.name), psub->to.charset);
406 else
407 TRACE("%s -> %s\n", debugstr_w(psub->from.name),
408 debugstr_w(psub->to.name));
409 return;
412 static LPWSTR strdupW(LPWSTR p)
414 LPWSTR ret;
415 DWORD len = (strlenW(p) + 1) * sizeof(WCHAR);
416 ret = HeapAlloc(GetProcessHeap(), 0, len);
417 memcpy(ret, p, len);
418 return ret;
421 static void split_subst_info(NameCs *nc, LPSTR str)
423 CHAR *p = strrchr(str, ',');
424 DWORD len;
426 nc->charset = -1;
427 if(p && *(p+1)) {
428 nc->charset = strtol(p+1, NULL, 10);
429 *p = '\0';
431 len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
432 nc->name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
433 MultiByteToWideChar(CP_ACP, 0, str, -1, nc->name, len);
436 static void LoadSubstList(void)
438 FontSubst *psub, **ppsub;
439 HKEY hkey;
440 DWORD valuelen, datalen, i = 0, type, dlen, vlen;
441 LPSTR value;
442 LPVOID data;
444 if(substlist) {
445 for(psub = substlist; psub;) {
446 FontSubst *ptmp;
447 HeapFree(GetProcessHeap(), 0, psub->to.name);
448 HeapFree(GetProcessHeap(), 0, psub->from.name);
449 ptmp = psub;
450 psub = psub->next;
451 HeapFree(GetProcessHeap(), 0, ptmp);
453 substlist = NULL;
456 if(RegOpenKeyA(HKEY_LOCAL_MACHINE,
457 "Software\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes",
458 &hkey) == ERROR_SUCCESS) {
460 RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
461 &valuelen, &datalen, NULL, NULL);
463 valuelen++; /* returned value doesn't include room for '\0' */
464 value = HeapAlloc(GetProcessHeap(), 0, valuelen * sizeof(CHAR));
465 data = HeapAlloc(GetProcessHeap(), 0, datalen);
467 dlen = datalen;
468 vlen = valuelen;
469 ppsub = &substlist;
470 while(RegEnumValueA(hkey, i++, value, &vlen, NULL, &type, data,
471 &dlen) == ERROR_SUCCESS) {
472 TRACE("Got %s=%s\n", debugstr_a(value), debugstr_a(data));
474 *ppsub = HeapAlloc(GetProcessHeap(), 0, sizeof(**ppsub));
475 (*ppsub)->next = NULL;
476 split_subst_info(&((*ppsub)->from), value);
477 split_subst_info(&((*ppsub)->to), data);
479 /* Win 2000 doesn't allow mapping between different charsets
480 or mapping of DEFAULT_CHARSET */
481 if(((*ppsub)->to.charset != (*ppsub)->from.charset) ||
482 (*ppsub)->to.charset == DEFAULT_CHARSET) {
483 HeapFree(GetProcessHeap(), 0, (*ppsub)->to.name);
484 HeapFree(GetProcessHeap(), 0, (*ppsub)->from.name);
485 HeapFree(GetProcessHeap(), 0, *ppsub);
486 *ppsub = NULL;
487 } else {
488 ppsub = &((*ppsub)->next);
490 /* reset dlen and vlen */
491 dlen = datalen;
492 vlen = valuelen;
494 HeapFree(GetProcessHeap(), 0, data);
495 HeapFree(GetProcessHeap(), 0, value);
496 RegCloseKey(hkey);
500 /***********************************************************
501 * The replacement list is a way to map an entire font
502 * family onto another family. For example adding
504 * [HKLM\Software\Wine\Wine\FontReplacements]
505 * "Wingdings"="Winedings"
507 * would enumerate the Winedings font both as Winedings and
508 * Wingdings. However if a real Wingdings font is present the
509 * replacement does not take place.
512 static void LoadReplaceList(void)
514 HKEY hkey;
515 DWORD valuelen, datalen, i = 0, type, dlen, vlen;
516 LPSTR value;
517 LPVOID data;
518 Family *family;
519 Face *face;
520 WCHAR old_nameW[200];
522 if(RegOpenKeyA(HKEY_LOCAL_MACHINE,
523 "Software\\Wine\\Wine\\FontReplacements",
524 &hkey) == ERROR_SUCCESS) {
526 RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
527 &valuelen, &datalen, NULL, NULL);
529 valuelen++; /* returned value doesn't include room for '\0' */
530 value = HeapAlloc(GetProcessHeap(), 0, valuelen * sizeof(CHAR));
531 data = HeapAlloc(GetProcessHeap(), 0, datalen);
533 dlen = datalen;
534 vlen = valuelen;
535 while(RegEnumValueA(hkey, i++, value, &vlen, NULL, &type, data,
536 &dlen) == ERROR_SUCCESS) {
537 TRACE("Got %s=%s\n", debugstr_a(value), debugstr_a(data));
538 /* "NewName"="Oldname" */
539 if(!MultiByteToWideChar(CP_ACP, 0, data, -1, old_nameW, sizeof(old_nameW)))
540 break;
542 /* Find the old family and hence all of the font files
543 in that family */
544 for(family = FontList; family; family = family->next) {
545 if(!strcmpiW(family->FamilyName, old_nameW)) {
546 for(face = family->FirstFace; face; face = face->next) {
547 TRACE("mapping %s %s to %s\n", debugstr_w(family->FamilyName),
548 debugstr_w(face->StyleName), value);
549 /* Now add a new entry with the new family name */
550 AddFontFileToList(face->file, value);
552 break;
555 /* reset dlen and vlen */
556 dlen = datalen;
557 vlen = valuelen;
559 HeapFree(GetProcessHeap(), 0, data);
560 HeapFree(GetProcessHeap(), 0, value);
561 RegCloseKey(hkey);
566 static BOOL ReadFontDir(char *dirname)
568 DIR *dir;
569 struct dirent *dent;
570 char path[MAX_PATH];
572 TRACE("Loading fonts from %s\n", debugstr_a(dirname));
574 dir = opendir(dirname);
575 if(!dir) {
576 ERR("Can't open directory %s\n", debugstr_a(dirname));
577 return FALSE;
579 while((dent = readdir(dir)) != NULL) {
580 struct stat statbuf;
582 if(!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
583 continue;
585 TRACE("Found %s in %s\n", debugstr_a(dent->d_name), debugstr_a(dirname));
587 sprintf(path, "%s/%s", dirname, dent->d_name);
589 if(stat(path, &statbuf) == -1)
591 WARN("Can't stat %s\n", debugstr_a(path));
592 continue;
594 if(S_ISDIR(statbuf.st_mode))
595 ReadFontDir(path);
596 else
597 AddFontFileToList(path, NULL);
599 closedir(dir);
600 return TRUE;
603 /*************************************************************
604 * WineEngAddFontResourceEx
607 INT WineEngAddFontResourceEx(LPCWSTR file, DWORD flags, PVOID pdv)
609 if (ft_handle) /* do it only if we have freetype up and running */
611 DWORD len = WideCharToMultiByte(CP_ACP, 0, file, -1, NULL, 0, NULL, NULL);
612 LPSTR fileA = HeapAlloc(GetProcessHeap(), 0, len);
613 char unixname[MAX_PATH];
614 WideCharToMultiByte(CP_ACP, 0, file, -1, fileA, len, NULL, NULL);
616 if(flags)
617 FIXME("Ignoring flags %lx\n", flags);
619 if(wine_get_unix_file_name(fileA, unixname, sizeof(unixname)))
620 AddFontFileToList(unixname, NULL);
621 HeapFree(GetProcessHeap(), 0, fileA);
623 return 1;
626 /*************************************************************
627 * WineEngRemoveFontResourceEx
630 BOOL WineEngRemoveFontResourceEx(LPCWSTR file, DWORD flags, PVOID pdv)
632 FIXME(":stub\n");
633 return TRUE;
636 /*************************************************************
637 * WineEngInit
639 * Initialize FreeType library and create a list of available faces
641 BOOL WineEngInit(void)
643 HKEY hkey;
644 DWORD valuelen, datalen, i = 0, type, dlen, vlen;
645 LPSTR value;
646 LPVOID data;
647 char windowsdir[MAX_PATH];
648 char unixname[MAX_PATH];
650 TRACE("\n");
652 ft_handle = wine_dlopen(SONAME_LIBFREETYPE, RTLD_NOW, NULL, 0);
653 if(!ft_handle) {
654 WINE_MESSAGE(
655 "Wine cannot find the FreeType font library. To enable Wine to\n"
656 "use TrueType fonts please install a version of FreeType greater than\n"
657 "or equal to 2.0.5.\n"
658 "http://www.freetype.org\n");
659 return FALSE;
662 #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;}
664 LOAD_FUNCPTR(FT_Cos)
665 LOAD_FUNCPTR(FT_Done_Face)
666 LOAD_FUNCPTR(FT_Get_Char_Index)
667 LOAD_FUNCPTR(FT_Get_Sfnt_Table)
668 LOAD_FUNCPTR(FT_Init_FreeType)
669 LOAD_FUNCPTR(FT_Load_Glyph)
670 LOAD_FUNCPTR(FT_MulFix)
671 LOAD_FUNCPTR(FT_New_Face)
672 LOAD_FUNCPTR(FT_Outline_Get_Bitmap)
673 LOAD_FUNCPTR(FT_Outline_Transform)
674 LOAD_FUNCPTR(FT_Outline_Translate)
675 LOAD_FUNCPTR(FT_Select_Charmap)
676 LOAD_FUNCPTR(FT_Set_Pixel_Sizes)
677 LOAD_FUNCPTR(FT_Sin)
678 LOAD_FUNCPTR(FT_Vector_Rotate)
680 #undef LOAD_FUNCPTR
681 /* Don't warn if this one is missing */
682 pFT_Library_Version = wine_dlsym(ft_handle, "FT_Library_Version", NULL, 0);
683 pFT_Load_Sfnt_Table = wine_dlsym(ft_handle, "FT_Load_Sfnt_Table", NULL, 0);
685 if(!wine_dlsym(ft_handle, "FT_Get_Postscript_Name", NULL, 0) &&
686 !wine_dlsym(ft_handle, "FT_Sqrt64", NULL, 0)) {
687 /* try to avoid 2.0.4: >= 2.0.5 has FT_Get_Postscript_Name and
688 <= 2.0.3 has FT_Sqrt64 */
689 goto sym_not_found;
692 if(pFT_Init_FreeType(&library) != 0) {
693 ERR("Can't init FreeType library\n");
694 wine_dlclose(ft_handle, NULL, 0);
695 ft_handle = NULL;
696 return FALSE;
698 FT_Version.major=FT_Version.minor=FT_Version.patch=-1;
699 if (pFT_Library_Version)
701 pFT_Library_Version(library,&FT_Version.major,&FT_Version.minor,&FT_Version.patch);
703 if (FT_Version.major<=0)
705 FT_Version.major=2;
706 FT_Version.minor=0;
707 FT_Version.patch=5;
709 TRACE("FreeType version is %d.%d.%d\n",FT_Version.major,FT_Version.minor,FT_Version.patch);
711 /* load in the fonts from %WINDOWSDIR%\\Fonts first of all */
712 GetWindowsDirectoryA(windowsdir, sizeof(windowsdir));
713 strcat(windowsdir, "\\Fonts");
714 if(wine_get_unix_file_name(windowsdir, unixname, sizeof(unixname)))
715 ReadFontDir(unixname);
717 /* now look under HKLM\Software\Microsoft\Windows\CurrentVersion\Fonts
718 for any fonts not installed in %WINDOWSDIR%\Fonts. They will have their
719 full path as the entry */
720 if(RegOpenKeyA(HKEY_LOCAL_MACHINE,
721 "Software\\Microsoft\\Windows\\CurrentVersion\\Fonts",
722 &hkey) == ERROR_SUCCESS) {
723 RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
724 &valuelen, &datalen, NULL, NULL);
726 valuelen++; /* returned value doesn't include room for '\0' */
727 value = HeapAlloc(GetProcessHeap(), 0, valuelen);
728 data = HeapAlloc(GetProcessHeap(), 0, datalen);
730 dlen = datalen;
731 vlen = valuelen;
732 while(RegEnumValueA(hkey, i++, value, &vlen, NULL, &type, data,
733 &dlen) == ERROR_SUCCESS) {
734 if(((LPSTR)data)[0] && ((LPSTR)data)[1] == ':')
735 if(wine_get_unix_file_name((LPSTR)data, unixname, sizeof(unixname)))
736 AddFontFileToList(unixname, NULL);
738 /* reset dlen and vlen */
739 dlen = datalen;
740 vlen = valuelen;
742 HeapFree(GetProcessHeap(), 0, data);
743 HeapFree(GetProcessHeap(), 0, value);
744 RegCloseKey(hkey);
748 /* then look in any directories that we've specified in the config file */
749 if(RegOpenKeyA(HKEY_LOCAL_MACHINE,
750 "Software\\Wine\\Wine\\Config\\FontDirs",
751 &hkey) == ERROR_SUCCESS) {
753 RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
754 &valuelen, &datalen, NULL, NULL);
756 valuelen++; /* returned value doesn't include room for '\0' */
757 value = HeapAlloc(GetProcessHeap(), 0, valuelen);
758 data = HeapAlloc(GetProcessHeap(), 0, datalen);
760 dlen = datalen;
761 vlen = valuelen;
762 i = 0;
763 while(RegEnumValueA(hkey, i++, value, &vlen, NULL, &type, data,
764 &dlen) == ERROR_SUCCESS) {
765 TRACE("Got %s=%s\n", value, (LPSTR)data);
766 ReadFontDir((LPSTR)data);
767 /* reset dlen and vlen */
768 dlen = datalen;
769 vlen = valuelen;
771 HeapFree(GetProcessHeap(), 0, data);
772 HeapFree(GetProcessHeap(), 0, value);
773 RegCloseKey(hkey);
776 DumpFontList();
777 LoadSubstList();
778 DumpSubstList();
779 LoadReplaceList();
780 return TRUE;
781 sym_not_found:
782 WINE_MESSAGE(
783 "Wine cannot find certain functions that it needs inside the FreeType\n"
784 "font library. To enable Wine to use TrueType fonts please upgrade\n"
785 "FreeType to at least version 2.0.5.\n"
786 "http://www.freetype.org\n");
787 wine_dlclose(ft_handle, NULL, 0);
788 ft_handle = NULL;
789 return FALSE;
793 static LONG calc_ppem_for_height(FT_Face ft_face, LONG height)
795 TT_OS2 *pOS2;
796 LONG ppem;
798 pOS2 = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_os2);
800 if(height == 0) height = 16;
802 /* Calc. height of EM square:
804 * For +ve lfHeight we have
805 * lfHeight = (winAscent + winDescent) * ppem / units_per_em
806 * Re-arranging gives:
807 * ppem = units_per_em * lfheight / (winAscent + winDescent)
809 * For -ve lfHeight we have
810 * |lfHeight| = ppem
811 * [i.e. |lfHeight| = (winAscent + winDescent - il) * ppem / units_per_em
812 * with il = winAscent + winDescent - units_per_em]
816 if(height > 0)
817 ppem = ft_face->units_per_EM * height /
818 (pOS2->usWinAscent + pOS2->usWinDescent);
819 else
820 ppem = -height;
822 return ppem;
825 static LONG load_VDMX(GdiFont, LONG);
827 static FT_Face OpenFontFile(GdiFont font, char *file, FT_Long face_index, LONG height)
829 FT_Error err;
830 FT_Face ft_face;
831 LONG ppem;
833 err = pFT_New_Face(library, file, face_index, &ft_face);
834 if(err) {
835 ERR("FT_New_Face rets %d\n", err);
836 return 0;
839 /* set it here, as load_VDMX needs it */
840 font->ft_face = ft_face;
842 /* load the VDMX table if we have one */
843 ppem = load_VDMX(font, height);
844 if(ppem == 0)
845 ppem = calc_ppem_for_height(ft_face, height);
847 pFT_Set_Pixel_Sizes(ft_face, 0, ppem);
849 return ft_face;
853 static int get_nearest_charset(Face *face)
855 /* Only get here if lfCharSet == DEFAULT_CHARSET or we couldn't find
856 a single face with the requested charset. The idea is to check if
857 the selected font supports the current ANSI codepage, if it does
858 return the corresponding charset, else return the first charset */
860 CHARSETINFO csi;
861 int acp = GetACP(), i;
862 DWORD fs0;
864 if(TranslateCharsetInfo((DWORD*)acp, &csi, TCI_SRCCODEPAGE))
865 if(csi.fs.fsCsb[0] & face->fs.fsCsb[0])
866 return csi.ciCharset;
868 for(i = 0; i < 32; i++) {
869 fs0 = 1L << i;
870 if(face->fs.fsCsb[0] & fs0) {
871 if(TranslateCharsetInfo(&fs0, &csi, TCI_SRCFONTSIG))
872 return csi.ciCharset;
873 else
874 FIXME("TCI failing on %lx\n", fs0);
878 FIXME("returning DEFAULT_CHARSET face->fs.fsCsb[0] = %08lx file = %s\n",
879 face->fs.fsCsb[0], face->file);
880 return DEFAULT_CHARSET;
883 static GdiFont alloc_font(void)
885 GdiFont ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*ret));
886 ret->gmsize = INIT_GM_SIZE;
887 ret->gm = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
888 ret->gmsize * sizeof(*ret->gm));
889 ret->next = NULL;
890 ret->potm = NULL;
891 ret->xform.eM11 = ret->xform.eM22 = 1.0;
892 return ret;
895 static void free_font(GdiFont font)
897 if (font->ft_face) pFT_Done_Face(font->ft_face);
898 if (font->potm) HeapFree(GetProcessHeap(), 0, font->potm);
899 if (font->name) HeapFree(GetProcessHeap(), 0, font->name);
900 HeapFree(GetProcessHeap(), 0, font->gm);
901 HeapFree(GetProcessHeap(), 0, font);
905 /*************************************************************
906 * load_VDMX
908 * load the vdmx entry for the specified height
911 #define MS_MAKE_TAG( _x1, _x2, _x3, _x4 ) \
912 ( ( (FT_ULong)_x4 << 24 ) | \
913 ( (FT_ULong)_x3 << 16 ) | \
914 ( (FT_ULong)_x2 << 8 ) | \
915 (FT_ULong)_x1 )
917 #define MS_VDMX_TAG MS_MAKE_TAG('V', 'D', 'M', 'X')
919 typedef struct {
920 BYTE bCharSet;
921 BYTE xRatio;
922 BYTE yStartRatio;
923 BYTE yEndRatio;
924 } Ratios;
927 static LONG load_VDMX(GdiFont font, LONG height)
929 BYTE hdr[6], tmp[2], group[4];
930 BYTE devXRatio, devYRatio;
931 USHORT numRecs, numRatios;
932 DWORD offset = -1;
933 LONG ppem = 0;
934 int i, result;
936 result = WineEngGetFontData(font, MS_VDMX_TAG, 0, hdr, 6);
938 if(result == GDI_ERROR) /* no vdmx table present, use linear scaling */
939 return ppem;
941 /* FIXME: need the real device aspect ratio */
942 devXRatio = 1;
943 devYRatio = 1;
945 numRecs = GET_BE_WORD(&hdr[2]);
946 numRatios = GET_BE_WORD(&hdr[4]);
948 TRACE("numRecs = %d numRatios = %d\n", numRecs, numRatios);
949 for(i = 0; i < numRatios; i++) {
950 Ratios ratio;
952 offset = (3 * 2) + (i * sizeof(Ratios));
953 WineEngGetFontData(font, MS_VDMX_TAG, offset, &ratio, sizeof(Ratios));
954 offset = -1;
956 TRACE("Ratios[%d] %d %d : %d -> %d\n", i, ratio.bCharSet, ratio.xRatio, ratio.yStartRatio, ratio.yEndRatio);
958 if(ratio.bCharSet != 1)
959 continue;
961 if((ratio.xRatio == 0 &&
962 ratio.yStartRatio == 0 &&
963 ratio.yEndRatio == 0) ||
964 (devXRatio == ratio.xRatio &&
965 devYRatio >= ratio.yStartRatio &&
966 devYRatio <= ratio.yEndRatio))
968 offset = (3 * 2) + (numRatios * 4) + (i * 2);
969 WineEngGetFontData(font, MS_VDMX_TAG, offset, tmp, 2);
970 offset = GET_BE_WORD(tmp);
971 break;
975 if(offset < 0) {
976 FIXME("No suitable ratio found\n");
977 return ppem;
980 if(WineEngGetFontData(font, MS_VDMX_TAG, offset, group, 4) != GDI_ERROR) {
981 USHORT recs;
982 BYTE startsz, endsz;
983 BYTE *vTable;
985 recs = GET_BE_WORD(group);
986 startsz = group[2];
987 endsz = group[3];
989 TRACE("recs=%d startsz=%d endsz=%d\n", recs, startsz, endsz);
991 vTable = HeapAlloc(GetProcessHeap(), 0, recs * 6);
992 result = WineEngGetFontData(font, MS_VDMX_TAG, offset + 4, vTable, recs * 6);
993 if(result == GDI_ERROR) {
994 FIXME("Failed to retrieve vTable\n");
995 goto end;
998 if(height > 0) {
999 for(i = 0; i < recs; i++) {
1000 SHORT yMax = GET_BE_WORD(&vTable[(i * 6) + 2]);
1001 SHORT yMin = GET_BE_WORD(&vTable[(i * 6) + 4]);
1002 ppem = GET_BE_WORD(&vTable[i * 6]);
1004 if(yMax + -yMin == height) {
1005 font->yMax = yMax;
1006 font->yMin = yMin;
1007 TRACE("ppem %ld found; height=%ld yMax=%d yMin=%d\n", ppem, height, font->yMax, font->yMin);
1008 break;
1010 if(yMax + -yMin > height) {
1011 if(--i < 0) {
1012 ppem = 0;
1013 goto end; /* failed */
1015 font->yMax = GET_BE_WORD(&vTable[(i * 6) + 2]);
1016 font->yMin = GET_BE_WORD(&vTable[(i * 6) + 4]);
1017 TRACE("ppem %ld found; height=%ld yMax=%d yMin=%d\n", ppem, height, font->yMax, font->yMin);
1018 break;
1021 if(!font->yMax) {
1022 ppem = 0;
1023 TRACE("ppem not found for height %ld\n", height);
1025 } else {
1026 ppem = -height;
1027 if(ppem < startsz || ppem > endsz)
1028 goto end;
1030 for(i = 0; i < recs; i++) {
1031 USHORT yPelHeight;
1032 yPelHeight = GET_BE_WORD(&vTable[i * 6]);
1034 if(yPelHeight > ppem)
1035 break; /* failed */
1037 if(yPelHeight == ppem) {
1038 font->yMax = GET_BE_WORD(&vTable[(i * 6) + 2]);
1039 font->yMin = GET_BE_WORD(&vTable[(i * 6) + 4]);
1040 TRACE("ppem %ld found; yMax=%d yMin=%d\n", ppem, font->yMax, font->yMin);
1041 break;
1045 end:
1046 HeapFree(GetProcessHeap(), 0, vTable);
1049 return ppem;
1053 /*************************************************************
1054 * WineEngCreateFontInstance
1057 GdiFont WineEngCreateFontInstance(DC *dc, HFONT hfont)
1059 GdiFont ret;
1060 Face *face;
1061 Family *family = NULL;
1062 BOOL bd, it;
1063 LOGFONTW lf;
1064 CHARSETINFO csi;
1066 if (!GetObjectW( hfont, sizeof(lf), &lf )) return NULL;
1068 TRACE("%s, h=%ld, it=%d, weight=%ld, PandF=%02x, charset=%d orient %ld escapement %ld\n",
1069 debugstr_w(lf.lfFaceName), lf.lfHeight, lf.lfItalic,
1070 lf.lfWeight, lf.lfPitchAndFamily, lf.lfCharSet, lf.lfOrientation,
1071 lf.lfEscapement);
1073 /* check the cache first */
1074 for(ret = GdiFontList; ret; ret = ret->next) {
1075 if(ret->hfont == hfont && !memcmp(&ret->xform, &dc->xformWorld2Vport, offsetof(XFORM, eDx))) {
1076 TRACE("returning cached gdiFont(%p) for hFont %p\n", ret, hfont);
1077 return ret;
1081 if(!FontList || !have_installed_roman_font) /* No fonts installed */
1083 TRACE("No fonts installed\n");
1084 return NULL;
1087 ret = alloc_font();
1088 memcpy(&ret->xform, &dc->xformWorld2Vport, sizeof(XFORM));
1090 /* If lfFaceName is "Symbol" then Windows fixes up lfCharSet to
1091 SYMBOL_CHARSET so that Symbol gets picked irrespective of the
1092 original value lfCharSet. Note this is a special case for
1093 Symbol and doesn't happen at least for "Wingdings*" */
1095 if(!strcmpiW(lf.lfFaceName, SymbolW))
1096 lf.lfCharSet = SYMBOL_CHARSET;
1098 if(!TranslateCharsetInfo((DWORD*)(INT)lf.lfCharSet, &csi, TCI_SRCCHARSET)) {
1099 switch(lf.lfCharSet) {
1100 case DEFAULT_CHARSET:
1101 csi.fs.fsCsb[0] = 0;
1102 break;
1103 default:
1104 FIXME("Untranslated charset %d\n", lf.lfCharSet);
1105 csi.fs.fsCsb[0] = 0;
1106 break;
1110 if(lf.lfFaceName[0] != '\0') {
1111 FontSubst *psub;
1112 for(psub = substlist; psub; psub = psub->next)
1113 if(!strcmpiW(lf.lfFaceName, psub->from.name) &&
1114 (psub->from.charset == -1 ||
1115 psub->from.charset == lf.lfCharSet))
1116 break;
1117 if(psub) {
1118 TRACE("substituting %s -> %s\n", debugstr_w(lf.lfFaceName),
1119 debugstr_w(psub->to.name));
1120 strcpyW(lf.lfFaceName, psub->to.name);
1123 /* We want a match on name and charset or just name if
1124 charset was DEFAULT_CHARSET. If the latter then
1125 we fixup the returned charset later in get_nearest_charset
1126 where we'll either use the charset of the current ansi codepage
1127 or if that's unavailable the first charset that the font supports.
1129 for(family = FontList; family; family = family->next) {
1130 if(!strcmpiW(family->FamilyName, lf.lfFaceName))
1131 if((csi.fs.fsCsb[0] & family->FirstFace->fs.fsCsb[0]) || !csi.fs.fsCsb[0])
1132 break;
1135 if(!family) { /* do other aliases here */
1136 if(!strcmpiW(lf.lfFaceName, SystemW))
1137 strcpyW(lf.lfFaceName, defSystem);
1138 else if(!strcmpiW(lf.lfFaceName, MSSansSerifW))
1139 strcpyW(lf.lfFaceName, defSans);
1140 else if(!strcmpiW(lf.lfFaceName, HelvW))
1141 strcpyW(lf.lfFaceName, defSans);
1142 else
1143 goto not_found;
1145 for(family = FontList; family; family = family->next) {
1146 if(!strcmpiW(family->FamilyName, lf.lfFaceName))
1147 if((csi.fs.fsCsb[0] & family->FirstFace->fs.fsCsb[0]) || !csi.fs.fsCsb[0])
1148 break;
1153 not_found:
1154 if(!family) {
1155 /* If requested charset was DEFAULT_CHARSET then try using charset
1156 corresponding to the current ansi codepage */
1157 if(!csi.fs.fsCsb[0]) {
1158 INT acp = GetACP();
1159 if(!TranslateCharsetInfo((DWORD*)acp, &csi, TCI_SRCCODEPAGE)) {
1160 FIXME("TCI failed on codepage %d\n", acp);
1161 csi.fs.fsCsb[0] = 0;
1162 } else
1163 lf.lfCharSet = csi.ciCharset;
1166 /* Face families are in the top 4 bits of lfPitchAndFamily,
1167 so mask with 0xF0 before testing */
1169 if((lf.lfPitchAndFamily & FIXED_PITCH) ||
1170 (lf.lfPitchAndFamily & 0xF0) == FF_MODERN)
1171 strcpyW(lf.lfFaceName, defFixed);
1172 else if((lf.lfPitchAndFamily & 0xF0) == FF_ROMAN)
1173 strcpyW(lf.lfFaceName, defSerif);
1174 else if((lf.lfPitchAndFamily & 0xF0) == FF_SWISS)
1175 strcpyW(lf.lfFaceName, defSans);
1176 else
1177 strcpyW(lf.lfFaceName, defSans);
1178 for(family = FontList; family; family = family->next) {
1179 if(!strcmpiW(family->FamilyName, lf.lfFaceName) &&
1180 (csi.fs.fsCsb[0] & family->FirstFace->fs.fsCsb[0]))
1181 break;
1185 if(!family) {
1186 for(family = FontList; family; family = family->next) {
1187 if(csi.fs.fsCsb[0] & family->FirstFace->fs.fsCsb[0])
1188 break;
1192 if(!family) {
1193 family = FontList;
1194 csi.fs.fsCsb[0] = 0;
1195 FIXME("just using first face for now\n");
1198 it = lf.lfItalic ? 1 : 0;
1199 bd = lf.lfWeight > 550 ? 1 : 0;
1201 for(face = family->FirstFace; face; face = face->next) {
1202 if(!(face->Italic ^ it) && !(face->Bold ^ bd))
1203 break;
1205 if(!face) {
1206 face = family->FirstFace;
1207 if(it && !face->Italic) ret->fake_italic = TRUE;
1208 if(bd && !face->Bold) ret->fake_bold = TRUE;
1211 memcpy(&ret->fs, &face->fs, sizeof(FONTSIGNATURE));
1213 if(csi.fs.fsCsb[0])
1214 ret->charset = lf.lfCharSet;
1215 else
1216 ret->charset = get_nearest_charset(face);
1218 TRACE("Chosen: %s %s\n", debugstr_w(family->FamilyName),
1219 debugstr_w(face->StyleName));
1221 ret->ft_face = OpenFontFile(ret, face->file, face->face_index,
1222 lf.lfHeight < 0 ?
1223 -abs(INTERNAL_YWSTODS(dc,lf.lfHeight)) :
1224 abs(INTERNAL_YWSTODS(dc, lf.lfHeight)));
1225 if (!ret->ft_face)
1227 free_font( ret );
1228 return 0;
1231 if(ret->charset == SYMBOL_CHARSET)
1232 pFT_Select_Charmap(ret->ft_face, ft_encoding_symbol);
1233 ret->orientation = lf.lfOrientation;
1234 ret->name = strdupW(family->FamilyName);
1236 TRACE("caching: gdiFont=%p hfont=%p\n", ret, hfont);
1237 ret->hfont = hfont;
1238 ret->next = GdiFontList;
1239 GdiFontList = ret;
1241 return ret;
1244 static void DumpGdiFontList(void)
1246 GdiFont gdiFont;
1248 TRACE("---------- gdiFont Cache ----------\n");
1249 for(gdiFont = GdiFontList; gdiFont; gdiFont = gdiFont->next) {
1250 LOGFONTW lf;
1251 GetObjectW( gdiFont->hfont, sizeof(lf), &lf );
1252 TRACE("gdiFont=%p hfont=%p (%s)\n",
1253 gdiFont, gdiFont->hfont, debugstr_w(lf.lfFaceName));
1257 /*************************************************************
1258 * WineEngDestroyFontInstance
1260 * free the gdiFont associated with this handle
1263 BOOL WineEngDestroyFontInstance(HFONT handle)
1265 GdiFont gdiFont;
1266 GdiFont gdiPrev = NULL;
1267 BOOL ret = FALSE;
1269 TRACE("destroying hfont=%p\n", handle);
1270 if(TRACE_ON(font))
1271 DumpGdiFontList();
1273 gdiFont = GdiFontList;
1274 while(gdiFont) {
1275 if(gdiFont->hfont == handle) {
1276 if(gdiPrev) {
1277 gdiPrev->next = gdiFont->next;
1278 free_font(gdiFont);
1279 gdiFont = gdiPrev->next;
1280 } else {
1281 GdiFontList = gdiFont->next;
1282 free_font(gdiFont);
1283 gdiFont = GdiFontList;
1285 ret = TRUE;
1286 } else {
1287 gdiPrev = gdiFont;
1288 gdiFont = gdiFont->next;
1291 return ret;
1294 static void GetEnumStructs(Face *face, LPENUMLOGFONTEXW pelf,
1295 NEWTEXTMETRICEXW *pntm, LPDWORD ptype)
1297 OUTLINETEXTMETRICW *potm;
1298 UINT size;
1299 GdiFont font = alloc_font();
1301 if (!(font->ft_face = OpenFontFile(font, face->file, face->face_index, 100)))
1303 free_font(font);
1304 return;
1307 font->name = strdupW(face->family->FamilyName);
1309 memset(&pelf->elfLogFont, 0, sizeof(LOGFONTW));
1311 size = WineEngGetOutlineTextMetrics(font, 0, NULL);
1312 potm = HeapAlloc(GetProcessHeap(), 0, size);
1313 WineEngGetOutlineTextMetrics(font, size, potm);
1315 #define TM potm->otmTextMetrics
1317 pntm->ntmTm.tmHeight = pelf->elfLogFont.lfHeight = TM.tmHeight;
1318 pntm->ntmTm.tmAscent = TM.tmAscent;
1319 pntm->ntmTm.tmDescent = TM.tmDescent;
1320 pntm->ntmTm.tmInternalLeading = TM.tmInternalLeading;
1321 pntm->ntmTm.tmExternalLeading = TM.tmExternalLeading;
1322 pntm->ntmTm.tmAveCharWidth = pelf->elfLogFont.lfWidth = TM.tmAveCharWidth;
1323 pntm->ntmTm.tmMaxCharWidth = TM.tmMaxCharWidth;
1324 pntm->ntmTm.tmWeight = pelf->elfLogFont.lfWeight = TM.tmWeight;
1325 pntm->ntmTm.tmOverhang = TM.tmOverhang;
1326 pntm->ntmTm.tmDigitizedAspectX = TM.tmDigitizedAspectX;
1327 pntm->ntmTm.tmDigitizedAspectY = TM.tmDigitizedAspectY;
1328 pntm->ntmTm.tmFirstChar = TM.tmFirstChar;
1329 pntm->ntmTm.tmLastChar = TM.tmLastChar;
1330 pntm->ntmTm.tmDefaultChar = TM.tmDefaultChar;
1331 pntm->ntmTm.tmBreakChar = TM.tmBreakChar;
1332 pntm->ntmTm.tmItalic = pelf->elfLogFont.lfItalic = TM.tmItalic;
1333 pntm->ntmTm.tmUnderlined = pelf->elfLogFont.lfUnderline = TM.tmUnderlined;
1334 pntm->ntmTm.tmStruckOut = pelf->elfLogFont.lfStrikeOut = TM.tmStruckOut;
1335 pntm->ntmTm.tmPitchAndFamily = TM.tmPitchAndFamily;
1336 pelf->elfLogFont.lfPitchAndFamily = (TM.tmPitchAndFamily & 0xf1) + 1;
1337 pntm->ntmTm.tmCharSet = pelf->elfLogFont.lfCharSet = TM.tmCharSet;
1338 pelf->elfLogFont.lfOutPrecision = OUT_STROKE_PRECIS;
1339 pelf->elfLogFont.lfClipPrecision = CLIP_STROKE_PRECIS;
1340 pelf->elfLogFont.lfQuality = DRAFT_QUALITY;
1342 pntm->ntmTm.ntmFlags = TM.tmItalic ? NTM_ITALIC : 0;
1343 if(TM.tmWeight > 550) pntm->ntmTm.ntmFlags |= NTM_BOLD;
1344 if(pntm->ntmTm.ntmFlags == 0) pntm->ntmTm.ntmFlags = NTM_REGULAR;
1346 pntm->ntmTm.ntmSizeEM = potm->otmEMSquare;
1347 pntm->ntmTm.ntmCellHeight = 0;
1348 pntm->ntmTm.ntmAvgWidth = 0;
1350 *ptype = TM.tmPitchAndFamily & TMPF_TRUETYPE ? TRUETYPE_FONTTYPE : 0;
1351 if(!(TM.tmPitchAndFamily & TMPF_VECTOR))
1352 *ptype |= RASTER_FONTTYPE;
1354 #undef TM
1355 memset(&pntm->ntmFontSig, 0, sizeof(FONTSIGNATURE));
1357 strncpyW(pelf->elfLogFont.lfFaceName,
1358 (WCHAR*)((char*)potm + (ptrdiff_t)potm->otmpFamilyName),
1359 LF_FACESIZE);
1360 strncpyW(pelf->elfFullName,
1361 (WCHAR*)((char*)potm + (ptrdiff_t)potm->otmpFaceName),
1362 LF_FULLFACESIZE);
1363 strncpyW(pelf->elfStyle,
1364 (WCHAR*)((char*)potm + (ptrdiff_t)potm->otmpStyleName),
1365 LF_FACESIZE);
1366 pelf->elfScript[0] = '\0'; /* This will get set in WineEngEnumFonts */
1368 HeapFree(GetProcessHeap(), 0, potm);
1369 free_font(font);
1370 return;
1373 /*************************************************************
1374 * WineEngEnumFonts
1377 DWORD WineEngEnumFonts(LPLOGFONTW plf, DEVICEFONTENUMPROC proc,
1378 LPARAM lparam)
1380 Family *family;
1381 Face *face;
1382 ENUMLOGFONTEXW elf;
1383 NEWTEXTMETRICEXW ntm;
1384 DWORD type, ret = 1;
1385 FONTSIGNATURE fs;
1386 CHARSETINFO csi;
1387 LOGFONTW lf;
1388 int i;
1390 TRACE("facename = %s charset %d\n", debugstr_w(plf->lfFaceName), plf->lfCharSet);
1392 if(plf->lfFaceName[0]) {
1393 FontSubst *psub;
1394 for(psub = substlist; psub; psub = psub->next)
1395 if(!strcmpiW(plf->lfFaceName, psub->from.name) &&
1396 (psub->from.charset == -1 ||
1397 psub->from.charset == plf->lfCharSet))
1398 break;
1399 if(psub) {
1400 TRACE("substituting %s -> %s\n", debugstr_w(plf->lfFaceName),
1401 debugstr_w(psub->to.name));
1402 memcpy(&lf, plf, sizeof(lf));
1403 strcpyW(lf.lfFaceName, psub->to.name);
1404 plf = &lf;
1406 for(family = FontList; family; family = family->next) {
1407 if(!strcmpiW(plf->lfFaceName, family->FamilyName)) {
1408 for(face = family->FirstFace; face; face = face->next) {
1409 GetEnumStructs(face, &elf, &ntm, &type);
1410 for(i = 0; i < 32; i++) {
1411 if(face->fs.fsCsb[0] & (1L << i)) {
1412 fs.fsCsb[0] = 1L << i;
1413 fs.fsCsb[1] = 0;
1414 if(!TranslateCharsetInfo(fs.fsCsb, &csi,
1415 TCI_SRCFONTSIG))
1416 csi.ciCharset = DEFAULT_CHARSET;
1417 if(i == 31) csi.ciCharset = SYMBOL_CHARSET;
1418 if(csi.ciCharset != DEFAULT_CHARSET) {
1419 elf.elfLogFont.lfCharSet =
1420 ntm.ntmTm.tmCharSet = csi.ciCharset;
1421 if(ElfScriptsW[i])
1422 strcpyW(elf.elfScript, ElfScriptsW[i]);
1423 else
1424 FIXME("Unknown elfscript for bit %d\n", i);
1425 TRACE("enuming face %s full %s style %s charset %d type %ld script %s it %d weight %ld ntmflags %08lx\n",
1426 debugstr_w(elf.elfLogFont.lfFaceName),
1427 debugstr_w(elf.elfFullName), debugstr_w(elf.elfStyle),
1428 csi.ciCharset, type, debugstr_w(elf.elfScript),
1429 elf.elfLogFont.lfItalic, elf.elfLogFont.lfWeight,
1430 ntm.ntmTm.ntmFlags);
1431 ret = proc(&elf, &ntm, type, lparam);
1432 if(!ret) goto end;
1439 } else {
1440 for(family = FontList; family; family = family->next) {
1441 GetEnumStructs(family->FirstFace, &elf, &ntm, &type);
1442 for(i = 0; i < 32; i++) {
1443 if(family->FirstFace->fs.fsCsb[0] & (1L << i)) {
1444 fs.fsCsb[0] = 1L << i;
1445 fs.fsCsb[1] = 0;
1446 if(!TranslateCharsetInfo(fs.fsCsb, &csi,
1447 TCI_SRCFONTSIG))
1448 csi.ciCharset = DEFAULT_CHARSET;
1449 if(i == 31) csi.ciCharset = SYMBOL_CHARSET;
1450 if(csi.ciCharset != DEFAULT_CHARSET) {
1451 elf.elfLogFont.lfCharSet = ntm.ntmTm.tmCharSet =
1452 csi.ciCharset;
1453 if(ElfScriptsW[i])
1454 strcpyW(elf.elfScript, ElfScriptsW[i]);
1455 else
1456 FIXME("Unknown elfscript for bit %d\n", i);
1457 TRACE("enuming face %s full %s style %s charset = %d type %ld script %s it %d weight %ld ntmflags %08lx\n",
1458 debugstr_w(elf.elfLogFont.lfFaceName),
1459 debugstr_w(elf.elfFullName), debugstr_w(elf.elfStyle),
1460 csi.ciCharset, type, debugstr_w(elf.elfScript),
1461 elf.elfLogFont.lfItalic, elf.elfLogFont.lfWeight,
1462 ntm.ntmTm.ntmFlags);
1463 ret = proc(&elf, &ntm, type, lparam);
1464 if(!ret) goto end;
1470 end:
1471 return ret;
1474 static void FTVectorToPOINTFX(FT_Vector *vec, POINTFX *pt)
1476 pt->x.value = vec->x >> 6;
1477 pt->x.fract = (vec->x & 0x3f) << 10;
1478 pt->x.fract |= ((pt->x.fract >> 6) | (pt->x.fract >> 12));
1479 pt->y.value = vec->y >> 6;
1480 pt->y.fract = (vec->y & 0x3f) << 10;
1481 pt->y.fract |= ((pt->y.fract >> 6) | (pt->y.fract >> 12));
1482 return;
1485 static FT_UInt get_glyph_index(GdiFont font, UINT glyph)
1487 if(font->charset == SYMBOL_CHARSET && glyph < 0x100)
1488 glyph = glyph + 0xf000;
1489 return pFT_Get_Char_Index(font->ft_face, glyph);
1492 /*************************************************************
1493 * WineEngGetGlyphIndices
1495 * FIXME: add support for GGI_MARK_NONEXISTING_GLYPHS
1497 DWORD WineEngGetGlyphIndices(GdiFont font, LPCWSTR lpstr, INT count,
1498 LPWORD pgi, DWORD flags)
1500 INT i;
1502 for(i = 0; i < count; i++)
1503 pgi[i] = get_glyph_index(font, lpstr[i]);
1505 return count;
1508 /*************************************************************
1509 * WineEngGetGlyphOutline
1511 * Behaves in exactly the same way as the win32 api GetGlyphOutline
1512 * except that the first parameter is the HWINEENGFONT of the font in
1513 * question rather than an HDC.
1516 DWORD WineEngGetGlyphOutline(GdiFont font, UINT glyph, UINT format,
1517 LPGLYPHMETRICS lpgm, DWORD buflen, LPVOID buf,
1518 const MAT2* lpmat)
1520 FT_Face ft_face = font->ft_face;
1521 FT_UInt glyph_index;
1522 DWORD width, height, pitch, needed = 0;
1523 FT_Bitmap ft_bitmap;
1524 FT_Error err;
1525 INT left, right, top = 0, bottom = 0;
1526 FT_Angle angle = 0;
1527 FT_Int load_flags = FT_LOAD_DEFAULT;
1529 TRACE("%p, %04x, %08x, %p, %08lx, %p, %p\n", font, glyph, format, lpgm,
1530 buflen, buf, lpmat);
1532 if(format & GGO_GLYPH_INDEX) {
1533 glyph_index = glyph;
1534 format &= ~GGO_GLYPH_INDEX;
1535 } else
1536 glyph_index = get_glyph_index(font, glyph);
1538 if(glyph_index >= font->gmsize) {
1539 font->gmsize = (glyph_index / INIT_GM_SIZE + 1) * INIT_GM_SIZE;
1540 font->gm = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, font->gm,
1541 font->gmsize * sizeof(*font->gm));
1542 } else {
1543 if(format == GGO_METRICS && font->gm[glyph_index].init) {
1544 memcpy(lpgm, &font->gm[glyph_index].gm, sizeof(*lpgm));
1545 return 1; /* FIXME */
1549 if(font->orientation || (format != GGO_METRICS && format != GGO_BITMAP))
1550 load_flags |= FT_LOAD_NO_BITMAP;
1552 err = pFT_Load_Glyph(ft_face, glyph_index, load_flags);
1554 if(err) {
1555 FIXME("FT_Load_Glyph on index %x returns %d\n", glyph_index, err);
1556 return GDI_ERROR;
1559 left = ft_face->glyph->metrics.horiBearingX & -64;
1560 right = ((ft_face->glyph->metrics.horiBearingX +
1561 ft_face->glyph->metrics.width) + 63) & -64;
1563 font->gm[glyph_index].adv = (ft_face->glyph->metrics.horiAdvance + 63) >> 6;
1564 font->gm[glyph_index].lsb = left >> 6;
1565 font->gm[glyph_index].bbx = (right - left) >> 6;
1567 if(font->orientation == 0) {
1568 top = (ft_face->glyph->metrics.horiBearingY + 63) & -64;
1569 bottom = (ft_face->glyph->metrics.horiBearingY -
1570 ft_face->glyph->metrics.height) & -64;
1571 lpgm->gmCellIncX = font->gm[glyph_index].adv;
1572 lpgm->gmCellIncY = 0;
1573 } else {
1574 INT xc, yc;
1575 FT_Vector vec;
1576 angle = font->orientation / 10 << 16;
1577 angle |= ((font->orientation % 10) * (1 << 16)) / 10;
1578 TRACE("angle %ld\n", angle >> 16);
1579 for(xc = 0; xc < 2; xc++) {
1580 for(yc = 0; yc < 2; yc++) {
1581 vec.x = ft_face->glyph->metrics.horiBearingX +
1582 xc * ft_face->glyph->metrics.width;
1583 vec.y = ft_face->glyph->metrics.horiBearingY -
1584 yc * ft_face->glyph->metrics.height;
1585 TRACE("Vec %ld,%ld\n", vec.x, vec.y);
1586 pFT_Vector_Rotate(&vec, angle);
1587 if(xc == 0 && yc == 0) {
1588 left = right = vec.x;
1589 top = bottom = vec.y;
1590 } else {
1591 if(vec.x < left) left = vec.x;
1592 else if(vec.x > right) right = vec.x;
1593 if(vec.y < bottom) bottom = vec.y;
1594 else if(vec.y > top) top = vec.y;
1598 left = left & -64;
1599 right = (right + 63) & -64;
1600 bottom = bottom & -64;
1601 top = (top + 63) & -64;
1603 TRACE("transformed box: (%d,%d - %d,%d)\n", left, top, right, bottom);
1604 vec.x = ft_face->glyph->metrics.horiAdvance;
1605 vec.y = 0;
1606 pFT_Vector_Rotate(&vec, angle);
1607 lpgm->gmCellIncX = (vec.x+63) >> 6;
1608 lpgm->gmCellIncY = -(vec.y+63) >> 6;
1610 lpgm->gmBlackBoxX = (right - left) >> 6;
1611 lpgm->gmBlackBoxY = (top - bottom) >> 6;
1612 lpgm->gmptGlyphOrigin.x = left >> 6;
1613 lpgm->gmptGlyphOrigin.y = top >> 6;
1615 memcpy(&font->gm[glyph_index].gm, lpgm, sizeof(*lpgm));
1616 font->gm[glyph_index].init = TRUE;
1618 if(format == GGO_METRICS)
1619 return 1; /* FIXME */
1621 if(ft_face->glyph->format != ft_glyph_format_outline && format != GGO_BITMAP) {
1622 FIXME("loaded a bitmap\n");
1623 return GDI_ERROR;
1626 switch(format) {
1627 case GGO_BITMAP:
1628 width = lpgm->gmBlackBoxX;
1629 height = lpgm->gmBlackBoxY;
1630 pitch = (width + 31) / 32 * 4;
1631 needed = pitch * height;
1633 if(!buf || !buflen) break;
1635 switch(ft_face->glyph->format) {
1636 case ft_glyph_format_bitmap:
1638 BYTE *src = ft_face->glyph->bitmap.buffer, *dst = buf;
1639 INT w = (ft_face->glyph->bitmap.width + 7) >> 3;
1640 INT h = ft_face->glyph->bitmap.rows;
1641 while(h--) {
1642 memcpy(dst, src, w);
1643 src += ft_face->glyph->bitmap.pitch;
1644 dst += pitch;
1646 break;
1649 case ft_glyph_format_outline:
1650 ft_bitmap.width = width;
1651 ft_bitmap.rows = height;
1652 ft_bitmap.pitch = pitch;
1653 ft_bitmap.pixel_mode = ft_pixel_mode_mono;
1654 ft_bitmap.buffer = buf;
1656 if(font->orientation) {
1657 FT_Matrix matrix;
1658 matrix.xx = matrix.yy = pFT_Cos(angle);
1659 matrix.xy = -pFT_Sin(angle);
1660 matrix.yx = -matrix.xy;
1662 pFT_Outline_Transform(&ft_face->glyph->outline, &matrix);
1665 if (lpmat) pFT_Outline_Transform(&ft_face->glyph->outline, (FT_Matrix *)lpmat);
1666 pFT_Outline_Translate(&ft_face->glyph->outline, -left, -bottom );
1668 /* Note: FreeType will only set 'black' bits for us. */
1669 memset(buf, 0, needed);
1670 pFT_Outline_Get_Bitmap(library, &ft_face->glyph->outline, &ft_bitmap);
1671 break;
1673 default:
1674 FIXME("loaded glyph format %x\n", ft_face->glyph->format);
1675 return GDI_ERROR;
1677 break;
1679 case GGO_GRAY2_BITMAP:
1680 case GGO_GRAY4_BITMAP:
1681 case GGO_GRAY8_BITMAP:
1682 case WINE_GGO_GRAY16_BITMAP:
1684 int mult, row, col;
1685 BYTE *start, *ptr;
1687 width = lpgm->gmBlackBoxX;
1688 height = lpgm->gmBlackBoxY;
1689 pitch = (width + 3) / 4 * 4;
1690 needed = pitch * height;
1692 if(!buf || !buflen) break;
1693 ft_bitmap.width = width;
1694 ft_bitmap.rows = height;
1695 ft_bitmap.pitch = pitch;
1696 ft_bitmap.pixel_mode = ft_pixel_mode_grays;
1697 ft_bitmap.buffer = buf;
1699 if(font->orientation) {
1700 FT_Matrix matrix;
1701 matrix.xx = matrix.yy = pFT_Cos(angle);
1702 matrix.xy = -pFT_Sin(angle);
1703 matrix.yx = -matrix.xy;
1704 pFT_Outline_Transform(&ft_face->glyph->outline, &matrix);
1707 if (lpmat) pFT_Outline_Transform(&ft_face->glyph->outline, (FT_Matrix *)lpmat);
1708 pFT_Outline_Translate(&ft_face->glyph->outline, -left, -bottom );
1710 pFT_Outline_Get_Bitmap(library, &ft_face->glyph->outline, &ft_bitmap);
1712 if(format == GGO_GRAY2_BITMAP)
1713 mult = 5;
1714 else if(format == GGO_GRAY4_BITMAP)
1715 mult = 17;
1716 else if(format == GGO_GRAY8_BITMAP)
1717 mult = 65;
1718 else if(format == WINE_GGO_GRAY16_BITMAP)
1719 break;
1720 else {
1721 assert(0);
1722 break;
1725 start = buf;
1726 for(row = 0; row < height; row++) {
1727 ptr = start;
1728 for(col = 0; col < width; col++, ptr++) {
1729 *ptr = (*(unsigned int*)ptr * mult + 128) / 256;
1731 start += pitch;
1733 break;
1736 case GGO_NATIVE:
1738 int contour, point = 0, first_pt;
1739 FT_Outline *outline = &ft_face->glyph->outline;
1740 TTPOLYGONHEADER *pph;
1741 TTPOLYCURVE *ppc;
1742 DWORD pph_start, cpfx, type;
1744 if(buflen == 0) buf = NULL;
1746 if (lpmat) pFT_Outline_Transform(outline, (FT_Matrix *)lpmat);
1748 for(contour = 0; contour < outline->n_contours; contour++) {
1749 pph_start = needed;
1750 pph = (TTPOLYGONHEADER *)((char *)buf + needed);
1751 first_pt = point;
1752 if(buf) {
1753 pph->dwType = TT_POLYGON_TYPE;
1754 FTVectorToPOINTFX(&outline->points[point], &pph->pfxStart);
1756 needed += sizeof(*pph);
1757 point++;
1758 while(point <= outline->contours[contour]) {
1759 ppc = (TTPOLYCURVE *)((char *)buf + needed);
1760 type = (outline->tags[point] & FT_Curve_Tag_On) ?
1761 TT_PRIM_LINE : TT_PRIM_QSPLINE;
1762 cpfx = 0;
1763 do {
1764 if(buf)
1765 FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
1766 cpfx++;
1767 point++;
1768 } while(point <= outline->contours[contour] &&
1769 (outline->tags[point] & FT_Curve_Tag_On) ==
1770 (outline->tags[point-1] & FT_Curve_Tag_On));
1771 /* At the end of a contour Windows adds the start point, but
1772 only for Beziers */
1773 if(point > outline->contours[contour] &&
1774 !(outline->tags[point-1] & FT_Curve_Tag_On)) {
1775 if(buf)
1776 FTVectorToPOINTFX(&outline->points[first_pt], &ppc->apfx[cpfx]);
1777 cpfx++;
1778 } else if(point <= outline->contours[contour] &&
1779 outline->tags[point] & FT_Curve_Tag_On) {
1780 /* add closing pt for bezier */
1781 if(buf)
1782 FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
1783 cpfx++;
1784 point++;
1786 if(buf) {
1787 ppc->wType = type;
1788 ppc->cpfx = cpfx;
1790 needed += sizeof(*ppc) + (cpfx - 1) * sizeof(POINTFX);
1792 if(buf)
1793 pph->cb = needed - pph_start;
1795 break;
1797 case GGO_BEZIER:
1799 /* Convert the quadratic Beziers to cubic Beziers.
1800 The parametric eqn for a cubic Bezier is, from PLRM:
1801 r(t) = at^3 + bt^2 + ct + r0
1802 with the control points:
1803 r1 = r0 + c/3
1804 r2 = r1 + (c + b)/3
1805 r3 = r0 + c + b + a
1807 A quadratic Beizer has the form:
1808 p(t) = (1-t)^2 p0 + 2(1-t)t p1 + t^2 p2
1810 So equating powers of t leads to:
1811 r1 = 2/3 p1 + 1/3 p0
1812 r2 = 2/3 p1 + 1/3 p2
1813 and of course r0 = p0, r3 = p2
1816 int contour, point = 0, first_pt;
1817 FT_Outline *outline = &ft_face->glyph->outline;
1818 TTPOLYGONHEADER *pph;
1819 TTPOLYCURVE *ppc;
1820 DWORD pph_start, cpfx, type;
1821 FT_Vector cubic_control[4];
1822 if(buflen == 0) buf = NULL;
1824 if (lpmat) pFT_Outline_Transform(outline, (FT_Matrix *)lpmat);
1826 for(contour = 0; contour < outline->n_contours; contour++) {
1827 pph_start = needed;
1828 pph = (TTPOLYGONHEADER *)((char *)buf + needed);
1829 first_pt = point;
1830 if(buf) {
1831 pph->dwType = TT_POLYGON_TYPE;
1832 FTVectorToPOINTFX(&outline->points[point], &pph->pfxStart);
1834 needed += sizeof(*pph);
1835 point++;
1836 while(point <= outline->contours[contour]) {
1837 ppc = (TTPOLYCURVE *)((char *)buf + needed);
1838 type = (outline->tags[point] & FT_Curve_Tag_On) ?
1839 TT_PRIM_LINE : TT_PRIM_CSPLINE;
1840 cpfx = 0;
1841 do {
1842 if(type == TT_PRIM_LINE) {
1843 if(buf)
1844 FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
1845 cpfx++;
1846 point++;
1847 } else {
1848 /* Unlike QSPLINEs, CSPLINEs always have their endpoint
1849 so cpfx = 3n */
1851 /* FIXME: Possible optimization in endpoint calculation
1852 if there are two consecutive curves */
1853 cubic_control[0] = outline->points[point-1];
1854 if(!(outline->tags[point-1] & FT_Curve_Tag_On)) {
1855 cubic_control[0].x += outline->points[point].x + 1;
1856 cubic_control[0].y += outline->points[point].y + 1;
1857 cubic_control[0].x >>= 1;
1858 cubic_control[0].y >>= 1;
1860 if(point+1 > outline->contours[contour])
1861 cubic_control[3] = outline->points[first_pt];
1862 else {
1863 cubic_control[3] = outline->points[point+1];
1864 if(!(outline->tags[point+1] & FT_Curve_Tag_On)) {
1865 cubic_control[3].x += outline->points[point].x + 1;
1866 cubic_control[3].y += outline->points[point].y + 1;
1867 cubic_control[3].x >>= 1;
1868 cubic_control[3].y >>= 1;
1871 /* r1 = 1/3 p0 + 2/3 p1
1872 r2 = 1/3 p2 + 2/3 p1 */
1873 cubic_control[1].x = (2 * outline->points[point].x + 1) / 3;
1874 cubic_control[1].y = (2 * outline->points[point].y + 1) / 3;
1875 cubic_control[2] = cubic_control[1];
1876 cubic_control[1].x += (cubic_control[0].x + 1) / 3;
1877 cubic_control[1].y += (cubic_control[0].y + 1) / 3;
1878 cubic_control[2].x += (cubic_control[3].x + 1) / 3;
1879 cubic_control[2].y += (cubic_control[3].y + 1) / 3;
1880 if(buf) {
1881 FTVectorToPOINTFX(&cubic_control[1], &ppc->apfx[cpfx]);
1882 FTVectorToPOINTFX(&cubic_control[2], &ppc->apfx[cpfx+1]);
1883 FTVectorToPOINTFX(&cubic_control[3], &ppc->apfx[cpfx+2]);
1885 cpfx += 3;
1886 point++;
1888 } while(point <= outline->contours[contour] &&
1889 (outline->tags[point] & FT_Curve_Tag_On) ==
1890 (outline->tags[point-1] & FT_Curve_Tag_On));
1891 /* At the end of a contour Windows adds the start point,
1892 but only for Beziers and we've already done that.
1894 if(point <= outline->contours[contour] &&
1895 outline->tags[point] & FT_Curve_Tag_On) {
1896 /* This is the closing pt of a bezier, but we've already
1897 added it, so just inc point and carry on */
1898 point++;
1900 if(buf) {
1901 ppc->wType = type;
1902 ppc->cpfx = cpfx;
1904 needed += sizeof(*ppc) + (cpfx - 1) * sizeof(POINTFX);
1906 if(buf)
1907 pph->cb = needed - pph_start;
1909 break;
1912 default:
1913 FIXME("Unsupported format %d\n", format);
1914 return GDI_ERROR;
1916 return needed;
1919 /*************************************************************
1920 * WineEngGetTextMetrics
1923 BOOL WineEngGetTextMetrics(GdiFont font, LPTEXTMETRICW ptm)
1925 if(!font->potm) {
1926 if(!WineEngGetOutlineTextMetrics(font, 0, NULL))
1927 return FALSE;
1929 if(!font->potm) return FALSE;
1930 memcpy(ptm, &font->potm->otmTextMetrics, sizeof(*ptm));
1931 return TRUE;
1935 /*************************************************************
1936 * WineEngGetOutlineTextMetrics
1939 UINT WineEngGetOutlineTextMetrics(GdiFont font, UINT cbSize,
1940 OUTLINETEXTMETRICW *potm)
1942 FT_Face ft_face = font->ft_face;
1943 UINT needed, lenfam, lensty, ret;
1944 TT_OS2 *pOS2;
1945 TT_HoriHeader *pHori;
1946 TT_Postscript *pPost;
1947 FT_Fixed x_scale, y_scale;
1948 WCHAR *family_nameW, *style_nameW;
1949 WCHAR spaceW[] = {' ', '\0'};
1950 char *cp;
1952 TRACE("font=%p\n", font);
1954 if(font->potm) {
1955 if(cbSize >= font->potm->otmSize)
1956 memcpy(potm, font->potm, font->potm->otmSize);
1957 return font->potm->otmSize;
1960 needed = sizeof(*potm);
1962 lenfam = (strlenW(font->name) + 1) * sizeof(WCHAR);
1963 family_nameW = strdupW(font->name);
1965 lensty = MultiByteToWideChar(CP_ACP, 0, ft_face->style_name, -1, NULL, 0)
1966 * sizeof(WCHAR);
1967 style_nameW = HeapAlloc(GetProcessHeap(), 0, lensty);
1968 MultiByteToWideChar(CP_ACP, 0, ft_face->style_name, -1,
1969 style_nameW, lensty);
1971 /* These names should be read from the TT name table */
1973 /* length of otmpFamilyName */
1974 needed += lenfam;
1976 /* length of otmpFaceName */
1977 if(!strcasecmp(ft_face->style_name, "regular")) {
1978 needed += lenfam; /* just the family name */
1979 } else {
1980 needed += lenfam + lensty; /* family + " " + style */
1983 /* length of otmpStyleName */
1984 needed += lensty;
1986 /* length of otmpFullName */
1987 needed += lenfam + lensty;
1990 x_scale = ft_face->size->metrics.x_scale;
1991 y_scale = ft_face->size->metrics.y_scale;
1993 pOS2 = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_os2);
1994 if(!pOS2) {
1995 FIXME("Can't find OS/2 table - not TT font?\n");
1996 ret = 0;
1997 goto end;
2000 pHori = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_hhea);
2001 if(!pHori) {
2002 FIXME("Can't find HHEA table - not TT font?\n");
2003 ret = 0;
2004 goto end;
2007 pPost = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_post); /* we can live with this failing */
2009 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",
2010 pOS2->usWinAscent, pOS2->usWinDescent,
2011 pOS2->sTypoAscender, pOS2->sTypoDescender, pOS2->sTypoLineGap,
2012 ft_face->ascender, ft_face->descender, ft_face->height,
2013 pHori->Ascender, pHori->Descender, pHori->Line_Gap,
2014 ft_face->bbox.yMax, ft_face->bbox.yMin);
2016 font->potm = HeapAlloc(GetProcessHeap(), 0, needed);
2017 font->potm->otmSize = needed;
2019 #define TM font->potm->otmTextMetrics
2021 if(font->yMax) {
2022 TM.tmAscent = font->yMax;
2023 TM.tmDescent = -font->yMin;
2024 TM.tmInternalLeading = (TM.tmAscent + TM.tmDescent) - ft_face->size->metrics.y_ppem;
2025 } else {
2026 TM.tmAscent = (pFT_MulFix(pOS2->usWinAscent, y_scale) + 32) >> 6;
2027 TM.tmDescent = (pFT_MulFix(pOS2->usWinDescent, y_scale) + 32) >> 6;
2028 TM.tmInternalLeading = (pFT_MulFix(pOS2->usWinAscent + pOS2->usWinDescent
2029 - ft_face->units_per_EM, y_scale) + 32) >> 6;
2032 TM.tmHeight = TM.tmAscent + TM.tmDescent;
2034 /* MSDN says:
2035 el = MAX(0, LineGap - ((WinAscent + WinDescent) - (Ascender - Descender)))
2037 TM.tmExternalLeading = max(0, (pFT_MulFix(pHori->Line_Gap -
2038 ((pOS2->usWinAscent + pOS2->usWinDescent) -
2039 (pHori->Ascender - pHori->Descender)), y_scale) + 32) >> 6);
2041 TM.tmAveCharWidth = (pFT_MulFix(pOS2->xAvgCharWidth, x_scale) + 32) >> 6;
2042 TM.tmMaxCharWidth = (pFT_MulFix(ft_face->bbox.xMax - ft_face->bbox.xMin, x_scale) + 32) >> 6;
2043 TM.tmWeight = font->fake_bold ? FW_BOLD : pOS2->usWeightClass;
2044 TM.tmOverhang = 0;
2045 TM.tmDigitizedAspectX = 300;
2046 TM.tmDigitizedAspectY = 300;
2047 TM.tmFirstChar = pOS2->usFirstCharIndex;
2048 TM.tmLastChar = pOS2->usLastCharIndex;
2049 TM.tmDefaultChar = pOS2->usDefaultChar;
2050 TM.tmBreakChar = pOS2->usBreakChar ? pOS2->usBreakChar : ' ';
2051 TM.tmItalic = font->fake_italic ? 255 : ((ft_face->style_flags & FT_STYLE_FLAG_ITALIC) ? 255 : 0);
2052 TM.tmUnderlined = 0; /* entry in OS2 table */
2053 TM.tmStruckOut = 0; /* entry in OS2 table */
2055 /* Yes TPMF_FIXED_PITCH is correct; braindead api */
2056 if(!FT_IS_FIXED_WIDTH(ft_face))
2057 TM.tmPitchAndFamily = TMPF_FIXED_PITCH;
2058 else
2059 TM.tmPitchAndFamily = 0;
2061 switch(pOS2->panose[PAN_FAMILYTYPE_INDEX]) {
2062 case PAN_FAMILY_SCRIPT:
2063 TM.tmPitchAndFamily |= FF_SCRIPT;
2064 break;
2065 case PAN_FAMILY_DECORATIVE:
2066 case PAN_FAMILY_PICTORIAL:
2067 TM.tmPitchAndFamily |= FF_DECORATIVE;
2068 break;
2069 case PAN_FAMILY_TEXT_DISPLAY:
2070 if(TM.tmPitchAndFamily == 0) /* fixed */
2071 TM.tmPitchAndFamily = FF_MODERN;
2072 else {
2073 switch(pOS2->panose[PAN_SERIFSTYLE_INDEX]) {
2074 case PAN_SERIF_NORMAL_SANS:
2075 case PAN_SERIF_OBTUSE_SANS:
2076 case PAN_SERIF_PERP_SANS:
2077 TM.tmPitchAndFamily |= FF_SWISS;
2078 break;
2079 default:
2080 TM.tmPitchAndFamily |= FF_ROMAN;
2083 break;
2084 default:
2085 TM.tmPitchAndFamily |= FF_DONTCARE;
2088 if(FT_IS_SCALABLE(ft_face))
2089 TM.tmPitchAndFamily |= TMPF_VECTOR;
2090 if(FT_IS_SFNT(ft_face))
2091 TM.tmPitchAndFamily |= TMPF_TRUETYPE;
2093 TM.tmCharSet = font->charset;
2094 #undef TM
2096 font->potm->otmFiller = 0;
2097 memcpy(&font->potm->otmPanoseNumber, pOS2->panose, PANOSE_COUNT);
2098 font->potm->otmfsSelection = pOS2->fsSelection;
2099 font->potm->otmfsType = pOS2->fsType;
2100 font->potm->otmsCharSlopeRise = pHori->caret_Slope_Rise;
2101 font->potm->otmsCharSlopeRun = pHori->caret_Slope_Run;
2102 font->potm->otmItalicAngle = 0; /* POST table */
2103 font->potm->otmEMSquare = ft_face->units_per_EM;
2104 font->potm->otmAscent = (pFT_MulFix(pOS2->sTypoAscender, y_scale) + 32) >> 6;
2105 font->potm->otmDescent = (pFT_MulFix(pOS2->sTypoDescender, y_scale) + 32) >> 6;
2106 font->potm->otmLineGap = (pFT_MulFix(pOS2->sTypoLineGap, y_scale) + 32) >> 6;
2107 font->potm->otmsCapEmHeight = (pFT_MulFix(pOS2->sCapHeight, y_scale) + 32) >> 6;
2108 font->potm->otmsXHeight = (pFT_MulFix(pOS2->sxHeight, y_scale) + 32) >> 6;
2109 font->potm->otmrcFontBox.left = ft_face->bbox.xMin;
2110 font->potm->otmrcFontBox.right = ft_face->bbox.xMax;
2111 font->potm->otmrcFontBox.top = ft_face->bbox.yMin;
2112 font->potm->otmrcFontBox.bottom = ft_face->bbox.yMax;
2113 font->potm->otmMacAscent = 0; /* where do these come from ? */
2114 font->potm->otmMacDescent = 0;
2115 font->potm->otmMacLineGap = 0;
2116 font->potm->otmusMinimumPPEM = 0; /* TT Header */
2117 font->potm->otmptSubscriptSize.x = (pFT_MulFix(pOS2->ySubscriptXSize, x_scale) + 32) >> 6;
2118 font->potm->otmptSubscriptSize.y = (pFT_MulFix(pOS2->ySubscriptYSize, y_scale) + 32) >> 6;
2119 font->potm->otmptSubscriptOffset.x = (pFT_MulFix(pOS2->ySubscriptXOffset, x_scale) + 32) >> 6;
2120 font->potm->otmptSubscriptOffset.y = (pFT_MulFix(pOS2->ySubscriptYOffset, y_scale) + 32) >> 6;
2121 font->potm->otmptSuperscriptSize.x = (pFT_MulFix(pOS2->ySuperscriptXSize, x_scale) + 32) >> 6;
2122 font->potm->otmptSuperscriptSize.y = (pFT_MulFix(pOS2->ySuperscriptYSize, y_scale) + 32) >> 6;
2123 font->potm->otmptSuperscriptOffset.x = (pFT_MulFix(pOS2->ySuperscriptXOffset, x_scale) + 32) >> 6;
2124 font->potm->otmptSuperscriptOffset.y = (pFT_MulFix(pOS2->ySuperscriptYOffset, y_scale) + 32) >> 6;
2125 font->potm->otmsStrikeoutSize = (pFT_MulFix(pOS2->yStrikeoutSize, y_scale) + 32) >> 6;
2126 font->potm->otmsStrikeoutPosition = (pFT_MulFix(pOS2->yStrikeoutPosition, y_scale) + 32) >> 6;
2127 if(!pPost) {
2128 font->potm->otmsUnderscoreSize = 0;
2129 font->potm->otmsUnderscorePosition = 0;
2130 } else {
2131 font->potm->otmsUnderscoreSize = (pFT_MulFix(pPost->underlineThickness, y_scale) + 32) >> 6;
2132 font->potm->otmsUnderscorePosition = (pFT_MulFix(pPost->underlinePosition, y_scale) + 32) >> 6;
2135 /* otmp* members should clearly have type ptrdiff_t, but M$ knows best */
2136 cp = (char*)font->potm + sizeof(*font->potm);
2137 font->potm->otmpFamilyName = (LPSTR)(cp - (char*)font->potm);
2138 strcpyW((WCHAR*)cp, family_nameW);
2139 cp += lenfam;
2140 font->potm->otmpStyleName = (LPSTR)(cp - (char*)font->potm);
2141 strcpyW((WCHAR*)cp, style_nameW);
2142 cp += lensty;
2143 font->potm->otmpFaceName = (LPSTR)(cp - (char*)font->potm);
2144 strcpyW((WCHAR*)cp, family_nameW);
2145 if(strcasecmp(ft_face->style_name, "regular")) {
2146 strcatW((WCHAR*)cp, spaceW);
2147 strcatW((WCHAR*)cp, style_nameW);
2148 cp += lenfam + lensty;
2149 } else
2150 cp += lenfam;
2151 font->potm->otmpFullName = (LPSTR)(cp - (char*)font->potm);
2152 strcpyW((WCHAR*)cp, family_nameW);
2153 strcatW((WCHAR*)cp, spaceW);
2154 strcatW((WCHAR*)cp, style_nameW);
2155 ret = needed;
2157 if(needed <= cbSize)
2158 memcpy(potm, font->potm, font->potm->otmSize);
2160 end:
2161 HeapFree(GetProcessHeap(), 0, style_nameW);
2162 HeapFree(GetProcessHeap(), 0, family_nameW);
2164 return ret;
2168 /*************************************************************
2169 * WineEngGetCharWidth
2172 BOOL WineEngGetCharWidth(GdiFont font, UINT firstChar, UINT lastChar,
2173 LPINT buffer)
2175 UINT c;
2176 GLYPHMETRICS gm;
2177 FT_UInt glyph_index;
2179 TRACE("%p, %d, %d, %p\n", font, firstChar, lastChar, buffer);
2181 for(c = firstChar; c <= lastChar; c++) {
2182 glyph_index = get_glyph_index(font, c);
2183 WineEngGetGlyphOutline(font, glyph_index, GGO_METRICS | GGO_GLYPH_INDEX,
2184 &gm, 0, NULL, NULL);
2185 buffer[c - firstChar] = font->gm[glyph_index].adv;
2187 return TRUE;
2190 /*************************************************************
2191 * WineEngGetTextExtentPoint
2194 BOOL WineEngGetTextExtentPoint(GdiFont font, LPCWSTR wstr, INT count,
2195 LPSIZE size)
2197 INT idx;
2198 GLYPHMETRICS gm;
2199 TEXTMETRICW tm;
2200 FT_UInt glyph_index;
2202 TRACE("%p, %s, %d, %p\n", font, debugstr_wn(wstr, count), count,
2203 size);
2205 size->cx = 0;
2206 WineEngGetTextMetrics(font, &tm);
2207 size->cy = tm.tmHeight;
2209 for(idx = 0; idx < count; idx++) {
2210 glyph_index = get_glyph_index(font, wstr[idx]);
2211 WineEngGetGlyphOutline(font, glyph_index, GGO_METRICS | GGO_GLYPH_INDEX,
2212 &gm, 0, NULL, NULL);
2213 size->cx += font->gm[glyph_index].adv;
2215 TRACE("return %ld,%ld\n", size->cx, size->cy);
2216 return TRUE;
2219 /*************************************************************
2220 * WineEngGetTextExtentPointI
2223 BOOL WineEngGetTextExtentPointI(GdiFont font, const WORD *indices, INT count,
2224 LPSIZE size)
2226 INT idx;
2227 GLYPHMETRICS gm;
2228 TEXTMETRICW tm;
2230 TRACE("%p, %p, %d, %p\n", font, indices, count, size);
2232 size->cx = 0;
2233 WineEngGetTextMetrics(font, &tm);
2234 size->cy = tm.tmHeight;
2236 for(idx = 0; idx < count; idx++) {
2237 WineEngGetGlyphOutline(font, indices[idx],
2238 GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL,
2239 NULL);
2240 size->cx += font->gm[indices[idx]].adv;
2242 TRACE("return %ld,%ld\n", size->cx, size->cy);
2243 return TRUE;
2246 /*************************************************************
2247 * WineEngGetFontData
2250 DWORD WineEngGetFontData(GdiFont font, DWORD table, DWORD offset, LPVOID buf,
2251 DWORD cbData)
2253 FT_Face ft_face = font->ft_face;
2254 DWORD len;
2255 FT_Error err;
2257 TRACE("font=%p, table=%08lx, offset=%08lx, buf=%p, cbData=%lx\n",
2258 font, table, offset, buf, cbData);
2260 if(!FT_IS_SFNT(ft_face))
2261 return GDI_ERROR;
2263 if(!buf || !cbData)
2264 len = 0;
2265 else
2266 len = cbData;
2268 if(table) { /* MS tags differ in endidness from FT ones */
2269 table = table >> 24 | table << 24 |
2270 (table >> 8 & 0xff00) | (table << 8 & 0xff0000);
2273 /* If the FT_Load_Sfnt_Table function is there we'll use it */
2274 if(pFT_Load_Sfnt_Table)
2275 err = pFT_Load_Sfnt_Table(ft_face, table, offset, buf, &len);
2276 else { /* Do it the hard way */
2277 TT_Face tt_face = (TT_Face) ft_face;
2278 SFNT_Interface *sfnt;
2279 if (FT_Version.major==2 && FT_Version.minor==0)
2281 /* 2.0.x */
2282 sfnt = *(SFNT_Interface**)((char*)tt_face + 528);
2284 else
2286 /* A field was added in the middle of the structure in 2.1.x */
2287 sfnt = *(SFNT_Interface**)((char*)tt_face + 532);
2289 err = sfnt->load_any(tt_face, table, offset, buf, &len);
2291 if(err) {
2292 TRACE("Can't find table %08lx.\n", table);
2293 return GDI_ERROR;
2295 return len;
2298 /*************************************************************
2299 * WineEngGetTextFace
2302 INT WineEngGetTextFace(GdiFont font, INT count, LPWSTR str)
2304 if(str) {
2305 lstrcpynW(str, font->name, count);
2306 return strlenW(font->name);
2307 } else
2308 return strlenW(font->name) + 1;
2311 UINT WineEngGetTextCharsetInfo(GdiFont font, LPFONTSIGNATURE fs, DWORD flags)
2313 if (fs) memcpy(fs, &font->fs, sizeof(FONTSIGNATURE));
2314 return font->charset;
2317 #else /* HAVE_FREETYPE */
2319 BOOL WineEngInit(void)
2321 return FALSE;
2323 GdiFont WineEngCreateFontInstance(DC *dc, HFONT hfont)
2325 return NULL;
2327 BOOL WineEngDestroyFontInstance(HFONT hfont)
2329 return FALSE;
2332 DWORD WineEngEnumFonts(LPLOGFONTW plf, DEVICEFONTENUMPROC proc, LPARAM lparam)
2334 return 1;
2337 DWORD WineEngGetGlyphIndices(GdiFont font, LPCWSTR lpstr, INT count,
2338 LPWORD pgi, DWORD flags)
2340 return GDI_ERROR;
2343 DWORD WineEngGetGlyphOutline(GdiFont font, UINT glyph, UINT format,
2344 LPGLYPHMETRICS lpgm, DWORD buflen, LPVOID buf,
2345 const MAT2* lpmat)
2347 ERR("called but we don't have FreeType\n");
2348 return GDI_ERROR;
2351 BOOL WineEngGetTextMetrics(GdiFont font, LPTEXTMETRICW ptm)
2353 ERR("called but we don't have FreeType\n");
2354 return FALSE;
2357 UINT WineEngGetOutlineTextMetrics(GdiFont font, UINT cbSize,
2358 OUTLINETEXTMETRICW *potm)
2360 ERR("called but we don't have FreeType\n");
2361 return 0;
2364 BOOL WineEngGetCharWidth(GdiFont font, UINT firstChar, UINT lastChar,
2365 LPINT buffer)
2367 ERR("called but we don't have FreeType\n");
2368 return FALSE;
2371 BOOL WineEngGetTextExtentPoint(GdiFont font, LPCWSTR wstr, INT count,
2372 LPSIZE size)
2374 ERR("called but we don't have FreeType\n");
2375 return FALSE;
2378 BOOL WineEngGetTextExtentPointI(GdiFont font, const WORD *indices, INT count,
2379 LPSIZE size)
2381 ERR("called but we don't have FreeType\n");
2382 return FALSE;
2385 DWORD WineEngGetFontData(GdiFont font, DWORD table, DWORD offset, LPVOID buf,
2386 DWORD cbData)
2388 ERR("called but we don't have FreeType\n");
2389 return GDI_ERROR;
2392 INT WineEngGetTextFace(GdiFont font, INT count, LPWSTR str)
2394 ERR("called but we don't have FreeType\n");
2395 return 0;
2398 INT WineEngAddFontResourceEx(LPCWSTR file, DWORD flags, PVOID pdv)
2400 FIXME(":stub\n");
2401 return 1;
2404 INT WineEngRemoveFontResourceEx(LPCWSTR file, DWORD flags, PVOID pdv)
2406 FIXME(":stub\n");
2407 return TRUE;
2410 UINT WineEngGetTextCharsetInfo(GdiFont font, LPFONTSIGNATURE fs, DWORD flags)
2412 FIXME(":stub\n");
2413 return DEFAULT_CHARSET;
2416 #endif /* HAVE_FREETYPE */