Removed trailing whitespace.
[wine/wine-gecko.git] / dlls / gdi / freetype.c
blob016384a3432bd03ef11c11e1e229494e3e0974f1
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"
34 #include "font.h"
36 #include <stdlib.h>
37 #include <sys/stat.h>
38 #include <string.h>
39 #include <dirent.h>
40 #include <stdio.h>
41 #include <assert.h>
43 WINE_DEFAULT_DEBUG_CHANNEL(font);
45 #ifdef HAVE_FREETYPE
47 #ifdef HAVE_FREETYPE_FREETYPE_H
48 #include <freetype/freetype.h>
49 #endif
50 #ifdef HAVE_FREETYPE_FTGLYPH_H
51 #include <freetype/ftglyph.h>
52 #endif
53 #ifdef HAVE_FREETYPE_TTTABLES_H
54 #include <freetype/tttables.h>
55 #endif
56 #ifdef HAVE_FREETYPE_FTSNAMES_H
57 #include <freetype/ftsnames.h>
58 #else
59 # ifdef HAVE_FREETYPE_FTNAMES_H
60 # include <freetype/ftnames.h>
61 # endif
62 #endif
63 #ifdef HAVE_FREETYPE_TTNAMEID_H
64 #include <freetype/ttnameid.h>
65 #endif
66 #ifdef HAVE_FREETYPE_FTOUTLN_H
67 #include <freetype/ftoutln.h>
68 #endif
69 #ifdef HAVE_FREETYPE_INTERNAL_SFNT_H
70 #include <freetype/internal/sfnt.h>
71 #endif
72 #ifdef HAVE_FREETYPE_FTTRIGON_H
73 #include <freetype/fttrigon.h>
74 #endif
76 #ifndef SONAME_LIBFREETYPE
77 #define SONAME_LIBFREETYPE "libfreetype.so"
78 #endif
80 static FT_Library library = 0;
82 static void *ft_handle = NULL;
84 #define MAKE_FUNCPTR(f) static typeof(f) * p##f = NULL;
85 MAKE_FUNCPTR(FT_Cos)
86 MAKE_FUNCPTR(FT_Done_Face)
87 MAKE_FUNCPTR(FT_Get_Char_Index)
88 MAKE_FUNCPTR(FT_Get_Sfnt_Table)
89 MAKE_FUNCPTR(FT_Init_FreeType)
90 MAKE_FUNCPTR(FT_Load_Glyph)
91 MAKE_FUNCPTR(FT_MulFix)
92 MAKE_FUNCPTR(FT_New_Face)
93 MAKE_FUNCPTR(FT_Outline_Get_Bitmap)
94 MAKE_FUNCPTR(FT_Outline_Transform)
95 MAKE_FUNCPTR(FT_Outline_Translate)
96 MAKE_FUNCPTR(FT_Select_Charmap)
97 MAKE_FUNCPTR(FT_Set_Pixel_Sizes)
98 MAKE_FUNCPTR(FT_Sin)
99 MAKE_FUNCPTR(FT_Vector_Rotate)
100 #undef MAKE_FUNCPTR
102 #define GET_BE_WORD(ptr) MAKEWORD( ((BYTE *)(ptr))[1], ((BYTE *)(ptr))[0] )
104 typedef struct tagFace {
105 WCHAR *StyleName;
106 char *file;
107 BOOL Italic;
108 BOOL Bold;
109 DWORD fsCsb[2]; /* codepage bitfield from FONTSIGNATURE */
110 struct tagFace *next;
111 } Face;
113 typedef struct tagFamily {
114 WCHAR *FamilyName;
115 Face *FirstFace;
116 struct tagFamily *next;
117 } Family;
119 typedef struct {
120 GLYPHMETRICS gm;
121 INT adv; /* These three hold to widths of the unrotated chars */
122 INT lsb;
123 INT bbx;
124 BOOL init;
125 } GM;
127 struct tagGdiFont {
128 FT_Face ft_face;
129 int charset;
130 BOOL fake_italic;
131 BOOL fake_bold;
132 INT orientation;
133 GM *gm;
134 DWORD gmsize;
135 HFONT hfont;
136 SHORT yMax;
137 SHORT yMin;
138 struct tagGdiFont *next;
141 #define INIT_GM_SIZE 128
143 static GdiFont GdiFontList = NULL;
145 static Family *FontList = NULL;
147 static WCHAR defSerif[] = {'T','i','m','e','s',' ','N','e','w',' ',
148 'R','o','m','a','n','\0'};
149 static WCHAR defSans[] = {'A','r','i','a','l','\0'};
150 static WCHAR defFixed[] = {'C','o','u','r','i','e','r',' ','N','e','w','\0'};
152 static WCHAR defSystem[] = {'A','r','i','a','l','\0'};
153 static WCHAR SystemW[] = {'S','y','s','t','e','m','\0'};
154 static WCHAR MSSansSerifW[] = {'M','S',' ','S','a','n','s',' ',
155 'S','e','r','i','f','\0'};
156 static WCHAR HelvW[] = {'H','e','l','v','\0'};
158 static WCHAR ArabicW[] = {'A','r','a','b','i','c','\0'};
159 static WCHAR BalticW[] = {'B','a','l','t','i','c','\0'};
160 static WCHAR Central_EuropeanW[] = {'C','e','n','t','r','a','l',' ',
161 'E','u','r','o','p','e','a','n','\0'};
162 static WCHAR CyrillicW[] = {'C','y','r','i','l','l','i','c','\0'};
163 static WCHAR GreekW[] = {'G','r','e','e','k','\0'};
164 static WCHAR HebrewW[] = {'H','e','b','r','e','w','\0'};
165 static WCHAR SymbolW[] = {'S','y','m','b','o','l','\0'};
166 static WCHAR ThaiW[] = {'T','h','a','i','\0'};
167 static WCHAR TurkishW[] = {'T','u','r','k','i','s','h','\0'};
168 static WCHAR VietnameseW[] = {'V','i','e','t','n','a','m','e','s','e','\0'};
169 static WCHAR WesternW[] = {'W','e','s','t','e','r','n','\0'};
171 static WCHAR *ElfScriptsW[32] = { /* these are in the order of the fsCsb[0] bits */
172 WesternW, /*00*/
173 Central_EuropeanW,
174 CyrillicW,
175 GreekW,
176 TurkishW,
177 HebrewW,
178 ArabicW,
179 BalticW,
180 VietnameseW, /*08*/
181 NULL, NULL, NULL, NULL, NULL, NULL, NULL, /*15*/
182 ThaiW,
183 NULL, NULL, NULL, NULL, NULL, NULL, NULL, /*23*/
184 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
185 SymbolW /*31*/
188 typedef struct {
189 WCHAR *name;
190 INT charset;
191 } NameCs;
193 typedef struct tagFontSubst {
194 NameCs from;
195 NameCs to;
196 struct tagFontSubst *next;
197 } FontSubst;
199 static FontSubst *substlist = NULL;
201 static BOOL AddFontFileToList(char *file)
203 FT_Face ft_face;
204 TT_OS2 *pOS2;
205 WCHAR *FamilyW, *StyleW;
206 DWORD len;
207 Family *family = FontList;
208 Family **insert = &FontList;
209 Face **insertface;
210 FT_Error err;
211 int i;
213 TRACE("Loading font file %s\n", debugstr_a(file));
214 if((err = pFT_New_Face(library, file, 0, &ft_face)) != 0) {
215 ERR("Unable to load font file %s err = %x\n", debugstr_a(file), err);
216 return FALSE;
219 if(!FT_IS_SFNT(ft_face)) { /* for now we'll skip everything but TT/OT */
220 pFT_Done_Face(ft_face);
221 return FALSE;
224 len = MultiByteToWideChar(CP_ACP, 0, ft_face->family_name, -1, NULL, 0);
225 FamilyW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
226 MultiByteToWideChar(CP_ACP, 0, ft_face->family_name, -1, FamilyW, len);
228 while(family) {
229 if(!strcmpW(family->FamilyName, FamilyW))
230 break;
231 insert = &family->next;
232 family = family->next;
234 if(!family) {
235 family = *insert = HeapAlloc(GetProcessHeap(), 0, sizeof(*family));
236 family->FamilyName = FamilyW;
237 family->FirstFace = NULL;
238 family->next = NULL;
239 } else {
240 HeapFree(GetProcessHeap(), 0, FamilyW);
243 len = MultiByteToWideChar(CP_ACP, 0, ft_face->style_name, -1, NULL, 0);
244 StyleW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
245 MultiByteToWideChar(CP_ACP, 0, ft_face->style_name, -1, StyleW, len);
248 for(insertface = &family->FirstFace; *insertface;
249 insertface = &(*insertface)->next) {
250 if(!strcmpW((*insertface)->StyleName, StyleW)) {
251 WARN("Already loaded font %s %s\n", debugstr_w(family->FamilyName),
252 debugstr_w(StyleW));
253 HeapFree(GetProcessHeap(), 0, StyleW);
254 pFT_Done_Face(ft_face);
255 return FALSE;
258 *insertface = HeapAlloc(GetProcessHeap(), 0, sizeof(**insertface));
259 (*insertface)->StyleName = StyleW;
260 (*insertface)->file = HeapAlloc(GetProcessHeap(),0,strlen(file)+1);
261 strcpy((*insertface)->file, file);
262 (*insertface)->next = NULL;
263 (*insertface)->Italic = (ft_face->style_flags & FT_STYLE_FLAG_ITALIC) ? 1 : 0;
264 (*insertface)->Bold = (ft_face->style_flags & FT_STYLE_FLAG_BOLD) ? 1 : 0;
266 pOS2 = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_os2);
267 if(pOS2) {
268 (*insertface)->fsCsb[0] = pOS2->ulCodePageRange1;
269 (*insertface)->fsCsb[1] = pOS2->ulCodePageRange2;
270 } else {
271 (*insertface)->fsCsb[0] = (*insertface)->fsCsb[1] = 0;
273 TRACE("fsCsb = %08lx %08lx\n", (*insertface)->fsCsb[0], (*insertface)->fsCsb[1]);
275 if((*insertface)->fsCsb[0] == 0) { /* let's see if we can find any interesting cmaps */
276 for(i = 0; i < ft_face->num_charmaps &&
277 !(*insertface)->fsCsb[0]; i++) {
278 switch(ft_face->charmaps[i]->encoding) {
279 case ft_encoding_unicode:
280 (*insertface)->fsCsb[0] = 1;
281 break;
282 case ft_encoding_symbol:
283 (*insertface)->fsCsb[0] = 1L << 31;
284 break;
285 default:
286 break;
291 pFT_Done_Face(ft_face);
293 TRACE("Added font %s %s\n", debugstr_w(family->FamilyName),
294 debugstr_w(StyleW));
295 return TRUE;
298 static void DumpFontList(void)
300 Family *family;
301 Face *face;
303 for(family = FontList; family; family = family->next) {
304 TRACE("Family: %s\n", debugstr_w(family->FamilyName));
305 for(face = family->FirstFace; face; face = face->next) {
306 TRACE("\t%s\n", debugstr_w(face->StyleName));
309 return;
312 static void DumpSubstList(void)
314 FontSubst *psub;
316 for(psub = substlist; psub; psub = psub->next)
317 if(psub->from.charset != -1 || psub->to.charset != -1)
318 TRACE("%s:%d -> %s:%d\n", debugstr_w(psub->from.name),
319 psub->from.charset, debugstr_w(psub->to.name), psub->to.charset);
320 else
321 TRACE("%s -> %s\n", debugstr_w(psub->from.name),
322 debugstr_w(psub->to.name));
323 return;
326 static void split_subst_info(NameCs *nc, LPSTR str)
328 CHAR *p = strrchr(str, ',');
329 DWORD len;
331 nc->charset = -1;
332 if(p && *(p+1)) {
333 nc->charset = strtol(p+1, NULL, 10);
334 *p = '\0';
336 len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
337 nc->name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
338 MultiByteToWideChar(CP_ACP, 0, str, -1, nc->name, len);
341 static void LoadSubstList(void)
343 FontSubst *psub, **ppsub;
344 HKEY hkey;
345 DWORD valuelen, datalen, i = 0, type, dlen, vlen;
346 LPSTR value;
347 LPVOID data;
349 if(substlist) {
350 for(psub = substlist; psub;) {
351 FontSubst *ptmp;
352 HeapFree(GetProcessHeap(), 0, psub->to.name);
353 HeapFree(GetProcessHeap(), 0, psub->from.name);
354 ptmp = psub;
355 psub = psub->next;
356 HeapFree(GetProcessHeap(), 0, ptmp);
358 substlist = NULL;
361 if(RegOpenKeyA(HKEY_LOCAL_MACHINE,
362 "Software\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes",
363 &hkey) == ERROR_SUCCESS) {
365 RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
366 &valuelen, &datalen, NULL, NULL);
368 valuelen++; /* returned value doesn't include room for '\0' */
369 value = HeapAlloc(GetProcessHeap(), 0, valuelen * sizeof(CHAR));
370 data = HeapAlloc(GetProcessHeap(), 0, datalen);
372 dlen = datalen;
373 vlen = valuelen;
374 ppsub = &substlist;
375 while(RegEnumValueA(hkey, i++, value, &vlen, NULL, &type, data,
376 &dlen) == ERROR_SUCCESS) {
377 TRACE("Got %s=%s\n", debugstr_a(value), debugstr_a(data));
379 *ppsub = HeapAlloc(GetProcessHeap(), 0, sizeof(**ppsub));
380 (*ppsub)->next = NULL;
381 split_subst_info(&((*ppsub)->from), value);
382 split_subst_info(&((*ppsub)->to), data);
384 /* Win 2000 doesn't allow mapping between different charsets
385 or mapping of DEFAULT_CHARSET */
386 if(((*ppsub)->to.charset != (*ppsub)->from.charset) ||
387 (*ppsub)->to.charset == DEFAULT_CHARSET) {
388 HeapFree(GetProcessHeap(), 0, (*ppsub)->to.name);
389 HeapFree(GetProcessHeap(), 0, (*ppsub)->from.name);
390 HeapFree(GetProcessHeap(), 0, *ppsub);
391 } else {
392 ppsub = &((*ppsub)->next);
394 /* reset dlen and vlen */
395 dlen = datalen;
396 vlen = valuelen;
398 HeapFree(GetProcessHeap(), 0, data);
399 HeapFree(GetProcessHeap(), 0, value);
400 RegCloseKey(hkey);
404 static BOOL ReadFontDir(char *dirname)
406 DIR *dir;
407 struct dirent *dent;
408 char path[MAX_PATH];
410 TRACE("Loading fonts from %s\n", debugstr_a(dirname));
412 dir = opendir(dirname);
413 if(!dir) {
414 ERR("Can't open directory %s\n", debugstr_a(dirname));
415 return FALSE;
417 while((dent = readdir(dir)) != NULL) {
418 struct stat statbuf;
420 if(!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
421 continue;
423 TRACE("Found %s in %s\n", debugstr_a(dent->d_name), debugstr_a(dirname));
425 sprintf(path, "%s/%s", dirname, dent->d_name);
427 if(stat(path, &statbuf) == -1)
429 WARN("Can't stat %s\n", debugstr_a(path));
430 continue;
432 if(S_ISDIR(statbuf.st_mode))
433 ReadFontDir(path);
434 else
435 AddFontFileToList(path);
437 return TRUE;
442 /*************************************************************
443 * WineEngInit
445 * Initialize FreeType library and create a list of available faces
447 BOOL WineEngInit(void)
449 HKEY hkey;
450 DWORD valuelen, datalen, i = 0, type, dlen, vlen;
451 LPSTR value;
452 LPVOID data;
453 char windowsdir[MAX_PATH];
454 char unixname[MAX_PATH];
456 TRACE("\n");
458 ft_handle = wine_dlopen(SONAME_LIBFREETYPE, RTLD_NOW, NULL, 0);
459 if(!ft_handle) {
460 WINE_MESSAGE(
461 "Wine cannot find the FreeType font library. To enable Wine to\n"
462 "use TrueType fonts please install a version of FreeType greater than\n"
463 "or equal to 2.0.5.\n"
464 "http://www.freetype.org\n");
465 return FALSE;
468 #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;}
470 LOAD_FUNCPTR(FT_Cos)
471 LOAD_FUNCPTR(FT_Done_Face)
472 LOAD_FUNCPTR(FT_Get_Char_Index)
473 LOAD_FUNCPTR(FT_Get_Sfnt_Table)
474 LOAD_FUNCPTR(FT_Init_FreeType)
475 LOAD_FUNCPTR(FT_Load_Glyph)
476 LOAD_FUNCPTR(FT_MulFix)
477 LOAD_FUNCPTR(FT_New_Face)
478 LOAD_FUNCPTR(FT_Outline_Get_Bitmap)
479 LOAD_FUNCPTR(FT_Outline_Transform)
480 LOAD_FUNCPTR(FT_Outline_Translate)
481 LOAD_FUNCPTR(FT_Select_Charmap)
482 LOAD_FUNCPTR(FT_Set_Pixel_Sizes)
483 LOAD_FUNCPTR(FT_Sin)
484 LOAD_FUNCPTR(FT_Vector_Rotate)
486 #undef LOAD_FUNCPTR
488 if(!wine_dlsym(ft_handle, "FT_Get_Postscript_Name", NULL, 0) &&
489 !wine_dlsym(ft_handle, "FT_Sqrt64", NULL, 0)) {
490 /* try to avoid 2.0.4: >= 2.0.5 has FT_Get_Postscript_Name and
491 <= 2.0.3 has FT_Sqrt64 */
492 goto sym_not_found;
495 if(pFT_Init_FreeType(&library) != 0) {
496 ERR("Can't init FreeType library\n");
497 wine_dlclose(ft_handle, NULL, 0);
498 return FALSE;
501 /* load in the fonts from %WINDOWSDIR%\\Fonts first of all */
502 GetWindowsDirectoryA(windowsdir, sizeof(windowsdir));
503 strcat(windowsdir, "\\Fonts");
504 wine_get_unix_file_name(windowsdir, unixname, sizeof(unixname));
505 ReadFontDir(unixname);
507 /* then look in any directories that we've specified in the config file */
508 if(RegOpenKeyA(HKEY_LOCAL_MACHINE,
509 "Software\\Wine\\Wine\\Config\\FontDirs",
510 &hkey) == ERROR_SUCCESS) {
512 RegQueryInfoKeyA(hkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
513 &valuelen, &datalen, NULL, NULL);
515 valuelen++; /* returned value doesn't include room for '\0' */
516 value = HeapAlloc(GetProcessHeap(), 0, valuelen);
517 data = HeapAlloc(GetProcessHeap(), 0, datalen);
519 dlen = datalen;
520 vlen = valuelen;
521 while(RegEnumValueA(hkey, i++, value, &vlen, NULL, &type, data,
522 &dlen) == ERROR_SUCCESS) {
523 TRACE("Got %s=%s\n", value, (LPSTR)data);
524 ReadFontDir((LPSTR)data);
525 /* reset dlen and vlen */
526 dlen = datalen;
527 vlen = valuelen;
529 HeapFree(GetProcessHeap(), 0, data);
530 HeapFree(GetProcessHeap(), 0, value);
531 RegCloseKey(hkey);
534 DumpFontList();
535 LoadSubstList();
536 DumpSubstList();
537 return TRUE;
538 sym_not_found:
539 WINE_MESSAGE(
540 "Wine cannot find certain functions that it needs inside the FreeType\n"
541 "font library. To enable Wine to use TrueType fonts please upgrade\n"
542 "FreeType to at least version 2.0.5.\n"
543 "http://www.freetype.org\n");
544 wine_dlclose(ft_handle, NULL, 0);
545 return FALSE;
549 static LONG calc_ppem_for_height(FT_Face ft_face, LONG height)
551 TT_OS2 *pOS2;
552 LONG ppem;
554 pOS2 = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_os2);
556 if(height == 0) height = 16;
558 /* Calc. height of EM square:
560 * For +ve lfHeight we have
561 * lfHeight = (winAscent + winDescent) * ppem / units_per_em
562 * Re-arranging gives:
563 * ppem = units_per_em * lfheight / (winAscent + winDescent)
565 * For -ve lfHeight we have
566 * |lfHeight| = ppem
567 * [i.e. |lfHeight| = (winAscent + winDescent - il) * ppem / units_per_em
568 * with il = winAscent + winDescent - units_per_em]
572 if(height > 0)
573 ppem = ft_face->units_per_EM * height /
574 (pOS2->usWinAscent + pOS2->usWinDescent);
575 else
576 ppem = -height;
578 return ppem;
581 static LONG load_VDMX(GdiFont, LONG);
583 static FT_Face OpenFontFile(GdiFont font, char *file, LONG height)
585 FT_Error err;
586 FT_Face ft_face;
587 LONG ppem;
589 err = pFT_New_Face(library, file, 0, &ft_face);
590 if(err) {
591 ERR("FT_New_Face rets %d\n", err);
592 return 0;
595 /* set it here, as load_VDMX needs it */
596 font->ft_face = ft_face;
598 /* load the VDMX table if we have one */
599 ppem = load_VDMX(font, height);
600 if(ppem == 0)
601 ppem = calc_ppem_for_height(ft_face, height);
603 pFT_Set_Pixel_Sizes(ft_face, 0, ppem);
605 return ft_face;
608 static int get_nearest_charset(Face *face, int lfcharset)
610 CHARSETINFO csi;
611 TranslateCharsetInfo((DWORD*)lfcharset, &csi, TCI_SRCCHARSET);
613 if(csi.fs.fsCsb[0] & face->fsCsb[0]) return lfcharset;
615 if(face->fsCsb[0] & 0x1) return ANSI_CHARSET;
617 if(face->fsCsb[0] & (1L << 31)) return SYMBOL_CHARSET;
619 FIXME("returning DEFAULT_CHARSET face->fsCsb[0] = %08lx file = %s\n",
620 face->fsCsb[0], face->file);
621 return DEFAULT_CHARSET;
624 static GdiFont alloc_font(void)
626 GdiFont ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*ret));
627 ret->gmsize = INIT_GM_SIZE;
628 ret->gm = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
629 ret->gmsize * sizeof(*ret->gm));
630 ret->next = NULL;
631 return ret;
634 static void free_font(GdiFont font)
636 if (font->ft_face) pFT_Done_Face(font->ft_face);
637 HeapFree(GetProcessHeap(), 0, font->gm);
638 HeapFree(GetProcessHeap(), 0, font);
642 /*************************************************************
643 * load_VDMX
645 * load the vdmx entry for the specified height
648 #define MS_MAKE_TAG( _x1, _x2, _x3, _x4 ) \
649 ( ( (FT_ULong)_x4 << 24 ) | \
650 ( (FT_ULong)_x3 << 16 ) | \
651 ( (FT_ULong)_x2 << 8 ) | \
652 (FT_ULong)_x1 )
654 #define MS_VDMX_TAG MS_MAKE_TAG('V', 'D', 'M', 'X')
656 typedef struct {
657 BYTE bCharSet;
658 BYTE xRatio;
659 BYTE yStartRatio;
660 BYTE yEndRatio;
661 } Ratios;
664 static LONG load_VDMX(GdiFont font, LONG height)
666 BYTE hdr[6], tmp[2], group[4];
667 BYTE devXRatio, devYRatio;
668 USHORT numRecs, numRatios;
669 DWORD offset = -1;
670 LONG ppem = 0;
671 int i, result;
673 result = WineEngGetFontData(font, MS_VDMX_TAG, 0, hdr, 6);
675 if(result == GDI_ERROR) /* no vdmx table present, use linear scaling */
676 return ppem;
678 /* FIXME: need the real device aspect ratio */
679 devXRatio = 1;
680 devYRatio = 1;
682 numRecs = GET_BE_WORD(&hdr[2]);
683 numRatios = GET_BE_WORD(&hdr[4]);
685 TRACE("numRecs = %d numRatios = %d\n", numRecs, numRatios);
686 for(i = 0; i < numRatios; i++) {
687 Ratios ratio;
689 offset = (3 * 2) + (i * sizeof(Ratios));
690 WineEngGetFontData(font, MS_VDMX_TAG, offset, &ratio, sizeof(Ratios));
691 offset = -1;
693 TRACE("Ratios[%d] %d %d : %d -> %d\n", i, ratio.bCharSet, ratio.xRatio, ratio.yStartRatio, ratio.yEndRatio);
695 if(ratio.bCharSet != 1)
696 continue;
698 if((ratio.xRatio == 0 &&
699 ratio.yStartRatio == 0 &&
700 ratio.yEndRatio == 0) ||
701 (devXRatio == ratio.xRatio &&
702 devYRatio >= ratio.yStartRatio &&
703 devYRatio <= ratio.yEndRatio))
705 offset = (3 * 2) + (numRatios * 4) + (i * 2);
706 WineEngGetFontData(font, MS_VDMX_TAG, offset, tmp, 2);
707 offset = GET_BE_WORD(tmp);
708 break;
712 if(offset < 0) {
713 FIXME("No suitable ratio found\n");
714 return ppem;
717 if(WineEngGetFontData(font, MS_VDMX_TAG, offset, group, 4) != GDI_ERROR) {
718 USHORT recs;
719 BYTE startsz, endsz;
720 BYTE *vTable;
722 recs = GET_BE_WORD(group);
723 startsz = group[2];
724 endsz = group[3];
726 TRACE("recs=%d startsz=%d endsz=%d\n", recs, startsz, endsz);
728 vTable = HeapAlloc(GetProcessHeap(), 0, recs * 6);
729 result = WineEngGetFontData(font, MS_VDMX_TAG, offset + 4, vTable, recs * 6);
730 if(result == GDI_ERROR) {
731 FIXME("Failed to retrieve vTable\n");
732 goto end;
735 if(height > 0) {
736 for(i = 0; i < recs; i++) {
737 SHORT yMax = GET_BE_WORD(&vTable[(i * 6) + 2]);
738 SHORT yMin = GET_BE_WORD(&vTable[(i * 6) + 4]);
739 ppem = GET_BE_WORD(&vTable[i * 6]);
741 if(yMax + -yMin == height) {
742 font->yMax = yMax;
743 font->yMin = yMin;
744 TRACE("ppem %ld found; height=%ld yMax=%d yMin=%d\n", ppem, height, font->yMax, font->yMin);
745 break;
747 if(yMax + -yMin > height) {
748 if(--i < 0) {
749 ppem = 0;
750 goto end; /* failed */
752 font->yMax = GET_BE_WORD(&vTable[(i * 6) + 2]);
753 font->yMin = GET_BE_WORD(&vTable[(i * 6) + 4]);
754 TRACE("ppem %ld found; height=%ld yMax=%d yMin=%d\n", ppem, height, font->yMax, font->yMin);
755 break;
758 if(!font->yMax) {
759 ppem = 0;
760 TRACE("ppem not found for height %ld\n", height);
762 } else {
763 ppem = -height;
764 if(ppem < startsz || ppem > endsz)
765 goto end;
767 for(i = 0; i < recs; i++) {
768 USHORT yPelHeight;
769 yPelHeight = GET_BE_WORD(&vTable[i * 6]);
771 if(yPelHeight > ppem)
772 break; /* failed */
774 if(yPelHeight == ppem) {
775 font->yMax = GET_BE_WORD(&vTable[(i * 6) + 2]);
776 font->yMin = GET_BE_WORD(&vTable[(i * 6) + 4]);
777 TRACE("ppem %ld found; yMax=%d yMin=%d\n", ppem, font->yMax, font->yMin);
778 break;
782 end:
783 HeapFree(GetProcessHeap(), 0, vTable);
786 return ppem;
790 /*************************************************************
791 * WineEngCreateFontInstance
794 GdiFont WineEngCreateFontInstance(DC *dc, HFONT hfont)
796 GdiFont ret;
797 Face *face;
798 Family *family = NULL;
799 WCHAR FaceName[LF_FACESIZE];
800 BOOL bd, it;
801 FONTOBJ *font = GDI_GetObjPtr(hfont, FONT_MAGIC);
802 LOGFONTW *plf = &font->logfont;
804 TRACE("%s, h=%ld, it=%d, weight=%ld, PandF=%02x, charset=%d orient %ld escapement %ld\n",
805 debugstr_w(plf->lfFaceName), plf->lfHeight, plf->lfItalic,
806 plf->lfWeight, plf->lfPitchAndFamily, plf->lfCharSet, plf->lfOrientation,
807 plf->lfEscapement);
809 /* check the cache first */
810 for(ret = GdiFontList; ret; ret = ret->next) {
811 if(ret->hfont == hfont) {
812 GDI_ReleaseObj(hfont);
813 TRACE("returning cached gdiFont(%p) for hFont %x\n", ret, hfont);
814 return ret;
818 if(!FontList) /* No fonts installed */
820 GDI_ReleaseObj(hfont);
821 TRACE("No fonts installed\n");
822 return NULL;
825 ret = alloc_font();
827 strcpyW(FaceName, plf->lfFaceName);
829 if(FaceName[0] != '\0') {
830 FontSubst *psub;
831 for(psub = substlist; psub; psub = psub->next)
832 if(!strcmpiW(FaceName, psub->from.name) &&
833 (psub->from.charset == -1 ||
834 psub->from.charset == plf->lfCharSet))
835 break;
836 if(psub) {
837 TRACE("substituting %s -> %s\n", debugstr_w(FaceName),
838 debugstr_w(psub->to.name));
839 strcpyW(FaceName, psub->to.name);
842 for(family = FontList; family; family = family->next) {
843 if(!strcmpiW(family->FamilyName, FaceName))
844 break;
847 if(!family) { /* do other aliases here */
848 if(!strcmpiW(FaceName, SystemW))
849 strcpyW(FaceName, defSystem);
850 else if(!strcmpiW(FaceName, MSSansSerifW))
851 strcpyW(FaceName, defSans);
852 else if(!strcmpiW(FaceName, HelvW))
853 strcpyW(FaceName, defSans);
854 else
855 goto not_found;
857 for(family = FontList; family; family = family->next) {
858 if(!strcmpiW(family->FamilyName, FaceName))
859 break;
864 not_found:
865 if(!family) {
866 if(plf->lfPitchAndFamily & FIXED_PITCH ||
867 plf->lfPitchAndFamily & FF_MODERN)
868 strcpyW(FaceName, defFixed);
869 else if(plf->lfPitchAndFamily & FF_ROMAN)
870 strcpyW(FaceName, defSerif);
871 else if(plf->lfPitchAndFamily & FF_SWISS)
872 strcpyW(FaceName, defSans);
873 else
874 strcpyW(FaceName, defSans);
875 for(family = FontList; family; family = family->next) {
876 if(!strcmpiW(family->FamilyName, FaceName))
877 break;
881 if(!family) {
882 family = FontList;
883 FIXME("just using first face for now\n");
886 it = plf->lfItalic ? 1 : 0;
887 bd = plf->lfWeight > 550 ? 1 : 0;
889 for(face = family->FirstFace; face; face = face->next) {
890 if(!(face->Italic ^ it) && !(face->Bold ^ bd))
891 break;
893 if(!face) {
894 face = family->FirstFace;
895 if(it && !face->Italic) ret->fake_italic = TRUE;
896 if(bd && !face->Bold) ret->fake_bold = TRUE;
898 ret->charset = get_nearest_charset(face, plf->lfCharSet);
900 TRACE("Choosen %s %s\n", debugstr_w(family->FamilyName),
901 debugstr_w(face->StyleName));
903 ret->ft_face = OpenFontFile(ret, face->file,
904 INTERNAL_YWSTODS(dc,plf->lfHeight));
905 if (!ret->ft_face)
907 GDI_ReleaseObj(hfont);
908 free_font( ret );
909 return 0;
912 if(ret->charset == SYMBOL_CHARSET)
913 pFT_Select_Charmap(ret->ft_face, ft_encoding_symbol);
914 ret->orientation = plf->lfOrientation;
915 GDI_ReleaseObj(hfont);
917 TRACE("caching: gdiFont=%p hfont=%x\n", ret, hfont);
918 ret->hfont = hfont;
919 ret->next = GdiFontList;
920 GdiFontList = ret;
922 return ret;
925 static void DumpGdiFontList(void)
927 GdiFont gdiFont;
929 TRACE("---------- gdiFont Cache ----------\n");
930 for(gdiFont = GdiFontList; gdiFont; gdiFont = gdiFont->next) {
931 FONTOBJ *font = GDI_GetObjPtr(gdiFont->hfont, FONT_MAGIC);
932 LOGFONTW *plf = &font->logfont;
933 TRACE("gdiFont=%p hfont=%x (%s)\n",
934 gdiFont, gdiFont->hfont, debugstr_w(plf->lfFaceName));
935 GDI_ReleaseObj(gdiFont->hfont);
939 /*************************************************************
940 * WineEngDestroyFontInstance
942 * free the gdiFont associated with this handle
945 BOOL WineEngDestroyFontInstance(HFONT handle)
947 GdiFont gdiFont;
948 GdiFont gdiPrev = NULL;
950 TRACE("destroying hfont=%x\n", handle);
951 if(TRACE_ON(font))
952 DumpGdiFontList();
954 for(gdiFont = GdiFontList; gdiFont; gdiFont = gdiFont->next) {
955 if(gdiFont->hfont == handle) {
956 if(gdiPrev)
957 gdiPrev->next = gdiFont->next;
958 else
959 GdiFontList = gdiFont->next;
961 free_font(gdiFont);
962 return TRUE;
964 gdiPrev = gdiFont;
966 return FALSE;
969 static void GetEnumStructs(Face *face, LPENUMLOGFONTEXW pelf,
970 LPNEWTEXTMETRICEXW pntm, LPDWORD ptype)
972 OUTLINETEXTMETRICW *potm;
973 UINT size;
974 GdiFont font = alloc_font();
976 if (!(font->ft_face = OpenFontFile(font, face->file, 100)))
978 free_font(font);
979 return;
982 memset(&pelf->elfLogFont, 0, sizeof(LOGFONTW));
984 size = WineEngGetOutlineTextMetrics(font, 0, NULL);
985 potm = HeapAlloc(GetProcessHeap(), 0, size);
986 WineEngGetOutlineTextMetrics(font, size, potm);
988 #define TM potm->otmTextMetrics
990 pntm->ntmTm.tmHeight = pelf->elfLogFont.lfHeight = TM.tmHeight;
991 pntm->ntmTm.tmAscent = TM.tmAscent;
992 pntm->ntmTm.tmDescent = TM.tmDescent;
993 pntm->ntmTm.tmInternalLeading = TM.tmInternalLeading;
994 pntm->ntmTm.tmExternalLeading = TM.tmExternalLeading;
995 pntm->ntmTm.tmAveCharWidth = pelf->elfLogFont.lfWidth = TM.tmAveCharWidth;
996 pntm->ntmTm.tmMaxCharWidth = TM.tmMaxCharWidth;
997 pntm->ntmTm.tmWeight = pelf->elfLogFont.lfWeight = TM.tmWeight;
998 pntm->ntmTm.tmOverhang = TM.tmOverhang;
999 pntm->ntmTm.tmDigitizedAspectX = TM.tmDigitizedAspectX;
1000 pntm->ntmTm.tmDigitizedAspectY = TM.tmDigitizedAspectY;
1001 pntm->ntmTm.tmFirstChar = TM.tmFirstChar;
1002 pntm->ntmTm.tmLastChar = TM.tmLastChar;
1003 pntm->ntmTm.tmDefaultChar = TM.tmDefaultChar;
1004 pntm->ntmTm.tmBreakChar = TM.tmBreakChar;
1005 pntm->ntmTm.tmItalic = pelf->elfLogFont.lfItalic = TM.tmItalic;
1006 pntm->ntmTm.tmUnderlined = pelf->elfLogFont.lfUnderline = TM.tmUnderlined;
1007 pntm->ntmTm.tmStruckOut = pelf->elfLogFont.lfStrikeOut = TM.tmStruckOut;
1008 pntm->ntmTm.tmPitchAndFamily = TM.tmPitchAndFamily;
1009 pelf->elfLogFont.lfPitchAndFamily = (TM.tmPitchAndFamily & 0xf1) + 1;
1010 pntm->ntmTm.tmCharSet = pelf->elfLogFont.lfCharSet = TM.tmCharSet;
1011 pelf->elfLogFont.lfOutPrecision = OUT_STROKE_PRECIS;
1012 pelf->elfLogFont.lfClipPrecision = CLIP_STROKE_PRECIS;
1013 pelf->elfLogFont.lfQuality = DRAFT_QUALITY;
1015 pntm->ntmTm.ntmFlags = TM.tmItalic ? NTM_ITALIC : 0;
1016 if(TM.tmWeight > 550) pntm->ntmTm.ntmFlags |= NTM_BOLD;
1017 if(pntm->ntmTm.ntmFlags == 0) pntm->ntmTm.ntmFlags = NTM_REGULAR;
1019 pntm->ntmTm.ntmSizeEM = potm->otmEMSquare;
1020 pntm->ntmTm.ntmCellHeight = 0;
1021 pntm->ntmTm.ntmAvgWidth = 0;
1023 *ptype = TM.tmPitchAndFamily & TMPF_TRUETYPE ? TRUETYPE_FONTTYPE : 0;
1024 if(!(TM.tmPitchAndFamily & TMPF_VECTOR))
1025 *ptype |= RASTER_FONTTYPE;
1027 #undef TM
1028 memset(&pntm->ntmFontSig, 0, sizeof(FONTSIGNATURE));
1030 strncpyW(pelf->elfLogFont.lfFaceName,
1031 (WCHAR*)((char*)potm + (ptrdiff_t)potm->otmpFamilyName),
1032 LF_FACESIZE);
1033 strncpyW(pelf->elfFullName,
1034 (WCHAR*)((char*)potm + (ptrdiff_t)potm->otmpFaceName),
1035 LF_FULLFACESIZE);
1036 strncpyW(pelf->elfStyle,
1037 (WCHAR*)((char*)potm + (ptrdiff_t)potm->otmpStyleName),
1038 LF_FACESIZE);
1039 pelf->elfScript[0] = '\0'; /* This will get set in WineEngEnumFonts */
1041 HeapFree(GetProcessHeap(), 0, potm);
1042 free_font(font);
1043 return;
1046 /*************************************************************
1047 * WineEngEnumFonts
1050 DWORD WineEngEnumFonts(LPLOGFONTW plf, DEVICEFONTENUMPROC proc,
1051 LPARAM lparam)
1053 Family *family;
1054 Face *face;
1055 ENUMLOGFONTEXW elf;
1056 NEWTEXTMETRICEXW ntm;
1057 DWORD type, ret = 1;
1058 FONTSIGNATURE fs;
1059 CHARSETINFO csi;
1060 int i;
1062 TRACE("facename = %s charset %d\n", debugstr_w(plf->lfFaceName), plf->lfCharSet);
1063 if(plf->lfFaceName[0]) {
1064 for(family = FontList; family; family = family->next) {
1065 if(!strcmpiW(plf->lfFaceName, family->FamilyName)) {
1066 for(face = family->FirstFace; face; face = face->next) {
1067 GetEnumStructs(face, &elf, &ntm, &type);
1068 for(i = 0; i < 32; i++) {
1069 if(face->fsCsb[0] & (1L << i)) {
1070 fs.fsCsb[0] = 1L << i;
1071 fs.fsCsb[1] = 0;
1072 if(!TranslateCharsetInfo(fs.fsCsb, &csi,
1073 TCI_SRCFONTSIG))
1074 csi.ciCharset = DEFAULT_CHARSET;
1075 if(i == 31) csi.ciCharset = SYMBOL_CHARSET;
1076 if(csi.ciCharset != DEFAULT_CHARSET) {
1077 elf.elfLogFont.lfCharSet =
1078 ntm.ntmTm.tmCharSet = csi.ciCharset;
1079 if(ElfScriptsW[i])
1080 strcpyW(elf.elfScript, ElfScriptsW[i]);
1081 else
1082 FIXME("Unknown elfscript for bit %d\n", i);
1083 TRACE("enuming face %s full %s style %s charset %d type %ld script %s it %d weight %ld ntmflags %08lx\n",
1084 debugstr_w(elf.elfLogFont.lfFaceName),
1085 debugstr_w(elf.elfFullName), debugstr_w(elf.elfStyle),
1086 csi.ciCharset, type, debugstr_w(elf.elfScript),
1087 elf.elfLogFont.lfItalic, elf.elfLogFont.lfWeight,
1088 ntm.ntmTm.ntmFlags);
1089 ret = proc(&elf, &ntm, type, lparam);
1090 if(!ret) goto end;
1097 } else {
1098 for(family = FontList; family; family = family->next) {
1099 GetEnumStructs(family->FirstFace, &elf, &ntm, &type);
1100 for(i = 0; i < 32; i++) {
1101 if(family->FirstFace->fsCsb[0] & (1L << i)) {
1102 fs.fsCsb[0] = 1L << i;
1103 fs.fsCsb[1] = 0;
1104 if(!TranslateCharsetInfo(fs.fsCsb, &csi,
1105 TCI_SRCFONTSIG))
1106 csi.ciCharset = DEFAULT_CHARSET;
1107 if(i == 31) csi.ciCharset = SYMBOL_CHARSET;
1108 if(csi.ciCharset != DEFAULT_CHARSET) {
1109 elf.elfLogFont.lfCharSet = ntm.ntmTm.tmCharSet =
1110 csi.ciCharset;
1111 if(ElfScriptsW[i])
1112 strcpyW(elf.elfScript, ElfScriptsW[i]);
1113 else
1114 FIXME("Unknown elfscript for bit %d\n", i);
1115 TRACE("enuming face %s full %s style %s charset = %d type %ld script %s it %d weight %ld ntmflags %08lx\n",
1116 debugstr_w(elf.elfLogFont.lfFaceName),
1117 debugstr_w(elf.elfFullName), debugstr_w(elf.elfStyle),
1118 csi.ciCharset, type, debugstr_w(elf.elfScript),
1119 elf.elfLogFont.lfItalic, elf.elfLogFont.lfWeight,
1120 ntm.ntmTm.ntmFlags);
1121 ret = proc(&elf, &ntm, type, lparam);
1122 if(!ret) goto end;
1128 end:
1129 return ret;
1132 static void FTVectorToPOINTFX(FT_Vector *vec, POINTFX *pt)
1134 pt->x.value = vec->x >> 6;
1135 pt->x.fract = (vec->x & 0x3f) << 10;
1136 pt->x.fract |= ((pt->x.fract >> 6) | (pt->x.fract >> 12));
1137 pt->y.value = vec->y >> 6;
1138 pt->y.fract = (vec->y & 0x3f) << 10;
1139 pt->y.fract |= ((pt->y.fract >> 6) | (pt->y.fract >> 12));
1140 return;
1143 static FT_UInt get_glyph_index(GdiFont font, UINT glyph)
1145 if(font->charset == SYMBOL_CHARSET && glyph < 0x100)
1146 glyph = glyph + 0xf000;
1147 return pFT_Get_Char_Index(font->ft_face, glyph);
1150 /*************************************************************
1151 * WineEngGetGlyphIndices
1153 * FIXME: add support for GGI_MARK_NONEXISTING_GLYPHS
1155 DWORD WineEngGetGlyphIndices(GdiFont font, LPCWSTR lpstr, INT count,
1156 LPWORD pgi, DWORD flags)
1158 INT i;
1160 for(i = 0; i < count; i++)
1161 pgi[i] = get_glyph_index(font, lpstr[i]);
1163 return count;
1166 /*************************************************************
1167 * WineEngGetGlyphOutline
1169 * Behaves in exactly the same way as the win32 api GetGlyphOutline
1170 * except that the first parameter is the HWINEENGFONT of the font in
1171 * question rather than an HDC.
1174 DWORD WineEngGetGlyphOutline(GdiFont font, UINT glyph, UINT format,
1175 LPGLYPHMETRICS lpgm, DWORD buflen, LPVOID buf,
1176 const MAT2* lpmat)
1178 FT_Face ft_face = font->ft_face;
1179 FT_UInt glyph_index;
1180 DWORD width, height, pitch, needed = 0;
1181 FT_Bitmap ft_bitmap;
1182 FT_Error err;
1183 INT left, right, top = 0, bottom = 0;
1184 FT_Angle angle = 0;
1186 TRACE("%p, %04x, %08x, %p, %08lx, %p, %p\n", font, glyph, format, lpgm,
1187 buflen, buf, lpmat);
1189 if(format & GGO_GLYPH_INDEX) {
1190 glyph_index = glyph;
1191 format &= ~GGO_GLYPH_INDEX;
1192 } else
1193 glyph_index = get_glyph_index(font, glyph);
1195 if(glyph_index >= font->gmsize) {
1196 font->gmsize = (glyph_index / INIT_GM_SIZE + 1) * INIT_GM_SIZE;
1197 font->gm = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, font->gm,
1198 font->gmsize * sizeof(*font->gm));
1199 } else {
1200 if(format == GGO_METRICS && font->gm[glyph_index].init) {
1201 memcpy(lpgm, &font->gm[glyph_index].gm, sizeof(*lpgm));
1202 return 1; /* FIXME */
1206 err = pFT_Load_Glyph(ft_face, glyph_index, FT_LOAD_DEFAULT);
1208 if(err) {
1209 FIXME("FT_Load_Glyph on index %x returns %d\n", glyph_index, err);
1210 return GDI_ERROR;
1213 left = ft_face->glyph->metrics.horiBearingX & -64;
1214 right = ((ft_face->glyph->metrics.horiBearingX +
1215 ft_face->glyph->metrics.width) + 63) & -64;
1217 font->gm[glyph_index].adv = (ft_face->glyph->metrics.horiAdvance + 63) >> 6;
1218 font->gm[glyph_index].lsb = left >> 6;
1219 font->gm[glyph_index].bbx = (right - left) >> 6;
1221 if(font->orientation == 0) {
1222 top = (ft_face->glyph->metrics.horiBearingY + 63) & -64;
1223 bottom = (ft_face->glyph->metrics.horiBearingY -
1224 ft_face->glyph->metrics.height) & -64;
1225 lpgm->gmCellIncX = font->gm[glyph_index].adv;
1226 lpgm->gmCellIncY = 0;
1227 } else {
1228 INT xc, yc;
1229 FT_Vector vec;
1230 angle = font->orientation / 10 << 16;
1231 angle |= ((font->orientation % 10) * (1 << 16)) / 10;
1232 TRACE("angle %ld\n", angle >> 16);
1233 for(xc = 0; xc < 2; xc++) {
1234 for(yc = 0; yc < 2; yc++) {
1235 vec.x = ft_face->glyph->metrics.horiBearingX +
1236 xc * ft_face->glyph->metrics.width;
1237 vec.y = ft_face->glyph->metrics.horiBearingY -
1238 yc * ft_face->glyph->metrics.height;
1239 TRACE("Vec %ld,%ld\n", vec.x, vec.y);
1240 pFT_Vector_Rotate(&vec, angle);
1241 if(xc == 0 && yc == 0) {
1242 left = right = vec.x;
1243 top = bottom = vec.y;
1244 } else {
1245 if(vec.x < left) left = vec.x;
1246 else if(vec.x > right) right = vec.x;
1247 if(vec.y < bottom) bottom = vec.y;
1248 else if(vec.y > top) top = vec.y;
1252 left = left & -64;
1253 right = (right + 63) & -64;
1254 bottom = bottom & -64;
1255 top = (top + 63) & -64;
1257 TRACE("transformed box: (%d,%d - %d,%d)\n", left, top, right, bottom);
1258 vec.x = ft_face->glyph->metrics.horiAdvance;
1259 vec.y = 0;
1260 pFT_Vector_Rotate(&vec, angle);
1261 lpgm->gmCellIncX = (vec.x+63) >> 6;
1262 lpgm->gmCellIncY = -(vec.y+63) >> 6;
1264 lpgm->gmBlackBoxX = (right - left) >> 6;
1265 lpgm->gmBlackBoxY = (top - bottom) >> 6;
1266 lpgm->gmptGlyphOrigin.x = left >> 6;
1267 lpgm->gmptGlyphOrigin.y = top >> 6;
1269 memcpy(&font->gm[glyph_index].gm, lpgm, sizeof(*lpgm));
1270 font->gm[glyph_index].init = TRUE;
1272 if(format == GGO_METRICS)
1273 return 1; /* FIXME */
1275 if(ft_face->glyph->format != ft_glyph_format_outline) {
1276 FIXME("loaded a bitmap\n");
1277 return GDI_ERROR;
1280 switch(format) {
1281 case GGO_BITMAP:
1282 width = lpgm->gmBlackBoxX;
1283 height = lpgm->gmBlackBoxY;
1284 pitch = (width + 31) / 32 * 4;
1285 needed = pitch * height;
1287 if(!buf || !buflen) break;
1288 ft_bitmap.width = width;
1289 ft_bitmap.rows = height;
1290 ft_bitmap.pitch = pitch;
1291 ft_bitmap.pixel_mode = ft_pixel_mode_mono;
1292 ft_bitmap.buffer = buf;
1294 if(font->orientation) {
1295 FT_Matrix matrix;
1296 matrix.xx = matrix.yy = pFT_Cos(angle);
1297 matrix.xy = -pFT_Sin(angle);
1298 matrix.yx = -matrix.xy;
1300 pFT_Outline_Transform(&ft_face->glyph->outline, &matrix);
1303 pFT_Outline_Translate(&ft_face->glyph->outline, -left, -bottom );
1305 /* Note: FreeType will only set 'black' bits for us. */
1306 memset(buf, 0, needed);
1307 pFT_Outline_Get_Bitmap(library, &ft_face->glyph->outline, &ft_bitmap);
1308 break;
1310 case GGO_GRAY2_BITMAP:
1311 case GGO_GRAY4_BITMAP:
1312 case GGO_GRAY8_BITMAP:
1313 case WINE_GGO_GRAY16_BITMAP:
1315 int mult, row, col;
1316 BYTE *start, *ptr;
1318 width = lpgm->gmBlackBoxX;
1319 height = lpgm->gmBlackBoxY;
1320 pitch = (width + 3) / 4 * 4;
1321 needed = pitch * height;
1323 if(!buf || !buflen) break;
1324 ft_bitmap.width = width;
1325 ft_bitmap.rows = height;
1326 ft_bitmap.pitch = pitch;
1327 ft_bitmap.pixel_mode = ft_pixel_mode_grays;
1328 ft_bitmap.buffer = buf;
1330 if(font->orientation) {
1331 FT_Matrix matrix;
1332 matrix.xx = matrix.yy = pFT_Cos(angle);
1333 matrix.xy = -pFT_Sin(angle);
1334 matrix.yx = -matrix.xy;
1335 pFT_Outline_Transform(&ft_face->glyph->outline, &matrix);
1338 pFT_Outline_Translate(&ft_face->glyph->outline, -left, -bottom );
1340 pFT_Outline_Get_Bitmap(library, &ft_face->glyph->outline, &ft_bitmap);
1342 if(format == GGO_GRAY2_BITMAP)
1343 mult = 5;
1344 else if(format == GGO_GRAY4_BITMAP)
1345 mult = 17;
1346 else if(format == GGO_GRAY8_BITMAP)
1347 mult = 65;
1348 else if(format == WINE_GGO_GRAY16_BITMAP)
1349 break;
1350 else {
1351 assert(0);
1352 break;
1355 start = buf;
1356 for(row = 0; row < height; row++) {
1357 ptr = start;
1358 for(col = 0; col < width; col++, ptr++) {
1359 *ptr = (*(unsigned int*)ptr * mult + 128) / 256;
1361 start += pitch;
1363 break;
1366 case GGO_NATIVE:
1368 int contour, point = 0, first_pt;
1369 FT_Outline *outline = &ft_face->glyph->outline;
1370 TTPOLYGONHEADER *pph;
1371 TTPOLYCURVE *ppc;
1372 DWORD pph_start, cpfx, type;
1374 if(buflen == 0) buf = NULL;
1376 for(contour = 0; contour < outline->n_contours; contour++) {
1377 pph_start = needed;
1378 pph = buf + needed;
1379 first_pt = point;
1380 if(buf) {
1381 pph->dwType = TT_POLYGON_TYPE;
1382 FTVectorToPOINTFX(&outline->points[point], &pph->pfxStart);
1384 needed += sizeof(*pph);
1385 point++;
1386 while(point <= outline->contours[contour]) {
1387 ppc = buf + needed;
1388 type = (outline->tags[point] == FT_Curve_Tag_On) ?
1389 TT_PRIM_LINE : TT_PRIM_QSPLINE;
1390 cpfx = 0;
1391 do {
1392 if(buf)
1393 FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
1394 cpfx++;
1395 point++;
1396 } while(point <= outline->contours[contour] &&
1397 outline->tags[point] == outline->tags[point-1]);
1398 /* At the end of a contour Windows adds the start point */
1399 if(point > outline->contours[contour]) {
1400 if(buf)
1401 FTVectorToPOINTFX(&outline->points[first_pt], &ppc->apfx[cpfx]);
1402 cpfx++;
1403 } else if(outline->tags[point] == FT_Curve_Tag_On) {
1404 /* add closing pt for bezier */
1405 if(buf)
1406 FTVectorToPOINTFX(&outline->points[point], &ppc->apfx[cpfx]);
1407 cpfx++;
1408 point++;
1410 if(buf) {
1411 ppc->wType = type;
1412 ppc->cpfx = cpfx;
1414 needed += sizeof(*ppc) + (cpfx - 1) * sizeof(POINTFX);
1416 if(buf)
1417 pph->cb = needed - pph_start;
1419 break;
1421 default:
1422 FIXME("Unsupported format %d\n", format);
1423 return GDI_ERROR;
1425 return needed;
1428 /*************************************************************
1429 * WineEngGetTextMetrics
1432 BOOL WineEngGetTextMetrics(GdiFont font, LPTEXTMETRICW ptm)
1434 FT_Face ft_face = font->ft_face;
1435 TT_OS2 *pOS2;
1436 TT_HoriHeader *pHori;
1437 FT_Fixed x_scale, y_scale;
1439 TRACE("font=%p, ptm=%p\n", font, ptm);
1441 x_scale = ft_face->size->metrics.x_scale;
1442 y_scale = ft_face->size->metrics.y_scale;
1444 pOS2 = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_os2);
1445 if(!pOS2) {
1446 FIXME("Can't find OS/2 table - not TT font?\n");
1447 return 0;
1450 pHori = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_hhea);
1451 if(!pHori) {
1452 FIXME("Can't find HHEA table - not TT font?\n");
1453 return 0;
1456 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",
1457 pOS2->usWinAscent, pOS2->usWinDescent,
1458 pOS2->sTypoAscender, pOS2->sTypoDescender, pOS2->sTypoLineGap,
1459 ft_face->ascender, ft_face->descender, ft_face->height,
1460 pHori->Ascender, pHori->Descender, pHori->Line_Gap,
1461 ft_face->bbox.yMax, ft_face->bbox.yMin);
1463 if(font->yMax) {
1464 ptm->tmAscent = font->yMax;
1465 ptm->tmDescent = -font->yMin;
1466 ptm->tmInternalLeading = (ptm->tmAscent + ptm->tmDescent) - ft_face->size->metrics.y_ppem;
1467 } else {
1468 ptm->tmAscent = (pFT_MulFix(pOS2->usWinAscent, y_scale) + 32) >> 6;
1469 ptm->tmDescent = (pFT_MulFix(pOS2->usWinDescent, y_scale) + 32) >> 6;
1470 ptm->tmInternalLeading = (pFT_MulFix(pOS2->usWinAscent + pOS2->usWinDescent
1471 - ft_face->units_per_EM, y_scale) + 32) >> 6;
1474 ptm->tmHeight = ptm->tmAscent + ptm->tmDescent;
1476 /* MSDN says:
1477 el = MAX(0, LineGap - ((WinAscent + WinDescent) - (Ascender - Descender)))
1479 ptm->tmExternalLeading = max(0, (pFT_MulFix(pHori->Line_Gap -
1480 ((pOS2->usWinAscent + pOS2->usWinDescent) -
1481 (pHori->Ascender - pHori->Descender)), y_scale) + 32) >> 6);
1483 ptm->tmAveCharWidth = (pFT_MulFix(pOS2->xAvgCharWidth, x_scale) + 32) >> 6;
1484 ptm->tmMaxCharWidth = (pFT_MulFix(ft_face->bbox.xMax - ft_face->bbox.xMin, x_scale) + 32) >> 6;
1485 ptm->tmWeight = font->fake_bold ? FW_BOLD : pOS2->usWeightClass;
1486 ptm->tmOverhang = 0;
1487 ptm->tmDigitizedAspectX = 300;
1488 ptm->tmDigitizedAspectY = 300;
1489 ptm->tmFirstChar = pOS2->usFirstCharIndex;
1490 ptm->tmLastChar = pOS2->usLastCharIndex;
1491 ptm->tmDefaultChar = pOS2->usDefaultChar;
1492 ptm->tmBreakChar = pOS2->usBreakChar;
1493 ptm->tmItalic = font->fake_italic ? 255 : ((ft_face->style_flags & FT_STYLE_FLAG_ITALIC) ? 255 : 0);
1494 ptm->tmUnderlined = 0; /* entry in OS2 table */
1495 ptm->tmStruckOut = 0; /* entry in OS2 table */
1497 /* Yes TPMF_FIXED_PITCH is correct; braindead api */
1498 if(!FT_IS_FIXED_WIDTH(ft_face))
1499 ptm->tmPitchAndFamily = TMPF_FIXED_PITCH;
1500 else
1501 ptm->tmPitchAndFamily = 0;
1503 switch(pOS2->panose[PAN_FAMILYTYPE_INDEX]) {
1504 case PAN_FAMILY_SCRIPT:
1505 ptm->tmPitchAndFamily |= FF_SCRIPT;
1506 break;
1507 case PAN_FAMILY_DECORATIVE:
1508 case PAN_FAMILY_PICTORIAL:
1509 ptm->tmPitchAndFamily |= FF_DECORATIVE;
1510 break;
1511 case PAN_FAMILY_TEXT_DISPLAY:
1512 if(ptm->tmPitchAndFamily == 0) /* fixed */
1513 ptm->tmPitchAndFamily = FF_MODERN;
1514 else {
1515 switch(pOS2->panose[PAN_SERIFSTYLE_INDEX]) {
1516 case PAN_SERIF_NORMAL_SANS:
1517 case PAN_SERIF_OBTUSE_SANS:
1518 case PAN_SERIF_PERP_SANS:
1519 ptm->tmPitchAndFamily |= FF_SWISS;
1520 break;
1521 default:
1522 ptm->tmPitchAndFamily |= FF_ROMAN;
1525 break;
1526 default:
1527 ptm->tmPitchAndFamily |= FF_DONTCARE;
1530 if(FT_IS_SCALABLE(ft_face))
1531 ptm->tmPitchAndFamily |= TMPF_VECTOR;
1532 if(FT_IS_SFNT(ft_face))
1533 ptm->tmPitchAndFamily |= TMPF_TRUETYPE;
1535 ptm->tmCharSet = font->charset;
1536 return TRUE;
1538 /*************************************************************
1539 * WineEngGetOutlineTextMetrics
1542 UINT WineEngGetOutlineTextMetrics(GdiFont font, UINT cbSize,
1543 OUTLINETEXTMETRICW *potm)
1545 FT_Face ft_face = font->ft_face;
1546 UINT needed, lenfam, lensty, ret;
1547 TT_OS2 *pOS2;
1548 TT_HoriHeader *pHori;
1549 FT_Fixed x_scale, y_scale;
1550 WCHAR *family_nameW, *style_nameW;
1551 WCHAR spaceW[] = {' ', '\0'};
1552 char *cp;
1554 TRACE("font=%p\n", font);
1556 needed = sizeof(*potm);
1558 lenfam = MultiByteToWideChar(CP_ACP, 0, ft_face->family_name, -1, NULL, 0)
1559 * sizeof(WCHAR);
1560 family_nameW = HeapAlloc(GetProcessHeap(), 0, lenfam);
1561 MultiByteToWideChar(CP_ACP, 0, ft_face->family_name, -1,
1562 family_nameW, lenfam);
1564 lensty = MultiByteToWideChar(CP_ACP, 0, ft_face->style_name, -1, NULL, 0)
1565 * sizeof(WCHAR);
1566 style_nameW = HeapAlloc(GetProcessHeap(), 0, lensty);
1567 MultiByteToWideChar(CP_ACP, 0, ft_face->style_name, -1,
1568 style_nameW, lensty);
1570 /* These names should be read from the TT name table */
1572 /* length of otmpFamilyName */
1573 needed += lenfam;
1575 /* length of otmpFaceName */
1576 if(!strcasecmp(ft_face->style_name, "regular")) {
1577 needed += lenfam; /* just the family name */
1578 } else {
1579 needed += lenfam + lensty; /* family + " " + style */
1582 /* length of otmpStyleName */
1583 needed += lensty;
1585 /* length of otmpFullName */
1586 needed += lenfam + lensty;
1588 if(needed > cbSize) {
1589 ret = needed;
1590 goto end;
1593 x_scale = ft_face->size->metrics.x_scale;
1594 y_scale = ft_face->size->metrics.y_scale;
1596 pOS2 = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_os2);
1597 if(!pOS2) {
1598 FIXME("Can't find OS/2 table - not TT font?\n");
1599 ret = 0;
1600 goto end;
1603 pHori = pFT_Get_Sfnt_Table(ft_face, ft_sfnt_hhea);
1604 if(!pHori) {
1605 FIXME("Can't find HHEA table - not TT font?\n");
1606 ret = 0;
1607 goto end;
1610 potm->otmSize = needed;
1612 WineEngGetTextMetrics(font, &potm->otmTextMetrics);
1614 potm->otmFiller = 0;
1615 memcpy(&potm->otmPanoseNumber, pOS2->panose, PANOSE_COUNT);
1616 potm->otmfsSelection = pOS2->fsSelection;
1617 potm->otmfsType = pOS2->fsType;
1618 potm->otmsCharSlopeRise = pHori->caret_Slope_Rise;
1619 potm->otmsCharSlopeRun = pHori->caret_Slope_Run;
1620 potm->otmItalicAngle = 0; /* POST table */
1621 potm->otmEMSquare = ft_face->units_per_EM;
1622 potm->otmAscent = (pFT_MulFix(pOS2->sTypoAscender, y_scale) + 32) >> 6;
1623 potm->otmDescent = (pFT_MulFix(pOS2->sTypoDescender, y_scale) + 32) >> 6;
1624 potm->otmLineGap = (pFT_MulFix(pOS2->sTypoLineGap, y_scale) + 32) >> 6;
1625 potm->otmsCapEmHeight = (pFT_MulFix(pOS2->sCapHeight, y_scale) + 32) >> 6;
1626 potm->otmsXHeight = (pFT_MulFix(pOS2->sxHeight, y_scale) + 32) >> 6;
1627 potm->otmrcFontBox.left = ft_face->bbox.xMin;
1628 potm->otmrcFontBox.right = ft_face->bbox.xMax;
1629 potm->otmrcFontBox.top = ft_face->bbox.yMin;
1630 potm->otmrcFontBox.bottom = ft_face->bbox.yMax;
1631 potm->otmMacAscent = 0; /* where do these come from ? */
1632 potm->otmMacDescent = 0;
1633 potm->otmMacLineGap = 0;
1634 potm->otmusMinimumPPEM = 0; /* TT Header */
1635 potm->otmptSubscriptSize.x = (pFT_MulFix(pOS2->ySubscriptXSize, x_scale) + 32) >> 6;
1636 potm->otmptSubscriptSize.y = (pFT_MulFix(pOS2->ySubscriptYSize, y_scale) + 32) >> 6;
1637 potm->otmptSubscriptOffset.x = (pFT_MulFix(pOS2->ySubscriptXOffset, x_scale) + 32) >> 6;
1638 potm->otmptSubscriptOffset.y = (pFT_MulFix(pOS2->ySubscriptYOffset, y_scale) + 32) >> 6;
1639 potm->otmptSuperscriptSize.x = (pFT_MulFix(pOS2->ySuperscriptXSize, x_scale) + 32) >> 6;
1640 potm->otmptSuperscriptSize.y = (pFT_MulFix(pOS2->ySuperscriptYSize, y_scale) + 32) >> 6;
1641 potm->otmptSuperscriptOffset.x = (pFT_MulFix(pOS2->ySuperscriptXOffset, x_scale) + 32) >> 6;
1642 potm->otmptSuperscriptOffset.y = (pFT_MulFix(pOS2->ySuperscriptYOffset, y_scale) + 32) >> 6;
1643 potm->otmsStrikeoutSize = (pFT_MulFix(pOS2->yStrikeoutSize, y_scale) + 32) >> 6;
1644 potm->otmsStrikeoutPosition = (pFT_MulFix(pOS2->yStrikeoutPosition, y_scale) + 32) >> 6;
1645 potm->otmsUnderscoreSize = 0; /* POST Header */
1646 potm->otmsUnderscorePosition = 0; /* POST Header */
1648 /* otmp* members should clearly have type ptrdiff_t, but M$ knows best */
1649 cp = (char*)potm + sizeof(*potm);
1650 potm->otmpFamilyName = (LPSTR)(cp - (char*)potm);
1651 strcpyW((WCHAR*)cp, family_nameW);
1652 cp += lenfam;
1653 potm->otmpStyleName = (LPSTR)(cp - (char*)potm);
1654 strcpyW((WCHAR*)cp, style_nameW);
1655 cp += lensty;
1656 potm->otmpFaceName = (LPSTR)(cp - (char*)potm);
1657 strcpyW((WCHAR*)cp, family_nameW);
1658 if(strcasecmp(ft_face->style_name, "regular")) {
1659 strcatW((WCHAR*)cp, spaceW);
1660 strcatW((WCHAR*)cp, style_nameW);
1661 cp += lenfam + lensty;
1662 } else
1663 cp += lenfam;
1664 potm->otmpFullName = (LPSTR)(cp - (char*)potm);
1665 strcpyW((WCHAR*)cp, family_nameW);
1666 strcatW((WCHAR*)cp, spaceW);
1667 strcatW((WCHAR*)cp, style_nameW);
1668 ret = needed;
1670 end:
1671 HeapFree(GetProcessHeap(), 0, style_nameW);
1672 HeapFree(GetProcessHeap(), 0, family_nameW);
1674 return ret;
1678 /*************************************************************
1679 * WineEngGetCharWidth
1682 BOOL WineEngGetCharWidth(GdiFont font, UINT firstChar, UINT lastChar,
1683 LPINT buffer)
1685 UINT c;
1686 GLYPHMETRICS gm;
1687 FT_UInt glyph_index;
1689 TRACE("%p, %d, %d, %p\n", font, firstChar, lastChar, buffer);
1691 for(c = firstChar; c <= lastChar; c++) {
1692 WineEngGetGlyphOutline(font, c, GGO_METRICS, &gm, 0, NULL, NULL);
1693 glyph_index = get_glyph_index(font, c);
1694 buffer[c - firstChar] = font->gm[glyph_index].adv;
1696 return TRUE;
1699 /*************************************************************
1700 * WineEngGetTextExtentPoint
1703 BOOL WineEngGetTextExtentPoint(GdiFont font, LPCWSTR wstr, INT count,
1704 LPSIZE size)
1706 UINT idx;
1707 GLYPHMETRICS gm;
1708 TEXTMETRICW tm;
1709 FT_UInt glyph_index;
1711 TRACE("%p, %s, %d, %p\n", font, debugstr_wn(wstr, count), count,
1712 size);
1714 size->cx = 0;
1715 WineEngGetTextMetrics(font, &tm);
1716 size->cy = tm.tmHeight;
1718 for(idx = 0; idx < count; idx++) {
1719 WineEngGetGlyphOutline(font, wstr[idx], GGO_METRICS, &gm, 0, NULL,
1720 NULL);
1721 glyph_index = get_glyph_index(font, wstr[idx]);
1722 size->cx += font->gm[glyph_index].adv;
1724 TRACE("return %ld,%ld\n", size->cx, size->cy);
1725 return TRUE;
1728 /*************************************************************
1729 * WineEngGetTextExtentPointI
1732 BOOL WineEngGetTextExtentPointI(GdiFont font, const WORD *indices, INT count,
1733 LPSIZE size)
1735 UINT idx;
1736 GLYPHMETRICS gm;
1737 TEXTMETRICW tm;
1739 TRACE("%p, %p, %d, %p\n", font, indices, count, size);
1741 size->cx = 0;
1742 WineEngGetTextMetrics(font, &tm);
1743 size->cy = tm.tmHeight;
1745 for(idx = 0; idx < count; idx++) {
1746 WineEngGetGlyphOutline(font, indices[idx],
1747 GGO_METRICS | GGO_GLYPH_INDEX, &gm, 0, NULL,
1748 NULL);
1749 size->cx += font->gm[indices[idx]].adv;
1751 TRACE("return %ld,%ld\n", size->cx, size->cy);
1752 return TRUE;
1755 /*************************************************************
1756 * WineEngGetFontData
1759 DWORD WineEngGetFontData(GdiFont font, DWORD table, DWORD offset, LPVOID buf,
1760 DWORD cbData)
1762 FT_Face ft_face = font->ft_face;
1763 TT_Face tt_face;
1764 SFNT_Interface *sfnt;
1765 DWORD len;
1766 FT_Error err;
1768 TRACE("font=%p, table=%08lx, offset=%08lx, buf=%p, cbData=%lx\n",
1769 font, table, offset, buf, cbData);
1771 if(!FT_IS_SFNT(ft_face))
1772 return GDI_ERROR;
1774 tt_face = (TT_Face) ft_face;
1775 sfnt = (SFNT_Interface*)tt_face->sfnt;
1777 if(!buf || !cbData)
1778 len = 0;
1779 else
1780 len = cbData;
1782 if(table) { /* MS tags differ in endidness from FT ones */
1783 table = table >> 24 | table << 24 |
1784 (table >> 8 & 0xff00) | (table << 8 & 0xff0000);
1787 err = sfnt->load_any(tt_face, table, offset, buf, &len);
1788 if(err) {
1789 TRACE("Can't find table %08lx.\n", table);
1790 return GDI_ERROR;
1792 return len;
1795 #else /* HAVE_FREETYPE */
1797 BOOL WineEngInit(void)
1799 return FALSE;
1801 GdiFont WineEngCreateFontInstance(DC *dc, HFONT hfont)
1803 return NULL;
1805 BOOL WineEngDestroyFontInstance(HFONT hfont)
1807 return FALSE;
1810 DWORD WineEngEnumFonts(LPLOGFONTW plf, DEVICEFONTENUMPROC proc, LPARAM lparam)
1812 return 1;
1815 DWORD WineEngGetGlyphIndices(GdiFont font, LPCWSTR lpstr, INT count,
1816 LPWORD pgi, DWORD flags)
1818 return GDI_ERROR;
1821 DWORD WineEngGetGlyphOutline(GdiFont font, UINT glyph, UINT format,
1822 LPGLYPHMETRICS lpgm, DWORD buflen, LPVOID buf,
1823 const MAT2* lpmat)
1825 ERR("called but we don't have FreeType\n");
1826 return GDI_ERROR;
1829 BOOL WineEngGetTextMetrics(GdiFont font, LPTEXTMETRICW ptm)
1831 ERR("called but we don't have FreeType\n");
1832 return FALSE;
1835 UINT WineEngGetOutlineTextMetrics(GdiFont font, UINT cbSize,
1836 OUTLINETEXTMETRICW *potm)
1838 ERR("called but we don't have FreeType\n");
1839 return 0;
1842 BOOL WineEngGetCharWidth(GdiFont font, UINT firstChar, UINT lastChar,
1843 LPINT buffer)
1845 ERR("called but we don't have FreeType\n");
1846 return FALSE;
1849 BOOL WineEngGetTextExtentPoint(GdiFont font, LPCWSTR wstr, INT count,
1850 LPSIZE size)
1852 ERR("called but we don't have FreeType\n");
1853 return FALSE;
1856 BOOL WineEngGetTextExtentPointI(GdiFont font, const WORD *indices, INT count,
1857 LPSIZE size)
1859 ERR("called but we don't have FreeType\n");
1860 return FALSE;
1863 DWORD WineEngGetFontData(GdiFont font, DWORD table, DWORD offset, LPVOID buf,
1864 DWORD cbData)
1866 ERR("called but we don't have FreeType\n");
1867 return GDI_ERROR;
1869 #endif /* HAVE_FREETYPE */