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