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