1 /**************************************************************************** 2 * 3 * t42parse.c 4 * 5 * Type 42 font parser (body). 6 * 7 * Copyright (C) 2002-2023 by 8 * Roberto Alameda. 9 * 10 * This file is part of the FreeType project, and may only be used, 11 * modified, and distributed under the terms of the FreeType project 12 * license, LICENSE.TXT. By continuing to use, modify, or distribute 13 * this file you indicate that you have read the license and 14 * understand and accept it fully. 15 * 16 */ 17 18 19 #include "t42parse.h" 20 #include "t42error.h" 21 #include <freetype/internal/ftdebug.h> 22 #include <freetype/internal/ftstream.h> 23 #include <freetype/internal/psaux.h> 24 25 26 /************************************************************************** 27 * 28 * The macro FT_COMPONENT is used in trace mode. It is an implicit 29 * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log 30 * messages during execution. 31 */ 32 #undef FT_COMPONENT 33 #define FT_COMPONENT t42 34 35 36 static void 37 t42_parse_font_matrix( FT_Face face, 38 void* loader_ ); 39 static void 40 t42_parse_encoding( FT_Face face, 41 void* loader_ ); 42 43 static void 44 t42_parse_charstrings( FT_Face face, 45 void* loader_ ); 46 47 static void 48 t42_parse_sfnts( FT_Face face, 49 void* loader_ ); 50 51 52 /* as Type42 fonts have no Private dict, */ 53 /* we set the last argument of T1_FIELD_XXX to 0 */ 54 static const 55 T1_FieldRec t42_keywords[] = 56 { 57 58 #undef FT_STRUCTURE 59 #define FT_STRUCTURE T1_FontInfo 60 #undef T1CODE 61 #define T1CODE T1_FIELD_LOCATION_FONT_INFO 62 63 T1_FIELD_STRING( "version", version, 0 ) 64 T1_FIELD_STRING( "Notice", notice, 0 ) 65 T1_FIELD_STRING( "FullName", full_name, 0 ) 66 T1_FIELD_STRING( "FamilyName", family_name, 0 ) 67 T1_FIELD_STRING( "Weight", weight, 0 ) 68 T1_FIELD_NUM ( "ItalicAngle", italic_angle, 0 ) 69 T1_FIELD_BOOL ( "isFixedPitch", is_fixed_pitch, 0 ) 70 T1_FIELD_NUM ( "UnderlinePosition", underline_position, 0 ) 71 T1_FIELD_NUM ( "UnderlineThickness", underline_thickness, 0 ) 72 73 #undef FT_STRUCTURE 74 #define FT_STRUCTURE PS_FontExtraRec 75 #undef T1CODE 76 #define T1CODE T1_FIELD_LOCATION_FONT_EXTRA 77 78 T1_FIELD_NUM ( "FSType", fs_type, 0 ) 79 80 #undef FT_STRUCTURE 81 #define FT_STRUCTURE T1_FontRec 82 #undef T1CODE 83 #define T1CODE T1_FIELD_LOCATION_FONT_DICT 84 85 T1_FIELD_KEY ( "FontName", font_name, 0 ) 86 T1_FIELD_NUM ( "PaintType", paint_type, 0 ) 87 T1_FIELD_NUM ( "FontType", font_type, 0 ) 88 T1_FIELD_FIXED( "StrokeWidth", stroke_width, 0 ) 89 90 #undef FT_STRUCTURE 91 #define FT_STRUCTURE FT_BBox 92 #undef T1CODE 93 #define T1CODE T1_FIELD_LOCATION_BBOX 94 95 T1_FIELD_BBOX( "FontBBox", xMin, 0 ) 96 97 T1_FIELD_CALLBACK( "FontMatrix", t42_parse_font_matrix, 0 ) 98 T1_FIELD_CALLBACK( "Encoding", t42_parse_encoding, 0 ) 99 T1_FIELD_CALLBACK( "CharStrings", t42_parse_charstrings, 0 ) 100 T1_FIELD_CALLBACK( "sfnts", t42_parse_sfnts, 0 ) 101 102 { 0, T1_FIELD_LOCATION_CID_INFO, T1_FIELD_TYPE_NONE, 0, 0, 0, 0, 0, 0 } 103 }; 104 105 106 #define T1_Add_Table( p, i, o, l ) (p)->funcs.add( (p), i, o, l ) 107 #define T1_Release_Table( p ) \ 108 do \ 109 { \ 110 if ( (p)->funcs.release ) \ 111 (p)->funcs.release( p ); \ 112 } while ( 0 ) 113 114 #define T1_Skip_Spaces( p ) (p)->root.funcs.skip_spaces( &(p)->root ) 115 #define T1_Skip_PS_Token( p ) (p)->root.funcs.skip_PS_token( &(p)->root ) 116 117 #define T1_ToInt( p ) \ 118 (p)->root.funcs.to_int( &(p)->root ) 119 #define T1_ToBytes( p, b, m, n, d ) \ 120 (p)->root.funcs.to_bytes( &(p)->root, b, m, n, d ) 121 122 #define T1_ToFixedArray( p, m, f, t ) \ 123 (p)->root.funcs.to_fixed_array( &(p)->root, m, f, t ) 124 #define T1_ToToken( p, t ) \ 125 (p)->root.funcs.to_token( &(p)->root, t ) 126 127 #define T1_Load_Field( p, f, o, m, pf ) \ 128 (p)->root.funcs.load_field( &(p)->root, f, o, m, pf ) 129 #define T1_Load_Field_Table( p, f, o, m, pf ) \ 130 (p)->root.funcs.load_field_table( &(p)->root, f, o, m, pf ) 131 132 133 /********************* Parsing Functions ******************/ 134 135 FT_LOCAL_DEF( FT_Error ) t42_parser_init(T42_Parser parser,FT_Stream stream,FT_Memory memory,PSAux_Service psaux)136 t42_parser_init( T42_Parser parser, 137 FT_Stream stream, 138 FT_Memory memory, 139 PSAux_Service psaux ) 140 { 141 FT_Error error = FT_Err_Ok; 142 FT_Long size; 143 144 145 psaux->ps_parser_funcs->init( &parser->root, NULL, NULL, memory ); 146 147 parser->stream = stream; 148 parser->base_len = 0; 149 parser->base_dict = NULL; 150 parser->in_memory = 0; 151 152 /******************************************************************** 153 * 154 * Here a short summary of what is going on: 155 * 156 * When creating a new Type 42 parser, we try to locate and load 157 * the base dictionary, loading the whole font into memory. 158 * 159 * When `loading' the base dictionary, we only set up pointers 160 * in the case of a memory-based stream. Otherwise, we allocate 161 * and load the base dictionary in it. 162 * 163 * parser->in_memory is set if we have a memory stream. 164 */ 165 166 if ( FT_STREAM_SEEK( 0L ) || 167 FT_FRAME_ENTER( 17 ) ) 168 goto Exit; 169 170 if ( ft_memcmp( stream->cursor, "%!PS-TrueTypeFont", 17 ) != 0 ) 171 { 172 FT_TRACE2(( " not a Type42 font\n" )); 173 error = FT_THROW( Unknown_File_Format ); 174 } 175 176 FT_FRAME_EXIT(); 177 178 if ( error || FT_STREAM_SEEK( 0 ) ) 179 goto Exit; 180 181 size = (FT_Long)stream->size; 182 183 /* now, try to load `size' bytes of the `base' dictionary we */ 184 /* found previously */ 185 186 /* if it is a memory-based resource, set up pointers */ 187 if ( !stream->read ) 188 { 189 parser->base_dict = (FT_Byte*)stream->base + stream->pos; 190 parser->base_len = size; 191 parser->in_memory = 1; 192 193 /* check that the `size' field is valid */ 194 if ( FT_STREAM_SKIP( size ) ) 195 goto Exit; 196 } 197 else 198 { 199 /* read segment in memory */ 200 if ( FT_QALLOC( parser->base_dict, size ) || 201 FT_STREAM_READ( parser->base_dict, size ) ) 202 goto Exit; 203 204 parser->base_len = size; 205 } 206 207 parser->root.base = parser->base_dict; 208 parser->root.cursor = parser->base_dict; 209 parser->root.limit = parser->root.cursor + parser->base_len; 210 211 Exit: 212 if ( error && !parser->in_memory ) 213 FT_FREE( parser->base_dict ); 214 215 return error; 216 } 217 218 219 FT_LOCAL_DEF( void ) t42_parser_done(T42_Parser parser)220 t42_parser_done( T42_Parser parser ) 221 { 222 FT_Memory memory = parser->root.memory; 223 224 225 /* free the base dictionary only when we have a disk stream */ 226 if ( !parser->in_memory ) 227 FT_FREE( parser->base_dict ); 228 229 if ( parser->root.funcs.done ) 230 parser->root.funcs.done( &parser->root ); 231 } 232 233 234 static int t42_is_space(FT_Byte c)235 t42_is_space( FT_Byte c ) 236 { 237 return ( c == ' ' || c == '\t' || 238 c == '\r' || c == '\n' || c == '\f' || 239 c == '\0' ); 240 } 241 242 243 static void t42_parse_font_matrix(FT_Face face,void * loader_)244 t42_parse_font_matrix( FT_Face face, /* T42_Face */ 245 void* loader_ ) 246 { 247 T42_Face t42face = (T42_Face)face; 248 T42_Loader loader = (T42_Loader)loader_; 249 T42_Parser parser = &loader->parser; 250 FT_Matrix* matrix = &t42face->type1.font_matrix; 251 FT_Vector* offset = &t42face->type1.font_offset; 252 FT_Fixed temp[6]; 253 FT_Fixed temp_scale; 254 FT_Int result; 255 256 257 result = T1_ToFixedArray( parser, 6, temp, 0 ); 258 259 if ( result < 6 ) 260 { 261 parser->root.error = FT_THROW( Invalid_File_Format ); 262 return; 263 } 264 265 temp_scale = FT_ABS( temp[3] ); 266 267 if ( temp_scale == 0 ) 268 { 269 FT_ERROR(( "t42_parse_font_matrix: invalid font matrix\n" )); 270 parser->root.error = FT_THROW( Invalid_File_Format ); 271 return; 272 } 273 274 /* atypical case */ 275 if ( temp_scale != 0x10000L ) 276 { 277 temp[0] = FT_DivFix( temp[0], temp_scale ); 278 temp[1] = FT_DivFix( temp[1], temp_scale ); 279 temp[2] = FT_DivFix( temp[2], temp_scale ); 280 temp[4] = FT_DivFix( temp[4], temp_scale ); 281 temp[5] = FT_DivFix( temp[5], temp_scale ); 282 temp[3] = temp[3] < 0 ? -0x10000L : 0x10000L; 283 } 284 285 matrix->xx = temp[0]; 286 matrix->yx = temp[1]; 287 matrix->xy = temp[2]; 288 matrix->yy = temp[3]; 289 290 if ( !FT_Matrix_Check( matrix ) ) 291 { 292 FT_ERROR(( "t42_parse_font_matrix: invalid font matrix\n" )); 293 parser->root.error = FT_THROW( Invalid_File_Format ); 294 return; 295 } 296 297 /* note that the offsets must be expressed in integer font units */ 298 offset->x = temp[4] >> 16; 299 offset->y = temp[5] >> 16; 300 } 301 302 303 static void t42_parse_encoding(FT_Face face,void * loader_)304 t42_parse_encoding( FT_Face face, 305 void* loader_ ) 306 { 307 T42_Face t42face = (T42_Face)face; 308 T42_Loader loader = (T42_Loader)loader_; 309 T42_Parser parser = &loader->parser; 310 FT_Byte* cur; 311 FT_Byte* limit = parser->root.limit; 312 313 PSAux_Service psaux = (PSAux_Service)t42face->psaux; 314 315 316 T1_Skip_Spaces( parser ); 317 cur = parser->root.cursor; 318 if ( cur >= limit ) 319 { 320 FT_ERROR(( "t42_parse_encoding: out of bounds\n" )); 321 parser->root.error = FT_THROW( Invalid_File_Format ); 322 return; 323 } 324 325 /* if we have a number or `[', the encoding is an array, */ 326 /* and we must load it now */ 327 if ( ft_isdigit( *cur ) || *cur == '[' ) 328 { 329 T1_Encoding encode = &t42face->type1.encoding; 330 FT_Int count, n; 331 PS_Table char_table = &loader->encoding_table; 332 FT_Memory memory = parser->root.memory; 333 FT_Error error; 334 FT_Bool only_immediates = 0; 335 336 337 /* read the number of entries in the encoding; should be 256 */ 338 if ( *cur == '[' ) 339 { 340 count = 256; 341 only_immediates = 1; 342 parser->root.cursor++; 343 } 344 else 345 count = (FT_Int)T1_ToInt( parser ); 346 347 /* only composite fonts (which we don't support) */ 348 /* can have larger values */ 349 if ( count > 256 ) 350 { 351 FT_ERROR(( "t42_parse_encoding: invalid encoding array size\n" )); 352 parser->root.error = FT_THROW( Invalid_File_Format ); 353 return; 354 } 355 356 T1_Skip_Spaces( parser ); 357 if ( parser->root.cursor >= limit ) 358 return; 359 360 /* PostScript happily allows overwriting of encoding arrays */ 361 if ( encode->char_index ) 362 { 363 FT_FREE( encode->char_index ); 364 FT_FREE( encode->char_name ); 365 T1_Release_Table( char_table ); 366 } 367 368 /* we use a T1_Table to store our charnames */ 369 loader->num_chars = encode->num_chars = count; 370 if ( FT_QNEW_ARRAY( encode->char_index, count ) || 371 FT_QNEW_ARRAY( encode->char_name, count ) || 372 FT_SET_ERROR( psaux->ps_table_funcs->init( 373 char_table, count, memory ) ) ) 374 { 375 parser->root.error = error; 376 return; 377 } 378 379 /* We need to `zero' out encoding_table.elements */ 380 for ( n = 0; n < count; n++ ) 381 (void)T1_Add_Table( char_table, n, ".notdef", 8 ); 382 383 /* Now we need to read records of the form */ 384 /* */ 385 /* ... charcode /charname ... */ 386 /* */ 387 /* for each entry in our table. */ 388 /* */ 389 /* We simply look for a number followed by an immediate */ 390 /* name. Note that this ignores correctly the sequence */ 391 /* that is often seen in type42 fonts: */ 392 /* */ 393 /* 0 1 255 { 1 index exch /.notdef put } for dup */ 394 /* */ 395 /* used to clean the encoding array before anything else. */ 396 /* */ 397 /* Alternatively, if the array is directly given as */ 398 /* */ 399 /* /Encoding [ ... ] */ 400 /* */ 401 /* we only read immediates. */ 402 403 n = 0; 404 T1_Skip_Spaces( parser ); 405 406 while ( parser->root.cursor < limit ) 407 { 408 cur = parser->root.cursor; 409 410 /* we stop when we encounter `def' or `]' */ 411 if ( *cur == 'd' && cur + 3 < limit ) 412 { 413 if ( cur[1] == 'e' && 414 cur[2] == 'f' && 415 t42_is_space( cur[3] ) ) 416 { 417 FT_TRACE6(( "encoding end\n" )); 418 cur += 3; 419 break; 420 } 421 } 422 if ( *cur == ']' ) 423 { 424 FT_TRACE6(( "encoding end\n" )); 425 cur++; 426 break; 427 } 428 429 /* check whether we have found an entry */ 430 if ( ft_isdigit( *cur ) || only_immediates ) 431 { 432 FT_Int charcode; 433 434 435 if ( only_immediates ) 436 charcode = n; 437 else 438 { 439 charcode = (FT_Int)T1_ToInt( parser ); 440 T1_Skip_Spaces( parser ); 441 442 /* protect against invalid charcode */ 443 if ( cur == parser->root.cursor ) 444 { 445 parser->root.error = FT_THROW( Unknown_File_Format ); 446 return; 447 } 448 } 449 450 cur = parser->root.cursor; 451 452 if ( cur + 2 < limit && *cur == '/' && n < count ) 453 { 454 FT_UInt len; 455 456 457 cur++; 458 459 parser->root.cursor = cur; 460 T1_Skip_PS_Token( parser ); 461 if ( parser->root.cursor >= limit ) 462 return; 463 if ( parser->root.error ) 464 return; 465 466 len = (FT_UInt)( parser->root.cursor - cur ); 467 468 parser->root.error = T1_Add_Table( char_table, charcode, 469 cur, len + 1 ); 470 if ( parser->root.error ) 471 return; 472 char_table->elements[charcode][len] = '\0'; 473 474 n++; 475 } 476 else if ( only_immediates ) 477 { 478 /* Since the current position is not updated for */ 479 /* immediates-only mode we would get an infinite loop if */ 480 /* we don't do anything here. */ 481 /* */ 482 /* This encoding array is not valid according to the */ 483 /* type42 specification (it might be an encoding for a CID */ 484 /* type42 font, however), so we conclude that this font is */ 485 /* NOT a type42 font. */ 486 parser->root.error = FT_THROW( Unknown_File_Format ); 487 return; 488 } 489 } 490 else 491 { 492 T1_Skip_PS_Token( parser ); 493 if ( parser->root.error ) 494 return; 495 } 496 497 T1_Skip_Spaces( parser ); 498 } 499 500 t42face->type1.encoding_type = T1_ENCODING_TYPE_ARRAY; 501 parser->root.cursor = cur; 502 } 503 504 /* Otherwise, we should have either `StandardEncoding', */ 505 /* `ExpertEncoding', or `ISOLatin1Encoding' */ 506 else 507 { 508 if ( cur + 17 < limit && 509 ft_strncmp( (const char*)cur, "StandardEncoding", 16 ) == 0 ) 510 t42face->type1.encoding_type = T1_ENCODING_TYPE_STANDARD; 511 512 else if ( cur + 15 < limit && 513 ft_strncmp( (const char*)cur, "ExpertEncoding", 14 ) == 0 ) 514 t42face->type1.encoding_type = T1_ENCODING_TYPE_EXPERT; 515 516 else if ( cur + 18 < limit && 517 ft_strncmp( (const char*)cur, "ISOLatin1Encoding", 17 ) == 0 ) 518 t42face->type1.encoding_type = T1_ENCODING_TYPE_ISOLATIN1; 519 520 else 521 parser->root.error = FT_ERR( Ignore ); 522 } 523 } 524 525 526 typedef enum T42_Load_Status_ 527 { 528 BEFORE_START, 529 BEFORE_TABLE_DIR, 530 OTHER_TABLES 531 532 } T42_Load_Status; 533 534 535 static void t42_parse_sfnts(FT_Face face,void * loader_)536 t42_parse_sfnts( FT_Face face, 537 void* loader_ ) 538 { 539 T42_Face t42face = (T42_Face)face; 540 T42_Loader loader = (T42_Loader)loader_; 541 T42_Parser parser = &loader->parser; 542 FT_Memory memory = parser->root.memory; 543 FT_Byte* cur; 544 FT_Byte* limit = parser->root.limit; 545 FT_Error error; 546 FT_Int num_tables = 0; 547 FT_Long ttf_count; 548 FT_Long ttf_reserved; 549 550 FT_ULong n, string_size, old_string_size, real_size; 551 FT_Byte* string_buf = NULL; 552 FT_Bool allocated = 0; 553 554 T42_Load_Status status; 555 556 /** There should only be one sfnts array, but free any previous. */ 557 FT_FREE( t42face->ttf_data ); 558 t42face->ttf_size = 0; 559 560 /* The format is */ 561 /* */ 562 /* /sfnts [ <hexstring> <hexstring> ... ] def */ 563 /* */ 564 /* or */ 565 /* */ 566 /* /sfnts [ */ 567 /* <num_bin_bytes> RD <binary data> */ 568 /* <num_bin_bytes> RD <binary data> */ 569 /* ... */ 570 /* ] def */ 571 /* */ 572 /* with exactly one space after the `RD' token. */ 573 574 T1_Skip_Spaces( parser ); 575 576 if ( parser->root.cursor >= limit || *parser->root.cursor++ != '[' ) 577 { 578 FT_ERROR(( "t42_parse_sfnts: can't find begin of sfnts vector\n" )); 579 error = FT_THROW( Invalid_File_Format ); 580 goto Fail; 581 } 582 583 T1_Skip_Spaces( parser ); 584 status = BEFORE_START; 585 string_size = 0; 586 old_string_size = 0; 587 ttf_count = 0; 588 ttf_reserved = 12; 589 if ( FT_QALLOC( t42face->ttf_data, ttf_reserved ) ) 590 goto Fail; 591 592 FT_TRACE2(( "\n" )); 593 FT_TRACE2(( "t42_parse_sfnts:\n" )); 594 595 while ( parser->root.cursor < limit ) 596 { 597 FT_ULong size; 598 599 600 cur = parser->root.cursor; 601 602 if ( *cur == ']' ) 603 { 604 parser->root.cursor++; 605 t42face->ttf_size = ttf_count; 606 goto Exit; 607 } 608 609 else if ( *cur == '<' ) 610 { 611 if ( string_buf && !allocated ) 612 { 613 FT_ERROR(( "t42_parse_sfnts: " 614 "can't handle mixed binary and hex strings\n" )); 615 error = FT_THROW( Invalid_File_Format ); 616 goto Fail; 617 } 618 619 T1_Skip_PS_Token( parser ); 620 if ( parser->root.error ) 621 goto Exit; 622 623 /* don't include delimiters */ 624 string_size = (FT_ULong)( ( parser->root.cursor - cur - 2 + 1 ) / 2 ); 625 if ( !string_size ) 626 { 627 FT_ERROR(( "t42_parse_sfnts: invalid data in sfnts array\n" )); 628 error = FT_THROW( Invalid_File_Format ); 629 goto Fail; 630 } 631 if ( FT_QREALLOC( string_buf, old_string_size, string_size ) ) 632 goto Fail; 633 634 allocated = 1; 635 636 parser->root.cursor = cur; 637 (void)T1_ToBytes( parser, string_buf, string_size, &real_size, 1 ); 638 old_string_size = string_size; 639 string_size = real_size; 640 } 641 642 else if ( ft_isdigit( *cur ) ) 643 { 644 FT_Long tmp; 645 646 647 if ( allocated ) 648 { 649 FT_ERROR(( "t42_parse_sfnts: " 650 "can't handle mixed binary and hex strings\n" )); 651 error = FT_THROW( Invalid_File_Format ); 652 goto Fail; 653 } 654 655 tmp = T1_ToInt( parser ); 656 if ( tmp < 0 ) 657 { 658 FT_ERROR(( "t42_parse_sfnts: invalid string size\n" )); 659 error = FT_THROW( Invalid_File_Format ); 660 goto Fail; 661 } 662 else 663 string_size = (FT_ULong)tmp; 664 665 T1_Skip_PS_Token( parser ); /* `RD' */ 666 if ( parser->root.error ) 667 return; 668 669 string_buf = parser->root.cursor + 1; /* one space after `RD' */ 670 671 if ( (FT_ULong)( limit - parser->root.cursor ) <= string_size ) 672 { 673 FT_ERROR(( "t42_parse_sfnts: too much binary data\n" )); 674 error = FT_THROW( Invalid_File_Format ); 675 goto Fail; 676 } 677 else 678 parser->root.cursor += string_size + 1; 679 } 680 681 if ( !string_buf ) 682 { 683 FT_ERROR(( "t42_parse_sfnts: invalid data in sfnts array\n" )); 684 error = FT_THROW( Invalid_File_Format ); 685 goto Fail; 686 } 687 688 /* A string can have a trailing zero (odd) byte for padding. */ 689 /* Ignore it. */ 690 if ( ( string_size & 1 ) && string_buf[string_size - 1] == 0 ) 691 string_size--; 692 693 if ( !string_size ) 694 { 695 FT_ERROR(( "t42_parse_sfnts: invalid string\n" )); 696 error = FT_THROW( Invalid_File_Format ); 697 goto Fail; 698 } 699 700 FT_TRACE2(( " PS string size %5lu bytes, offset 0x%08lx (%lu)\n", 701 string_size, ttf_count, ttf_count )); 702 703 /* The whole TTF is now loaded into `string_buf'. We are */ 704 /* checking its contents while copying it to `ttf_data'. */ 705 706 size = (FT_ULong)( limit - parser->root.cursor ); 707 708 for ( n = 0; n < string_size; n++ ) 709 { 710 switch ( status ) 711 { 712 case BEFORE_START: 713 /* load offset table, 12 bytes */ 714 if ( ttf_count < 12 ) 715 { 716 t42face->ttf_data[ttf_count++] = string_buf[n]; 717 continue; 718 } 719 else 720 { 721 FT_Long ttf_reserved_prev = ttf_reserved; 722 723 724 num_tables = 16 * t42face->ttf_data[4] + t42face->ttf_data[5]; 725 status = BEFORE_TABLE_DIR; 726 ttf_reserved = 12 + 16 * num_tables; 727 728 FT_TRACE2(( " SFNT directory contains %d tables\n", 729 num_tables )); 730 731 if ( (FT_Long)size < ttf_reserved ) 732 { 733 FT_ERROR(( "t42_parse_sfnts: invalid data in sfnts array\n" )); 734 error = FT_THROW( Invalid_File_Format ); 735 goto Fail; 736 } 737 738 if ( FT_QREALLOC( t42face->ttf_data, ttf_reserved_prev, 739 ttf_reserved ) ) 740 goto Fail; 741 } 742 FALL_THROUGH; 743 744 case BEFORE_TABLE_DIR: 745 /* the offset table is read; read the table directory */ 746 if ( ttf_count < ttf_reserved ) 747 { 748 t42face->ttf_data[ttf_count++] = string_buf[n]; 749 continue; 750 } 751 else 752 { 753 int i; 754 FT_ULong len; 755 FT_Long ttf_reserved_prev = ttf_reserved; 756 757 758 FT_TRACE2(( "\n" )); 759 FT_TRACE2(( " table length\n" )); 760 FT_TRACE2(( " ------------------------------\n" )); 761 762 for ( i = 0; i < num_tables; i++ ) 763 { 764 FT_Byte* p = t42face->ttf_data + 12 + 16 * i + 12; 765 766 767 len = FT_PEEK_ULONG( p ); 768 FT_TRACE2(( " %4i 0x%08lx (%lu)\n", i, len, len )); 769 770 if ( len > size || 771 ttf_reserved > (FT_Long)( size - len ) ) 772 { 773 FT_ERROR(( "t42_parse_sfnts:" 774 " invalid data in sfnts array\n" )); 775 error = FT_THROW( Invalid_File_Format ); 776 goto Fail; 777 } 778 779 /* Pad to a 4-byte boundary length */ 780 ttf_reserved += (FT_Long)( ( len + 3 ) & ~3U ); 781 } 782 ttf_reserved += 1; 783 784 status = OTHER_TABLES; 785 786 FT_TRACE2(( "\n" )); 787 FT_TRACE2(( " allocating %ld bytes\n", ttf_reserved )); 788 FT_TRACE2(( "\n" )); 789 790 if ( FT_QREALLOC( t42face->ttf_data, ttf_reserved_prev, 791 ttf_reserved ) ) 792 goto Fail; 793 } 794 FALL_THROUGH; 795 796 case OTHER_TABLES: 797 /* all other tables are just copied */ 798 if ( ttf_count >= ttf_reserved ) 799 { 800 FT_ERROR(( "t42_parse_sfnts: too much binary data\n" )); 801 error = FT_THROW( Invalid_File_Format ); 802 goto Fail; 803 } 804 t42face->ttf_data[ttf_count++] = string_buf[n]; 805 } 806 } 807 808 T1_Skip_Spaces( parser ); 809 } 810 811 /* if control reaches this point, the format was not valid */ 812 error = FT_THROW( Invalid_File_Format ); 813 814 Fail: 815 parser->root.error = error; 816 817 Exit: 818 if ( parser->root.error ) 819 { 820 FT_FREE( t42face->ttf_data ); 821 t42face->ttf_size = 0; 822 } 823 if ( allocated ) 824 FT_FREE( string_buf ); 825 } 826 827 828 static void t42_parse_charstrings(FT_Face face,void * loader_)829 t42_parse_charstrings( FT_Face face, /* T42_Face */ 830 void* loader_ ) 831 { 832 T42_Face t42face = (T42_Face)face; 833 T42_Loader loader = (T42_Loader)loader_; 834 T42_Parser parser = &loader->parser; 835 PS_Table code_table = &loader->charstrings; 836 PS_Table name_table = &loader->glyph_names; 837 PS_Table swap_table = &loader->swap_table; 838 FT_Memory memory = parser->root.memory; 839 FT_Error error; 840 841 PSAux_Service psaux = (PSAux_Service)t42face->psaux; 842 843 FT_Byte* cur; 844 FT_Byte* limit = parser->root.limit; 845 FT_Int n; 846 FT_Int notdef_index = 0; 847 FT_Byte notdef_found = 0; 848 849 850 T1_Skip_Spaces( parser ); 851 852 if ( parser->root.cursor >= limit ) 853 { 854 FT_ERROR(( "t42_parse_charstrings: out of bounds\n" )); 855 error = FT_THROW( Invalid_File_Format ); 856 goto Fail; 857 } 858 859 if ( ft_isdigit( *parser->root.cursor ) ) 860 { 861 loader->num_glyphs = T1_ToInt( parser ); 862 if ( parser->root.error ) 863 return; 864 if ( loader->num_glyphs < 0 ) 865 { 866 FT_ERROR(( "t42_parse_encoding: invalid number of glyphs\n" )); 867 error = FT_THROW( Invalid_File_Format ); 868 goto Fail; 869 } 870 871 /* we certainly need more than 4 bytes per glyph */ 872 if ( loader->num_glyphs > ( limit - parser->root.cursor ) >> 2 ) 873 { 874 FT_TRACE0(( "t42_parse_charstrings: adjusting number of glyphs" 875 " (from %d to %zu)\n", 876 loader->num_glyphs, 877 ( limit - parser->root.cursor ) >> 2 )); 878 loader->num_glyphs = ( limit - parser->root.cursor ) >> 2; 879 } 880 881 } 882 else if ( *parser->root.cursor == '<' ) 883 { 884 /* We have `<< ... >>'. Count the number of `/' in the dictionary */ 885 /* to get its size. */ 886 FT_Int count = 0; 887 888 889 T1_Skip_PS_Token( parser ); 890 if ( parser->root.error ) 891 return; 892 T1_Skip_Spaces( parser ); 893 cur = parser->root.cursor; 894 895 while ( parser->root.cursor < limit ) 896 { 897 if ( *parser->root.cursor == '/' ) 898 count++; 899 else if ( *parser->root.cursor == '>' ) 900 { 901 loader->num_glyphs = count; 902 parser->root.cursor = cur; /* rewind */ 903 break; 904 } 905 T1_Skip_PS_Token( parser ); 906 if ( parser->root.error ) 907 return; 908 T1_Skip_Spaces( parser ); 909 } 910 } 911 else 912 { 913 FT_ERROR(( "t42_parse_charstrings: invalid token\n" )); 914 error = FT_THROW( Invalid_File_Format ); 915 goto Fail; 916 } 917 918 if ( parser->root.cursor >= limit ) 919 { 920 FT_ERROR(( "t42_parse_charstrings: out of bounds\n" )); 921 error = FT_THROW( Invalid_File_Format ); 922 goto Fail; 923 } 924 925 /* initialize tables */ 926 927 /* contrary to Type1, we disallow multiple CharStrings arrays */ 928 if ( swap_table->init ) 929 { 930 FT_ERROR(( "t42_parse_charstrings:" 931 " only one CharStrings array allowed\n" )); 932 error = FT_THROW( Invalid_File_Format ); 933 goto Fail; 934 } 935 936 error = psaux->ps_table_funcs->init( code_table, 937 loader->num_glyphs, 938 memory ); 939 if ( error ) 940 goto Fail; 941 942 error = psaux->ps_table_funcs->init( name_table, 943 loader->num_glyphs, 944 memory ); 945 if ( error ) 946 goto Fail; 947 948 /* Initialize table for swapping index notdef_index and */ 949 /* index 0 names and codes (if necessary). */ 950 951 error = psaux->ps_table_funcs->init( swap_table, 4, memory ); 952 if ( error ) 953 goto Fail; 954 955 n = 0; 956 957 for (;;) 958 { 959 /* We support two formats. */ 960 /* */ 961 /* `/glyphname' + index [+ `def'] */ 962 /* `(glyphname)' [+ `cvn'] + index [+ `def'] */ 963 /* */ 964 /* The latter format gets created by the */ 965 /* LilyPond typesetting program. */ 966 967 T1_Skip_Spaces( parser ); 968 969 cur = parser->root.cursor; 970 if ( cur >= limit ) 971 break; 972 973 /* We stop when we find an `end' keyword or '>' */ 974 if ( *cur == 'e' && 975 cur + 3 < limit && 976 cur[1] == 'n' && 977 cur[2] == 'd' && 978 t42_is_space( cur[3] ) ) 979 break; 980 if ( *cur == '>' ) 981 break; 982 983 T1_Skip_PS_Token( parser ); 984 if ( parser->root.cursor >= limit ) 985 { 986 FT_ERROR(( "t42_parse_charstrings: out of bounds\n" )); 987 error = FT_THROW( Invalid_File_Format ); 988 goto Fail; 989 } 990 if ( parser->root.error ) 991 return; 992 993 if ( *cur == '/' || *cur == '(' ) 994 { 995 FT_UInt len; 996 FT_Bool have_literal = FT_BOOL( *cur == '(' ); 997 998 999 if ( cur + ( have_literal ? 3 : 2 ) >= limit ) 1000 { 1001 FT_ERROR(( "t42_parse_charstrings: out of bounds\n" )); 1002 error = FT_THROW( Invalid_File_Format ); 1003 goto Fail; 1004 } 1005 1006 cur++; /* skip `/' */ 1007 len = (FT_UInt)( parser->root.cursor - cur ); 1008 if ( have_literal ) 1009 len--; 1010 1011 error = T1_Add_Table( name_table, n, cur, len + 1 ); 1012 if ( error ) 1013 goto Fail; 1014 1015 /* add a trailing zero to the name table */ 1016 name_table->elements[n][len] = '\0'; 1017 1018 /* record index of /.notdef */ 1019 if ( *cur == '.' && 1020 ft_strcmp( ".notdef", 1021 (const char*)( name_table->elements[n] ) ) == 0 ) 1022 { 1023 notdef_index = n; 1024 notdef_found = 1; 1025 } 1026 1027 T1_Skip_Spaces( parser ); 1028 1029 if ( have_literal ) 1030 T1_Skip_PS_Token( parser ); 1031 1032 cur = parser->root.cursor; 1033 1034 (void)T1_ToInt( parser ); 1035 if ( parser->root.cursor >= limit ) 1036 { 1037 FT_ERROR(( "t42_parse_charstrings: out of bounds\n" )); 1038 error = FT_THROW( Invalid_File_Format ); 1039 goto Fail; 1040 } 1041 1042 len = (FT_UInt)( parser->root.cursor - cur ); 1043 1044 error = T1_Add_Table( code_table, n, cur, len + 1 ); 1045 if ( error ) 1046 goto Fail; 1047 1048 code_table->elements[n][len] = '\0'; 1049 1050 n++; 1051 if ( n >= loader->num_glyphs ) 1052 break; 1053 } 1054 } 1055 1056 loader->num_glyphs = n; 1057 1058 if ( !notdef_found ) 1059 { 1060 FT_ERROR(( "t42_parse_charstrings: no /.notdef glyph\n" )); 1061 error = FT_THROW( Invalid_File_Format ); 1062 goto Fail; 1063 } 1064 1065 /* if /.notdef does not occupy index 0, do our magic. */ 1066 if ( ft_strcmp( ".notdef", (const char*)name_table->elements[0] ) ) 1067 { 1068 /* Swap glyph in index 0 with /.notdef glyph. First, add index 0 */ 1069 /* name and code entries to swap_table. Then place notdef_index */ 1070 /* name and code entries into swap_table. Then swap name and code */ 1071 /* entries at indices notdef_index and 0 using values stored in */ 1072 /* swap_table. */ 1073 1074 /* Index 0 name */ 1075 error = T1_Add_Table( swap_table, 0, 1076 name_table->elements[0], 1077 name_table->lengths [0] ); 1078 if ( error ) 1079 goto Fail; 1080 1081 /* Index 0 code */ 1082 error = T1_Add_Table( swap_table, 1, 1083 code_table->elements[0], 1084 code_table->lengths [0] ); 1085 if ( error ) 1086 goto Fail; 1087 1088 /* Index notdef_index name */ 1089 error = T1_Add_Table( swap_table, 2, 1090 name_table->elements[notdef_index], 1091 name_table->lengths [notdef_index] ); 1092 if ( error ) 1093 goto Fail; 1094 1095 /* Index notdef_index code */ 1096 error = T1_Add_Table( swap_table, 3, 1097 code_table->elements[notdef_index], 1098 code_table->lengths [notdef_index] ); 1099 if ( error ) 1100 goto Fail; 1101 1102 error = T1_Add_Table( name_table, notdef_index, 1103 swap_table->elements[0], 1104 swap_table->lengths [0] ); 1105 if ( error ) 1106 goto Fail; 1107 1108 error = T1_Add_Table( code_table, notdef_index, 1109 swap_table->elements[1], 1110 swap_table->lengths [1] ); 1111 if ( error ) 1112 goto Fail; 1113 1114 error = T1_Add_Table( name_table, 0, 1115 swap_table->elements[2], 1116 swap_table->lengths [2] ); 1117 if ( error ) 1118 goto Fail; 1119 1120 error = T1_Add_Table( code_table, 0, 1121 swap_table->elements[3], 1122 swap_table->lengths [3] ); 1123 if ( error ) 1124 goto Fail; 1125 1126 } 1127 1128 return; 1129 1130 Fail: 1131 parser->root.error = error; 1132 } 1133 1134 1135 static FT_Error t42_load_keyword(T42_Face face,T42_Loader loader,T1_Field field)1136 t42_load_keyword( T42_Face face, 1137 T42_Loader loader, 1138 T1_Field field ) 1139 { 1140 FT_Error error; 1141 void* dummy_object; 1142 void** objects; 1143 FT_UInt max_objects = 0; 1144 1145 1146 /* if the keyword has a dedicated callback, call it */ 1147 if ( field->type == T1_FIELD_TYPE_CALLBACK ) 1148 { 1149 field->reader( (FT_Face)face, loader ); 1150 error = loader->parser.root.error; 1151 goto Exit; 1152 } 1153 1154 /* now the keyword is either a simple field or a table of fields; */ 1155 /* we are now going to take care of it */ 1156 1157 switch ( field->location ) 1158 { 1159 case T1_FIELD_LOCATION_FONT_INFO: 1160 dummy_object = &face->type1.font_info; 1161 break; 1162 1163 case T1_FIELD_LOCATION_FONT_EXTRA: 1164 dummy_object = &face->type1.font_extra; 1165 break; 1166 1167 case T1_FIELD_LOCATION_BBOX: 1168 dummy_object = &face->type1.font_bbox; 1169 break; 1170 1171 default: 1172 dummy_object = &face->type1; 1173 } 1174 1175 objects = &dummy_object; 1176 1177 if ( field->type == T1_FIELD_TYPE_INTEGER_ARRAY || 1178 field->type == T1_FIELD_TYPE_FIXED_ARRAY ) 1179 error = T1_Load_Field_Table( &loader->parser, field, 1180 objects, max_objects, 0 ); 1181 else 1182 error = T1_Load_Field( &loader->parser, field, 1183 objects, max_objects, 0 ); 1184 1185 Exit: 1186 return error; 1187 } 1188 1189 1190 FT_LOCAL_DEF( FT_Error ) t42_parse_dict(T42_Face face,T42_Loader loader,FT_Byte * base,FT_Long size)1191 t42_parse_dict( T42_Face face, 1192 T42_Loader loader, 1193 FT_Byte* base, 1194 FT_Long size ) 1195 { 1196 T42_Parser parser = &loader->parser; 1197 FT_Byte* limit; 1198 FT_Int n_keywords = (FT_Int)( sizeof ( t42_keywords ) / 1199 sizeof ( t42_keywords[0] ) ); 1200 1201 1202 parser->root.cursor = base; 1203 parser->root.limit = base + size; 1204 parser->root.error = FT_Err_Ok; 1205 1206 limit = parser->root.limit; 1207 1208 T1_Skip_Spaces( parser ); 1209 1210 while ( parser->root.cursor < limit ) 1211 { 1212 FT_Byte* cur; 1213 1214 1215 cur = parser->root.cursor; 1216 1217 /* look for `FontDirectory' which causes problems for some fonts */ 1218 if ( *cur == 'F' && cur + 25 < limit && 1219 ft_strncmp( (char*)cur, "FontDirectory", 13 ) == 0 ) 1220 { 1221 FT_Byte* cur2; 1222 1223 1224 /* skip the `FontDirectory' keyword */ 1225 T1_Skip_PS_Token( parser ); 1226 T1_Skip_Spaces ( parser ); 1227 cur = cur2 = parser->root.cursor; 1228 1229 /* look up the `known' keyword */ 1230 while ( cur < limit ) 1231 { 1232 if ( *cur == 'k' && cur + 5 < limit && 1233 ft_strncmp( (char*)cur, "known", 5 ) == 0 ) 1234 break; 1235 1236 T1_Skip_PS_Token( parser ); 1237 if ( parser->root.error ) 1238 goto Exit; 1239 T1_Skip_Spaces ( parser ); 1240 cur = parser->root.cursor; 1241 } 1242 1243 if ( cur < limit ) 1244 { 1245 T1_TokenRec token; 1246 1247 1248 /* skip the `known' keyword and the token following it */ 1249 T1_Skip_PS_Token( parser ); 1250 T1_ToToken( parser, &token ); 1251 1252 /* if the last token was an array, skip it! */ 1253 if ( token.type == T1_TOKEN_TYPE_ARRAY ) 1254 cur2 = parser->root.cursor; 1255 } 1256 parser->root.cursor = cur2; 1257 } 1258 1259 /* look for immediates */ 1260 else if ( *cur == '/' && cur + 2 < limit ) 1261 { 1262 FT_UInt len; 1263 1264 1265 cur++; 1266 1267 parser->root.cursor = cur; 1268 T1_Skip_PS_Token( parser ); 1269 if ( parser->root.error ) 1270 goto Exit; 1271 1272 len = (FT_UInt)( parser->root.cursor - cur ); 1273 1274 if ( len > 0 && len < 22 && parser->root.cursor < limit ) 1275 { 1276 int i; 1277 1278 1279 /* now compare the immediate name to the keyword table */ 1280 1281 /* loop through all known keywords */ 1282 for ( i = 0; i < n_keywords; i++ ) 1283 { 1284 T1_Field keyword = (T1_Field)&t42_keywords[i]; 1285 FT_Byte *name = (FT_Byte*)keyword->ident; 1286 1287 1288 if ( !name ) 1289 continue; 1290 1291 if ( cur[0] == name[0] && 1292 len == ft_strlen( (const char *)name ) && 1293 ft_memcmp( cur, name, len ) == 0 ) 1294 { 1295 /* we found it -- run the parsing callback! */ 1296 parser->root.error = t42_load_keyword( face, 1297 loader, 1298 keyword ); 1299 if ( parser->root.error ) 1300 return parser->root.error; 1301 break; 1302 } 1303 } 1304 } 1305 } 1306 else 1307 { 1308 T1_Skip_PS_Token( parser ); 1309 if ( parser->root.error ) 1310 goto Exit; 1311 } 1312 1313 T1_Skip_Spaces( parser ); 1314 } 1315 1316 Exit: 1317 return parser->root.error; 1318 } 1319 1320 1321 FT_LOCAL_DEF( void ) t42_loader_init(T42_Loader loader,T42_Face face)1322 t42_loader_init( T42_Loader loader, 1323 T42_Face face ) 1324 { 1325 FT_UNUSED( face ); 1326 1327 FT_ZERO( loader ); 1328 loader->num_glyphs = 0; 1329 loader->num_chars = 0; 1330 1331 /* initialize the tables -- simply set their `init' field to 0 */ 1332 loader->encoding_table.init = 0; 1333 loader->charstrings.init = 0; 1334 loader->glyph_names.init = 0; 1335 } 1336 1337 1338 FT_LOCAL_DEF( void ) t42_loader_done(T42_Loader loader)1339 t42_loader_done( T42_Loader loader ) 1340 { 1341 T42_Parser parser = &loader->parser; 1342 1343 1344 /* finalize tables */ 1345 T1_Release_Table( &loader->encoding_table ); 1346 T1_Release_Table( &loader->charstrings ); 1347 T1_Release_Table( &loader->glyph_names ); 1348 T1_Release_Table( &loader->swap_table ); 1349 1350 /* finalize parser */ 1351 t42_parser_done( parser ); 1352 } 1353 1354 1355 /* END */ 1356