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