tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ttload.c (41323B)


      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-2025 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  )
     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 )
    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
    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 )
    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.  Use value 1 to access the table
    539   *     directory.
    540   *
    541   *   offset ::
    542   *     The starting offset in the table (or the file if
    543   *     tag == 0).
    544   *
    545   *   length ::
    546   *     The address of the decision variable:
    547   *
    548   *     If length == NULL:
    549   *       Loads the whole table.  Returns an error if
    550   *       `offset' == 0!
    551   *
    552   *     If *length == 0:
    553   *       Exits immediately; returning the length of the given
    554   *       table or of the font file, depending on the value of
    555   *       `tag'.
    556   *
    557   *     If *length != 0:
    558   *       Loads the next `length' bytes of table or font,
    559   *       starting at offset `offset' (in table or font too).
    560   *
    561   * @Output:
    562   *   buffer ::
    563   *     The address of target buffer.
    564   *
    565   * @Return:
    566   *   FreeType error code.  0 means success.
    567   */
    568  FT_LOCAL_DEF( FT_Error )
    569  tt_face_load_any( TT_Face    face,
    570                    FT_ULong   tag,
    571                    FT_Long    offset,
    572                    FT_Byte*   buffer,
    573                    FT_ULong*  length )
    574  {
    575    FT_Error   error;
    576    FT_Stream  stream;
    577    TT_Table   table;
    578    FT_ULong   size;
    579 
    580 
    581    if ( tag == 0 )
    582    {
    583      /* The whole font file. */
    584      size = face->root.stream->size;
    585    }
    586    else if ( tag == 1 )
    587    {
    588      /* The currently selected font's table directory.            */
    589      /*                                                           */
    590      /* Note that `face_index` is also used to enumerate elements */
    591      /* of containers like a Mac Resource; this means we must     */
    592      /* check whether we actually have a TTC (with multiple table */
    593      /* directories).                                             */
    594      FT_Long  idx = face->root.face_index & 0xFFFF;
    595 
    596 
    597      if ( idx >= face->ttc_header.count )
    598        idx = 0;
    599 
    600      offset += face->ttc_header.offsets[idx];
    601      size    = 4 + 8 + 16 * face->num_tables;
    602    }
    603    else
    604    {
    605      /* look for tag in font directory */
    606      table = tt_face_lookup_table( face, tag );
    607      if ( !table )
    608      {
    609        error = FT_THROW( Table_Missing );
    610        goto Exit;
    611      }
    612 
    613      offset += table->Offset;
    614      size    = table->Length;
    615    }
    616 
    617    if ( length && *length == 0 )
    618    {
    619      *length = size;
    620 
    621      return FT_Err_Ok;
    622    }
    623 
    624    if ( length )
    625      size = *length;
    626 
    627    stream = face->root.stream;
    628    /* the `if' is syntactic sugar for picky compilers */
    629    if ( FT_STREAM_READ_AT( offset, buffer, size ) )
    630      goto Exit;
    631 
    632  Exit:
    633    return error;
    634  }
    635 
    636 
    637  /**************************************************************************
    638   *
    639   * @Function:
    640   *   tt_face_load_generic_header
    641   *
    642   * @Description:
    643   *   Loads the TrueType table `head' or `bhed'.
    644   *
    645   * @Input:
    646   *   face ::
    647   *     A handle to the target face object.
    648   *
    649   *   stream ::
    650   *     The input stream.
    651   *
    652   * @Return:
    653   *   FreeType error code.  0 means success.
    654   */
    655  static FT_Error
    656  tt_face_load_generic_header( TT_Face    face,
    657                               FT_Stream  stream,
    658                               FT_ULong   tag )
    659  {
    660    FT_Error    error;
    661    TT_Header*  header;
    662 
    663    static const FT_Frame_Field  header_fields[] =
    664    {
    665 #undef  FT_STRUCTURE
    666 #define FT_STRUCTURE  TT_Header
    667 
    668      FT_FRAME_START( 54 ),
    669        FT_FRAME_ULONG ( Table_Version ),
    670        FT_FRAME_ULONG ( Font_Revision ),
    671        FT_FRAME_LONG  ( CheckSum_Adjust ),
    672        FT_FRAME_LONG  ( Magic_Number ),
    673        FT_FRAME_USHORT( Flags ),
    674        FT_FRAME_USHORT( Units_Per_EM ),
    675        FT_FRAME_ULONG ( Created[0] ),
    676        FT_FRAME_ULONG ( Created[1] ),
    677        FT_FRAME_ULONG ( Modified[0] ),
    678        FT_FRAME_ULONG ( Modified[1] ),
    679        FT_FRAME_SHORT ( xMin ),
    680        FT_FRAME_SHORT ( yMin ),
    681        FT_FRAME_SHORT ( xMax ),
    682        FT_FRAME_SHORT ( yMax ),
    683        FT_FRAME_USHORT( Mac_Style ),
    684        FT_FRAME_USHORT( Lowest_Rec_PPEM ),
    685        FT_FRAME_SHORT ( Font_Direction ),
    686        FT_FRAME_SHORT ( Index_To_Loc_Format ),
    687        FT_FRAME_SHORT ( Glyph_Data_Format ),
    688      FT_FRAME_END
    689    };
    690 
    691 
    692    error = face->goto_table( face, tag, stream, 0 );
    693    if ( error )
    694      goto Exit;
    695 
    696    header = &face->header;
    697 
    698    if ( FT_STREAM_READ_FIELDS( header_fields, header ) )
    699      goto Exit;
    700 
    701    FT_TRACE3(( "Units per EM: %4hu\n", header->Units_Per_EM ));
    702    FT_TRACE3(( "IndexToLoc:   %4hd\n", header->Index_To_Loc_Format ));
    703 
    704  Exit:
    705    return error;
    706  }
    707 
    708 
    709  FT_LOCAL_DEF( FT_Error )
    710  tt_face_load_head( TT_Face    face,
    711                     FT_Stream  stream )
    712  {
    713    return tt_face_load_generic_header( face, stream, TTAG_head );
    714  }
    715 
    716 
    717 #ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
    718 
    719  FT_LOCAL_DEF( FT_Error )
    720  tt_face_load_bhed( TT_Face    face,
    721                     FT_Stream  stream )
    722  {
    723    return tt_face_load_generic_header( face, stream, TTAG_bhed );
    724  }
    725 
    726 #endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */
    727 
    728 
    729  /**************************************************************************
    730   *
    731   * @Function:
    732   *   tt_face_load_maxp
    733   *
    734   * @Description:
    735   *   Loads the maximum profile into a face object.
    736   *
    737   * @Input:
    738   *   face ::
    739   *     A handle to the target face object.
    740   *
    741   *   stream ::
    742   *     The input stream.
    743   *
    744   * @Return:
    745   *   FreeType error code.  0 means success.
    746   */
    747  FT_LOCAL_DEF( FT_Error )
    748  tt_face_load_maxp( TT_Face    face,
    749                     FT_Stream  stream )
    750  {
    751    FT_Error        error;
    752    TT_MaxProfile*  maxProfile = &face->max_profile;
    753 
    754    static const FT_Frame_Field  maxp_fields[] =
    755    {
    756 #undef  FT_STRUCTURE
    757 #define FT_STRUCTURE  TT_MaxProfile
    758 
    759      FT_FRAME_START( 6 ),
    760        FT_FRAME_LONG  ( version ),
    761        FT_FRAME_USHORT( numGlyphs ),
    762      FT_FRAME_END
    763    };
    764 
    765    static const FT_Frame_Field  maxp_fields_extra[] =
    766    {
    767      FT_FRAME_START( 26 ),
    768        FT_FRAME_USHORT( maxPoints ),
    769        FT_FRAME_USHORT( maxContours ),
    770        FT_FRAME_USHORT( maxCompositePoints ),
    771        FT_FRAME_USHORT( maxCompositeContours ),
    772        FT_FRAME_USHORT( maxZones ),
    773        FT_FRAME_USHORT( maxTwilightPoints ),
    774        FT_FRAME_USHORT( maxStorage ),
    775        FT_FRAME_USHORT( maxFunctionDefs ),
    776        FT_FRAME_USHORT( maxInstructionDefs ),
    777        FT_FRAME_USHORT( maxStackElements ),
    778        FT_FRAME_USHORT( maxSizeOfInstructions ),
    779        FT_FRAME_USHORT( maxComponentElements ),
    780        FT_FRAME_USHORT( maxComponentDepth ),
    781      FT_FRAME_END
    782    };
    783 
    784 
    785    error = face->goto_table( face, TTAG_maxp, stream, 0 );
    786    if ( error )
    787      goto Exit;
    788 
    789    if ( FT_STREAM_READ_FIELDS( maxp_fields, maxProfile ) )
    790      goto Exit;
    791 
    792    maxProfile->maxPoints             = 0;
    793    maxProfile->maxContours           = 0;
    794    maxProfile->maxCompositePoints    = 0;
    795    maxProfile->maxCompositeContours  = 0;
    796    maxProfile->maxZones              = 0;
    797    maxProfile->maxTwilightPoints     = 0;
    798    maxProfile->maxStorage            = 0;
    799    maxProfile->maxFunctionDefs       = 0;
    800    maxProfile->maxInstructionDefs    = 0;
    801    maxProfile->maxStackElements      = 0;
    802    maxProfile->maxSizeOfInstructions = 0;
    803    maxProfile->maxComponentElements  = 0;
    804    maxProfile->maxComponentDepth     = 0;
    805 
    806    if ( maxProfile->version >= 0x10000L )
    807    {
    808      if ( FT_STREAM_READ_FIELDS( maxp_fields_extra, maxProfile ) )
    809        goto Exit;
    810 
    811      /* XXX: an adjustment that is necessary to load certain */
    812      /*      broken fonts like `Keystrokes MT' :-(           */
    813      /*                                                      */
    814      /*   We allocate 64 function entries by default when    */
    815      /*   the maxFunctionDefs value is smaller.              */
    816 
    817      if ( maxProfile->maxFunctionDefs < 64 )
    818        maxProfile->maxFunctionDefs = 64;
    819 
    820      /* we add 4 phantom points later */
    821      if ( maxProfile->maxTwilightPoints > ( 0xFFFFU - 4 ) )
    822      {
    823        FT_TRACE0(( "tt_face_load_maxp:"
    824                    " too much twilight points in `maxp' table;\n" ));
    825        FT_TRACE0(( "                  "
    826                    " some glyphs might be rendered incorrectly\n" ));
    827 
    828        maxProfile->maxTwilightPoints = 0xFFFFU - 4;
    829      }
    830    }
    831 
    832    FT_TRACE3(( "numGlyphs: %hu\n", maxProfile->numGlyphs ));
    833 
    834  Exit:
    835    return error;
    836  }
    837 
    838 
    839  /**************************************************************************
    840   *
    841   * @Function:
    842   *   tt_face_load_name
    843   *
    844   * @Description:
    845   *   Loads the name records.
    846   *
    847   * @Input:
    848   *   face ::
    849   *     A handle to the target face object.
    850   *
    851   *   stream ::
    852   *     The input stream.
    853   *
    854   * @Return:
    855   *   FreeType error code.  0 means success.
    856   */
    857  FT_LOCAL_DEF( FT_Error )
    858  tt_face_load_name( TT_Face    face,
    859                     FT_Stream  stream )
    860  {
    861    FT_Error      error;
    862    FT_Memory     memory = stream->memory;
    863    FT_ULong      table_pos, table_len;
    864    FT_ULong      storage_start, storage_limit;
    865    TT_NameTable  table;
    866    TT_Name       names    = NULL;
    867    TT_LangTag    langTags = NULL;
    868 
    869    static const FT_Frame_Field  name_table_fields[] =
    870    {
    871 #undef  FT_STRUCTURE
    872 #define FT_STRUCTURE  TT_NameTableRec
    873 
    874      FT_FRAME_START( 6 ),
    875        FT_FRAME_USHORT( format ),
    876        FT_FRAME_USHORT( numNameRecords ),
    877        FT_FRAME_USHORT( storageOffset ),
    878      FT_FRAME_END
    879    };
    880 
    881    static const FT_Frame_Field  name_record_fields[] =
    882    {
    883 #undef  FT_STRUCTURE
    884 #define FT_STRUCTURE  TT_NameRec
    885 
    886      /* no FT_FRAME_START */
    887        FT_FRAME_USHORT( platformID ),
    888        FT_FRAME_USHORT( encodingID ),
    889        FT_FRAME_USHORT( languageID ),
    890        FT_FRAME_USHORT( nameID ),
    891        FT_FRAME_USHORT( stringLength ),
    892        FT_FRAME_USHORT( stringOffset ),
    893      FT_FRAME_END
    894    };
    895 
    896    static const FT_Frame_Field  langTag_record_fields[] =
    897    {
    898 #undef  FT_STRUCTURE
    899 #define FT_STRUCTURE  TT_LangTagRec
    900 
    901      /* no FT_FRAME_START */
    902        FT_FRAME_USHORT( stringLength ),
    903        FT_FRAME_USHORT( stringOffset ),
    904      FT_FRAME_END
    905    };
    906 
    907 
    908    table         = &face->name_table;
    909    table->stream = stream;
    910 
    911    error = face->goto_table( face, TTAG_name, stream, &table_len );
    912    if ( error )
    913      goto Exit;
    914 
    915    table_pos = FT_STREAM_POS();
    916 
    917    if ( FT_STREAM_READ_FIELDS( name_table_fields, table ) )
    918      goto Exit;
    919 
    920    /* Some popular Asian fonts have an invalid `storageOffset' value (it */
    921    /* should be at least `6 + 12*numNameRecords').  However, the string  */
    922    /* offsets, computed as `storageOffset + entry->stringOffset', are    */
    923    /* valid pointers within the name table...                            */
    924    /*                                                                    */
    925    /* We thus can't check `storageOffset' right now.                     */
    926    /*                                                                    */
    927    storage_start = table_pos + 6 + 12 * table->numNameRecords;
    928    storage_limit = table_pos + table_len;
    929 
    930    if ( storage_start > storage_limit )
    931    {
    932      FT_ERROR(( "tt_face_load_name: invalid `name' table\n" ));
    933      error = FT_THROW( Name_Table_Missing );
    934      goto Exit;
    935    }
    936 
    937    /* `name' format 1 contains additional language tag records, */
    938    /* which we load first                                       */
    939    if ( table->format == 1 )
    940    {
    941      if ( FT_STREAM_SEEK( storage_start )            ||
    942           FT_READ_USHORT( table->numLangTagRecords ) )
    943        goto Exit;
    944 
    945      storage_start += 2 + 4 * table->numLangTagRecords;
    946 
    947      /* allocate language tag records array */
    948      if ( FT_QNEW_ARRAY( langTags, table->numLangTagRecords ) ||
    949           FT_FRAME_ENTER( table->numLangTagRecords * 4 )      )
    950        goto Exit;
    951 
    952      /* load language tags */
    953      {
    954        TT_LangTag  entry = langTags;
    955        TT_LangTag  limit = FT_OFFSET( entry, table->numLangTagRecords );
    956 
    957 
    958        for ( ; entry < limit; entry++ )
    959        {
    960          (void)FT_STREAM_READ_FIELDS( langTag_record_fields, entry );
    961 
    962          /* check that the langTag string is within the table */
    963          entry->stringOffset += table_pos + table->storageOffset;
    964          if ( entry->stringOffset                       < storage_start ||
    965               entry->stringOffset + entry->stringLength > storage_limit )
    966          {
    967            /* invalid entry; ignore it */
    968            entry->stringLength = 0;
    969          }
    970 
    971          /* mark the string as not yet loaded */
    972          entry->string = NULL;
    973        }
    974 
    975        table->langTags = langTags;
    976        langTags        = NULL;
    977      }
    978 
    979      FT_FRAME_EXIT();
    980 
    981      (void)FT_STREAM_SEEK( table_pos + 6 );
    982    }
    983 
    984    /* allocate name records array */
    985    if ( FT_QNEW_ARRAY( names, table->numNameRecords ) ||
    986         FT_FRAME_ENTER( table->numNameRecords * 12 )  )
    987      goto Exit;
    988 
    989    /* load name records */
    990    {
    991      TT_Name  entry = names;
    992      FT_UInt  count = table->numNameRecords;
    993      FT_UInt  valid = 0;
    994 
    995 
    996      for ( ; count > 0; count-- )
    997      {
    998        if ( FT_STREAM_READ_FIELDS( name_record_fields, entry ) )
    999          continue;
   1000 
   1001        /* check that the name is not empty */
   1002        if ( entry->stringLength == 0 )
   1003          continue;
   1004 
   1005        /* check that the name string is within the table */
   1006        entry->stringOffset += table_pos + table->storageOffset;
   1007        if ( entry->stringOffset                       < storage_start ||
   1008             entry->stringOffset + entry->stringLength > storage_limit )
   1009        {
   1010          /* invalid entry; ignore it */
   1011          continue;
   1012        }
   1013 
   1014        /* assure that we have a valid language tag ID, and   */
   1015        /* that the corresponding langTag entry is valid, too */
   1016        if ( table->format == 1 && entry->languageID >= 0x8000U )
   1017        {
   1018          if ( entry->languageID - 0x8000U >= table->numLangTagRecords    ||
   1019               !table->langTags[entry->languageID - 0x8000U].stringLength )
   1020          {
   1021            /* invalid entry; ignore it */
   1022            continue;
   1023          }
   1024        }
   1025 
   1026        /* mark the string as not yet converted */
   1027        entry->string = NULL;
   1028 
   1029        valid++;
   1030        entry++;
   1031      }
   1032 
   1033      /* reduce array size to the actually used elements */
   1034      FT_MEM_QRENEW_ARRAY( names,
   1035                           table->numNameRecords,
   1036                           valid );
   1037      table->names          = names;
   1038      names                 = NULL;
   1039      table->numNameRecords = valid;
   1040    }
   1041 
   1042    FT_FRAME_EXIT();
   1043 
   1044    /* everything went well, update face->num_names */
   1045    face->num_names = (FT_UShort)table->numNameRecords;
   1046 
   1047  Exit:
   1048    FT_FREE( names );
   1049    FT_FREE( langTags );
   1050    return error;
   1051  }
   1052 
   1053 
   1054  /**************************************************************************
   1055   *
   1056   * @Function:
   1057   *   tt_face_free_name
   1058   *
   1059   * @Description:
   1060   *   Frees the name records.
   1061   *
   1062   * @Input:
   1063   *   face ::
   1064   *     A handle to the target face object.
   1065   */
   1066  FT_LOCAL_DEF( void )
   1067  tt_face_free_name( TT_Face  face )
   1068  {
   1069    FT_Memory     memory = face->root.memory;
   1070    TT_NameTable  table  = &face->name_table;
   1071 
   1072 
   1073    if ( table->names )
   1074    {
   1075      TT_Name  entry = table->names;
   1076      TT_Name  limit = entry + table->numNameRecords;
   1077 
   1078 
   1079      for ( ; entry < limit; entry++ )
   1080        FT_FREE( entry->string );
   1081 
   1082      FT_FREE( table->names );
   1083    }
   1084 
   1085    if ( table->langTags )
   1086    {
   1087      TT_LangTag  entry = table->langTags;
   1088      TT_LangTag  limit = entry + table->numLangTagRecords;
   1089 
   1090 
   1091      for ( ; entry < limit; entry++ )
   1092        FT_FREE( entry->string );
   1093 
   1094      FT_FREE( table->langTags );
   1095    }
   1096 
   1097    table->numNameRecords    = 0;
   1098    table->numLangTagRecords = 0;
   1099    table->format            = 0;
   1100    table->storageOffset     = 0;
   1101  }
   1102 
   1103 
   1104  /**************************************************************************
   1105   *
   1106   * @Function:
   1107   *   tt_face_load_cmap
   1108   *
   1109   * @Description:
   1110   *   Loads the cmap directory in a face object.  The cmaps themselves
   1111   *   are loaded on demand in the `ttcmap.c' module.
   1112   *
   1113   * @Input:
   1114   *   face ::
   1115   *     A handle to the target face object.
   1116   *
   1117   *   stream ::
   1118   *     A handle to the input stream.
   1119   *
   1120   * @Return:
   1121   *   FreeType error code.  0 means success.
   1122   */
   1123 
   1124  FT_LOCAL_DEF( FT_Error )
   1125  tt_face_load_cmap( TT_Face    face,
   1126                     FT_Stream  stream )
   1127  {
   1128    FT_Error  error;
   1129 
   1130 
   1131    error = face->goto_table( face, TTAG_cmap, stream, &face->cmap_size );
   1132    if ( error )
   1133      goto Exit;
   1134 
   1135    if ( FT_FRAME_EXTRACT( face->cmap_size, face->cmap_table ) )
   1136      face->cmap_size = 0;
   1137 
   1138  Exit:
   1139    return error;
   1140  }
   1141 
   1142 
   1143 
   1144  /**************************************************************************
   1145   *
   1146   * @Function:
   1147   *   tt_face_load_os2
   1148   *
   1149   * @Description:
   1150   *   Loads the OS2 table.
   1151   *
   1152   * @Input:
   1153   *   face ::
   1154   *     A handle to the target face object.
   1155   *
   1156   *   stream ::
   1157   *     A handle to the input stream.
   1158   *
   1159   * @Return:
   1160   *   FreeType error code.  0 means success.
   1161   */
   1162  FT_LOCAL_DEF( FT_Error )
   1163  tt_face_load_os2( TT_Face    face,
   1164                    FT_Stream  stream )
   1165  {
   1166    FT_Error  error;
   1167    TT_OS2*   os2;
   1168 
   1169    static const FT_Frame_Field  os2_fields[] =
   1170    {
   1171 #undef  FT_STRUCTURE
   1172 #define FT_STRUCTURE  TT_OS2
   1173 
   1174      FT_FRAME_START( 78 ),
   1175        FT_FRAME_USHORT( version ),
   1176        FT_FRAME_SHORT ( xAvgCharWidth ),
   1177        FT_FRAME_USHORT( usWeightClass ),
   1178        FT_FRAME_USHORT( usWidthClass ),
   1179        FT_FRAME_SHORT ( fsType ),
   1180        FT_FRAME_SHORT ( ySubscriptXSize ),
   1181        FT_FRAME_SHORT ( ySubscriptYSize ),
   1182        FT_FRAME_SHORT ( ySubscriptXOffset ),
   1183        FT_FRAME_SHORT ( ySubscriptYOffset ),
   1184        FT_FRAME_SHORT ( ySuperscriptXSize ),
   1185        FT_FRAME_SHORT ( ySuperscriptYSize ),
   1186        FT_FRAME_SHORT ( ySuperscriptXOffset ),
   1187        FT_FRAME_SHORT ( ySuperscriptYOffset ),
   1188        FT_FRAME_SHORT ( yStrikeoutSize ),
   1189        FT_FRAME_SHORT ( yStrikeoutPosition ),
   1190        FT_FRAME_SHORT ( sFamilyClass ),
   1191        FT_FRAME_BYTE  ( panose[0] ),
   1192        FT_FRAME_BYTE  ( panose[1] ),
   1193        FT_FRAME_BYTE  ( panose[2] ),
   1194        FT_FRAME_BYTE  ( panose[3] ),
   1195        FT_FRAME_BYTE  ( panose[4] ),
   1196        FT_FRAME_BYTE  ( panose[5] ),
   1197        FT_FRAME_BYTE  ( panose[6] ),
   1198        FT_FRAME_BYTE  ( panose[7] ),
   1199        FT_FRAME_BYTE  ( panose[8] ),
   1200        FT_FRAME_BYTE  ( panose[9] ),
   1201        FT_FRAME_ULONG ( ulUnicodeRange1 ),
   1202        FT_FRAME_ULONG ( ulUnicodeRange2 ),
   1203        FT_FRAME_ULONG ( ulUnicodeRange3 ),
   1204        FT_FRAME_ULONG ( ulUnicodeRange4 ),
   1205        FT_FRAME_BYTE  ( achVendID[0] ),
   1206        FT_FRAME_BYTE  ( achVendID[1] ),
   1207        FT_FRAME_BYTE  ( achVendID[2] ),
   1208        FT_FRAME_BYTE  ( achVendID[3] ),
   1209 
   1210        FT_FRAME_USHORT( fsSelection ),
   1211        FT_FRAME_USHORT( usFirstCharIndex ),
   1212        FT_FRAME_USHORT( usLastCharIndex ),
   1213        FT_FRAME_SHORT ( sTypoAscender ),
   1214        FT_FRAME_SHORT ( sTypoDescender ),
   1215        FT_FRAME_SHORT ( sTypoLineGap ),
   1216        FT_FRAME_USHORT( usWinAscent ),
   1217        FT_FRAME_USHORT( usWinDescent ),
   1218      FT_FRAME_END
   1219    };
   1220 
   1221    /* `OS/2' version 1 and newer */
   1222    static const FT_Frame_Field  os2_fields_extra1[] =
   1223    {
   1224      FT_FRAME_START( 8 ),
   1225        FT_FRAME_ULONG( ulCodePageRange1 ),
   1226        FT_FRAME_ULONG( ulCodePageRange2 ),
   1227      FT_FRAME_END
   1228    };
   1229 
   1230    /* `OS/2' version 2 and newer */
   1231    static const FT_Frame_Field  os2_fields_extra2[] =
   1232    {
   1233      FT_FRAME_START( 10 ),
   1234        FT_FRAME_SHORT ( sxHeight ),
   1235        FT_FRAME_SHORT ( sCapHeight ),
   1236        FT_FRAME_USHORT( usDefaultChar ),
   1237        FT_FRAME_USHORT( usBreakChar ),
   1238        FT_FRAME_USHORT( usMaxContext ),
   1239      FT_FRAME_END
   1240    };
   1241 
   1242    /* `OS/2' version 5 and newer */
   1243    static const FT_Frame_Field  os2_fields_extra5[] =
   1244    {
   1245      FT_FRAME_START( 4 ),
   1246        FT_FRAME_USHORT( usLowerOpticalPointSize ),
   1247        FT_FRAME_USHORT( usUpperOpticalPointSize ),
   1248      FT_FRAME_END
   1249    };
   1250 
   1251 
   1252    /* We now support old Mac fonts where the OS/2 table doesn't  */
   1253    /* exist.  Simply put, we set the `version' field to 0xFFFF   */
   1254    /* and test this value each time we need to access the table. */
   1255    error = face->goto_table( face, TTAG_OS2, stream, 0 );
   1256    if ( error )
   1257      goto Exit;
   1258 
   1259    os2 = &face->os2;
   1260 
   1261    if ( FT_STREAM_READ_FIELDS( os2_fields, os2 ) )
   1262      goto Exit;
   1263 
   1264    os2->ulCodePageRange1        = 0;
   1265    os2->ulCodePageRange2        = 0;
   1266    os2->sxHeight                = 0;
   1267    os2->sCapHeight              = 0;
   1268    os2->usDefaultChar           = 0;
   1269    os2->usBreakChar             = 0;
   1270    os2->usMaxContext            = 0;
   1271    os2->usLowerOpticalPointSize = 0;
   1272    os2->usUpperOpticalPointSize = 0xFFFF;
   1273 
   1274    if ( os2->version >= 0x0001 )
   1275    {
   1276      /* only version 1 tables */
   1277      if ( FT_STREAM_READ_FIELDS( os2_fields_extra1, os2 ) )
   1278        goto Exit;
   1279 
   1280      if ( os2->version >= 0x0002 )
   1281      {
   1282        /* only version 2 tables */
   1283        if ( FT_STREAM_READ_FIELDS( os2_fields_extra2, os2 ) )
   1284          goto Exit;
   1285 
   1286        if ( os2->version >= 0x0005 )
   1287        {
   1288          /* only version 5 tables */
   1289          if ( FT_STREAM_READ_FIELDS( os2_fields_extra5, os2 ) )
   1290            goto Exit;
   1291        }
   1292      }
   1293    }
   1294 
   1295    FT_TRACE3(( "sTypoAscender:  %4hd\n",   os2->sTypoAscender ));
   1296    FT_TRACE3(( "sTypoDescender: %4hd\n",   os2->sTypoDescender ));
   1297    FT_TRACE3(( "usWinAscent:    %4hu\n",   os2->usWinAscent ));
   1298    FT_TRACE3(( "usWinDescent:   %4hu\n",   os2->usWinDescent ));
   1299    FT_TRACE3(( "fsSelection:    0x%2hx\n", os2->fsSelection ));
   1300 
   1301  Exit:
   1302    return error;
   1303  }
   1304 
   1305 
   1306  /**************************************************************************
   1307   *
   1308   * @Function:
   1309   *   tt_face_load_postscript
   1310   *
   1311   * @Description:
   1312   *   Loads the Postscript table.
   1313   *
   1314   * @Input:
   1315   *   face ::
   1316   *     A handle to the target face object.
   1317   *
   1318   *   stream ::
   1319   *     A handle to the input stream.
   1320   *
   1321   * @Return:
   1322   *   FreeType error code.  0 means success.
   1323   */
   1324  FT_LOCAL_DEF( FT_Error )
   1325  tt_face_load_post( TT_Face    face,
   1326                     FT_Stream  stream )
   1327  {
   1328    FT_Error        error;
   1329    TT_Postscript*  post = &face->postscript;
   1330 
   1331    static const FT_Frame_Field  post_fields[] =
   1332    {
   1333 #undef  FT_STRUCTURE
   1334 #define FT_STRUCTURE  TT_Postscript
   1335 
   1336      FT_FRAME_START( 32 ),
   1337        FT_FRAME_LONG ( FormatType ),
   1338        FT_FRAME_LONG ( italicAngle ),
   1339        FT_FRAME_SHORT( underlinePosition ),
   1340        FT_FRAME_SHORT( underlineThickness ),
   1341        FT_FRAME_ULONG( isFixedPitch ),
   1342        FT_FRAME_ULONG( minMemType42 ),
   1343        FT_FRAME_ULONG( maxMemType42 ),
   1344        FT_FRAME_ULONG( minMemType1 ),
   1345        FT_FRAME_ULONG( maxMemType1 ),
   1346      FT_FRAME_END
   1347    };
   1348 
   1349 
   1350    error = face->goto_table( face, TTAG_post, stream, 0 );
   1351    if ( error )
   1352      return error;
   1353 
   1354    if ( FT_STREAM_READ_FIELDS( post_fields, post ) )
   1355      return error;
   1356 
   1357    if ( post->FormatType != 0x00030000L &&
   1358         post->FormatType != 0x00025000L &&
   1359         post->FormatType != 0x00020000L &&
   1360         post->FormatType != 0x00010000L )
   1361      return FT_THROW( Invalid_Post_Table_Format );
   1362 
   1363    /* we don't load the glyph names, we do that in another */
   1364    /* module (ttpost).                                     */
   1365 
   1366    FT_TRACE3(( "FormatType:   0x%lx\n", post->FormatType ));
   1367    FT_TRACE3(( "isFixedPitch:   %s\n", post->isFixedPitch
   1368                                        ? "  yes" : "   no" ));
   1369 
   1370    return FT_Err_Ok;
   1371  }
   1372 
   1373 
   1374  /**************************************************************************
   1375   *
   1376   * @Function:
   1377   *   tt_face_load_pclt
   1378   *
   1379   * @Description:
   1380   *   Loads the PCL 5 Table.
   1381   *
   1382   * @Input:
   1383   *   face ::
   1384   *     A handle to the target face object.
   1385   *
   1386   *   stream ::
   1387   *     A handle to the input stream.
   1388   *
   1389   * @Return:
   1390   *   FreeType error code.  0 means success.
   1391   */
   1392  FT_LOCAL_DEF( FT_Error )
   1393  tt_face_load_pclt( TT_Face    face,
   1394                     FT_Stream  stream )
   1395  {
   1396    static const FT_Frame_Field  pclt_fields[] =
   1397    {
   1398 #undef  FT_STRUCTURE
   1399 #define FT_STRUCTURE  TT_PCLT
   1400 
   1401      FT_FRAME_START( 54 ),
   1402        FT_FRAME_ULONG ( Version ),
   1403        FT_FRAME_ULONG ( FontNumber ),
   1404        FT_FRAME_USHORT( Pitch ),
   1405        FT_FRAME_USHORT( xHeight ),
   1406        FT_FRAME_USHORT( Style ),
   1407        FT_FRAME_USHORT( TypeFamily ),
   1408        FT_FRAME_USHORT( CapHeight ),
   1409        FT_FRAME_USHORT( SymbolSet ),
   1410        FT_FRAME_BYTES ( TypeFace, 16 ),
   1411        FT_FRAME_BYTES ( CharacterComplement, 8 ),
   1412        FT_FRAME_BYTES ( FileName, 6 ),
   1413        FT_FRAME_CHAR  ( StrokeWeight ),
   1414        FT_FRAME_CHAR  ( WidthType ),
   1415        FT_FRAME_BYTE  ( SerifStyle ),
   1416        FT_FRAME_BYTE  ( Reserved ),
   1417      FT_FRAME_END
   1418    };
   1419 
   1420    FT_Error  error;
   1421    TT_PCLT*  pclt = &face->pclt;
   1422 
   1423 
   1424    /* optional table */
   1425    error = face->goto_table( face, TTAG_PCLT, stream, 0 );
   1426    if ( error )
   1427      goto Exit;
   1428 
   1429    if ( FT_STREAM_READ_FIELDS( pclt_fields, pclt ) )
   1430      goto Exit;
   1431 
   1432  Exit:
   1433    return error;
   1434  }
   1435 
   1436 
   1437  /**************************************************************************
   1438   *
   1439   * @Function:
   1440   *   tt_face_load_gasp
   1441   *
   1442   * @Description:
   1443   *   Loads the `gasp' table into a face object.
   1444   *
   1445   * @Input:
   1446   *   face ::
   1447   *     A handle to the target face object.
   1448   *
   1449   *   stream ::
   1450   *     The input stream.
   1451   *
   1452   * @Return:
   1453   *   FreeType error code.  0 means success.
   1454   */
   1455  FT_LOCAL_DEF( FT_Error )
   1456  tt_face_load_gasp( TT_Face    face,
   1457                     FT_Stream  stream )
   1458  {
   1459    FT_Error   error;
   1460    FT_Memory  memory = stream->memory;
   1461 
   1462    FT_UShort      j, num_ranges;
   1463    TT_GaspRange   gasp_ranges = NULL;
   1464 
   1465 
   1466    /* the gasp table is optional */
   1467    error = face->goto_table( face, TTAG_gasp, stream, 0 );
   1468    if ( error )
   1469      goto Exit;
   1470 
   1471    if ( FT_FRAME_ENTER( 4L ) )
   1472      goto Exit;
   1473 
   1474    face->gasp.version = FT_GET_USHORT();
   1475    num_ranges         = FT_GET_USHORT();
   1476 
   1477    FT_FRAME_EXIT();
   1478 
   1479    /* only support versions 0 and 1 of the table */
   1480    if ( face->gasp.version >= 2 )
   1481    {
   1482      face->gasp.numRanges = 0;
   1483      error = FT_THROW( Invalid_Table );
   1484      goto Exit;
   1485    }
   1486 
   1487    FT_TRACE3(( "numRanges: %hu\n", num_ranges ));
   1488 
   1489    if ( FT_QNEW_ARRAY( gasp_ranges, num_ranges ) ||
   1490         FT_FRAME_ENTER( num_ranges * 4L )        )
   1491      goto Exit;
   1492 
   1493    for ( j = 0; j < num_ranges; j++ )
   1494    {
   1495      gasp_ranges[j].maxPPEM  = FT_GET_USHORT();
   1496      gasp_ranges[j].gaspFlag = FT_GET_USHORT();
   1497 
   1498      FT_TRACE3(( "gaspRange %hu: rangeMaxPPEM %5hu, rangeGaspBehavior 0x%hx\n",
   1499                  j,
   1500                  gasp_ranges[j].maxPPEM,
   1501                  gasp_ranges[j].gaspFlag ));
   1502    }
   1503 
   1504    face->gasp.gaspRanges = gasp_ranges;
   1505    gasp_ranges           = NULL;
   1506    face->gasp.numRanges  = num_ranges;
   1507 
   1508    FT_FRAME_EXIT();
   1509 
   1510  Exit:
   1511    FT_FREE( gasp_ranges );
   1512    return error;
   1513  }
   1514 
   1515 
   1516 /* END */