1 | /**************************************************************************** |
2 | * |
3 | * ttpost.c |
4 | * |
5 | * PostScript name table processing for TrueType and OpenType fonts |
6 | * (body). |
7 | * |
8 | * Copyright (C) 1996-2019 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 <ft2build.h> |
29 | #include FT_INTERNAL_DEBUG_H |
30 | #include FT_INTERNAL_STREAM_H |
31 | #include FT_TRUETYPE_TAGS_H |
32 | |
33 | |
34 | #ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES |
35 | |
36 | #include "ttpost.h" |
37 | |
38 | #include "sferrors.h" |
39 | |
40 | |
41 | /************************************************************************** |
42 | * |
43 | * The macro FT_COMPONENT is used in trace mode. It is an implicit |
44 | * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log |
45 | * messages during execution. |
46 | */ |
47 | #undef FT_COMPONENT |
48 | #define FT_COMPONENT ttpost |
49 | |
50 | |
51 | /* If this configuration macro is defined, we rely on the `psnames' */ |
52 | /* module to grab the glyph names. */ |
53 | |
54 | #ifdef FT_CONFIG_OPTION_POSTSCRIPT_NAMES |
55 | |
56 | |
57 | #include FT_SERVICE_POSTSCRIPT_CMAPS_H |
58 | |
59 | #define MAC_NAME( x ) (FT_String*)psnames->macintosh_name( (FT_UInt)(x) ) |
60 | |
61 | |
62 | #else /* FT_CONFIG_OPTION_POSTSCRIPT_NAMES */ |
63 | |
64 | |
65 | /* Otherwise, we ignore the `psnames' module, and provide our own */ |
66 | /* table of Mac names. Thus, it is possible to build a version of */ |
67 | /* FreeType without the Type 1 driver & psnames module. */ |
68 | |
69 | #define MAC_NAME( x ) (FT_String*)tt_post_default_names[x] |
70 | |
71 | /* the 258 default Mac PS glyph names; see file `tools/glnames.py' */ |
72 | |
73 | static const FT_String* const tt_post_default_names[258] = |
74 | { |
75 | /* 0 */ |
76 | ".notdef" , ".null" , "nonmarkingreturn" , "space" , "exclam" , |
77 | "quotedbl" , "numbersign" , "dollar" , "percent" , "ampersand" , |
78 | /* 10 */ |
79 | "quotesingle" , "parenleft" , "parenright" , "asterisk" , "plus" , |
80 | "comma" , "hyphen" , "period" , "slash" , "zero" , |
81 | /* 20 */ |
82 | "one" , "two" , "three" , "four" , "five" , |
83 | "six" , "seven" , "eight" , "nine" , "colon" , |
84 | /* 30 */ |
85 | "semicolon" , "less" , "equal" , "greater" , "question" , |
86 | "at" , "A" , "B" , "C" , "D" , |
87 | /* 40 */ |
88 | "E" , "F" , "G" , "H" , "I" , |
89 | "J" , "K" , "L" , "M" , "N" , |
90 | /* 50 */ |
91 | "O" , "P" , "Q" , "R" , "S" , |
92 | "T" , "U" , "V" , "W" , "X" , |
93 | /* 60 */ |
94 | "Y" , "Z" , "bracketleft" , "backslash" , "bracketright" , |
95 | "asciicircum" , "underscore" , "grave" , "a" , "b" , |
96 | /* 70 */ |
97 | "c" , "d" , "e" , "f" , "g" , |
98 | "h" , "i" , "j" , "k" , "l" , |
99 | /* 80 */ |
100 | "m" , "n" , "o" , "p" , "q" , |
101 | "r" , "s" , "t" , "u" , "v" , |
102 | /* 90 */ |
103 | "w" , "x" , "y" , "z" , "braceleft" , |
104 | "bar" , "braceright" , "asciitilde" , "Adieresis" , "Aring" , |
105 | /* 100 */ |
106 | "Ccedilla" , "Eacute" , "Ntilde" , "Odieresis" , "Udieresis" , |
107 | "aacute" , "agrave" , "acircumflex" , "adieresis" , "atilde" , |
108 | /* 110 */ |
109 | "aring" , "ccedilla" , "eacute" , "egrave" , "ecircumflex" , |
110 | "edieresis" , "iacute" , "igrave" , "icircumflex" , "idieresis" , |
111 | /* 120 */ |
112 | "ntilde" , "oacute" , "ograve" , "ocircumflex" , "odieresis" , |
113 | "otilde" , "uacute" , "ugrave" , "ucircumflex" , "udieresis" , |
114 | /* 130 */ |
115 | "dagger" , "degree" , "cent" , "sterling" , "section" , |
116 | "bullet" , "paragraph" , "germandbls" , "registered" , "copyright" , |
117 | /* 140 */ |
118 | "trademark" , "acute" , "dieresis" , "notequal" , "AE" , |
119 | "Oslash" , "infinity" , "plusminus" , "lessequal" , "greaterequal" , |
120 | /* 150 */ |
121 | "yen" , "mu" , "partialdiff" , "summation" , "product" , |
122 | "pi" , "integral" , "ordfeminine" , "ordmasculine" , "Omega" , |
123 | /* 160 */ |
124 | "ae" , "oslash" , "questiondown" , "exclamdown" , "logicalnot" , |
125 | "radical" , "florin" , "approxequal" , "Delta" , "guillemotleft" , |
126 | /* 170 */ |
127 | "guillemotright" , "ellipsis" , "nonbreakingspace" , "Agrave" , "Atilde" , |
128 | "Otilde" , "OE" , "oe" , "endash" , "emdash" , |
129 | /* 180 */ |
130 | "quotedblleft" , "quotedblright" , "quoteleft" , "quoteright" , "divide" , |
131 | "lozenge" , "ydieresis" , "Ydieresis" , "fraction" , "currency" , |
132 | /* 190 */ |
133 | "guilsinglleft" , "guilsinglright" , "fi" , "fl" , "daggerdbl" , |
134 | "periodcentered" , "quotesinglbase" , "quotedblbase" , "perthousand" , "Acircumflex" , |
135 | /* 200 */ |
136 | "Ecircumflex" , "Aacute" , "Edieresis" , "Egrave" , "Iacute" , |
137 | "Icircumflex" , "Idieresis" , "Igrave" , "Oacute" , "Ocircumflex" , |
138 | /* 210 */ |
139 | "apple" , "Ograve" , "Uacute" , "Ucircumflex" , "Ugrave" , |
140 | "dotlessi" , "circumflex" , "tilde" , "macron" , "breve" , |
141 | /* 220 */ |
142 | "dotaccent" , "ring" , "cedilla" , "hungarumlaut" , "ogonek" , |
143 | "caron" , "Lslash" , "lslash" , "Scaron" , "scaron" , |
144 | /* 230 */ |
145 | "Zcaron" , "zcaron" , "brokenbar" , "Eth" , "eth" , |
146 | "Yacute" , "yacute" , "Thorn" , "thorn" , "minus" , |
147 | /* 240 */ |
148 | "multiply" , "onesuperior" , "twosuperior" , "threesuperior" , "onehalf" , |
149 | "onequarter" , "threequarters" , "franc" , "Gbreve" , "gbreve" , |
150 | /* 250 */ |
151 | "Idotaccent" , "Scedilla" , "scedilla" , "Cacute" , "cacute" , |
152 | "Ccaron" , "ccaron" , "dcroat" , |
153 | }; |
154 | |
155 | |
156 | #endif /* FT_CONFIG_OPTION_POSTSCRIPT_NAMES */ |
157 | |
158 | |
159 | static FT_Error |
160 | load_format_20( TT_Face face, |
161 | FT_Stream stream, |
162 | FT_ULong post_limit ) |
163 | { |
164 | FT_Memory memory = stream->memory; |
165 | FT_Error error; |
166 | |
167 | FT_Int num_glyphs; |
168 | FT_UShort num_names; |
169 | |
170 | FT_UShort* glyph_indices = NULL; |
171 | FT_Char** name_strings = NULL; |
172 | |
173 | |
174 | if ( FT_READ_USHORT( num_glyphs ) ) |
175 | goto Exit; |
176 | |
177 | /* UNDOCUMENTED! The number of glyphs in this table can be smaller */ |
178 | /* than the value in the maxp table (cf. cyberbit.ttf). */ |
179 | |
180 | /* There already exist fonts which have more than 32768 glyph names */ |
181 | /* in this table, so the test for this threshold has been dropped. */ |
182 | |
183 | if ( num_glyphs > face->max_profile.numGlyphs ) |
184 | { |
185 | error = FT_THROW( Invalid_File_Format ); |
186 | goto Exit; |
187 | } |
188 | |
189 | /* load the indices */ |
190 | { |
191 | FT_Int n; |
192 | |
193 | |
194 | if ( FT_NEW_ARRAY ( glyph_indices, num_glyphs ) || |
195 | FT_FRAME_ENTER( num_glyphs * 2L ) ) |
196 | goto Fail; |
197 | |
198 | for ( n = 0; n < num_glyphs; n++ ) |
199 | glyph_indices[n] = FT_GET_USHORT(); |
200 | |
201 | FT_FRAME_EXIT(); |
202 | } |
203 | |
204 | /* compute number of names stored in table */ |
205 | { |
206 | FT_Int n; |
207 | |
208 | |
209 | num_names = 0; |
210 | |
211 | for ( n = 0; n < num_glyphs; n++ ) |
212 | { |
213 | FT_Int idx; |
214 | |
215 | |
216 | idx = glyph_indices[n]; |
217 | if ( idx >= 258 ) |
218 | { |
219 | idx -= 257; |
220 | if ( idx > num_names ) |
221 | num_names = (FT_UShort)idx; |
222 | } |
223 | } |
224 | } |
225 | |
226 | /* now load the name strings */ |
227 | { |
228 | FT_UShort n; |
229 | |
230 | |
231 | if ( FT_NEW_ARRAY( name_strings, num_names ) ) |
232 | goto Fail; |
233 | |
234 | for ( n = 0; n < num_names; n++ ) |
235 | { |
236 | FT_UInt len; |
237 | |
238 | |
239 | if ( FT_STREAM_POS() >= post_limit ) |
240 | break; |
241 | else |
242 | { |
243 | FT_TRACE6(( "load_format_20: %d byte left in post table\n" , |
244 | post_limit - FT_STREAM_POS() )); |
245 | |
246 | if ( FT_READ_BYTE( len ) ) |
247 | goto Fail1; |
248 | } |
249 | |
250 | if ( len > post_limit || |
251 | FT_STREAM_POS() > post_limit - len ) |
252 | { |
253 | FT_Int d = (FT_Int)post_limit - (FT_Int)FT_STREAM_POS(); |
254 | |
255 | |
256 | FT_ERROR(( "load_format_20:" |
257 | " exceeding string length (%d)," |
258 | " truncating at end of post table (%d byte left)\n" , |
259 | len, d )); |
260 | len = (FT_UInt)FT_MAX( 0, d ); |
261 | } |
262 | |
263 | if ( FT_NEW_ARRAY( name_strings[n], len + 1 ) || |
264 | FT_STREAM_READ( name_strings[n], len ) ) |
265 | goto Fail1; |
266 | |
267 | name_strings[n][len] = '\0'; |
268 | } |
269 | |
270 | if ( n < num_names ) |
271 | { |
272 | FT_ERROR(( "load_format_20:" |
273 | " all entries in post table are already parsed," |
274 | " using NULL names for gid %d - %d\n" , |
275 | n, num_names - 1 )); |
276 | for ( ; n < num_names; n++ ) |
277 | if ( FT_NEW_ARRAY( name_strings[n], 1 ) ) |
278 | goto Fail1; |
279 | else |
280 | name_strings[n][0] = '\0'; |
281 | } |
282 | } |
283 | |
284 | /* all right, set table fields and exit successfully */ |
285 | { |
286 | TT_Post_20 table = &face->postscript_names.names.format_20; |
287 | |
288 | |
289 | table->num_glyphs = (FT_UShort)num_glyphs; |
290 | table->num_names = (FT_UShort)num_names; |
291 | table->glyph_indices = glyph_indices; |
292 | table->glyph_names = name_strings; |
293 | } |
294 | return FT_Err_Ok; |
295 | |
296 | Fail1: |
297 | { |
298 | FT_UShort n; |
299 | |
300 | |
301 | for ( n = 0; n < num_names; n++ ) |
302 | FT_FREE( name_strings[n] ); |
303 | } |
304 | |
305 | Fail: |
306 | FT_FREE( name_strings ); |
307 | FT_FREE( glyph_indices ); |
308 | |
309 | Exit: |
310 | return error; |
311 | } |
312 | |
313 | |
314 | static FT_Error |
315 | load_format_25( TT_Face face, |
316 | FT_Stream stream, |
317 | FT_ULong post_limit ) |
318 | { |
319 | FT_Memory memory = stream->memory; |
320 | FT_Error error; |
321 | |
322 | FT_Int num_glyphs; |
323 | FT_Char* offset_table = NULL; |
324 | |
325 | FT_UNUSED( post_limit ); |
326 | |
327 | |
328 | if ( FT_READ_USHORT( num_glyphs ) ) |
329 | goto Exit; |
330 | |
331 | /* check the number of glyphs */ |
332 | if ( num_glyphs > face->max_profile.numGlyphs || |
333 | num_glyphs > 258 || |
334 | num_glyphs < 1 ) |
335 | { |
336 | error = FT_THROW( Invalid_File_Format ); |
337 | goto Exit; |
338 | } |
339 | |
340 | if ( FT_NEW_ARRAY( offset_table, num_glyphs ) || |
341 | FT_STREAM_READ( offset_table, num_glyphs ) ) |
342 | goto Fail; |
343 | |
344 | /* now check the offset table */ |
345 | { |
346 | FT_Int n; |
347 | |
348 | |
349 | for ( n = 0; n < num_glyphs; n++ ) |
350 | { |
351 | FT_Long idx = (FT_Long)n + offset_table[n]; |
352 | |
353 | |
354 | if ( idx < 0 || idx > num_glyphs ) |
355 | { |
356 | error = FT_THROW( Invalid_File_Format ); |
357 | goto Fail; |
358 | } |
359 | } |
360 | } |
361 | |
362 | /* OK, set table fields and exit successfully */ |
363 | { |
364 | TT_Post_25 table = &face->postscript_names.names.format_25; |
365 | |
366 | |
367 | table->num_glyphs = (FT_UShort)num_glyphs; |
368 | table->offsets = offset_table; |
369 | } |
370 | |
371 | return FT_Err_Ok; |
372 | |
373 | Fail: |
374 | FT_FREE( offset_table ); |
375 | |
376 | Exit: |
377 | return error; |
378 | } |
379 | |
380 | |
381 | static FT_Error |
382 | load_post_names( TT_Face face ) |
383 | { |
384 | FT_Stream stream; |
385 | FT_Error error; |
386 | FT_Fixed format; |
387 | FT_ULong post_len; |
388 | FT_ULong post_limit; |
389 | |
390 | |
391 | /* get a stream for the face's resource */ |
392 | stream = face->root.stream; |
393 | |
394 | /* seek to the beginning of the PS names table */ |
395 | error = face->goto_table( face, TTAG_post, stream, &post_len ); |
396 | if ( error ) |
397 | goto Exit; |
398 | |
399 | post_limit = FT_STREAM_POS() + post_len; |
400 | |
401 | format = face->postscript.FormatType; |
402 | |
403 | /* go to beginning of subtable */ |
404 | if ( FT_STREAM_SKIP( 32 ) ) |
405 | goto Exit; |
406 | |
407 | /* now read postscript table */ |
408 | if ( format == 0x00020000L ) |
409 | error = load_format_20( face, stream, post_limit ); |
410 | else if ( format == 0x00025000L ) |
411 | error = load_format_25( face, stream, post_limit ); |
412 | else |
413 | error = FT_THROW( Invalid_File_Format ); |
414 | |
415 | face->postscript_names.loaded = 1; |
416 | |
417 | Exit: |
418 | return error; |
419 | } |
420 | |
421 | |
422 | FT_LOCAL_DEF( void ) |
423 | tt_face_free_ps_names( TT_Face face ) |
424 | { |
425 | FT_Memory memory = face->root.memory; |
426 | TT_Post_Names names = &face->postscript_names; |
427 | FT_Fixed format; |
428 | |
429 | |
430 | if ( names->loaded ) |
431 | { |
432 | format = face->postscript.FormatType; |
433 | |
434 | if ( format == 0x00020000L ) |
435 | { |
436 | TT_Post_20 table = &names->names.format_20; |
437 | FT_UShort n; |
438 | |
439 | |
440 | FT_FREE( table->glyph_indices ); |
441 | table->num_glyphs = 0; |
442 | |
443 | for ( n = 0; n < table->num_names; n++ ) |
444 | FT_FREE( table->glyph_names[n] ); |
445 | |
446 | FT_FREE( table->glyph_names ); |
447 | table->num_names = 0; |
448 | } |
449 | else if ( format == 0x00025000L ) |
450 | { |
451 | TT_Post_25 table = &names->names.format_25; |
452 | |
453 | |
454 | FT_FREE( table->offsets ); |
455 | table->num_glyphs = 0; |
456 | } |
457 | } |
458 | names->loaded = 0; |
459 | } |
460 | |
461 | |
462 | /************************************************************************** |
463 | * |
464 | * @Function: |
465 | * tt_face_get_ps_name |
466 | * |
467 | * @Description: |
468 | * Get the PostScript glyph name of a glyph. |
469 | * |
470 | * @Input: |
471 | * face :: |
472 | * A handle to the parent face. |
473 | * |
474 | * idx :: |
475 | * The glyph index. |
476 | * |
477 | * @InOut: |
478 | * PSname :: |
479 | * The address of a string pointer. Undefined in case of |
480 | * error, otherwise it is a pointer to the glyph name. |
481 | * |
482 | * You must not modify the returned string! |
483 | * |
484 | * @Output: |
485 | * FreeType error code. 0 means success. |
486 | */ |
487 | FT_LOCAL_DEF( FT_Error ) |
488 | tt_face_get_ps_name( TT_Face face, |
489 | FT_UInt idx, |
490 | FT_String** PSname ) |
491 | { |
492 | FT_Error error; |
493 | TT_Post_Names names; |
494 | FT_Fixed format; |
495 | |
496 | #ifdef FT_CONFIG_OPTION_POSTSCRIPT_NAMES |
497 | FT_Service_PsCMaps psnames; |
498 | #endif |
499 | |
500 | |
501 | if ( !face ) |
502 | return FT_THROW( Invalid_Face_Handle ); |
503 | |
504 | if ( idx >= (FT_UInt)face->max_profile.numGlyphs ) |
505 | return FT_THROW( Invalid_Glyph_Index ); |
506 | |
507 | #ifdef FT_CONFIG_OPTION_POSTSCRIPT_NAMES |
508 | psnames = (FT_Service_PsCMaps)face->psnames; |
509 | if ( !psnames ) |
510 | return FT_THROW( Unimplemented_Feature ); |
511 | #endif |
512 | |
513 | names = &face->postscript_names; |
514 | |
515 | /* `.notdef' by default */ |
516 | *PSname = MAC_NAME( 0 ); |
517 | |
518 | format = face->postscript.FormatType; |
519 | |
520 | if ( format == 0x00010000L ) |
521 | { |
522 | if ( idx < 258 ) /* paranoid checking */ |
523 | *PSname = MAC_NAME( idx ); |
524 | } |
525 | else if ( format == 0x00020000L ) |
526 | { |
527 | TT_Post_20 table = &names->names.format_20; |
528 | |
529 | |
530 | if ( !names->loaded ) |
531 | { |
532 | error = load_post_names( face ); |
533 | if ( error ) |
534 | goto End; |
535 | } |
536 | |
537 | if ( idx < (FT_UInt)table->num_glyphs ) |
538 | { |
539 | FT_UShort name_index = table->glyph_indices[idx]; |
540 | |
541 | |
542 | if ( name_index < 258 ) |
543 | *PSname = MAC_NAME( name_index ); |
544 | else |
545 | *PSname = (FT_String*)table->glyph_names[name_index - 258]; |
546 | } |
547 | } |
548 | else if ( format == 0x00025000L ) |
549 | { |
550 | TT_Post_25 table = &names->names.format_25; |
551 | |
552 | |
553 | if ( !names->loaded ) |
554 | { |
555 | error = load_post_names( face ); |
556 | if ( error ) |
557 | goto End; |
558 | } |
559 | |
560 | if ( idx < (FT_UInt)table->num_glyphs ) /* paranoid checking */ |
561 | *PSname = MAC_NAME( (FT_Int)idx + table->offsets[idx] ); |
562 | } |
563 | |
564 | /* nothing to do for format == 0x00030000L */ |
565 | |
566 | End: |
567 | return FT_Err_Ok; |
568 | } |
569 | |
570 | #else /* !TT_CONFIG_OPTION_POSTSCRIPT_NAMES */ |
571 | |
572 | /* ANSI C doesn't like empty source files */ |
573 | typedef int _tt_post_dummy; |
574 | |
575 | #endif /* !TT_CONFIG_OPTION_POSTSCRIPT_NAMES */ |
576 | |
577 | |
578 | /* END */ |
579 | |