Initial revision
diff --git a/src/sfnt/ttload.c b/src/sfnt/ttload.c
new file mode 100644
index 0000000..dbdde79
--- /dev/null
+++ b/src/sfnt/ttload.c
@@ -0,0 +1,1477 @@
+/***************************************************************************/
+/*                                                                         */
+/*  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 <ftdebug.h>
+
+#include <ttload.h>
+#include <tttags.h>
+#include <ttcmap.h>
+#include <tterrors.h>
+
+/* required by the tracing mode */
+#undef  FT_COMPONENT
+#define FT_COMPONENT      trace_ttload
+
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* <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..                 */
+  /*                                                                       */
+  EXPORT_FUNC
+  TT_Table*  TT_LookUp_Table( TT_Face   face,
+                              TT_ULong  tag  )
+  {
+    TT_Table*  entry;
+    TT_Table*  limit;
+    
+    FT_TRACE4(( "TT_LookUp_Table( %08lx, %c%c%c%c )\n",
+                  (TT_Long)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..                 */
+  /*                                                                       */
+  EXPORT_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;
+  }
+
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* <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.                                     */
+  /*    faceIndex :: The index of the TrueType font, if we're opening a    */
+  /*                 collection.                                           */
+  /*                                                                       */
+  /* <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,
+                               TT_Long    faceIndex )
+  {
+    TT_Error   error;
+    FT_Memory  memory = stream->memory;
+
+    TT_TableDir  tableDir;
+    TT_ULong     tag;
+
+    TT_Table *entry, *limit;
+
+
+    FT_TRACE2(( "TT_Load_Directory( %08lx, %ld )\n",
+              (TT_Long)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 must be a normal    */
+    /* TrueType file..                                                 */
+
+    if ( READ_ULong(tag) )
+      goto Exit;
+
+    if ( tag == TTAG_ttcf )
+    {
+      TT_Int  n;
+    
+      FT_TRACE4(( "TT_Load_Directory: file is a collection\n" ));
+      
+      /* it's a TrueType collection, i.e. a file containing several */
+      /* font files. Read the font directory now                    */
+      /*                                                            */
+      if ( ACCESS_Frame( 8 ) ) goto Exit;   
+        
+      face->ttc_header.version  = GET_Long();
+      face->ttc_header.DirCount = GET_Long();
+           
+      FORGET_Frame();
+
+      /* 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 (faceIndex >= face->ttc_header.DirCount)
+      {
+        error = TT_Err_Bad_Argument;
+        goto Exit;
+      }      
+      
+      /* if we're checking the font format, exit immediately */
+      if (faceIndex < 0)
+        goto Exit;
+        
+      /* seek to the appropriate TrueType file, then read tag */
+      if ( FILE_Skip( face->ttc_header.TableDirectory[faceIndex] - 12 ) ||
+           READ_Long( tableDir.version )                                )
+        goto Exit;
+    }
+    else
+    {
+      FT_TRACE6(( "TT_Load_Directory: file is not a collection\n" ));
+      /* the file isn't a collection, exit if we're asking for a */
+      /* collected font..                                        */
+      if (faceIndex > 0)
+      {
+        error = TT_Err_File_Is_Not_Collection;
+        goto Exit;
+      }
+      
+      tableDir.version = tag;
+    }
+
+    if ( ACCESS_Frame( 8L ) )
+      goto Exit;
+
+    tableDir.numTables = GET_UShort();
+
+    tableDir.searchRange   = GET_UShort();
+    tableDir.entrySelector = GET_UShort();
+    tableDir.rangeShift    = GET_UShort();
+
+    FORGET_Frame();
+
+    FT_TRACE2(( "-- Tables count   : %12u\n",  tableDir.numTables ));
+    FT_TRACE2(( "-- Format version : %08lx\n", tableDir.version ));
+
+    /* Check that we have a `sfnt' format there                        */
+    /* We must also be able to accept Mac/GX fonts, as well as OT ones */
+
+    if ( tableDir.version != 0x00010000 &&    /* MS fonts          */
+         tableDir.version != TTAG_true  &&    /* Mac fonts         */
+         tableDir.version != TTAG_OTTO  )    /* OT-Type2 fonts    */
+    {
+      FT_TRACE2(( "[not a valid TTF or OTF font]" ));
+      error = TT_Err_Invalid_File_Format;
+      goto Exit;
+    }
+
+    /* if we're performing a font format check, exit immediately */
+    /* with success                                              */
+    if ( faceIndex < 0 )
+      goto Exit;
+
+    face->num_tables = tableDir.numTables;
+
+    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;
+
+
+    FT_TRACE2(( "Load_TT_Header( %08lx )\n", (TT_Long)face ));
+
+    error = face->goto_table( face, TTAG_head, stream, 0 );
+    if ( error )
+    {
+      FT_TRACE0(( "Font Header is missing!\n" ));
+      goto Exit;
+    }
+
+    if ( ACCESS_Frame( 54L ) )
+      goto Exit;
+
+    header = &face->header;
+
+    header->Table_Version = GET_ULong();
+    header->Font_Revision = GET_ULong();
+
+    header->CheckSum_Adjust = GET_Long();
+    header->Magic_Number    = GET_Long();
+
+    header->Flags        = GET_UShort();
+    header->Units_Per_EM = GET_UShort();
+
+    header->Created [0] = GET_Long();
+    header->Created [1] = GET_Long();
+    header->Modified[0] = GET_Long();
+    header->Modified[1] = GET_Long();
+
+    header->xMin = GET_Short();
+    header->yMin = GET_Short();
+    header->xMax = GET_Short();
+    header->yMax = GET_Short();
+
+    header->Mac_Style       = GET_UShort();
+    header->Lowest_Rec_PPEM = GET_UShort();
+
+    header->Font_Direction      = GET_Short();
+    header->Index_To_Loc_Format = GET_Short();
+    header->Glyph_Data_Format   = GET_Short();
+
+    FORGET_Frame();
+
+    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;
+
+
+    FT_TRACE2(( "Load_TT_MaxProfile( %08lx )\n", (TT_Long)face ));
+
+    error = face->goto_table( face, TTAG_maxp, stream, 0 );
+    if (error) goto Exit;
+
+    if ( ACCESS_Frame( 32L ) )
+      goto Exit;
+
+    /* read frame data into face table */
+    maxProfile->version               = GET_ULong();
+    maxProfile->numGlyphs             = GET_UShort();
+
+    maxProfile->maxPoints             = GET_UShort();
+    maxProfile->maxContours           = GET_UShort();
+    maxProfile->maxCompositePoints    = GET_UShort();
+    maxProfile->maxCompositeContours  = GET_UShort();
+
+    maxProfile->maxZones              = GET_UShort();
+    maxProfile->maxTwilightPoints     = GET_UShort();
+
+    maxProfile->maxStorage            = GET_UShort();
+    maxProfile->maxFunctionDefs       = GET_UShort();
+    maxProfile->maxInstructionDefs    = GET_UShort();
+    maxProfile->maxStackElements      = GET_UShort();
+    maxProfile->maxSizeOfInstructions = GET_UShort();
+    maxProfile->maxComponentElements  = GET_UShort();
+    maxProfile->maxComponentDepth     = GET_UShort();
+
+    FORGET_Frame();
+
+    /* 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( %08lx )\n",
+              vertical ? "Vertical" : "Horizontal", (TT_Long)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;
+
+    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 ( ACCESS_Frame( 36L ) )
+      goto Exit;
+
+    header->Version   = GET_ULong();
+    header->Ascender  = GET_Short();
+    header->Descender = GET_Short();
+    header->Line_Gap  = GET_Short();
+
+    header->advance_Width_Max = GET_UShort();
+
+    header->min_Left_Side_Bearing  = GET_Short();
+    header->min_Right_Side_Bearing = GET_Short();
+    header->xMax_Extent            = GET_Short();
+    header->caret_Slope_Rise       = GET_Short();
+    header->caret_Slope_Run        = GET_Short();
+
+    header->Reserved[0] = GET_Short();  /* this is caret_Offset for
+                                           vertical headers */
+    header->Reserved[1] = GET_Short();
+    header->Reserved[2] = GET_Short();
+    header->Reserved[3] = GET_Short();
+    header->Reserved[4] = GET_Short();
+
+    header->metric_Data_Format = GET_Short();
+    header->number_Of_HMetrics = GET_UShort();
+
+    FORGET_Frame();
+
+    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;
+
+
+    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();
+
+    if ( ACCESS_Frame( 6L ) )
+      goto Exit;
+
+    names = &face->name_table;
+
+    /* Load the initial names data. */
+    names->format         = GET_UShort();
+    names->numNameRecords = GET_UShort();
+    names->storageOffset  = GET_UShort();
+
+    FORGET_Frame();
+
+    /* 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;
+
+        cur->platformID   = GET_UShort();
+        cur->encodingID   = GET_UShort();
+        cur->languageID   = GET_UShort();
+        cur->nameID       = GET_UShort();
+        cur->stringLength = GET_UShort();
+        cur->stringOffset = GET_UShort();
+
+        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     off, cur_off, table_start;
+    TT_CMapDir  cmap_dir;
+
+    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 ( ACCESS_Frame( 4L ) )           /* 4 bytes cmap header */
+      goto Exit;
+
+    cmap_dir.tableVersionNumber = GET_UShort();
+    cmap_dir.numCMaps           = GET_UShort();
+
+    FORGET_Frame();
+
+    off = FILE_Pos();  /* save offset to cmapdir[] which follows */
+
+    /* 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;
+
+      for ( ; charmap < limit; charmap++ )
+      {
+        TT_CMapTable*  cmap;
+
+        charmap->root.face = (FT_Face)face;
+        cmap               = &charmap->cmap;
+
+        if ( FILE_Seek( off )  ||
+             ACCESS_Frame( 8L ) )
+          goto Exit;
+
+        cmap->loaded             = FALSE;
+        cmap->platformID         = GET_UShort();
+        cmap->platformEncodingID = GET_UShort();
+
+        cur_off = GET_Long();
+
+        FORGET_Frame();
+
+        off = FILE_Pos();
+
+        if ( FILE_Seek( table_start + cur_off ) ||
+             ACCESS_Frame( 6L ) )
+          goto Exit;
+
+        cmap->format  = GET_UShort();
+        cmap->length  = GET_UShort();
+        cmap->version = GET_UShort();
+
+        FORGET_Frame();
+
+        cmap->offset = FILE_Pos();
+        charmap++;
+      }
+    }
+
+    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_Int    j;
+    TT_OS2*   os2;
+
+
+    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;
+    }
+
+    if ( ACCESS_Frame( 78L ) )
+      goto Exit;
+
+    os2 = &face->os2;
+
+    os2->version             = GET_UShort();
+    os2->xAvgCharWidth       = GET_Short();
+    os2->usWeightClass       = GET_UShort();
+    os2->usWidthClass        = GET_UShort();
+    os2->fsType              = GET_Short();
+    os2->ySubscriptXSize     = GET_Short();
+    os2->ySubscriptYSize     = GET_Short();
+    os2->ySubscriptXOffset   = GET_Short();
+    os2->ySubscriptYOffset   = GET_Short();
+    os2->ySuperscriptXSize   = GET_Short();
+    os2->ySuperscriptYSize   = GET_Short();
+    os2->ySuperscriptXOffset = GET_Short();
+    os2->ySuperscriptYOffset = GET_Short();
+    os2->yStrikeoutSize      = GET_Short();
+    os2->yStrikeoutPosition  = GET_Short();
+    os2->sFamilyClass        = GET_Short();
+
+    for ( j = 0; j < 10; j++ )
+      os2->panose[j] = GET_Byte();
+
+    os2->ulUnicodeRange1     = GET_ULong();
+    os2->ulUnicodeRange2     = GET_ULong();
+    os2->ulUnicodeRange3     = GET_ULong();
+    os2->ulUnicodeRange4     = GET_ULong();
+
+    for ( j = 0; j < 4; j++ )
+      os2->achVendID[j] = GET_Byte();
+
+    os2->fsSelection         = GET_UShort();
+    os2->usFirstCharIndex    = GET_UShort();
+    os2->usLastCharIndex     = GET_UShort();
+    os2->sTypoAscender       = GET_Short();
+    os2->sTypoDescender      = GET_Short();
+    os2->sTypoLineGap        = GET_Short();
+    os2->usWinAscent         = GET_UShort();
+    os2->usWinDescent        = GET_UShort();
+
+    FORGET_Frame();
+
+    os2->ulCodePageRange1 = 0;
+    os2->ulCodePageRange2 = 0;
+
+    if ( os2->version >= 0x0001 )
+    {
+      /* only version 1 tables */
+
+      if ( ACCESS_Frame( 8L ) )  /* read into frame */
+        goto Exit;
+
+      os2->ulCodePageRange1 = GET_ULong();
+      os2->ulCodePageRange2 = GET_ULong();
+
+      FORGET_Frame();
+    }
+
+    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;
+
+    FT_TRACE2(( "PostScript " ));
+
+    error = face->goto_table( face, TTAG_post, stream, 0 );
+    if (error)
+      return TT_Err_Post_Table_Missing;
+
+    if ( ACCESS_Frame( 32L ) )
+      return error;
+
+    /* read frame data into face table */
+
+    post->FormatType         = GET_ULong();
+    post->italicAngle        = GET_ULong();
+    post->underlinePosition  = GET_Short();
+    post->underlineThickness = GET_Short();
+    post->isFixedPitch       = GET_ULong();
+    post->minMemType42       = GET_ULong();
+    post->maxMemType42       = GET_ULong();
+    post->minMemType1        = GET_ULong();
+    post->maxMemType1        = GET_ULong();
+
+    FORGET_Frame();
+
+    /* 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_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( %08lx )\n", (TT_Long)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 */