push cc8bc80451cc24f4d7cf75168b569f0ebfe19547
[wine/hacks.git] / dlls / wineps.drv / truetype.c
blobb5269590da1b269494efc1e7c42fdbe7a6a2acda
1 /*******************************************************************************
2 * TrueType font-related functions for Wine PostScript driver. Currently just
3 * uses FreeType to read font metrics.
5 * Copyright 2001 Ian Pilcher
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.
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.
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
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.
28 #include "config.h"
29 #include "wine/port.h"
31 #ifdef HAVE_FREETYPE
34 * These stupid #ifdefs should work for FreeType 2.0.1 and 2.0.2. Beyond that
35 * is anybody's guess.
38 #ifdef HAVE_FT2BUILD_H
39 #include <ft2build.h>
40 #endif
41 #ifdef HAVE_FREETYPE_FREETYPE_H
42 #include <freetype/freetype.h>
43 #endif
44 #ifdef HAVE_FREETYPE_FTGLYPH_H
45 #include <freetype/ftglyph.h>
46 #endif
47 #ifdef HAVE_FREETYPE_TTTABLES_H
48 #include <freetype/tttables.h>
49 #endif
50 #ifdef HAVE_FREETYPE_FTSNAMES_H
51 #include <freetype/ftsnames.h>
52 #else
53 # ifdef HAVE_FREETYPE_FTNAMES_H
54 # include <freetype/ftnames.h>
55 # endif
56 #endif
57 #ifdef HAVE_FREETYPE_TTNAMEID_H
58 #include <freetype/ttnameid.h>
59 #endif
61 #include <sys/types.h>
62 #ifdef HAVE_DIRENT_H
63 # include <dirent.h>
64 #endif
65 #include <string.h>
66 #include <stdarg.h>
67 #include <stdio.h>
68 #include <errno.h>
70 #include "windef.h"
71 #include "winbase.h"
72 #include "winerror.h"
73 #include "winreg.h"
74 #include "psdrv.h"
75 #include "wine/debug.h"
77 WINE_DEFAULT_DEBUG_CHANNEL(psdrv);
79 #define REQUIRED_FACE_FLAGS ( FT_FACE_FLAG_SCALABLE | \
80 FT_FACE_FLAG_HORIZONTAL | \
81 FT_FACE_FLAG_SFNT | \
82 FT_FACE_FLAG_GLYPH_NAMES )
84 #define GLYPH_LOAD_FLAGS ( FT_LOAD_NO_SCALE | \
85 FT_LOAD_IGNORE_TRANSFORM | \
86 FT_LOAD_LINEAR_DESIGN )
88 static void *ft_handle = NULL;
90 #define MAKE_FUNCPTR(f) static typeof(f) * p##f = NULL;
91 MAKE_FUNCPTR(FT_Done_Face)
92 MAKE_FUNCPTR(FT_Done_FreeType)
93 MAKE_FUNCPTR(FT_Get_Char_Index)
94 MAKE_FUNCPTR(FT_Get_Glyph_Name)
95 MAKE_FUNCPTR(FT_Get_Sfnt_Name)
96 MAKE_FUNCPTR(FT_Get_Sfnt_Name_Count)
97 MAKE_FUNCPTR(FT_Get_Sfnt_Table)
98 MAKE_FUNCPTR(FT_Init_FreeType)
99 MAKE_FUNCPTR(FT_Load_Glyph)
100 MAKE_FUNCPTR(FT_New_Face)
101 MAKE_FUNCPTR(FT_Set_Charmap)
102 #undef MAKE_FUNCPTR
104 /*******************************************************************************
105 * FindCharMap
107 * Finds Windows character map and creates "EncodingScheme" string. Returns
108 * FALSE to indicate memory allocation or FreeType error; sets *p_charmap to
109 * NULL if no Windows encoding is present.
111 * Returns Unicode character map if present; otherwise uses the first Windows
112 * character map found.
115 static const LPCSTR encoding_names[7] =
117 "WindowsSymbol", /* TT_MS_ID_SYMBOL_CS */
118 "WindowsUnicode", /* TT_MS_ID_UNICODE_CS */
119 "WindowsShiftJIS", /* TT_MS_ID_SJIS */
120 "WindowsPRC", /* TT_MS_ID_GB2312 */
121 "WindowsBig5", /* TT_MS_ID_BIG_5 */
122 "WindowsWansung", /* TT_MS_ID_WANSUNG */
123 "WindowsJohab" /* TT_MS_ID_JOHAB */
124 /* "WindowsUnknown65535" is the longest possible (encoding_id is a UShort) */
127 static BOOL FindCharMap(FT_Face face, FT_CharMap *p_charmap, LPSTR *p_sz)
129 FT_Int i;
130 FT_Error error;
131 FT_CharMap charmap = NULL;
133 for (i = 0; i < face->num_charmaps; ++i)
135 if (face->charmaps[i]->platform_id != TT_PLATFORM_MICROSOFT)
136 continue;
138 if (face->charmaps[i]->encoding_id == TT_MS_ID_UNICODE_CS)
140 charmap = face->charmaps[i];
141 break;
144 if (charmap == NULL)
145 charmap = face->charmaps[i];
148 *p_charmap = charmap;
150 if (charmap == NULL)
152 WARN("No Windows character map found\n");
153 return TRUE;
156 error = pFT_Set_Charmap(face, charmap);
157 if (error != FT_Err_Ok)
159 ERR("%s returned %i\n", "FT_Set_Charmap", error);
160 return FALSE;
163 *p_sz = HeapAlloc(PSDRV_Heap, 0, sizeof("WindowsUnknown65535"));
164 if (*p_sz == NULL)
165 return FALSE;
167 if (charmap->encoding_id < 7)
168 strcpy(*p_sz, encoding_names[charmap->encoding_id]);
169 else
170 sprintf(*p_sz, "%s%u", "WindowsUnknown", charmap->encoding_id);
172 return TRUE;
175 /*******************************************************************************
176 * MSTTStrToSz
178 * Converts a string in the TrueType NAME table to a null-terminated ASCII
179 * character string. Space for the string is allocated from the driver heap.
180 * Only handles platform_id = 3 (TT_PLATFORM_MICROSOFT) strings (16-bit, big
181 * endian). It also only handles ASCII character codes (< 128).
183 * Sets *p_sz to NULL if string cannot be converted; only returns FALSE for
184 * memory allocation failure.
187 static BOOL MSTTStrToSz(const FT_SfntName *name, LPSTR *p_sz)
189 FT_UShort i;
190 INT len;
191 BYTE *wsz;
192 LPSTR sz;
194 len = name->string_len / 2; /* # of 16-bit chars */
196 *p_sz = sz = HeapAlloc(PSDRV_Heap, 0, len + 1);
197 if (sz == NULL)
198 return FALSE;
200 wsz = (BYTE *)name->string;
202 for (i = 0; i < len; ++i, ++sz)
204 USHORT wc = (wsz[0] << 8) + wsz[1];
205 wsz += 2;
207 if (wc > 127)
209 WARN("Non-ASCII character 0x%.4x\n", wc);
210 HeapFree(PSDRV_Heap, 0, *p_sz);
211 *p_sz = NULL;
212 return TRUE;
215 *sz = (CHAR)wc;
218 *sz = '\0';
220 return TRUE;
223 /*******************************************************************************
224 * FindMSTTString
226 * Finds the requested Microsoft platform string in the TrueType NAME table and
227 * converts it to a null-terminated ASCII string. Currently looks for U.S.
228 * English names only.
230 * Sets string to NULL if not present or cannot be converted; returns FALSE
231 * only for memory allocation failure.
234 static BOOL FindMSTTString(FT_Face face, FT_CharMap charmap, FT_UShort name_id,
235 LPSTR *p_sz)
237 FT_UInt num_strings, string_index;
238 FT_SfntName name;
239 FT_Error error;
241 num_strings = pFT_Get_Sfnt_Name_Count(face);
243 for (string_index = 0; string_index < num_strings; ++string_index)
245 error = pFT_Get_Sfnt_Name(face, string_index, &name);
246 if (error != FT_Err_Ok)
248 ERR("%s returned %i\n", "FT_Get_Sfnt_Name", error);
249 return FALSE;
252 /* FIXME - Handle other languages? */
254 if (name.platform_id != TT_PLATFORM_MICROSOFT ||
255 name.language_id != TT_MS_LANGID_ENGLISH_UNITED_STATES)
256 continue;
258 if (name.platform_id != charmap->platform_id ||
259 name.encoding_id != charmap->encoding_id)
260 continue;
262 if (name.name_id != name_id)
263 continue;
265 return MSTTStrToSz(&name, p_sz);
268 *p_sz = NULL; /* didn't find it */
270 return TRUE;
273 /*******************************************************************************
274 * PSUnits
276 * Convert TrueType font units (relative to font em square) to PostScript
277 * units.
280 static inline float PSUnits(LONG x, USHORT em_size)
282 return 1000.0 * (float)x / (float)em_size;
285 /*******************************************************************************
286 * StartAFM
288 * Allocates space for the AFM on the driver heap and reads basic font metrics
289 * from the HEAD, POST, HHEA, and OS/2 tables. Returns FALSE for memory
290 * allocation error; sets *p_afm to NULL if required information is missing.
293 static BOOL StartAFM(FT_Face face, AFM **p_afm)
295 TT_Header *head;
296 TT_Postscript *post;
297 TT_OS2 *os2;
298 TT_HoriHeader *hhea;
299 USHORT em_size;
300 AFM *afm;
302 head = pFT_Get_Sfnt_Table(face, ft_sfnt_head);
303 post = pFT_Get_Sfnt_Table(face, ft_sfnt_post);
304 os2 = pFT_Get_Sfnt_Table(face, ft_sfnt_os2);
305 hhea = pFT_Get_Sfnt_Table(face, ft_sfnt_hhea);
307 if (head == NULL || post == NULL || os2 == NULL || hhea == NULL ||
308 os2->version == 0xffff) /* old Macintosh font */
310 WARN("Required table(s) missing\n");
311 *p_afm = NULL;
312 return TRUE;
315 *p_afm = afm = HeapAlloc(PSDRV_Heap, 0, sizeof(*afm));
316 if (afm == NULL)
317 return FALSE;
319 afm->WinMetrics.usUnitsPerEm = em_size = head->Units_Per_EM;
320 afm->WinMetrics.sAscender = hhea->Ascender;
321 afm->WinMetrics.sDescender = hhea->Descender;
322 afm->WinMetrics.sLineGap = hhea->Line_Gap;
323 afm->WinMetrics.sTypoAscender = os2->sTypoAscender;
324 afm->WinMetrics.sTypoDescender = os2->sTypoDescender;
325 afm->WinMetrics.sTypoLineGap = os2->sTypoLineGap;
326 afm->WinMetrics.usWinAscent = os2->usWinAscent;
327 afm->WinMetrics.usWinDescent = os2->usWinDescent;
328 afm->WinMetrics.sAvgCharWidth = os2->xAvgCharWidth;
330 afm->Weight = os2->usWeightClass;
331 afm->ItalicAngle = ((float)(post->italicAngle)) / 65536.0;
332 afm->IsFixedPitch = (post-> isFixedPitch == 0) ? FALSE : TRUE;
333 afm->UnderlinePosition = PSUnits(post->underlinePosition, em_size);
334 afm->UnderlineThickness = PSUnits(post->underlineThickness, em_size);
336 afm->FontBBox.llx = PSUnits(head->xMin, em_size);
337 afm->FontBBox.lly = PSUnits(head->yMin, em_size);
338 afm->FontBBox.urx = PSUnits(head->xMax, em_size);
339 afm->FontBBox.ury = PSUnits(head->yMax, em_size);
341 afm->Ascender = PSUnits(os2->sTypoAscender, em_size);
342 afm->Descender = PSUnits(os2->sTypoDescender, em_size);
344 return TRUE;
347 /*******************************************************************************
348 * ReadCharMetrics
350 * Reads metrics for each glyph in a TrueType font. Returns false for memory
351 * allocation or FreeType error; sets *p_metrics to NULL for non-fatal error.
354 static BOOL ReadCharMetrics(FT_Face face, AFM *afm, AFMMETRICS **p_metrics)
356 FT_ULong charcode, index;
357 AFMMETRICS *metrics;
358 USHORT em_size = afm->WinMetrics.usUnitsPerEm;
360 for (charcode = 0, index = 0; charcode < 65536; ++charcode)
361 if (pFT_Get_Char_Index(face, charcode) != 0)
362 ++index; /* count # of glyphs */
364 afm->NumofMetrics = index;
366 *p_metrics = metrics = HeapAlloc(PSDRV_Heap, 0, index * sizeof(*metrics));
367 if (metrics == NULL)
368 return FALSE;
370 for (charcode = 0, index = 0; charcode < 65536; ++charcode)
372 FT_UInt glyph_index = pFT_Get_Char_Index(face, charcode);
373 FT_Error error;
374 CHAR buffer[128]; /* for glyph names */
376 if (glyph_index == 0)
377 continue;
379 error = pFT_Load_Glyph(face, glyph_index, GLYPH_LOAD_FLAGS);
380 if (error != FT_Err_Ok)
382 ERR("%s returned %i\n", "FT_Load_Glyph", error);
383 goto cleanup;
386 error = pFT_Get_Glyph_Name(face, glyph_index, buffer, sizeof(buffer));
387 if (error != FT_Err_Ok)
389 ERR("%s returned %i\n", "FT_Get_Glyph_Name", error);
390 goto cleanup;
393 metrics[index].N = PSDRV_GlyphName(buffer);
394 if (metrics[index].N == NULL)
395 goto cleanup;
397 metrics[index].C = metrics[index].UV = charcode;
398 metrics[index].WX = PSUnits(face->glyph->metrics.horiAdvance, em_size);
400 ++index;
403 if (afm->WinMetrics.sAvgCharWidth == 0)
404 afm->WinMetrics.sAvgCharWidth = PSDRV_CalcAvgCharWidth(afm);
406 return TRUE;
408 cleanup:
409 HeapFree(PSDRV_Heap, 0, metrics);
411 return FALSE;
414 /*******************************************************************************
415 * BuildTrueTypeAFM
417 * Builds the AFM for a TrueType font and adds it to the driver font list.
418 * Returns FALSE only on an unexpected error (memory allocation failure or
419 * FreeType error).
422 static BOOL BuildTrueTypeAFM(FT_Face face)
424 AFM *afm;
425 AFMMETRICS *metrics;
426 LPSTR font_name, full_name, family_name, encoding_scheme = NULL;
427 FT_CharMap charmap;
428 BOOL retval, added;
430 retval = StartAFM(face, &afm);
431 if (retval == FALSE || afm == NULL)
432 return retval;
434 retval = FindCharMap(face, &charmap, &encoding_scheme);
435 if (retval == FALSE || charmap == NULL)
436 goto cleanup_afm;
438 retval = FindMSTTString(face, charmap, TT_NAME_ID_PS_NAME, &font_name);
439 if (retval == FALSE || font_name == NULL)
440 goto cleanup_encoding_scheme;
442 retval = FindMSTTString(face, charmap, TT_NAME_ID_FULL_NAME, &full_name);
443 if (retval == FALSE || full_name == NULL)
444 goto cleanup_font_name;
446 retval = FindMSTTString(face, charmap, TT_NAME_ID_FONT_FAMILY,
447 &family_name);
448 if (retval == FALSE || family_name == NULL)
449 goto cleanup_full_name;
451 retval = ReadCharMetrics(face, afm, &metrics);
452 if (retval == FALSE || metrics == NULL)
453 goto cleanup_family_name;
455 afm->EncodingScheme = encoding_scheme; afm->FontName = font_name;
456 afm->FullName = full_name; afm->FamilyName = family_name;
457 afm->Metrics = metrics;
459 retval = PSDRV_AddAFMtoList(&PSDRV_AFMFontList, afm, &added);
460 if (retval == FALSE || added == FALSE)
461 goto cleanup_family_name;
463 return TRUE;
465 /* clean up after fatal or non-fatal errors */
467 cleanup_family_name:
468 HeapFree(PSDRV_Heap, 0, family_name);
469 cleanup_full_name:
470 HeapFree(PSDRV_Heap, 0, full_name);
471 cleanup_font_name:
472 HeapFree(PSDRV_Heap, 0, font_name);
473 cleanup_encoding_scheme:
474 HeapFree(PSDRV_Heap, 0, encoding_scheme);
475 cleanup_afm:
476 HeapFree(PSDRV_Heap, 0, afm);
478 return retval;
481 /*******************************************************************************
482 * ReadTrueTypeFile
484 * Reads font metrics from TrueType font file. Only returns FALSE for
485 * unexpected errors (memory allocation failure or FreeType error).
488 static BOOL ReadTrueTypeFile(FT_Library library, LPCSTR filename)
490 FT_Error error;
491 FT_Face face;
493 TRACE("%s\n", filename);
495 error = pFT_New_Face(library, filename, 0, &face);
496 if (error != FT_Err_Ok)
498 WARN("FreeType error %i opening %s\n", error, filename);
499 return TRUE;
502 if ((face->face_flags & REQUIRED_FACE_FLAGS) == REQUIRED_FACE_FLAGS)
504 if (BuildTrueTypeAFM(face) == FALSE)
506 pFT_Done_Face(face);
507 return FALSE;
510 else
512 WARN("Required information missing from %s\n", filename);
515 error = pFT_Done_Face(face);
516 if (error != FT_Err_Ok)
518 ERR("%s returned %i\n", "FT_Done_Face", error);
519 return FALSE;
522 return TRUE;
525 /*******************************************************************************
526 * ReadTrueTypeDir
528 * Reads all TrueType font files in a directory.
531 static BOOL ReadTrueTypeDir(FT_Library library, LPCSTR dirname)
533 struct dirent *dent;
534 DIR *dir;
535 CHAR filename[256];
537 dir = opendir(dirname);
538 if (dir == NULL)
540 WARN("'%s' opening %s\n", strerror(errno), dirname);
541 return TRUE;
544 while ((dent = readdir(dir)) != NULL)
546 CHAR *file_extension = strrchr(dent->d_name, '.');
547 int fn_len;
549 if (file_extension == NULL || strcasecmp(file_extension, ".ttf") != 0)
550 continue;
552 fn_len = snprintf(filename, 256, "%s/%s", dirname, dent->d_name);
553 if (fn_len < 0 || fn_len > sizeof(filename) - 1)
555 WARN("Path '%s/%s' is too long\n", dirname, dent->d_name);
556 continue;
559 if (ReadTrueTypeFile(library, filename) == FALSE)
561 closedir(dir);
562 return FALSE;
566 closedir(dir);
568 return TRUE;
571 /*******************************************************************************
572 * PSDRV_GetTrueTypeMetrics
574 * Reads font metrics from TrueType font files in directories listed in the
575 * [TrueType Font Directories] section of the Wine configuration file.
577 * If this function fails (returns FALSE), the driver will fail to initialize
578 * and the driver heap will be destroyed, so it's not necessary to HeapFree
579 * everything in that event.
582 BOOL PSDRV_GetTrueTypeMetrics(void)
584 static const WCHAR pathW[] = {'P','a','t','h',0};
585 FT_Error error;
586 FT_Library library;
587 HKEY hkey;
588 DWORD len;
589 LPWSTR valueW;
590 LPSTR valueA, ptr;
592 /* @@ Wine registry key: HKCU\Software\Wine\Fonts */
593 if (RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Fonts", &hkey) != ERROR_SUCCESS)
594 return TRUE;
596 ft_handle = wine_dlopen(SONAME_LIBFREETYPE, RTLD_NOW, NULL, 0);
597 if(!ft_handle) {
598 WINE_MESSAGE(
599 "Wine cannot find the FreeType font library. To enable Wine to\n"
600 "use TrueType fonts please install a version of FreeType greater than\n"
601 "or equal to 2.0.5.\n"
602 "http://www.freetype.org\n");
603 RegCloseKey(hkey);
604 return TRUE;
607 #define LOAD_FUNCPTR(f) if((p##f = wine_dlsym(ft_handle, #f, NULL, 0)) == NULL) goto sym_not_found;
608 LOAD_FUNCPTR(FT_Done_Face)
609 LOAD_FUNCPTR(FT_Done_FreeType)
610 LOAD_FUNCPTR(FT_Get_Char_Index)
611 LOAD_FUNCPTR(FT_Get_Glyph_Name)
612 LOAD_FUNCPTR(FT_Get_Sfnt_Name)
613 LOAD_FUNCPTR(FT_Get_Sfnt_Name_Count)
614 LOAD_FUNCPTR(FT_Get_Sfnt_Table)
615 LOAD_FUNCPTR(FT_Init_FreeType)
616 LOAD_FUNCPTR(FT_Load_Glyph)
617 LOAD_FUNCPTR(FT_New_Face)
618 LOAD_FUNCPTR(FT_Set_Charmap)
619 #undef LOAD_FUNCPTR
621 error = pFT_Init_FreeType(&library);
622 if (error != FT_Err_Ok)
624 ERR("%s returned %i\n", "FT_Init_FreeType", error);
625 wine_dlclose(ft_handle, NULL, 0);
626 RegCloseKey(hkey);
627 return FALSE;
630 if (RegQueryValueExW( hkey, pathW, NULL, NULL, NULL, &len ) == ERROR_SUCCESS)
632 len += sizeof(WCHAR);
633 valueW = HeapAlloc( GetProcessHeap(), 0, len );
634 if (RegQueryValueExW( hkey, pathW, NULL, NULL, (LPBYTE)valueW, &len ) == ERROR_SUCCESS)
636 len = WideCharToMultiByte( CP_UNIXCP, 0, valueW, -1, NULL, 0, NULL, NULL );
637 valueA = HeapAlloc( GetProcessHeap(), 0, len );
638 WideCharToMultiByte( CP_UNIXCP, 0, valueW, -1, valueA, len, NULL, NULL );
639 TRACE( "got font path %s\n", debugstr_a(valueA) );
640 ptr = valueA;
641 while (ptr)
643 LPSTR next = strchr( ptr, ':' );
644 if (next) *next++ = 0;
645 ReadTrueTypeDir( library, ptr );
646 ptr = next;
648 HeapFree( GetProcessHeap(), 0, valueA );
650 HeapFree( GetProcessHeap(), 0, valueW );
653 RegCloseKey(hkey);
654 pFT_Done_FreeType(library);
655 wine_dlclose(ft_handle, NULL, 0);
656 ft_handle = NULL;
657 return TRUE;
659 sym_not_found:
660 WINE_MESSAGE(
661 "Wine cannot find certain functions that it needs inside the FreeType\n"
662 "font library. To enable Wine to use TrueType fonts please upgrade\n"
663 "FreeType to at least version 2.0.5.\n"
664 "http://www.freetype.org\n");
665 RegCloseKey(hkey);
666 wine_dlclose(ft_handle, NULL, 0);
667 ft_handle = NULL;
668 return TRUE;
671 #endif /* HAVE_FREETYPE */