When including 'wine/port.h', include it first.
[wine] / dlls / wineps / truetype.c
1 /*******************************************************************************
2  *  TrueType font-related functions for Wine PostScript driver.  Currently just
3  *  uses FreeType to read font metrics.
4  *
5  *  Copyright 2001  Ian Pilcher
6  *
7  *
8  *  NOTE:  Many of the functions in this file can return either fatal errors
9  *      (memory allocation failure or unexpected FreeType error) or non-fatal
10  *      errors (unusable font file).  Fatal errors are indicated by returning
11  *      FALSE; see individual function descriptions for how they indicate non-
12  *      fatal errors.
13  *
14  */
15 #include "config.h"
16
17 #ifdef HAVE_FREETYPE
18
19 /*
20  *  These stupid #ifdefs should work for FreeType 2.0.1 and 2.0.2.  Beyond that
21  *  is anybody's guess.
22  */
23
24 #ifdef HAVE_FREETYPE_FREETYPE_H
25 #include <freetype/freetype.h>
26 #endif
27 #ifdef HAVE_FREETYPE_FTGLYPH_H
28 #include <freetype/ftglyph.h>
29 #endif
30 #ifdef HAVE_FREETYPE_TTTABLES_H
31 #include <freetype/tttables.h>
32 #endif
33 #ifdef HAVE_FREETYPE_FTNAMES_H
34 #include <freetype/ftnames.h>
35 #endif
36 #ifdef HAVE_FREETYPE_FTSNAMES_H
37 #include <freetype/ftsnames.h>
38 #endif
39 #ifdef HAVE_FREETYPE_TTNAMEID_H
40 #include <freetype/ttnameid.h>
41 #endif
42
43 #include <sys/types.h>
44 #include <dirent.h>
45 #include <string.h>
46 #include <stdio.h>
47 #include <errno.h>
48
49 #include "winnt.h"
50 #include "winerror.h"
51 #include "winreg.h"
52 #include "psdrv.h"
53 #include "debugtools.h"
54
55 DEFAULT_DEBUG_CHANNEL(psdrv);
56
57 #define REQUIRED_FACE_FLAGS     (   FT_FACE_FLAG_SCALABLE   |   \
58                                     FT_FACE_FLAG_HORIZONTAL |   \
59                                     FT_FACE_FLAG_SFNT       |   \
60                                     FT_FACE_FLAG_GLYPH_NAMES    )
61
62 #define GLYPH_LOAD_FLAGS        (   FT_LOAD_NO_SCALE            |   \
63                                     FT_LOAD_IGNORE_TRANSFORM    |   \
64                                     FT_LOAD_LINEAR_DESIGN           )
65                                     
66 /*******************************************************************************
67  *  FindCharMap
68  *
69  *  Finds Windows character map and creates "EncodingScheme" string.  Returns
70  *  FALSE to indicate memory allocation or FreeType error; sets *p_charmap to
71  *  NULL if no Windows encoding is present.
72  *
73  *  Returns Unicode character map if present; otherwise uses the first Windows
74  *  character map found.
75  *
76  */
77 static const LPCSTR encoding_names[7] =
78 {
79     "WindowsSymbol",        /* TT_MS_ID_SYMBOL_CS */
80     "WindowsUnicode",       /* TT_MS_ID_UNICODE_CS */
81     "WindowsShiftJIS",      /* TT_MS_ID_SJIS */
82     "WindowsPRC",           /* TT_MS_ID_GB2312 */
83     "WindowsBig5",          /* TT_MS_ID_BIG_5 */
84     "WindowsWansung",       /* TT_MS_ID_WANSUNG */
85     "WindowsJohab"          /* TT_MS_ID_JOHAB */
86 /*  "WindowsUnknown65535"   is the longest possible (encoding_id is a UShort) */
87 };
88
89 static BOOL FindCharMap(FT_Face face, FT_CharMap *p_charmap, LPSTR *p_sz)
90 {
91     FT_Int      i;
92     FT_Error    error;
93     FT_CharMap  charmap = NULL;
94     
95     for (i = 0; i < face->num_charmaps; ++i)
96     {
97         if (face->charmaps[i]->platform_id != TT_PLATFORM_MICROSOFT)
98             continue;
99             
100         if (face->charmaps[i]->encoding_id == TT_MS_ID_UNICODE_CS)
101         {
102             charmap = face->charmaps[i];
103             break;
104         }
105         
106         if (charmap == NULL)
107             charmap = face->charmaps[i];
108     }
109     
110     *p_charmap = charmap;
111     
112     if (charmap == NULL)
113     {
114         WARN("No Windows character map found\n");
115         return TRUE;
116     }
117     
118     error = FT_Set_Charmap(face, charmap);
119     if (error != FT_Err_Ok)
120     {
121         ERR("%s returned %i\n", "FT_Set_Charmap", error);
122         return FALSE;
123     }
124     
125     *p_sz = HeapAlloc(PSDRV_Heap, 0, sizeof("WindowsUnknown65535"));
126     if (*p_sz == NULL)
127         return FALSE;
128         
129     if (charmap->encoding_id < 7)
130         strcpy(*p_sz, encoding_names[charmap->encoding_id]);
131     else
132         sprintf(*p_sz, "%s%u", "WindowsUnknown", charmap->encoding_id);
133         
134     return TRUE;
135 }
136
137 /*******************************************************************************
138  *  MSTTStrToSz
139  *
140  *  Converts a string in the TrueType NAME table to a null-terminated ASCII
141  *  character string.  Space for the string is allocated from the driver heap.
142  *  Only handles platform_id = 3 (TT_PLATFORM_MICROSOFT) strings (16-bit, big
143  *  endian).  It also only handles ASCII character codes (< 128).
144  *
145  *  Sets *p_sz to NULL if string cannot be converted; only returns FALSE for
146  *  memory allocation failure.
147  *
148  */
149 static BOOL MSTTStrToSz(const FT_SfntName *name, LPSTR *p_sz)
150 {
151     FT_UShort   i;
152     INT         len;
153     USHORT      *wsz;
154     LPSTR       sz;
155     
156     len = name->string_len / 2;                     /* # of 16-bit chars */
157     
158     *p_sz = sz = HeapAlloc(PSDRV_Heap, 0, len + 1);
159     if (sz == NULL)
160         return FALSE;
161         
162     wsz = (USHORT *)(name->string);
163     
164     for (i = 0; i < len; ++i, ++sz, ++wsz)
165     {
166         USHORT  wc = *wsz;
167         
168 #ifndef WORDS_BIGENDIAN
169         wc = (wc >> 8) | (wc << 8);
170 #endif
171
172         if (wc > 127)
173         {
174             WARN("Non-ASCII character 0x%.4x\n", wc);
175             HeapFree(PSDRV_Heap, 0, *p_sz);
176             *p_sz = NULL;
177             return TRUE;
178         }
179         
180         *sz = (CHAR)wc;
181     }
182     
183     *sz = '\0';
184     
185     return TRUE;
186 }
187
188 /*******************************************************************************
189  *  FindMSTTString
190  *
191  *  Finds the requested Microsoft platform string in the TrueType NAME table and
192  *  converts it to a null-terminated ASCII string.  Currently looks for U.S.
193  *  English names only.
194  *
195  *  Sets string to NULL if not present or cannot be converted; returns FALSE
196  *  only for memory allocation failure.
197  *
198  */
199 static BOOL FindMSTTString(FT_Face face, FT_CharMap charmap, FT_UShort name_id,
200         LPSTR *p_sz)
201 {
202     FT_UInt         num_strings, string_index;
203     FT_SfntName     name;
204     FT_Error        error;
205     
206     num_strings = FT_Get_Sfnt_Name_Count(face);
207     
208     for (string_index = 0; string_index < num_strings; ++string_index)
209     {
210         error = FT_Get_Sfnt_Name(face, string_index, &name);
211         if (error != FT_Err_Ok)
212         {
213             ERR("%s returned %i\n", "FT_Get_Sfnt_Name", error);
214             return FALSE;
215         }
216         
217         /* FIXME - Handle other languages? */
218         
219         if (name.platform_id != TT_PLATFORM_MICROSOFT ||
220                 name.language_id != TT_MS_LANGID_ENGLISH_UNITED_STATES)
221             continue;
222             
223         if (name.platform_id != charmap->platform_id ||
224                 name.encoding_id != charmap->encoding_id)
225             continue;
226             
227         if (name.name_id != name_id)
228             continue;
229             
230         return MSTTStrToSz(&name, p_sz);
231     }
232     
233     *p_sz = NULL;                   /* didn't find it */
234     
235     return TRUE;
236 }
237
238 /*******************************************************************************
239  *  PSUnits
240  *
241  *  Convert TrueType font units (relative to font em square) to PostScript
242  *  units.
243  *
244  */
245 inline static float PSUnits(LONG x, USHORT em_size)
246 {
247     return 1000.0 * (float)x / (float)em_size;
248 }
249
250 /*******************************************************************************
251  *  StartAFM
252  *
253  *  Allocates space for the AFM on the driver heap and reads basic font metrics
254  *  from the HEAD, POST, HHEA, and OS/2 tables.  Returns FALSE for memory
255  *  allocation error; sets *p_afm to NULL if required information is missing.
256  *
257  */
258 static BOOL StartAFM(FT_Face face, AFM **p_afm)
259 {
260     TT_Header       *head;
261     TT_Postscript   *post;
262     TT_OS2          *os2;
263     TT_HoriHeader   *hhea;
264     USHORT          em_size;
265     AFM             *afm;
266     
267     head = FT_Get_Sfnt_Table(face, ft_sfnt_head);
268     post = FT_Get_Sfnt_Table(face, ft_sfnt_post);
269     os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2);
270     hhea = FT_Get_Sfnt_Table(face, ft_sfnt_hhea);
271     
272     if (head == NULL || post == NULL || os2 == NULL || hhea == NULL ||
273             os2->version == 0xffff)                     /* old Macintosh font */
274     {
275         WARN("Required table(s) missing\n");
276         *p_afm = NULL;
277         return TRUE;
278     }
279     
280     *p_afm = afm = HeapAlloc(PSDRV_Heap, 0, sizeof(*afm));
281     if (afm == NULL)
282         return FALSE;
283     
284     afm->WinMetrics.usUnitsPerEm = em_size = head->Units_Per_EM;
285     afm->WinMetrics.sAscender = hhea->Ascender;
286     afm->WinMetrics.sDescender = hhea->Descender;
287     afm->WinMetrics.sLineGap = hhea->Line_Gap;
288     afm->WinMetrics.sTypoAscender = os2->sTypoAscender;
289     afm->WinMetrics.sTypoDescender = os2->sTypoDescender;
290     afm->WinMetrics.sTypoLineGap = os2->sTypoLineGap;
291     afm->WinMetrics.usWinAscent = os2->usWinAscent;
292     afm->WinMetrics.usWinDescent = os2->usWinDescent;
293     afm->WinMetrics.sAvgCharWidth = os2->xAvgCharWidth;
294         
295     afm->Weight = os2->usWeightClass;
296     afm->ItalicAngle = ((float)(post->italicAngle)) / 65536.0;
297     afm->IsFixedPitch = (post-> isFixedPitch == 0) ? FALSE : TRUE;
298     afm->UnderlinePosition = PSUnits(post->underlinePosition, em_size);
299     afm->UnderlineThickness = PSUnits(post->underlineThickness, em_size);
300
301     afm->FontBBox.llx = PSUnits(head->xMin, em_size);
302     afm->FontBBox.lly = PSUnits(head->yMin, em_size);
303     afm->FontBBox.urx = PSUnits(head->xMax, em_size);
304     afm->FontBBox.ury = PSUnits(head->yMax, em_size);
305     
306     afm->Ascender = PSUnits(os2->sTypoAscender, em_size);
307     afm->Descender = PSUnits(os2->sTypoDescender, em_size);
308     
309     return TRUE;
310 }
311
312 /*******************************************************************************
313  *  ReadCharMetrics
314  *
315  *  Reads metrics for each glyph in a TrueType font.  Returns false for memory
316  *  allocation or FreeType error; sets *p_metrics to NULL for non-fatal error.
317  *
318  */
319 static BOOL ReadCharMetrics(FT_Face face, AFM *afm, AFMMETRICS **p_metrics)
320 {
321     FT_ULong    charcode, index;
322     AFMMETRICS  *metrics;
323     USHORT      em_size = afm->WinMetrics.usUnitsPerEm;
324     
325     for (charcode = 0, index = 0; charcode < 65536; ++charcode)
326         if (FT_Get_Char_Index(face, charcode) != 0)
327             ++index;                                    /* count # of glyphs */
328             
329     afm->NumofMetrics = index;
330     
331     *p_metrics = metrics = HeapAlloc(PSDRV_Heap, 0, index * sizeof(*metrics));
332     if (metrics == NULL)
333         return FALSE;
334         
335     for (charcode = 0, index = 0; charcode < 65536; ++charcode)
336     {
337         FT_UInt     glyph_index = FT_Get_Char_Index(face, charcode);
338         FT_Error    error;
339         CHAR        buffer[128];                /* for glyph names */
340         
341         if (glyph_index == 0)
342             continue;
343             
344         error = FT_Load_Glyph(face, glyph_index, GLYPH_LOAD_FLAGS);
345         if (error != FT_Err_Ok)
346         {
347             ERR("%s returned %i\n", "FT_Load_Glyph", error);
348             goto cleanup;
349         }
350         
351         error = FT_Get_Glyph_Name(face, glyph_index, buffer, sizeof(buffer));
352         if (error != FT_Err_Ok)
353         {
354             ERR("%s returned %i\n", "FT_Get_Glyph_Name", error);
355             goto cleanup;
356         }
357         
358         metrics[index].N = PSDRV_GlyphName(buffer);
359         if (metrics[index].N == NULL)
360             goto cleanup;
361             
362         metrics[index].C = metrics[index].UV = charcode;
363         metrics[index].WX = PSUnits(face->glyph->metrics.horiAdvance, em_size);
364         
365         ++index;
366     }
367     
368     if (afm->WinMetrics.sAvgCharWidth == 0)
369         afm->WinMetrics.sAvgCharWidth = PSDRV_CalcAvgCharWidth(afm);
370         
371     return TRUE;
372     
373     cleanup:
374         HeapFree(PSDRV_Heap, 0, metrics);
375     
376     return FALSE;
377 }
378
379 /*******************************************************************************
380  *  BuildTrueTypeAFM
381  *
382  *  Builds the AFM for a TrueType font and adds it to the driver font list.
383  *  Returns FALSE only on an unexpected error (memory allocation failure or
384  *  FreeType error).
385  *
386  */
387 static BOOL BuildTrueTypeAFM(FT_Face face)
388 {
389     AFM         *afm;
390     AFMMETRICS  *metrics;
391     LPSTR       font_name, full_name, family_name, encoding_scheme;
392     FT_CharMap  charmap;
393     BOOL        retval, added;
394     
395     retval = StartAFM(face, &afm);
396     if (retval == FALSE || afm == NULL)
397         return retval;
398     
399     retval = FindCharMap(face, &charmap, &encoding_scheme);
400     if (retval == FALSE || charmap == NULL)
401         goto cleanup_afm;
402         
403     retval = FindMSTTString(face, charmap, TT_NAME_ID_PS_NAME, &font_name);
404     if (retval == FALSE || font_name == NULL)
405         goto cleanup_encoding_scheme;
406         
407     retval = FindMSTTString(face, charmap, TT_NAME_ID_FULL_NAME, &full_name);
408     if (retval == FALSE || full_name == NULL)
409         goto cleanup_font_name;
410         
411     retval = FindMSTTString(face, charmap, TT_NAME_ID_FONT_FAMILY,
412             &family_name);
413     if (retval == FALSE || family_name == NULL)
414         goto cleanup_full_name;
415         
416     retval = ReadCharMetrics(face, afm, &metrics);
417     if (retval == FALSE || metrics == NULL)
418         goto cleanup_family_name;
419         
420     afm->EncodingScheme = encoding_scheme; afm->FontName = font_name;
421     afm->FullName = full_name; afm->FamilyName = family_name;
422     afm->Metrics = metrics;
423         
424     retval = PSDRV_AddAFMtoList(&PSDRV_AFMFontList, afm, &added);
425     if (retval == FALSE || added == FALSE)
426         goto cleanup_family_name;
427         
428     return TRUE;
429     
430     /* clean up after fatal or non-fatal errors */
431         
432     cleanup_family_name:
433         HeapFree(PSDRV_Heap, 0, family_name);
434     cleanup_full_name:
435         HeapFree(PSDRV_Heap, 0, full_name);
436     cleanup_font_name:
437         HeapFree(PSDRV_Heap, 0, font_name);
438     cleanup_encoding_scheme:
439         HeapFree(PSDRV_Heap, 0, encoding_scheme);
440     cleanup_afm:
441         HeapFree(PSDRV_Heap, 0, afm);
442         
443     return retval;
444 }
445                                     
446 /*******************************************************************************
447  *  ReadTrueTypeFile
448  *
449  *  Reads font metrics from TrueType font file.  Only returns FALSE for
450  *  unexpected errors (memory allocation failure or FreeType error).
451  *
452  */
453 static BOOL ReadTrueTypeFile(FT_Library library, LPCSTR filename)
454 {
455     FT_Error        error;
456     FT_Face         face;
457     
458     TRACE("%s\n", filename);
459     
460     error = FT_New_Face(library, filename, 0, &face);
461     if (error != FT_Err_Ok)
462     {
463         WARN("FreeType error %i opening %s\n", error, filename);
464         return TRUE;
465     }
466     
467     if ((face->face_flags & REQUIRED_FACE_FLAGS) == REQUIRED_FACE_FLAGS)
468     {
469         if (BuildTrueTypeAFM(face) == FALSE)
470         {
471             FT_Done_Face(face);
472             return FALSE;
473         }
474     }
475     else
476     {
477         WARN("Required information missing from %s\n", filename);
478     }
479     
480     error = FT_Done_Face(face);
481     if (error != FT_Err_Ok)
482     {
483         ERR("%s returned %i\n", "FT_Done_Face", error);
484         return FALSE;
485     }
486     
487     return TRUE;
488 }
489
490 /*******************************************************************************
491  *  ReadTrueTypeDir
492  *
493  *  Reads all TrueType font files in a directory.
494  *
495  */
496 static BOOL ReadTrueTypeDir(FT_Library library, LPCSTR dirname)
497 {
498     struct dirent   *dent;
499     DIR             *dir;
500     CHAR            filename[256];
501     
502     dir = opendir(dirname);
503     if (dir == NULL)
504     {
505         WARN("'%s' opening %s\n", strerror(errno), dirname);
506         return TRUE;;
507     }
508     
509     while ((dent = readdir(dir)) != NULL)
510     {
511         CHAR        *file_extension = strrchr(dent->d_name, '.');
512         int         fn_len;
513         
514         if (file_extension == NULL || strcasecmp(file_extension, ".ttf") != 0)
515             continue;
516             
517         fn_len = snprintf(filename, 256, "%s/%s", dirname, dent->d_name);
518         if (fn_len < 0 || fn_len > sizeof(filename) - 1)
519         {
520             WARN("Path '%s/%s' is too long\n", dirname, dent->d_name);
521             continue;
522         }
523         
524         if (ReadTrueTypeFile(library, filename) ==  FALSE)
525         {
526             closedir(dir);
527             return FALSE;
528         }
529     }
530     
531     closedir(dir);
532     
533     return TRUE;
534 }
535                             
536 /*******************************************************************************
537  *  PSDRV_GetTrueTypeMetrics
538  *
539  *  Reads font metrics from TrueType font files in directories listed in the
540  *  [TrueType Font Directories] section of the Wine configuration file.
541  *
542  *  If this function fails (returns FALSE), the driver will fail to initialize
543  *  and the driver heap will be destroyed, so it's not necessary to HeapFree
544  *  everything in that event.
545  *
546  */
547 BOOL PSDRV_GetTrueTypeMetrics(void)
548 {
549     CHAR        name_buf[256], value_buf[256];
550     INT         i = 0;
551     FT_Error    error;
552     FT_Library  library;
553     HKEY        hkey;
554     DWORD       type, name_len, value_len;
555
556     if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,
557             "Software\\Wine\\Wine\\Config\\TrueType Font Directories",
558             0, KEY_READ, &hkey) != ERROR_SUCCESS)
559         return TRUE;
560
561     error = FT_Init_FreeType(&library);
562     if (error != FT_Err_Ok)
563     {
564         ERR("%s returned %i\n", "FT_Init_FreeType", error);
565         RegCloseKey(hkey);
566         return FALSE;
567     }
568
569     name_len = sizeof(name_buf);
570     value_len = sizeof(value_buf);
571     
572     while (RegEnumValueA(hkey, i++, name_buf, &name_len, NULL, &type, value_buf,
573             &value_len) == ERROR_SUCCESS)
574     {
575         value_buf[sizeof(value_buf) - 1] = '\0';
576         
577         if (ReadTrueTypeDir(library, value_buf) == FALSE)
578         {
579             RegCloseKey(hkey);
580             FT_Done_FreeType(library);
581             return FALSE;
582         }
583         
584         /* initialize lengths for new iteration */
585         
586         name_len = sizeof(name_buf);
587         value_len = sizeof(value_buf);
588     }
589     
590     RegCloseKey(hkey);
591     FT_Done_FreeType(library);
592     return TRUE;
593 }
594
595 #endif  /* HAVE_FREETYPE */