Move Applications tab before Libraries tab.
[wine.git] / dlls / gdi / freetype.c
blobd4d381247cf84c2cb0f12525b1ccbca9e10905e0
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 &&
359 !(*insertface)->fs.fsCsb[0]; i++) {
360 switch(ft_face->charmaps[i]->encoding) {
361 case ft_encoding_unicode:
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;
1165 if(lf.lfPitchAndFamily & FIXED_PITCH ||
1166 lf.lfPitchAndFamily & FF_MODERN)
1167 strcpyW(lf.lfFaceName, defFixed);
1168 else if(lf.lfPitchAndFamily & FF_ROMAN)
1169 strcpyW(lf.lfFaceName, defSerif);
1170 else if(lf.lfPitchAndFamily & FF_SWISS)
1171 strcpyW(lf.lfFaceName, defSans);
1172 else
1173 strcpyW(lf.lfFaceName, defSans);
1174 for(family = FontList; family; family = family->next) {
1175 if(!strcmpiW(family->FamilyName, lf.lfFaceName) &&
1176 (csi.fs.fsCsb[0] & family->FirstFace->fs.fsCsb[0]))
1177 break;
1181 if(!family) {
1182 for(family = FontList; family; family = family->next) {
1183 if(csi.fs.fsCsb[0] & family->FirstFace->fs.fsCsb[0])
1184 break;
1188 if(!family) {
1189 family = FontList;
1190 csi.fs.fsCsb[0] = 0;
1191 FIXME("just using first face for now\n");
1194 it = lf.lfItalic ? 1 : 0;
1195 bd = lf.lfWeight > 550 ? 1 : 0;
1197 for(face = family->FirstFace; face; face = face->next) {
1198 if(!(face->Italic ^ it) && !(face->Bold ^ bd))
1199 break;
1201 if(!face) {
1202 face = family->FirstFace;
1203 if(it && !face->Italic) ret->fake_italic = TRUE;
1204 if(bd && !face->Bold) ret->fake_bold = TRUE;
1207 memcpy(&ret->fs, &face->fs, sizeof(FONTSIGNATURE));
1209 if(csi.fs.fsCsb[0])
1210 ret->charset = lf.lfCharSet;
1211 else
1212 ret->charset = get_nearest_charset(face);
1214 TRACE("Chosen: %s %s\n", debugstr_w(family->FamilyName),
1215 debugstr_w(face->StyleName));
1217 ret->ft_face = OpenFontFile(ret, face->file, face->face_index,
1218 lf.lfHeight < 0 ?
1219 -abs(INTERNAL_YWSTODS(dc,lf.lfHeight)) :
1220 abs(INTERNAL_YWSTODS(dc, lf.lfHeight)));
1221 if (!ret->ft_face)
1223 free_font( ret );
1224 return 0;
1227 if(ret->charset == SYMBOL_CHARSET)
1228 pFT_Select_Charmap(ret->ft_face, ft_encoding_symbol);
1229 ret->orientation = lf.lfOrientation;
1230 ret->name = strdupW(family->FamilyName);
1232 TRACE("caching: gdiFont=%p hfont=%p\n", ret, hfont);
1233 ret->hfont = hfont;
1234 ret->next = GdiFontList;
1235 GdiFontList = ret;
1237 return ret;
1240 static void DumpGdiFontList(void)
1242 GdiFont gdiFont;
1244 TRACE("---------- gdiFont Cache ----------\n");
1245 for(gdiFont = GdiFontList; gdiFont; gdiFont = gdiFont->next) {
1246 LOGFONTW lf;
1247 GetObjectW( gdiFont->hfont, sizeof(lf), &lf );
1248 TRACE("gdiFont=%p hfont=%p (%s)\n",
1249 gdiFont, gdiFont->hfont, debugstr_w(lf.lfFaceName));
1253 /*************************************************************
1254 * WineEngDestroyFontInstance
1256 * free the gdiFont associated with this handle
1259 BOOL WineEngDestroyFontInstance(HFONT handle)
1261 GdiFont gdiFont;
1262 GdiFont gdiPrev = NULL;
1263 BOOL ret = FALSE;
1265 TRACE("destroying hfont=%p\n", handle);
1266 if(TRACE_ON(font))
1267 DumpGdiFontList();
1269 gdiFont = GdiFontList;
1270 while(gdiFont) {
1271 if(gdiFont->hfont == handle) {
1272 if(gdiPrev) {
1273 gdiPrev->next = gdiFont->next;
1274 free_font(gdiFont);
1275 gdiFont = gdiPrev->next;
1276 } else {
1277 GdiFontList = gdiFont->next;
1278 free_font(gdiFont);
1279 gdiFont = GdiFontList;
1281 ret = TRUE;
1282 } else {
1283 gdiPrev = gdiFont;
1284 gdiFont = gdiFont->next;
1287 return ret;
1290 static void GetEnumStructs(Face *face, LPENUMLOGFONTEXW pelf,
1291 NEWTEXTMETRICEXW *pntm, LPDWORD ptype)
1293 OUTLINETEXTMETRICW *potm;
1294 UINT size;
1295 GdiFont font = alloc_font();
1297 if (!(font->ft_face = OpenFontFile(font, face->file, face->face_index, 100)))
1299 free_font(font);
1300 return;
1303 font->name = strdupW(face->family->FamilyName);
1305 memset(&pelf->elfLogFont, 0, sizeof(LOGFONTW));
1307 size = WineEngGetOutlineTextMetrics(font, 0, NULL);
1308 potm = HeapAlloc(GetProcessHeap(), 0, size);
1309 WineEngGetOutlineTextMetrics(font, size, potm);
1311 #define TM potm->otmTextMetrics
1313 pntm->ntmTm.tmHeight = pelf->elfLogFont.lfHeight = TM.tmHeight;
1314 pntm->ntmTm.tmAscent = TM.tmAscent;
1315 pntm->ntmTm.tmDescent = TM.tmDescent;
1316 pntm->ntmTm.tmInternalLeading = TM.tmInternalLeading;
1317 pntm->ntmTm.tmExternalLeading = TM.tmExternalLeading;
1318 pntm->ntmTm.tmAveCharWidth = pelf->elfLogFont.lfWidth = TM.tmAveCharWidth;
1319 pntm->ntmTm.tmMaxCharWidth = TM.tmMaxCharWidth;
1320 pntm->ntmTm.tmWeight = pelf->elfLogFont.lfWeight = TM.tmWeight;
1321 pntm->ntmTm.tmOverhang = TM.tmOverhang;
1322 pntm->ntmTm.tmDigitizedAspectX = TM.tmDigitizedAspectX;
1323 pntm->ntmTm.tmDigitizedAspectY = TM.tmDigitizedAspectY;
1324 pntm->ntmTm.tmFirstChar = TM.tmFirstChar;
1325 pntm->ntmTm.tmLastChar = TM.tmLastChar;
1326 pntm->ntmTm.tmDefaultChar = TM.tmDefaultChar;
1327 pntm->ntmTm.tmBreakChar = TM.tmBreakChar;
1328 pntm->ntmTm.tmItalic = pelf->elfLogFont.lfItalic = TM.tmItalic;
1329 pntm->ntmTm.tmUnderlined = pelf->elfLogFont.lfUnderline = TM.tmUnderlined;
1330 pntm->ntmTm.tmStruckOut = pelf->elfLogFont.lfStrikeOut = TM.tmStruckOut;
1331 pntm->ntmTm.tmPitchAndFamily = TM.tmPitchAndFamily;
1332 pelf->elfLogFont.lfPitchAndFamily = (TM.tmPitchAndFamily & 0xf1) + 1;
1333 pntm->ntmTm.tmCharSet = pelf->elfLogFont.lfCharSet = TM.tmCharSet;
1334 pelf->elfLogFont.lfOutPrecision = OUT_STROKE_PRECIS;
1335 pelf->elfLogFont.lfClipPrecision = CLIP_STROKE_PRECIS;
1336 pelf->elfLogFont.lfQuality = DRAFT_QUALITY;
1338 pntm->ntmTm.ntmFlags = TM.tmItalic ? NTM_ITALIC : 0;
1339 if(TM.tmWeight > 550) pntm->ntmTm.ntmFlags |= NTM_BOLD;
1340 if(pntm->ntmTm.ntmFlags == 0) pntm->ntmTm.ntmFlags = NTM_REGULAR;
1342 pntm->ntmTm.ntmSizeEM = potm->otmEMSquare;
1343 pntm->ntmTm.ntmCellHeight = 0;
1344 pntm->ntmTm.ntmAvgWidth = 0;
1346 *ptype = TM.tmPitchAndFamily & TMPF_TRUETYPE ? TRUETYPE_FONTTYPE : 0;
1347 if(!(TM.tmPitchAndFamily & TMPF_VECTOR))
1348 *ptype |= RASTER_FONTTYPE;
1350 #undef TM
1351 memset(&pntm->ntmFontSig, 0, sizeof(FONTSIGNATURE));
1353 strncpyW(pelf->elfLogFont.lfFaceName,
1354 (WCHAR*)((char*)potm + (ptrdiff_t)potm->otmpFamilyName),
1355 LF_FACESIZE);
1356 strncpyW(pelf->elfFullName,
1357 (WCHAR*)((char*)potm + (ptrdiff_t)potm->otmpFaceName),
1358 LF_FULLFACESIZE);
1359 strncpyW(pelf->elfStyle,
1360 (WCHAR*)((char*)potm + (ptrdiff_t)potm->otmpStyleName),
1361 LF_FACESIZE);
1362 pelf->elfScript[0] = '\0'; /* This will get set in WineEngEnumFonts */
1364 HeapFree(GetProcessHeap(), 0, potm);
1365 free_font(font);
1366 return;
1369 /*************************************************************
1370 * WineEngEnumFonts
1373 DWORD WineEngEnumFonts(LPLOGFONTW plf, DEVICEFONTENUMPROC proc,
1374 LPARAM lparam)
1376 Family *family;
1377 Face *face;
1378 ENUMLOGFONTEXW elf;
1379 NEWTEXTMETRICEXW ntm;
1380 DWORD type, ret = 1;
1381 FONTSIGNATURE fs;
1382 CHARSETINFO csi;
1383 LOGFONTW lf;
1384 int i;
1386 TRACE("facename = %s charset %d\n", debugstr_w(plf->lfFaceName), plf->lfCharSet);
1388 if(plf->lfFaceName[0]) {
1389 FontSubst *psub;
1390 for(psub = substlist; psub; psub = psub->next)
1391 if(!strcmpiW(plf->lfFaceName, psub->from.name) &&
1392 (psub->from.charset == -1 ||
1393 psub->from.charset == plf->lfCharSet))
1394 break;
1395 if(psub) {
1396 TRACE("substituting %s -> %s\n", debugstr_w(plf->lfFaceName),
1397 debugstr_w(psub->to.name));
1398 memcpy(&lf, plf, sizeof(lf));
1399 strcpyW(lf.lfFaceName, psub->to.name);
1400 plf = &lf;
1402 for(family = FontList; family; family = family->next) {
1403 if(!strcmpiW(plf->lfFaceName, family->FamilyName)) {
1404 for(face = family->FirstFace; face; face = face->next) {
1405 GetEnumStructs(face, &elf, &ntm, &type);
1406 for(i = 0; i < 32; i++) {
1407 if(face->fs.fsCsb[0] & (1L << i)) {
1408 fs.fsCsb[0] = 1L << i;
1409 fs.fsCsb[1] = 0;
1410 if(!TranslateCharsetInfo(fs.fsCsb, &csi,
1411 TCI_SRCFONTSIG))
1412 csi.ciCharset = DEFAULT_CHARSET;
1413 if(i == 31) csi.ciCharset = SYMBOL_CHARSET;
1414 if(csi.ciCharset != DEFAULT_CHARSET) {
1415 elf.elfLogFont.lfCharSet =
1416 ntm.ntmTm.tmCharSet = csi.ciCharset;
1417 if(ElfScriptsW[i])
1418 strcpyW(elf.elfScript, ElfScriptsW[i]);
1419 else
1420 FIXME("Unknown elfscript for bit %d\n", i);
1421 TRACE("enuming face %s full %s style %s charset %d type %ld script %s it %d weight %ld ntmflags %08lx\n",
1422 debugstr_w(elf.elfLogFont.lfFaceName),
1423 debugstr_w(elf.elfFullName), debugstr_w(elf.elfStyle),
1424 csi.ciCharset, type, debugstr_w(elf.elfScript),
1425 elf.elfLogFont.lfItalic, elf.elfLogFont.lfWeight,
1426 ntm.ntmTm.ntmFlags);
1427 ret = proc(&elf, &ntm, type, lparam);
1428 if(!ret) goto end;
1435 } else {
1436 for(family = FontList; family; family = family->next) {
1437 GetEnumStructs(family->FirstFace, &elf, &ntm, &type);
1438 for(i = 0; i < 32; i++) {
1439 if(family->FirstFace->fs.fsCsb[0] & (1L << i)) {
1440 fs.fsCsb[0] = 1L << i;
1441 fs.fsCsb[1] = 0;
1442 if(!TranslateCharsetInfo(fs.fsCsb, &csi,
1443 TCI_SRCFONTSIG))
1444 csi.ciCharset = DEFAULT_CHARSET;
1445 if(i == 31) csi.ciCharset = SYMBOL_CHARSET;
1446 if(csi.ciCharset != DEFAULT_CHARSET) {
1447 elf.elfLogFont.lfCharSet = ntm.ntmTm.tmCharSet =
1448 csi.ciCharset;
1449 if(ElfScriptsW[i])
1450 strcpyW(elf.elfScript, ElfScriptsW[i]);
1451 else
1452 FIXME("Unknown elfscript for bit %d\n", i);
1453 TRACE("enuming face %s full %s style %s charset = %d type %ld script %s it %d weight %ld ntmflags %08lx\n",
1454 debugstr_w(elf.elfLogFont.lfFaceName),
1455 debugstr_w(elf.elfFullName), debugstr_w(elf.elfStyle),
1456 csi.ciCharset, type, debugstr_w(elf.elfScript),
1457 elf.elfLogFont.lfItalic, elf.elfLogFont.lfWeight,
1458 ntm.ntmTm.ntmFlags);
1459 ret = proc(&elf, &ntm, type, lparam);
1460 if(!ret) goto end;
1466 end:
1467 return ret;
1470 static void FTVectorToPOINTFX(FT_Vector *vec, POINTFX *pt)
1472 pt->x.value = vec->x >> 6;
1473 pt->x.fract = (vec->x & 0x3f) << 10;
1474 pt->x.fract |= ((pt->x.fract >> 6) | (pt->x.fract >> 12));
1475 pt->y.value = vec->y >> 6;
1476 pt->y.fract = (vec->y & 0x3f) << 10;
1477 pt->y.fract |= ((pt->y.fract >> 6) | (pt->y.fract >> 12));
1478 return;
1481 static FT_UInt get_glyph_index(GdiFont font, UINT glyph)
1483 if(font->charset == SYMBOL_CHARSET && glyph < 0x100)
1484 glyph = glyph + 0xf000;
1485 return pFT_Get_Char_Index(font->ft_face, glyph);
1488 /*************************************************************
1489 * WineEngGetGlyphIndices
1491 * FIXME: add support for GGI_MARK_NONEXISTING_GLYPHS
1493 DWORD WineEngGetGlyphIndices(GdiFont font, LPCWSTR lpstr, INT count,
1494 LPWORD pgi, DWORD flags)
1496 INT i;
1498 for(i = 0; i < count; i++)
1499 pgi[i] = get_glyph_index(font, lpstr[i]);
1501 return count;
1504 /*************************************************************
1505 * WineEngGetGlyphOutline
1507 * Behaves in exactly the same way as the win32 api GetGlyphOutline
1508 * except that the first parameter is the HWINEENGFONT of the font in
1509 * question rather than an HDC.
1512 DWORD WineEngGetGlyphOutline(GdiFont font, UINT glyph, UINT format,
1513 LPGLYPHMETRICS lpgm, DWORD buflen, LPVOID buf,
1514 const MAT2* lpmat)
1516 FT_Face ft_face = font->ft_face;
1517 FT_UInt glyph_index;
1518 DWORD width, height, pitch, needed = 0;
1519 FT_Bitmap ft_bitmap;
1520 FT_Error err;
1521 INT left, right, top = 0, bottom = 0;
1522 FT_Angle angle = 0;
1523 FT_Int load_flags = FT_LOAD_DEFAULT;
1525 TRACE("%p, %04x, %08x, %p, %08lx, %p, %p\n", font, glyph, format, lpgm,
1526 buflen, buf, lpmat);
1528 if(format & GGO_GLYPH_INDEX) {
1529 glyph_index = glyph;
1530 format &= ~GGO_GLYPH_INDEX;
1531 } else
1532 glyph_index = get_glyph_index(font, glyph);
1534 if(glyph_index >= font->gmsize) {
1535 font->gmsize = (glyph_index / INIT_GM_SIZE + 1) * INIT_GM_SIZE;
1536 font->gm = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, font->gm,
1537 font->gmsize * sizeof(*font->gm));
1538 } else {
1539 if(format == GGO_METRICS && font->gm[glyph_index].init) {
1540 memcpy(lpgm, &font->gm[glyph_index].gm, sizeof(*lpgm));
1541 return 1; /* FIXME */
1545 if(font->orientation || (format != GGO_METRICS && format != GGO_BITMAP))
1546 load_flags |= FT_LOAD_NO_BITMAP;
1548 err = pFT_Load_Glyph(ft_face, glyph_index, load_flags);
1550 if(err) {
1551 FIXME("FT_Load_Glyph on index %x returns %d\n", glyph_index, err);
1552 return GDI_ERROR;
1555 left = ft_face->glyph->metrics.horiBearingX & -64;
1556 right = ((ft_face->glyph->metrics.horiBearingX +
1557 ft_face->glyph->metrics.width) + 63) & -64;
1559 font->gm[glyph_index].adv = (ft_face->glyph->metrics.horiAdvance + 63) >> 6;
1560 font->gm[glyph_index].lsb = left >> 6;
1561 font->gm[glyph_index].bbx = (right - left) >> 6;
1563 if(font->orientation == 0) {
1564 top = (ft_face->glyph->metrics.horiBearingY + 63) & -64;
1565 bottom = (ft_face->glyph->metrics.horiBearingY -
1566 ft_face->glyph->metrics.height) & -64;
1567 lpgm->gmCellIncX = font->gm[glyph_index].adv;
1568 lpgm->gmCellIncY = 0;
1569 } else {
1570 INT xc, yc;
1571 FT_Vector vec;
1572 angle = font->orientation / 10 << 16;
1573 angle |= ((font->orientation % 10) * (1 << 16)) / 10;
1574 TRACE("angle %ld\n", angle >> 16);
1575 for(xc = 0; xc < 2; xc++) {
1576 for(yc = 0; yc < 2; yc++) {
1577 vec.x = ft_face->glyph->metrics.horiBearingX +
1578 xc * ft_face->glyph->metrics.width;
1579 vec.y = ft_face->glyph->metrics.horiBearingY -
1580 yc * ft_face->glyph->metrics.height;
1581 TRACE("Vec %ld,%ld\n", vec.x, vec.y);
1582 pFT_Vector_Rotate(&vec, angle);
1583 if(xc == 0 && yc == 0) {
1584 left = right = vec.x;
1585 top = bottom = vec.y;
1586 } else {
1587 if(vec.x < left) left = vec.x;
1588 else if(vec.x > right) right = vec.x;
1589 if(vec.y < bottom) bottom = vec.y;
1590 else if(vec.y > top) top = vec.y;
1594 left = left & -64;
1595 right = (right + 63) & -64;
1596 bottom = bottom & -64;
1597 top = (top + 63) & -64;
1599 TRACE("transformed box: (%d,%d - %d,%d)\n", left, top, right, bottom);
1600 vec.x = ft_face->glyph->metrics.horiAdvance;
1601 vec.y = 0;
1602 pFT_Vector_Rotate(&vec, angle);
1603 lpgm->gmCellIncX = (vec.x+63) >> 6;
1604 lpgm->gmCellIncY = -(vec.y+63) >> 6;
1606 lpgm->gmBlackBoxX = (right - left) >> 6;
1607 lpgm->gmBlackBoxY = (top - bottom) >> 6;
1608 lpgm->gmptGlyphOrigin.x = left >> 6;
1609 lpgm->gmptGlyphOrigin.y = top >> 6;
1611 memcpy(&font->gm[glyph_index].gm, lpgm, sizeof(*lpgm));
1612 font->gm[glyph_index].init = TRUE;
1614 if(format == GGO_METRICS)
1615 return 1; /* FIXME */
1617 if(ft_face->glyph->format != ft_glyph_format_outline && format != GGO_BITMAP) {
1618 FIXME("loaded a bitmap\n");
1619 return GDI_ERROR;
1622 switch(format) {
1623 case GGO_BITMAP:
1624 width = lpgm->gmBlackBoxX;
1625 height = lpgm->gmBlackBoxY;
1626 pitch = (width + 31) / 32 * 4;
1627 needed = pitch * height;
1629 if(!buf || !buflen) break;
1631 switch(ft_face->glyph->format) {
1632 case ft_glyph_format_bitmap:
1634 BYTE *src = ft_face->glyph->bitmap.buffer, *dst = buf;
1635 INT w = (ft_face->glyph->bitmap.width + 7) >> 3;
1636 INT h = ft_face->glyph->bitmap.rows;
1637 while(h--) {
1638 memcpy(dst, src, w);
1639 src += ft_face->glyph->bitmap.pitch;
1640 dst += pitch;
1642 break;
1645 case ft_glyph_format_outline:
1646 ft_bitmap.width = width;
1647 ft_bitmap.rows = height;
1648 ft_bitmap.pitch = pitch;
1649 ft_bitmap.pixel_mode = ft_pixel_mode_mono;
1650 ft_bitmap.buffer = buf;
1652 if(font->orientation) {
1653 FT_Matrix matrix;
1654 matrix.xx = matrix.yy = pFT_Cos(angle);
1655 matrix.xy = -pFT_Sin(angle);
1656 matrix.yx = -matrix.xy;
1658 pFT_Outline_Transform(&ft_face->glyph->outline, &matrix);
1661 if (lpmat) pFT_Outline_Transform(&ft_face->glyph->outline, (FT_Matrix *)lpmat);
1662 pFT_Outline_Translate(&ft_face->glyph->outline, -left, -bottom );
1664 /* Note: FreeType will only set 'black' bits for us. */
1665 memset(buf, 0, needed);
1666 pFT_Outline_Get_Bitmap(library, &ft_face->glyph->outline, &ft_bitmap);
1667 break;
1669 default:
1670 FIXME("loaded glyph format %x\n", ft_face->glyph->format);
1671 return GDI_ERROR;
1673 break;
1675 case GGO_GRAY2_BITMAP:
1676 case GGO_GRAY4_BITMAP:
1677 case GGO_GRAY8_BITMAP:
1678 case WINE_GGO_GRAY16_BITMAP:
1680 int mult, row, col;
1681 BYTE *start, *ptr;
1683 width = lpgm->gmBlackBoxX;
1684 height = lpgm->gmBlackBoxY;
1685 pitch = (width + 3) / 4 * 4;
1686 needed = pitch * height;
1688 if(!buf || !buflen) break;
1689 ft_bitmap.width = width;
1690 ft_bitmap.rows = height;
1691 ft_bitmap.pitch = pitch;
1692 ft_bitmap.pixel_mode = ft_pixel_mode_grays;
1693 ft_bitmap.buffer = buf;
1695 if(font->orientation) {
1696 FT_Matrix matrix;
1697 matrix.xx = matrix.yy = pFT_Cos(angle);
1698 matrix.xy = -pFT_Sin(angle);
1699 matrix.yx = -matrix.xy;
1700 pFT_Outline_Transform(&ft_face->glyph->outline, &matrix);
1703 if (lpmat) pFT_Outline_Transform(&ft_face->glyph->outline, (FT_Matrix *)lpmat);
1704 pFT_Outline_Translate(&ft_face->glyph->outline, -left, -bottom );
1706 pFT_Outline_Get_Bitmap(library, &ft_face->glyph->outline, &ft_bitmap);
1708 if(format == GGO_GRAY2_BITMAP)
1709 mult = 5;
1710 else if(format == GGO_GRAY4_BITMAP)
1711 mult = 17;
1712 else if(format == GGO_GRAY8_BITMAP)
1713 mult = 65;
1714 else if(format == WINE_GGO_GRAY16_BITMAP)
1715 break;
1716 else {
1717 assert(0);
1718 break;
1721 start = buf;
1722 for(row = 0; row < height; row++) {
1723 ptr = start;
1724 for(col = 0; col < width; col++, ptr++) {
1725 *ptr = (*(unsigned int*)ptr * mult + 128) / 256;
1727 start += pitch;
1729 break;
1732 case GGO_NATIVE:
1734 int contour, point = 0, first_pt;
1735 FT_Outline *outline = &ft_face->glyph->outline;
1736 TTPOLYGONHEADER *pph;
1737 TTPOLYCURVE *ppc;
1738 DWORD pph_start, cpfx, type;
1740 if(buflen == 0) buf = NULL;
1742 if (lpmat) pFT_Outline_Transform(outline, (FT_Matrix *)lpmat);
1744 for(contour = 0; contour < outline->n_contours; contour++) {
1745 pph_start = needed;
1746 pph = (TTPOLYGONHEADER *)((char *)buf + needed);
1747 first_pt = point;
1748 if(buf) {
1749 pph->dwType = TT_POLYGON_TYPE;
1750 FTVectorToPOINTFX(&outline->points[point], &pph->pfxStart);
1752 needed += sizeof(*pph);
1753 point++;
1754 while(point <= outline->contours[contour]) {
1755 ppc = (TTPOLYCURVE *)((char *)buf + needed);
1756 type = (outline->tags[point] & FT_Curve_Tag_On) ?
1757 TT_PRIM_LINE : TT_PRIM_QSPLINE;
1758 cpfx = 0;
1759 do {
1760 if(buf)
1761 FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
1762 cpfx++;
1763 point++;
1764 } while(point <= outline->contours[contour] &&
1765 (outline->tags[point] & FT_Curve_Tag_On) ==
1766 (outline->tags[point-1] & FT_Curve_Tag_On));
1767 /* At the end of a contour Windows adds the start point, but
1768 only for Beziers */
1769 if(point > outline->contours[contour] &&
1770 !(outline->tags[point-1] & FT_Curve_Tag_On)) {
1771 if(buf)
1772 FTVectorToPOINTFX(&outline->points[first_pt], &ppc->apfx[cpfx]);
1773 cpfx++;
1774 } else if(point <= outline->contours[contour] &&
1775 outline->tags[point] & FT_Curve_Tag_On) {
1776 /* add closing pt for bezier */
1777 if(buf)
1778 FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
1779 cpfx++;
1780 point++;
1782 if(buf) {
1783 ppc->wType = type;
1784 ppc->cpfx = cpfx;
1786 needed += sizeof(*ppc) + (cpfx - 1) * sizeof(POINTFX);
1788 if(buf)
1789 pph->cb = needed - pph_start;
1791 break;
1793 case GGO_BEZIER:
1795 /* Convert the quadratic Beziers to cubic Beziers.
1796 The parametric eqn for a cubic Bezier is, from PLRM:
1797 r(t) = at^3 + bt^2 + ct + r0
1798 with the control points:
1799 r1 = r0 + c/3
1800 r2 = r1 + (c + b)/3
1801 r3 = r0 + c + b + a
1803 A quadratic Beizer has the form:
1804 p(t) = (1-t)^2 p0 + 2(1-t)t p1 + t^2 p2
1806 So equating powers of t leads to:
1807 r1 = 2/3 p1 + 1/3 p0
1808 r2 = 2/3 p1 + 1/3 p2
1809 and of course r0 = p0, r3 = p2
1812 int contour, point = 0, first_pt;
1813 FT_Outline *outline = &ft_face->glyph->outline;
1814 TTPOLYGONHEADER *pph;
1815 TTPOLYCURVE *ppc;
1816 DWORD pph_start, cpfx, type;
1817 FT_Vector cubic_control[4];
1818 if(buflen == 0) buf = NULL;
1820 if (lpmat) pFT_Outline_Transform(outline, (FT_Matrix *)lpmat);
1822 for(contour = 0; contour < outline->n_contours; contour++) {
1823 pph_start = needed;
1824 pph = (TTPOLYGONHEADER *)((char *)buf + needed);
1825 first_pt = point;
1826 if(buf) {
1827 pph->dwType = TT_POLYGON_TYPE;
1828 FTVectorToPOINTFX(&outline->points[point], &pph->pfxStart);
1830 needed += sizeof(*pph);
1831 point++;
1832 while(point <= outline->contours[contour]) {
1833 ppc = (TTPOLYCURVE *)((char *)buf + needed);
1834 type = (outline->tags[point] & FT_Curve_Tag_On) ?
1835 TT_PRIM_LINE : TT_PRIM_CSPLINE;
1836 cpfx = 0;
1837 do {
1838 if(type == TT_PRIM_LINE) {
1839 if(buf)
1840 FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
1841 cpfx++;
1842 point++;
1843 } else {
1844 /* Unlike QSPLINEs, CSPLINEs always have their endpoint
1845 so cpfx = 3n */
1847 /* FIXME: Possible optimization in endpoint calculation
1848 if there are two consecutive curves */
1849 cubic_control[0] = outline->points[point-1];
1850 if(!(outline->tags[point-1] & FT_Curve_Tag_On)) {
1851 cubic_control[0].x += outline->points[point].x + 1;
1852 cubic_control[0].y += outline->points[point].y + 1;
1853 cubic_control[0].x >>= 1;
1854 cubic_control[0].y >>= 1;
1856 if(point+1 > outline->contours[contour])
1857 cubic_control[3] = outline->points[first_pt];
1858 else {
1859 cubic_control[3] = outline->points[point+1];
1860 if(!(outline->tags[point+1] & FT_Curve_Tag_On)) {
1861 cubic_control[3].x += outline->points[point].x + 1;
1862 cubic_control[3].y += outline->points[point].y + 1;
1863 cubic_control[3].x >>= 1;
1864 cubic_control[3].y >>= 1;
1867 /* r1 = 1/3 p0 + 2/3 p1
1868 r2 = 1/3 p2 + 2/3 p1 */
1869 cubic_control[1].x = (2 * outline->points[point].x + 1) / 3;
1870 cubic_control[1].y = (2 * outline->points[point].y + 1) / 3;
1871 cubic_control[2] = cubic_control[1];
1872 cubic_control[1].x += (cubic_control[0].x + 1) / 3;
1873 cubic_control[1].y += (cubic_control[0].y + 1) / 3;
1874 cubic_control[2].x += (cubic_control[3].x + 1) / 3;
1875 cubic_control[2].y += (cubic_control[3].y + 1) / 3;
1876 if(buf) {
1877 FTVectorToPOINTFX(&cubic_control[1], &ppc->apfx[cpfx]);
1878 FTVectorToPOINTFX(&cubic_control[2], &ppc->apfx[cpfx+1]);
1879 FTVectorToPOINTFX(&cubic_control[3], &ppc->apfx[cpfx+2]);
1881 cpfx += 3;
1882 point++;
1884 } while(point <= outline->contours[contour] &&
1885 (outline->tags[point] & FT_Curve_Tag_On) ==
1886 (outline->tags[point-1] & FT_Curve_Tag_On));
1887 /* At the end of a contour Windows adds the start point,
1888 but only for Beziers and we've already done that.
1890 if(point <= outline->contours[contour] &&
1891 outline->tags[point] & FT_Curve_Tag_On) {
1892 /* This is the closing pt of a bezier, but we've already
1893 added it, so just inc point and carry on */
1894 point++;
1896 if(buf) {
1897 ppc->wType = type;
1898 ppc->cpfx = cpfx;
1900 needed += sizeof(*ppc) + (cpfx - 1) * sizeof(POINTFX);
1902 if(buf)
1903 pph->cb = needed - pph_start;
1905 break;
1908 default:
1909 FIXME("Unsupported format %d\n", format);
1910 return GDI_ERROR;
1912 return needed;
1915 /*************************************************************
1916 * WineEngGetTextMetrics
1919 BOOL WineEngGetTextMetrics(GdiFont font, LPTEXTMETRICW ptm)
1921 if(!font->potm) {
1922 if(!WineEngGetOutlineTextMetrics(font, 0, NULL))
1923 return FALSE;
1925 if(!font->potm) return FALSE;
1926 memcpy(ptm, &font->potm->otmTextMetrics, sizeof(*ptm));
1927 return TRUE;
1931 /*************************************************************
1932 * WineEngGetOutlineTextMetrics
1935 UINT WineEngGetOutlineTextMetrics(GdiFont font, UINT cbSize,
1936 OUTLINETEXTMETRICW *potm)
1938 FT_Face ft_face = font->ft_face;
1939 UINT needed, lenfam, lensty, ret;
1940 TT_OS2 *pOS2;
1941 TT_HoriHeader *pHori;
1942 TT_Postscript *pPost;
1943 FT_Fixed x_scale, y_scale;
1944 WCHAR *family_nameW, *style_nameW;
1945 WCHAR spaceW[] = {' ', '\0'};
1946 char *cp;
1948 TRACE("font=%p\n", font);
1950 if(font->potm) {
1951 if(cbSize >= font->potm->otmSize)
1952 memcpy(potm, font->potm, font->potm->otmSize);
1953 return font->potm->otmSize;
1956 needed = sizeof(*potm);
1958 lenfam = (strlenW(font->name) + 1) * sizeof(WCHAR);
1959 family_nameW = strdupW(font->name);
1961 lensty = MultiByteToWideChar(CP_ACP, 0, ft_face->style_name, -1, NULL, 0)
1962 * sizeof(WCHAR);
1963 style_nameW = HeapAlloc(GetProcessHeap(), 0, lensty);
1964 MultiByteToWideChar(CP_ACP, 0, ft_face->style_name, -1,
1965 style_nameW, lensty);
1967 /* These names should be read from the TT name table */
1969 /* length of otmpFamilyName */
1970 needed += lenfam;
1972 /* length of otmpFaceName */
1973 if(!strcasecmp(ft_face->style_name, "regular")) {
1974 needed += lenfam; /* just the family name */
1975 } else {
1976 needed += lenfam + lensty; /* family + " " + style */
1979 /* length of otmpStyleName */
1980 needed += lensty;
1982 /* length of otmpFullName */
1983 needed += lenfam + lensty;
1986 x_scale = ft_face->size->metrics.x_scale;
1987 y_scale = ft_face->size->metrics.y_scale;
1989 pOS2 = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_os2);
1990 if(!pOS2) {
1991 FIXME("Can't find OS/2 table - not TT font?\n");
1992 ret = 0;
1993 goto end;
1996 pHori = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_hhea);
1997 if(!pHori) {
1998 FIXME("Can't find HHEA table - not TT font?\n");
1999 ret = 0;
2000 goto end;
2003 pPost = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_post); /* we can live with this failing */
2005 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",
2006 pOS2->usWinAscent, pOS2->usWinDescent,
2007 pOS2->sTypoAscender, pOS2->sTypoDescender, pOS2->sTypoLineGap,
2008 ft_face->ascender, ft_face->descender, ft_face->height,
2009 pHori->Ascender, pHori->Descender, pHori->Line_Gap,
2010 ft_face->bbox.yMax, ft_face->bbox.yMin);
2012 font->potm = HeapAlloc(GetProcessHeap(), 0, needed);
2013 font->potm->otmSize = needed;
2015 #define TM font->potm->otmTextMetrics
2017 if(font->yMax) {
2018 TM.tmAscent = font->yMax;
2019 TM.tmDescent = -font->yMin;
2020 TM.tmInternalLeading = (TM.tmAscent + TM.tmDescent) - ft_face->size->metrics.y_ppem;
2021 } else {
2022 TM.tmAscent = (pFT_MulFix(pOS2->usWinAscent, y_scale) + 32) >> 6;
2023 TM.tmDescent = (pFT_MulFix(pOS2->usWinDescent, y_scale) + 32) >> 6;
2024 TM.tmInternalLeading = (pFT_MulFix(pOS2->usWinAscent + pOS2->usWinDescent
2025 - ft_face->units_per_EM, y_scale) + 32) >> 6;
2028 TM.tmHeight = TM.tmAscent + TM.tmDescent;
2030 /* MSDN says:
2031 el = MAX(0, LineGap - ((WinAscent + WinDescent) - (Ascender - Descender)))
2033 TM.tmExternalLeading = max(0, (pFT_MulFix(pHori->Line_Gap -
2034 ((pOS2->usWinAscent + pOS2->usWinDescent) -
2035 (pHori->Ascender - pHori->Descender)), y_scale) + 32) >> 6);
2037 TM.tmAveCharWidth = (pFT_MulFix(pOS2->xAvgCharWidth, x_scale) + 32) >> 6;
2038 TM.tmMaxCharWidth = (pFT_MulFix(ft_face->bbox.xMax - ft_face->bbox.xMin, x_scale) + 32) >> 6;
2039 TM.tmWeight = font->fake_bold ? FW_BOLD : pOS2->usWeightClass;
2040 TM.tmOverhang = 0;
2041 TM.tmDigitizedAspectX = 300;
2042 TM.tmDigitizedAspectY = 300;
2043 TM.tmFirstChar = pOS2->usFirstCharIndex;
2044 TM.tmLastChar = pOS2->usLastCharIndex;
2045 TM.tmDefaultChar = pOS2->usDefaultChar;
2046 TM.tmBreakChar = pOS2->usBreakChar ? pOS2->usBreakChar : ' ';
2047 TM.tmItalic = font->fake_italic ? 255 : ((ft_face->style_flags & FT_STYLE_FLAG_ITALIC) ? 255 : 0);
2048 TM.tmUnderlined = 0; /* entry in OS2 table */
2049 TM.tmStruckOut = 0; /* entry in OS2 table */
2051 /* Yes TPMF_FIXED_PITCH is correct; braindead api */
2052 if(!FT_IS_FIXED_WIDTH(ft_face))
2053 TM.tmPitchAndFamily = TMPF_FIXED_PITCH;
2054 else
2055 TM.tmPitchAndFamily = 0;
2057 switch(pOS2->panose[PAN_FAMILYTYPE_INDEX]) {
2058 case PAN_FAMILY_SCRIPT:
2059 TM.tmPitchAndFamily |= FF_SCRIPT;
2060 break;
2061 case PAN_FAMILY_DECORATIVE:
2062 case PAN_FAMILY_PICTORIAL:
2063 TM.tmPitchAndFamily |= FF_DECORATIVE;
2064 break;
2065 case PAN_FAMILY_TEXT_DISPLAY:
2066 if(TM.tmPitchAndFamily == 0) /* fixed */
2067 TM.tmPitchAndFamily = FF_MODERN;
2068 else {
2069 switch(pOS2->panose[PAN_SERIFSTYLE_INDEX]) {
2070 case PAN_SERIF_NORMAL_SANS:
2071 case PAN_SERIF_OBTUSE_SANS:
2072 case PAN_SERIF_PERP_SANS:
2073 TM.tmPitchAndFamily |= FF_SWISS;
2074 break;
2075 default:
2076 TM.tmPitchAndFamily |= FF_ROMAN;
2079 break;
2080 default:
2081 TM.tmPitchAndFamily |= FF_DONTCARE;
2084 if(FT_IS_SCALABLE(ft_face))
2085 TM.tmPitchAndFamily |= TMPF_VECTOR;
2086 if(FT_IS_SFNT(ft_face))
2087 TM.tmPitchAndFamily |= TMPF_TRUETYPE;
2089 TM.tmCharSet = font->charset;
2090 #undef TM
2092 font->potm->otmFiller = 0;
2093 memcpy(&font->potm->otmPanoseNumber, pOS2->panose, PANOSE_COUNT);
2094 font->potm->otmfsSelection = pOS2->fsSelection;
2095 font->potm->otmfsType = pOS2->fsType;
2096 font->potm->otmsCharSlopeRise = pHori->caret_Slope_Rise;
2097 font->potm->otmsCharSlopeRun = pHori->caret_Slope_Run;
2098 font->potm->otmItalicAngle = 0; /* POST table */
2099 font->potm->otmEMSquare = ft_face->units_per_EM;
2100 font->potm->otmAscent = (pFT_MulFix(pOS2->sTypoAscender, y_scale) + 32) >> 6;
2101 font->potm->otmDescent = (pFT_MulFix(pOS2->sTypoDescender, y_scale) + 32) >> 6;
2102 font->potm->otmLineGap = (pFT_MulFix(pOS2->sTypoLineGap, y_scale) + 32) >> 6;
2103 font->potm->otmsCapEmHeight = (pFT_MulFix(pOS2->sCapHeight, y_scale) + 32) >> 6;
2104 font->potm->otmsXHeight = (pFT_MulFix(pOS2->sxHeight, y_scale) + 32) >> 6;
2105 font->potm->otmrcFontBox.left = ft_face->bbox.xMin;
2106 font->potm->otmrcFontBox.right = ft_face->bbox.xMax;
2107 font->potm->otmrcFontBox.top = ft_face->bbox.yMin;
2108 font->potm->otmrcFontBox.bottom = ft_face->bbox.yMax;
2109 font->potm->otmMacAscent = 0; /* where do these come from ? */
2110 font->potm->otmMacDescent = 0;
2111 font->potm->otmMacLineGap = 0;
2112 font->potm->otmusMinimumPPEM = 0; /* TT Header */
2113 font->potm->otmptSubscriptSize.x = (pFT_MulFix(pOS2->ySubscriptXSize, x_scale) + 32) >> 6;
2114 font->potm->otmptSubscriptSize.y = (pFT_MulFix(pOS2->ySubscriptYSize, y_scale) + 32) >> 6;
2115 font->potm->otmptSubscriptOffset.x = (pFT_MulFix(pOS2->ySubscriptXOffset, x_scale) + 32) >> 6;
2116 font->potm->otmptSubscriptOffset.y = (pFT_MulFix(pOS2->ySubscriptYOffset, y_scale) + 32) >> 6;
2117 font->potm->otmptSuperscriptSize.x = (pFT_MulFix(pOS2->ySuperscriptXSize, x_scale) + 32) >> 6;
2118 font->potm->otmptSuperscriptSize.y = (pFT_MulFix(pOS2->ySuperscriptYSize, y_scale) + 32) >> 6;
2119 font->potm->otmptSuperscriptOffset.x = (pFT_MulFix(pOS2->ySuperscriptXOffset, x_scale) + 32) >> 6;
2120 font->potm->otmptSuperscriptOffset.y = (pFT_MulFix(pOS2->ySuperscriptYOffset, y_scale) + 32) >> 6;
2121 font->potm->otmsStrikeoutSize = (pFT_MulFix(pOS2->yStrikeoutSize, y_scale) + 32) >> 6;
2122 font->potm->otmsStrikeoutPosition = (pFT_MulFix(pOS2->yStrikeoutPosition, y_scale) + 32) >> 6;
2123 if(!pPost) {
2124 font->potm->otmsUnderscoreSize = 0;
2125 font->potm->otmsUnderscorePosition = 0;
2126 } else {
2127 font->potm->otmsUnderscoreSize = (pFT_MulFix(pPost->underlineThickness, y_scale) + 32) >> 6;
2128 font->potm->otmsUnderscorePosition = (pFT_MulFix(pPost->underlinePosition, y_scale) + 32) >> 6;
2131 /* otmp* members should clearly have type ptrdiff_t, but M$ knows best */
2132 cp = (char*)font->potm + sizeof(*font->potm);
2133 font->potm->otmpFamilyName = (LPSTR)(cp - (char*)font->potm);
2134 strcpyW((WCHAR*)cp, family_nameW);
2135 cp += lenfam;
2136 font->potm->otmpStyleName = (LPSTR)(cp - (char*)font->potm);
2137 strcpyW((WCHAR*)cp, style_nameW);
2138 cp += lensty;
2139 font->potm->otmpFaceName = (LPSTR)(cp - (char*)font->potm);
2140 strcpyW((WCHAR*)cp, family_nameW);
2141 if(strcasecmp(ft_face->style_name, "regular")) {
2142 strcatW((WCHAR*)cp, spaceW);
2143 strcatW((WCHAR*)cp, style_nameW);
2144 cp += lenfam + lensty;
2145 } else
2146 cp += lenfam;
2147 font->potm->otmpFullName = (LPSTR)(cp - (char*)font->potm);
2148 strcpyW((WCHAR*)cp, family_nameW);
2149 strcatW((WCHAR*)cp, spaceW);
2150 strcatW((WCHAR*)cp, style_nameW);
2151 ret = needed;
2153 if(needed <= cbSize)
2154 memcpy(potm, font->potm, font->potm->otmSize);
2156 end:
2157 HeapFree(GetProcessHeap(), 0, style_nameW);
2158 HeapFree(GetProcessHeap(), 0, family_nameW);
2160 return ret;
2164 /*************************************************************
2165 * WineEngGetCharWidth
2168 BOOL WineEngGetCharWidth(GdiFont font, UINT firstChar, UINT lastChar,
2169 LPINT buffer)
2171 UINT c;
2172 GLYPHMETRICS gm;
2173 FT_UInt glyph_index;
2175 TRACE("%p, %d, %d, %p\n", font, firstChar, lastChar, buffer);
2177 for(c = firstChar; c <= lastChar; c++) {
2178 glyph_index = get_glyph_index(font, c);
2179 WineEngGetGlyphOutline(font, glyph_index, GGO_METRICS | GGO_GLYPH_INDEX,
2180 &gm, 0, NULL, NULL);
2181 buffer[c - firstChar] = font->gm[glyph_index].adv;
2183 return TRUE;
2186 /*************************************************************
2187 * WineEngGetTextExtentPoint
2190 BOOL WineEngGetTextExtentPoint(GdiFont font, LPCWSTR wstr, INT count,
2191 LPSIZE size)
2193 INT idx;
2194 GLYPHMETRICS gm;
2195 TEXTMETRICW tm;
2196 FT_UInt glyph_index;
2198 TRACE("%p, %s, %d, %p\n", font, debugstr_wn(wstr, count), count,
2199 size);
2201 size->cx = 0;
2202 WineEngGetTextMetrics(font, &tm);
2203 size->cy = tm.tmHeight;
2205 for(idx = 0; idx < count; idx++) {
2206 glyph_index = get_glyph_index(font, wstr[idx]);
2207 WineEngGetGlyphOutline(font, glyph_index, GGO_METRICS | GGO_GLYPH_INDEX,
2208 &gm, 0, NULL, NULL);
2209 size->cx += font->gm[glyph_index].adv;
2211 TRACE("return %ld,%ld\n", size->cx, size->cy);
2212 return TRUE;
2215 /*************************************************************
2216 * WineEngGetTextExtentPointI
2219 BOOL WineEngGetTextExtentPointI(GdiFont font, const WORD *indices, INT count,
2220 LPSIZE size)
2222 INT idx;
2223 GLYPHMETRICS gm;
2224 TEXTMETRICW tm;
2226 TRACE("%p, %p, %d, %p\n", font, indices, count, size);
2228 size->cx = 0;
2229 WineEngGetTextMetrics(font, &tm);
2230 size->cy = tm.tmHeight;
2232 for(idx = 0; idx < count; idx++) {
2233 WineEngGetGlyphOutline(font, indices[idx],
2234 GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL,
2235 NULL);
2236 size->cx += font->gm[indices[idx]].adv;
2238 TRACE("return %ld,%ld\n", size->cx, size->cy);
2239 return TRUE;
2242 /*************************************************************
2243 * WineEngGetFontData
2246 DWORD WineEngGetFontData(GdiFont font, DWORD table, DWORD offset, LPVOID buf,
2247 DWORD cbData)
2249 FT_Face ft_face = font->ft_face;
2250 DWORD len;
2251 FT_Error err;
2253 TRACE("font=%p, table=%08lx, offset=%08lx, buf=%p, cbData=%lx\n",
2254 font, table, offset, buf, cbData);
2256 if(!FT_IS_SFNT(ft_face))
2257 return GDI_ERROR;
2259 if(!buf || !cbData)
2260 len = 0;
2261 else
2262 len = cbData;
2264 if(table) { /* MS tags differ in endidness from FT ones */
2265 table = table >> 24 | table << 24 |
2266 (table >> 8 & 0xff00) | (table << 8 & 0xff0000);
2269 /* If the FT_Load_Sfnt_Table function is there we'll use it */
2270 if(pFT_Load_Sfnt_Table)
2271 err = pFT_Load_Sfnt_Table(ft_face, table, offset, buf, &len);
2272 else { /* Do it the hard way */
2273 TT_Face tt_face = (TT_Face) ft_face;
2274 SFNT_Interface *sfnt;
2275 if (FT_Version.major==2 && FT_Version.minor==0)
2277 /* 2.0.x */
2278 sfnt = *(SFNT_Interface**)((char*)tt_face + 528);
2280 else
2282 /* A field was added in the middle of the structure in 2.1.x */
2283 sfnt = *(SFNT_Interface**)((char*)tt_face + 532);
2285 err = sfnt->load_any(tt_face, table, offset, buf, &len);
2287 if(err) {
2288 TRACE("Can't find table %08lx.\n", table);
2289 return GDI_ERROR;
2291 return len;
2294 /*************************************************************
2295 * WineEngGetTextFace
2298 INT WineEngGetTextFace(GdiFont font, INT count, LPWSTR str)
2300 if(str) {
2301 lstrcpynW(str, font->name, count);
2302 return strlenW(font->name);
2303 } else
2304 return strlenW(font->name) + 1;
2307 UINT WineEngGetTextCharsetInfo(GdiFont font, LPFONTSIGNATURE fs, DWORD flags)
2309 if (fs) memcpy(fs, &font->fs, sizeof(FONTSIGNATURE));
2310 return font->charset;
2313 #else /* HAVE_FREETYPE */
2315 BOOL WineEngInit(void)
2317 return FALSE;
2319 GdiFont WineEngCreateFontInstance(DC *dc, HFONT hfont)
2321 return NULL;
2323 BOOL WineEngDestroyFontInstance(HFONT hfont)
2325 return FALSE;
2328 DWORD WineEngEnumFonts(LPLOGFONTW plf, DEVICEFONTENUMPROC proc, LPARAM lparam)
2330 return 1;
2333 DWORD WineEngGetGlyphIndices(GdiFont font, LPCWSTR lpstr, INT count,
2334 LPWORD pgi, DWORD flags)
2336 return GDI_ERROR;
2339 DWORD WineEngGetGlyphOutline(GdiFont font, UINT glyph, UINT format,
2340 LPGLYPHMETRICS lpgm, DWORD buflen, LPVOID buf,
2341 const MAT2* lpmat)
2343 ERR("called but we don't have FreeType\n");
2344 return GDI_ERROR;
2347 BOOL WineEngGetTextMetrics(GdiFont font, LPTEXTMETRICW ptm)
2349 ERR("called but we don't have FreeType\n");
2350 return FALSE;
2353 UINT WineEngGetOutlineTextMetrics(GdiFont font, UINT cbSize,
2354 OUTLINETEXTMETRICW *potm)
2356 ERR("called but we don't have FreeType\n");
2357 return 0;
2360 BOOL WineEngGetCharWidth(GdiFont font, UINT firstChar, UINT lastChar,
2361 LPINT buffer)
2363 ERR("called but we don't have FreeType\n");
2364 return FALSE;
2367 BOOL WineEngGetTextExtentPoint(GdiFont font, LPCWSTR wstr, INT count,
2368 LPSIZE size)
2370 ERR("called but we don't have FreeType\n");
2371 return FALSE;
2374 BOOL WineEngGetTextExtentPointI(GdiFont font, const WORD *indices, INT count,
2375 LPSIZE size)
2377 ERR("called but we don't have FreeType\n");
2378 return FALSE;
2381 DWORD WineEngGetFontData(GdiFont font, DWORD table, DWORD offset, LPVOID buf,
2382 DWORD cbData)
2384 ERR("called but we don't have FreeType\n");
2385 return GDI_ERROR;
2388 INT WineEngGetTextFace(GdiFont font, INT count, LPWSTR str)
2390 ERR("called but we don't have FreeType\n");
2391 return 0;
2394 INT WineEngAddFontResourceEx(LPCWSTR file, DWORD flags, PVOID pdv)
2396 FIXME(":stub\n");
2397 return 1;
2400 INT WineEngRemoveFontResourceEx(LPCWSTR file, DWORD flags, PVOID pdv)
2402 FIXME(":stub\n");
2403 return TRUE;
2406 UINT WineEngGetTextCharsetInfo(GdiFont font, LPFONTSIGNATURE fs, DWORD flags)
2408 FIXME(":stub\n");
2409 return DEFAULT_CHARSET;
2412 #endif /* HAVE_FREETYPE */