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