shithub: freetype+ttf2subf

ref: a3b6c6c2906c04b73a80674b3f75c3e21e7f21c6
dir: /src/sfnt/ttload.c/

View raw version
/***************************************************************************/
/*                                                                         */
/*  ttload.c                                                               */
/*                                                                         */
/*    TrueType tables loader (body).                                       */
/*                                                                         */
/*  Copyright 1996-1999 by                                                 */
/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
/*                                                                         */
/*  This file is part of the FreeType project, and may only be used        */
/*  modified and distributed under the terms of the FreeType project       */
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
/*  this file you indicate that you have read the license and              */
/*  understand and accept it fully.                                        */
/*                                                                         */
/*                                                                         */
/*  WARNING: This file should not be compiled directly; it is meant to be  */
/*           included in the source of several font drivers (i.e., the TTF */
/*           and OTF drivers).                                             */
/*                                                                         */
/***************************************************************************/


#include <freetype/internal/ftdebug.h>
#include <freetype/internal/tterrors.h>
#include <freetype/tttags.h>

#include <ttload.h>
#include <ttcmap.h>

/* required by the tracing mode */
#undef  FT_COMPONENT
#define FT_COMPONENT      trace_ttload

#define READ_FIELDS


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    TT_LookUp_Table                                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Looks for a TrueType table by name.                                */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face :: A face object handle.                                      */
  /*    tag  :: The  searched tag.                                         */
  /*                                                                       */
  /* <Return>                                                              */
  /*    pointer to table directory entry. 0 if not found..                 */
  /*                                                                       */
  LOCAL_FUNC
  TT_Table*  TT_LookUp_Table( TT_Face   face,
                              TT_ULong  tag  )
  {
    TT_Table*  entry;
    TT_Table*  limit;

    FT_TRACE4(( "TT_LookUp_Table( %08p, %c%c%c%c )\n",
                  face,
                  (TT_Char)(tag >> 24),
                  (TT_Char)(tag >> 16),
                  (TT_Char)(tag >> 8),
                  (TT_Char)(tag) ));

    entry = face->dir_tables;
    limit = entry + face->num_tables;
    for ( ; entry < limit; entry++ )
    {
      if ( entry->Tag == tag )
        return entry;
    }
    FT_TRACE4(( "    Could not find table!\n" ));
    return 0;
  }

  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    TT_Goto_Table                                                      */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Looks for a TrueType table by name, then seek a stream to it.      */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face   :: a face object handle.                                    */
  /*    tag    :: the  searched tag.                                       */
  /*    stream :: the stream to seek when the table is found               */
  /*                                                                       */
  /* <Return>                                                              */
  /*    pointer to table directory entry. 0 if not found..                 */
  /*                                                                       */
  LOCAL_FUNC
  TT_Error   TT_Goto_Table( TT_Face    face,
                            TT_ULong   tag,
                            FT_Stream  stream,
                            TT_ULong  *length )
  {
    TT_Table*  table;
    TT_Error   error;

    table = TT_LookUp_Table( face, tag );
    if (table)
    {
      if (length)
        *length = table->Length;

      (void)FILE_Seek( table->Offset );
    }
    else
      error = TT_Err_Table_Missing;

    return error;
  }


  /*************************************************************************/
  /*                                                                       */
  /* <FuncType>                                                            */
  /*    TT_Load_SFNT_Header                                                */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Loads the header of a SFNT font file. Supports collections..       */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face      :: A handle to the target face object.                   */
  /*    stream    :: The input stream.                                     */
  /*                                                                       */
  /* <Output>                                                              */
  /*    sfnt      :: the sfnt header                                       */
  /*                                                                       */
  /* <Return>                                                              */
  /*    TrueType error code.  0 means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The stream cursor must be at the font file's origin                */
  /*    This function recognizes fonts embedded in a "TrueType collection" */
  /*                                                                       */
  /*    This function checks that the header is valid by looking at the    */
  /*    values of "search_range", "entry_selector" and "range_shift"..     */
  /*                                                                       */
  LOCAL_FUNC
  TT_Error   TT_Load_SFNT_Header( TT_Face       face,
                                  FT_Stream     stream,
                                  TT_Long       face_index,
                                  SFNT_Header*  sfnt )
  {
    TT_Error  error;
    TT_ULong  format_tag;
    FT_Memory memory = stream->memory;
    
    const FT_Frame_Field  sfnt_header_fields[] = {
                  FT_FRAME_START(8),
                    FT_FRAME_USHORT( SFNT_Header, num_tables ),
                    FT_FRAME_USHORT( SFNT_Header, search_range ),
                    FT_FRAME_USHORT( SFNT_Header, entry_selector ),
                    FT_FRAME_USHORT( SFNT_Header, range_shift ),
                  FT_FRAME_END };
                  
    const FT_Frame_Field  ttc_header_fields[] = {
           FT_FRAME_START(8),  /* frame of 8 bytes */
             FT_FRAME_LONG( TTC_Header, version  ),
             FT_FRAME_LONG( TTC_Header, DirCount ),
           FT_FRAME_END };

    FT_TRACE2(( "TT_Load_SFNT_Header(%08p, %ld )\n",
                face, faceIndex ));

    face->ttc_header.Tag      = 0;
    face->ttc_header.version  = 0;
    face->ttc_header.DirCount = 0;

    face->num_tables = 0;

    /* first of all, read the  first 4 bytes. If it's `ttcf', then the */
    /* file is a TrueType collection, otherwise it can be any other    */
    /* kind of font..                                                  */
    if ( READ_ULong(format_tag) ) goto Exit;

    if ( format_tag == TTAG_ttcf )
    {
      TT_Int  n;

      FT_TRACE4(( "TT_Load_SFNT_Header: file is a collection\n" ));

      /* it's a TrueType collection, i.e. a file containing several */
      /* font files. Read the font directory now                    */
      /*                                                            */
      if ( READ_Fields( ttc_header_fields, &face->ttc_header ) )
        goto Exit;

      /* now read the offsets of each font in the file         */
      /*                                                       */
      if ( ALLOC_ARRAY( face->ttc_header.TableDirectory,
                        face->ttc_header.DirCount,
                        TT_ULong )                        ||
           ACCESS_Frame( face->ttc_header.DirCount * 4L ) )
        goto Exit;

      for ( n = 0; n < face->ttc_header.DirCount; n++ )
        face->ttc_header.TableDirectory[n] = GET_ULong();

      FORGET_Frame();

      /* check face index */
      if (face_index >= face->ttc_header.DirCount)
      {
        error = TT_Err_Bad_Argument;
        goto Exit;
      }

      /* seek to the appropriate TrueType file, then read tag */
      if ( FILE_Seek( face->ttc_header.TableDirectory[face_index] ) ||
           READ_Long( format_tag )                                  )
        goto Exit;
    }

    /* the format tag was read, now check the rest of the header */
    sfnt->format_tag = format_tag;
    if ( READ_Fields( sfnt_header_fields, sfnt ) )
      goto Exit;
      
    /* now, check the values of "num_tables", "seach_range", etc.. */
    {
      TT_UInt  num_tables     = sfnt->num_tables;
      TT_UInt  search_range   = sfnt->search_range;
      TT_ULong entry_selector = 1L << sfnt->entry_selector;
      
      if ( entry_selector    > num_tables || entry_selector*2 <= num_tables ||
           search_range  != 16*entry_selector ||
           num_tables*16 != search_range + sfnt->range_shift )
      {           
        FT_TRACE2(( "TT_Load_SFNT_Header: file is not SFNT !\n" ));
        error = FT_Err_Unknown_File_Format;
      }
    }

  Exit:
    return error;
  }                                  



  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    TT_Load_Directory                                                  */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Loads the table directory into a face object.                      */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face      :: A handle to the target face object.                   */
  /*    stream    :: The input stream.                                     */
  /*    sfnt      :: sfnt directory header                                 */
  /*                                                                       */
  /* <Return>                                                              */
  /*    TrueType error code.  0 means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The stream cursor must be at the font file's origin                */
  /*                                                                       */
  LOCAL_FUNC
  TT_Error  TT_Load_Directory( TT_Face       face,
                               FT_Stream     stream,
                               SFNT_Header*  sfnt )
  {
    TT_Error   error;
    FT_Memory  memory = stream->memory;

    TT_Table *entry, *limit;

    FT_TRACE2(( "TT_Load_Directory( %08p, %ld )\n",
                face, faceIndex ));

    FT_TRACE2(( "-- Tables count   : %12u\n",  sfnt->num_tables ));
    FT_TRACE2(( "-- Format version : %08lx\n", sfnt->format_tag ));

    face->num_tables = sfnt->num_tables;

    if ( ALLOC_ARRAY( face->dir_tables,
                      face->num_tables,
                      TT_Table ) )
      goto Exit;

    if ( ACCESS_Frame( face->num_tables * 16L ) )
      goto Exit;

    entry = face->dir_tables;
    limit = entry + face->num_tables;

    for ( ; entry < limit; entry++ )
    {                    /* loop through the tables and get all entries */
      entry->Tag      = GET_Tag4();
      entry->CheckSum = GET_ULong();
      entry->Offset   = GET_Long();
      entry->Length   = GET_Long();

      FT_TRACE2(( "  %c%c%c%c  -  %08lx  -  %08lx\n",
                (TT_Char)(entry->Tag >> 24),
                (TT_Char)(entry->Tag >> 16),
                (TT_Char)(entry->Tag >> 8 ),
                (TT_Char)(entry->Tag),
                entry->Offset,
                entry->Length ));
    }

    FORGET_Frame();

    FT_TRACE2(( "Directory loaded\n\n" ));

  Exit:
    return error;
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    TT_Load_Any                                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Loads any font table into client memory.  Used by the              */
  /*    TT_Get_Font_Data() API function.                                   */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face   :: The face object to look for.                             */
  /*                                                                       */
  /*    tag    :: The tag of table to load.  Use the value 0 if you  want  */
  /*              to access the whole font file, else set this parameter   */
  /*              to a valid TrueType table tag that you can forge with    */
  /*              the MAKE_TT_TAG macro.                                   */
  /*                                                                       */
  /*    offset :: The starting offset in the table (or the file if         */
  /*              tag == 0).                                               */
  /*                                                                       */
  /*    length :: The address of the decision variable:                    */
  /*                                                                       */
  /*                If length == NULL:                                     */
  /*                  Loads the whole table.  Returns an error if          */
  /*                  `offset' == 0!                                       */
  /*                                                                       */
  /*                If *length == 0:                                       */
  /*                  Exits immediately; returning the length of the given */
  /*                  table or of the font file, depending on the value of */
  /*                  `tag'.                                               */
  /*                                                                       */
  /*                If *length != 0:                                       */
  /*                  Loads the next `length' bytes of table or font,      */
  /*                  starting at offset `offset' (in table or font too).  */
  /*                                                                       */
  /* <Output>                                                              */
  /*    buffer :: The address of target buffer.                            */
  /*                                                                       */
  /* <Return>                                                              */
  /*    TrueType error code.  0 means success.                             */
  /*                                                                       */
  LOCAL_FUNC
  TT_Error  TT_Load_Any( TT_Face   face,
                         TT_ULong  tag,
                         TT_Long   offset,
                         void*     buffer,
                         TT_Long*  length )
  {
    TT_Error   error;
    FT_Stream  stream;
    TT_Table*  table;
    TT_ULong   size;

    if ( tag != 0 )
    {
      /* look for tag in font directory */
      table = TT_LookUp_Table( face, tag );
      if ( !table )
      {
        error = TT_Err_Table_Missing;
        goto Exit;
      }

      offset += table->Offset;
      size    = table->Length;
    }
    else
    /* tag = 0 -- the use want to access the font file directly */
    {
      size = face->root.stream->size;
    }

    if ( length && *length == 0 )
    {
      *length = size;
      return TT_Err_Ok;
    }

    if ( length )
      size = *length;

    stream = face->root.stream;
    (void)FILE_Read_At( offset, buffer, size );

  Exit:
    return error;
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    TT_Load_Header                                                     */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Loads the TrueType font header.                                    */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face   :: A handle to the target face object.                      */
  /*    stream :: The input stream.                                        */
  /*                                                                       */
  /* <Return>                                                              */
  /*    TrueType error code.  0 means success.                             */
  /*                                                                       */
  LOCAL_FUNC
  TT_Error  TT_Load_Header( TT_Face    face,
                            FT_Stream  stream )
  {
    TT_Error    error;
    TT_Header*  header;

    static const FT_Frame_Field  header_fields[] = {
            FT_FRAME_START(54),
              FT_FRAME_ULONG(  TT_Header, Table_Version ),
              FT_FRAME_ULONG(  TT_Header, Font_Revision ),
              FT_FRAME_LONG(   TT_Header, CheckSum_Adjust ),
              FT_FRAME_LONG(   TT_Header, Magic_Number ),
              FT_FRAME_USHORT( TT_Header, Flags ),
              FT_FRAME_USHORT( TT_Header, Units_Per_EM ),
              FT_FRAME_LONG(   TT_Header, Created[0] ),
              FT_FRAME_LONG(   TT_Header, Created[1] ),
              FT_FRAME_LONG(   TT_Header, Modified[0] ),
              FT_FRAME_LONG(   TT_Header, Modified[1] ),
              FT_FRAME_SHORT(  TT_Header, xMin ),
              FT_FRAME_SHORT(  TT_Header, yMin ),
              FT_FRAME_SHORT(  TT_Header, xMax ),
              FT_FRAME_SHORT(  TT_Header, yMax ),
              FT_FRAME_USHORT( TT_Header, Mac_Style ),
              FT_FRAME_USHORT( TT_Header, Lowest_Rec_PPEM ),
              FT_FRAME_SHORT(  TT_Header, Font_Direction ),
              FT_FRAME_SHORT(  TT_Header, Index_To_Loc_Format ),
              FT_FRAME_SHORT(  TT_Header, Glyph_Data_Format ),
            FT_FRAME_END };

    FT_TRACE2(( "Load_TT_Header( %08p )\n", face ));

    error = face->goto_table( face, TTAG_head, stream, 0 );
    if ( error )
    {
      FT_TRACE0(( "Font Header is missing!\n" ));
      goto Exit;
    }

    header = &face->header;

    if ( READ_Fields( header_fields, header ) )
      goto Exit;

    FT_TRACE2(( "    Units per EM : %8u\n", header->Units_Per_EM ));
    FT_TRACE2(( "    IndexToLoc   : %8d\n", header->Index_To_Loc_Format ));
    FT_TRACE2(( "Font Header Loaded.\n" ));

  Exit:
    return error;
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    TT_Load_MaxProfile                                                 */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Loads the maximum profile into a face object.                      */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face   :: A handle to the target face object.                      */
  /*    stream :: The input stream.                                        */
  /*                                                                       */
  /* <Return>                                                              */
  /*    TrueType error code.  0 means success.                             */
  /*                                                                       */
  LOCAL_FUNC
  TT_Error  TT_Load_MaxProfile( TT_Face    face,
                                FT_Stream  stream )
  {
    TT_Error        error;
    TT_MaxProfile*  maxProfile = &face->max_profile;

    const FT_Frame_Field  maxp_fields[] = {
              FT_FRAME_START(32),
                FT_FRAME_ULONG(  TT_MaxProfile, version ),
                FT_FRAME_USHORT( TT_MaxProfile, numGlyphs ),
                FT_FRAME_USHORT( TT_MaxProfile, maxPoints ),
                FT_FRAME_USHORT( TT_MaxProfile, maxContours ),
                FT_FRAME_USHORT( TT_MaxProfile, maxCompositePoints ),
                FT_FRAME_USHORT( TT_MaxProfile, maxCompositeContours ),
                FT_FRAME_USHORT( TT_MaxProfile, maxZones ),
                FT_FRAME_USHORT( TT_MaxProfile, maxTwilightPoints ),
                FT_FRAME_USHORT( TT_MaxProfile, maxStorage ),
                FT_FRAME_USHORT( TT_MaxProfile, maxFunctionDefs ),
                FT_FRAME_USHORT( TT_MaxProfile, maxInstructionDefs ),
                FT_FRAME_USHORT( TT_MaxProfile, maxStackElements ),
                FT_FRAME_USHORT( TT_MaxProfile, maxSizeOfInstructions ),
                FT_FRAME_USHORT( TT_MaxProfile, maxComponentElements ),
                FT_FRAME_USHORT( TT_MaxProfile, maxComponentDepth ),
              FT_FRAME_END };

    FT_TRACE2(( "Load_TT_MaxProfile( %08p )\n", face ));

    error = face->goto_table( face, TTAG_maxp, stream, 0 );
    if (error) goto Exit;

    if ( READ_Fields( maxp_fields, maxProfile ) ) goto Exit;

    /* XXX: an adjustment that is necessary to load certain */
    /*       broken fonts like `Keystrokes MT' :-(          */
    /*                                                      */
    /*   We allocate 64 function entries by default when    */
    /*   the maxFunctionDefs field is null.                 */

    if ( maxProfile->maxFunctionDefs == 0 )
      maxProfile->maxFunctionDefs = 64;

    face->root.num_glyphs = maxProfile->numGlyphs;

    face->root.max_points = MAX( maxProfile->maxCompositePoints,
                                 maxProfile->maxPoints );

    face->root.max_contours = MAX( maxProfile->maxCompositeContours,
                                   maxProfile->maxContours );

    face->max_components = (TT_ULong)maxProfile->maxComponentElements +
                           maxProfile->maxComponentDepth;

    /* XXX: some fonts have maxComponents set to 0; we will */
    /*      then use 16 of them by default.                 */
    if ( face->max_components == 0 )
      face->max_components = 16;

    /* We also increase maxPoints and maxContours in order to support */
    /* some broken fonts.                                             */
    face->root.max_points   += 8;
    face->root.max_contours += 4;

    FT_TRACE2(( "MAXP loaded.\n" ));
  Exit:
    return error;
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    TT_Load_Metrics                                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Loads the horizontal or vertical metrics table into a face object. */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face     :: A handle to the target face object.                    */
  /*    stream   :: The input stream.                                      */
  /*    vertical :: A boolean flag.  If set, load vertical metrics.        */
  /*                                                                       */
  /* <Return>                                                              */
  /*    TrueType error code.  0 means success.                             */
  /*                                                                       */
  static
  TT_Error  TT_Load_Metrics( TT_Face    face,
                             FT_Stream  stream,
                             TT_Bool    vertical )
  {
    TT_Error   error;
    FT_Memory  memory = stream->memory;

    TT_ULong   table_len;
    TT_Long    num_shorts, num_longs, num_shorts_checked;

    TT_LongMetrics**   longs;
    TT_ShortMetrics**  shorts;

    FT_TRACE2(( "TT_Load_%s_Metrics( %08p )\n",
              vertical ? "Vertical" : "Horizontal", face ));

    if ( vertical )
    {
      /* The table is optional, quit silently if it wasn't found       */
      /* XXX: Some fonts have a valid vertical header with a non-null  */
      /*      `number_of_VMetrics' fields, but no corresponding `vmtx' */
      /*      table to get the metrics from (e.g. mingliu).            */
      /*                                                               */
      /*      For safety, we set the field to 0!                       */
      /*                                                               */
      error = face->goto_table( face, TTAG_vmtx, stream, &table_len );
      if ( error )
      {
        /* Set the number_Of_VMetrics to 0! */
        FT_TRACE2(( "  no vertical header in file.\n" ));
        face->vertical.number_Of_VMetrics = 0;
        error = TT_Err_Ok;
        goto Exit;
      }

      num_longs = face->vertical.number_Of_VMetrics;
      longs     = (TT_LongMetrics**)&face->vertical.long_metrics;
      shorts    = (TT_ShortMetrics**)&face->vertical.short_metrics;
    }
    else
    {
      error = face->goto_table( face, TTAG_hmtx, stream, &table_len );
      if (error)
      {
        FT_ERROR(( " no horizontal metrics in file!\n" ));
        error = TT_Err_Hmtx_Table_Missing;
        goto Exit;
      }

      num_longs = face->horizontal.number_Of_HMetrics;
      longs     = (TT_LongMetrics**)&face->horizontal.long_metrics;
      shorts    = (TT_ShortMetrics**)&face->horizontal.short_metrics;
    }

    /* never trust derived values */

    num_shorts         = face->max_profile.numGlyphs - num_longs;
    num_shorts_checked = ( table_len - num_longs*4L )/2;

    if ( num_shorts < 0 )
    {
      FT_ERROR(( "!! more metrics than glyphs!\n" ));
      error = ( vertical ? TT_Err_Invalid_Vert_Metrics
                         : TT_Err_Invalid_Horiz_Metrics );
      goto Exit;
    }

    if ( ALLOC_ARRAY( *longs,  num_longs,  TT_LongMetrics ) ||
         ALLOC_ARRAY( *shorts, num_shorts, TT_ShortMetrics ) )
      goto Exit;

    if ( ACCESS_Frame( table_len ) )
      goto Exit;

    {
      TT_LongMetrics*  cur   = *longs;
      TT_LongMetrics*  limit = cur + num_longs;

      for ( ; cur < limit; cur++ )
      {
        cur->advance = GET_UShort();
        cur->bearing = GET_Short();
      }
    }

    /* do we have an inconsistent number of metric values ? */
    {
      TT_ShortMetrics*  cur   = *shorts;
      TT_ShortMetrics*  limit = cur + MIN( num_shorts, num_shorts_checked );

      for ( ; cur < limit; cur++ )
        *cur = GET_Short();

      /* we fill up the missing left side bearings with the    */
      /* last valid value. Since this will occur for buggy CJK */
      /* fonts usually, nothing serious will happen            */
      if ( num_shorts > num_shorts_checked && num_shorts_checked > 0 )
      {
        TT_Short  val = *(shorts)[num_shorts_checked-1];

        limit = *shorts + num_shorts;
        for ( ; cur < limit; cur++ )
          *cur = val;
      }
    }

    FORGET_Frame();

    FT_TRACE2(( "loaded\n" ));

  Exit:
    return error;
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    TT_Load_Metrics_Header                                             */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Loads the horizontal or vertical header in a face object.          */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face     :: A handle to the target face object.                    */
  /*    stream   :: The input stream.                                      */
  /*    vertical :: A boolean flag.  If set, load vertical metrics.        */
  /*                                                                       */
  /* <Return>                                                              */
  /*    TrueType error code.  0 means success.                             */
  /*                                                                       */
  LOCAL_FUNC
  TT_Error  TT_Load_Metrics_Header( TT_Face    face,
                                    FT_Stream  stream,
                                    TT_Bool    vertical )
  {
    TT_Error        error;
    TT_HoriHeader*  header;

    const FT_Frame_Field  metrics_header_fields[] = {
              FT_FRAME_START(36),
                FT_FRAME_ULONG(  TT_HoriHeader, Version ),
                FT_FRAME_SHORT(  TT_HoriHeader, Ascender ),
                FT_FRAME_SHORT(  TT_HoriHeader, Descender ),
                FT_FRAME_SHORT(  TT_HoriHeader, Line_Gap ),
                FT_FRAME_USHORT( TT_HoriHeader, advance_Width_Max ),
                FT_FRAME_SHORT(  TT_HoriHeader, min_Left_Side_Bearing ),
                FT_FRAME_SHORT(  TT_HoriHeader, min_Right_Side_Bearing ),
                FT_FRAME_SHORT(  TT_HoriHeader, xMax_Extent ),
                FT_FRAME_SHORT(  TT_HoriHeader, caret_Slope_Rise ),
                FT_FRAME_SHORT(  TT_HoriHeader, caret_Slope_Run ),
                FT_FRAME_SHORT(  TT_HoriHeader, Reserved[0] ),
                FT_FRAME_SHORT(  TT_HoriHeader, Reserved[1] ),
                FT_FRAME_SHORT(  TT_HoriHeader, Reserved[2] ),
                FT_FRAME_SHORT(  TT_HoriHeader, Reserved[3] ),
                FT_FRAME_SHORT(  TT_HoriHeader, Reserved[4] ),
                FT_FRAME_SHORT(  TT_HoriHeader, metric_Data_Format ),
                FT_FRAME_USHORT( TT_HoriHeader, number_Of_HMetrics ),
              FT_FRAME_END };

    FT_TRACE2(( vertical ? "Vertical header " : "Horizontal header " ));

    if ( vertical )
    {
      face->vertical_info = 0;

      /* The vertical header table is optional, so return quietly if */
      /* we don't find it.                                           */
      error = face->goto_table( face, TTAG_vhea, stream, 0 );
      if (error)
      {
        error = TT_Err_Ok;
        goto Exit;
      }

      face->vertical_info = 1;
      header = (TT_HoriHeader*)&face->vertical;
    }
    else
    {
      /* The horizontal header is mandatory, return an error if we */
      /* don't find it.                                            */
      error = face->goto_table( face, TTAG_hhea, stream, 0 );
      if (error)
      {
        error = TT_Err_Horiz_Header_Missing;
        goto Exit;
      }

      header = &face->horizontal;
    }

    if ( READ_Fields( metrics_header_fields, header ) ) goto Exit;

    header->long_metrics  = NULL;
    header->short_metrics = NULL;

    FT_TRACE2(( "loaded\n" ));

    /* Now try to load the corresponding metrics */

    error = TT_Load_Metrics( face, stream, vertical );
  Exit:
    return error;
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    TT_Load_Names                                                      */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Loads the name records.                                            */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face   :: A handle to the target face object.                      */
  /*    stream :: The input stream.                                        */
  /*                                                                       */
  /* <Return>                                                              */
  /*    TrueType error code.  0 means success.                             */
  /*                                                                       */
  LOCAL_FUNC
  TT_Error  TT_Load_Names( TT_Face  face, FT_Stream  stream )
  {
    TT_Error   error;
    FT_Memory  memory = stream->memory;

    TT_ULong   table_pos, table_len;
    TT_ULong   storageSize;

    TT_NameTable*  names;

    const FT_Frame_Field  name_table_fields[] = {
              FT_FRAME_START(6),
                FT_FRAME_USHORT( TT_NameTable, format ),
                FT_FRAME_USHORT( TT_NameTable, numNameRecords ),
                FT_FRAME_USHORT( TT_NameTable, storageOffset ),
              FT_FRAME_END };

    const FT_Frame_Field  name_record_fields[] = {
                FT_FRAME_USHORT( TT_NameRec, platformID ),
                FT_FRAME_USHORT( TT_NameRec, encodingID ),
                FT_FRAME_USHORT( TT_NameRec, languageID ),
                FT_FRAME_USHORT( TT_NameRec, nameID ),
                FT_FRAME_USHORT( TT_NameRec, stringLength ),
                FT_FRAME_USHORT( TT_NameRec, stringOffset ),
              FT_FRAME_END };


    FT_TRACE2(( "Names " ));

    error = face->goto_table( face, TTAG_name, stream, &table_len );
    if (error)
    {
      /* The name table is required so indicate failure. */
      FT_TRACE2(( "is missing!\n" ));
      error = TT_Err_Name_Table_Missing;
      goto Exit;
    }

    table_pos = FILE_Pos();

    names = &face->name_table;

    if ( READ_Fields( name_table_fields, names ) ) goto Exit;

    /* Allocate the array of name records. */
    if ( ALLOC_ARRAY( names->names,
                      names->numNameRecords,
                      TT_NameRec )                   ||
         ACCESS_Frame( names->numNameRecords * 12L ) )
      goto Exit;

    /* Load the name records and determine how much storage is needed */
    /* to hold the strings themselves.                                */
    {
      TT_NameRec*    cur   = names->names;
      TT_NameRec*    limit = cur + names->numNameRecords;

      storageSize = 0;

      for ( ; cur < limit; cur ++ )
      {
        TT_ULong  upper;

        (void)READ_Fields( name_record_fields, cur );

        upper = (TT_ULong)(cur->stringOffset + cur->stringLength);
        if ( upper > storageSize ) storageSize = upper;
      }
    }

    FORGET_Frame();

    if (storageSize > 0)
    {
      /* allocate the name storage area in memory, then read it */
      if ( ALLOC( names->storage, storageSize )  ||
           FILE_Read_At( table_pos + names->storageOffset,
                         (void*)names->storage, storageSize ) )
        goto Exit;

      /* Go through and assign the string pointers to the name records. */
      {
        TT_NameRec*  cur   = names->names;
        TT_NameRec*  limit = cur + names->numNameRecords;

        for ( ; cur < limit; cur++ )
          cur->string = names->storage + cur->stringOffset;
      }

#ifdef FT_DEBUG_LEVEL_TRACE
      /* Print Name Record Table in case of debugging */
      {
        TT_NameRec*  cur   = names->names;
        TT_NameRec*  limit = cur + names->numNameRecords;

        for ( ; cur < limit; cur++ )
        {
          TT_UInt  j;

          FT_TRACE2(( "%d %d %x %d ",
                       cur->platformID,
                       cur->encodingID,
                       cur->languageID,
                       cur->nameID ));

          /* I know that M$ encoded strings are Unicode,            */
          /* but this works reasonable well for debugging purposes. */
          if ( cur->string )
            for ( j = 0; j < cur->stringLength; j++ )
            {
              TT_Char  c = *(cur->string + j);

              if ( (TT_Byte)c < 128 )
                FT_TRACE2(( "%c", c ));
            }
        }
      }
      FT_TRACE2(( "\n" ));
#endif
    }
    FT_TRACE2(( "loaded\n" ));

  Exit:
    return error;
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    TT_Free_Names                                                      */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Frees the name records.                                            */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face :: A handle to the target face object.                        */
  /*                                                                       */
  LOCAL_FUNC
  void  TT_Free_Names( TT_Face  face )
  {
    FT_Memory      memory = face->root.driver->memory;
    TT_NameTable*  names  = &face->name_table;


    /* free strings table */
    FREE( names->names );

    /* free strings storage */
    FREE( names->storage );

    names->numNameRecords = 0;
    names->format         = 0;
    names->storageOffset  = 0;
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    TT_Load_CMap                                                       */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Loads the cmap directory in a face object.  The cmaps itselves are */
  /*    loaded on demand in the `ttcmap.c' module.                         */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face    :: A handle to the target face object.                     */
  /*    stream  :: A handle to the input stream.                           */
  /*                                                                       */
  /* <Return>                                                              */
  /*     TrueType error code.  0 means success.                            */
  /*                                                                       */
  LOCAL_FUNC
  TT_Error  TT_Load_CMap( TT_Face    face,
                          FT_Stream  stream )
  {
    TT_Error    error;
    FT_Memory   memory = stream->memory;
    TT_Long     table_start;
    TT_CMapDir  cmap_dir;

    const FT_Frame_Field  cmap_fields[] = {
              FT_FRAME_START(4),
                FT_FRAME_USHORT( TT_CMapDir, tableVersionNumber ),
                FT_FRAME_USHORT( TT_CMapDir, numCMaps ),
              FT_FRAME_END };

    const FT_Frame_Field  cmap_rec_fields[] = {
              FT_FRAME_START(6),
                FT_FRAME_USHORT( TT_CMapTable, format ),
                FT_FRAME_USHORT( TT_CMapTable, length ),
                FT_FRAME_USHORT( TT_CMapTable, version ),
              FT_FRAME_END };

    FT_TRACE2(( "CMaps " ));

    error = face->goto_table( face, TTAG_cmap, stream, 0 );
    if (error)
    {
      error = TT_Err_CMap_Table_Missing;
      goto Exit;
    }

    table_start = FILE_Pos();

    if ( READ_Fields( cmap_fields, &cmap_dir ) ) goto Exit;

    /* save space in face table for cmap tables */
    if ( ALLOC_ARRAY( face->charmaps,
                      cmap_dir.numCMaps,
                      TT_CharMapRec ) )
      goto Exit;

    face->num_charmaps = cmap_dir.numCMaps;
    {
      TT_CharMap  charmap = face->charmaps;
      TT_CharMap  limit   = charmap + face->num_charmaps;

      /* read the header of each charmap first */
      if ( ACCESS_Frame( face->num_charmaps*8L ) ) goto Exit;
      for ( ; charmap < limit; charmap++ )
      {
        TT_CMapTable*  cmap;

        charmap->root.face = (FT_Face)face;
        cmap               = &charmap->cmap;

        cmap->loaded             = FALSE;
        cmap->platformID         = GET_UShort();
        cmap->platformEncodingID = GET_UShort();
        cmap->offset             = (TT_ULong)GET_Long();
      }
      FORGET_Frame();

      /* now read the rest of each table */
      for ( charmap = face->charmaps; charmap < limit; charmap++ )
      {
        TT_CMapTable* cmap = &charmap->cmap;

        if ( FILE_Seek( table_start + (TT_Long)cmap->offset ) ||
             READ_Fields( cmap_rec_fields, cmap )             )
          goto Exit;

        cmap->offset = FILE_Pos();
      }
    }

    FT_TRACE2(( "loaded\n" ));
  Exit:
    return error;
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    TT_Load_OS2                                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Loads the OS2 table.                                               */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face   :: A handle to the target face object.                      */
  /*    stream :: A handle to the input stream.                            */
  /*                                                                       */
  /* <Return>                                                              */
  /*    TrueType error code.  0 means success.                             */
  /*                                                                       */
  LOCAL_FUNC
  TT_Error  TT_Load_OS2( TT_Face    face,
                         FT_Stream  stream )
  {
    TT_Error  error;
    TT_OS2*   os2;

    const FT_Frame_Field  os2_fields[] = {
              FT_FRAME_START(78),
                FT_FRAME_USHORT( TT_OS2, version ),
                FT_FRAME_SHORT(  TT_OS2, xAvgCharWidth ),
                FT_FRAME_USHORT( TT_OS2, usWeightClass ),
                FT_FRAME_USHORT( TT_OS2, usWidthClass ),
                FT_FRAME_SHORT(  TT_OS2, fsType ),
                FT_FRAME_SHORT(  TT_OS2, ySubscriptXSize ),
                FT_FRAME_SHORT(  TT_OS2, ySubscriptYSize ),
                FT_FRAME_SHORT(  TT_OS2, ySubscriptXOffset ),
                FT_FRAME_SHORT(  TT_OS2, ySubscriptYOffset ),
                FT_FRAME_SHORT(  TT_OS2, ySuperscriptXSize ),
                FT_FRAME_SHORT(  TT_OS2, ySuperscriptYSize ),
                FT_FRAME_SHORT(  TT_OS2, ySuperscriptXOffset ),
                FT_FRAME_SHORT(  TT_OS2, ySuperscriptYOffset ),
                FT_FRAME_SHORT(  TT_OS2, yStrikeoutSize ),
                FT_FRAME_SHORT(  TT_OS2, yStrikeoutPosition ),
                FT_FRAME_SHORT(  TT_OS2, sFamilyClass ),
                FT_FRAME_BYTE(   TT_OS2, panose[0] ),
                FT_FRAME_BYTE(   TT_OS2, panose[1] ),
                FT_FRAME_BYTE(   TT_OS2, panose[2] ),
                FT_FRAME_BYTE(   TT_OS2, panose[3] ),
                FT_FRAME_BYTE(   TT_OS2, panose[4] ),
                FT_FRAME_BYTE(   TT_OS2, panose[5] ),
                FT_FRAME_BYTE(   TT_OS2, panose[6] ),
                FT_FRAME_BYTE(   TT_OS2, panose[7] ),
                FT_FRAME_BYTE(   TT_OS2, panose[8] ),
                FT_FRAME_BYTE(   TT_OS2, panose[9] ),
                FT_FRAME_ULONG(  TT_OS2, ulUnicodeRange1 ),
                FT_FRAME_ULONG(  TT_OS2, ulUnicodeRange2 ),
                FT_FRAME_ULONG(  TT_OS2, ulUnicodeRange3 ),
                FT_FRAME_ULONG(  TT_OS2, ulUnicodeRange4 ),
                FT_FRAME_BYTE(   TT_OS2, achVendID[0] ),
                FT_FRAME_BYTE(   TT_OS2, achVendID[1] ),
                FT_FRAME_BYTE(   TT_OS2, achVendID[2] ),
                FT_FRAME_BYTE(   TT_OS2, achVendID[3] ),

                FT_FRAME_USHORT( TT_OS2, fsSelection ),
                FT_FRAME_USHORT( TT_OS2, usFirstCharIndex ),
                FT_FRAME_USHORT( TT_OS2, usLastCharIndex ),
                FT_FRAME_SHORT(  TT_OS2, sTypoAscender ),
                FT_FRAME_SHORT(  TT_OS2, sTypoDescender ),
                FT_FRAME_SHORT(  TT_OS2, sTypoLineGap ),
                FT_FRAME_USHORT( TT_OS2, usWinAscent ),
                FT_FRAME_USHORT( TT_OS2, usWinDescent ),
              FT_FRAME_END };

    const FT_Frame_Field  os2_fields_extra[] = {
              FT_FRAME_START(8),
                FT_FRAME_ULONG( TT_OS2, ulCodePageRange1 ),
                FT_FRAME_ULONG( TT_OS2, ulCodePageRange2 ),
              FT_FRAME_END };

    const FT_Frame_Field  os2_fields_extra2[] = {
              FT_FRAME_START(10),
                FT_FRAME_SHORT( TT_OS2,  sxHeight ),
                FT_FRAME_SHORT( TT_OS2,  sCapHeight ),
                FT_FRAME_USHORT( TT_OS2, usDefaultChar ),
                FT_FRAME_USHORT( TT_OS2, usBreakChar ),
                FT_FRAME_USHORT( TT_OS2, usMaxContext ),
              FT_FRAME_END };

    FT_TRACE2(( "OS/2 Table " ));

    /* We now support old Mac fonts where the OS/2 table doesn't  */
    /* exist.  Simply put, we set the `version' field to 0xFFFF   */
    /* and test this value each time we need to access the table. */
    error = face->goto_table( face, TTAG_OS2, stream, 0 );
    if (error)
    {
      FT_TRACE2(( "is missing\n!" ));
      face->os2.version = 0xFFFF;
      error = TT_Err_Ok;
      goto Exit;
    }

    os2 = &face->os2;

    if ( READ_Fields( os2_fields, os2 ) ) goto Exit;

    os2->ulCodePageRange1 = 0;
    os2->ulCodePageRange2 = 0;

    if ( os2->version >= 0x0001 )
    {
      /* only version 1 tables */
      if ( READ_Fields( os2_fields_extra, os2 ) ) goto Exit;

      if ( os2->version >= 0x0002 )
      {
        /* only version 2 tables */
        if ( READ_Fields( os2_fields_extra2, os2 ) ) goto Exit;
      }
    }

    FT_TRACE2(( "loaded\n" ));

  Exit:
    return error;
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    TT_Load_Postscript                                                 */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Loads the Postscript table.                                        */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face   :: A handle to the target face object.                      */
  /*    stream :: A handle to the input stream.                            */
  /*                                                                       */
  /* <Return>                                                              */
  /*    TrueType error code.  0 means success.                             */
  /*                                                                       */
  LOCAL_FUNC
  TT_Error  TT_Load_PostScript( TT_Face    face,
                                FT_Stream  stream )
  {
    TT_Error        error;
    TT_Postscript*  post = &face->postscript;

    static const FT_Frame_Field  post_fields[] = {
              FT_FRAME_START(32),
                FT_FRAME_ULONG( TT_Postscript, FormatType ),
                FT_FRAME_ULONG( TT_Postscript, italicAngle ),
                FT_FRAME_SHORT( TT_Postscript, underlinePosition ),
                FT_FRAME_SHORT( TT_Postscript, underlineThickness ),
                FT_FRAME_ULONG( TT_Postscript, isFixedPitch ),
                FT_FRAME_ULONG( TT_Postscript, minMemType42 ),
                FT_FRAME_ULONG( TT_Postscript, maxMemType42 ),
                FT_FRAME_ULONG( TT_Postscript, minMemType1 ),
                FT_FRAME_ULONG( TT_Postscript, maxMemType1 ),
              FT_FRAME_END };

    FT_TRACE2(( "PostScript " ));

    error = face->goto_table( face, TTAG_post, stream, 0 );
    if (error)
      return TT_Err_Post_Table_Missing;

    if ( READ_Fields( post_fields, post ) ) return error;

    /* we don't load the glyph names, we do that in another */
    /* module (ttpost).                                     */
    FT_TRACE2(( "loaded\n" ));

    return TT_Err_Ok;
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    TT_Load_PCLT                                                       */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Loads the PCL 5 Table.                                             */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face   :: A handle to the target face object.                      */
  /*    stream :: A handle to the input stream.                            */
  /*                                                                       */
  /* <Return>                                                              */
  /*    TrueType error code.  0 means success.                             */
  /*                                                                       */
  LOCAL_FUNC
  TT_Error  TT_Load_PCLT( TT_Face    face,
                          FT_Stream  stream )
  {
    static const FT_Frame_Field  pclt_fields[] = {
	               FT_FRAME_START( 20 ),
				      FT_FRAME_ULONG ( TT_PCLT, Version ),
					  FT_FRAME_ULONG ( TT_PCLT, FontNumber ),
					  FT_FRAME_USHORT( TT_PCLT, Pitch ),
					  FT_FRAME_USHORT( TT_PCLT, xHeight ),
					  FT_FRAME_USHORT( TT_PCLT, Style ),
					  FT_FRAME_USHORT( TT_PCLT, TypeFamily ),
					  FT_FRAME_USHORT( TT_PCLT, CapHeight ),
				   FT_FRAME_END };
  
    static const FT_Frame_Field  pclt_fields2[] = {
	               FT_FRAME_START( 4 ),
				     FT_FRAME_CHAR( TT_PCLT, StrokeWeight ),
					 FT_FRAME_CHAR( TT_PCLT, WidthType ),
					 FT_FRAME_BYTE( TT_PCLT, SerifStyle ),
					 FT_FRAME_BYTE( TT_PCLT, Reserved ),
	               FT_FRAME_END };
  
    TT_Error    error;
	TT_PCLT*    pclt = &face->pclt;
	
    FT_TRACE2(( "PCLT " ));

	/* optional table */
	error = face->goto_table( face, TTAG_PCLT, stream, 0 );
	if (error)
	{
	  FT_TRACE2(( "missing (optional)\n" ));
	  pclt->Version = 0;
	  return 0;
	}
	
	if ( READ_Fields( pclt_fields, pclt )            ||
	     FILE_Read  ( pclt->TypeFace, 16 )           ||
		 FILE_Read  ( pclt->CharacterComplement, 8 ) ||
		 FILE_Read  ( pclt->FileName, 6 )            ||
		 READ_Fields( pclt_fields2, pclt )           )
      goto Exit;
	
	FT_TRACE2(( "loaded\n" ));
  Exit:	
	return error;
  }						  

  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    TT_Load_Gasp                                                       */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Loads the `GASP' table into a face object.                         */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face   :: A handle to the target face object.                      */
  /*    stream :: The input stream.                                        */
  /*                                                                       */
  /* <Return>                                                              */
  /*    TrueType error code.  0 means success.                             */
  /*                                                                       */
  LOCAL_FUNC
  TT_Error  TT_Load_Gasp( TT_Face    face,
                          FT_Stream  stream )
  {
    TT_Error   error;
    FT_Memory  memory = stream->memory;

    TT_UInt        j,num_ranges;
    TT_GaspRange*  gaspranges;


    FT_TRACE2(( "TT_Load_Gasp( %08p )\n", face ));

    /* the gasp table is optional */
    error = face->goto_table( face, TTAG_gasp, stream, 0 );
    if (error) return TT_Err_Ok;

    if ( ACCESS_Frame( 4L ) )
      goto Exit;

    face->gasp.version   = GET_UShort();
    face->gasp.numRanges = GET_UShort();

    FORGET_Frame();

    num_ranges = face->gasp.numRanges;
    FT_TRACE3(( "number of ranges = %d\n", num_ranges ));

    if ( ALLOC_ARRAY( gaspranges, num_ranges, TT_GaspRange ) ||
         ACCESS_Frame( num_ranges * 4L ) )
      goto Exit;

    face->gasp.gaspRanges = gaspranges;

    for ( j = 0; j < num_ranges; j++ )
    {
      gaspranges[j].maxPPEM  = GET_UShort();
      gaspranges[j].gaspFlag = GET_UShort();

      FT_TRACE3(( " [max:%d flag:%d]",
                gaspranges[j].maxPPEM,
                gaspranges[j].gaspFlag ));
    }
    FT_TRACE3(( "\n" ));

    FORGET_Frame();
    FT_TRACE2(( "GASP loaded\n" ));

  Exit:
    return error;
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    TT_Load_Kern                                                       */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Loads the first kerning table with format 0 in the font.  Only     */
  /*    accepts the first horizontal kerning table.  Developers should use */
  /*    the `ftxkern' extension to access other kerning tables in the font */
  /*    file, if they really want to.                                      */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face   :: A handle to the target face object.                      */
  /*    stream :: The input stream.                                        */
  /*                                                                       */
  /* <Return>                                                              */
  /*    TrueType error code.  0 means success.                             */
  /*                                                                       */
  LOCAL_FUNC
  TT_Error  TT_Load_Kern( TT_Face    face,
                          FT_Stream  stream )
  {
    TT_Error   error;
    FT_Memory  memory = stream->memory;

    TT_UInt    n, num_tables, version;

    /* the kern table is optional. exit silently if it's missing */
    error = face->goto_table( face, TTAG_kern, stream, 0 );
    if ( error ) return TT_Err_Ok;

    if ( ACCESS_Frame( 4L ) ) goto Exit;

    version    = GET_UShort();
    num_tables = GET_UShort();

    FORGET_Frame();

    for ( n = 0; n < num_tables; n++ )
    {
      TT_UInt  coverage;
      TT_UInt  length;

      if ( ACCESS_Frame( 6L ) ) goto Exit;

      version  = GET_UShort();      /* version                 */
      length   = GET_UShort() - 6;  /* substract header length */
      coverage = GET_UShort();

      FORGET_Frame();

      if ( coverage == 0x0001 )
      {
        TT_UInt          num_pairs;
        TT_Kern_0_Pair*  pair;
        TT_Kern_0_Pair*  limit;

        /* found a horizontal format 0 kerning table ! */
        if ( ACCESS_Frame(8L) )
          goto Exit;

        num_pairs = GET_UShort();
        /* skip the rest */

        FORGET_Frame();

        /* allocate array of kerning pairs */
        if ( ALLOC_ARRAY( face->kern_pairs, num_pairs, TT_Kern_0_Pair ) ||
             ACCESS_Frame( 6L * num_pairs )                             )
          goto Exit;

        pair  = face->kern_pairs;
        limit = pair + num_pairs;
        for ( ; pair < limit; pair++ )
        {
          pair->left  = GET_UShort();
          pair->right = GET_UShort();
          pair->value = GET_UShort();
        }

        FORGET_Frame();

        face->num_kern_pairs   = num_pairs;
        face->kern_table_index = n;
        goto Exit;
      }

      if ( FILE_Skip( length ) )
        goto Exit;
    }

    /* no kern table found -- doesn't matter */
    face->kern_table_index = -1;
    face->num_kern_pairs   = 0;
    face->kern_pairs       = NULL;

  Exit:
    return error;
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    TT_Load_Hdmx                                                       */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Loads the horizontal device metrics table.                         */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face   :: A handle to the target face object.                      */
  /*    stream :: A handle to the input stream.                            */
  /*                                                                       */
  /* <Return>                                                              */
  /*    TrueType error code.  0 means success.                             */
  /*                                                                       */
  LOCAL_FUNC
  TT_Error  TT_Load_Hdmx( TT_Face    face,
                          FT_Stream  stream )
  {
    TT_Error  error;
    FT_Memory memory = stream->memory;

    TT_Hdmx*     hdmx = &face->hdmx;
    TT_Long      num_glyphs;
    TT_Long      record_size;

    hdmx->version     = 0;
    hdmx->num_records = 0;
    hdmx->records     = 0;

    /* this table is optional */
    error = face->goto_table( face, TTAG_hdmx, stream, 0 );
    if (error) return TT_Err_Ok;

    if ( ACCESS_Frame( 8L ) ) goto Exit;

    hdmx->version     = GET_UShort();
    hdmx->num_records = GET_Short();
    record_size       = GET_Long();

    FORGET_Frame();

    /* Only recognize format 0 */
    if ( hdmx->version != 0 )
      goto Exit;

    if ( ALLOC_ARRAY( hdmx->records, hdmx->num_records, TT_HdmxRec ) )
      goto Exit;

    num_glyphs   = face->root.num_glyphs;
    record_size -= num_glyphs + 2;

    {
      TT_HdmxRec*  cur   = hdmx->records;
      TT_HdmxRec*  limit = cur + hdmx->num_records;

      for ( ; cur < limit; cur++ )
      {
        /* read record */
        if ( READ_Byte( cur->ppem      ) ||
             READ_Byte( cur->max_width ) )
          goto Exit;

        if ( ALLOC( cur->widths, num_glyphs )  ||
             FILE_Read( cur->widths, num_glyphs ) )
          goto Exit;

        /* skip padding bytes */
        if ( record_size > 0 && FILE_Skip(record_size) )
            goto Exit;
      }
    }

  Exit:
    return error;
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    TT_Free_Hdmx                                                       */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Frees the horizontal device metrics table.                         */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face :: A handle to the target face object.                        */
  /*                                                                       */
  LOCAL_FUNC
  void  TT_Free_Hdmx( TT_Face  face )
  {
    if ( face )
    {
      TT_Int     n;
      FT_Memory  memory = face->root.driver->memory;

      for ( n = 0; n < face->hdmx.num_records; n++ )
        FREE( face->hdmx.records[n].widths );

      FREE( face->hdmx.records );
      face->hdmx.num_records = 0;
    }
  }


/* END */