1 /***************************************************************************/ 2 /* */ 3 /* ttload.c */ 4 /* */ 5 /* Load the basic TrueType tables, i.e., tables that can be either in */ 6 /* TTF or OTF fonts (body). */ 7 /* */ 8 /* Copyright 1996-2018 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 #include <ft2build.h> 21 #include FT_INTERNAL_DEBUG_H 22 #include FT_INTERNAL_STREAM_H 23 #include FT_TRUETYPE_TAGS_H 24 #include "ttload.h" 25 26 #include "sferrors.h" 27 28 29 /*************************************************************************/ 30 /* */ 31 /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ 32 /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ 33 /* messages during execution. */ 34 /* */ 35 #undef FT_COMPONENT 36 #define FT_COMPONENT trace_ttload 37 38 39 /*************************************************************************/ 40 /* */ 41 /* <Function> */ 42 /* tt_face_lookup_table */ 43 /* */ 44 /* <Description> */ 45 /* Looks for a TrueType table by name. */ 46 /* */ 47 /* <Input> */ 48 /* face :: A face object handle. */ 49 /* */ 50 /* tag :: The searched tag. */ 51 /* */ 52 /* <Return> */ 53 /* A pointer to the table directory entry. 0 if not found. */ 54 /* */ 55 FT_LOCAL_DEF( TT_Table ) tt_face_lookup_table(TT_Face face,FT_ULong tag)56 tt_face_lookup_table( TT_Face face, 57 FT_ULong tag ) 58 { 59 TT_Table entry; 60 TT_Table limit; 61 #ifdef FT_DEBUG_LEVEL_TRACE 62 FT_Bool zero_length = FALSE; 63 #endif 64 65 66 FT_TRACE4(( "tt_face_lookup_table: %08p, `%c%c%c%c' -- ", 67 face, 68 (FT_Char)( tag >> 24 ), 69 (FT_Char)( tag >> 16 ), 70 (FT_Char)( tag >> 8 ), 71 (FT_Char)( tag ) )); 72 73 entry = face->dir_tables; 74 limit = entry + face->num_tables; 75 76 for ( ; entry < limit; entry++ ) 77 { 78 /* For compatibility with Windows, we consider */ 79 /* zero-length tables the same as missing tables. */ 80 if ( entry->Tag == tag ) 81 { 82 if ( entry->Length != 0 ) 83 { 84 FT_TRACE4(( "found table.\n" )); 85 return entry; 86 } 87 #ifdef FT_DEBUG_LEVEL_TRACE 88 zero_length = TRUE; 89 #endif 90 } 91 } 92 93 #ifdef FT_DEBUG_LEVEL_TRACE 94 if ( zero_length ) 95 FT_TRACE4(( "ignoring empty table\n" )); 96 else 97 FT_TRACE4(( "could not find table\n" )); 98 #endif 99 100 return NULL; 101 } 102 103 104 /*************************************************************************/ 105 /* */ 106 /* <Function> */ 107 /* tt_face_goto_table */ 108 /* */ 109 /* <Description> */ 110 /* Looks for a TrueType table by name, then seek a stream to it. */ 111 /* */ 112 /* <Input> */ 113 /* face :: A face object handle. */ 114 /* */ 115 /* tag :: The searched tag. */ 116 /* */ 117 /* stream :: The stream to seek when the table is found. */ 118 /* */ 119 /* <Output> */ 120 /* length :: The length of the table if found, undefined otherwise. */ 121 /* */ 122 /* <Return> */ 123 /* FreeType error code. 0 means success. */ 124 /* */ 125 FT_LOCAL_DEF( FT_Error ) tt_face_goto_table(TT_Face face,FT_ULong tag,FT_Stream stream,FT_ULong * length)126 tt_face_goto_table( TT_Face face, 127 FT_ULong tag, 128 FT_Stream stream, 129 FT_ULong* length ) 130 { 131 TT_Table table; 132 FT_Error error; 133 134 135 table = tt_face_lookup_table( face, tag ); 136 if ( table ) 137 { 138 if ( length ) 139 *length = table->Length; 140 141 if ( FT_STREAM_SEEK( table->Offset ) ) 142 goto Exit; 143 } 144 else 145 error = FT_THROW( Table_Missing ); 146 147 Exit: 148 return error; 149 } 150 151 152 /* Here, we */ 153 /* */ 154 /* - check that `num_tables' is valid (and adjust it if necessary); */ 155 /* also return the number of valid table entries */ 156 /* */ 157 /* - look for a `head' table, check its size, and parse it to check */ 158 /* whether its `magic' field is correctly set */ 159 /* */ 160 /* - errors (except errors returned by stream handling) */ 161 /* */ 162 /* SFNT_Err_Unknown_File_Format: */ 163 /* no table is defined in directory, it is not sfnt-wrapped */ 164 /* data */ 165 /* SFNT_Err_Table_Missing: */ 166 /* table directory is valid, but essential tables */ 167 /* (head/bhed/SING) are missing */ 168 /* */ 169 static FT_Error check_table_dir(SFNT_Header sfnt,FT_Stream stream,FT_UShort * valid)170 check_table_dir( SFNT_Header sfnt, 171 FT_Stream stream, 172 FT_UShort* valid ) 173 { 174 FT_Error error; 175 FT_UShort nn, valid_entries = 0; 176 FT_UInt has_head = 0, has_sing = 0, has_meta = 0; 177 FT_ULong offset = sfnt->offset + 12; 178 179 static const FT_Frame_Field table_dir_entry_fields[] = 180 { 181 #undef FT_STRUCTURE 182 #define FT_STRUCTURE TT_TableRec 183 184 FT_FRAME_START( 16 ), 185 FT_FRAME_ULONG( Tag ), 186 FT_FRAME_ULONG( CheckSum ), 187 FT_FRAME_ULONG( Offset ), 188 FT_FRAME_ULONG( Length ), 189 FT_FRAME_END 190 }; 191 192 193 if ( FT_STREAM_SEEK( offset ) ) 194 goto Exit; 195 196 for ( nn = 0; nn < sfnt->num_tables; nn++ ) 197 { 198 TT_TableRec table; 199 200 201 if ( FT_STREAM_READ_FIELDS( table_dir_entry_fields, &table ) ) 202 { 203 nn--; 204 FT_TRACE2(( "check_table_dir:" 205 " can read only %d table%s in font (instead of %d)\n", 206 nn, nn == 1 ? "" : "s", sfnt->num_tables )); 207 sfnt->num_tables = nn; 208 break; 209 } 210 211 /* we ignore invalid tables */ 212 213 if ( table.Offset > stream->size ) 214 { 215 FT_TRACE2(( "check_table_dir: table entry %d invalid\n", nn )); 216 continue; 217 } 218 else if ( table.Length > stream->size - table.Offset ) 219 { 220 /* Some tables have such a simple structure that clipping its */ 221 /* contents is harmless. This also makes FreeType less sensitive */ 222 /* to invalid table lengths (which programs like Acroread seem to */ 223 /* ignore in general). */ 224 225 if ( table.Tag == TTAG_hmtx || 226 table.Tag == TTAG_vmtx ) 227 valid_entries++; 228 else 229 { 230 FT_TRACE2(( "check_table_dir: table entry %d invalid\n", nn )); 231 continue; 232 } 233 } 234 else 235 valid_entries++; 236 237 if ( table.Tag == TTAG_head || table.Tag == TTAG_bhed ) 238 { 239 FT_UInt32 magic; 240 241 242 #ifndef TT_CONFIG_OPTION_EMBEDDED_BITMAPS 243 if ( table.Tag == TTAG_head ) 244 #endif 245 has_head = 1; 246 247 /* 248 * The table length should be 0x36, but certain font tools make it 249 * 0x38, so we will just check that it is greater. 250 * 251 * Note that according to the specification, the table must be 252 * padded to 32-bit lengths, but this doesn't apply to the value of 253 * its `Length' field! 254 * 255 */ 256 if ( table.Length < 0x36 ) 257 { 258 FT_TRACE2(( "check_table_dir:" 259 " `head' or `bhed' table too small\n" )); 260 error = FT_THROW( Table_Missing ); 261 goto Exit; 262 } 263 264 if ( FT_STREAM_SEEK( table.Offset + 12 ) || 265 FT_READ_ULONG( magic ) ) 266 goto Exit; 267 268 if ( magic != 0x5F0F3CF5UL ) 269 FT_TRACE2(( "check_table_dir:" 270 " invalid magic number in `head' or `bhed' table\n")); 271 272 if ( FT_STREAM_SEEK( offset + ( nn + 1 ) * 16 ) ) 273 goto Exit; 274 } 275 else if ( table.Tag == TTAG_SING ) 276 has_sing = 1; 277 else if ( table.Tag == TTAG_META ) 278 has_meta = 1; 279 } 280 281 *valid = valid_entries; 282 283 if ( !valid_entries ) 284 { 285 FT_TRACE2(( "check_table_dir: no valid tables found\n" )); 286 error = FT_THROW( Unknown_File_Format ); 287 goto Exit; 288 } 289 290 /* if `sing' and `meta' tables are present, there is no `head' table */ 291 if ( has_head || ( has_sing && has_meta ) ) 292 { 293 error = FT_Err_Ok; 294 goto Exit; 295 } 296 else 297 { 298 FT_TRACE2(( "check_table_dir:" )); 299 #ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS 300 FT_TRACE2(( " neither `head', `bhed', nor `sing' table found\n" )); 301 #else 302 FT_TRACE2(( " neither `head' nor `sing' table found\n" )); 303 #endif 304 error = FT_THROW( Table_Missing ); 305 } 306 307 Exit: 308 return error; 309 } 310 311 312 /*************************************************************************/ 313 /* */ 314 /* <Function> */ 315 /* tt_face_load_font_dir */ 316 /* */ 317 /* <Description> */ 318 /* Loads the header of a SFNT font file. */ 319 /* */ 320 /* <Input> */ 321 /* face :: A handle to the target face object. */ 322 /* */ 323 /* stream :: The input stream. */ 324 /* */ 325 /* <Output> */ 326 /* sfnt :: The SFNT header. */ 327 /* */ 328 /* <Return> */ 329 /* FreeType error code. 0 means success. */ 330 /* */ 331 /* <Note> */ 332 /* The stream cursor must be at the beginning of the font directory. */ 333 /* */ 334 FT_LOCAL_DEF( FT_Error ) tt_face_load_font_dir(TT_Face face,FT_Stream stream)335 tt_face_load_font_dir( TT_Face face, 336 FT_Stream stream ) 337 { 338 SFNT_HeaderRec sfnt; 339 FT_Error error; 340 FT_Memory memory = stream->memory; 341 FT_UShort nn, valid_entries = 0; 342 343 static const FT_Frame_Field offset_table_fields[] = 344 { 345 #undef FT_STRUCTURE 346 #define FT_STRUCTURE SFNT_HeaderRec 347 348 FT_FRAME_START( 8 ), 349 FT_FRAME_USHORT( num_tables ), 350 FT_FRAME_USHORT( search_range ), 351 FT_FRAME_USHORT( entry_selector ), 352 FT_FRAME_USHORT( range_shift ), 353 FT_FRAME_END 354 }; 355 356 357 FT_TRACE2(( "tt_face_load_font_dir: %08p\n", face )); 358 359 /* read the offset table */ 360 361 sfnt.offset = FT_STREAM_POS(); 362 363 if ( FT_READ_ULONG( sfnt.format_tag ) || 364 FT_STREAM_READ_FIELDS( offset_table_fields, &sfnt ) ) 365 goto Exit; 366 367 /* many fonts don't have these fields set correctly */ 368 #if 0 369 if ( sfnt.search_range != 1 << ( sfnt.entry_selector + 4 ) || 370 sfnt.search_range + sfnt.range_shift != sfnt.num_tables << 4 ) 371 return FT_THROW( Unknown_File_Format ); 372 #endif 373 374 /* load the table directory */ 375 376 FT_TRACE2(( "-- Number of tables: %10u\n", sfnt.num_tables )); 377 FT_TRACE2(( "-- Format version: 0x%08lx\n", sfnt.format_tag )); 378 379 if ( sfnt.format_tag != TTAG_OTTO ) 380 { 381 /* check first */ 382 error = check_table_dir( &sfnt, stream, &valid_entries ); 383 if ( error ) 384 { 385 FT_TRACE2(( "tt_face_load_font_dir:" 386 " invalid table directory for TrueType\n" )); 387 goto Exit; 388 } 389 } 390 else 391 valid_entries = sfnt.num_tables; 392 393 face->num_tables = valid_entries; 394 face->format_tag = sfnt.format_tag; 395 396 if ( FT_QNEW_ARRAY( face->dir_tables, face->num_tables ) ) 397 goto Exit; 398 399 if ( FT_STREAM_SEEK( sfnt.offset + 12 ) || 400 FT_FRAME_ENTER( sfnt.num_tables * 16L ) ) 401 goto Exit; 402 403 FT_TRACE2(( "\n" 404 " tag offset length checksum\n" 405 " ----------------------------------\n" )); 406 407 valid_entries = 0; 408 for ( nn = 0; nn < sfnt.num_tables; nn++ ) 409 { 410 TT_TableRec entry; 411 FT_UShort i; 412 FT_Bool duplicate; 413 414 415 entry.Tag = FT_GET_TAG4(); 416 entry.CheckSum = FT_GET_ULONG(); 417 entry.Offset = FT_GET_ULONG(); 418 entry.Length = FT_GET_ULONG(); 419 420 /* ignore invalid tables that can't be sanitized */ 421 422 if ( entry.Offset > stream->size ) 423 continue; 424 else if ( entry.Length > stream->size - entry.Offset ) 425 { 426 if ( entry.Tag == TTAG_hmtx || 427 entry.Tag == TTAG_vmtx ) 428 { 429 #ifdef FT_DEBUG_LEVEL_TRACE 430 FT_ULong old_length = entry.Length; 431 #endif 432 433 434 /* make metrics table length a multiple of 4 */ 435 entry.Length = ( stream->size - entry.Offset ) & ~3U; 436 437 FT_TRACE2(( " %c%c%c%c %08lx %08lx %08lx" 438 " (sanitized; original length %08lx)", 439 (FT_Char)( entry.Tag >> 24 ), 440 (FT_Char)( entry.Tag >> 16 ), 441 (FT_Char)( entry.Tag >> 8 ), 442 (FT_Char)( entry.Tag ), 443 entry.Offset, 444 entry.Length, 445 entry.CheckSum, 446 old_length )); 447 } 448 else 449 continue; 450 } 451 #ifdef FT_DEBUG_LEVEL_TRACE 452 else 453 FT_TRACE2(( " %c%c%c%c %08lx %08lx %08lx", 454 (FT_Char)( entry.Tag >> 24 ), 455 (FT_Char)( entry.Tag >> 16 ), 456 (FT_Char)( entry.Tag >> 8 ), 457 (FT_Char)( entry.Tag ), 458 entry.Offset, 459 entry.Length, 460 entry.CheckSum )); 461 #endif 462 463 /* ignore duplicate tables – the first one wins */ 464 duplicate = 0; 465 for ( i = 0; i < valid_entries; i++ ) 466 { 467 if ( face->dir_tables[i].Tag == entry.Tag ) 468 { 469 duplicate = 1; 470 break; 471 } 472 } 473 if ( duplicate ) 474 { 475 FT_TRACE2(( " (duplicate, ignored)\n" )); 476 continue; 477 } 478 else 479 { 480 FT_TRACE2(( "\n" )); 481 482 /* we finally have a valid entry */ 483 face->dir_tables[valid_entries++] = entry; 484 } 485 } 486 487 /* final adjustment to number of tables */ 488 face->num_tables = valid_entries; 489 490 FT_FRAME_EXIT(); 491 492 FT_TRACE2(( "table directory loaded\n\n" )); 493 494 Exit: 495 return error; 496 } 497 498 499 /*************************************************************************/ 500 /* */ 501 /* <Function> */ 502 /* tt_face_load_any */ 503 /* */ 504 /* <Description> */ 505 /* Loads any font table into client memory. */ 506 /* */ 507 /* <Input> */ 508 /* face :: The face object to look for. */ 509 /* */ 510 /* tag :: The tag of table to load. Use the value 0 if you want */ 511 /* to access the whole font file, else set this parameter */ 512 /* to a valid TrueType table tag that you can forge with */ 513 /* the MAKE_TT_TAG macro. */ 514 /* */ 515 /* offset :: The starting offset in the table (or the file if */ 516 /* tag == 0). */ 517 /* */ 518 /* length :: The address of the decision variable: */ 519 /* */ 520 /* If length == NULL: */ 521 /* Loads the whole table. Returns an error if */ 522 /* `offset' == 0! */ 523 /* */ 524 /* If *length == 0: */ 525 /* Exits immediately; returning the length of the given */ 526 /* table or of the font file, depending on the value of */ 527 /* `tag'. */ 528 /* */ 529 /* If *length != 0: */ 530 /* Loads the next `length' bytes of table or font, */ 531 /* starting at offset `offset' (in table or font too). */ 532 /* */ 533 /* <Output> */ 534 /* buffer :: The address of target buffer. */ 535 /* */ 536 /* <Return> */ 537 /* FreeType error code. 0 means success. */ 538 /* */ 539 FT_LOCAL_DEF( FT_Error ) tt_face_load_any(TT_Face face,FT_ULong tag,FT_Long offset,FT_Byte * buffer,FT_ULong * length)540 tt_face_load_any( TT_Face face, 541 FT_ULong tag, 542 FT_Long offset, 543 FT_Byte* buffer, 544 FT_ULong* length ) 545 { 546 FT_Error error; 547 FT_Stream stream; 548 TT_Table table; 549 FT_ULong size; 550 551 552 if ( tag != 0 ) 553 { 554 /* look for tag in font directory */ 555 table = tt_face_lookup_table( face, tag ); 556 if ( !table ) 557 { 558 error = FT_THROW( Table_Missing ); 559 goto Exit; 560 } 561 562 offset += table->Offset; 563 size = table->Length; 564 } 565 else 566 /* tag == 0 -- the user wants to access the font file directly */ 567 size = face->root.stream->size; 568 569 if ( length && *length == 0 ) 570 { 571 *length = size; 572 573 return FT_Err_Ok; 574 } 575 576 if ( length ) 577 size = *length; 578 579 stream = face->root.stream; 580 /* the `if' is syntactic sugar for picky compilers */ 581 if ( FT_STREAM_READ_AT( offset, buffer, size ) ) 582 goto Exit; 583 584 Exit: 585 return error; 586 } 587 588 589 /*************************************************************************/ 590 /* */ 591 /* <Function> */ 592 /* tt_face_load_generic_header */ 593 /* */ 594 /* <Description> */ 595 /* Loads the TrueType table `head' or `bhed'. */ 596 /* */ 597 /* <Input> */ 598 /* face :: A handle to the target face object. */ 599 /* */ 600 /* stream :: The input stream. */ 601 /* */ 602 /* <Return> */ 603 /* FreeType error code. 0 means success. */ 604 /* */ 605 static FT_Error tt_face_load_generic_header(TT_Face face,FT_Stream stream,FT_ULong tag)606 tt_face_load_generic_header( TT_Face face, 607 FT_Stream stream, 608 FT_ULong tag ) 609 { 610 FT_Error error; 611 TT_Header* header; 612 613 static const FT_Frame_Field header_fields[] = 614 { 615 #undef FT_STRUCTURE 616 #define FT_STRUCTURE TT_Header 617 618 FT_FRAME_START( 54 ), 619 FT_FRAME_ULONG ( Table_Version ), 620 FT_FRAME_ULONG ( Font_Revision ), 621 FT_FRAME_LONG ( CheckSum_Adjust ), 622 FT_FRAME_LONG ( Magic_Number ), 623 FT_FRAME_USHORT( Flags ), 624 FT_FRAME_USHORT( Units_Per_EM ), 625 FT_FRAME_LONG ( Created[0] ), 626 FT_FRAME_LONG ( Created[1] ), 627 FT_FRAME_LONG ( Modified[0] ), 628 FT_FRAME_LONG ( Modified[1] ), 629 FT_FRAME_SHORT ( xMin ), 630 FT_FRAME_SHORT ( yMin ), 631 FT_FRAME_SHORT ( xMax ), 632 FT_FRAME_SHORT ( yMax ), 633 FT_FRAME_USHORT( Mac_Style ), 634 FT_FRAME_USHORT( Lowest_Rec_PPEM ), 635 FT_FRAME_SHORT ( Font_Direction ), 636 FT_FRAME_SHORT ( Index_To_Loc_Format ), 637 FT_FRAME_SHORT ( Glyph_Data_Format ), 638 FT_FRAME_END 639 }; 640 641 642 error = face->goto_table( face, tag, stream, 0 ); 643 if ( error ) 644 goto Exit; 645 646 header = &face->header; 647 648 if ( FT_STREAM_READ_FIELDS( header_fields, header ) ) 649 goto Exit; 650 651 FT_TRACE3(( "Units per EM: %4u\n", header->Units_Per_EM )); 652 FT_TRACE3(( "IndexToLoc: %4d\n", header->Index_To_Loc_Format )); 653 654 Exit: 655 return error; 656 } 657 658 659 FT_LOCAL_DEF( FT_Error ) tt_face_load_head(TT_Face face,FT_Stream stream)660 tt_face_load_head( TT_Face face, 661 FT_Stream stream ) 662 { 663 return tt_face_load_generic_header( face, stream, TTAG_head ); 664 } 665 666 667 #ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS 668 669 FT_LOCAL_DEF( FT_Error ) tt_face_load_bhed(TT_Face face,FT_Stream stream)670 tt_face_load_bhed( TT_Face face, 671 FT_Stream stream ) 672 { 673 return tt_face_load_generic_header( face, stream, TTAG_bhed ); 674 } 675 676 #endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */ 677 678 679 /*************************************************************************/ 680 /* */ 681 /* <Function> */ 682 /* tt_face_load_maxp */ 683 /* */ 684 /* <Description> */ 685 /* Loads the maximum profile into a face object. */ 686 /* */ 687 /* <Input> */ 688 /* face :: A handle to the target face object. */ 689 /* */ 690 /* stream :: The input stream. */ 691 /* */ 692 /* <Return> */ 693 /* FreeType error code. 0 means success. */ 694 /* */ 695 FT_LOCAL_DEF( FT_Error ) tt_face_load_maxp(TT_Face face,FT_Stream stream)696 tt_face_load_maxp( TT_Face face, 697 FT_Stream stream ) 698 { 699 FT_Error error; 700 TT_MaxProfile* maxProfile = &face->max_profile; 701 702 static const FT_Frame_Field maxp_fields[] = 703 { 704 #undef FT_STRUCTURE 705 #define FT_STRUCTURE TT_MaxProfile 706 707 FT_FRAME_START( 6 ), 708 FT_FRAME_LONG ( version ), 709 FT_FRAME_USHORT( numGlyphs ), 710 FT_FRAME_END 711 }; 712 713 static const FT_Frame_Field maxp_fields_extra[] = 714 { 715 FT_FRAME_START( 26 ), 716 FT_FRAME_USHORT( maxPoints ), 717 FT_FRAME_USHORT( maxContours ), 718 FT_FRAME_USHORT( maxCompositePoints ), 719 FT_FRAME_USHORT( maxCompositeContours ), 720 FT_FRAME_USHORT( maxZones ), 721 FT_FRAME_USHORT( maxTwilightPoints ), 722 FT_FRAME_USHORT( maxStorage ), 723 FT_FRAME_USHORT( maxFunctionDefs ), 724 FT_FRAME_USHORT( maxInstructionDefs ), 725 FT_FRAME_USHORT( maxStackElements ), 726 FT_FRAME_USHORT( maxSizeOfInstructions ), 727 FT_FRAME_USHORT( maxComponentElements ), 728 FT_FRAME_USHORT( maxComponentDepth ), 729 FT_FRAME_END 730 }; 731 732 733 error = face->goto_table( face, TTAG_maxp, stream, 0 ); 734 if ( error ) 735 goto Exit; 736 737 if ( FT_STREAM_READ_FIELDS( maxp_fields, maxProfile ) ) 738 goto Exit; 739 740 maxProfile->maxPoints = 0; 741 maxProfile->maxContours = 0; 742 maxProfile->maxCompositePoints = 0; 743 maxProfile->maxCompositeContours = 0; 744 maxProfile->maxZones = 0; 745 maxProfile->maxTwilightPoints = 0; 746 maxProfile->maxStorage = 0; 747 maxProfile->maxFunctionDefs = 0; 748 maxProfile->maxInstructionDefs = 0; 749 maxProfile->maxStackElements = 0; 750 maxProfile->maxSizeOfInstructions = 0; 751 maxProfile->maxComponentElements = 0; 752 maxProfile->maxComponentDepth = 0; 753 754 if ( maxProfile->version >= 0x10000L ) 755 { 756 if ( FT_STREAM_READ_FIELDS( maxp_fields_extra, maxProfile ) ) 757 goto Exit; 758 759 /* XXX: an adjustment that is necessary to load certain */ 760 /* broken fonts like `Keystrokes MT' :-( */ 761 /* */ 762 /* We allocate 64 function entries by default when */ 763 /* the maxFunctionDefs value is smaller. */ 764 765 if ( maxProfile->maxFunctionDefs < 64 ) 766 maxProfile->maxFunctionDefs = 64; 767 768 /* we add 4 phantom points later */ 769 if ( maxProfile->maxTwilightPoints > ( 0xFFFFU - 4 ) ) 770 { 771 FT_TRACE0(( "tt_face_load_maxp:" 772 " too much twilight points in `maxp' table;\n" 773 " " 774 " some glyphs might be rendered incorrectly\n" )); 775 776 maxProfile->maxTwilightPoints = 0xFFFFU - 4; 777 } 778 } 779 780 FT_TRACE3(( "numGlyphs: %u\n", maxProfile->numGlyphs )); 781 782 Exit: 783 return error; 784 } 785 786 787 /*************************************************************************/ 788 /* */ 789 /* <Function> */ 790 /* tt_face_load_name */ 791 /* */ 792 /* <Description> */ 793 /* Loads the name records. */ 794 /* */ 795 /* <Input> */ 796 /* face :: A handle to the target face object. */ 797 /* */ 798 /* stream :: The input stream. */ 799 /* */ 800 /* <Return> */ 801 /* FreeType error code. 0 means success. */ 802 /* */ 803 FT_LOCAL_DEF( FT_Error ) tt_face_load_name(TT_Face face,FT_Stream stream)804 tt_face_load_name( TT_Face face, 805 FT_Stream stream ) 806 { 807 FT_Error error; 808 FT_Memory memory = stream->memory; 809 FT_ULong table_pos, table_len; 810 FT_ULong storage_start, storage_limit; 811 TT_NameTable table; 812 813 static const FT_Frame_Field name_table_fields[] = 814 { 815 #undef FT_STRUCTURE 816 #define FT_STRUCTURE TT_NameTableRec 817 818 FT_FRAME_START( 6 ), 819 FT_FRAME_USHORT( format ), 820 FT_FRAME_USHORT( numNameRecords ), 821 FT_FRAME_USHORT( storageOffset ), 822 FT_FRAME_END 823 }; 824 825 static const FT_Frame_Field name_record_fields[] = 826 { 827 #undef FT_STRUCTURE 828 #define FT_STRUCTURE TT_NameRec 829 830 /* no FT_FRAME_START */ 831 FT_FRAME_USHORT( platformID ), 832 FT_FRAME_USHORT( encodingID ), 833 FT_FRAME_USHORT( languageID ), 834 FT_FRAME_USHORT( nameID ), 835 FT_FRAME_USHORT( stringLength ), 836 FT_FRAME_USHORT( stringOffset ), 837 FT_FRAME_END 838 }; 839 840 static const FT_Frame_Field langTag_record_fields[] = 841 { 842 #undef FT_STRUCTURE 843 #define FT_STRUCTURE TT_LangTagRec 844 845 /* no FT_FRAME_START */ 846 FT_FRAME_USHORT( stringLength ), 847 FT_FRAME_USHORT( stringOffset ), 848 FT_FRAME_END 849 }; 850 851 852 table = &face->name_table; 853 table->stream = stream; 854 855 error = face->goto_table( face, TTAG_name, stream, &table_len ); 856 if ( error ) 857 goto Exit; 858 859 table_pos = FT_STREAM_POS(); 860 861 if ( FT_STREAM_READ_FIELDS( name_table_fields, table ) ) 862 goto Exit; 863 864 /* Some popular Asian fonts have an invalid `storageOffset' value (it */ 865 /* should be at least `6 + 12*numNameRecords'). However, the string */ 866 /* offsets, computed as `storageOffset + entry->stringOffset', are */ 867 /* valid pointers within the name table... */ 868 /* */ 869 /* We thus can't check `storageOffset' right now. */ 870 /* */ 871 storage_start = table_pos + 6 + 12 * table->numNameRecords; 872 storage_limit = table_pos + table_len; 873 874 if ( storage_start > storage_limit ) 875 { 876 FT_ERROR(( "tt_face_load_name: invalid `name' table\n" )); 877 error = FT_THROW( Name_Table_Missing ); 878 goto Exit; 879 } 880 881 /* `name' format 1 contains additional language tag records, */ 882 /* which we load first */ 883 if ( table->format == 1 ) 884 { 885 if ( FT_STREAM_SEEK( storage_start ) || 886 FT_READ_USHORT( table->numLangTagRecords ) ) 887 goto Exit; 888 889 storage_start += 2 + 4 * table->numLangTagRecords; 890 891 /* allocate language tag records array */ 892 if ( FT_NEW_ARRAY( table->langTags, table->numLangTagRecords ) || 893 FT_FRAME_ENTER( table->numLangTagRecords * 4 ) ) 894 goto Exit; 895 896 /* load language tags */ 897 { 898 TT_LangTag entry = table->langTags; 899 TT_LangTag limit = entry + table->numLangTagRecords; 900 901 902 for ( ; entry < limit; entry++ ) 903 { 904 (void)FT_STREAM_READ_FIELDS( langTag_record_fields, entry ); 905 906 /* check that the langTag string is within the table */ 907 entry->stringOffset += table_pos + table->storageOffset; 908 if ( entry->stringOffset < storage_start || 909 entry->stringOffset + entry->stringLength > storage_limit ) 910 { 911 /* invalid entry; ignore it */ 912 entry->stringLength = 0; 913 } 914 } 915 } 916 917 FT_FRAME_EXIT(); 918 919 (void)FT_STREAM_SEEK( table_pos + 6 ); 920 } 921 922 /* allocate name records array */ 923 if ( FT_NEW_ARRAY( table->names, table->numNameRecords ) || 924 FT_FRAME_ENTER( table->numNameRecords * 12 ) ) 925 goto Exit; 926 927 /* load name records */ 928 { 929 TT_Name entry = table->names; 930 FT_UInt count = table->numNameRecords; 931 932 933 for ( ; count > 0; count-- ) 934 { 935 if ( FT_STREAM_READ_FIELDS( name_record_fields, entry ) ) 936 continue; 937 938 /* check that the name is not empty */ 939 if ( entry->stringLength == 0 ) 940 continue; 941 942 /* check that the name string is within the table */ 943 entry->stringOffset += table_pos + table->storageOffset; 944 if ( entry->stringOffset < storage_start || 945 entry->stringOffset + entry->stringLength > storage_limit ) 946 { 947 /* invalid entry; ignore it */ 948 continue; 949 } 950 951 /* assure that we have a valid language tag ID, and */ 952 /* that the corresponding langTag entry is valid, too */ 953 if ( table->format == 1 && entry->languageID >= 0x8000U ) 954 { 955 if ( entry->languageID - 0x8000U >= table->numLangTagRecords || 956 !table->langTags[entry->languageID - 0x8000U].stringLength ) 957 { 958 /* invalid entry; ignore it */ 959 continue; 960 } 961 } 962 963 entry++; 964 } 965 966 /* reduce array size to the actually used elements */ 967 count = (FT_UInt)( entry - table->names ); 968 (void)FT_RENEW_ARRAY( table->names, 969 table->numNameRecords, 970 count ); 971 table->numNameRecords = count; 972 } 973 974 FT_FRAME_EXIT(); 975 976 /* everything went well, update face->num_names */ 977 face->num_names = (FT_UShort)table->numNameRecords; 978 979 Exit: 980 return error; 981 } 982 983 984 /*************************************************************************/ 985 /* */ 986 /* <Function> */ 987 /* tt_face_free_name */ 988 /* */ 989 /* <Description> */ 990 /* Frees the name records. */ 991 /* */ 992 /* <Input> */ 993 /* face :: A handle to the target face object. */ 994 /* */ 995 FT_LOCAL_DEF( void ) tt_face_free_name(TT_Face face)996 tt_face_free_name( TT_Face face ) 997 { 998 FT_Memory memory = face->root.driver->root.memory; 999 TT_NameTable table = &face->name_table; 1000 1001 1002 if ( table->names ) 1003 { 1004 TT_Name entry = table->names; 1005 TT_Name limit = entry + table->numNameRecords; 1006 1007 1008 for ( ; entry < limit; entry++ ) 1009 FT_FREE( entry->string ); 1010 1011 FT_FREE( table->names ); 1012 } 1013 1014 if ( table->langTags ) 1015 { 1016 TT_LangTag entry = table->langTags; 1017 TT_LangTag limit = entry + table->numLangTagRecords; 1018 1019 1020 for ( ; entry < limit; entry++ ) 1021 FT_FREE( entry->string ); 1022 1023 FT_FREE( table->langTags ); 1024 } 1025 1026 table->numNameRecords = 0; 1027 table->numLangTagRecords = 0; 1028 table->format = 0; 1029 table->storageOffset = 0; 1030 } 1031 1032 1033 /*************************************************************************/ 1034 /* */ 1035 /* <Function> */ 1036 /* tt_face_load_cmap */ 1037 /* */ 1038 /* <Description> */ 1039 /* Loads the cmap directory in a face object. The cmaps themselves */ 1040 /* are loaded on demand in the `ttcmap.c' module. */ 1041 /* */ 1042 /* <Input> */ 1043 /* face :: A handle to the target face object. */ 1044 /* */ 1045 /* stream :: A handle to the input stream. */ 1046 /* */ 1047 /* <Return> */ 1048 /* FreeType error code. 0 means success. */ 1049 /* */ 1050 1051 FT_LOCAL_DEF( FT_Error ) tt_face_load_cmap(TT_Face face,FT_Stream stream)1052 tt_face_load_cmap( TT_Face face, 1053 FT_Stream stream ) 1054 { 1055 FT_Error error; 1056 1057 1058 error = face->goto_table( face, TTAG_cmap, stream, &face->cmap_size ); 1059 if ( error ) 1060 goto Exit; 1061 1062 if ( FT_FRAME_EXTRACT( face->cmap_size, face->cmap_table ) ) 1063 face->cmap_size = 0; 1064 1065 Exit: 1066 return error; 1067 } 1068 1069 1070 1071 /*************************************************************************/ 1072 /* */ 1073 /* <Function> */ 1074 /* tt_face_load_os2 */ 1075 /* */ 1076 /* <Description> */ 1077 /* Loads the OS2 table. */ 1078 /* */ 1079 /* <Input> */ 1080 /* face :: A handle to the target face object. */ 1081 /* */ 1082 /* stream :: A handle to the input stream. */ 1083 /* */ 1084 /* <Return> */ 1085 /* FreeType error code. 0 means success. */ 1086 /* */ 1087 FT_LOCAL_DEF( FT_Error ) tt_face_load_os2(TT_Face face,FT_Stream stream)1088 tt_face_load_os2( TT_Face face, 1089 FT_Stream stream ) 1090 { 1091 FT_Error error; 1092 TT_OS2* os2; 1093 1094 static const FT_Frame_Field os2_fields[] = 1095 { 1096 #undef FT_STRUCTURE 1097 #define FT_STRUCTURE TT_OS2 1098 1099 FT_FRAME_START( 78 ), 1100 FT_FRAME_USHORT( version ), 1101 FT_FRAME_SHORT ( xAvgCharWidth ), 1102 FT_FRAME_USHORT( usWeightClass ), 1103 FT_FRAME_USHORT( usWidthClass ), 1104 FT_FRAME_SHORT ( fsType ), 1105 FT_FRAME_SHORT ( ySubscriptXSize ), 1106 FT_FRAME_SHORT ( ySubscriptYSize ), 1107 FT_FRAME_SHORT ( ySubscriptXOffset ), 1108 FT_FRAME_SHORT ( ySubscriptYOffset ), 1109 FT_FRAME_SHORT ( ySuperscriptXSize ), 1110 FT_FRAME_SHORT ( ySuperscriptYSize ), 1111 FT_FRAME_SHORT ( ySuperscriptXOffset ), 1112 FT_FRAME_SHORT ( ySuperscriptYOffset ), 1113 FT_FRAME_SHORT ( yStrikeoutSize ), 1114 FT_FRAME_SHORT ( yStrikeoutPosition ), 1115 FT_FRAME_SHORT ( sFamilyClass ), 1116 FT_FRAME_BYTE ( panose[0] ), 1117 FT_FRAME_BYTE ( panose[1] ), 1118 FT_FRAME_BYTE ( panose[2] ), 1119 FT_FRAME_BYTE ( panose[3] ), 1120 FT_FRAME_BYTE ( panose[4] ), 1121 FT_FRAME_BYTE ( panose[5] ), 1122 FT_FRAME_BYTE ( panose[6] ), 1123 FT_FRAME_BYTE ( panose[7] ), 1124 FT_FRAME_BYTE ( panose[8] ), 1125 FT_FRAME_BYTE ( panose[9] ), 1126 FT_FRAME_ULONG ( ulUnicodeRange1 ), 1127 FT_FRAME_ULONG ( ulUnicodeRange2 ), 1128 FT_FRAME_ULONG ( ulUnicodeRange3 ), 1129 FT_FRAME_ULONG ( ulUnicodeRange4 ), 1130 FT_FRAME_BYTE ( achVendID[0] ), 1131 FT_FRAME_BYTE ( achVendID[1] ), 1132 FT_FRAME_BYTE ( achVendID[2] ), 1133 FT_FRAME_BYTE ( achVendID[3] ), 1134 1135 FT_FRAME_USHORT( fsSelection ), 1136 FT_FRAME_USHORT( usFirstCharIndex ), 1137 FT_FRAME_USHORT( usLastCharIndex ), 1138 FT_FRAME_SHORT ( sTypoAscender ), 1139 FT_FRAME_SHORT ( sTypoDescender ), 1140 FT_FRAME_SHORT ( sTypoLineGap ), 1141 FT_FRAME_USHORT( usWinAscent ), 1142 FT_FRAME_USHORT( usWinDescent ), 1143 FT_FRAME_END 1144 }; 1145 1146 /* `OS/2' version 1 and newer */ 1147 static const FT_Frame_Field os2_fields_extra1[] = 1148 { 1149 FT_FRAME_START( 8 ), 1150 FT_FRAME_ULONG( ulCodePageRange1 ), 1151 FT_FRAME_ULONG( ulCodePageRange2 ), 1152 FT_FRAME_END 1153 }; 1154 1155 /* `OS/2' version 2 and newer */ 1156 static const FT_Frame_Field os2_fields_extra2[] = 1157 { 1158 FT_FRAME_START( 10 ), 1159 FT_FRAME_SHORT ( sxHeight ), 1160 FT_FRAME_SHORT ( sCapHeight ), 1161 FT_FRAME_USHORT( usDefaultChar ), 1162 FT_FRAME_USHORT( usBreakChar ), 1163 FT_FRAME_USHORT( usMaxContext ), 1164 FT_FRAME_END 1165 }; 1166 1167 /* `OS/2' version 5 and newer */ 1168 static const FT_Frame_Field os2_fields_extra5[] = 1169 { 1170 FT_FRAME_START( 4 ), 1171 FT_FRAME_USHORT( usLowerOpticalPointSize ), 1172 FT_FRAME_USHORT( usUpperOpticalPointSize ), 1173 FT_FRAME_END 1174 }; 1175 1176 1177 /* We now support old Mac fonts where the OS/2 table doesn't */ 1178 /* exist. Simply put, we set the `version' field to 0xFFFF */ 1179 /* and test this value each time we need to access the table. */ 1180 error = face->goto_table( face, TTAG_OS2, stream, 0 ); 1181 if ( error ) 1182 goto Exit; 1183 1184 os2 = &face->os2; 1185 1186 if ( FT_STREAM_READ_FIELDS( os2_fields, os2 ) ) 1187 goto Exit; 1188 1189 os2->ulCodePageRange1 = 0; 1190 os2->ulCodePageRange2 = 0; 1191 os2->sxHeight = 0; 1192 os2->sCapHeight = 0; 1193 os2->usDefaultChar = 0; 1194 os2->usBreakChar = 0; 1195 os2->usMaxContext = 0; 1196 os2->usLowerOpticalPointSize = 0; 1197 os2->usUpperOpticalPointSize = 0xFFFF; 1198 1199 if ( os2->version >= 0x0001 ) 1200 { 1201 /* only version 1 tables */ 1202 if ( FT_STREAM_READ_FIELDS( os2_fields_extra1, os2 ) ) 1203 goto Exit; 1204 1205 if ( os2->version >= 0x0002 ) 1206 { 1207 /* only version 2 tables */ 1208 if ( FT_STREAM_READ_FIELDS( os2_fields_extra2, os2 ) ) 1209 goto Exit; 1210 1211 if ( os2->version >= 0x0005 ) 1212 { 1213 /* only version 5 tables */ 1214 if ( FT_STREAM_READ_FIELDS( os2_fields_extra5, os2 ) ) 1215 goto Exit; 1216 } 1217 } 1218 } 1219 1220 FT_TRACE3(( "sTypoAscender: %4d\n", os2->sTypoAscender )); 1221 FT_TRACE3(( "sTypoDescender: %4d\n", os2->sTypoDescender )); 1222 FT_TRACE3(( "usWinAscent: %4u\n", os2->usWinAscent )); 1223 FT_TRACE3(( "usWinDescent: %4u\n", os2->usWinDescent )); 1224 FT_TRACE3(( "fsSelection: 0x%2x\n", os2->fsSelection )); 1225 1226 Exit: 1227 return error; 1228 } 1229 1230 1231 /*************************************************************************/ 1232 /* */ 1233 /* <Function> */ 1234 /* tt_face_load_postscript */ 1235 /* */ 1236 /* <Description> */ 1237 /* Loads the Postscript table. */ 1238 /* */ 1239 /* <Input> */ 1240 /* face :: A handle to the target face object. */ 1241 /* */ 1242 /* stream :: A handle to the input stream. */ 1243 /* */ 1244 /* <Return> */ 1245 /* FreeType error code. 0 means success. */ 1246 /* */ 1247 FT_LOCAL_DEF( FT_Error ) tt_face_load_post(TT_Face face,FT_Stream stream)1248 tt_face_load_post( TT_Face face, 1249 FT_Stream stream ) 1250 { 1251 FT_Error error; 1252 TT_Postscript* post = &face->postscript; 1253 1254 static const FT_Frame_Field post_fields[] = 1255 { 1256 #undef FT_STRUCTURE 1257 #define FT_STRUCTURE TT_Postscript 1258 1259 FT_FRAME_START( 32 ), 1260 FT_FRAME_LONG ( FormatType ), 1261 FT_FRAME_LONG ( italicAngle ), 1262 FT_FRAME_SHORT( underlinePosition ), 1263 FT_FRAME_SHORT( underlineThickness ), 1264 FT_FRAME_ULONG( isFixedPitch ), 1265 FT_FRAME_ULONG( minMemType42 ), 1266 FT_FRAME_ULONG( maxMemType42 ), 1267 FT_FRAME_ULONG( minMemType1 ), 1268 FT_FRAME_ULONG( maxMemType1 ), 1269 FT_FRAME_END 1270 }; 1271 1272 1273 error = face->goto_table( face, TTAG_post, stream, 0 ); 1274 if ( error ) 1275 return error; 1276 1277 if ( FT_STREAM_READ_FIELDS( post_fields, post ) ) 1278 return error; 1279 1280 /* we don't load the glyph names, we do that in another */ 1281 /* module (ttpost). */ 1282 1283 FT_TRACE3(( "FormatType: 0x%x\n", post->FormatType )); 1284 FT_TRACE3(( "isFixedPitch: %s\n", post->isFixedPitch 1285 ? " yes" : " no" )); 1286 1287 return FT_Err_Ok; 1288 } 1289 1290 1291 /*************************************************************************/ 1292 /* */ 1293 /* <Function> */ 1294 /* tt_face_load_pclt */ 1295 /* */ 1296 /* <Description> */ 1297 /* Loads the PCL 5 Table. */ 1298 /* */ 1299 /* <Input> */ 1300 /* face :: A handle to the target face object. */ 1301 /* */ 1302 /* stream :: A handle to the input stream. */ 1303 /* */ 1304 /* <Return> */ 1305 /* FreeType error code. 0 means success. */ 1306 /* */ 1307 FT_LOCAL_DEF( FT_Error ) tt_face_load_pclt(TT_Face face,FT_Stream stream)1308 tt_face_load_pclt( TT_Face face, 1309 FT_Stream stream ) 1310 { 1311 static const FT_Frame_Field pclt_fields[] = 1312 { 1313 #undef FT_STRUCTURE 1314 #define FT_STRUCTURE TT_PCLT 1315 1316 FT_FRAME_START( 54 ), 1317 FT_FRAME_ULONG ( Version ), 1318 FT_FRAME_ULONG ( FontNumber ), 1319 FT_FRAME_USHORT( Pitch ), 1320 FT_FRAME_USHORT( xHeight ), 1321 FT_FRAME_USHORT( Style ), 1322 FT_FRAME_USHORT( TypeFamily ), 1323 FT_FRAME_USHORT( CapHeight ), 1324 FT_FRAME_USHORT( SymbolSet ), 1325 FT_FRAME_BYTES ( TypeFace, 16 ), 1326 FT_FRAME_BYTES ( CharacterComplement, 8 ), 1327 FT_FRAME_BYTES ( FileName, 6 ), 1328 FT_FRAME_CHAR ( StrokeWeight ), 1329 FT_FRAME_CHAR ( WidthType ), 1330 FT_FRAME_BYTE ( SerifStyle ), 1331 FT_FRAME_BYTE ( Reserved ), 1332 FT_FRAME_END 1333 }; 1334 1335 FT_Error error; 1336 TT_PCLT* pclt = &face->pclt; 1337 1338 1339 /* optional table */ 1340 error = face->goto_table( face, TTAG_PCLT, stream, 0 ); 1341 if ( error ) 1342 goto Exit; 1343 1344 if ( FT_STREAM_READ_FIELDS( pclt_fields, pclt ) ) 1345 goto Exit; 1346 1347 Exit: 1348 return error; 1349 } 1350 1351 1352 /*************************************************************************/ 1353 /* */ 1354 /* <Function> */ 1355 /* tt_face_load_gasp */ 1356 /* */ 1357 /* <Description> */ 1358 /* Loads the `gasp' table into a face object. */ 1359 /* */ 1360 /* <Input> */ 1361 /* face :: A handle to the target face object. */ 1362 /* */ 1363 /* stream :: The input stream. */ 1364 /* */ 1365 /* <Return> */ 1366 /* FreeType error code. 0 means success. */ 1367 /* */ 1368 FT_LOCAL_DEF( FT_Error ) tt_face_load_gasp(TT_Face face,FT_Stream stream)1369 tt_face_load_gasp( TT_Face face, 1370 FT_Stream stream ) 1371 { 1372 FT_Error error; 1373 FT_Memory memory = stream->memory; 1374 1375 FT_UInt j,num_ranges; 1376 TT_GaspRange gaspranges = NULL; 1377 1378 1379 /* the gasp table is optional */ 1380 error = face->goto_table( face, TTAG_gasp, stream, 0 ); 1381 if ( error ) 1382 goto Exit; 1383 1384 if ( FT_FRAME_ENTER( 4L ) ) 1385 goto Exit; 1386 1387 face->gasp.version = FT_GET_USHORT(); 1388 face->gasp.numRanges = FT_GET_USHORT(); 1389 1390 FT_FRAME_EXIT(); 1391 1392 /* only support versions 0 and 1 of the table */ 1393 if ( face->gasp.version >= 2 ) 1394 { 1395 face->gasp.numRanges = 0; 1396 error = FT_THROW( Invalid_Table ); 1397 goto Exit; 1398 } 1399 1400 num_ranges = face->gasp.numRanges; 1401 FT_TRACE3(( "numRanges: %u\n", num_ranges )); 1402 1403 if ( FT_QNEW_ARRAY( face->gasp.gaspRanges, num_ranges ) || 1404 FT_FRAME_ENTER( num_ranges * 4L ) ) 1405 goto Exit; 1406 1407 gaspranges = face->gasp.gaspRanges; 1408 1409 for ( j = 0; j < num_ranges; j++ ) 1410 { 1411 gaspranges[j].maxPPEM = FT_GET_USHORT(); 1412 gaspranges[j].gaspFlag = FT_GET_USHORT(); 1413 1414 FT_TRACE3(( "gaspRange %d: rangeMaxPPEM %5d, rangeGaspBehavior 0x%x\n", 1415 j, 1416 gaspranges[j].maxPPEM, 1417 gaspranges[j].gaspFlag )); 1418 } 1419 1420 FT_FRAME_EXIT(); 1421 1422 Exit: 1423 return error; 1424 } 1425 1426 1427 /* END */ 1428