Add tests for NORM_IGNORENONSPACE and NORM_IGNORESYMBOLS in the
[wine/wine64.git] / dlls / gdi / freetype.c
blob6e3b52b60d693e9ca57d512b955b14d2ad1345e9
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 "windef.h"
27 #include "winerror.h"
28 #include "winreg.h"
29 #include "wingdi.h"
30 #include "wine/unicode.h"
31 #include "wine/port.h"
32 #include "wine/debug.h"
33 #include "gdi.h"
35 #include <stdlib.h>
36 #include <sys/stat.h>
37 #include <string.h>
38 #include <dirent.h>
39 #include <stdio.h>
40 #include <assert.h>
42 WINE_DEFAULT_DEBUG_CHANNEL(font);
44 #ifdef HAVE_FREETYPE
46 #ifdef HAVE_FREETYPE_FREETYPE_H
47 #include <freetype/freetype.h>
48 #endif
49 #ifdef HAVE_FREETYPE_FTGLYPH_H
50 #include <freetype/ftglyph.h>
51 #endif
52 #ifdef HAVE_FREETYPE_TTTABLES_H
53 #include <freetype/tttables.h>
54 #endif
55 #ifdef HAVE_FREETYPE_FTSNAMES_H
56 #include <freetype/ftsnames.h>
57 #else
58 # ifdef HAVE_FREETYPE_FTNAMES_H
59 # include <freetype/ftnames.h>
60 # endif
61 #endif
62 #ifdef HAVE_FREETYPE_TTNAMEID_H
63 #include <freetype/ttnameid.h>
64 #endif
65 #ifdef HAVE_FREETYPE_FTOUTLN_H
66 #include <freetype/ftoutln.h>
67 #endif
68 #ifdef HAVE_FREETYPE_INTERNAL_SFNT_H
69 #include <freetype/internal/sfnt.h>
70 #endif
71 #ifdef HAVE_FREETYPE_FTTRIGON_H
72 #include <freetype/fttrigon.h>
73 #endif
75 #ifndef SONAME_LIBFREETYPE
76 #define SONAME_LIBFREETYPE "libfreetype.so"
77 #endif
79 static FT_Library library = 0;
80 typedef struct
82 FT_Int major;
83 FT_Int minor;
84 FT_Int patch;
85 } FT_Version_t;
86 static FT_Version_t FT_Version;
88 static void *ft_handle = NULL;
90 #define MAKE_FUNCPTR(f) static typeof(f) * p##f = NULL
91 MAKE_FUNCPTR(FT_Cos);
92 MAKE_FUNCPTR(FT_Done_Face);
93 MAKE_FUNCPTR(FT_Get_Char_Index);
94 MAKE_FUNCPTR(FT_Get_Sfnt_Table);
95 MAKE_FUNCPTR(FT_Init_FreeType);
96 MAKE_FUNCPTR(FT_Load_Glyph);
97 MAKE_FUNCPTR(FT_MulFix);
98 MAKE_FUNCPTR(FT_New_Face);
99 MAKE_FUNCPTR(FT_Outline_Get_Bitmap);
100 MAKE_FUNCPTR(FT_Outline_Transform);
101 MAKE_FUNCPTR(FT_Outline_Translate);
102 MAKE_FUNCPTR(FT_Select_Charmap);
103 MAKE_FUNCPTR(FT_Set_Pixel_Sizes);
104 MAKE_FUNCPTR(FT_Sin);
105 MAKE_FUNCPTR(FT_Vector_Rotate);
106 #undef MAKE_FUNCPTR
107 static void (*pFT_Library_Version)(FT_Library,FT_Int*,FT_Int*,FT_Int*);
108 static FT_Error (*pFT_Load_Sfnt_Table)(FT_Face,FT_ULong,FT_Long,FT_Byte*,FT_ULong*);
110 #define GET_BE_WORD(ptr) MAKEWORD( ((BYTE *)(ptr))[1], ((BYTE *)(ptr))[0] )
112 typedef struct tagFace {
113 WCHAR *StyleName;
114 char *file;
115 FT_Long face_index;
116 BOOL Italic;
117 BOOL Bold;
118 FONTSIGNATURE fs;
119 FT_Fixed font_version;
120 struct tagFace *next;
121 struct tagFamily *family;
122 } Face;
124 typedef struct tagFamily {
125 WCHAR *FamilyName;
126 Face *FirstFace;
127 struct tagFamily *next;
128 } Family;
130 typedef struct {
131 GLYPHMETRICS gm;
132 INT adv; /* These three hold to widths of the unrotated chars */
133 INT lsb;
134 INT bbx;
135 BOOL init;
136 } GM;
138 struct tagGdiFont {
139 FT_Face ft_face;
140 XFORM xform;
141 LPWSTR name;
142 int charset;
143 BOOL fake_italic;
144 BOOL fake_bold;
145 INT orientation;
146 GM *gm;
147 DWORD gmsize;
148 HFONT hfont;
149 SHORT yMax;
150 SHORT yMin;
151 OUTLINETEXTMETRICW *potm;
152 FONTSIGNATURE fs;
153 struct tagGdiFont *next;
156 #define INIT_GM_SIZE 128
158 static GdiFont GdiFontList = NULL;
160 static Family *FontList = NULL;
162 static WCHAR defSerif[] = {'T','i','m','e','s',' ','N','e','w',' ',
163 'R','o','m','a','n','\0'};
164 static WCHAR defSans[] = {'A','r','i','a','l','\0'};
165 static WCHAR defFixed[] = {'C','o','u','r','i','e','r',' ','N','e','w','\0'};
167 static WCHAR defSystem[] = {'A','r','i','a','l','\0'};
168 static WCHAR SystemW[] = {'S','y','s','t','e','m','\0'};
169 static WCHAR MSSansSerifW[] = {'M','S',' ','S','a','n','s',' ',
170 'S','e','r','i','f','\0'};
171 static WCHAR HelvW[] = {'H','e','l','v','\0'};
173 static WCHAR ArabicW[] = {'A','r','a','b','i','c','\0'};
174 static WCHAR BalticW[] = {'B','a','l','t','i','c','\0'};
175 static WCHAR CHINESE_BIG5W[] = {'C','H','I','N','E','S','E','_','B','I','G','5','\0'};
176 static WCHAR CHINESE_GB2312W[] = {'C','H','I','N','E','S','E','_','G','B','2','3','1','2','\0'};
177 static WCHAR Central_EuropeanW[] = {'C','e','n','t','r','a','l',' ',
178 'E','u','r','o','p','e','a','n','\0'};
179 static WCHAR CyrillicW[] = {'C','y','r','i','l','l','i','c','\0'};
180 static WCHAR GreekW[] = {'G','r','e','e','k','\0'};
181 static WCHAR HangulW[] = {'H','a','n','g','u','l','\0'};
182 static WCHAR Hangul_Johab_W[] = {'H','a','n','g','u','l','(','J','o','h','a','b',')','\0'};
183 static WCHAR HebrewW[] = {'H','e','b','r','e','w','\0'};
184 static WCHAR JapaneseW[] = {'J','a','p','a','n','e','s','e','\0'};
185 static WCHAR SymbolW[] = {'S','y','m','b','o','l','\0'};
186 static WCHAR ThaiW[] = {'T','h','a','i','\0'};
187 static WCHAR TurkishW[] = {'T','u','r','k','i','s','h','\0'};
188 static WCHAR VietnameseW[] = {'V','i','e','t','n','a','m','e','s','e','\0'};
189 static WCHAR WesternW[] = {'W','e','s','t','e','r','n','\0'};
191 static WCHAR *ElfScriptsW[32] = { /* these are in the order of the fsCsb[0] bits */
192 WesternW, /*00*/
193 Central_EuropeanW,
194 CyrillicW,
195 GreekW,
196 TurkishW,
197 HebrewW,
198 ArabicW,
199 BalticW,
200 VietnameseW, /*08*/
201 NULL, NULL, NULL, NULL, NULL, NULL, NULL, /*15*/
202 ThaiW,
203 JapaneseW,
204 CHINESE_GB2312W,
205 HangulW,
206 CHINESE_BIG5W,
207 Hangul_Johab_W,
208 NULL, NULL, /*23*/
209 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
210 SymbolW /*31*/
213 typedef struct {
214 WCHAR *name;
215 INT charset;
216 } NameCs;
218 typedef struct tagFontSubst {
219 NameCs from;
220 NameCs to;
221 struct tagFontSubst *next;
222 } FontSubst;
224 static FontSubst *substlist = NULL;
225 static BOOL have_installed_roman_font = FALSE; /* CreateFontInstance will fail if this is still FALSE */
227 static BOOL AddFontFileToList(char *file, char *fake_family)
229 FT_Face ft_face;
230 TT_OS2 *pOS2;
231 TT_Header *pHeader;
232 WCHAR *FamilyW, *StyleW;
233 DWORD len;
234 Family *family = FontList;
235 Family **insert = &FontList;
236 Face **insertface, *next;
237 FT_Error err;
238 FT_Long face_index = 0, num_faces;
239 int i;
241 do {
242 char *family_name = fake_family;
244 TRACE("Loading font file %s index %ld\n", debugstr_a(file), face_index);
245 if((err = pFT_New_Face(library, file, face_index, &ft_face)) != 0) {
246 WARN("Unable to load font file %s err = %x\n", debugstr_a(file), err);
247 return FALSE;
250 if(!FT_IS_SFNT(ft_face)) { /* for now we'll skip everything but TT/OT */
251 pFT_Done_Face(ft_face);
252 return FALSE;
254 if(!pFT_Get_Sfnt_Table(ft_face, ft_sfnt_os2) ||
255 !pFT_Get_Sfnt_Table(ft_face, ft_sfnt_hhea) ||
256 !(pHeader = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_head))) {
257 TRACE("Font file %s lacks either an OS2, HHEA or HEAD table.\n"
258 "Skipping this font.\n", debugstr_a(file));
259 pFT_Done_Face(ft_face);
260 return FALSE;
263 if(!ft_face->family_name || !ft_face->style_name) {
264 TRACE("Font file %s lacks either a family or style name\n", debugstr_a(file));
265 pFT_Done_Face(ft_face);
266 return FALSE;
269 if(!family_name)
270 family_name = ft_face->family_name;
272 len = MultiByteToWideChar(CP_ACP, 0, family_name, -1, NULL, 0);
273 FamilyW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
274 MultiByteToWideChar(CP_ACP, 0, family_name, -1, FamilyW, len);
276 while(family) {
277 if(!strcmpW(family->FamilyName, FamilyW))
278 break;
279 insert = &family->next;
280 family = family->next;
282 if(!family) {
283 family = *insert = HeapAlloc(GetProcessHeap(), 0, sizeof(*family));
284 family->FamilyName = FamilyW;
285 family->FirstFace = NULL;
286 family->next = NULL;
287 } else {
288 HeapFree(GetProcessHeap(), 0, FamilyW);
291 len = MultiByteToWideChar(CP_ACP, 0, ft_face->style_name, -1, NULL, 0);
292 StyleW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
293 MultiByteToWideChar(CP_ACP, 0, ft_face->style_name, -1, StyleW, len);
295 next = NULL;
296 for(insertface = &family->FirstFace; *insertface;
297 insertface = &(*insertface)->next) {
298 if(!strcmpW((*insertface)->StyleName, StyleW)) {
299 TRACE("Already loaded font %s %s original version is %lx, this version is %lx\n",
300 debugstr_w(family->FamilyName), debugstr_w(StyleW),
301 (*insertface)->font_version, pHeader->Font_Revision);
303 if(fake_family) {
304 TRACE("This font is a replacement but the original really exists, so we'll skip the replacement\n");
305 HeapFree(GetProcessHeap(), 0, StyleW);
306 pFT_Done_Face(ft_face);
307 return FALSE;
309 if(pHeader->Font_Revision <= (*insertface)->font_version) {
310 TRACE("Original font is newer so skipping this one\n");
311 HeapFree(GetProcessHeap(), 0, StyleW);
312 pFT_Done_Face(ft_face);
313 return FALSE;
314 } else {
315 TRACE("Replacing original with this one\n");
316 next = (*insertface)->next;
317 HeapFree(GetProcessHeap(), 0, (*insertface)->file);
318 HeapFree(GetProcessHeap(), 0, (*insertface)->StyleName);
319 HeapFree(GetProcessHeap(), 0, *insertface);
320 break;
324 *insertface = HeapAlloc(GetProcessHeap(), 0, sizeof(**insertface));
325 (*insertface)->StyleName = StyleW;
326 (*insertface)->file = HeapAlloc(GetProcessHeap(),0,strlen(file)+1);
327 strcpy((*insertface)->file, file);
328 (*insertface)->face_index = face_index;
329 (*insertface)->next = next;
330 (*insertface)->Italic = (ft_face->style_flags & FT_STYLE_FLAG_ITALIC) ? 1 : 0;
331 (*insertface)->Bold = (ft_face->style_flags & FT_STYLE_FLAG_BOLD) ? 1 : 0;
332 (*insertface)->font_version = pHeader->Font_Revision;
333 (*insertface)->family = family;
335 pOS2 = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_os2);
336 if(pOS2) {
337 (*insertface)->fs.fsCsb[0] = pOS2->ulCodePageRange1;
338 (*insertface)->fs.fsCsb[1] = pOS2->ulCodePageRange2;
339 (*insertface)->fs.fsUsb[0] = pOS2->ulUnicodeRange1;
340 (*insertface)->fs.fsUsb[1] = pOS2->ulUnicodeRange2;
341 (*insertface)->fs.fsUsb[2] = pOS2->ulUnicodeRange3;
342 (*insertface)->fs.fsUsb[3] = pOS2->ulUnicodeRange4;
343 } else {
344 (*insertface)->fs.fsCsb[0] = (*insertface)->fs.fsCsb[1] = 0;
345 (*insertface)->fs.fsUsb[0] = 0;
346 (*insertface)->fs.fsUsb[1] = 0;
347 (*insertface)->fs.fsUsb[2] = 0;
348 (*insertface)->fs.fsUsb[3] = 0;
350 TRACE("fsCsb = %08lx %08lx/%08lx %08lx %08lx %08lx\n",
351 (*insertface)->fs.fsCsb[0], (*insertface)->fs.fsCsb[1],
352 (*insertface)->fs.fsUsb[0], (*insertface)->fs.fsUsb[1],
353 (*insertface)->fs.fsUsb[2], (*insertface)->fs.fsUsb[3]);
355 if((*insertface)->fs.fsCsb[0] == 0) { /* let's see if we can find any interesting cmaps */
356 for(i = 0; i < ft_face->num_charmaps &&
357 !(*insertface)->fs.fsCsb[0]; i++) {
358 switch(ft_face->charmaps[i]->encoding) {
359 case ft_encoding_unicode:
360 (*insertface)->fs.fsCsb[0] = 1;
361 break;
362 case ft_encoding_symbol:
363 (*insertface)->fs.fsCsb[0] = 1L << 31;
364 break;
365 default:
366 break;
371 if((*insertface)->fs.fsCsb[0] & ~(1L << 31))
372 have_installed_roman_font = TRUE;
374 num_faces = ft_face->num_faces;
375 pFT_Done_Face(ft_face);
376 TRACE("Added font %s %s\n", debugstr_w(family->FamilyName),
377 debugstr_w(StyleW));
378 } while(num_faces > ++face_index);
379 return TRUE;
382 static void DumpFontList(void)
384 Family *family;
385 Face *face;
387 for(family = FontList; family; family = family->next) {
388 TRACE("Family: %s\n", debugstr_w(family->FamilyName));
389 for(face = family->FirstFace; face; face = face->next) {
390 TRACE("\t%s\n", debugstr_w(face->StyleName));
393 return;
396 static void DumpSubstList(void)
398 FontSubst *psub;
400 for(psub = substlist; psub; psub = psub->next)
401 if(psub->from.charset != -1 || psub->to.charset != -1)
402 TRACE("%s:%d -> %s:%d\n", debugstr_w(psub->from.name),
403 psub->from.charset, debugstr_w(psub->to.name), psub->to.charset);
404 else
405 TRACE("%s -> %s\n", debugstr_w(psub->from.name),
406 debugstr_w(psub->to.name));
407 return;
410 static LPWSTR strdupW(LPWSTR p)
412 LPWSTR ret;
413 DWORD len = (strlenW(p) + 1) * sizeof(WCHAR);
414 ret = HeapAlloc(GetProcessHeap(), 0, len);
415 memcpy(ret, p, len);
416 return ret;
419 static void split_subst_info(NameCs *nc, LPSTR str)
421 CHAR *p = strrchr(str, ',');
422 DWORD len;
424 nc->charset = -1;
425 if(p && *(p+1)) {
426 nc->charset = strtol(p+1, NULL, 10);
427 *p = '\0';
429 len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
430 nc->name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
431 MultiByteToWideChar(CP_ACP, 0, str, -1, nc->name, len);
434 static void LoadSubstList(void)
436 FontSubst *psub, **ppsub;
437 HKEY hkey;
438 DWORD valuelen, datalen, i = 0, type, dlen, vlen;
439 LPSTR value;
440 LPVOID data;
442 if(substlist) {
443 for(psub = substlist; psub;) {
444 FontSubst *ptmp;
445 HeapFree(GetProcessHeap(), 0, psub->to.name);
446 HeapFree(GetProcessHeap(), 0, psub->from.name);
447 ptmp = psub;
448 psub = psub->next;
449 HeapFree(GetProcessHeap(), 0, ptmp);
451 substlist = NULL;
454 if(RegOpenKeyA(HKEY_LOCAL_MACHINE,
455 "Software\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes",
456 &hkey) == ERROR_SUCCESS) {
458 RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
459 &valuelen, &datalen, NULL, NULL);
461 valuelen++; /* returned value doesn't include room for '\0' */
462 value = HeapAlloc(GetProcessHeap(), 0, valuelen * sizeof(CHAR));
463 data = HeapAlloc(GetProcessHeap(), 0, datalen);
465 dlen = datalen;
466 vlen = valuelen;
467 ppsub = &substlist;
468 while(RegEnumValueA(hkey, i++, value, &vlen, NULL, &type, data,
469 &dlen) == ERROR_SUCCESS) {
470 TRACE("Got %s=%s\n", debugstr_a(value), debugstr_a(data));
472 *ppsub = HeapAlloc(GetProcessHeap(), 0, sizeof(**ppsub));
473 (*ppsub)->next = NULL;
474 split_subst_info(&((*ppsub)->from), value);
475 split_subst_info(&((*ppsub)->to), data);
477 /* Win 2000 doesn't allow mapping between different charsets
478 or mapping of DEFAULT_CHARSET */
479 if(((*ppsub)->to.charset != (*ppsub)->from.charset) ||
480 (*ppsub)->to.charset == DEFAULT_CHARSET) {
481 HeapFree(GetProcessHeap(), 0, (*ppsub)->to.name);
482 HeapFree(GetProcessHeap(), 0, (*ppsub)->from.name);
483 HeapFree(GetProcessHeap(), 0, *ppsub);
484 *ppsub = NULL;
485 } else {
486 ppsub = &((*ppsub)->next);
488 /* reset dlen and vlen */
489 dlen = datalen;
490 vlen = valuelen;
492 HeapFree(GetProcessHeap(), 0, data);
493 HeapFree(GetProcessHeap(), 0, value);
494 RegCloseKey(hkey);
498 /***********************************************************
499 * The replacement list is a way to map an entire font
500 * family onto another family. For example adding
502 * [HKLM\Software\Wine\Wine\FontReplacements]
503 * "Wingdings"="Winedings"
505 * would enumerate the Winedings font both as Winedings and
506 * Wingdings. However if a real Wingdings font is present the
507 * replacement does not take place.
510 static void LoadReplaceList(void)
512 HKEY hkey;
513 DWORD valuelen, datalen, i = 0, type, dlen, vlen;
514 LPSTR value;
515 LPVOID data;
516 Family *family;
517 Face *face;
518 WCHAR old_nameW[200];
520 if(RegOpenKeyA(HKEY_LOCAL_MACHINE,
521 "Software\\Wine\\Wine\\FontReplacements",
522 &hkey) == ERROR_SUCCESS) {
524 RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
525 &valuelen, &datalen, NULL, NULL);
527 valuelen++; /* returned value doesn't include room for '\0' */
528 value = HeapAlloc(GetProcessHeap(), 0, valuelen * sizeof(CHAR));
529 data = HeapAlloc(GetProcessHeap(), 0, datalen);
531 dlen = datalen;
532 vlen = valuelen;
533 while(RegEnumValueA(hkey, i++, value, &vlen, NULL, &type, data,
534 &dlen) == ERROR_SUCCESS) {
535 TRACE("Got %s=%s\n", debugstr_a(value), debugstr_a(data));
536 /* "NewName"="Oldname" */
537 if(!MultiByteToWideChar(CP_ACP, 0, data, -1, old_nameW, sizeof(old_nameW)))
538 break;
540 /* Find the old family and hence all of the font files
541 in that family */
542 for(family = FontList; family; family = family->next) {
543 if(!strcmpiW(family->FamilyName, old_nameW)) {
544 for(face = family->FirstFace; face; face = face->next) {
545 TRACE("mapping %s %s to %s\n", debugstr_w(family->FamilyName),
546 debugstr_w(face->StyleName), value);
547 /* Now add a new entry with the new family name */
548 AddFontFileToList(face->file, value);
550 break;
553 /* reset dlen and vlen */
554 dlen = datalen;
555 vlen = valuelen;
557 HeapFree(GetProcessHeap(), 0, data);
558 HeapFree(GetProcessHeap(), 0, value);
559 RegCloseKey(hkey);
564 static BOOL ReadFontDir(char *dirname)
566 DIR *dir;
567 struct dirent *dent;
568 char path[MAX_PATH];
570 TRACE("Loading fonts from %s\n", debugstr_a(dirname));
572 dir = opendir(dirname);
573 if(!dir) {
574 ERR("Can't open directory %s\n", debugstr_a(dirname));
575 return FALSE;
577 while((dent = readdir(dir)) != NULL) {
578 struct stat statbuf;
580 if(!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
581 continue;
583 TRACE("Found %s in %s\n", debugstr_a(dent->d_name), debugstr_a(dirname));
585 sprintf(path, "%s/%s", dirname, dent->d_name);
587 if(stat(path, &statbuf) == -1)
589 WARN("Can't stat %s\n", debugstr_a(path));
590 continue;
592 if(S_ISDIR(statbuf.st_mode))
593 ReadFontDir(path);
594 else
595 AddFontFileToList(path, NULL);
597 closedir(dir);
598 return TRUE;
601 /*************************************************************
602 * WineEngAddFontResourceEx
605 INT WineEngAddFontResourceEx(LPCWSTR file, DWORD flags, PVOID pdv)
607 if (ft_handle) /* do it only if we have freetype up and running */
609 DWORD len = WideCharToMultiByte(CP_ACP, 0, file, -1, NULL, 0, NULL, NULL);
610 LPSTR fileA = HeapAlloc(GetProcessHeap(), 0, len);
611 char unixname[MAX_PATH];
612 WideCharToMultiByte(CP_ACP, 0, file, -1, fileA, len, NULL, NULL);
614 if(flags)
615 FIXME("Ignoring flags %lx\n", flags);
617 if(wine_get_unix_file_name(fileA, unixname, sizeof(unixname)))
618 AddFontFileToList(unixname, NULL);
619 HeapFree(GetProcessHeap(), 0, fileA);
621 return 1;
624 /*************************************************************
625 * WineEngRemoveFontResourceEx
628 BOOL WineEngRemoveFontResourceEx(LPCWSTR file, DWORD flags, PVOID pdv)
630 FIXME(":stub\n");
631 return TRUE;
634 /*************************************************************
635 * WineEngInit
637 * Initialize FreeType library and create a list of available faces
639 BOOL WineEngInit(void)
641 HKEY hkey;
642 DWORD valuelen, datalen, i = 0, type, dlen, vlen;
643 LPSTR value;
644 LPVOID data;
645 char windowsdir[MAX_PATH];
646 char unixname[MAX_PATH];
648 TRACE("\n");
650 ft_handle = wine_dlopen(SONAME_LIBFREETYPE, RTLD_NOW, NULL, 0);
651 if(!ft_handle) {
652 WINE_MESSAGE(
653 "Wine cannot find the FreeType font library. To enable Wine to\n"
654 "use TrueType fonts please install a version of FreeType greater than\n"
655 "or equal to 2.0.5.\n"
656 "http://www.freetype.org\n");
657 return FALSE;
660 #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;}
662 LOAD_FUNCPTR(FT_Cos)
663 LOAD_FUNCPTR(FT_Done_Face)
664 LOAD_FUNCPTR(FT_Get_Char_Index)
665 LOAD_FUNCPTR(FT_Get_Sfnt_Table)
666 LOAD_FUNCPTR(FT_Init_FreeType)
667 LOAD_FUNCPTR(FT_Load_Glyph)
668 LOAD_FUNCPTR(FT_MulFix)
669 LOAD_FUNCPTR(FT_New_Face)
670 LOAD_FUNCPTR(FT_Outline_Get_Bitmap)
671 LOAD_FUNCPTR(FT_Outline_Transform)
672 LOAD_FUNCPTR(FT_Outline_Translate)
673 LOAD_FUNCPTR(FT_Select_Charmap)
674 LOAD_FUNCPTR(FT_Set_Pixel_Sizes)
675 LOAD_FUNCPTR(FT_Sin)
676 LOAD_FUNCPTR(FT_Vector_Rotate)
678 #undef LOAD_FUNCPTR
679 /* Don't warn if this one is missing */
680 pFT_Library_Version = wine_dlsym(ft_handle, "FT_Library_Version", NULL, 0);
681 pFT_Load_Sfnt_Table = wine_dlsym(ft_handle, "FT_Load_Sfnt_Table", NULL, 0);
683 if(!wine_dlsym(ft_handle, "FT_Get_Postscript_Name", NULL, 0) &&
684 !wine_dlsym(ft_handle, "FT_Sqrt64", NULL, 0)) {
685 /* try to avoid 2.0.4: >= 2.0.5 has FT_Get_Postscript_Name and
686 <= 2.0.3 has FT_Sqrt64 */
687 goto sym_not_found;
690 if(pFT_Init_FreeType(&library) != 0) {
691 ERR("Can't init FreeType library\n");
692 wine_dlclose(ft_handle, NULL, 0);
693 ft_handle = NULL;
694 return FALSE;
696 FT_Version.major=FT_Version.minor=FT_Version.patch=-1;
697 if (pFT_Library_Version)
699 pFT_Library_Version(library,&FT_Version.major,&FT_Version.minor,&FT_Version.patch);
701 if (FT_Version.major<=0)
703 FT_Version.major=2;
704 FT_Version.minor=0;
705 FT_Version.patch=5;
707 TRACE("FreeType version is %d.%d.%d\n",FT_Version.major,FT_Version.minor,FT_Version.patch);
709 /* load in the fonts from %WINDOWSDIR%\\Fonts first of all */
710 GetWindowsDirectoryA(windowsdir, sizeof(windowsdir));
711 strcat(windowsdir, "\\Fonts");
712 if(wine_get_unix_file_name(windowsdir, unixname, sizeof(unixname)))
713 ReadFontDir(unixname);
715 /* now look under HKLM\Software\Microsoft\Windows\CurrentVersion\Fonts
716 for any fonts not installed in %WINDOWSDIR%\Fonts. They will have their
717 full path as the entry */
718 if(RegOpenKeyA(HKEY_LOCAL_MACHINE,
719 "Software\\Microsoft\\Windows\\CurrentVersion\\Fonts",
720 &hkey) == ERROR_SUCCESS) {
721 RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
722 &valuelen, &datalen, NULL, NULL);
724 valuelen++; /* returned value doesn't include room for '\0' */
725 value = HeapAlloc(GetProcessHeap(), 0, valuelen);
726 data = HeapAlloc(GetProcessHeap(), 0, datalen);
728 dlen = datalen;
729 vlen = valuelen;
730 while(RegEnumValueA(hkey, i++, value, &vlen, NULL, &type, data,
731 &dlen) == ERROR_SUCCESS) {
732 if(((LPSTR)data)[0] && ((LPSTR)data)[1] == ':')
733 if(wine_get_unix_file_name((LPSTR)data, unixname, sizeof(unixname)))
734 AddFontFileToList(unixname, NULL);
736 /* reset dlen and vlen */
737 dlen = datalen;
738 vlen = valuelen;
740 HeapFree(GetProcessHeap(), 0, data);
741 HeapFree(GetProcessHeap(), 0, value);
742 RegCloseKey(hkey);
746 /* then look in any directories that we've specified in the config file */
747 if(RegOpenKeyA(HKEY_LOCAL_MACHINE,
748 "Software\\Wine\\Wine\\Config\\FontDirs",
749 &hkey) == ERROR_SUCCESS) {
751 RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
752 &valuelen, &datalen, NULL, NULL);
754 valuelen++; /* returned value doesn't include room for '\0' */
755 value = HeapAlloc(GetProcessHeap(), 0, valuelen);
756 data = HeapAlloc(GetProcessHeap(), 0, datalen);
758 dlen = datalen;
759 vlen = valuelen;
760 i = 0;
761 while(RegEnumValueA(hkey, i++, value, &vlen, NULL, &type, data,
762 &dlen) == ERROR_SUCCESS) {
763 TRACE("Got %s=%s\n", value, (LPSTR)data);
764 ReadFontDir((LPSTR)data);
765 /* reset dlen and vlen */
766 dlen = datalen;
767 vlen = valuelen;
769 HeapFree(GetProcessHeap(), 0, data);
770 HeapFree(GetProcessHeap(), 0, value);
771 RegCloseKey(hkey);
774 DumpFontList();
775 LoadSubstList();
776 DumpSubstList();
777 LoadReplaceList();
778 return TRUE;
779 sym_not_found:
780 WINE_MESSAGE(
781 "Wine cannot find certain functions that it needs inside the FreeType\n"
782 "font library. To enable Wine to use TrueType fonts please upgrade\n"
783 "FreeType to at least version 2.0.5.\n"
784 "http://www.freetype.org\n");
785 wine_dlclose(ft_handle, NULL, 0);
786 ft_handle = NULL;
787 return FALSE;
791 static LONG calc_ppem_for_height(FT_Face ft_face, LONG height)
793 TT_OS2 *pOS2;
794 LONG ppem;
796 pOS2 = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_os2);
798 if(height == 0) height = 16;
800 /* Calc. height of EM square:
802 * For +ve lfHeight we have
803 * lfHeight = (winAscent + winDescent) * ppem / units_per_em
804 * Re-arranging gives:
805 * ppem = units_per_em * lfheight / (winAscent + winDescent)
807 * For -ve lfHeight we have
808 * |lfHeight| = ppem
809 * [i.e. |lfHeight| = (winAscent + winDescent - il) * ppem / units_per_em
810 * with il = winAscent + winDescent - units_per_em]
814 if(height > 0)
815 ppem = ft_face->units_per_EM * height /
816 (pOS2->usWinAscent + pOS2->usWinDescent);
817 else
818 ppem = -height;
820 return ppem;
823 static LONG load_VDMX(GdiFont, LONG);
825 static FT_Face OpenFontFile(GdiFont font, char *file, FT_Long face_index, LONG height)
827 FT_Error err;
828 FT_Face ft_face;
829 LONG ppem;
831 err = pFT_New_Face(library, file, face_index, &ft_face);
832 if(err) {
833 ERR("FT_New_Face rets %d\n", err);
834 return 0;
837 /* set it here, as load_VDMX needs it */
838 font->ft_face = ft_face;
840 /* load the VDMX table if we have one */
841 ppem = load_VDMX(font, height);
842 if(ppem == 0)
843 ppem = calc_ppem_for_height(ft_face, height);
845 pFT_Set_Pixel_Sizes(ft_face, 0, ppem);
847 return ft_face;
851 static int get_nearest_charset(Face *face)
853 /* Only get here if lfCharSet == DEFAULT_CHARSET or we couldn't find
854 a single face with the requested charset. The idea is to check if
855 the selected font supports the current ANSI codepage, if it does
856 return the corresponding charset, else return the first charset */
858 CHARSETINFO csi;
859 int acp = GetACP(), i;
860 DWORD fs0;
862 if(TranslateCharsetInfo((DWORD*)acp, &csi, TCI_SRCCODEPAGE))
863 if(csi.fs.fsCsb[0] & face->fs.fsCsb[0])
864 return csi.ciCharset;
866 for(i = 0; i < 32; i++) {
867 fs0 = 1L << i;
868 if(face->fs.fsCsb[0] & fs0) {
869 if(TranslateCharsetInfo(&fs0, &csi, TCI_SRCFONTSIG))
870 return csi.ciCharset;
871 else
872 FIXME("TCI failing on %lx\n", fs0);
876 FIXME("returning DEFAULT_CHARSET face->fs.fsCsb[0] = %08lx file = %s\n",
877 face->fs.fsCsb[0], face->file);
878 return DEFAULT_CHARSET;
881 static GdiFont alloc_font(void)
883 GdiFont ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*ret));
884 ret->gmsize = INIT_GM_SIZE;
885 ret->gm = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
886 ret->gmsize * sizeof(*ret->gm));
887 ret->next = NULL;
888 ret->potm = NULL;
889 ret->xform.eM11 = ret->xform.eM22 = 1.0;
890 return ret;
893 static void free_font(GdiFont font)
895 if (font->ft_face) pFT_Done_Face(font->ft_face);
896 if (font->potm) HeapFree(GetProcessHeap(), 0, font->potm);
897 if (font->name) HeapFree(GetProcessHeap(), 0, font->name);
898 HeapFree(GetProcessHeap(), 0, font->gm);
899 HeapFree(GetProcessHeap(), 0, font);
903 /*************************************************************
904 * load_VDMX
906 * load the vdmx entry for the specified height
909 #define MS_MAKE_TAG( _x1, _x2, _x3, _x4 ) \
910 ( ( (FT_ULong)_x4 << 24 ) | \
911 ( (FT_ULong)_x3 << 16 ) | \
912 ( (FT_ULong)_x2 << 8 ) | \
913 (FT_ULong)_x1 )
915 #define MS_VDMX_TAG MS_MAKE_TAG('V', 'D', 'M', 'X')
917 typedef struct {
918 BYTE bCharSet;
919 BYTE xRatio;
920 BYTE yStartRatio;
921 BYTE yEndRatio;
922 } Ratios;
925 static LONG load_VDMX(GdiFont font, LONG height)
927 BYTE hdr[6], tmp[2], group[4];
928 BYTE devXRatio, devYRatio;
929 USHORT numRecs, numRatios;
930 DWORD offset = -1;
931 LONG ppem = 0;
932 int i, result;
934 result = WineEngGetFontData(font, MS_VDMX_TAG, 0, hdr, 6);
936 if(result == GDI_ERROR) /* no vdmx table present, use linear scaling */
937 return ppem;
939 /* FIXME: need the real device aspect ratio */
940 devXRatio = 1;
941 devYRatio = 1;
943 numRecs = GET_BE_WORD(&hdr[2]);
944 numRatios = GET_BE_WORD(&hdr[4]);
946 TRACE("numRecs = %d numRatios = %d\n", numRecs, numRatios);
947 for(i = 0; i < numRatios; i++) {
948 Ratios ratio;
950 offset = (3 * 2) + (i * sizeof(Ratios));
951 WineEngGetFontData(font, MS_VDMX_TAG, offset, &ratio, sizeof(Ratios));
952 offset = -1;
954 TRACE("Ratios[%d] %d %d : %d -> %d\n", i, ratio.bCharSet, ratio.xRatio, ratio.yStartRatio, ratio.yEndRatio);
956 if(ratio.bCharSet != 1)
957 continue;
959 if((ratio.xRatio == 0 &&
960 ratio.yStartRatio == 0 &&
961 ratio.yEndRatio == 0) ||
962 (devXRatio == ratio.xRatio &&
963 devYRatio >= ratio.yStartRatio &&
964 devYRatio <= ratio.yEndRatio))
966 offset = (3 * 2) + (numRatios * 4) + (i * 2);
967 WineEngGetFontData(font, MS_VDMX_TAG, offset, tmp, 2);
968 offset = GET_BE_WORD(tmp);
969 break;
973 if(offset < 0) {
974 FIXME("No suitable ratio found\n");
975 return ppem;
978 if(WineEngGetFontData(font, MS_VDMX_TAG, offset, group, 4) != GDI_ERROR) {
979 USHORT recs;
980 BYTE startsz, endsz;
981 BYTE *vTable;
983 recs = GET_BE_WORD(group);
984 startsz = group[2];
985 endsz = group[3];
987 TRACE("recs=%d startsz=%d endsz=%d\n", recs, startsz, endsz);
989 vTable = HeapAlloc(GetProcessHeap(), 0, recs * 6);
990 result = WineEngGetFontData(font, MS_VDMX_TAG, offset + 4, vTable, recs * 6);
991 if(result == GDI_ERROR) {
992 FIXME("Failed to retrieve vTable\n");
993 goto end;
996 if(height > 0) {
997 for(i = 0; i < recs; i++) {
998 SHORT yMax = GET_BE_WORD(&vTable[(i * 6) + 2]);
999 SHORT yMin = GET_BE_WORD(&vTable[(i * 6) + 4]);
1000 ppem = GET_BE_WORD(&vTable[i * 6]);
1002 if(yMax + -yMin == height) {
1003 font->yMax = yMax;
1004 font->yMin = yMin;
1005 TRACE("ppem %ld found; height=%ld yMax=%d yMin=%d\n", ppem, height, font->yMax, font->yMin);
1006 break;
1008 if(yMax + -yMin > height) {
1009 if(--i < 0) {
1010 ppem = 0;
1011 goto end; /* failed */
1013 font->yMax = GET_BE_WORD(&vTable[(i * 6) + 2]);
1014 font->yMin = GET_BE_WORD(&vTable[(i * 6) + 4]);
1015 TRACE("ppem %ld found; height=%ld yMax=%d yMin=%d\n", ppem, height, font->yMax, font->yMin);
1016 break;
1019 if(!font->yMax) {
1020 ppem = 0;
1021 TRACE("ppem not found for height %ld\n", height);
1023 } else {
1024 ppem = -height;
1025 if(ppem < startsz || ppem > endsz)
1026 goto end;
1028 for(i = 0; i < recs; i++) {
1029 USHORT yPelHeight;
1030 yPelHeight = GET_BE_WORD(&vTable[i * 6]);
1032 if(yPelHeight > ppem)
1033 break; /* failed */
1035 if(yPelHeight == ppem) {
1036 font->yMax = GET_BE_WORD(&vTable[(i * 6) + 2]);
1037 font->yMin = GET_BE_WORD(&vTable[(i * 6) + 4]);
1038 TRACE("ppem %ld found; yMax=%d yMin=%d\n", ppem, font->yMax, font->yMin);
1039 break;
1043 end:
1044 HeapFree(GetProcessHeap(), 0, vTable);
1047 return ppem;
1051 /*************************************************************
1052 * WineEngCreateFontInstance
1055 GdiFont WineEngCreateFontInstance(DC *dc, HFONT hfont)
1057 GdiFont ret;
1058 Face *face;
1059 Family *family = NULL;
1060 BOOL bd, it;
1061 LOGFONTW lf;
1062 CHARSETINFO csi;
1064 if (!GetObjectW( hfont, sizeof(lf), &lf )) return NULL;
1066 TRACE("%s, h=%ld, it=%d, weight=%ld, PandF=%02x, charset=%d orient %ld escapement %ld\n",
1067 debugstr_w(lf.lfFaceName), lf.lfHeight, lf.lfItalic,
1068 lf.lfWeight, lf.lfPitchAndFamily, lf.lfCharSet, lf.lfOrientation,
1069 lf.lfEscapement);
1071 /* check the cache first */
1072 for(ret = GdiFontList; ret; ret = ret->next) {
1073 if(ret->hfont == hfont && !memcmp(&ret->xform, &dc->xformWorld2Vport, offsetof(XFORM, eDx))) {
1074 TRACE("returning cached gdiFont(%p) for hFont %p\n", ret, hfont);
1075 return ret;
1079 if(!FontList || !have_installed_roman_font) /* No fonts installed */
1081 TRACE("No fonts installed\n");
1082 return NULL;
1085 ret = alloc_font();
1086 memcpy(&ret->xform, &dc->xformWorld2Vport, sizeof(XFORM));
1088 /* If lfFaceName is "Symbol" then Windows fixes up lfCharSet to
1089 SYMBOL_CHARSET so that Symbol gets picked irrespective of the
1090 original value lfCharSet. Note this is a special case for
1091 Symbol and doesn't happen at least for "Wingdings*" */
1093 if(!strcmpiW(lf.lfFaceName, SymbolW))
1094 lf.lfCharSet = SYMBOL_CHARSET;
1096 if(!TranslateCharsetInfo((DWORD*)(INT)lf.lfCharSet, &csi, TCI_SRCCHARSET)) {
1097 switch(lf.lfCharSet) {
1098 case DEFAULT_CHARSET:
1099 csi.fs.fsCsb[0] = 0;
1100 break;
1101 default:
1102 FIXME("Untranslated charset %d\n", lf.lfCharSet);
1103 csi.fs.fsCsb[0] = 0;
1104 break;
1108 if(lf.lfFaceName[0] != '\0') {
1109 FontSubst *psub;
1110 for(psub = substlist; psub; psub = psub->next)
1111 if(!strcmpiW(lf.lfFaceName, psub->from.name) &&
1112 (psub->from.charset == -1 ||
1113 psub->from.charset == lf.lfCharSet))
1114 break;
1115 if(psub) {
1116 TRACE("substituting %s -> %s\n", debugstr_w(lf.lfFaceName),
1117 debugstr_w(psub->to.name));
1118 strcpyW(lf.lfFaceName, psub->to.name);
1121 /* We want a match on name and charset or just name if
1122 charset was DEFAULT_CHARSET. If the latter then
1123 we fixup the returned charset later in get_nearest_charset
1124 where we'll either use the charset of the current ansi codepage
1125 or if that's unavailable the first charset that the font supports.
1127 for(family = FontList; family; family = family->next) {
1128 if(!strcmpiW(family->FamilyName, lf.lfFaceName))
1129 if((csi.fs.fsCsb[0] & family->FirstFace->fs.fsCsb[0]) || !csi.fs.fsCsb[0])
1130 break;
1133 if(!family) { /* do other aliases here */
1134 if(!strcmpiW(lf.lfFaceName, SystemW))
1135 strcpyW(lf.lfFaceName, defSystem);
1136 else if(!strcmpiW(lf.lfFaceName, MSSansSerifW))
1137 strcpyW(lf.lfFaceName, defSans);
1138 else if(!strcmpiW(lf.lfFaceName, HelvW))
1139 strcpyW(lf.lfFaceName, defSans);
1140 else
1141 goto not_found;
1143 for(family = FontList; family; family = family->next) {
1144 if(!strcmpiW(family->FamilyName, lf.lfFaceName))
1145 if((csi.fs.fsCsb[0] & family->FirstFace->fs.fsCsb[0]) || !csi.fs.fsCsb[0])
1146 break;
1151 not_found:
1152 if(!family) {
1153 /* If requested charset was DEFAULT_CHARSET then try using charset
1154 corresponding to the current ansi codepage */
1155 if(!csi.fs.fsCsb[0]) {
1156 INT acp = GetACP();
1157 if(!TranslateCharsetInfo((DWORD*)acp, &csi, TCI_SRCCODEPAGE)) {
1158 FIXME("TCI failed on codepage %d\n", acp);
1159 csi.fs.fsCsb[0] = 0;
1160 } else
1161 lf.lfCharSet = csi.ciCharset;
1163 if(lf.lfPitchAndFamily & FIXED_PITCH ||
1164 lf.lfPitchAndFamily & FF_MODERN)
1165 strcpyW(lf.lfFaceName, defFixed);
1166 else if(lf.lfPitchAndFamily & FF_ROMAN)
1167 strcpyW(lf.lfFaceName, defSerif);
1168 else if(lf.lfPitchAndFamily & FF_SWISS)
1169 strcpyW(lf.lfFaceName, defSans);
1170 else
1171 strcpyW(lf.lfFaceName, defSans);
1172 for(family = FontList; family; family = family->next) {
1173 if(!strcmpiW(family->FamilyName, lf.lfFaceName) &&
1174 (csi.fs.fsCsb[0] & family->FirstFace->fs.fsCsb[0]))
1175 break;
1179 if(!family) {
1180 for(family = FontList; family; family = family->next) {
1181 if(csi.fs.fsCsb[0] & family->FirstFace->fs.fsCsb[0])
1182 break;
1186 if(!family) {
1187 family = FontList;
1188 csi.fs.fsCsb[0] = 0;
1189 FIXME("just using first face for now\n");
1192 it = lf.lfItalic ? 1 : 0;
1193 bd = lf.lfWeight > 550 ? 1 : 0;
1195 for(face = family->FirstFace; face; face = face->next) {
1196 if(!(face->Italic ^ it) && !(face->Bold ^ bd))
1197 break;
1199 if(!face) {
1200 face = family->FirstFace;
1201 if(it && !face->Italic) ret->fake_italic = TRUE;
1202 if(bd && !face->Bold) ret->fake_bold = TRUE;
1205 memcpy(&ret->fs, &face->fs, sizeof(FONTSIGNATURE));
1207 if(csi.fs.fsCsb[0])
1208 ret->charset = lf.lfCharSet;
1209 else
1210 ret->charset = get_nearest_charset(face);
1212 TRACE("Chosen: %s %s\n", debugstr_w(family->FamilyName),
1213 debugstr_w(face->StyleName));
1215 ret->ft_face = OpenFontFile(ret, face->file, face->face_index,
1216 lf.lfHeight < 0 ?
1217 -abs(INTERNAL_YWSTODS(dc,lf.lfHeight)) :
1218 abs(INTERNAL_YWSTODS(dc, lf.lfHeight)));
1219 if (!ret->ft_face)
1221 free_font( ret );
1222 return 0;
1225 if(ret->charset == SYMBOL_CHARSET)
1226 pFT_Select_Charmap(ret->ft_face, ft_encoding_symbol);
1227 ret->orientation = lf.lfOrientation;
1228 ret->name = strdupW(family->FamilyName);
1230 TRACE("caching: gdiFont=%p hfont=%p\n", ret, hfont);
1231 ret->hfont = hfont;
1232 ret->next = GdiFontList;
1233 GdiFontList = ret;
1235 return ret;
1238 static void DumpGdiFontList(void)
1240 GdiFont gdiFont;
1242 TRACE("---------- gdiFont Cache ----------\n");
1243 for(gdiFont = GdiFontList; gdiFont; gdiFont = gdiFont->next) {
1244 LOGFONTW lf;
1245 GetObjectW( gdiFont->hfont, sizeof(lf), &lf );
1246 TRACE("gdiFont=%p hfont=%p (%s)\n",
1247 gdiFont, gdiFont->hfont, debugstr_w(lf.lfFaceName));
1251 /*************************************************************
1252 * WineEngDestroyFontInstance
1254 * free the gdiFont associated with this handle
1257 BOOL WineEngDestroyFontInstance(HFONT handle)
1259 GdiFont gdiFont;
1260 GdiFont gdiPrev = NULL;
1261 BOOL ret = FALSE;
1263 TRACE("destroying hfont=%p\n", handle);
1264 if(TRACE_ON(font))
1265 DumpGdiFontList();
1267 gdiFont = GdiFontList;
1268 while(gdiFont) {
1269 if(gdiFont->hfont == handle) {
1270 if(gdiPrev) {
1271 gdiPrev->next = gdiFont->next;
1272 free_font(gdiFont);
1273 gdiFont = gdiPrev->next;
1274 } else {
1275 GdiFontList = gdiFont->next;
1276 free_font(gdiFont);
1277 gdiFont = GdiFontList;
1279 ret = TRUE;
1280 } else {
1281 gdiPrev = gdiFont;
1282 gdiFont = gdiFont->next;
1285 return ret;
1288 static void GetEnumStructs(Face *face, LPENUMLOGFONTEXW pelf,
1289 NEWTEXTMETRICEXW *pntm, LPDWORD ptype)
1291 OUTLINETEXTMETRICW *potm;
1292 UINT size;
1293 GdiFont font = alloc_font();
1295 if (!(font->ft_face = OpenFontFile(font, face->file, face->face_index, 100)))
1297 free_font(font);
1298 return;
1301 font->name = strdupW(face->family->FamilyName);
1303 memset(&pelf->elfLogFont, 0, sizeof(LOGFONTW));
1305 size = WineEngGetOutlineTextMetrics(font, 0, NULL);
1306 potm = HeapAlloc(GetProcessHeap(), 0, size);
1307 WineEngGetOutlineTextMetrics(font, size, potm);
1309 #define TM potm->otmTextMetrics
1311 pntm->ntmTm.tmHeight = pelf->elfLogFont.lfHeight = TM.tmHeight;
1312 pntm->ntmTm.tmAscent = TM.tmAscent;
1313 pntm->ntmTm.tmDescent = TM.tmDescent;
1314 pntm->ntmTm.tmInternalLeading = TM.tmInternalLeading;
1315 pntm->ntmTm.tmExternalLeading = TM.tmExternalLeading;
1316 pntm->ntmTm.tmAveCharWidth = pelf->elfLogFont.lfWidth = TM.tmAveCharWidth;
1317 pntm->ntmTm.tmMaxCharWidth = TM.tmMaxCharWidth;
1318 pntm->ntmTm.tmWeight = pelf->elfLogFont.lfWeight = TM.tmWeight;
1319 pntm->ntmTm.tmOverhang = TM.tmOverhang;
1320 pntm->ntmTm.tmDigitizedAspectX = TM.tmDigitizedAspectX;
1321 pntm->ntmTm.tmDigitizedAspectY = TM.tmDigitizedAspectY;
1322 pntm->ntmTm.tmFirstChar = TM.tmFirstChar;
1323 pntm->ntmTm.tmLastChar = TM.tmLastChar;
1324 pntm->ntmTm.tmDefaultChar = TM.tmDefaultChar;
1325 pntm->ntmTm.tmBreakChar = TM.tmBreakChar;
1326 pntm->ntmTm.tmItalic = pelf->elfLogFont.lfItalic = TM.tmItalic;
1327 pntm->ntmTm.tmUnderlined = pelf->elfLogFont.lfUnderline = TM.tmUnderlined;
1328 pntm->ntmTm.tmStruckOut = pelf->elfLogFont.lfStrikeOut = TM.tmStruckOut;
1329 pntm->ntmTm.tmPitchAndFamily = TM.tmPitchAndFamily;
1330 pelf->elfLogFont.lfPitchAndFamily = (TM.tmPitchAndFamily & 0xf1) + 1;
1331 pntm->ntmTm.tmCharSet = pelf->elfLogFont.lfCharSet = TM.tmCharSet;
1332 pelf->elfLogFont.lfOutPrecision = OUT_STROKE_PRECIS;
1333 pelf->elfLogFont.lfClipPrecision = CLIP_STROKE_PRECIS;
1334 pelf->elfLogFont.lfQuality = DRAFT_QUALITY;
1336 pntm->ntmTm.ntmFlags = TM.tmItalic ? NTM_ITALIC : 0;
1337 if(TM.tmWeight > 550) pntm->ntmTm.ntmFlags |= NTM_BOLD;
1338 if(pntm->ntmTm.ntmFlags == 0) pntm->ntmTm.ntmFlags = NTM_REGULAR;
1340 pntm->ntmTm.ntmSizeEM = potm->otmEMSquare;
1341 pntm->ntmTm.ntmCellHeight = 0;
1342 pntm->ntmTm.ntmAvgWidth = 0;
1344 *ptype = TM.tmPitchAndFamily & TMPF_TRUETYPE ? TRUETYPE_FONTTYPE : 0;
1345 if(!(TM.tmPitchAndFamily & TMPF_VECTOR))
1346 *ptype |= RASTER_FONTTYPE;
1348 #undef TM
1349 memset(&pntm->ntmFontSig, 0, sizeof(FONTSIGNATURE));
1351 strncpyW(pelf->elfLogFont.lfFaceName,
1352 (WCHAR*)((char*)potm + (ptrdiff_t)potm->otmpFamilyName),
1353 LF_FACESIZE);
1354 strncpyW(pelf->elfFullName,
1355 (WCHAR*)((char*)potm + (ptrdiff_t)potm->otmpFaceName),
1356 LF_FULLFACESIZE);
1357 strncpyW(pelf->elfStyle,
1358 (WCHAR*)((char*)potm + (ptrdiff_t)potm->otmpStyleName),
1359 LF_FACESIZE);
1360 pelf->elfScript[0] = '\0'; /* This will get set in WineEngEnumFonts */
1362 HeapFree(GetProcessHeap(), 0, potm);
1363 free_font(font);
1364 return;
1367 /*************************************************************
1368 * WineEngEnumFonts
1371 DWORD WineEngEnumFonts(LPLOGFONTW plf, DEVICEFONTENUMPROC proc,
1372 LPARAM lparam)
1374 Family *family;
1375 Face *face;
1376 ENUMLOGFONTEXW elf;
1377 NEWTEXTMETRICEXW ntm;
1378 DWORD type, ret = 1;
1379 FONTSIGNATURE fs;
1380 CHARSETINFO csi;
1381 LOGFONTW lf;
1382 int i;
1384 TRACE("facename = %s charset %d\n", debugstr_w(plf->lfFaceName), plf->lfCharSet);
1386 if(plf->lfFaceName[0]) {
1387 FontSubst *psub;
1388 for(psub = substlist; psub; psub = psub->next)
1389 if(!strcmpiW(plf->lfFaceName, psub->from.name) &&
1390 (psub->from.charset == -1 ||
1391 psub->from.charset == plf->lfCharSet))
1392 break;
1393 if(psub) {
1394 TRACE("substituting %s -> %s\n", debugstr_w(plf->lfFaceName),
1395 debugstr_w(psub->to.name));
1396 memcpy(&lf, plf, sizeof(lf));
1397 strcpyW(lf.lfFaceName, psub->to.name);
1398 plf = &lf;
1400 for(family = FontList; family; family = family->next) {
1401 if(!strcmpiW(plf->lfFaceName, family->FamilyName)) {
1402 for(face = family->FirstFace; face; face = face->next) {
1403 GetEnumStructs(face, &elf, &ntm, &type);
1404 for(i = 0; i < 32; i++) {
1405 if(face->fs.fsCsb[0] & (1L << i)) {
1406 fs.fsCsb[0] = 1L << i;
1407 fs.fsCsb[1] = 0;
1408 if(!TranslateCharsetInfo(fs.fsCsb, &csi,
1409 TCI_SRCFONTSIG))
1410 csi.ciCharset = DEFAULT_CHARSET;
1411 if(i == 31) csi.ciCharset = SYMBOL_CHARSET;
1412 if(csi.ciCharset != DEFAULT_CHARSET) {
1413 elf.elfLogFont.lfCharSet =
1414 ntm.ntmTm.tmCharSet = csi.ciCharset;
1415 if(ElfScriptsW[i])
1416 strcpyW(elf.elfScript, ElfScriptsW[i]);
1417 else
1418 FIXME("Unknown elfscript for bit %d\n", i);
1419 TRACE("enuming face %s full %s style %s charset %d type %ld script %s it %d weight %ld ntmflags %08lx\n",
1420 debugstr_w(elf.elfLogFont.lfFaceName),
1421 debugstr_w(elf.elfFullName), debugstr_w(elf.elfStyle),
1422 csi.ciCharset, type, debugstr_w(elf.elfScript),
1423 elf.elfLogFont.lfItalic, elf.elfLogFont.lfWeight,
1424 ntm.ntmTm.ntmFlags);
1425 ret = proc(&elf, &ntm, type, lparam);
1426 if(!ret) goto end;
1433 } else {
1434 for(family = FontList; family; family = family->next) {
1435 GetEnumStructs(family->FirstFace, &elf, &ntm, &type);
1436 for(i = 0; i < 32; i++) {
1437 if(family->FirstFace->fs.fsCsb[0] & (1L << i)) {
1438 fs.fsCsb[0] = 1L << i;
1439 fs.fsCsb[1] = 0;
1440 if(!TranslateCharsetInfo(fs.fsCsb, &csi,
1441 TCI_SRCFONTSIG))
1442 csi.ciCharset = DEFAULT_CHARSET;
1443 if(i == 31) csi.ciCharset = SYMBOL_CHARSET;
1444 if(csi.ciCharset != DEFAULT_CHARSET) {
1445 elf.elfLogFont.lfCharSet = ntm.ntmTm.tmCharSet =
1446 csi.ciCharset;
1447 if(ElfScriptsW[i])
1448 strcpyW(elf.elfScript, ElfScriptsW[i]);
1449 else
1450 FIXME("Unknown elfscript for bit %d\n", i);
1451 TRACE("enuming face %s full %s style %s charset = %d type %ld script %s it %d weight %ld ntmflags %08lx\n",
1452 debugstr_w(elf.elfLogFont.lfFaceName),
1453 debugstr_w(elf.elfFullName), debugstr_w(elf.elfStyle),
1454 csi.ciCharset, type, debugstr_w(elf.elfScript),
1455 elf.elfLogFont.lfItalic, elf.elfLogFont.lfWeight,
1456 ntm.ntmTm.ntmFlags);
1457 ret = proc(&elf, &ntm, type, lparam);
1458 if(!ret) goto end;
1464 end:
1465 return ret;
1468 static void FTVectorToPOINTFX(FT_Vector *vec, POINTFX *pt)
1470 pt->x.value = vec->x >> 6;
1471 pt->x.fract = (vec->x & 0x3f) << 10;
1472 pt->x.fract |= ((pt->x.fract >> 6) | (pt->x.fract >> 12));
1473 pt->y.value = vec->y >> 6;
1474 pt->y.fract = (vec->y & 0x3f) << 10;
1475 pt->y.fract |= ((pt->y.fract >> 6) | (pt->y.fract >> 12));
1476 return;
1479 static FT_UInt get_glyph_index(GdiFont font, UINT glyph)
1481 if(font->charset == SYMBOL_CHARSET && glyph < 0x100)
1482 glyph = glyph + 0xf000;
1483 return pFT_Get_Char_Index(font->ft_face, glyph);
1486 /*************************************************************
1487 * WineEngGetGlyphIndices
1489 * FIXME: add support for GGI_MARK_NONEXISTING_GLYPHS
1491 DWORD WineEngGetGlyphIndices(GdiFont font, LPCWSTR lpstr, INT count,
1492 LPWORD pgi, DWORD flags)
1494 INT i;
1496 for(i = 0; i < count; i++)
1497 pgi[i] = get_glyph_index(font, lpstr[i]);
1499 return count;
1502 /*************************************************************
1503 * WineEngGetGlyphOutline
1505 * Behaves in exactly the same way as the win32 api GetGlyphOutline
1506 * except that the first parameter is the HWINEENGFONT of the font in
1507 * question rather than an HDC.
1510 DWORD WineEngGetGlyphOutline(GdiFont font, UINT glyph, UINT format,
1511 LPGLYPHMETRICS lpgm, DWORD buflen, LPVOID buf,
1512 const MAT2* lpmat)
1514 FT_Face ft_face = font->ft_face;
1515 FT_UInt glyph_index;
1516 DWORD width, height, pitch, needed = 0;
1517 FT_Bitmap ft_bitmap;
1518 FT_Error err;
1519 INT left, right, top = 0, bottom = 0;
1520 FT_Angle angle = 0;
1521 FT_Int load_flags = FT_LOAD_DEFAULT;
1523 TRACE("%p, %04x, %08x, %p, %08lx, %p, %p\n", font, glyph, format, lpgm,
1524 buflen, buf, lpmat);
1526 if(format & GGO_GLYPH_INDEX) {
1527 glyph_index = glyph;
1528 format &= ~GGO_GLYPH_INDEX;
1529 } else
1530 glyph_index = get_glyph_index(font, glyph);
1532 if(glyph_index >= font->gmsize) {
1533 font->gmsize = (glyph_index / INIT_GM_SIZE + 1) * INIT_GM_SIZE;
1534 font->gm = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, font->gm,
1535 font->gmsize * sizeof(*font->gm));
1536 } else {
1537 if(format == GGO_METRICS && font->gm[glyph_index].init) {
1538 memcpy(lpgm, &font->gm[glyph_index].gm, sizeof(*lpgm));
1539 return 1; /* FIXME */
1543 if(font->orientation || (format != GGO_METRICS && format != GGO_BITMAP))
1544 load_flags |= FT_LOAD_NO_BITMAP;
1546 err = pFT_Load_Glyph(ft_face, glyph_index, load_flags);
1548 if(err) {
1549 FIXME("FT_Load_Glyph on index %x returns %d\n", glyph_index, err);
1550 return GDI_ERROR;
1553 left = ft_face->glyph->metrics.horiBearingX & -64;
1554 right = ((ft_face->glyph->metrics.horiBearingX +
1555 ft_face->glyph->metrics.width) + 63) & -64;
1557 font->gm[glyph_index].adv = (ft_face->glyph->metrics.horiAdvance + 63) >> 6;
1558 font->gm[glyph_index].lsb = left >> 6;
1559 font->gm[glyph_index].bbx = (right - left) >> 6;
1561 if(font->orientation == 0) {
1562 top = (ft_face->glyph->metrics.horiBearingY + 63) & -64;
1563 bottom = (ft_face->glyph->metrics.horiBearingY -
1564 ft_face->glyph->metrics.height) & -64;
1565 lpgm->gmCellIncX = font->gm[glyph_index].adv;
1566 lpgm->gmCellIncY = 0;
1567 } else {
1568 INT xc, yc;
1569 FT_Vector vec;
1570 angle = font->orientation / 10 << 16;
1571 angle |= ((font->orientation % 10) * (1 << 16)) / 10;
1572 TRACE("angle %ld\n", angle >> 16);
1573 for(xc = 0; xc < 2; xc++) {
1574 for(yc = 0; yc < 2; yc++) {
1575 vec.x = ft_face->glyph->metrics.horiBearingX +
1576 xc * ft_face->glyph->metrics.width;
1577 vec.y = ft_face->glyph->metrics.horiBearingY -
1578 yc * ft_face->glyph->metrics.height;
1579 TRACE("Vec %ld,%ld\n", vec.x, vec.y);
1580 pFT_Vector_Rotate(&vec, angle);
1581 if(xc == 0 && yc == 0) {
1582 left = right = vec.x;
1583 top = bottom = vec.y;
1584 } else {
1585 if(vec.x < left) left = vec.x;
1586 else if(vec.x > right) right = vec.x;
1587 if(vec.y < bottom) bottom = vec.y;
1588 else if(vec.y > top) top = vec.y;
1592 left = left & -64;
1593 right = (right + 63) & -64;
1594 bottom = bottom & -64;
1595 top = (top + 63) & -64;
1597 TRACE("transformed box: (%d,%d - %d,%d)\n", left, top, right, bottom);
1598 vec.x = ft_face->glyph->metrics.horiAdvance;
1599 vec.y = 0;
1600 pFT_Vector_Rotate(&vec, angle);
1601 lpgm->gmCellIncX = (vec.x+63) >> 6;
1602 lpgm->gmCellIncY = -(vec.y+63) >> 6;
1604 lpgm->gmBlackBoxX = (right - left) >> 6;
1605 lpgm->gmBlackBoxY = (top - bottom) >> 6;
1606 lpgm->gmptGlyphOrigin.x = left >> 6;
1607 lpgm->gmptGlyphOrigin.y = top >> 6;
1609 memcpy(&font->gm[glyph_index].gm, lpgm, sizeof(*lpgm));
1610 font->gm[glyph_index].init = TRUE;
1612 if(format == GGO_METRICS)
1613 return 1; /* FIXME */
1615 if(ft_face->glyph->format != ft_glyph_format_outline && format != GGO_BITMAP) {
1616 FIXME("loaded a bitmap\n");
1617 return GDI_ERROR;
1620 switch(format) {
1621 case GGO_BITMAP:
1622 width = lpgm->gmBlackBoxX;
1623 height = lpgm->gmBlackBoxY;
1624 pitch = (width + 31) / 32 * 4;
1625 needed = pitch * height;
1627 if(!buf || !buflen) break;
1629 switch(ft_face->glyph->format) {
1630 case ft_glyph_format_bitmap:
1632 BYTE *src = ft_face->glyph->bitmap.buffer, *dst = buf;
1633 INT w = (ft_face->glyph->bitmap.width + 7) >> 3;
1634 INT h = ft_face->glyph->bitmap.rows;
1635 while(h--) {
1636 memcpy(dst, src, w);
1637 src += ft_face->glyph->bitmap.pitch;
1638 dst += pitch;
1640 break;
1643 case ft_glyph_format_outline:
1644 ft_bitmap.width = width;
1645 ft_bitmap.rows = height;
1646 ft_bitmap.pitch = pitch;
1647 ft_bitmap.pixel_mode = ft_pixel_mode_mono;
1648 ft_bitmap.buffer = buf;
1650 if(font->orientation) {
1651 FT_Matrix matrix;
1652 matrix.xx = matrix.yy = pFT_Cos(angle);
1653 matrix.xy = -pFT_Sin(angle);
1654 matrix.yx = -matrix.xy;
1656 pFT_Outline_Transform(&ft_face->glyph->outline, &matrix);
1659 if (lpmat) pFT_Outline_Transform(&ft_face->glyph->outline, (FT_Matrix *)lpmat);
1660 pFT_Outline_Translate(&ft_face->glyph->outline, -left, -bottom );
1662 /* Note: FreeType will only set 'black' bits for us. */
1663 memset(buf, 0, needed);
1664 pFT_Outline_Get_Bitmap(library, &ft_face->glyph->outline, &ft_bitmap);
1665 break;
1667 default:
1668 FIXME("loaded glyph format %x\n", ft_face->glyph->format);
1669 return GDI_ERROR;
1671 break;
1673 case GGO_GRAY2_BITMAP:
1674 case GGO_GRAY4_BITMAP:
1675 case GGO_GRAY8_BITMAP:
1676 case WINE_GGO_GRAY16_BITMAP:
1678 int mult, row, col;
1679 BYTE *start, *ptr;
1681 width = lpgm->gmBlackBoxX;
1682 height = lpgm->gmBlackBoxY;
1683 pitch = (width + 3) / 4 * 4;
1684 needed = pitch * height;
1686 if(!buf || !buflen) break;
1687 ft_bitmap.width = width;
1688 ft_bitmap.rows = height;
1689 ft_bitmap.pitch = pitch;
1690 ft_bitmap.pixel_mode = ft_pixel_mode_grays;
1691 ft_bitmap.buffer = buf;
1693 if(font->orientation) {
1694 FT_Matrix matrix;
1695 matrix.xx = matrix.yy = pFT_Cos(angle);
1696 matrix.xy = -pFT_Sin(angle);
1697 matrix.yx = -matrix.xy;
1698 pFT_Outline_Transform(&ft_face->glyph->outline, &matrix);
1701 if (lpmat) pFT_Outline_Transform(&ft_face->glyph->outline, (FT_Matrix *)lpmat);
1702 pFT_Outline_Translate(&ft_face->glyph->outline, -left, -bottom );
1704 pFT_Outline_Get_Bitmap(library, &ft_face->glyph->outline, &ft_bitmap);
1706 if(format == GGO_GRAY2_BITMAP)
1707 mult = 5;
1708 else if(format == GGO_GRAY4_BITMAP)
1709 mult = 17;
1710 else if(format == GGO_GRAY8_BITMAP)
1711 mult = 65;
1712 else if(format == WINE_GGO_GRAY16_BITMAP)
1713 break;
1714 else {
1715 assert(0);
1716 break;
1719 start = buf;
1720 for(row = 0; row < height; row++) {
1721 ptr = start;
1722 for(col = 0; col < width; col++, ptr++) {
1723 *ptr = (*(unsigned int*)ptr * mult + 128) / 256;
1725 start += pitch;
1727 break;
1730 case GGO_NATIVE:
1732 int contour, point = 0, first_pt;
1733 FT_Outline *outline = &ft_face->glyph->outline;
1734 TTPOLYGONHEADER *pph;
1735 TTPOLYCURVE *ppc;
1736 DWORD pph_start, cpfx, type;
1738 if(buflen == 0) buf = NULL;
1740 if (lpmat) pFT_Outline_Transform(outline, (FT_Matrix *)lpmat);
1742 for(contour = 0; contour < outline->n_contours; contour++) {
1743 pph_start = needed;
1744 pph = (TTPOLYGONHEADER *)((char *)buf + needed);
1745 first_pt = point;
1746 if(buf) {
1747 pph->dwType = TT_POLYGON_TYPE;
1748 FTVectorToPOINTFX(&outline->points[point], &pph->pfxStart);
1750 needed += sizeof(*pph);
1751 point++;
1752 while(point <= outline->contours[contour]) {
1753 ppc = (TTPOLYCURVE *)((char *)buf + needed);
1754 type = (outline->tags[point] & FT_Curve_Tag_On) ?
1755 TT_PRIM_LINE : TT_PRIM_QSPLINE;
1756 cpfx = 0;
1757 do {
1758 if(buf)
1759 FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
1760 cpfx++;
1761 point++;
1762 } while(point <= outline->contours[contour] &&
1763 (outline->tags[point] & FT_Curve_Tag_On) ==
1764 (outline->tags[point-1] & FT_Curve_Tag_On));
1765 /* At the end of a contour Windows adds the start point, but
1766 only for Beziers */
1767 if(point > outline->contours[contour] &&
1768 !(outline->tags[point-1] & FT_Curve_Tag_On)) {
1769 if(buf)
1770 FTVectorToPOINTFX(&outline->points[first_pt], &ppc->apfx[cpfx]);
1771 cpfx++;
1772 } else if(point <= outline->contours[contour] &&
1773 outline->tags[point] & FT_Curve_Tag_On) {
1774 /* add closing pt for bezier */
1775 if(buf)
1776 FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
1777 cpfx++;
1778 point++;
1780 if(buf) {
1781 ppc->wType = type;
1782 ppc->cpfx = cpfx;
1784 needed += sizeof(*ppc) + (cpfx - 1) * sizeof(POINTFX);
1786 if(buf)
1787 pph->cb = needed - pph_start;
1789 break;
1791 case GGO_BEZIER:
1793 /* Convert the quadratic Beziers to cubic Beziers.
1794 The parametric eqn for a cubic Bezier is, from PLRM:
1795 r(t) = at^3 + bt^2 + ct + r0
1796 with the control points:
1797 r1 = r0 + c/3
1798 r2 = r1 + (c + b)/3
1799 r3 = r0 + c + b + a
1801 A quadratic Beizer has the form:
1802 p(t) = (1-t)^2 p0 + 2(1-t)t p1 + t^2 p2
1804 So equating powers of t leads to:
1805 r1 = 2/3 p1 + 1/3 p0
1806 r2 = 2/3 p1 + 1/3 p2
1807 and of course r0 = p0, r3 = p2
1810 int contour, point = 0, first_pt;
1811 FT_Outline *outline = &ft_face->glyph->outline;
1812 TTPOLYGONHEADER *pph;
1813 TTPOLYCURVE *ppc;
1814 DWORD pph_start, cpfx, type;
1815 FT_Vector cubic_control[4];
1816 if(buflen == 0) buf = NULL;
1818 if (lpmat) pFT_Outline_Transform(outline, (FT_Matrix *)lpmat);
1820 for(contour = 0; contour < outline->n_contours; contour++) {
1821 pph_start = needed;
1822 pph = (TTPOLYGONHEADER *)((char *)buf + needed);
1823 first_pt = point;
1824 if(buf) {
1825 pph->dwType = TT_POLYGON_TYPE;
1826 FTVectorToPOINTFX(&outline->points[point], &pph->pfxStart);
1828 needed += sizeof(*pph);
1829 point++;
1830 while(point <= outline->contours[contour]) {
1831 ppc = (TTPOLYCURVE *)((char *)buf + needed);
1832 type = (outline->tags[point] & FT_Curve_Tag_On) ?
1833 TT_PRIM_LINE : TT_PRIM_CSPLINE;
1834 cpfx = 0;
1835 do {
1836 if(type == TT_PRIM_LINE) {
1837 if(buf)
1838 FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
1839 cpfx++;
1840 point++;
1841 } else {
1842 /* Unlike QSPLINEs, CSPLINEs always have their endpoint
1843 so cpfx = 3n */
1845 /* FIXME: Possible optimization in endpoint calculation
1846 if there are two consecutive curves */
1847 cubic_control[0] = outline->points[point-1];
1848 if(!(outline->tags[point-1] & FT_Curve_Tag_On)) {
1849 cubic_control[0].x += outline->points[point].x + 1;
1850 cubic_control[0].y += outline->points[point].y + 1;
1851 cubic_control[0].x >>= 1;
1852 cubic_control[0].y >>= 1;
1854 if(point+1 > outline->contours[contour])
1855 cubic_control[3] = outline->points[first_pt];
1856 else {
1857 cubic_control[3] = outline->points[point+1];
1858 if(!(outline->tags[point+1] & FT_Curve_Tag_On)) {
1859 cubic_control[3].x += outline->points[point].x + 1;
1860 cubic_control[3].y += outline->points[point].y + 1;
1861 cubic_control[3].x >>= 1;
1862 cubic_control[3].y >>= 1;
1865 /* r1 = 1/3 p0 + 2/3 p1
1866 r2 = 1/3 p2 + 2/3 p1 */
1867 cubic_control[1].x = (2 * outline->points[point].x + 1) / 3;
1868 cubic_control[1].y = (2 * outline->points[point].y + 1) / 3;
1869 cubic_control[2] = cubic_control[1];
1870 cubic_control[1].x += (cubic_control[0].x + 1) / 3;
1871 cubic_control[1].y += (cubic_control[0].y + 1) / 3;
1872 cubic_control[2].x += (cubic_control[3].x + 1) / 3;
1873 cubic_control[2].y += (cubic_control[3].y + 1) / 3;
1874 if(buf) {
1875 FTVectorToPOINTFX(&cubic_control[1], &ppc->apfx[cpfx]);
1876 FTVectorToPOINTFX(&cubic_control[2], &ppc->apfx[cpfx+1]);
1877 FTVectorToPOINTFX(&cubic_control[3], &ppc->apfx[cpfx+2]);
1879 cpfx += 3;
1880 point++;
1882 } while(point <= outline->contours[contour] &&
1883 (outline->tags[point] & FT_Curve_Tag_On) ==
1884 (outline->tags[point-1] & FT_Curve_Tag_On));
1885 /* At the end of a contour Windows adds the start point,
1886 but only for Beziers and we've already done that.
1888 if(point <= outline->contours[contour] &&
1889 outline->tags[point] & FT_Curve_Tag_On) {
1890 /* This is the closing pt of a bezier, but we've already
1891 added it, so just inc point and carry on */
1892 point++;
1894 if(buf) {
1895 ppc->wType = type;
1896 ppc->cpfx = cpfx;
1898 needed += sizeof(*ppc) + (cpfx - 1) * sizeof(POINTFX);
1900 if(buf)
1901 pph->cb = needed - pph_start;
1903 break;
1906 default:
1907 FIXME("Unsupported format %d\n", format);
1908 return GDI_ERROR;
1910 return needed;
1913 /*************************************************************
1914 * WineEngGetTextMetrics
1917 BOOL WineEngGetTextMetrics(GdiFont font, LPTEXTMETRICW ptm)
1919 if(!font->potm) {
1920 if(!WineEngGetOutlineTextMetrics(font, 0, NULL))
1921 return FALSE;
1923 if(!font->potm) return FALSE;
1924 memcpy(ptm, &font->potm->otmTextMetrics, sizeof(*ptm));
1925 return TRUE;
1929 /*************************************************************
1930 * WineEngGetOutlineTextMetrics
1933 UINT WineEngGetOutlineTextMetrics(GdiFont font, UINT cbSize,
1934 OUTLINETEXTMETRICW *potm)
1936 FT_Face ft_face = font->ft_face;
1937 UINT needed, lenfam, lensty, ret;
1938 TT_OS2 *pOS2;
1939 TT_HoriHeader *pHori;
1940 TT_Postscript *pPost;
1941 FT_Fixed x_scale, y_scale;
1942 WCHAR *family_nameW, *style_nameW;
1943 WCHAR spaceW[] = {' ', '\0'};
1944 char *cp;
1946 TRACE("font=%p\n", font);
1948 if(font->potm) {
1949 if(cbSize >= font->potm->otmSize)
1950 memcpy(potm, font->potm, font->potm->otmSize);
1951 return font->potm->otmSize;
1954 needed = sizeof(*potm);
1956 lenfam = (strlenW(font->name) + 1) * sizeof(WCHAR);
1957 family_nameW = strdupW(font->name);
1959 lensty = MultiByteToWideChar(CP_ACP, 0, ft_face->style_name, -1, NULL, 0)
1960 * sizeof(WCHAR);
1961 style_nameW = HeapAlloc(GetProcessHeap(), 0, lensty);
1962 MultiByteToWideChar(CP_ACP, 0, ft_face->style_name, -1,
1963 style_nameW, lensty);
1965 /* These names should be read from the TT name table */
1967 /* length of otmpFamilyName */
1968 needed += lenfam;
1970 /* length of otmpFaceName */
1971 if(!strcasecmp(ft_face->style_name, "regular")) {
1972 needed += lenfam; /* just the family name */
1973 } else {
1974 needed += lenfam + lensty; /* family + " " + style */
1977 /* length of otmpStyleName */
1978 needed += lensty;
1980 /* length of otmpFullName */
1981 needed += lenfam + lensty;
1984 x_scale = ft_face->size->metrics.x_scale;
1985 y_scale = ft_face->size->metrics.y_scale;
1987 pOS2 = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_os2);
1988 if(!pOS2) {
1989 FIXME("Can't find OS/2 table - not TT font?\n");
1990 ret = 0;
1991 goto end;
1994 pHori = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_hhea);
1995 if(!pHori) {
1996 FIXME("Can't find HHEA table - not TT font?\n");
1997 ret = 0;
1998 goto end;
2001 pPost = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_post); /* we can live with this failing */
2003 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",
2004 pOS2->usWinAscent, pOS2->usWinDescent,
2005 pOS2->sTypoAscender, pOS2->sTypoDescender, pOS2->sTypoLineGap,
2006 ft_face->ascender, ft_face->descender, ft_face->height,
2007 pHori->Ascender, pHori->Descender, pHori->Line_Gap,
2008 ft_face->bbox.yMax, ft_face->bbox.yMin);
2010 font->potm = HeapAlloc(GetProcessHeap(), 0, needed);
2011 font->potm->otmSize = needed;
2013 #define TM font->potm->otmTextMetrics
2015 if(font->yMax) {
2016 TM.tmAscent = font->yMax;
2017 TM.tmDescent = -font->yMin;
2018 TM.tmInternalLeading = (TM.tmAscent + TM.tmDescent) - ft_face->size->metrics.y_ppem;
2019 } else {
2020 TM.tmAscent = (pFT_MulFix(pOS2->usWinAscent, y_scale) + 32) >> 6;
2021 TM.tmDescent = (pFT_MulFix(pOS2->usWinDescent, y_scale) + 32) >> 6;
2022 TM.tmInternalLeading = (pFT_MulFix(pOS2->usWinAscent + pOS2->usWinDescent
2023 - ft_face->units_per_EM, y_scale) + 32) >> 6;
2026 TM.tmHeight = TM.tmAscent + TM.tmDescent;
2028 /* MSDN says:
2029 el = MAX(0, LineGap - ((WinAscent + WinDescent) - (Ascender - Descender)))
2031 TM.tmExternalLeading = max(0, (pFT_MulFix(pHori->Line_Gap -
2032 ((pOS2->usWinAscent + pOS2->usWinDescent) -
2033 (pHori->Ascender - pHori->Descender)), y_scale) + 32) >> 6);
2035 TM.tmAveCharWidth = (pFT_MulFix(pOS2->xAvgCharWidth, x_scale) + 32) >> 6;
2036 TM.tmMaxCharWidth = (pFT_MulFix(ft_face->bbox.xMax - ft_face->bbox.xMin, x_scale) + 32) >> 6;
2037 TM.tmWeight = font->fake_bold ? FW_BOLD : pOS2->usWeightClass;
2038 TM.tmOverhang = 0;
2039 TM.tmDigitizedAspectX = 300;
2040 TM.tmDigitizedAspectY = 300;
2041 TM.tmFirstChar = pOS2->usFirstCharIndex;
2042 TM.tmLastChar = pOS2->usLastCharIndex;
2043 TM.tmDefaultChar = pOS2->usDefaultChar;
2044 TM.tmBreakChar = pOS2->usBreakChar ? pOS2->usBreakChar : ' ';
2045 TM.tmItalic = font->fake_italic ? 255 : ((ft_face->style_flags & FT_STYLE_FLAG_ITALIC) ? 255 : 0);
2046 TM.tmUnderlined = 0; /* entry in OS2 table */
2047 TM.tmStruckOut = 0; /* entry in OS2 table */
2049 /* Yes TPMF_FIXED_PITCH is correct; braindead api */
2050 if(!FT_IS_FIXED_WIDTH(ft_face))
2051 TM.tmPitchAndFamily = TMPF_FIXED_PITCH;
2052 else
2053 TM.tmPitchAndFamily = 0;
2055 switch(pOS2->panose[PAN_FAMILYTYPE_INDEX]) {
2056 case PAN_FAMILY_SCRIPT:
2057 TM.tmPitchAndFamily |= FF_SCRIPT;
2058 break;
2059 case PAN_FAMILY_DECORATIVE:
2060 case PAN_FAMILY_PICTORIAL:
2061 TM.tmPitchAndFamily |= FF_DECORATIVE;
2062 break;
2063 case PAN_FAMILY_TEXT_DISPLAY:
2064 if(TM.tmPitchAndFamily == 0) /* fixed */
2065 TM.tmPitchAndFamily = FF_MODERN;
2066 else {
2067 switch(pOS2->panose[PAN_SERIFSTYLE_INDEX]) {
2068 case PAN_SERIF_NORMAL_SANS:
2069 case PAN_SERIF_OBTUSE_SANS:
2070 case PAN_SERIF_PERP_SANS:
2071 TM.tmPitchAndFamily |= FF_SWISS;
2072 break;
2073 default:
2074 TM.tmPitchAndFamily |= FF_ROMAN;
2077 break;
2078 default:
2079 TM.tmPitchAndFamily |= FF_DONTCARE;
2082 if(FT_IS_SCALABLE(ft_face))
2083 TM.tmPitchAndFamily |= TMPF_VECTOR;
2084 if(FT_IS_SFNT(ft_face))
2085 TM.tmPitchAndFamily |= TMPF_TRUETYPE;
2087 TM.tmCharSet = font->charset;
2088 #undef TM
2090 font->potm->otmFiller = 0;
2091 memcpy(&font->potm->otmPanoseNumber, pOS2->panose, PANOSE_COUNT);
2092 font->potm->otmfsSelection = pOS2->fsSelection;
2093 font->potm->otmfsType = pOS2->fsType;
2094 font->potm->otmsCharSlopeRise = pHori->caret_Slope_Rise;
2095 font->potm->otmsCharSlopeRun = pHori->caret_Slope_Run;
2096 font->potm->otmItalicAngle = 0; /* POST table */
2097 font->potm->otmEMSquare = ft_face->units_per_EM;
2098 font->potm->otmAscent = (pFT_MulFix(pOS2->sTypoAscender, y_scale) + 32) >> 6;
2099 font->potm->otmDescent = (pFT_MulFix(pOS2->sTypoDescender, y_scale) + 32) >> 6;
2100 font->potm->otmLineGap = (pFT_MulFix(pOS2->sTypoLineGap, y_scale) + 32) >> 6;
2101 font->potm->otmsCapEmHeight = (pFT_MulFix(pOS2->sCapHeight, y_scale) + 32) >> 6;
2102 font->potm->otmsXHeight = (pFT_MulFix(pOS2->sxHeight, y_scale) + 32) >> 6;
2103 font->potm->otmrcFontBox.left = ft_face->bbox.xMin;
2104 font->potm->otmrcFontBox.right = ft_face->bbox.xMax;
2105 font->potm->otmrcFontBox.top = ft_face->bbox.yMin;
2106 font->potm->otmrcFontBox.bottom = ft_face->bbox.yMax;
2107 font->potm->otmMacAscent = 0; /* where do these come from ? */
2108 font->potm->otmMacDescent = 0;
2109 font->potm->otmMacLineGap = 0;
2110 font->potm->otmusMinimumPPEM = 0; /* TT Header */
2111 font->potm->otmptSubscriptSize.x = (pFT_MulFix(pOS2->ySubscriptXSize, x_scale) + 32) >> 6;
2112 font->potm->otmptSubscriptSize.y = (pFT_MulFix(pOS2->ySubscriptYSize, y_scale) + 32) >> 6;
2113 font->potm->otmptSubscriptOffset.x = (pFT_MulFix(pOS2->ySubscriptXOffset, x_scale) + 32) >> 6;
2114 font->potm->otmptSubscriptOffset.y = (pFT_MulFix(pOS2->ySubscriptYOffset, y_scale) + 32) >> 6;
2115 font->potm->otmptSuperscriptSize.x = (pFT_MulFix(pOS2->ySuperscriptXSize, x_scale) + 32) >> 6;
2116 font->potm->otmptSuperscriptSize.y = (pFT_MulFix(pOS2->ySuperscriptYSize, y_scale) + 32) >> 6;
2117 font->potm->otmptSuperscriptOffset.x = (pFT_MulFix(pOS2->ySuperscriptXOffset, x_scale) + 32) >> 6;
2118 font->potm->otmptSuperscriptOffset.y = (pFT_MulFix(pOS2->ySuperscriptYOffset, y_scale) + 32) >> 6;
2119 font->potm->otmsStrikeoutSize = (pFT_MulFix(pOS2->yStrikeoutSize, y_scale) + 32) >> 6;
2120 font->potm->otmsStrikeoutPosition = (pFT_MulFix(pOS2->yStrikeoutPosition, y_scale) + 32) >> 6;
2121 if(!pPost) {
2122 font->potm->otmsUnderscoreSize = 0;
2123 font->potm->otmsUnderscorePosition = 0;
2124 } else {
2125 font->potm->otmsUnderscoreSize = (pFT_MulFix(pPost->underlineThickness, y_scale) + 32) >> 6;
2126 font->potm->otmsUnderscorePosition = (pFT_MulFix(pPost->underlinePosition, y_scale) + 32) >> 6;
2129 /* otmp* members should clearly have type ptrdiff_t, but M$ knows best */
2130 cp = (char*)font->potm + sizeof(*font->potm);
2131 font->potm->otmpFamilyName = (LPSTR)(cp - (char*)font->potm);
2132 strcpyW((WCHAR*)cp, family_nameW);
2133 cp += lenfam;
2134 font->potm->otmpStyleName = (LPSTR)(cp - (char*)font->potm);
2135 strcpyW((WCHAR*)cp, style_nameW);
2136 cp += lensty;
2137 font->potm->otmpFaceName = (LPSTR)(cp - (char*)font->potm);
2138 strcpyW((WCHAR*)cp, family_nameW);
2139 if(strcasecmp(ft_face->style_name, "regular")) {
2140 strcatW((WCHAR*)cp, spaceW);
2141 strcatW((WCHAR*)cp, style_nameW);
2142 cp += lenfam + lensty;
2143 } else
2144 cp += lenfam;
2145 font->potm->otmpFullName = (LPSTR)(cp - (char*)font->potm);
2146 strcpyW((WCHAR*)cp, family_nameW);
2147 strcatW((WCHAR*)cp, spaceW);
2148 strcatW((WCHAR*)cp, style_nameW);
2149 ret = needed;
2151 if(needed <= cbSize)
2152 memcpy(potm, font->potm, font->potm->otmSize);
2154 end:
2155 HeapFree(GetProcessHeap(), 0, style_nameW);
2156 HeapFree(GetProcessHeap(), 0, family_nameW);
2158 return ret;
2162 /*************************************************************
2163 * WineEngGetCharWidth
2166 BOOL WineEngGetCharWidth(GdiFont font, UINT firstChar, UINT lastChar,
2167 LPINT buffer)
2169 UINT c;
2170 GLYPHMETRICS gm;
2171 FT_UInt glyph_index;
2173 TRACE("%p, %d, %d, %p\n", font, firstChar, lastChar, buffer);
2175 for(c = firstChar; c <= lastChar; c++) {
2176 glyph_index = get_glyph_index(font, c);
2177 WineEngGetGlyphOutline(font, glyph_index, GGO_METRICS | GGO_GLYPH_INDEX,
2178 &gm, 0, NULL, NULL);
2179 buffer[c - firstChar] = font->gm[glyph_index].adv;
2181 return TRUE;
2184 /*************************************************************
2185 * WineEngGetTextExtentPoint
2188 BOOL WineEngGetTextExtentPoint(GdiFont font, LPCWSTR wstr, INT count,
2189 LPSIZE size)
2191 INT idx;
2192 GLYPHMETRICS gm;
2193 TEXTMETRICW tm;
2194 FT_UInt glyph_index;
2196 TRACE("%p, %s, %d, %p\n", font, debugstr_wn(wstr, count), count,
2197 size);
2199 size->cx = 0;
2200 WineEngGetTextMetrics(font, &tm);
2201 size->cy = tm.tmHeight;
2203 for(idx = 0; idx < count; idx++) {
2204 glyph_index = get_glyph_index(font, wstr[idx]);
2205 WineEngGetGlyphOutline(font, glyph_index, GGO_METRICS | GGO_GLYPH_INDEX,
2206 &gm, 0, NULL, NULL);
2207 size->cx += font->gm[glyph_index].adv;
2209 TRACE("return %ld,%ld\n", size->cx, size->cy);
2210 return TRUE;
2213 /*************************************************************
2214 * WineEngGetTextExtentPointI
2217 BOOL WineEngGetTextExtentPointI(GdiFont font, const WORD *indices, INT count,
2218 LPSIZE size)
2220 INT idx;
2221 GLYPHMETRICS gm;
2222 TEXTMETRICW tm;
2224 TRACE("%p, %p, %d, %p\n", font, indices, count, size);
2226 size->cx = 0;
2227 WineEngGetTextMetrics(font, &tm);
2228 size->cy = tm.tmHeight;
2230 for(idx = 0; idx < count; idx++) {
2231 WineEngGetGlyphOutline(font, indices[idx],
2232 GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL,
2233 NULL);
2234 size->cx += font->gm[indices[idx]].adv;
2236 TRACE("return %ld,%ld\n", size->cx, size->cy);
2237 return TRUE;
2240 /*************************************************************
2241 * WineEngGetFontData
2244 DWORD WineEngGetFontData(GdiFont font, DWORD table, DWORD offset, LPVOID buf,
2245 DWORD cbData)
2247 FT_Face ft_face = font->ft_face;
2248 DWORD len;
2249 FT_Error err;
2251 TRACE("font=%p, table=%08lx, offset=%08lx, buf=%p, cbData=%lx\n",
2252 font, table, offset, buf, cbData);
2254 if(!FT_IS_SFNT(ft_face))
2255 return GDI_ERROR;
2257 if(!buf || !cbData)
2258 len = 0;
2259 else
2260 len = cbData;
2262 if(table) { /* MS tags differ in endidness from FT ones */
2263 table = table >> 24 | table << 24 |
2264 (table >> 8 & 0xff00) | (table << 8 & 0xff0000);
2267 /* If the FT_Load_Sfnt_Table function is there we'll use it */
2268 if(pFT_Load_Sfnt_Table)
2269 err = pFT_Load_Sfnt_Table(ft_face, table, offset, buf, &len);
2270 else { /* Do it the hard way */
2271 TT_Face tt_face = (TT_Face) ft_face;
2272 SFNT_Interface *sfnt;
2273 if (FT_Version.major==2 && FT_Version.minor==0)
2275 /* 2.0.x */
2276 sfnt = *(SFNT_Interface**)((char*)tt_face + 528);
2278 else
2280 /* A field was added in the middle of the structure in 2.1.x */
2281 sfnt = *(SFNT_Interface**)((char*)tt_face + 532);
2283 err = sfnt->load_any(tt_face, table, offset, buf, &len);
2285 if(err) {
2286 TRACE("Can't find table %08lx.\n", table);
2287 return GDI_ERROR;
2289 return len;
2292 /*************************************************************
2293 * WineEngGetTextFace
2296 INT WineEngGetTextFace(GdiFont font, INT count, LPWSTR str)
2298 if(str) {
2299 lstrcpynW(str, font->name, count);
2300 return strlenW(font->name);
2301 } else
2302 return strlenW(font->name) + 1;
2305 UINT WineEngGetTextCharsetInfo(GdiFont font, LPFONTSIGNATURE fs, DWORD flags)
2307 if (fs) memcpy(fs, &font->fs, sizeof(FONTSIGNATURE));
2308 return font->charset;
2311 #else /* HAVE_FREETYPE */
2313 BOOL WineEngInit(void)
2315 return FALSE;
2317 GdiFont WineEngCreateFontInstance(DC *dc, HFONT hfont)
2319 return NULL;
2321 BOOL WineEngDestroyFontInstance(HFONT hfont)
2323 return FALSE;
2326 DWORD WineEngEnumFonts(LPLOGFONTW plf, DEVICEFONTENUMPROC proc, LPARAM lparam)
2328 return 1;
2331 DWORD WineEngGetGlyphIndices(GdiFont font, LPCWSTR lpstr, INT count,
2332 LPWORD pgi, DWORD flags)
2334 return GDI_ERROR;
2337 DWORD WineEngGetGlyphOutline(GdiFont font, UINT glyph, UINT format,
2338 LPGLYPHMETRICS lpgm, DWORD buflen, LPVOID buf,
2339 const MAT2* lpmat)
2341 ERR("called but we don't have FreeType\n");
2342 return GDI_ERROR;
2345 BOOL WineEngGetTextMetrics(GdiFont font, LPTEXTMETRICW ptm)
2347 ERR("called but we don't have FreeType\n");
2348 return FALSE;
2351 UINT WineEngGetOutlineTextMetrics(GdiFont font, UINT cbSize,
2352 OUTLINETEXTMETRICW *potm)
2354 ERR("called but we don't have FreeType\n");
2355 return 0;
2358 BOOL WineEngGetCharWidth(GdiFont font, UINT firstChar, UINT lastChar,
2359 LPINT buffer)
2361 ERR("called but we don't have FreeType\n");
2362 return FALSE;
2365 BOOL WineEngGetTextExtentPoint(GdiFont font, LPCWSTR wstr, INT count,
2366 LPSIZE size)
2368 ERR("called but we don't have FreeType\n");
2369 return FALSE;
2372 BOOL WineEngGetTextExtentPointI(GdiFont font, const WORD *indices, INT count,
2373 LPSIZE size)
2375 ERR("called but we don't have FreeType\n");
2376 return FALSE;
2379 DWORD WineEngGetFontData(GdiFont font, DWORD table, DWORD offset, LPVOID buf,
2380 DWORD cbData)
2382 ERR("called but we don't have FreeType\n");
2383 return GDI_ERROR;
2386 INT WineEngGetTextFace(GdiFont font, INT count, LPWSTR str)
2388 ERR("called but we don't have FreeType\n");
2389 return 0;
2392 INT WineEngAddFontResourceEx(LPCWSTR file, DWORD flags, PVOID pdv)
2394 FIXME(":stub\n");
2395 return 1;
2398 INT WineEngRemoveFontResourceEx(LPCWSTR file, DWORD flags, PVOID pdv)
2400 FIXME(":stub\n");
2401 return TRUE;
2404 UINT WineEngGetTextCharsetInfo(GdiFont font, LPFONTSIGNATURE fs, DWORD flags)
2406 FIXME(":stub\n");
2407 return DEFAULT_CHARSET;
2410 #endif /* HAVE_FREETYPE */