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