1 | /**************************************************************************** |
2 | * |
3 | * ttpost.c |
4 | * |
5 | * PostScript name table processing for TrueType and OpenType fonts |
6 | * (body). |
7 | * |
8 | * Copyright (C) 1996-2023 by |
9 | * David Turner, Robert Wilhelm, and Werner Lemberg. |
10 | * |
11 | * This file is part of the FreeType project, and may only be used, |
12 | * modified, and distributed under the terms of the FreeType project |
13 | * license, LICENSE.TXT. By continuing to use, modify, or distribute |
14 | * this file you indicate that you have read the license and |
15 | * understand and accept it fully. |
16 | * |
17 | */ |
18 | |
19 | /************************************************************************** |
20 | * |
21 | * The post table is not completely loaded by the core engine. This |
22 | * file loads the missing PS glyph names and implements an API to access |
23 | * them. |
24 | * |
25 | */ |
26 | |
27 | |
28 | #include <freetype/internal/ftdebug.h> |
29 | #include <freetype/internal/ftstream.h> |
30 | #include <freetype/tttags.h> |
31 | |
32 | |
33 | #ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES |
34 | |
35 | #include "ttpost.h" |
36 | |
37 | #include "sferrors.h" |
38 | |
39 | |
40 | /************************************************************************** |
41 | * |
42 | * The macro FT_COMPONENT is used in trace mode. It is an implicit |
43 | * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log |
44 | * messages during execution. |
45 | */ |
46 | #undef FT_COMPONENT |
47 | #define FT_COMPONENT ttpost |
48 | |
49 | |
50 | /* If this configuration macro is defined, we rely on the `psnames' */ |
51 | /* module to grab the glyph names. */ |
52 | |
53 | #ifdef FT_CONFIG_OPTION_POSTSCRIPT_NAMES |
54 | |
55 | |
56 | #include <freetype/internal/services/svpscmap.h> |
57 | |
58 | #define MAC_NAME( x ) (FT_String*)psnames->macintosh_name( (FT_UInt)(x) ) |
59 | |
60 | |
61 | #else /* !FT_CONFIG_OPTION_POSTSCRIPT_NAMES */ |
62 | |
63 | |
64 | /* Otherwise, we ignore the `psnames' module, and provide our own */ |
65 | /* table of Mac names. Thus, it is possible to build a version of */ |
66 | /* FreeType without the Type 1 driver & psnames module. */ |
67 | |
68 | #define MAC_NAME( x ) (FT_String*)tt_post_default_names[x] |
69 | |
70 | /* the 258 default Mac PS glyph names; see file `tools/glnames.py' */ |
71 | |
72 | static const FT_String* const tt_post_default_names[258] = |
73 | { |
74 | /* 0 */ |
75 | ".notdef" , ".null" , "nonmarkingreturn" , "space" , "exclam" , |
76 | "quotedbl" , "numbersign" , "dollar" , "percent" , "ampersand" , |
77 | /* 10 */ |
78 | "quotesingle" , "parenleft" , "parenright" , "asterisk" , "plus" , |
79 | "comma" , "hyphen" , "period" , "slash" , "zero" , |
80 | /* 20 */ |
81 | "one" , "two" , "three" , "four" , "five" , |
82 | "six" , "seven" , "eight" , "nine" , "colon" , |
83 | /* 30 */ |
84 | "semicolon" , "less" , "equal" , "greater" , "question" , |
85 | "at" , "A" , "B" , "C" , "D" , |
86 | /* 40 */ |
87 | "E" , "F" , "G" , "H" , "I" , |
88 | "J" , "K" , "L" , "M" , "N" , |
89 | /* 50 */ |
90 | "O" , "P" , "Q" , "R" , "S" , |
91 | "T" , "U" , "V" , "W" , "X" , |
92 | /* 60 */ |
93 | "Y" , "Z" , "bracketleft" , "backslash" , "bracketright" , |
94 | "asciicircum" , "underscore" , "grave" , "a" , "b" , |
95 | /* 70 */ |
96 | "c" , "d" , "e" , "f" , "g" , |
97 | "h" , "i" , "j" , "k" , "l" , |
98 | /* 80 */ |
99 | "m" , "n" , "o" , "p" , "q" , |
100 | "r" , "s" , "t" , "u" , "v" , |
101 | /* 90 */ |
102 | "w" , "x" , "y" , "z" , "braceleft" , |
103 | "bar" , "braceright" , "asciitilde" , "Adieresis" , "Aring" , |
104 | /* 100 */ |
105 | "Ccedilla" , "Eacute" , "Ntilde" , "Odieresis" , "Udieresis" , |
106 | "aacute" , "agrave" , "acircumflex" , "adieresis" , "atilde" , |
107 | /* 110 */ |
108 | "aring" , "ccedilla" , "eacute" , "egrave" , "ecircumflex" , |
109 | "edieresis" , "iacute" , "igrave" , "icircumflex" , "idieresis" , |
110 | /* 120 */ |
111 | "ntilde" , "oacute" , "ograve" , "ocircumflex" , "odieresis" , |
112 | "otilde" , "uacute" , "ugrave" , "ucircumflex" , "udieresis" , |
113 | /* 130 */ |
114 | "dagger" , "degree" , "cent" , "sterling" , "section" , |
115 | "bullet" , "paragraph" , "germandbls" , "registered" , "copyright" , |
116 | /* 140 */ |
117 | "trademark" , "acute" , "dieresis" , "notequal" , "AE" , |
118 | "Oslash" , "infinity" , "plusminus" , "lessequal" , "greaterequal" , |
119 | /* 150 */ |
120 | "yen" , "mu" , "partialdiff" , "summation" , "product" , |
121 | "pi" , "integral" , "ordfeminine" , "ordmasculine" , "Omega" , |
122 | /* 160 */ |
123 | "ae" , "oslash" , "questiondown" , "exclamdown" , "logicalnot" , |
124 | "radical" , "florin" , "approxequal" , "Delta" , "guillemotleft" , |
125 | /* 170 */ |
126 | "guillemotright" , "ellipsis" , "nonbreakingspace" , "Agrave" , "Atilde" , |
127 | "Otilde" , "OE" , "oe" , "endash" , "emdash" , |
128 | /* 180 */ |
129 | "quotedblleft" , "quotedblright" , "quoteleft" , "quoteright" , "divide" , |
130 | "lozenge" , "ydieresis" , "Ydieresis" , "fraction" , "currency" , |
131 | /* 190 */ |
132 | "guilsinglleft" , "guilsinglright" , "fi" , "fl" , "daggerdbl" , |
133 | "periodcentered" , "quotesinglbase" , "quotedblbase" , "perthousand" , "Acircumflex" , |
134 | /* 200 */ |
135 | "Ecircumflex" , "Aacute" , "Edieresis" , "Egrave" , "Iacute" , |
136 | "Icircumflex" , "Idieresis" , "Igrave" , "Oacute" , "Ocircumflex" , |
137 | /* 210 */ |
138 | "apple" , "Ograve" , "Uacute" , "Ucircumflex" , "Ugrave" , |
139 | "dotlessi" , "circumflex" , "tilde" , "macron" , "breve" , |
140 | /* 220 */ |
141 | "dotaccent" , "ring" , "cedilla" , "hungarumlaut" , "ogonek" , |
142 | "caron" , "Lslash" , "lslash" , "Scaron" , "scaron" , |
143 | /* 230 */ |
144 | "Zcaron" , "zcaron" , "brokenbar" , "Eth" , "eth" , |
145 | "Yacute" , "yacute" , "Thorn" , "thorn" , "minus" , |
146 | /* 240 */ |
147 | "multiply" , "onesuperior" , "twosuperior" , "threesuperior" , "onehalf" , |
148 | "onequarter" , "threequarters" , "franc" , "Gbreve" , "gbreve" , |
149 | /* 250 */ |
150 | "Idotaccent" , "Scedilla" , "scedilla" , "Cacute" , "cacute" , |
151 | "Ccaron" , "ccaron" , "dcroat" , |
152 | }; |
153 | |
154 | |
155 | #endif /* !FT_CONFIG_OPTION_POSTSCRIPT_NAMES */ |
156 | |
157 | |
158 | static FT_Error |
159 | load_format_20( TT_Post_Names names, |
160 | FT_Stream stream, |
161 | FT_UShort num_glyphs, |
162 | FT_ULong post_len ) |
163 | { |
164 | FT_Memory memory = stream->memory; |
165 | FT_Error error; |
166 | |
167 | FT_UShort n; |
168 | FT_UShort num_names = 0; |
169 | |
170 | FT_UShort* glyph_indices = NULL; |
171 | FT_Byte** name_strings = NULL; |
172 | FT_Byte* q; |
173 | |
174 | |
175 | if ( (FT_ULong)num_glyphs * 2 > post_len ) |
176 | { |
177 | error = FT_THROW( Invalid_File_Format ); |
178 | goto Exit; |
179 | } |
180 | |
181 | /* load the indices and note their maximum */ |
182 | if ( FT_QNEW_ARRAY( glyph_indices, num_glyphs ) || |
183 | FT_FRAME_ENTER( num_glyphs * 2 ) ) |
184 | goto Fail; |
185 | |
186 | q = (FT_Byte*)stream->cursor; |
187 | |
188 | for ( n = 0; n < num_glyphs; n++ ) |
189 | { |
190 | FT_UShort idx = FT_NEXT_USHORT( q ); |
191 | |
192 | |
193 | if ( idx > num_names ) |
194 | num_names = idx; |
195 | |
196 | glyph_indices[n] = idx; |
197 | } |
198 | |
199 | FT_FRAME_EXIT(); |
200 | |
201 | /* compute number of names stored in the table */ |
202 | num_names = num_names > 257 ? num_names - 257 : 0; |
203 | |
204 | /* now load the name strings */ |
205 | if ( num_names ) |
206 | { |
207 | FT_ULong p; |
208 | FT_Byte* strings; |
209 | |
210 | |
211 | post_len -= (FT_ULong)num_glyphs * 2; |
212 | |
213 | if ( FT_QALLOC( name_strings, num_names * sizeof ( FT_Byte* ) + |
214 | post_len + 1 ) ) |
215 | goto Fail; |
216 | |
217 | strings = (FT_Byte*)( name_strings + num_names ); |
218 | if ( FT_STREAM_READ( strings, post_len ) ) |
219 | goto Fail; |
220 | |
221 | /* convert from Pascal- to C-strings and set pointers */ |
222 | for ( p = 0, n = 0; p < post_len && n < num_names; n++ ) |
223 | { |
224 | FT_UInt len = strings[p]; |
225 | |
226 | |
227 | if ( len > 63U ) |
228 | { |
229 | error = FT_THROW( Invalid_File_Format ); |
230 | goto Fail; |
231 | } |
232 | |
233 | strings[p] = 0; |
234 | name_strings[n] = strings + p + 1; |
235 | p += len + 1; |
236 | } |
237 | strings[post_len] = 0; |
238 | |
239 | /* deal with missing or insufficient string data */ |
240 | if ( n < num_names ) |
241 | { |
242 | FT_TRACE4(( "load_format_20: %hu PostScript names are truncated\n" , |
243 | num_names - n )); |
244 | |
245 | for ( ; n < num_names; n++ ) |
246 | name_strings[n] = strings + post_len; |
247 | } |
248 | } |
249 | |
250 | /* all right, set table fields and exit successfully */ |
251 | names->num_glyphs = num_glyphs; |
252 | names->num_names = num_names; |
253 | names->glyph_indices = glyph_indices; |
254 | names->glyph_names = name_strings; |
255 | |
256 | return FT_Err_Ok; |
257 | |
258 | Fail: |
259 | FT_FREE( name_strings ); |
260 | FT_FREE( glyph_indices ); |
261 | |
262 | Exit: |
263 | return error; |
264 | } |
265 | |
266 | |
267 | static FT_Error |
268 | load_format_25( TT_Post_Names names, |
269 | FT_Stream stream, |
270 | FT_UShort num_glyphs, |
271 | FT_ULong post_len ) |
272 | { |
273 | FT_Memory memory = stream->memory; |
274 | FT_Error error; |
275 | |
276 | FT_UShort n; |
277 | FT_UShort* glyph_indices = NULL; |
278 | FT_Byte* q; |
279 | |
280 | |
281 | /* check the number of glyphs, including the theoretical limit */ |
282 | if ( num_glyphs > post_len || |
283 | num_glyphs > 258 + 128 ) |
284 | { |
285 | error = FT_THROW( Invalid_File_Format ); |
286 | goto Exit; |
287 | } |
288 | |
289 | /* load the indices and check their Mac range */ |
290 | if ( FT_QNEW_ARRAY( glyph_indices, num_glyphs ) || |
291 | FT_FRAME_ENTER( num_glyphs ) ) |
292 | goto Fail; |
293 | |
294 | q = (FT_Byte*)stream->cursor; |
295 | |
296 | for ( n = 0; n < num_glyphs; n++ ) |
297 | { |
298 | FT_Int idx = n + FT_NEXT_CHAR( q ); |
299 | |
300 | |
301 | if ( idx < 0 || idx > 257 ) |
302 | idx = 0; |
303 | |
304 | glyph_indices[n] = (FT_UShort)idx; |
305 | } |
306 | |
307 | FT_FRAME_EXIT(); |
308 | |
309 | /* OK, set table fields and exit successfully */ |
310 | names->num_glyphs = num_glyphs; |
311 | names->glyph_indices = glyph_indices; |
312 | |
313 | return FT_Err_Ok; |
314 | |
315 | Fail: |
316 | FT_FREE( glyph_indices ); |
317 | |
318 | Exit: |
319 | return error; |
320 | } |
321 | |
322 | |
323 | static FT_Error |
324 | load_post_names( TT_Face face ) |
325 | { |
326 | FT_Error error = FT_Err_Ok; |
327 | FT_Stream stream = face->root.stream; |
328 | FT_Fixed format = face->postscript.FormatType; |
329 | FT_ULong post_len; |
330 | FT_UShort num_glyphs; |
331 | |
332 | |
333 | /* seek to the beginning of the PS names table */ |
334 | error = face->goto_table( face, TTAG_post, stream, &post_len ); |
335 | if ( error ) |
336 | goto Exit; |
337 | |
338 | /* UNDOCUMENTED! The number of glyphs in this table can be smaller */ |
339 | /* than the value in the maxp table (cf. cyberbit.ttf). */ |
340 | if ( post_len < 34 || |
341 | FT_STREAM_SKIP( 32 ) || |
342 | FT_READ_USHORT( num_glyphs ) || |
343 | num_glyphs > face->max_profile.numGlyphs || |
344 | num_glyphs == 0 ) |
345 | goto Exit; |
346 | |
347 | /* now read postscript names data */ |
348 | if ( format == 0x00020000L ) |
349 | error = load_format_20( &face->postscript_names, stream, |
350 | num_glyphs, post_len - 34 ); |
351 | else if ( format == 0x00025000L ) |
352 | error = load_format_25( &face->postscript_names, stream, |
353 | num_glyphs, post_len - 34 ); |
354 | |
355 | Exit: |
356 | face->postscript_names.loaded = 1; /* even if failed */ |
357 | return error; |
358 | } |
359 | |
360 | |
361 | FT_LOCAL_DEF( void ) |
362 | tt_face_free_ps_names( TT_Face face ) |
363 | { |
364 | FT_Memory memory = face->root.memory; |
365 | TT_Post_Names names = &face->postscript_names; |
366 | |
367 | |
368 | if ( names->num_glyphs ) |
369 | { |
370 | FT_FREE( names->glyph_indices ); |
371 | names->num_glyphs = 0; |
372 | } |
373 | |
374 | if ( names->num_names ) |
375 | { |
376 | FT_FREE( names->glyph_names ); |
377 | names->num_names = 0; |
378 | } |
379 | |
380 | names->loaded = 0; |
381 | } |
382 | |
383 | |
384 | /************************************************************************** |
385 | * |
386 | * @Function: |
387 | * tt_face_get_ps_name |
388 | * |
389 | * @Description: |
390 | * Get the PostScript glyph name of a glyph. |
391 | * |
392 | * @Input: |
393 | * face :: |
394 | * A handle to the parent face. |
395 | * |
396 | * idx :: |
397 | * The glyph index. |
398 | * |
399 | * @InOut: |
400 | * PSname :: |
401 | * The address of a string pointer. Undefined in case of |
402 | * error, otherwise it is a pointer to the glyph name. |
403 | * |
404 | * You must not modify the returned string! |
405 | * |
406 | * @Output: |
407 | * FreeType error code. 0 means success. |
408 | */ |
409 | FT_LOCAL_DEF( FT_Error ) |
410 | tt_face_get_ps_name( TT_Face face, |
411 | FT_UInt idx, |
412 | FT_String** PSname ) |
413 | { |
414 | FT_Error error; |
415 | FT_Fixed format; |
416 | |
417 | #ifdef FT_CONFIG_OPTION_POSTSCRIPT_NAMES |
418 | FT_Service_PsCMaps psnames; |
419 | #endif |
420 | |
421 | |
422 | if ( !face ) |
423 | return FT_THROW( Invalid_Face_Handle ); |
424 | |
425 | if ( idx >= (FT_UInt)face->max_profile.numGlyphs ) |
426 | return FT_THROW( Invalid_Glyph_Index ); |
427 | |
428 | #ifdef FT_CONFIG_OPTION_POSTSCRIPT_NAMES |
429 | psnames = (FT_Service_PsCMaps)face->psnames; |
430 | if ( !psnames ) |
431 | return FT_THROW( Unimplemented_Feature ); |
432 | #endif |
433 | |
434 | /* `.notdef' by default */ |
435 | *PSname = MAC_NAME( 0 ); |
436 | |
437 | format = face->postscript.FormatType; |
438 | |
439 | if ( format == 0x00010000L ) |
440 | { |
441 | if ( idx < 258 ) /* paranoid checking */ |
442 | *PSname = MAC_NAME( idx ); |
443 | } |
444 | else if ( format == 0x00020000L || |
445 | format == 0x00025000L ) |
446 | { |
447 | TT_Post_Names names = &face->postscript_names; |
448 | |
449 | |
450 | if ( !names->loaded ) |
451 | { |
452 | error = load_post_names( face ); |
453 | if ( error ) |
454 | goto End; |
455 | } |
456 | |
457 | if ( idx < (FT_UInt)names->num_glyphs ) |
458 | { |
459 | FT_UShort name_index = names->glyph_indices[idx]; |
460 | |
461 | |
462 | if ( name_index < 258 ) |
463 | *PSname = MAC_NAME( name_index ); |
464 | else /* only for version 2.0 */ |
465 | *PSname = (FT_String*)names->glyph_names[name_index - 258]; |
466 | } |
467 | } |
468 | |
469 | /* nothing to do for format == 0x00030000L */ |
470 | |
471 | End: |
472 | /* post format errors ignored */ |
473 | return FT_Err_Ok; |
474 | } |
475 | |
476 | #else /* !TT_CONFIG_OPTION_POSTSCRIPT_NAMES */ |
477 | |
478 | /* ANSI C doesn't like empty source files */ |
479 | typedef int tt_post_dummy_; |
480 | |
481 | #endif /* !TT_CONFIG_OPTION_POSTSCRIPT_NAMES */ |
482 | |
483 | |
484 | /* END */ |
485 | |