ddraw: Added a todo_wine test for SetCooperativeLevel.
[wine/multimedia.git] / dlls / wineps.drv / truetype.c
blobe53b4ad6047b443178b1a35c5f9fb3746d2e07ff
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 #endif
53 #ifdef HAVE_FREETYPE_TTNAMEID_H
54 #include <freetype/ttnameid.h>
55 #endif
57 #include <sys/types.h>
58 #ifdef HAVE_DIRENT_H
59 # include <dirent.h>
60 #endif
61 #include <string.h>
62 #include <stdarg.h>
63 #include <stdio.h>
64 #include <errno.h>
66 #include "windef.h"
67 #include "winbase.h"
68 #include "winerror.h"
69 #include "winreg.h"
70 #include "winnls.h"
71 #include "psdrv.h"
72 #include "wine/library.h"
73 #include "wine/debug.h"
75 WINE_DEFAULT_DEBUG_CHANNEL(psdrv);
77 #define REQUIRED_FACE_FLAGS ( FT_FACE_FLAG_SCALABLE | \
78 FT_FACE_FLAG_HORIZONTAL | \
79 FT_FACE_FLAG_SFNT | \
80 FT_FACE_FLAG_GLYPH_NAMES )
82 #define GLYPH_LOAD_FLAGS ( FT_LOAD_NO_SCALE | \
83 FT_LOAD_IGNORE_TRANSFORM | \
84 FT_LOAD_LINEAR_DESIGN )
86 static void *ft_handle = NULL;
88 #define MAKE_FUNCPTR(f) static typeof(f) * p##f = NULL;
89 MAKE_FUNCPTR(FT_Done_Face)
90 MAKE_FUNCPTR(FT_Done_FreeType)
91 MAKE_FUNCPTR(FT_Get_Char_Index)
92 MAKE_FUNCPTR(FT_Get_Glyph_Name)
93 MAKE_FUNCPTR(FT_Get_Sfnt_Name)
94 MAKE_FUNCPTR(FT_Get_Sfnt_Name_Count)
95 MAKE_FUNCPTR(FT_Get_Sfnt_Table)
96 MAKE_FUNCPTR(FT_Init_FreeType)
97 MAKE_FUNCPTR(FT_Load_Glyph)
98 MAKE_FUNCPTR(FT_New_Face)
99 MAKE_FUNCPTR(FT_Set_Charmap)
100 #undef MAKE_FUNCPTR
102 /*******************************************************************************
103 * FindCharMap
105 * Finds Windows character map and creates "EncodingScheme" string. Returns
106 * FALSE to indicate memory allocation or FreeType error; sets *p_charmap to
107 * NULL if no Windows encoding is present.
109 * Returns Unicode character map if present; otherwise uses the first Windows
110 * character map found.
113 static const LPCSTR encoding_names[7] =
115 "WindowsSymbol", /* TT_MS_ID_SYMBOL_CS */
116 "WindowsUnicode", /* TT_MS_ID_UNICODE_CS */
117 "WindowsShiftJIS", /* TT_MS_ID_SJIS */
118 "WindowsPRC", /* TT_MS_ID_GB2312 */
119 "WindowsBig5", /* TT_MS_ID_BIG_5 */
120 "WindowsWansung", /* TT_MS_ID_WANSUNG */
121 "WindowsJohab" /* TT_MS_ID_JOHAB */
122 /* "WindowsUnknown65535" is the longest possible (encoding_id is a UShort) */
125 static BOOL FindCharMap(FT_Face face, FT_CharMap *p_charmap, LPSTR *p_sz)
127 FT_Int i;
128 FT_Error error;
129 FT_CharMap charmap = NULL;
131 for (i = 0; i < face->num_charmaps; ++i)
133 if (face->charmaps[i]->platform_id != TT_PLATFORM_MICROSOFT)
134 continue;
136 if (face->charmaps[i]->encoding_id == TT_MS_ID_UNICODE_CS)
138 charmap = face->charmaps[i];
139 break;
142 if (charmap == NULL)
143 charmap = face->charmaps[i];
146 *p_charmap = charmap;
148 if (charmap == NULL)
150 WARN("No Windows character map found\n");
151 return TRUE;
154 error = pFT_Set_Charmap(face, charmap);
155 if (error != FT_Err_Ok)
157 ERR("%s returned %i\n", "FT_Set_Charmap", error);
158 return FALSE;
161 *p_sz = HeapAlloc(PSDRV_Heap, 0, sizeof("WindowsUnknown65535"));
162 if (*p_sz == NULL)
163 return FALSE;
165 if (charmap->encoding_id < 7)
166 strcpy(*p_sz, encoding_names[charmap->encoding_id]);
167 else
168 sprintf(*p_sz, "%s%u", "WindowsUnknown", charmap->encoding_id);
170 return TRUE;
173 /*******************************************************************************
174 * MSTTStrToSz
176 * Converts a string in the TrueType NAME table to a null-terminated ASCII
177 * character string. Space for the string is allocated from the driver heap.
178 * Only handles platform_id = 3 (TT_PLATFORM_MICROSOFT) strings (16-bit, big
179 * endian). It also only handles ASCII character codes (< 128).
181 * Sets *p_sz to NULL if string cannot be converted; only returns FALSE for
182 * memory allocation failure.
185 static BOOL MSTTStrToSz(const FT_SfntName *name, LPSTR *p_sz)
187 FT_UShort i;
188 INT len;
189 BYTE *wsz;
190 LPSTR sz;
192 len = name->string_len / 2; /* # of 16-bit chars */
194 *p_sz = sz = HeapAlloc(PSDRV_Heap, 0, len + 1);
195 if (sz == NULL)
196 return FALSE;
198 wsz = (BYTE *)name->string;
200 for (i = 0; i < len; ++i, ++sz)
202 USHORT wc = (wsz[0] << 8) + wsz[1];
203 wsz += 2;
205 if (wc > 127)
207 WARN("Non-ASCII character 0x%.4x\n", wc);
208 HeapFree(PSDRV_Heap, 0, *p_sz);
209 *p_sz = NULL;
210 return TRUE;
213 *sz = (CHAR)wc;
216 *sz = '\0';
218 return TRUE;
221 /*******************************************************************************
222 * FindMSTTString
224 * Finds the requested Microsoft platform string in the TrueType NAME table and
225 * converts it to a null-terminated ASCII string. Currently looks for U.S.
226 * English names only.
228 * Sets string to NULL if not present or cannot be converted; returns FALSE
229 * only for memory allocation failure.
232 static BOOL FindMSTTString(FT_Face face, FT_CharMap charmap, FT_UShort name_id,
233 LPSTR *p_sz)
235 FT_UInt num_strings, string_index;
236 FT_SfntName name;
237 FT_Error error;
239 num_strings = pFT_Get_Sfnt_Name_Count(face);
241 for (string_index = 0; string_index < num_strings; ++string_index)
243 error = pFT_Get_Sfnt_Name(face, string_index, &name);
244 if (error != FT_Err_Ok)
246 ERR("%s returned %i\n", "FT_Get_Sfnt_Name", error);
247 return FALSE;
250 /* FIXME - Handle other languages? */
252 if (name.platform_id != TT_PLATFORM_MICROSOFT ||
253 name.language_id != TT_MS_LANGID_ENGLISH_UNITED_STATES)
254 continue;
256 if (name.platform_id != charmap->platform_id ||
257 name.encoding_id != charmap->encoding_id)
258 continue;
260 if (name.name_id != name_id)
261 continue;
263 return MSTTStrToSz(&name, p_sz);
266 *p_sz = NULL; /* didn't find it */
268 return TRUE;
271 /*******************************************************************************
272 * PSUnits
274 * Convert TrueType font units (relative to font em square) to PostScript
275 * units.
278 static inline float PSUnits(LONG x, USHORT em_size)
280 return 1000.0 * (float)x / (float)em_size;
283 /*******************************************************************************
284 * StartAFM
286 * Allocates space for the AFM on the driver heap and reads basic font metrics
287 * from the HEAD, POST, HHEA, and OS/2 tables. Returns FALSE for memory
288 * allocation error; sets *p_afm to NULL if required information is missing.
291 static BOOL StartAFM(FT_Face face, AFM **p_afm)
293 TT_Header *head;
294 TT_Postscript *post;
295 TT_OS2 *os2;
296 TT_HoriHeader *hhea;
297 USHORT em_size;
298 AFM *afm;
300 head = pFT_Get_Sfnt_Table(face, ft_sfnt_head);
301 post = pFT_Get_Sfnt_Table(face, ft_sfnt_post);
302 os2 = pFT_Get_Sfnt_Table(face, ft_sfnt_os2);
303 hhea = pFT_Get_Sfnt_Table(face, ft_sfnt_hhea);
305 if (head == NULL || post == NULL || os2 == NULL || hhea == NULL ||
306 os2->version == 0xffff) /* old Macintosh font */
308 WARN("Required table(s) missing\n");
309 *p_afm = NULL;
310 return TRUE;
313 *p_afm = afm = HeapAlloc(PSDRV_Heap, 0, sizeof(*afm));
314 if (afm == NULL)
315 return FALSE;
317 afm->WinMetrics.usUnitsPerEm = em_size = head->Units_Per_EM;
318 afm->WinMetrics.sAscender = hhea->Ascender;
319 afm->WinMetrics.sDescender = hhea->Descender;
320 afm->WinMetrics.sLineGap = hhea->Line_Gap;
321 afm->WinMetrics.sTypoAscender = os2->sTypoAscender;
322 afm->WinMetrics.sTypoDescender = os2->sTypoDescender;
323 afm->WinMetrics.sTypoLineGap = os2->sTypoLineGap;
324 afm->WinMetrics.usWinAscent = os2->usWinAscent;
325 afm->WinMetrics.usWinDescent = os2->usWinDescent;
326 afm->WinMetrics.sAvgCharWidth = os2->xAvgCharWidth;
328 afm->Weight = os2->usWeightClass;
329 afm->ItalicAngle = ((float)(post->italicAngle)) / 65536.0;
330 afm->IsFixedPitch = (post-> isFixedPitch == 0) ? FALSE : TRUE;
331 afm->UnderlinePosition = PSUnits(post->underlinePosition, em_size);
332 afm->UnderlineThickness = PSUnits(post->underlineThickness, em_size);
334 afm->FontBBox.llx = PSUnits(head->xMin, em_size);
335 afm->FontBBox.lly = PSUnits(head->yMin, em_size);
336 afm->FontBBox.urx = PSUnits(head->xMax, em_size);
337 afm->FontBBox.ury = PSUnits(head->yMax, em_size);
339 afm->Ascender = PSUnits(os2->sTypoAscender, em_size);
340 afm->Descender = PSUnits(os2->sTypoDescender, em_size);
342 return TRUE;
345 /*******************************************************************************
346 * ReadCharMetrics
348 * Reads metrics for each glyph in a TrueType font. Returns false for memory
349 * allocation or FreeType error; sets *p_metrics to NULL for non-fatal error.
352 static BOOL ReadCharMetrics(FT_Face face, AFM *afm, AFMMETRICS **p_metrics)
354 FT_ULong charcode, index;
355 AFMMETRICS *metrics;
356 USHORT em_size = afm->WinMetrics.usUnitsPerEm;
358 for (charcode = 0, index = 0; charcode < 65536; ++charcode)
359 if (pFT_Get_Char_Index(face, charcode) != 0)
360 ++index; /* count # of glyphs */
362 afm->NumofMetrics = index;
364 *p_metrics = metrics = HeapAlloc(PSDRV_Heap, 0, index * sizeof(*metrics));
365 if (metrics == NULL)
366 return FALSE;
368 for (charcode = 0, index = 0; charcode < 65536; ++charcode)
370 FT_UInt glyph_index = pFT_Get_Char_Index(face, charcode);
371 FT_Error error;
372 CHAR buffer[128]; /* for glyph names */
374 if (glyph_index == 0)
375 continue;
377 error = pFT_Load_Glyph(face, glyph_index, GLYPH_LOAD_FLAGS);
378 if (error != FT_Err_Ok)
380 ERR("%s returned %i\n", "FT_Load_Glyph", error);
381 goto cleanup;
384 error = pFT_Get_Glyph_Name(face, glyph_index, buffer, sizeof(buffer));
385 if (error != FT_Err_Ok)
387 ERR("%s returned %i\n", "FT_Get_Glyph_Name", error);
388 goto cleanup;
391 metrics[index].N = PSDRV_GlyphName(buffer);
392 if (metrics[index].N == NULL)
393 goto cleanup;
395 metrics[index].C = metrics[index].UV = charcode;
396 metrics[index].WX = PSUnits(face->glyph->metrics.horiAdvance, em_size);
398 ++index;
401 if (afm->WinMetrics.sAvgCharWidth == 0)
402 afm->WinMetrics.sAvgCharWidth = PSDRV_CalcAvgCharWidth(afm);
404 return TRUE;
406 cleanup:
407 HeapFree(PSDRV_Heap, 0, metrics);
409 return FALSE;
412 /*******************************************************************************
413 * BuildTrueTypeAFM
415 * Builds the AFM for a TrueType font and adds it to the driver font list.
416 * Returns FALSE only on an unexpected error (memory allocation failure or
417 * FreeType error).
420 static BOOL BuildTrueTypeAFM(FT_Face face)
422 AFM *afm;
423 AFMMETRICS *metrics;
424 LPSTR font_name, full_name, family_name, encoding_scheme = NULL;
425 FT_CharMap charmap;
426 BOOL retval, added;
428 retval = StartAFM(face, &afm);
429 if (retval == FALSE || afm == NULL)
430 return retval;
432 retval = FindCharMap(face, &charmap, &encoding_scheme);
433 if (retval == FALSE || charmap == NULL)
434 goto cleanup_afm;
436 retval = FindMSTTString(face, charmap, TT_NAME_ID_PS_NAME, &font_name);
437 if (retval == FALSE || font_name == NULL)
438 goto cleanup_encoding_scheme;
440 retval = FindMSTTString(face, charmap, TT_NAME_ID_FULL_NAME, &full_name);
441 if (retval == FALSE || full_name == NULL)
442 goto cleanup_font_name;
444 retval = FindMSTTString(face, charmap, TT_NAME_ID_FONT_FAMILY,
445 &family_name);
446 if (retval == FALSE || family_name == NULL)
447 goto cleanup_full_name;
449 retval = ReadCharMetrics(face, afm, &metrics);
450 if (retval == FALSE || metrics == NULL)
451 goto cleanup_family_name;
453 afm->EncodingScheme = encoding_scheme; afm->FontName = font_name;
454 afm->FullName = full_name; afm->FamilyName = family_name;
455 afm->Metrics = metrics;
457 retval = PSDRV_AddAFMtoList(&PSDRV_AFMFontList, afm, &added);
458 if (retval == FALSE || added == FALSE)
459 goto cleanup_family_name;
461 return TRUE;
463 /* clean up after fatal or non-fatal errors */
465 cleanup_family_name:
466 HeapFree(PSDRV_Heap, 0, family_name);
467 cleanup_full_name:
468 HeapFree(PSDRV_Heap, 0, full_name);
469 cleanup_font_name:
470 HeapFree(PSDRV_Heap, 0, font_name);
471 cleanup_encoding_scheme:
472 HeapFree(PSDRV_Heap, 0, encoding_scheme);
473 cleanup_afm:
474 HeapFree(PSDRV_Heap, 0, afm);
476 return retval;
479 /*******************************************************************************
480 * ReadTrueTypeFile
482 * Reads font metrics from TrueType font file. Only returns FALSE for
483 * unexpected errors (memory allocation failure or FreeType error).
486 static BOOL ReadTrueTypeFile(FT_Library library, LPCSTR filename)
488 FT_Error error;
489 FT_Face face;
491 TRACE("%s\n", filename);
493 error = pFT_New_Face(library, filename, 0, &face);
494 if (error != FT_Err_Ok)
496 WARN("FreeType error %i opening %s\n", error, filename);
497 return TRUE;
500 if ((face->face_flags & REQUIRED_FACE_FLAGS) == REQUIRED_FACE_FLAGS)
502 if (BuildTrueTypeAFM(face) == FALSE)
504 pFT_Done_Face(face);
505 return FALSE;
508 else
510 WARN("Required information missing from %s\n", filename);
513 error = pFT_Done_Face(face);
514 if (error != FT_Err_Ok)
516 ERR("%s returned %i\n", "FT_Done_Face", error);
517 return FALSE;
520 return TRUE;
523 /*******************************************************************************
524 * ReadTrueTypeDir
526 * Reads all TrueType font files in a directory.
529 static BOOL ReadTrueTypeDir(FT_Library library, LPCSTR dirname)
531 struct dirent *dent;
532 DIR *dir;
533 CHAR filename[256];
535 dir = opendir(dirname);
536 if (dir == NULL)
538 WARN("'%s' opening %s\n", strerror(errno), dirname);
539 return TRUE;
542 while ((dent = readdir(dir)) != NULL)
544 CHAR *file_extension = strrchr(dent->d_name, '.');
545 int fn_len;
547 if (file_extension == NULL || strcasecmp(file_extension, ".ttf") != 0)
548 continue;
550 fn_len = snprintf(filename, 256, "%s/%s", dirname, dent->d_name);
551 if (fn_len < 0 || fn_len > sizeof(filename) - 1)
553 WARN("Path '%s/%s' is too long\n", dirname, dent->d_name);
554 continue;
557 if (ReadTrueTypeFile(library, filename) == FALSE)
559 closedir(dir);
560 return FALSE;
564 closedir(dir);
566 return TRUE;
569 /*******************************************************************************
570 * PSDRV_GetTrueTypeMetrics
572 * Reads font metrics from TrueType font files in directories listed in the
573 * HKEY_CURRENT_USER\\Software\\Wine\\Fonts\\Path registry string.
575 * If this function fails (returns FALSE), the driver will fail to initialize
576 * and the driver heap will be destroyed, so it's not necessary to HeapFree
577 * everything in that event.
580 BOOL PSDRV_GetTrueTypeMetrics(void)
582 static const WCHAR pathW[] = {'P','a','t','h',0};
583 FT_Error error;
584 FT_Library library;
585 HKEY hkey;
586 DWORD len;
587 LPWSTR valueW;
588 LPSTR valueA, ptr;
590 /* @@ Wine registry key: HKCU\Software\Wine\Fonts */
591 if (RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Fonts", &hkey) != ERROR_SUCCESS)
592 return TRUE;
594 ft_handle = wine_dlopen(SONAME_LIBFREETYPE, RTLD_NOW, NULL, 0);
595 if(!ft_handle) {
596 WINE_MESSAGE(
597 "Wine cannot find the FreeType font library. To enable Wine to\n"
598 "use TrueType fonts please install a version of FreeType greater than\n"
599 "or equal to 2.0.5.\n"
600 "http://www.freetype.org\n");
601 RegCloseKey(hkey);
602 return TRUE;
605 #define LOAD_FUNCPTR(f) if((p##f = wine_dlsym(ft_handle, #f, NULL, 0)) == NULL) goto sym_not_found;
606 LOAD_FUNCPTR(FT_Done_Face)
607 LOAD_FUNCPTR(FT_Done_FreeType)
608 LOAD_FUNCPTR(FT_Get_Char_Index)
609 LOAD_FUNCPTR(FT_Get_Glyph_Name)
610 LOAD_FUNCPTR(FT_Get_Sfnt_Name)
611 LOAD_FUNCPTR(FT_Get_Sfnt_Name_Count)
612 LOAD_FUNCPTR(FT_Get_Sfnt_Table)
613 LOAD_FUNCPTR(FT_Init_FreeType)
614 LOAD_FUNCPTR(FT_Load_Glyph)
615 LOAD_FUNCPTR(FT_New_Face)
616 LOAD_FUNCPTR(FT_Set_Charmap)
617 #undef LOAD_FUNCPTR
619 error = pFT_Init_FreeType(&library);
620 if (error != FT_Err_Ok)
622 ERR("%s returned %i\n", "FT_Init_FreeType", error);
623 wine_dlclose(ft_handle, NULL, 0);
624 RegCloseKey(hkey);
625 return FALSE;
628 if (RegQueryValueExW( hkey, pathW, NULL, NULL, NULL, &len ) == ERROR_SUCCESS)
630 len += sizeof(WCHAR);
631 valueW = HeapAlloc( GetProcessHeap(), 0, len );
632 if (RegQueryValueExW( hkey, pathW, NULL, NULL, (LPBYTE)valueW, &len ) == ERROR_SUCCESS)
634 len = WideCharToMultiByte( CP_UNIXCP, 0, valueW, -1, NULL, 0, NULL, NULL );
635 valueA = HeapAlloc( GetProcessHeap(), 0, len );
636 WideCharToMultiByte( CP_UNIXCP, 0, valueW, -1, valueA, len, NULL, NULL );
637 TRACE( "got font path %s\n", debugstr_a(valueA) );
638 ptr = valueA;
639 while (ptr)
641 LPSTR next = strchr( ptr, ':' );
642 if (next) *next++ = 0;
643 ReadTrueTypeDir( library, ptr );
644 ptr = next;
646 HeapFree( GetProcessHeap(), 0, valueA );
648 HeapFree( GetProcessHeap(), 0, valueW );
651 RegCloseKey(hkey);
652 pFT_Done_FreeType(library);
653 wine_dlclose(ft_handle, NULL, 0);
654 ft_handle = NULL;
655 return TRUE;
657 sym_not_found:
658 WINE_MESSAGE(
659 "Wine cannot find certain functions that it needs inside the FreeType\n"
660 "font library. To enable Wine to use TrueType fonts please upgrade\n"
661 "FreeType to at least version 2.0.5.\n"
662 "http://www.freetype.org\n");
663 RegCloseKey(hkey);
664 wine_dlclose(ft_handle, NULL, 0);
665 ft_handle = NULL;
666 return TRUE;
669 #endif /* HAVE_FREETYPE */