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 load_format_20(TT_Post_Names names,FT_Stream stream,FT_UShort num_glyphs,FT_ULong post_len)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 load_format_25(TT_Post_Names names,FT_Stream stream,FT_UShort num_glyphs,FT_ULong post_len)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 load_post_names(TT_Face face)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 ) tt_face_free_ps_names(TT_Face face)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 ) tt_face_get_ps_name(TT_Face face,FT_UInt idx,FT_String ** PSname)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