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-2020 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  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 ::
  49    *     A face object handle.
  50    *
  51    *   tag ::
  52    *     The searched tag.
  53    *
  54    * @Return:
  55    *   A pointer to the table directory entry.  0 if not found.
  56    */
  57   FT_LOCAL_DEF( TT_Table  )
  58   tt_face_lookup_table( TT_Face   face,
  59                         FT_ULong  tag  )
  60   {
  61     TT_Table  entry;
  62     TT_Table  limit;
  63 #ifdef FT_DEBUG_LEVEL_TRACE
  64     FT_Bool   zero_length = FALSE;
  65 #endif
  66 
  67 
  68     FT_TRACE4(( "tt_face_lookup_table: %08p, `%c%c%c%c' -- ",
  69                 face,
  70                 (FT_Char)( tag >> 24 ),
  71                 (FT_Char)( tag >> 16 ),
  72                 (FT_Char)( tag >> 8  ),
  73                 (FT_Char)( tag       ) ));
  74 
  75     entry = face->dir_tables;
  76     limit = entry + face->num_tables;
  77 
  78     for ( ; entry < limit; entry++ )
  79     {
  80       /* For compatibility with Windows, we consider    */
  81       /* zero-length tables the same as missing tables. */
  82       if ( entry->Tag == tag )
  83       {
  84         if ( entry->Length != 0 )
  85         {
  86           FT_TRACE4(( "found table.\n" ));
  87           return entry;
  88         }
  89 #ifdef FT_DEBUG_LEVEL_TRACE
  90         zero_length = TRUE;
  91 #endif
  92       }
  93     }
  94 
  95 #ifdef FT_DEBUG_LEVEL_TRACE
  96     if ( zero_length )
  97       FT_TRACE4(( "ignoring empty table\n" ));
  98     else
  99       FT_TRACE4(( "could not find table\n" ));
 100 #endif
 101 
 102     return NULL;
 103   }
 104 
 105 
 106   /**************************************************************************
 107    *
 108    * @Function:
 109    *   tt_face_goto_table
 110    *
 111    * @Description:
 112    *   Looks for a TrueType table by name, then seek a stream to it.
 113    *
 114    * @Input:
 115    *   face ::
 116    *     A face object handle.
 117    *
 118    *   tag ::
 119    *     The searched tag.
 120    *
 121    *   stream ::
 122    *     The stream to seek when the table is found.
 123    *
 124    * @Output:
 125    *   length ::
 126    *     The length of the table if found, undefined otherwise.
 127    *
 128    * @Return:
 129    *   FreeType error code.  0 means success.
 130    */
 131   FT_LOCAL_DEF( FT_Error )
 132   tt_face_goto_table( TT_Face    face,
 133                       FT_ULong   tag,
 134                       FT_Stream  stream,
 135                       FT_ULong*  length )
 136   {
 137     TT_Table  table;
 138     FT_Error  error;
 139 
 140 
 141     table = tt_face_lookup_table( face, tag );
 142     if ( table )
 143     {
 144       if ( length )
 145         *length = table->Length;
 146 
 147       if ( FT_STREAM_SEEK( table->Offset ) )
 148         goto Exit;
 149     }
 150     else
 151       error = FT_THROW( Table_Missing );
 152 
 153   Exit:
 154     return error;
 155   }
 156 
 157 
 158   /* Here, we                                                         */
 159   /*                                                                  */
 160   /* - check that `num_tables' is valid (and adjust it if necessary); */
 161   /*   also return the number of valid table entries                  */
 162   /*                                                                  */
 163   /* - look for a `head' table, check its size, and parse it to check */
 164   /*   whether its `magic' field is correctly set                     */
 165   /*                                                                  */
 166   /* - errors (except errors returned by stream handling)             */
 167   /*                                                                  */
 168   /*     SFNT_Err_Unknown_File_Format:                                */
 169   /*       no table is defined in directory, it is not sfnt-wrapped   */
 170   /*       data                                                       */
 171   /*     SFNT_Err_Table_Missing:                                      */
 172   /*       table directory is valid, but essential tables             */
 173   /*       (head/bhed/SING) are missing                               */
 174   /*                                                                  */
 175   static FT_Error
 176   check_table_dir( SFNT_Header  sfnt,
 177                    FT_Stream    stream,
 178                    FT_UShort*   valid )
 179   {
 180     FT_Error   error;
 181     FT_UShort  nn, valid_entries = 0;
 182     FT_UInt    has_head = 0, has_sing = 0, has_meta = 0;
 183     FT_ULong   offset = sfnt->offset + 12;
 184 
 185     static const FT_Frame_Field  table_dir_entry_fields[] =
 186     {
 187 #undef  FT_STRUCTURE
 188 #define FT_STRUCTURE  TT_TableRec
 189 
 190       FT_FRAME_START( 16 ),
 191         FT_FRAME_ULONG( Tag ),
 192         FT_FRAME_ULONG( CheckSum ),
 193         FT_FRAME_ULONG( Offset ),
 194         FT_FRAME_ULONG( Length ),
 195       FT_FRAME_END
 196     };
 197 
 198 
 199     if ( FT_STREAM_SEEK( offset ) )
 200       goto Exit;
 201 
 202     for ( nn = 0; nn < sfnt->num_tables; nn++ )
 203     {
 204       TT_TableRec  table;
 205 
 206 
 207       if ( FT_STREAM_READ_FIELDS( table_dir_entry_fields, &table ) )
 208       {
 209         nn--;
 210         FT_TRACE2(( "check_table_dir:"
 211                     " can read only %d table%s in font (instead of %d)\n",
 212                     nn, nn == 1 ? "" : "s", sfnt->num_tables ));
 213         sfnt->num_tables = nn;
 214         break;
 215       }
 216 
 217       /* we ignore invalid tables */
 218 
 219       if ( table.Offset > stream->size )
 220       {
 221         FT_TRACE2(( "check_table_dir: table entry %d invalid\n", nn ));
 222         continue;
 223       }
 224       else if ( table.Length > stream->size - table.Offset )
 225       {
 226         /* Some tables have such a simple structure that clipping its     */
 227         /* contents is harmless.  This also makes FreeType less sensitive */
 228         /* to invalid table lengths (which programs like Acroread seem to */
 229         /* ignore in general).                                            */
 230 
 231         if ( table.Tag == TTAG_hmtx ||
 232              table.Tag == TTAG_vmtx )
 233           valid_entries++;
 234         else
 235         {
 236           FT_TRACE2(( "check_table_dir: table entry %d invalid\n", nn ));
 237           continue;
 238         }
 239       }
 240       else
 241         valid_entries++;
 242 
 243       if ( table.Tag == TTAG_head || table.Tag == TTAG_bhed )
 244       {
 245         FT_UInt32  magic;
 246 
 247 
 248 #ifndef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
 249         if ( table.Tag == TTAG_head )
 250 #endif
 251           has_head = 1;
 252 
 253         /*
 254          * The table length should be 0x36, but certain font tools make it
 255          * 0x38, so we will just check that it is greater.
 256          *
 257          * Note that according to the specification, the table must be
 258          * padded to 32-bit lengths, but this doesn't apply to the value of
 259          * its `Length' field!
 260          *
 261          */
 262         if ( table.Length < 0x36 )
 263         {
 264           FT_TRACE2(( "check_table_dir:"
 265                       " `head' or `bhed' table too small\n" ));
 266           error = FT_THROW( Table_Missing );
 267           goto Exit;
 268         }
 269 
 270         if ( FT_STREAM_SEEK( table.Offset + 12 ) ||
 271              FT_READ_ULONG( magic )              )
 272           goto Exit;
 273 
 274         if ( magic != 0x5F0F3CF5UL )
 275           FT_TRACE2(( "check_table_dir:"
 276                       " invalid magic number in `head' or `bhed' table\n"));
 277 
 278         if ( FT_STREAM_SEEK( offset + ( nn + 1 ) * 16 ) )
 279           goto Exit;
 280       }
 281       else if ( table.Tag == TTAG_SING )
 282         has_sing = 1;
 283       else if ( table.Tag == TTAG_META )
 284         has_meta = 1;
 285     }
 286 
 287     *valid = valid_entries;
 288 
 289     if ( !valid_entries )
 290     {
 291       FT_TRACE2(( "check_table_dir: no valid tables found\n" ));
 292       error = FT_THROW( Unknown_File_Format );
 293       goto Exit;
 294     }
 295 
 296     /* if `sing' and `meta' tables are present, there is no `head' table */
 297     if ( has_head || ( has_sing && has_meta ) )
 298     {
 299       error = FT_Err_Ok;
 300       goto Exit;
 301     }
 302     else
 303     {
 304       FT_TRACE2(( "check_table_dir:" ));
 305 #ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
 306       FT_TRACE2(( " neither `head', `bhed', nor `sing' table found\n" ));
 307 #else
 308       FT_TRACE2(( " neither `head' nor `sing' table found\n" ));
 309 #endif
 310       error = FT_THROW( Table_Missing );
 311     }
 312 
 313   Exit:
 314     return error;
 315   }
 316 
 317 
 318   /**************************************************************************
 319    *
 320    * @Function:
 321    *   tt_face_load_font_dir
 322    *
 323    * @Description:
 324    *   Loads the header of a SFNT font file.
 325    *
 326    * @Input:
 327    *   face ::
 328    *     A handle to the target face object.
 329    *
 330    *   stream ::
 331    *     The input stream.
 332    *
 333    * @Output:
 334    *   sfnt ::
 335    *     The SFNT header.
 336    *
 337    * @Return:
 338    *   FreeType error code.  0 means success.
 339    *
 340    * @Note:
 341    *   The stream cursor must be at the beginning of the font directory.
 342    */
 343   FT_LOCAL_DEF( FT_Error )
 344   tt_face_load_font_dir( TT_Face    face,
 345                          FT_Stream  stream )
 346   {
 347     SFNT_HeaderRec  sfnt;
 348     FT_Error        error;
 349     FT_Memory       memory = stream->memory;
 350     FT_UShort       nn, valid_entries = 0;
 351 
 352     static const FT_Frame_Field  offset_table_fields[] =
 353     {
 354 #undef  FT_STRUCTURE
 355 #define FT_STRUCTURE  SFNT_HeaderRec
 356 
 357       FT_FRAME_START( 8 ),
 358         FT_FRAME_USHORT( num_tables ),
 359         FT_FRAME_USHORT( search_range ),
 360         FT_FRAME_USHORT( entry_selector ),
 361         FT_FRAME_USHORT( range_shift ),
 362       FT_FRAME_END
 363     };
 364 
 365 
 366     FT_TRACE2(( "tt_face_load_font_dir: %08p\n", face ));
 367 
 368     /* read the offset table */
 369 
 370     sfnt.offset = FT_STREAM_POS();
 371 
 372     if ( FT_READ_ULONG( sfnt.format_tag )                    ||
 373          FT_STREAM_READ_FIELDS( offset_table_fields, &sfnt ) )
 374       goto Exit;
 375 
 376     /* many fonts don't have these fields set correctly */
 377 #if 0
 378     if ( sfnt.search_range != 1 << ( sfnt.entry_selector + 4 )        ||
 379          sfnt.search_range + sfnt.range_shift != sfnt.num_tables << 4 )
 380       return FT_THROW( Unknown_File_Format );
 381 #endif
 382 
 383     /* load the table directory */
 384 
 385     FT_TRACE2(( "-- Number of tables: %10u\n",    sfnt.num_tables ));
 386     FT_TRACE2(( "-- Format version:   0x%08lx\n", sfnt.format_tag ));
 387 
 388     if ( sfnt.format_tag != TTAG_OTTO )
 389     {
 390       /* check first */
 391       error = check_table_dir( &sfnt, stream, &valid_entries );
 392       if ( error )
 393       {
 394         FT_TRACE2(( "tt_face_load_font_dir:"
 395                     " invalid table directory for TrueType\n" ));
 396         goto Exit;
 397       }
 398     }
 399     else
 400     {
 401       valid_entries = sfnt.num_tables;
 402       if ( !valid_entries )
 403       {
 404         FT_TRACE2(( "tt_face_load_font_dir: no valid tables found\n" ));
 405         error = FT_THROW( Unknown_File_Format );
 406         goto Exit;
 407       }
 408     }
 409 
 410     face->num_tables = valid_entries;
 411     face->format_tag = sfnt.format_tag;
 412 
 413     if ( FT_QNEW_ARRAY( face->dir_tables, face->num_tables ) )
 414       goto Exit;
 415 
 416     if ( FT_STREAM_SEEK( sfnt.offset + 12 )      ||
 417          FT_FRAME_ENTER( sfnt.num_tables * 16L ) )
 418       goto Exit;
 419 
 420     FT_TRACE2(( "\n"
 421                 "  tag    offset    length   checksum\n"
 422                 "  ----------------------------------\n" ));
 423 
 424     valid_entries = 0;
 425     for ( nn = 0; nn < sfnt.num_tables; nn++ )
 426     {
 427       TT_TableRec  entry;
 428       FT_UShort    i;
 429       FT_Bool      duplicate;
 430 
 431 
 432       entry.Tag      = FT_GET_TAG4();
 433       entry.CheckSum = FT_GET_ULONG();
 434       entry.Offset   = FT_GET_ULONG();
 435       entry.Length   = FT_GET_ULONG();
 436 
 437       /* ignore invalid tables that can't be sanitized */
 438 
 439       if ( entry.Offset > stream->size )
 440         continue;
 441       else if ( entry.Length > stream->size - entry.Offset )
 442       {
 443         if ( entry.Tag == TTAG_hmtx ||
 444              entry.Tag == TTAG_vmtx )
 445         {
 446 #ifdef FT_DEBUG_LEVEL_TRACE
 447           FT_ULong  old_length = entry.Length;
 448 #endif
 449 
 450 
 451           /* make metrics table length a multiple of 4 */
 452           entry.Length = ( stream->size - entry.Offset ) & ~3U;
 453 
 454           FT_TRACE2(( "  %c%c%c%c  %08lx  %08lx  %08lx"
 455                       " (sanitized; original length %08lx)",
 456                       (FT_Char)( entry.Tag >> 24 ),
 457                       (FT_Char)( entry.Tag >> 16 ),
 458                       (FT_Char)( entry.Tag >> 8  ),
 459                       (FT_Char)( entry.Tag       ),
 460                       entry.Offset,
 461                       entry.Length,
 462                       entry.CheckSum,
 463                       old_length ));
 464         }
 465         else
 466           continue;
 467       }
 468 #ifdef FT_DEBUG_LEVEL_TRACE
 469       else
 470         FT_TRACE2(( "  %c%c%c%c  %08lx  %08lx  %08lx",
 471                     (FT_Char)( entry.Tag >> 24 ),
 472                     (FT_Char)( entry.Tag >> 16 ),
 473                     (FT_Char)( entry.Tag >> 8  ),
 474                     (FT_Char)( entry.Tag       ),
 475                     entry.Offset,
 476                     entry.Length,
 477                     entry.CheckSum ));
 478 #endif
 479 
 480       /* ignore duplicate tables – the first one wins */
 481       duplicate = 0;
 482       for ( i = 0; i < valid_entries; i++ )
 483       {
 484         if ( face->dir_tables[i].Tag == entry.Tag )
 485         {
 486           duplicate = 1;
 487           break;
 488         }
 489       }
 490       if ( duplicate )
 491       {
 492         FT_TRACE2(( "  (duplicate, ignored)\n" ));
 493         continue;
 494       }
 495       else
 496       {
 497         FT_TRACE2(( "\n" ));
 498 
 499         /* we finally have a valid entry */
 500         face->dir_tables[valid_entries++] = entry;
 501       }
 502     }
 503 
 504     /* final adjustment to number of tables */
 505     face->num_tables = valid_entries;
 506 
 507     FT_FRAME_EXIT();
 508 
 509     FT_TRACE2(( "table directory loaded\n\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 )
 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
 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 )
 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 )
 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 )
 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                     "                  "
 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 )
 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 
 841     static const FT_Frame_Field  name_table_fields[] =
 842     {
 843 #undef  FT_STRUCTURE
 844 #define FT_STRUCTURE  TT_NameTableRec
 845 
 846       FT_FRAME_START( 6 ),
 847         FT_FRAME_USHORT( format ),
 848         FT_FRAME_USHORT( numNameRecords ),
 849         FT_FRAME_USHORT( storageOffset ),
 850       FT_FRAME_END
 851     };
 852 
 853     static const FT_Frame_Field  name_record_fields[] =
 854     {
 855 #undef  FT_STRUCTURE
 856 #define FT_STRUCTURE  TT_NameRec
 857 
 858       /* no FT_FRAME_START */
 859         FT_FRAME_USHORT( platformID ),
 860         FT_FRAME_USHORT( encodingID ),
 861         FT_FRAME_USHORT( languageID ),
 862         FT_FRAME_USHORT( nameID ),
 863         FT_FRAME_USHORT( stringLength ),
 864         FT_FRAME_USHORT( stringOffset ),
 865       FT_FRAME_END
 866     };
 867 
 868     static const FT_Frame_Field  langTag_record_fields[] =
 869     {
 870 #undef  FT_STRUCTURE
 871 #define FT_STRUCTURE  TT_LangTagRec
 872 
 873       /* no FT_FRAME_START */
 874         FT_FRAME_USHORT( stringLength ),
 875         FT_FRAME_USHORT( stringOffset ),
 876       FT_FRAME_END
 877     };
 878 
 879 
 880     table         = &face->name_table;
 881     table->stream = stream;
 882 
 883     error = face->goto_table( face, TTAG_name, stream, &table_len );
 884     if ( error )
 885       goto Exit;
 886 
 887     table_pos = FT_STREAM_POS();
 888 
 889     if ( FT_STREAM_READ_FIELDS( name_table_fields, table ) )
 890       goto Exit;
 891 
 892     /* Some popular Asian fonts have an invalid `storageOffset' value (it */
 893     /* should be at least `6 + 12*numNameRecords').  However, the string  */
 894     /* offsets, computed as `storageOffset + entry->stringOffset', are    */
 895     /* valid pointers within the name table...                            */
 896     /*                                                                    */
 897     /* We thus can't check `storageOffset' right now.                     */
 898     /*                                                                    */
 899     storage_start = table_pos + 6 + 12 * table->numNameRecords;
 900     storage_limit = table_pos + table_len;
 901 
 902     if ( storage_start > storage_limit )
 903     {
 904       FT_ERROR(( "tt_face_load_name: invalid `name' table\n" ));
 905       error = FT_THROW( Name_Table_Missing );
 906       goto Exit;
 907     }
 908 
 909     /* `name' format 1 contains additional language tag records, */
 910     /* which we load first                                       */
 911     if ( table->format == 1 )
 912     {
 913       if ( FT_STREAM_SEEK( storage_start )            ||
 914            FT_READ_USHORT( table->numLangTagRecords ) )
 915         goto Exit;
 916 
 917       storage_start += 2 + 4 * table->numLangTagRecords;
 918 
 919       /* allocate language tag records array */
 920       if ( FT_NEW_ARRAY( table->langTags, table->numLangTagRecords ) ||
 921            FT_FRAME_ENTER( table->numLangTagRecords * 4 )            )
 922         goto Exit;
 923 
 924       /* load language tags */
 925       {
 926         TT_LangTag  entry = table->langTags;
 927         TT_LangTag  limit = FT_OFFSET( entry, table->numLangTagRecords );
 928 
 929 
 930         for ( ; entry < limit; entry++ )
 931         {
 932           (void)FT_STREAM_READ_FIELDS( langTag_record_fields, entry );
 933 
 934           /* check that the langTag string is within the table */
 935           entry->stringOffset += table_pos + table->storageOffset;
 936           if ( entry->stringOffset                       < storage_start ||
 937                entry->stringOffset + entry->stringLength > storage_limit )
 938           {
 939             /* invalid entry; ignore it */
 940             entry->stringLength = 0;
 941           }
 942         }
 943       }
 944 
 945       FT_FRAME_EXIT();
 946 
 947       (void)FT_STREAM_SEEK( table_pos + 6 );
 948     }
 949 
 950     /* allocate name records array */
 951     if ( FT_NEW_ARRAY( table->names, table->numNameRecords ) ||
 952          FT_FRAME_ENTER( table->numNameRecords * 12 )        )
 953       goto Exit;
 954 
 955     /* load name records */
 956     {
 957       TT_Name  entry = table->names;
 958       FT_UInt  count = table->numNameRecords;
 959 
 960 
 961       for ( ; count > 0; count-- )
 962       {
 963         if ( FT_STREAM_READ_FIELDS( name_record_fields, entry ) )
 964           continue;
 965 
 966         /* check that the name is not empty */
 967         if ( entry->stringLength == 0 )
 968           continue;
 969 
 970         /* check that the name string is within the table */
 971         entry->stringOffset += table_pos + table->storageOffset;
 972         if ( entry->stringOffset                       < storage_start ||
 973              entry->stringOffset + entry->stringLength > storage_limit )
 974         {
 975           /* invalid entry; ignore it */
 976           continue;
 977         }
 978 
 979         /* assure that we have a valid language tag ID, and   */
 980         /* that the corresponding langTag entry is valid, too */
 981         if ( table->format == 1 && entry->languageID >= 0x8000U )
 982         {
 983           if ( entry->languageID - 0x8000U >= table->numLangTagRecords    ||
 984                !table->langTags[entry->languageID - 0x8000U].stringLength )
 985           {
 986             /* invalid entry; ignore it */
 987             continue;
 988           }
 989         }
 990 
 991         entry++;
 992       }
 993 
 994       /* reduce array size to the actually used elements */
 995       count = (FT_UInt)( entry - table->names );
 996       (void)FT_RENEW_ARRAY( table->names,
 997                             table->numNameRecords,
 998                             count );
 999       table->numNameRecords = count;
1000     }
1001 
1002     FT_FRAME_EXIT();
1003 
1004     /* everything went well, update face->num_names */
1005     face->num_names = (FT_UShort)table->numNameRecords;
1006 
1007   Exit:
1008     return error;
1009   }
1010 
1011 
1012   /**************************************************************************
1013    *
1014    * @Function:
1015    *   tt_face_free_name
1016    *
1017    * @Description:
1018    *   Frees the name records.
1019    *
1020    * @Input:
1021    *   face ::
1022    *     A handle to the target face object.
1023    */
1024   FT_LOCAL_DEF( void )
1025   tt_face_free_name( TT_Face  face )
1026   {
1027     FT_Memory     memory = face->root.driver->root.memory;
1028     TT_NameTable  table  = &face->name_table;
1029 
1030 
1031     if ( table->names )
1032     {
1033       TT_Name  entry = table->names;
1034       TT_Name  limit = entry + table->numNameRecords;
1035 
1036 
1037       for ( ; entry < limit; entry++ )
1038         FT_FREE( entry->string );
1039 
1040       FT_FREE( table->names );
1041     }
1042 
1043     if ( table->langTags )
1044     {
1045       TT_LangTag  entry = table->langTags;
1046       TT_LangTag  limit = entry + table->numLangTagRecords;
1047 
1048 
1049       for ( ; entry < limit; entry++ )
1050         FT_FREE( entry->string );
1051 
1052       FT_FREE( table->langTags );
1053     }
1054 
1055     table->numNameRecords    = 0;
1056     table->numLangTagRecords = 0;
1057     table->format            = 0;
1058     table->storageOffset     = 0;
1059   }
1060 
1061 
1062   /**************************************************************************
1063    *
1064    * @Function:
1065    *   tt_face_load_cmap
1066    *
1067    * @Description:
1068    *   Loads the cmap directory in a face object.  The cmaps themselves
1069    *   are loaded on demand in the `ttcmap.c' module.
1070    *
1071    * @Input:
1072    *   face ::
1073    *     A handle to the target face object.
1074    *
1075    *   stream ::
1076    *     A handle to the input stream.
1077    *
1078    * @Return:
1079    *   FreeType error code.  0 means success.
1080    */
1081 
1082   FT_LOCAL_DEF( FT_Error )
1083   tt_face_load_cmap( TT_Face    face,
1084                      FT_Stream  stream )
1085   {
1086     FT_Error  error;
1087 
1088 
1089     error = face->goto_table( face, TTAG_cmap, stream, &face->cmap_size );
1090     if ( error )
1091       goto Exit;
1092 
1093     if ( FT_FRAME_EXTRACT( face->cmap_size, face->cmap_table ) )
1094       face->cmap_size = 0;
1095 
1096   Exit:
1097     return error;
1098   }
1099 
1100 
1101 
1102   /**************************************************************************
1103    *
1104    * @Function:
1105    *   tt_face_load_os2
1106    *
1107    * @Description:
1108    *   Loads the OS2 table.
1109    *
1110    * @Input:
1111    *   face ::
1112    *     A handle to the target face object.
1113    *
1114    *   stream ::
1115    *     A handle to the input stream.
1116    *
1117    * @Return:
1118    *   FreeType error code.  0 means success.
1119    */
1120   FT_LOCAL_DEF( FT_Error )
1121   tt_face_load_os2( TT_Face    face,
1122                     FT_Stream  stream )
1123   {
1124     FT_Error  error;
1125     TT_OS2*   os2;
1126 
1127     static const FT_Frame_Field  os2_fields[] =
1128     {
1129 #undef  FT_STRUCTURE
1130 #define FT_STRUCTURE  TT_OS2
1131 
1132       FT_FRAME_START( 78 ),
1133         FT_FRAME_USHORT( version ),
1134         FT_FRAME_SHORT ( xAvgCharWidth ),
1135         FT_FRAME_USHORT( usWeightClass ),
1136         FT_FRAME_USHORT( usWidthClass ),
1137         FT_FRAME_SHORT ( fsType ),
1138         FT_FRAME_SHORT ( ySubscriptXSize ),
1139         FT_FRAME_SHORT ( ySubscriptYSize ),
1140         FT_FRAME_SHORT ( ySubscriptXOffset ),
1141         FT_FRAME_SHORT ( ySubscriptYOffset ),
1142         FT_FRAME_SHORT ( ySuperscriptXSize ),
1143         FT_FRAME_SHORT ( ySuperscriptYSize ),
1144         FT_FRAME_SHORT ( ySuperscriptXOffset ),
1145         FT_FRAME_SHORT ( ySuperscriptYOffset ),
1146         FT_FRAME_SHORT ( yStrikeoutSize ),
1147         FT_FRAME_SHORT ( yStrikeoutPosition ),
1148         FT_FRAME_SHORT ( sFamilyClass ),
1149         FT_FRAME_BYTE  ( panose[0] ),
1150         FT_FRAME_BYTE  ( panose[1] ),
1151         FT_FRAME_BYTE  ( panose[2] ),
1152         FT_FRAME_BYTE  ( panose[3] ),
1153         FT_FRAME_BYTE  ( panose[4] ),
1154         FT_FRAME_BYTE  ( panose[5] ),
1155         FT_FRAME_BYTE  ( panose[6] ),
1156         FT_FRAME_BYTE  ( panose[7] ),
1157         FT_FRAME_BYTE  ( panose[8] ),
1158         FT_FRAME_BYTE  ( panose[9] ),
1159         FT_FRAME_ULONG ( ulUnicodeRange1 ),
1160         FT_FRAME_ULONG ( ulUnicodeRange2 ),
1161         FT_FRAME_ULONG ( ulUnicodeRange3 ),
1162         FT_FRAME_ULONG ( ulUnicodeRange4 ),
1163         FT_FRAME_BYTE  ( achVendID[0] ),
1164         FT_FRAME_BYTE  ( achVendID[1] ),
1165         FT_FRAME_BYTE  ( achVendID[2] ),
1166         FT_FRAME_BYTE  ( achVendID[3] ),
1167 
1168         FT_FRAME_USHORT( fsSelection ),
1169         FT_FRAME_USHORT( usFirstCharIndex ),
1170         FT_FRAME_USHORT( usLastCharIndex ),
1171         FT_FRAME_SHORT ( sTypoAscender ),
1172         FT_FRAME_SHORT ( sTypoDescender ),
1173         FT_FRAME_SHORT ( sTypoLineGap ),
1174         FT_FRAME_USHORT( usWinAscent ),
1175         FT_FRAME_USHORT( usWinDescent ),
1176       FT_FRAME_END
1177     };
1178 
1179     /* `OS/2' version 1 and newer */
1180     static const FT_Frame_Field  os2_fields_extra1[] =
1181     {
1182       FT_FRAME_START( 8 ),
1183         FT_FRAME_ULONG( ulCodePageRange1 ),
1184         FT_FRAME_ULONG( ulCodePageRange2 ),
1185       FT_FRAME_END
1186     };
1187 
1188     /* `OS/2' version 2 and newer */
1189     static const FT_Frame_Field  os2_fields_extra2[] =
1190     {
1191       FT_FRAME_START( 10 ),
1192         FT_FRAME_SHORT ( sxHeight ),
1193         FT_FRAME_SHORT ( sCapHeight ),
1194         FT_FRAME_USHORT( usDefaultChar ),
1195         FT_FRAME_USHORT( usBreakChar ),
1196         FT_FRAME_USHORT( usMaxContext ),
1197       FT_FRAME_END
1198     };
1199 
1200     /* `OS/2' version 5 and newer */
1201     static const FT_Frame_Field  os2_fields_extra5[] =
1202     {
1203       FT_FRAME_START( 4 ),
1204         FT_FRAME_USHORT( usLowerOpticalPointSize ),
1205         FT_FRAME_USHORT( usUpperOpticalPointSize ),
1206       FT_FRAME_END
1207     };
1208 
1209 
1210     /* We now support old Mac fonts where the OS/2 table doesn't  */
1211     /* exist.  Simply put, we set the `version' field to 0xFFFF   */
1212     /* and test this value each time we need to access the table. */
1213     error = face->goto_table( face, TTAG_OS2, stream, 0 );
1214     if ( error )
1215       goto Exit;
1216 
1217     os2 = &face->os2;
1218 
1219     if ( FT_STREAM_READ_FIELDS( os2_fields, os2 ) )
1220       goto Exit;
1221 
1222     os2->ulCodePageRange1        = 0;
1223     os2->ulCodePageRange2        = 0;
1224     os2->sxHeight                = 0;
1225     os2->sCapHeight              = 0;
1226     os2->usDefaultChar           = 0;
1227     os2->usBreakChar             = 0;
1228     os2->usMaxContext            = 0;
1229     os2->usLowerOpticalPointSize = 0;
1230     os2->usUpperOpticalPointSize = 0xFFFF;
1231 
1232     if ( os2->version >= 0x0001 )
1233     {
1234       /* only version 1 tables */
1235       if ( FT_STREAM_READ_FIELDS( os2_fields_extra1, os2 ) )
1236         goto Exit;
1237 
1238       if ( os2->version >= 0x0002 )
1239       {
1240         /* only version 2 tables */
1241         if ( FT_STREAM_READ_FIELDS( os2_fields_extra2, os2 ) )
1242           goto Exit;
1243 
1244         if ( os2->version >= 0x0005 )
1245         {
1246           /* only version 5 tables */
1247           if ( FT_STREAM_READ_FIELDS( os2_fields_extra5, os2 ) )
1248             goto Exit;
1249         }
1250       }
1251     }
1252 
1253     FT_TRACE3(( "sTypoAscender:  %4d\n",   os2->sTypoAscender ));
1254     FT_TRACE3(( "sTypoDescender: %4d\n",   os2->sTypoDescender ));
1255     FT_TRACE3(( "usWinAscent:    %4u\n",   os2->usWinAscent ));
1256     FT_TRACE3(( "usWinDescent:   %4u\n",   os2->usWinDescent ));
1257     FT_TRACE3(( "fsSelection:    0x%2x\n", os2->fsSelection ));
1258 
1259   Exit:
1260     return error;
1261   }
1262 
1263 
1264   /**************************************************************************
1265    *
1266    * @Function:
1267    *   tt_face_load_postscript
1268    *
1269    * @Description:
1270    *   Loads the Postscript table.
1271    *
1272    * @Input:
1273    *   face ::
1274    *     A handle to the target face object.
1275    *
1276    *   stream ::
1277    *     A handle to the input stream.
1278    *
1279    * @Return:
1280    *   FreeType error code.  0 means success.
1281    */
1282   FT_LOCAL_DEF( FT_Error )
1283   tt_face_load_post( TT_Face    face,
1284                      FT_Stream  stream )
1285   {
1286     FT_Error        error;
1287     TT_Postscript*  post = &face->postscript;
1288 
1289     static const FT_Frame_Field  post_fields[] =
1290     {
1291 #undef  FT_STRUCTURE
1292 #define FT_STRUCTURE  TT_Postscript
1293 
1294       FT_FRAME_START( 32 ),
1295         FT_FRAME_LONG ( FormatType ),
1296         FT_FRAME_LONG ( italicAngle ),
1297         FT_FRAME_SHORT( underlinePosition ),
1298         FT_FRAME_SHORT( underlineThickness ),
1299         FT_FRAME_ULONG( isFixedPitch ),
1300         FT_FRAME_ULONG( minMemType42 ),
1301         FT_FRAME_ULONG( maxMemType42 ),
1302         FT_FRAME_ULONG( minMemType1 ),
1303         FT_FRAME_ULONG( maxMemType1 ),
1304       FT_FRAME_END
1305     };
1306 
1307 
1308     error = face->goto_table( face, TTAG_post, stream, 0 );
1309     if ( error )
1310       return error;
1311 
1312     if ( FT_STREAM_READ_FIELDS( post_fields, post ) )
1313       return error;
1314 
1315     /* we don't load the glyph names, we do that in another */
1316     /* module (ttpost).                                     */
1317 
1318     FT_TRACE3(( "FormatType:   0x%x\n", post->FormatType ));
1319     FT_TRACE3(( "isFixedPitch:   %s\n", post->isFixedPitch
1320                                         ? "  yes" : "   no" ));
1321 
1322     return FT_Err_Ok;
1323   }
1324 
1325 
1326   /**************************************************************************
1327    *
1328    * @Function:
1329    *   tt_face_load_pclt
1330    *
1331    * @Description:
1332    *   Loads the PCL 5 Table.
1333    *
1334    * @Input:
1335    *   face ::
1336    *     A handle to the target face object.
1337    *
1338    *   stream ::
1339    *     A handle to the input stream.
1340    *
1341    * @Return:
1342    *   FreeType error code.  0 means success.
1343    */
1344   FT_LOCAL_DEF( FT_Error )
1345   tt_face_load_pclt( TT_Face    face,
1346                      FT_Stream  stream )
1347   {
1348     static const FT_Frame_Field  pclt_fields[] =
1349     {
1350 #undef  FT_STRUCTURE
1351 #define FT_STRUCTURE  TT_PCLT
1352 
1353       FT_FRAME_START( 54 ),
1354         FT_FRAME_ULONG ( Version ),
1355         FT_FRAME_ULONG ( FontNumber ),
1356         FT_FRAME_USHORT( Pitch ),
1357         FT_FRAME_USHORT( xHeight ),
1358         FT_FRAME_USHORT( Style ),
1359         FT_FRAME_USHORT( TypeFamily ),
1360         FT_FRAME_USHORT( CapHeight ),
1361         FT_FRAME_USHORT( SymbolSet ),
1362         FT_FRAME_BYTES ( TypeFace, 16 ),
1363         FT_FRAME_BYTES ( CharacterComplement, 8 ),
1364         FT_FRAME_BYTES ( FileName, 6 ),
1365         FT_FRAME_CHAR  ( StrokeWeight ),
1366         FT_FRAME_CHAR  ( WidthType ),
1367         FT_FRAME_BYTE  ( SerifStyle ),
1368         FT_FRAME_BYTE  ( Reserved ),
1369       FT_FRAME_END
1370     };
1371 
1372     FT_Error  error;
1373     TT_PCLT*  pclt = &face->pclt;
1374 
1375 
1376     /* optional table */
1377     error = face->goto_table( face, TTAG_PCLT, stream, 0 );
1378     if ( error )
1379       goto Exit;
1380 
1381     if ( FT_STREAM_READ_FIELDS( pclt_fields, pclt ) )
1382       goto Exit;
1383 
1384   Exit:
1385     return error;
1386   }
1387 
1388 
1389   /**************************************************************************
1390    *
1391    * @Function:
1392    *   tt_face_load_gasp
1393    *
1394    * @Description:
1395    *   Loads the `gasp' table into a face object.
1396    *
1397    * @Input:
1398    *   face ::
1399    *     A handle to the target face object.
1400    *
1401    *   stream ::
1402    *     The input stream.
1403    *
1404    * @Return:
1405    *   FreeType error code.  0 means success.
1406    */
1407   FT_LOCAL_DEF( FT_Error )
1408   tt_face_load_gasp( TT_Face    face,
1409                      FT_Stream  stream )
1410   {
1411     FT_Error   error;
1412     FT_Memory  memory = stream->memory;
1413 
1414     FT_UInt        j,num_ranges;
1415     TT_GaspRange   gaspranges = NULL;
1416 
1417 
1418     /* the gasp table is optional */
1419     error = face->goto_table( face, TTAG_gasp, stream, 0 );
1420     if ( error )
1421       goto Exit;
1422 
1423     if ( FT_FRAME_ENTER( 4L ) )
1424       goto Exit;
1425 
1426     face->gasp.version   = FT_GET_USHORT();
1427     face->gasp.numRanges = FT_GET_USHORT();
1428 
1429     FT_FRAME_EXIT();
1430 
1431     /* only support versions 0 and 1 of the table */
1432     if ( face->gasp.version >= 2 )
1433     {
1434       face->gasp.numRanges = 0;
1435       error = FT_THROW( Invalid_Table );
1436       goto Exit;
1437     }
1438 
1439     num_ranges = face->gasp.numRanges;
1440     FT_TRACE3(( "numRanges: %u\n", num_ranges ));
1441 
1442     if ( FT_QNEW_ARRAY( face->gasp.gaspRanges, num_ranges ) ||
1443          FT_FRAME_ENTER( num_ranges * 4L )                  )
1444       goto Exit;
1445 
1446     gaspranges = face->gasp.gaspRanges;
1447 
1448     for ( j = 0; j < num_ranges; j++ )
1449     {
1450       gaspranges[j].maxPPEM  = FT_GET_USHORT();
1451       gaspranges[j].gaspFlag = FT_GET_USHORT();
1452 
1453       FT_TRACE3(( "gaspRange %d: rangeMaxPPEM %5d, rangeGaspBehavior 0x%x\n",
1454                   j,
1455                   gaspranges[j].maxPPEM,
1456                   gaspranges[j].gaspFlag ));
1457     }
1458 
1459     FT_FRAME_EXIT();
1460 
1461   Exit:
1462     return error;
1463   }
1464 
1465 
1466 /* END */