Replace PROFILE_* functions by Reg*.
[wine/multimedia.git] / dlls / wineps / truetype.c
blob934530b1de6a2bb63f5a4387a05c458c4bf1e500
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 */
8 #include "config.h"
10 #ifdef HAVE_FREETYPE
13 * These stupid #ifdefs should work for FreeType 2.0.1 and 2.0.2. Beyond that
14 * is anybody's guess.
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
36 #include <sys/types.h>
37 #include <dirent.h>
38 #include <string.h>
39 #include <stdio.h>
41 #include "winnt.h"
42 #include "winreg.h"
43 #include "psdrv.h"
44 #include "debugtools.h"
45 #include "heap.h"
47 DEFAULT_DEBUG_CHANNEL(psdrv);
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 )
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;
63 /*******************************************************************************
65 * FindCharMap
67 * Sets charmap and points afm->EncodingScheme to encoding name (in driver
68 * heap). Leaves both uninitialized if font contains no Windows encoding.
70 * Returns FALSE to indicate memory allocation error.
73 static const char *encoding_names[7] =
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 */
84 static BOOL FindCharMap(AFM *afm)
86 FT_Int i;
87 FT_Error error;
89 charmap = NULL;
91 for (i = 0; i < face->num_charmaps; ++i)
93 if (face->charmaps[i]->platform_id != TT_PLATFORM_MICROSOFT)
94 continue;
96 if (face->charmaps[i]->encoding_id == TT_MS_ID_UNICODE_CS)
98 charmap = face->charmaps[i];
99 break;
102 if (charmap == NULL)
103 charmap = face->charmaps[i];
106 if (charmap == NULL)
107 return TRUE;
109 error = FT_Set_Charmap(face, charmap);
110 if (error != FT_Err_Ok)
112 ERR("%s returned %i\n", "FT_Set_CharMap", error);
113 return FALSE;
116 if (charmap->encoding_id < 7)
118 afm->EncodingScheme = HEAP_strdupA(PSDRV_Heap, 0,
119 encoding_names[charmap->encoding_id]);
120 if (afm->EncodingScheme == NULL)
121 return FALSE;
123 else
125 afm->EncodingScheme = HeapAlloc(PSDRV_Heap, 0, /* encoding_id */
126 sizeof("WindowsUnknown65535")); /* is a UShort */
127 if (afm->EncodingScheme == NULL)
128 return FALSE;
130 sprintf(afm->EncodingScheme, "%s%u", "WindowsUnknown",
131 charmap->encoding_id);
134 return TRUE;
137 /*******************************************************************************
138 * NameTableString
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.
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).
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).
151 static BOOL NameTableString(LPSTR *sz, const FT_SfntName *name)
153 FT_UShort i, len, *ws;
154 LPSTR s;
156 if (name->platform_id != TT_PLATFORM_MICROSOFT)
158 ERR("Unsupported encoding %i\n", name->platform_id);
159 return FALSE; /* should never get here */
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);
168 for (i = 0; i < len; ++i, ++s, ++ws)
170 FT_UShort wc = *ws;
172 #ifndef WORDS_BIGENDIAN
173 wc = (wc >> 8) | (wc << 8);
174 #endif
176 if (wc > 127)
178 WARN("Non-ASCII character 0x%.4x\n", wc);
179 HeapFree(PSDRV_Heap, 0, *sz);
180 *sz = NULL;
181 return TRUE;
184 *s = (CHAR)wc;
187 *s = '\0';
188 return TRUE;
191 /*******************************************************************************
192 * ReadNameTable
194 * Reads various font names from the TrueType 'NAME' table. Currently looks
195 * for U.S. English names only,
197 * May leave a pointer uninitialized if the desired string is not present;
198 * returns FALSE only in the event of an unexpected error.
201 static BOOL ReadNameTable(AFM *afm)
203 FT_UInt numStrings, stringIndex;
204 FT_SfntName name;
205 FT_Error error;
207 numStrings = FT_Get_Sfnt_Name_Count(face);
209 for (stringIndex = 0; stringIndex < numStrings; ++stringIndex)
211 error = FT_Get_Sfnt_Name(face, stringIndex, &name);
212 if (error != FT_Err_Ok)
214 ERR("%s returned %i\n", "FT_Get_Sfnt_Name", error);
215 return FALSE;
218 /* FIXME - Handle other languages? */
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;
225 switch (name.name_id)
227 case TT_NAME_ID_FONT_FAMILY:
229 if (NameTableString(&(afm->FamilyName), &name) == FALSE)
230 return FALSE;
231 break;
233 case TT_NAME_ID_FULL_NAME:
235 if (NameTableString(&(afm->FullName), &name) == FALSE)
236 return FALSE;
237 break;
239 case TT_NAME_ID_PS_NAME:
241 if (NameTableString(&(afm->FontName), &name) == FALSE)
242 return FALSE;
243 break;
247 return TRUE;
250 /*******************************************************************************
251 * FreeAFM
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.
258 static void FreeAFM(AFM *afm)
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);
273 HeapFree(PSDRV_Heap, 0, afm);
276 /*******************************************************************************
277 * PSUnits
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.
284 #define PSUnits(x) (((float)(x)) * 1000.0 / ((float)(head->Units_Per_EM)))
286 /*******************************************************************************
287 * ReadMetricsTables
289 * Reads basic font metrics from the 'head', 'post', and 'OS/2' tables.
290 * Returns FALSE if any table is missing.
293 static BOOL ReadMetricsTables(AFM *afm)
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);
300 if (head == NULL || post == NULL || hhea == NULL || os2 == NULL)
301 return FALSE;
303 if (os2->version == 0xffff) /* Old Macintosh font */
304 return FALSE;
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);
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);
317 /* CapHeight & XHeight set by ReadCharMetrics */
319 afm->Ascender = PSUnits(os2->sTypoAscender);
320 afm->Descender = PSUnits(os2->sTypoDescender);
321 afm->FullAscender = afm->FontBBox.ury; /* get rid of this */
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;
333 return TRUE;
336 /*******************************************************************************
337 * ReadCharMetrics
339 * Reads metrics for each glyph in a TrueType font. Since FreeAFM will try to
340 * free afm->Metrics and afm->Encoding if they are non-NULL, don't free them
341 * in the event of an error. (FreeAFM depends on the fact that afm->Encoding
342 * and its associated array of UNICODEGLYPHs are allocated as a single object.)
345 static BOOL ReadCharMetrics(AFM *afm)
347 FT_ULong charcode, index;
348 UNICODEGLYPH *glyphs;
351 * There does not seem to be an easy way to get the number of characters
352 * in an encoding out of a TrueType font.
354 for (charcode = 0, index = 0; charcode < 65536; ++charcode)
356 if (FT_Get_Char_Index(face, charcode) != 0)
357 ++index;
360 afm->NumofMetrics = index;
362 afm->Metrics = HeapAlloc(PSDRV_Heap, 0, index * sizeof(AFMMETRICS));
363 afm->Encoding = HeapAlloc(PSDRV_Heap, 0, sizeof(UNICODEVECTOR) +
364 index * sizeof(UNICODEGLYPH));
365 if (afm->Metrics == NULL || afm->Encoding == NULL)
366 return FALSE;
368 glyphs = (UNICODEGLYPH *)(afm->Encoding + 1);
369 afm->Encoding->size = index;
370 afm->Encoding->glyphs = glyphs;
372 for (charcode = 0, index = 0; charcode <= 65536; ++charcode)
374 FT_UInt glyph_index = FT_Get_Char_Index(face, charcode);
375 FT_Error error;
376 FT_Glyph glyph;
377 FT_BBox bbox;
378 char buffer[256];
380 if (glyph_index == 0)
381 continue;
383 error = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_SCALE |
384 FT_LOAD_IGNORE_TRANSFORM | FT_LOAD_LINEAR_DESIGN);
385 if (error != FT_Err_Ok)
387 ERR("%s returned %i\n", "FT_Load_Glyph", error);
388 return FALSE;
391 error = FT_Get_Glyph(face->glyph, &glyph);
392 if (error != FT_Err_Ok)
394 ERR("%s returned %i\n", "FT_Get_Glyph", error);
395 return FALSE;
398 FT_Glyph_Get_CBox(glyph, ft_glyph_bbox_unscaled, &bbox);
400 error = FT_Get_Glyph_Name(face, glyph_index, buffer, 255);
401 if (error != FT_Err_Ok)
403 ERR("%s returned %i\n", "FT_Get_Glyph_Name", error);
404 return FALSE;
407 afm->Metrics[index].N = PSDRV_GlyphName(buffer);
408 if (afm->Metrics[index].N == NULL)
409 return FALSE;
411 afm->Metrics[index].C = charcode;
412 afm->Metrics[index].UV = charcode;
413 afm->Metrics[index].WX = PSUnits(face->glyph->metrics.horiAdvance);
414 afm->Metrics[index].B.llx = PSUnits(bbox.xMin);
415 afm->Metrics[index].B.lly = PSUnits(bbox.yMin);
416 afm->Metrics[index].B.urx = PSUnits(bbox.xMax);
417 afm->Metrics[index].B.ury = PSUnits(bbox.yMax);
418 afm->Metrics[index].L = NULL;
420 TRACE("Metrics for '%s' WX = %f B = %f,%f - %f,%f\n",
421 afm->Metrics[index].N->sz, afm->Metrics[index].WX,
422 afm->Metrics[index].B.llx, afm->Metrics[index].B.lly,
423 afm->Metrics[index].B.urx, afm->Metrics[index].B.ury);
425 glyphs[index].UV = charcode;
426 glyphs[index].name = afm->Metrics[index].N;
428 if (charcode == 0x0048) /* 'H' */
429 afm->CapHeight = PSUnits(bbox.yMax);
430 if (charcode == 0x0078) /* 'x' */
431 afm->XHeight = PSUnits(bbox.yMax);
433 ++index;
436 return TRUE;
439 /*******************************************************************************
440 * ReadTrueTypeAFM
442 * Fills in AFM structure for opened TrueType font file. Returns FALSE only on
443 * an unexpected error (memory allocation failure or FreeType error); otherwise
444 * returns TRUE. Leaves it to the caller (ReadTrueTypeFile) to clean up.
447 static BOOL ReadTrueTypeAFM(AFM *afm)
450 if ((face->face_flags & REQUIRED_FACE_FLAGS) != REQUIRED_FACE_FLAGS)
452 WARN("Font flags do not match requirements\n");
453 return TRUE;
456 if (FindCharMap(afm) == FALSE)
457 return FALSE;
459 if (charmap == NULL)
461 WARN("No Windows encodings in font\n");
462 return TRUE;
465 TRACE("Using encoding '%s'\n", afm->EncodingScheme);
467 if (ReadNameTable(afm) == FALSE)
468 return FALSE;
470 if (afm->FamilyName == NULL || afm->FullName == NULL ||
471 afm->FontName == NULL)
473 WARN("Required strings missing from font\n");
474 return TRUE;
477 if (ReadMetricsTables(afm) == FALSE) /* Non-fatal */
479 WARN("Required metrics tables missing from font\n");
480 return TRUE;
483 if (ReadCharMetrics(afm) == FALSE)
484 return FALSE;
486 return TRUE;
489 /*******************************************************************************
490 * ReadTrueTypeFile
492 * Reads PostScript-style font metrics from a TrueType font file. Only returns
493 * FALSE for unexpected errors (memory allocation, etc.); returns TRUE if it's
494 * just a bad font file.
497 static BOOL ReadTrueTypeFile(LPCSTR filename)
499 FT_Error error;
500 AFM *afm;
502 TRACE("'%s'\n", filename);
504 afm = HeapAlloc(PSDRV_Heap, HEAP_ZERO_MEMORY, sizeof(AFM));
505 if (afm == NULL)
506 return FALSE;
508 error = FT_New_Face(library, filename, 0, &face);
510 if (error != FT_Err_Ok)
512 WARN("FreeType error %i opening '%s'\n", error, filename);
513 HeapFree(PSDRV_Heap, 0, afm);
514 return TRUE;
517 if (ReadTrueTypeAFM(afm) == FALSE)
519 FreeAFM(afm);
520 FT_Done_Face(face);
521 return FALSE;
524 error = FT_Done_Face(face);
525 if (error != FT_Err_Ok)
527 ERR("%s returned %i\n", "FT_Done_Face", error);
528 FreeAFM(afm);
529 return FALSE;
532 if (afm->Encoding == NULL) /* last element to be set */
534 FreeAFM(afm);
535 return TRUE;
538 if (PSDRV_AddAFMtoList(&PSDRV_AFMFontList, afm) == FALSE)
540 FreeAFM(afm);
541 return FALSE;
544 return TRUE;
549 /*******************************************************************************
550 * PSDRV_GetTrueTypeMetrics
552 * Reads PostScript-stype font metrics from TrueType font files in directories
553 * listed in the [TrueType Font Directories] section of the Wine configuration
554 * file.
556 * If this function fails, the driver will fail to initialize and the driver
557 * heap will be destroyed, so it's not necessary to HeapFree everything in
558 * that event.
561 BOOL PSDRV_GetTrueTypeMetrics(void)
563 CHAR keybuf[256], namebuf[256];
564 INT i = 0;
565 FT_Error error;
566 HKEY hkey;
567 DWORD type, key_len, name_len;
569 error = FT_Init_FreeType(&library);
570 if (error != FT_Err_Ok)
572 ERR("%s returned %i\n", "FT_Init_FreeType", error);
573 return FALSE;
576 if(RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\TrueType Font Directories",
577 0, KEY_READ, &hkey))
578 goto no_metrics;
580 key_len = sizeof(keybuf);
581 name_len = sizeof(namebuf);
582 while(!RegEnumValueA(hkey, i++, keybuf, &key_len, NULL, &type, namebuf, &name_len))
584 struct dirent *dent;
585 DIR *dir;
586 INT dnlen; /* directory name length */
588 namebuf[sizeof(namebuf) - 1] = '\0';
589 dir = opendir(namebuf);
590 if (dir == NULL)
592 WARN("Error opening directory '%s'\n", namebuf);
593 continue;
596 dnlen = strlen(namebuf);
597 namebuf[dnlen] = '/'; /* 2 slashes is OK, 0 is not */
598 ++dnlen;
600 while ((dent = readdir(dir)) != NULL)
602 INT fnlen; /* file name length */
604 fnlen = strlen(dent->d_name);
606 if (fnlen < 5 || strcasecmp(dent->d_name + fnlen - 4, ".ttf") != 0)
608 TRACE("Skipping filename '%s'\n", dent->d_name);
609 continue;
612 if (dnlen + fnlen + 1 > sizeof(namebuf)) /* allow for '\0' */
614 WARN("Path '%s/%s' is too long\n", namebuf, dent->d_name);
615 continue;
618 memcpy(namebuf + dnlen, dent->d_name, fnlen + 1);
620 if (ReadTrueTypeFile(namebuf) == FALSE)
622 ERR("Error reading '%s'\n", namebuf);
623 closedir(dir);
624 RegCloseKey(hkey);
625 FT_Done_FreeType(library);
626 return FALSE;
630 closedir(dir);
632 /* initialize lengths for new iteration */
633 key_len = sizeof(keybuf);
634 name_len = sizeof(namebuf);
636 RegCloseKey(hkey);
638 no_metrics:
639 FT_Done_FreeType(library);
640 return TRUE;
643 #endif /* HAVE_FREETYPE */