1 /*******************************************************************************
2 * TrueType font-related functions for Wine PostScript driver. Currently just
3 * uses FreeType to read font metrics.
5 * Copyright 2001 Ian Pilcher
12 #include <freetype/freetype.h>
14 #include FT_TRUETYPE_NAMES_H
15 #include FT_TRUETYPE_TABLES_H
17 #include <sys/types.h>
25 #include "debugtools.h"
28 DEFAULT_DEBUG_CHANNEL(psdrv
);
31 #define REQUIRED_FACE_FLAGS ( FT_FACE_FLAG_SCALABLE | \
32 FT_FACE_FLAG_HORIZONTAL | \
34 FT_FACE_FLAG_GLYPH_NAMES )
36 static FT_Library library
;
38 static FT_CharMap charmap
;
39 static TT_Header
*head
;
40 static TT_Postscript
*post
;
42 static TT_HoriHeader
*hhea
;
44 /*******************************************************************************
48 * Sets charmap and points afm->EncodingScheme to encoding name (in driver
49 * heap). Leaves both uninitialized if font contains no Windows encoding.
51 * Returns FALSE to indicate memory allocation error.
54 static const char *encoding_names
[7] =
56 "WindowsSymbol", /* TT_MS_ID_SYMBOL_CS */
57 "WindowsUnicode", /* TT_MS_ID_UNICODE_CS */
58 "WindowsShiftJIS", /* TT_MS_ID_SJIS */
59 "WindowsPRC", /* TT_MS_ID_GB2312 */
60 "WindowsBig5", /* TT_MS_ID_BIG_5 */
61 "WindowsWansung", /* TT_MS_ID_WANSUNG */
62 "WindowsJohab" /* TT_MS_ID_JOHAB */
65 static BOOL
FindCharMap(AFM
*afm
)
72 for (i
= 0; i
< face
->num_charmaps
; ++i
)
74 if (face
->charmaps
[i
]->platform_id
!= TT_PLATFORM_MICROSOFT
)
77 if (face
->charmaps
[i
]->encoding_id
== TT_MS_ID_UNICODE_CS
)
79 charmap
= face
->charmaps
[i
];
84 charmap
= face
->charmaps
[i
];
90 error
= FT_Set_Charmap(face
, charmap
);
91 if (error
!= FT_Err_Ok
)
93 ERR("%s returned %i\n", "FT_Set_CharMap", error
);
97 if (charmap
->encoding_id
< 7)
99 afm
->EncodingScheme
= HEAP_strdupA(PSDRV_Heap
, 0,
100 encoding_names
[charmap
->encoding_id
]);
101 if (afm
->EncodingScheme
== NULL
)
106 afm
->EncodingScheme
= HeapAlloc(PSDRV_Heap
, 0,
107 sizeof("WindowsUnknown") + 1 + charmap
->encoding_id
/ 10);
108 if (afm
->EncodingScheme
== NULL
)
111 sprintf(afm
->EncodingScheme
, "%s%u", "WindowsUnknown",
112 charmap
->encoding_id
);
118 /*******************************************************************************
121 * Converts a name table string to a null-terminated character string. The
122 * space for the character string is allocated from the driver heap.
124 * This function handles only platform_id = 3 (TT_PLATFORM_MICROSOFT) -- 16-bit
125 * big-endian strings. It also only handles ASCII character codes (< 128).
127 * This function will set *sz to NULL if it cannot parse the string, but it
128 * will only return FALSE in the event of an unexpected error (memory
129 * allocation failure).
132 static BOOL
NameTableString(LPSTR
*sz
, const FT_SfntName
*name
)
134 FT_UShort i
, len
, *ws
;
137 if (name
->platform_id
!= TT_PLATFORM_MICROSOFT
)
139 ERR("Unsupported encoding %i\n", name
->platform_id
);
140 return FALSE
; /* should never get here */
143 len
= name
->string_len
/ 2;
144 s
= *sz
= HeapAlloc(PSDRV_Heap
, 0, len
+ 1);
147 ws
= (FT_UShort
*)(name
->string
);
149 for (i
= 0; i
< len
; ++i
, ++s
, ++ws
)
153 #ifndef WORDS_BIGENDIAN
154 wc
= (wc
>> 8) | (wc
<< 8);
159 WARN("Non-ASCII character 0x%.4x\n", wc
);
160 HeapFree(PSDRV_Heap
, 0, *sz
);
172 /*******************************************************************************
175 * Reads various font names from the TrueType 'NAME' table. Currently looks
176 * for U.S. English names only,
178 * May leave a pointer uninitialized if the desired string is not present;
179 * returns FALSE only in the event of an unexpected error.
182 static BOOL
ReadNameTable(AFM
*afm
)
184 FT_UInt numStrings
, stringIndex
;
188 numStrings
= FT_Get_Sfnt_Name_Count(face
);
190 for (stringIndex
= 0; stringIndex
< numStrings
; ++stringIndex
)
192 error
= FT_Get_Sfnt_Name(face
, stringIndex
, &name
);
193 if (error
!= FT_Err_Ok
)
195 ERR("%s returned %i\n", "FT_Get_Sfnt_Name", error
);
199 /* FIXME - Handle other languages? */
201 if (name
.language_id
!= TT_MS_LANGID_ENGLISH_UNITED_STATES
||
202 name
.platform_id
!= charmap
->platform_id
||
203 name
.encoding_id
!= charmap
->encoding_id
)
206 switch (name
.name_id
)
208 case TT_NAME_ID_FONT_FAMILY
:
210 if (NameTableString(&(afm
->FamilyName
), &name
) == FALSE
)
214 case TT_NAME_ID_FULL_NAME
:
216 if (NameTableString(&(afm
->FullName
), &name
) == FALSE
)
220 case TT_NAME_ID_PS_NAME
:
222 if (NameTableString(&(afm
->FontName
), &name
) == FALSE
)
231 /*******************************************************************************
234 * Frees an AFM and all subsidiary objects. For this function to work
235 * properly, the AFM must have been allocated with HEAP_ZERO_MEMORY, and the
236 * UNICODEVECTOR and it's associated array of UNICODEGLYPHs must have been
237 * allocated as a single object.
239 static void FreeAFM(AFM
*afm
)
241 if (afm
->FontName
!= NULL
)
242 HeapFree(PSDRV_Heap
, 0, afm
->FontName
);
243 if (afm
->FullName
!= NULL
)
244 HeapFree(PSDRV_Heap
, 0, afm
->FullName
);
245 if (afm
->FamilyName
!= NULL
)
246 HeapFree(PSDRV_Heap
, 0, afm
->FamilyName
);
247 if (afm
->EncodingScheme
!= NULL
)
248 HeapFree(PSDRV_Heap
, 0, afm
->EncodingScheme
);
249 if (afm
->Metrics
!= NULL
)
250 HeapFree(PSDRV_Heap
, 0, afm
->Metrics
);
251 if (afm
->Encoding
!= NULL
)
252 HeapFree(PSDRV_Heap
, 0, afm
->Encoding
);
254 HeapFree(PSDRV_Heap
, 0, afm
);
257 /*******************************************************************************
260 * Convert TrueType font units (relative to font em square) to PostScript
261 * units. This is defined as a macro, so it can handle different TrueType
262 * data types as inputs.
265 #define PSUnits(x) (((float)(x)) * 1000.0 / ((float)(head->Units_Per_EM)))
267 /*******************************************************************************
270 * Reads basic font metrics from the 'head', 'post', and 'OS/2' tables.
271 * Returns FALSE if any table is missing.
274 static BOOL
ReadMetricsTables(AFM
*afm
)
276 head
= FT_Get_Sfnt_Table(face
, ft_sfnt_head
);
277 post
= FT_Get_Sfnt_Table(face
, ft_sfnt_post
);
278 hhea
= FT_Get_Sfnt_Table(face
, ft_sfnt_hhea
);
279 os2
= FT_Get_Sfnt_Table(face
, ft_sfnt_os2
);
281 if (head
== NULL
|| post
== NULL
|| hhea
== NULL
|| os2
== NULL
)
284 if (os2
->version
== 0xffff) /* Old Macintosh font */
287 afm
->Weight
= os2
->usWeightClass
;
288 afm
->ItalicAngle
= ((float)(post
->italicAngle
)) / 65536.0;
289 afm
->IsFixedPitch
= (post
->isFixedPitch
== 0) ? FALSE
: TRUE
;
290 afm
->UnderlinePosition
= PSUnits(post
->underlinePosition
);
291 afm
->UnderlineThickness
= PSUnits(post
->underlineThickness
);
293 afm
->FontBBox
.llx
= PSUnits(head
->xMin
);
294 afm
->FontBBox
.lly
= PSUnits(head
->yMin
);
295 afm
->FontBBox
.urx
= PSUnits(head
->xMax
);
296 afm
->FontBBox
.ury
= PSUnits(head
->yMax
);
298 /* CapHeight & XHeight set by ReadCharMetrics */
300 afm
->Ascender
= PSUnits(os2
->sTypoAscender
);
301 afm
->Descender
= PSUnits(os2
->sTypoDescender
);
302 afm
->FullAscender
= afm
->FontBBox
.ury
; /* get rid of this */
304 afm
->WinMetrics
.usUnitsPerEm
= head
->Units_Per_EM
;
305 afm
->WinMetrics
.sAscender
= hhea
->Ascender
;
306 afm
->WinMetrics
.sDescender
= hhea
->Descender
;
307 afm
->WinMetrics
.sLineGap
= hhea
->Line_Gap
;
308 afm
->WinMetrics
.sTypoAscender
= os2
->sTypoAscender
;
309 afm
->WinMetrics
.sTypoDescender
= os2
->sTypoDescender
;
310 afm
->WinMetrics
.sTypoLineGap
= os2
->sTypoLineGap
;
311 afm
->WinMetrics
.usWinAscent
= os2
->usWinAscent
;
312 afm
->WinMetrics
.usWinDescent
= os2
->usWinDescent
;
317 /*******************************************************************************
320 * Reads metrics for each glyph in a TrueType font. Since FreeAFM will try to
321 * free afm->Metrics and afm->Encoding if they are non-NULL, don't free them
322 * in the event of an error. (FreeAFM depends on the fact that afm->Encoding
323 * and its associated array of UNICODEGLYPHs are allocated as a single object.)
326 static BOOL
ReadCharMetrics(AFM
*afm
)
328 FT_ULong charcode
, index
;
329 UNICODEGLYPH
*glyphs
;
332 * There does not seem to be an easy way to get the number of characters
333 * in an encoding out of a TrueType font.
335 for (charcode
= 0, index
= 0; charcode
< 65536; ++charcode
)
337 if (FT_Get_Char_Index(face
, charcode
) != 0)
341 afm
->NumofMetrics
= index
;
343 afm
->Metrics
= HeapAlloc(PSDRV_Heap
, 0, index
* sizeof(AFMMETRICS
));
344 afm
->Encoding
= HeapAlloc(PSDRV_Heap
, 0, sizeof(UNICODEVECTOR
) +
345 index
* sizeof(UNICODEGLYPH
));
346 if (afm
->Metrics
== NULL
|| afm
->Encoding
== NULL
)
349 glyphs
= (UNICODEGLYPH
*)(afm
->Encoding
+ 1);
350 afm
->Encoding
->size
= index
;
351 afm
->Encoding
->glyphs
= glyphs
;
353 for (charcode
= 0, index
= 0; charcode
<= 65536; ++charcode
)
355 FT_UInt glyph_index
= FT_Get_Char_Index(face
, charcode
);
361 if (glyph_index
== 0)
364 error
= FT_Load_Glyph(face
, glyph_index
, FT_LOAD_NO_SCALE
|
365 FT_LOAD_IGNORE_TRANSFORM
| FT_LOAD_LINEAR_DESIGN
);
366 if (error
!= FT_Err_Ok
)
368 ERR("%s returned %i\n", "FT_Load_Glyph", error
);
372 error
= FT_Get_Glyph(face
->glyph
, &glyph
);
373 if (error
!= FT_Err_Ok
)
375 ERR("%s returned %i\n", "FT_Get_Glyph", error
);
379 FT_Glyph_Get_CBox(glyph
, ft_glyph_bbox_unscaled
, &bbox
);
381 error
= FT_Get_Glyph_Name(face
, glyph_index
, buffer
, 255);
382 if (error
!= FT_Err_Ok
)
384 ERR("%s returned %i\n", "FT_Get_Glyph_Name", error
);
388 afm
->Metrics
[index
].N
= PSDRV_GlyphName(buffer
);
389 if (afm
->Metrics
[index
].N
== NULL
)
392 afm
->Metrics
[index
].C
= charcode
;
393 afm
->Metrics
[index
].UV
= charcode
;
394 afm
->Metrics
[index
].WX
= PSUnits(face
->glyph
->metrics
.horiAdvance
);
395 afm
->Metrics
[index
].B
.llx
= PSUnits(bbox
.xMin
);
396 afm
->Metrics
[index
].B
.lly
= PSUnits(bbox
.yMin
);
397 afm
->Metrics
[index
].B
.urx
= PSUnits(bbox
.xMax
);
398 afm
->Metrics
[index
].B
.ury
= PSUnits(bbox
.yMax
);
399 afm
->Metrics
[index
].L
= NULL
;
401 TRACE("Metrics for '%s' WX = %f B = %f,%f - %f,%f\n",
402 afm
->Metrics
[index
].N
->sz
, afm
->Metrics
[index
].WX
,
403 afm
->Metrics
[index
].B
.llx
, afm
->Metrics
[index
].B
.lly
,
404 afm
->Metrics
[index
].B
.urx
, afm
->Metrics
[index
].B
.ury
);
406 glyphs
[index
].UV
= charcode
;
407 glyphs
[index
].name
= afm
->Metrics
[index
].N
;
409 if (charcode
== 0x0048) /* 'H' */
410 afm
->CapHeight
= PSUnits(bbox
.yMax
);
411 if (charcode
== 0x0078) /* 'x' */
412 afm
->XHeight
= PSUnits(bbox
.yMax
);
420 /*******************************************************************************
423 * Fills in AFM structure for opened TrueType font file. Returns FALSE only on
424 * an unexpected error (memory allocation failure or FreeType error); otherwise
425 * returns TRUE. Leaves it to the caller (ReadTrueTypeFile) to clean up.
428 static BOOL
ReadTrueTypeAFM(AFM
*afm
)
431 if ((face
->face_flags
& REQUIRED_FACE_FLAGS
) != REQUIRED_FACE_FLAGS
)
433 WARN("Font flags do not match requirements\n");
437 if (FindCharMap(afm
) == FALSE
)
442 WARN("No Windows encodings in font\n");
446 TRACE("Using encoding '%s'\n", afm
->EncodingScheme
);
448 if (ReadNameTable(afm
) == FALSE
)
451 if (afm
->FamilyName
== NULL
|| afm
->FullName
== NULL
||
452 afm
->FontName
== NULL
)
454 WARN("Required strings missing from font\n");
458 if (ReadMetricsTables(afm
) == FALSE
) /* Non-fatal */
460 WARN("Required metrics tables missing from font\n");
464 if (ReadCharMetrics(afm
) == FALSE
)
470 /*******************************************************************************
473 * Reads PostScript-style font metrics from a TrueType font file. Only returns
474 * FALSE for unexpected errors (memory allocation, etc.); returns TRUE if it's
475 * just a bad font file.
478 static BOOL
ReadTrueTypeFile(LPCSTR filename
)
483 TRACE("'%s'\n", filename
);
485 afm
= HeapAlloc(PSDRV_Heap
, HEAP_ZERO_MEMORY
, sizeof(AFM
));
489 error
= FT_New_Face(library
, filename
, 0, &face
);
491 if (error
!= FT_Err_Ok
)
493 WARN("FreeType error %i opening '%s'\n", error
, filename
);
494 HeapFree(PSDRV_Heap
, 0, afm
);
498 if (ReadTrueTypeAFM(afm
) == FALSE
)
505 error
= FT_Done_Face(face
);
506 if (error
!= FT_Err_Ok
)
508 ERR("%s returned %i\n", "FT_Done_Face", error
);
513 if (afm
->Encoding
== NULL
) /* last element to be set */
519 if (PSDRV_AddAFMtoList(&PSDRV_AFMFontList
, afm
) == FALSE
)
530 /*******************************************************************************
531 * PSDRV_GetTrueTypeMetrics
533 * Reads PostScript-stype font metrics from TrueType font files in directories
534 * listed in the [TrueType Font Directories] section of the Wine configuration
537 * If this function fails, the driver will fail to initialize and the driver
538 * heap will be destroyed, so it's not necessary to HeapFree everything in
542 BOOL
PSDRV_GetTrueTypeMetrics()
544 CHAR keybuf
[256], namebuf
[256];
548 error
= FT_Init_FreeType(&library
);
549 if (error
!= FT_Err_Ok
)
551 ERR("%s returned %i\n", "FT_Init_FreeType", error
);
555 while (PROFILE_EnumWineIniString("TrueType Font Directories", i
++, keybuf
,
556 sizeof(keybuf
), namebuf
, sizeof(namebuf
)))
560 INT dnlen
; /* directory name length */
562 namebuf
[sizeof(namebuf
) - 1] = '\0';
563 dir
= opendir(namebuf
);
566 WARN("Error opening directory '%s'\n", namebuf
);
570 dnlen
= strlen(namebuf
);
571 namebuf
[dnlen
] = '/'; /* 2 slashes is OK, 0 is not */
574 while ((dent
= readdir(dir
)) != NULL
)
576 INT fnlen
; /* file name length */
578 fnlen
= strlen(dent
->d_name
);
580 if (fnlen
< 5 || strcasecmp(dent
->d_name
+ fnlen
- 4, ".ttf") != 0)
582 TRACE("Skipping filename '%s'\n", dent
->d_name
);
586 if (dnlen
+ fnlen
+ 1 > sizeof(namebuf
)) /* allow for '\0' */
588 WARN("Path '%s/%s' is too long\n", namebuf
, dent
->d_name
);
592 memcpy(namebuf
+ dnlen
, dent
->d_name
, fnlen
+ 1);
594 if (ReadTrueTypeFile(namebuf
) == FALSE
)
596 ERR("Error reading '%s'\n", namebuf
);
598 FT_Done_FreeType(library
);
606 FT_Done_FreeType(library
);
610 #endif /* HAVE_FREETYPE */