| 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 | |