Added an unknown VxD error code.
[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 #include "config.h"
9
10 #ifdef HAVE_FREETYPE
11
12 #include <freetype/freetype.h>
13 #include <freetype/ftnames.h>
14 #include <freetype/ttnameid.h>
15 #include <freetype/tttables.h>
16 #include <freetype/ftglyph.h>
17 #include <sys/types.h>
18 #include <dirent.h>
19 #include <string.h>
20 #include <stdio.h>
21
22 #include "winnt.h"
23 #include "options.h"
24 #include "psdrv.h"
25 #include "debugtools.h"
26 #include "heap.h"
27
28 DEFAULT_DEBUG_CHANNEL(psdrv);
29
30
31 #define REQUIRED_FACE_FLAGS     (   FT_FACE_FLAG_SCALABLE   |   \
32                                     FT_FACE_FLAG_HORIZONTAL |   \
33                                     FT_FACE_FLAG_SFNT       |   \
34                                     FT_FACE_FLAG_GLYPH_NAMES    )
35
36 static FT_Library       library;
37 static FT_Face          face;
38 static FT_CharMap       charmap;
39 static TT_Header        *head;
40 static TT_Postscript    *post;
41 static TT_OS2           *os2;
42 static TT_HoriHeader    *hhea;
43
44 /*******************************************************************************
45  *
46  *  FindCharMap
47  *
48  *  Sets charmap and points afm->EncodingScheme to encoding name (in driver
49  *  heap).  Leaves both uninitialized if font contains no Windows encoding.
50  *
51  *  Returns FALSE to indicate memory allocation error.
52  *
53  */
54 static const char *encoding_names[7] =
55 {
56     "WindowsSymbol",        /* TT_MS_ID_SYMBOL_CS */
57     "WindowsUnicode",       /* TT_MS_ID_UNICODE_CS */
58     "WindowsShiftJIS",      /* TT_MS_ID_SJIS */
59     "WindowsPRC",           /* TT_MS_ID_GB2312 */
60     "WindowsBig5",          /* TT_MS_ID_BIG_5 */
61     "WindowsWansung",       /* TT_MS_ID_WANSUNG */
62     "WindowsJohab"          /* TT_MS_ID_JOHAB */
63 };
64  
65 static BOOL FindCharMap(AFM *afm)
66 {
67     FT_Int      i;
68     FT_Error    error;
69     
70     charmap = NULL;
71     
72     for (i = 0; i < face->num_charmaps; ++i)
73     {
74         if (face->charmaps[i]->platform_id != TT_PLATFORM_MICROSOFT)
75             continue;
76             
77         if (face->charmaps[i]->encoding_id == TT_MS_ID_UNICODE_CS)
78         {
79             charmap = face->charmaps[i];
80             break;
81         }
82             
83         if (charmap == NULL)
84             charmap = face->charmaps[i];
85     }
86     
87     if (charmap == NULL)
88         return TRUE;
89         
90     error = FT_Set_Charmap(face, charmap);
91     if (error != FT_Err_Ok)
92     {
93         ERR("%s returned %i\n", "FT_Set_CharMap", error);
94         return FALSE;
95     }
96         
97     if (charmap->encoding_id < 7)
98     {
99         afm->EncodingScheme = HEAP_strdupA(PSDRV_Heap, 0,
100                 encoding_names[charmap->encoding_id]);
101         if (afm->EncodingScheme == NULL)
102             return FALSE;
103     }
104     else
105     {
106         afm->EncodingScheme = HeapAlloc(PSDRV_Heap, 0,
107                 sizeof("WindowsUnknown") + 1 + charmap->encoding_id / 10);
108         if (afm->EncodingScheme == NULL)
109             return FALSE;
110             
111         sprintf(afm->EncodingScheme, "%s%u", "WindowsUnknown",
112                 charmap->encoding_id);
113     }
114     
115     return TRUE;
116 }
117
118 /*******************************************************************************
119  *  NameTableString
120  *
121  *  Converts a name table string to a null-terminated character string.  The
122  *  space for the character string is allocated from the driver heap.
123  *
124  *  This function handles only platform_id = 3 (TT_PLATFORM_MICROSOFT) -- 16-bit
125  *  big-endian strings.  It also only handles ASCII character codes (< 128).
126  *
127  *  This function will set *sz to NULL if it cannot parse the string, but it
128  *  will only return FALSE in the event of an unexpected error (memory
129  *  allocation failure).
130  *
131  */
132 static BOOL NameTableString(LPSTR *sz, const FT_SfntName *name)
133 {    
134     FT_UShort   i, len, *ws;
135     LPSTR       s;
136     
137     if (name->platform_id != TT_PLATFORM_MICROSOFT)
138     {
139         ERR("Unsupported encoding %i\n", name->platform_id);
140         return FALSE;       /* should never get here */
141     }
142     
143     len = name->string_len / 2;
144     s = *sz = HeapAlloc(PSDRV_Heap, 0, len + 1);
145     if (s == NULL)
146         return FALSE;
147     ws = (FT_UShort *)(name->string);
148         
149     for (i = 0; i < len; ++i, ++s, ++ws)
150     {
151         FT_UShort   wc = *ws;
152         
153 #ifndef WORDS_BIGENDIAN
154         wc = (wc >> 8) | (wc << 8);
155 #endif
156
157         if (wc > 127)
158         {
159             WARN("Non-ASCII character 0x%.4x\n", wc);
160             HeapFree(PSDRV_Heap, 0, *sz);
161             *sz = NULL;
162             return TRUE;
163         }
164         
165         *s = (CHAR)wc;
166     }
167     
168     *s = '\0';
169     return TRUE;
170 }
171
172 /*******************************************************************************
173  *  ReadNameTable
174  *
175  *  Reads various font names from the TrueType 'NAME' table.  Currently looks
176  *  for U.S. English names only,
177  *
178  *  May leave a pointer uninitialized if the desired string is not present;
179  *  returns FALSE only in the event of an unexpected error.
180  *
181  */
182 static BOOL ReadNameTable(AFM *afm)
183 {
184     FT_UInt     numStrings, stringIndex;
185     FT_SfntName name;
186     FT_Error    error;
187
188     numStrings = FT_Get_Sfnt_Name_Count(face);
189     
190     for (stringIndex = 0; stringIndex < numStrings; ++stringIndex)
191     {
192         error = FT_Get_Sfnt_Name(face, stringIndex, &name);
193         if (error != FT_Err_Ok)
194         {
195             ERR("%s returned %i\n", "FT_Get_Sfnt_Name", error);
196             return FALSE;
197         }
198         
199         /* FIXME - Handle other languages? */
200         
201         if (name.language_id != TT_MS_LANGID_ENGLISH_UNITED_STATES ||
202                 name.platform_id != charmap->platform_id ||
203                 name.encoding_id != charmap->encoding_id)
204             continue;
205         
206         switch (name.name_id)
207         {
208             case TT_NAME_ID_FONT_FAMILY:
209             
210                 if (NameTableString(&(afm->FamilyName), &name) == FALSE)
211                     return FALSE;
212                 break;
213             
214             case TT_NAME_ID_FULL_NAME:
215             
216                 if (NameTableString(&(afm->FullName), &name) == FALSE)
217                     return FALSE;
218                 break;
219             
220             case TT_NAME_ID_PS_NAME:
221             
222                 if (NameTableString(&(afm->FontName), &name) == FALSE)
223                     return FALSE;
224                 break;
225         }
226     }
227     
228     return TRUE;
229 }
230
231 /*******************************************************************************
232  *  FreeAFM
233  *
234  *  Frees an AFM and all subsidiary objects.  For this function to work
235  *  properly, the AFM must have been allocated with HEAP_ZERO_MEMORY, and the
236  *  UNICODEVECTOR and it's associated array of UNICODEGLYPHs must have been
237  *  allocated as a single object.
238  */
239 static void FreeAFM(AFM *afm)
240 {
241     if (afm->FontName != NULL)
242         HeapFree(PSDRV_Heap, 0, afm->FontName);
243     if (afm->FullName != NULL)
244         HeapFree(PSDRV_Heap, 0, afm->FullName);
245     if (afm->FamilyName != NULL)
246         HeapFree(PSDRV_Heap, 0, afm->FamilyName);
247     if (afm->EncodingScheme != NULL)
248         HeapFree(PSDRV_Heap, 0, afm->EncodingScheme);
249     if (afm->Metrics != NULL)
250         HeapFree(PSDRV_Heap, 0, afm->Metrics);
251     if (afm->Encoding != NULL)
252         HeapFree(PSDRV_Heap, 0, afm->Encoding);
253         
254     HeapFree(PSDRV_Heap, 0, afm);
255 }
256
257 /*******************************************************************************
258  *  PSUnits
259  *
260  *  Convert TrueType font units (relative to font em square) to PostScript
261  *  units.  This is defined as a macro, so it can handle different TrueType
262  *  data types as inputs.
263  *
264  */
265 #define PSUnits(x)  (((float)(x)) * 1000.0 / ((float)(head->Units_Per_EM)))
266
267 /*******************************************************************************
268  *  ReadMetricsTables
269  *
270  *  Reads basic font metrics from the 'head', 'post', and 'OS/2' tables.
271  *  Returns FALSE if any table is missing.
272  *
273  */ 
274 static BOOL ReadMetricsTables(AFM *afm)
275 {
276     head = FT_Get_Sfnt_Table(face, ft_sfnt_head);
277     post = FT_Get_Sfnt_Table(face, ft_sfnt_post);
278     hhea = FT_Get_Sfnt_Table(face, ft_sfnt_hhea);
279     os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2);
280     
281     if (head == NULL || post == NULL || hhea == NULL || os2 == NULL)
282         return FALSE;
283     
284     if (os2->version == 0xffff)             /* Old Macintosh font */
285         return FALSE;
286     
287     afm->Weight = os2->usWeightClass;
288     afm->ItalicAngle = ((float)(post->italicAngle)) / 65536.0;
289     afm->IsFixedPitch = (post->isFixedPitch == 0) ? FALSE : TRUE;
290     afm->UnderlinePosition = PSUnits(post->underlinePosition);
291     afm->UnderlineThickness = PSUnits(post->underlineThickness);
292             
293     afm->FontBBox.llx = PSUnits(head->xMin);
294     afm->FontBBox.lly = PSUnits(head->yMin);
295     afm->FontBBox.urx = PSUnits(head->xMax);
296     afm->FontBBox.ury = PSUnits(head->yMax);
297     
298     /* CapHeight & XHeight set by ReadCharMetrics */
299     
300     afm->Ascender = PSUnits(os2->sTypoAscender);
301     afm->Descender = PSUnits(os2->sTypoDescender);
302     afm->FullAscender = afm->FontBBox.ury;          /* get rid of this */
303     
304     afm->WinMetrics.usUnitsPerEm = head->Units_Per_EM;
305     afm->WinMetrics.sAscender = hhea->Ascender;
306     afm->WinMetrics.sDescender = hhea->Descender;
307     afm->WinMetrics.sLineGap = hhea->Line_Gap;
308     afm->WinMetrics.sTypoAscender = os2->sTypoAscender;
309     afm->WinMetrics.sTypoDescender = os2->sTypoDescender;
310     afm->WinMetrics.sTypoLineGap = os2->sTypoLineGap;
311     afm->WinMetrics.usWinAscent = os2->usWinAscent;
312     afm->WinMetrics.usWinDescent = os2->usWinDescent;
313     
314     return TRUE;
315 }
316
317 /*******************************************************************************
318  *  ReadCharMetrics
319  *
320  *  Reads metrics for each glyph in a TrueType font.  Since FreeAFM will try to
321  *  free afm->Metrics and afm->Encoding if they are non-NULL, don't free them
322  *  in the event of an error.  (FreeAFM depends on the fact that afm->Encoding
323  *  and its associated array of UNICODEGLYPHs are allocated as a single object.)
324  *
325  */
326 static BOOL ReadCharMetrics(AFM *afm)
327 {
328     FT_ULong        charcode, index;
329     UNICODEGLYPH    *glyphs;
330     
331     /*
332      *  There does not seem to be an easy way to get the number of characters
333      *  in an encoding out of a TrueType font.
334      */
335     for (charcode = 0, index = 0; charcode < 65536; ++charcode)
336     {
337         if (FT_Get_Char_Index(face, charcode) != 0)
338             ++index;
339     }
340     
341     afm->NumofMetrics = index;
342     
343     afm->Metrics = HeapAlloc(PSDRV_Heap, 0, index * sizeof(AFMMETRICS));
344     afm->Encoding = HeapAlloc(PSDRV_Heap, 0, sizeof(UNICODEVECTOR) +
345             index * sizeof(UNICODEGLYPH));
346     if (afm->Metrics == NULL || afm->Encoding == NULL)
347         return FALSE;
348         
349     glyphs = (UNICODEGLYPH *)(afm->Encoding + 1);
350     afm->Encoding->size = index;
351     afm->Encoding->glyphs = glyphs;
352     
353     for (charcode = 0, index = 0; charcode <= 65536; ++charcode)
354     {
355         FT_UInt     glyph_index = FT_Get_Char_Index(face, charcode);
356         FT_Error    error;
357         FT_Glyph    glyph;
358         FT_BBox     bbox;
359         char        buffer[256];
360         
361         if (glyph_index == 0)
362             continue;
363             
364         error = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_SCALE |
365                 FT_LOAD_IGNORE_TRANSFORM | FT_LOAD_LINEAR_DESIGN);
366         if (error != FT_Err_Ok)
367         {
368             ERR("%s returned %i\n", "FT_Load_Glyph", error);
369             return FALSE;
370         }
371         
372         error = FT_Get_Glyph(face->glyph, &glyph);
373         if (error != FT_Err_Ok)
374         {
375             ERR("%s returned %i\n", "FT_Get_Glyph", error);
376             return FALSE;
377         }
378         
379         FT_Glyph_Get_CBox(glyph, ft_glyph_bbox_unscaled, &bbox);
380         
381         error = FT_Get_Glyph_Name(face, glyph_index, buffer, 255);
382         if (error != FT_Err_Ok)
383         {
384             ERR("%s returned %i\n", "FT_Get_Glyph_Name", error);
385             return FALSE;
386         }
387         
388         afm->Metrics[index].N = PSDRV_GlyphName(buffer);
389         if (afm->Metrics[index].N == NULL)
390             return FALSE;
391         
392         afm->Metrics[index].C = charcode;
393         afm->Metrics[index].UV = charcode;
394         afm->Metrics[index].WX = PSUnits(face->glyph->metrics.horiAdvance);
395         afm->Metrics[index].B.llx = PSUnits(bbox.xMin);
396         afm->Metrics[index].B.lly = PSUnits(bbox.yMin);
397         afm->Metrics[index].B.urx = PSUnits(bbox.xMax);
398         afm->Metrics[index].B.ury = PSUnits(bbox.yMax);
399         afm->Metrics[index].L = NULL;
400         
401         TRACE("Metrics for '%s' WX = %f B = %f,%f - %f,%f\n",
402                 afm->Metrics[index].N->sz, afm->Metrics[index].WX,
403                 afm->Metrics[index].B.llx, afm->Metrics[index].B.lly,
404                 afm->Metrics[index].B.urx, afm->Metrics[index].B.ury);
405         
406         glyphs[index].UV = charcode;
407         glyphs[index].name = afm->Metrics[index].N;
408         
409         if (charcode == 0x0048)                     /* 'H' */
410             afm->CapHeight = PSUnits(bbox.yMax);
411         if (charcode == 0x0078)                     /* 'x' */
412             afm->XHeight = PSUnits(bbox.yMax);
413                 
414         ++index;
415     }
416     
417     return TRUE;
418 }
419
420 /*******************************************************************************
421  *  ReadTrueTypeAFM
422  *
423  *  Fills in AFM structure for opened TrueType font file.  Returns FALSE only on
424  *  an unexpected error (memory allocation failure or FreeType error); otherwise
425  *  returns TRUE.  Leaves it to the caller (ReadTrueTypeFile) to clean up.
426  *
427  */
428 static BOOL ReadTrueTypeAFM(AFM *afm)
429 {
430
431     if ((face->face_flags & REQUIRED_FACE_FLAGS) != REQUIRED_FACE_FLAGS)
432     {
433         WARN("Font flags do not match requirements\n");
434         return TRUE;
435     }
436     
437     if (FindCharMap(afm) == FALSE)
438         return FALSE;
439         
440     if (charmap == NULL)
441     {
442         WARN("No Windows encodings in font\n");
443         return TRUE;
444     }
445     
446     TRACE("Using encoding '%s'\n", afm->EncodingScheme);
447
448     if (ReadNameTable(afm) == FALSE)
449         return FALSE;
450    
451     if (afm->FamilyName == NULL || afm->FullName == NULL ||
452             afm->FontName == NULL)
453     {
454         WARN("Required strings missing from font\n");
455         return TRUE;
456     }
457
458     if (ReadMetricsTables(afm) == FALSE)    /* Non-fatal */
459     {
460         WARN("Required metrics tables missing from font\n");
461         return TRUE;
462     }
463     
464     if (ReadCharMetrics(afm) == FALSE)
465         return FALSE;
466
467     return TRUE;
468 }
469
470 /*******************************************************************************
471  *  ReadTrueTypeFile
472  *
473  *  Reads PostScript-style font metrics from a TrueType font file.  Only returns
474  *  FALSE for unexpected errors (memory allocation, etc.); returns TRUE if it's
475  *  just a bad font file.
476  *
477  */
478 static BOOL ReadTrueTypeFile(LPCSTR filename)
479 {
480     FT_Error    error;
481     AFM         *afm;
482     
483     TRACE("'%s'\n", filename);
484
485     afm = HeapAlloc(PSDRV_Heap, HEAP_ZERO_MEMORY, sizeof(AFM));
486     if (afm == NULL)
487         return FALSE;
488
489     error = FT_New_Face(library, filename, 0, &face);
490
491     if (error != FT_Err_Ok)
492     {
493         WARN("FreeType error %i opening '%s'\n", error, filename);
494         HeapFree(PSDRV_Heap, 0, afm);
495         return TRUE;
496     }
497     
498     if (ReadTrueTypeAFM(afm) == FALSE)
499     {
500         FreeAFM(afm);
501         FT_Done_Face(face);
502         return FALSE;
503     }    
504
505     error = FT_Done_Face(face);
506     if (error != FT_Err_Ok)
507     {
508         ERR("%s returned %i\n", "FT_Done_Face", error);
509         FreeAFM(afm);
510         return FALSE;
511     }
512     
513     if (afm->Encoding == NULL)      /* last element to be set */
514     {
515         FreeAFM(afm);
516         return TRUE;
517     }
518     
519     if (PSDRV_AddAFMtoList(&PSDRV_AFMFontList, afm) == FALSE)
520     {
521         FreeAFM(afm);
522         return FALSE;
523     }
524     
525     return TRUE;
526 }
527     
528
529
530 /*******************************************************************************
531  *  PSDRV_GetTrueTypeMetrics
532  *
533  *  Reads PostScript-stype font metrics from TrueType font files in directories
534  *  listed in the [TrueType Font Directories] section of the Wine configuration
535  *  file.
536  *
537  *  If this function fails, the driver will fail to initialize and the driver
538  *  heap will be destroyed, so it's not necessary to HeapFree everything in
539  *  that event.
540  *
541  */
542 BOOL PSDRV_GetTrueTypeMetrics()
543 {
544     CHAR        keybuf[256], namebuf[256];
545     INT         i = 0;
546     FT_Error    error;
547
548     error = FT_Init_FreeType(&library);
549     if (error != FT_Err_Ok)
550     {
551         ERR("%s returned %i\n", "FT_Init_FreeType", error);
552         return FALSE;
553     }
554
555     while (PROFILE_EnumWineIniString("TrueType Font Directories", i++, keybuf,
556             sizeof(keybuf), namebuf, sizeof(namebuf)))
557     {
558         struct dirent   *dent;
559         DIR             *dir;
560         INT             dnlen;      /* directory name length */
561
562         namebuf[sizeof(namebuf) - 1] = '\0';
563         dir = opendir(namebuf);
564         if (dir == NULL)
565         {
566             WARN("Error opening directory '%s'\n", namebuf);
567             continue;
568         }
569         
570         dnlen = strlen(namebuf);
571         namebuf[dnlen] = '/';           /* 2 slashes is OK, 0 is not */
572         ++dnlen;
573         
574         while ((dent = readdir(dir)) != NULL)
575         {
576             INT fnlen;      /* file name length */
577             
578             fnlen = strlen(dent->d_name);
579             
580             if (fnlen < 5 || strcasecmp(dent->d_name + fnlen - 4, ".ttf") != 0)
581             {
582                 TRACE("Skipping filename '%s'\n", dent->d_name);
583                 continue;
584             }
585             
586             if (dnlen + fnlen + 1 > sizeof(namebuf))    /* allow for '\0' */
587             {
588                 WARN("Path '%s/%s' is too long\n", namebuf, dent->d_name);
589                 continue;
590             }
591             
592             memcpy(namebuf + dnlen, dent->d_name, fnlen + 1);
593             
594             if (ReadTrueTypeFile(namebuf) == FALSE)
595             {
596                 ERR("Error reading '%s'\n", namebuf);
597                 closedir(dir);
598                 FT_Done_FreeType(library);
599                 return FALSE;
600             }
601         }
602         
603         closedir(dir);
604     }
605    
606     FT_Done_FreeType(library);
607     return TRUE;
608 }
609
610 #endif  /* HAVE_FREETYPE */