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