shithub: freetype+ttf2subf

Download patch

ref: e5680279b21106173e342eab28552ae0e027196a
parent: e70d553111963cb0031869f708038f52c9f57fe9
author: David Turner <[email protected]>
date: Fri Feb 25 19:12:04 EST 2005

* many, many files: several memory optimizations were implemented to
  drastically reduce the heap usage of FreeType, especially in the case
  of memory-mapped files. The idea is to avoid loading and decoding tables
  in the heap, and instead access the raw data whenever possible (i.e.
  when it doesn't compromise performance).

  This had several impacts: first, opening vera.ttf uses a ridiculous amount
  of memory (when the FT_Library footprint is accounted for), until you start
  loading glyphs. Even then, you'll save at least 20 Kb compared to the non
  optimized case. performance of various operations, including open/close
  has also been dramatically improved.

  More optimisations to come. The auto-hinter eats memory like crazy? This
  must be stopped...

git/fs: mount .git/fs: mount/attach disallowed
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+2005-02-25  David Turner  <[email protected]>
+
+  * many, many files: several memory optimizations were implemented to
+  drastically reduce the heap usage of FreeType, especially in the case
+  of memory-mapped files. The idea is to avoid loading and decoding tables
+  in the heap, and instead access the raw data whenever possible (i.e.
+  when it doesn't compromise performance).
+  
+  This had several impacts: first, opening vera.ttf uses a ridiculous amount
+  of memory (when the FT_Library footprint is accounted for), until you start
+  loading glyphs. Even then, you'll save at least 20 Kb compared to the non
+  optimized case. performance of various operations, including open/close
+  has also been dramatically improved.
+  
+  More optimisations to come. The auto-hinter eats memory like crazy? This
+  must be stopped...
+
 2005-02-22  David Turner  <[email protected]>
 
 	* src/base/ftdbgmem.c: adding the ability to list all allocation sites
--- a/include/freetype/internal/sfnt.h
+++ b/include/freetype/internal/sfnt.h
@@ -477,6 +477,24 @@
   typedef void
   (*TT_Free_Table_Func)( TT_Face  face );
 
+ /**
+  * @functype: TT_Face_GetKerningFunc
+  *
+  * @description:
+  *   return the horizontal kerning value between two glyphs
+  *
+  * @input:
+  *    face        :: handle to source face object
+  *    left_glyph  :: left glyph index
+  *    right_glyph :: right glyph index
+  *
+  * @return:
+  *    kerning value in font units.
+  */
+  typedef FT_Int
+  (*TT_Face_GetKerningFunc)( TT_Face   face,
+                             FT_UInt   left_glyph,
+                             FT_UInt   right_glyph );
 
   /*************************************************************************/
   /*                                                                       */
@@ -534,6 +552,9 @@
     TT_Load_SBit_Image_Func      load_sbit_image;
     TT_Free_Table_Func           free_sbits;
 
+    /* sett `ttkern.h' */
+    TT_Face_GetKerningFunc       get_kerning;
+    
     /* see `ttpost.h' */
     TT_Get_PS_Name_Func          get_psname;
     TT_Free_Table_Func           free_psnames;
--- a/include/freetype/internal/tttypes.h
+++ b/include/freetype/internal/tttypes.h
@@ -310,7 +310,7 @@
 
   } TT_GaspRec;
 
-
+#ifndef FT_OPTIMIZE_MEMORY
   /*************************************************************************/
   /*                                                                       */
   /* <Struct>                                                              */
@@ -360,8 +360,6 @@
 
   } TT_HdmxRec, *TT_Hdmx;
 
-
-
   /*************************************************************************/
   /*                                                                       */
   /* <Struct>                                                              */
@@ -387,6 +385,7 @@
     FT_FWord   value;  /* kerning value                */
 
   } TT_Kern0_PairRec, *TT_Kern0_Pair;
+#endif /* !OPTIMIZE_MEMORY */
 
 
   /*************************************************************************/
@@ -1199,6 +1198,10 @@
 
     TT_Header             header;       /* TrueType header table          */
     TT_HoriHeader         horizontal;   /* TrueType horizontal header     */
+#ifdef FT_OPTIMIZE_MEMORY
+    FT_Byte*              horz_metrics;
+    FT_ULong              horz_metrics_size;
+#endif
 
     TT_MaxProfile         max_profile;
     FT_ULong              max_components;
@@ -1205,6 +1208,10 @@
 
     FT_Bool               vertical_info;
     TT_VertHeader         vertical;     /* TT Vertical header, if present */
+#ifdef FT_OPTIMIZE_MEMORY
+    FT_Byte*              vert_metrics;
+    FT_ULong              vert_metrics_size;
+#endif
 
     FT_UShort             num_names;    /* number of name records  */
     TT_NameTableRec       name_table;   /* name table              */
@@ -1239,7 +1246,15 @@
     /***********************************************************************/
 
     /* horizontal device metrics */
+#ifdef FT_OPTIMIZE_MEMORY
+    FT_Byte*              hdmx_table;
+    FT_ULong              hdmx_table_size;
+    FT_UInt               hdmx_record_count;
+    FT_ULong              hdmx_record_size;
+    FT_Byte*              hdmx_record_sizes;
+#else
     TT_HdmxRec            hdmx;
+#endif
 
     /* grid-fitting and scaling table */
     TT_GaspRec            gasp;                 /* the `gasp' table */
@@ -1285,10 +1300,18 @@
     FT_ULong              cvt_size;
     FT_Short*             cvt;
 
+#ifdef FT_OPTIMIZE_MEMORY
+    FT_Byte*              kern_table;
+    FT_ULong              kern_table_size;
+    FT_UInt               num_kern_tables;
+    FT_UInt32             kern_avail_bits;
+    FT_UInt32             kern_order_bits;
+#else
     /* the format 0 kerning table, if any */
     FT_Int                num_kern_pairs;
     FT_Int                kern_table_index;
     TT_Kern0_Pair         kern_pairs;
+#endif
 
     /* A pointer to the bytecode interpreter to use.  This is also */
     /* used to hook the debugger for the `ttdebug' utility.        */
--- a/src/base/ftdbgmem.c
+++ b/src/base/ftdbgmem.c
@@ -497,8 +497,6 @@
       node  = *pnode;
       if ( node )
       {
-        FT_MemSource  source;
-
         if ( node->size < 0 )
         {
           /* this block was already freed.  This means that our memory is */
@@ -536,7 +534,7 @@
       if ( source->cur_blocks > source->max_blocks )
         source->max_blocks = source->cur_blocks;
 
-      if ( size > source->cur_max )
+      if ( size > (FT_ULong)source->cur_max )
         source->cur_max = size;
 
       source->all_size += size;
@@ -937,10 +935,10 @@
 
       printf( "FreeType Memory Dump: current=%ld max=%ld total=%ld count=%ld\n",
               table->alloc_current, table->alloc_max, table->alloc_total, table->alloc_count );
-      printf( " block  block      sizes      sizes      sizes   source\n" );
-      printf( " count   high        sum    highsum        max   location\n" );
+      printf( " block  block    sizes    sizes    sizes   source\n" );
+      printf( " count   high      sum  highsum      max   location\n" );
       printf( "-------------------------------------------------\n" );
-      fmt = "%6ld %6ld %10ld %10ld %10ld %s:%d\n";
+      fmt = "%6ld %6ld %8ld %8ld %8ld %s:%d\n";
 
       for ( ; bucket < limit; bucket++ )
       {
--- a/src/cff/cffdrivr.c
+++ b/src/cff/cffdrivr.c
@@ -67,7 +67,7 @@
   /*************************************************************************/
   /*                                                                       */
   /* <Function>                                                            */
-  /*    Get_Kerning                                                        */
+  /*    cff_get_kerning                                                    */
   /*                                                                       */
   /* <Description>                                                         */
   /*    A driver method used to return the kerning vector between two      */
@@ -97,56 +97,21 @@
   /*    They can be implemented by format-specific interfaces.             */
   /*                                                                       */
   FT_CALLBACK_DEF( FT_Error )
-  Get_Kerning( FT_Face     ttface,          /* TT_Face */
-               FT_UInt     left_glyph,
-               FT_UInt     right_glyph,
-               FT_Vector*  kerning )
+  cff_get_kerning( FT_Face     ttface,          /* TT_Face */
+                   FT_UInt     left_glyph,
+                   FT_UInt     right_glyph,
+                   FT_Vector*  kerning )
   {
-    TT_Face        face = (TT_Face)ttface;
-    TT_Kern0_Pair  pair;
+    TT_Face       face = (TT_Face)ttface;
+    SFNT_Service  sfnt = face->sfnt;
 
-
-    if ( !face )
-      return CFF_Err_Invalid_Face_Handle;
-
     kerning->x = 0;
     kerning->y = 0;
 
-    if ( face->kern_pairs )
-    {
-      /* there are some kerning pairs in this font file! */
-      FT_ULong  search_tag = PAIR_TAG( left_glyph, right_glyph );
-      FT_Long   left, right;
+    if ( sfnt )
+      kerning->x = sfnt->get_kerning( face, left_glyph, right_glyph );
 
-
-      left  = 0;
-      right = face->num_kern_pairs - 1;
-
-      while ( left <= right )
-      {
-        FT_Long   middle = left + ( ( right - left ) >> 1 );
-        FT_ULong  cur_pair;
-
-
-        pair     = face->kern_pairs + middle;
-        cur_pair = PAIR_TAG( pair->left, pair->right );
-
-        if ( cur_pair == search_tag )
-          goto Found;
-
-        if ( cur_pair < search_tag )
-          left = middle + 1;
-        else
-          right = middle - 1;
-      }
-    }
-
-  Exit:
-    return CFF_Err_Ok;
-
-  Found:
-    kerning->x = pair->value;
-    goto Exit;
+    return 0;
   }
 
 
@@ -472,7 +437,7 @@
 
     Load_Glyph,
 
-    Get_Kerning,
+    cff_get_kerning,
     0,                      /* FT_Face_AttachFunc      */
     0                       /* FT_Face_GetAdvancesFunc */
   };
--- a/src/sfnt/Jamfile
+++ b/src/sfnt/Jamfile
@@ -8,7 +8,7 @@
 
   if $(FT2_MULTI)
   {
-    _sources = sfobjs sfdriver ttcmap ttpost ttload ttsbit ;
+    _sources = sfobjs sfdriver ttcmap ttpost ttload ttsbit ttkern ;
   }
   else
   {
--- a/src/sfnt/rules.mk
+++ b/src/sfnt/rules.mk
@@ -29,6 +29,7 @@
                 $(SFNT_DIR)/ttcmap.c   \
                 $(SFNT_DIR)/ttsbit.c   \
                 $(SFNT_DIR)/ttpost.c   \
+                $(SFNT_DIR)/ttkern.c   \
                 $(SFNT_DIR)/sfobjs.c   \
                 $(SFNT_DIR)/sfdriver.c
 
--- a/src/sfnt/sfdriver.c
+++ b/src/sfnt/sfdriver.c
@@ -385,6 +385,9 @@
 
 #ifdef TT_CONFIG_OPTION_POSTSCRIPT_NAMES
 
+    /* see `ttkern.h' */
+    tt_face_get_kerning,
+
     /* see `ttpost.h' */
     tt_face_get_ps_name,
     tt_face_free_ps_names,
--- a/src/sfnt/sfnt.c
+++ b/src/sfnt/sfnt.c
@@ -21,6 +21,7 @@
 #include <ft2build.h>
 #include "ttload.c"
 #include "ttcmap.c"
+#include "ttkern.c"
 #include "sfobjs.c"
 #include "sfdriver.c"
 
--- a/src/sfnt/sfobjs.c
+++ b/src/sfnt/sfobjs.c
@@ -20,6 +20,7 @@
 #include "sfobjs.h"
 #include "ttload.h"
 #include "ttcmap.h"
+#include "ttkern.h"
 #include FT_INTERNAL_SFNT_H
 #include FT_TRUETYPE_IDS_H
 #include FT_TRUETYPE_TAGS_H
@@ -503,11 +504,13 @@
 #endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */
 
     if ( LOAD_( hdmx )    ||
-         LOAD_( gasp )    ||
-         LOAD_( kerning ) ||
          LOAD_( pclt )    )
       goto Exit;
 
+    /* consider the kerning and gasp tables as optional */
+    (void)LOAD_( gasp );
+    (void)LOAD_( kerning );
+
     face->root.family_name = tt_face_get_name( face,
                                                TT_NAME_ID_PREFERRED_FAMILY );
     if ( !face->root.family_name )
@@ -553,9 +556,11 @@
       if ( face->vertical_info )
         flags |= FT_FACE_FLAG_VERTICAL;
 
+#if 0
       /* kerning available ? */
-      if ( face->kern_pairs )
+      if ( TT_FACE_HAS_KERNING(face) )
         flags |= FT_FACE_FLAG_KERNING;
+#endif
 
 #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
       /* Don't bother to load the tables unless somebody asks for them. */
@@ -802,8 +807,7 @@
     }
 
     /* freeing the kerning table */
-    FT_FREE( face->kern_pairs );
-    face->num_kern_pairs = 0;
+    tt_face_done_kern( face );
 
     /* freeing the collection table */
     FT_FREE( face->ttc_header.offsets );
@@ -823,8 +827,19 @@
     }
 
     /* freeing the horizontal metrics */
+#ifdef FT_OPTIMIZE_MEMORY
+    {
+      FT_Stream  stream = FT_FACE_STREAM( face );
+      
+      FT_FRAME_RELEASE( face->horz_metrics );
+      FT_FRAME_RELEASE( face->vert_metrics );
+      face->horz_metrics_size = 0;
+      face->vert_metrics_size = 0;
+    }
+#else
     FT_FREE( face->horizontal.long_metrics );
     FT_FREE( face->horizontal.short_metrics );
+#endif
 
     /* freeing the vertical ones, if any */
     if ( face->vertical_info )
--- /dev/null
+++ b/src/sfnt/ttkern.c
@@ -1,0 +1,479 @@
+/***************************************************************************/
+/*                                                                         */
+/*  ttkern.h                                                               */
+/*                                                                         */
+/*    Load the basic TrueType kerning table. This doesn't handle           */
+/*    kerning data within the GPOS table at the moment.                    */
+/*                                                                         */
+/*  Copyright 1996-2001, 2002, 2003, 2004 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.                                        */
+/*                                                                         */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_DEBUG_H
+#include FT_INTERNAL_STREAM_H
+#include FT_TRUETYPE_TAGS_H
+#include "ttload.h"
+
+#include "sferrors.h"
+
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* The macro FT_COMPONENT is used in trace mode.  It is an implicit      */
+  /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log  */
+  /* messages during execution.                                            */
+  /*                                                                       */
+#undef  FT_COMPONENT
+#define FT_COMPONENT  trace_ttload
+
+
+#undef  TT_KERN_INDEX
+#define TT_KERN_INDEX( g1, g2 )  ( ( (FT_ULong)(g1) << 16 ) | (g2) )
+
+
+#ifdef FT_OPTIMIZE_MEMORY
+
+  FT_LOCAL_DEF( FT_Error )
+  tt_face_load_kern( TT_Face    face,
+                     FT_Stream  stream )
+  {
+    FT_Error   error;
+    FT_Memory  memory = stream->memory;
+    FT_ULong   table_size;
+    FT_Byte*   p;
+    FT_Byte*   p_limit;
+    FT_UInt    nn, num_tables;
+    FT_UInt32  avail = 0, ordered = 0;
+
+
+    /* the kern table is optional; exit silently if it is missing */
+    error = face->goto_table( face, TTAG_kern, stream, &table_size );
+    if ( error )
+      goto Exit;
+
+    if ( table_size < 4 )  /* the case of a malformed table */
+    {
+      FT_ERROR(( "kerning table is too small - ignored\n" ));
+      error = SFNT_Err_Table_Missing;
+      goto Exit;
+    }
+
+    if ( FT_FRAME_EXTRACT( table_size, face->kern_table ) )
+    {
+      FT_ERROR(( "could not extract kerning table\n" ));
+      goto Exit;
+    }
+      
+    face->kern_table_size = table_size;
+    
+    p       = face->kern_table;
+    p_limit = p + table_size;
+
+    p         += 2; /* skip version */
+    num_tables = FT_NEXT_USHORT(p);
+    
+    if ( num_tables > 32 ) /* we only support up to 32 sub-tables */
+      num_tables = 32;
+
+    for ( nn = 0; nn < num_tables; nn++ )
+    {
+      FT_UInt    num_pairs, version, length, coverage;
+      FT_Byte*   p_next;
+      FT_UInt32  mask = 1UL << nn;
+      
+      if ( p + 6 > p_limit )
+        break;
+
+      p_next = p;
+      
+      version  = FT_NEXT_USHORT(p);
+      length   = FT_NEXT_USHORT(p);
+      coverage = FT_NEXT_USHORT(p);
+      
+      if ( length <= 6 )
+        break;
+
+      p_next += length;
+      
+      if ( (coverage & ~8) != 0x0001 ||  /* only use horizontal kerning tables */
+           p+8 > p_limit               )
+        goto NextTable;
+
+      num_pairs = FT_NEXT_USHORT(p);
+      p        += 6;
+      
+      if ( p + 6*num_pairs > p_limit )
+        goto NextTable;
+
+      avail |= mask;
+      
+     /* now, try to see if the pairs in this table are ordered.
+      * when they are, we'll be able to use binary search
+      */
+      if ( num_pairs > 0 )
+      {
+        FT_UInt    count;
+        FT_UInt    old_pair;
+        
+        old_pair = FT_NEXT_ULONG(p);
+        p       += 2;
+
+        for ( count = num_pairs-1; count > 0; count-- )
+        {
+          FT_UInt32  cur_pair;
+          
+          cur_pair = FT_NEXT_ULONG(p);
+          if ( cur_pair <= old_pair )
+            break;
+
+          p += 2;
+          old_pair = cur_pair;
+        }
+        
+        if ( count == 0 )
+          ordered |= mask;
+      }
+
+    NextTable:
+      p = p_next;
+    }
+
+    face->num_kern_tables = nn;
+    face->kern_avail_bits = avail;
+    face->kern_order_bits = ordered;
+    
+  Exit:
+    return error;
+  }
+
+
+  FT_LOCAL_DEF( void )
+  tt_face_done_kern( TT_Face  face )
+  {
+    FT_Stream  stream = face->root.stream;
+    
+    FT_FRAME_RELEASE( face->kern_table );
+    face->kern_table_size = 0;
+    face->num_kern_tables = 0;
+    face->kern_avail_bits = 0;
+    face->kern_order_bits = 0;
+  }
+
+
+  FT_LOCAL_DEF( FT_Int )
+  tt_face_get_kerning( TT_Face  face,
+                       FT_UInt  left_glyph,
+                       FT_UInt  right_glyph )
+  {
+    FT_Int    result = 0;
+    FT_Int    value;
+    FT_UInt   count, mask = 1;
+    FT_Byte*  p       = face->kern_table;
+    FT_Byte*  p_limit = p + face->kern_table_size;
+
+    p   += 4;
+    mask = 0x0001;
+
+    for ( count = face->num_kern_tables; count > 0; count--, mask <<= 1 )
+    {
+      FT_Byte* base     = p;
+      FT_Byte* next     = base;
+      FT_UInt  version  = FT_NEXT_USHORT(p);
+      FT_UInt  length   = FT_NEXT_USHORT(p);
+      FT_UInt  coverage = FT_NEXT_USHORT(p);
+      
+      next = base + length;
+      
+      if ( (face->kern_avail_bits & mask) == 0 )
+        goto NextTable;
+
+      if ( p+8 > next )
+        goto NextTable;
+
+      switch ( coverage >> 8 )
+      {
+      case 0:
+          {
+            FT_UInt   num_pairs = FT_NEXT_USHORT(p);
+            FT_ULong  key0      = TT_KERN_INDEX(left_glyph,right_glyph);
+            FT_Int    value     = 0;
+            
+            p += 6;
+            
+            if ( face->kern_order_bits & mask )   /* binary search */
+            {
+              FT_UInt   min = 0;
+              FT_UInt   max = num_pairs;
+              FT_Byte*  q;
+              
+              while ( min < max )
+              {
+                FT_UInt   mid = (min+max) >> 1;
+                FT_Byte*  q = p + 6*mid;
+                FT_ULong   key;
+                
+                key   = FT_NEXT_ULONG(q);
+                
+                if ( key == key0 )
+                {
+                  value = FT_PEEK_SHORT(q);
+                  break;
+                }
+                if ( key < key0 )
+                  min = mid+1;
+                else
+                  max = mid;
+              }
+            }
+            else /* linear search */
+            {
+              FT_UInt  count = num_pairs;
+
+              for ( ; count > 0; count-- )
+              {
+                FT_ULong  key = FT_NEXT_ULONG(p);
+                
+                if ( key == key0 )
+                {
+                  value = FT_PEEK_SHORT(p);
+                  break;
+                }
+                p += 2;
+              }
+            }
+          }
+          break;
+
+      /* we don't support format 2 because we've never seen a single font
+       * using it in real life...
+       */
+
+      default:
+          ;
+      }
+
+      if ( coverage & 8 ) /* overide or addition */
+        result = value;
+      else
+        result += value;
+        
+    NextTable:
+      p = next;
+    }
+
+  Exit:
+    return result;
+  }
+
+  
+#else /* !OPTMIZE_MEMORY */
+
+  FT_CALLBACK_DEF( int )
+  tt_kern_pair_compare( const void*  a,
+                        const void*  b );
+
+
+  FT_LOCAL_DEF( FT_Error )
+  tt_face_load_kern( TT_Face    face,
+                     FT_Stream  stream )
+  {
+    FT_Error   error;
+    FT_Memory  memory = stream->memory;
+
+    FT_UInt    n, num_tables;
+
+
+    /* the kern table is optional; exit silently if it is missing */
+    error = face->goto_table( face, TTAG_kern, stream, 0 );
+    if ( error )
+      return SFNT_Err_Ok;
+
+    if ( FT_FRAME_ENTER( 4L ) )
+      goto Exit;
+
+    (void)FT_GET_USHORT();         /* version */
+    num_tables = FT_GET_USHORT();
+
+    FT_FRAME_EXIT();
+
+    for ( n = 0; n < num_tables; n++ )
+    {
+      FT_UInt  coverage;
+      FT_UInt  length;
+
+
+      if ( FT_FRAME_ENTER( 6L ) )
+        goto Exit;
+
+      (void)FT_GET_USHORT();           /* version                 */
+      length   = FT_GET_USHORT() - 6;  /* substract header length */
+      coverage = FT_GET_USHORT();
+
+      FT_FRAME_EXIT();
+
+      if ( coverage == 0x0001 )
+      {
+        FT_UInt        num_pairs;
+        TT_Kern0_Pair  pair;
+        TT_Kern0_Pair  limit;
+
+
+        /* found a horizontal format 0 kerning table! */
+        if ( FT_FRAME_ENTER( 8L ) )
+          goto Exit;
+
+        num_pairs = FT_GET_USHORT();
+
+        /* skip the rest */
+
+        FT_FRAME_EXIT();
+
+        /* allocate array of kerning pairs */
+        if ( FT_QNEW_ARRAY( face->kern_pairs, num_pairs ) ||
+             FT_FRAME_ENTER( 6L * num_pairs )             )
+          goto Exit;
+
+        pair  = face->kern_pairs;
+        limit = pair + num_pairs;
+        for ( ; pair < limit; pair++ )
+        {
+          pair->left  = FT_GET_USHORT();
+          pair->right = FT_GET_USHORT();
+          pair->value = FT_GET_USHORT();
+        }
+
+        FT_FRAME_EXIT();
+
+        face->num_kern_pairs   = num_pairs;
+        face->kern_table_index = n;
+
+        /* ensure that the kerning pair table is sorted (yes, some */
+        /* fonts have unsorted tables!)                            */
+
+        if ( num_pairs > 0 )
+        {
+          TT_Kern0_Pair  pair0 = face->kern_pairs;
+          FT_ULong       prev  = TT_KERN_INDEX( pair0->left, pair0->right );
+          
+
+          for ( pair0++; pair0 < limit; pair0++ )
+          {
+            FT_ULong  next = TT_KERN_INDEX( pair0->left, pair0->right );
+            
+
+            if ( next < prev )
+              goto SortIt;
+              
+            prev = next;
+          }
+          goto Exit;
+          
+        SortIt:
+          ft_qsort( (void*)face->kern_pairs, (int)num_pairs,
+                    sizeof ( TT_Kern0_PairRec ), tt_kern_pair_compare );
+        }
+
+        goto Exit;
+      }
+
+      if ( FT_STREAM_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;
+  }
+
+  FT_CALLBACK_DEF( int )
+  tt_kern_pair_compare( const void*  a,
+                        const void*  b )
+  {
+    TT_Kern0_Pair  pair1 = (TT_Kern0_Pair)a;
+    TT_Kern0_Pair  pair2 = (TT_Kern0_Pair)b;
+
+    FT_ULong  index1 = TT_KERN_INDEX( pair1->left, pair1->right );
+    FT_ULong  index2 = TT_KERN_INDEX( pair2->left, pair2->right );
+
+
+    return ( index1 < index2 ? -1 :
+           ( index1 > index2 ?  1 : 0 ));
+  }
+
+
+  FT_LOCAL_DEF( void )
+  tt_face_done_kern( TT_Face  face )
+  {
+    FT_Memory  memory = face->root.stream->memory;
+    
+    FT_FREE( face->kern_pairs );
+    face->num_kern_pairs = 0;
+  }
+
+
+
+  FT_LOCAL_DEF( FT_Int )
+  tt_face_get_kerning( TT_Face     face,
+                       FT_UInt     left_glyph,
+                       FT_UInt     right_glyph )
+  {
+    FT_Int         result = 0;
+    TT_Kern0_Pair  pair;
+
+
+    if ( face && face->kern_pairs )
+    {
+      /* there are some kerning pairs in this font file! */
+      FT_ULong  search_tag = TT_KERN_INDEX( left_glyph, right_glyph );
+      FT_Long   left, right;
+
+
+      left  = 0;
+      right = face->num_kern_pairs - 1;
+
+      while ( left <= right )
+      {
+        FT_Long   middle = left + ( ( right - left ) >> 1 );
+        FT_ULong  cur_pair;
+
+
+        pair     = face->kern_pairs + middle;
+        cur_pair = TT_KERN_INDEX( pair->left, pair->right );
+
+        if ( cur_pair == search_tag )
+          goto Found;
+
+        if ( cur_pair < search_tag )
+          left = middle + 1;
+        else
+          right = middle - 1;
+      }
+    }
+
+  Exit:
+    return result;
+
+  Found:
+    result = pair->value;
+    goto Exit;
+  }
+
+#endif /* !OPTIMIZE_MEMORY */
+
+
+#undef TT_KERN_INDEX
+  
+/* END */
--- /dev/null
+++ b/src/sfnt/ttkern.h
@@ -1,0 +1,55 @@
+/***************************************************************************/
+/*                                                                         */
+/*  ttkern.h                                                               */
+/*                                                                         */
+/*    Load the basic TrueType kerning table. This doesn't handle           */
+/*    kerning data within the GPOS table at the moment.                    */
+/*                                                                         */
+/*  Copyright 1996-2001, 2002 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.                                        */
+/*                                                                         */
+/***************************************************************************/
+
+
+#ifndef __TTKERN_H__
+#define __TTKERN_H__
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_STREAM_H
+#include FT_INTERNAL_TRUETYPE_TYPES_H
+
+
+FT_BEGIN_HEADER
+
+
+  FT_LOCAL( FT_Error  )
+  tt_face_load_kern( TT_Face    face,
+                     FT_Stream  stream );
+
+  FT_LOCAL( void )
+  tt_face_done_kern( TT_Face  face );
+
+  FT_LOCAL( FT_Int )
+  tt_face_get_kerning( TT_Face     face,
+                       FT_UInt     left_glyph,
+                       FT_UInt     right_glyph );
+
+#ifdef FT_OPTIMIZE_MEMORY
+#  define  TT_FACE_HAS_KERNING(face)   ((face)->kern_avail_bits != 0)
+#else
+#  define  TT_FACE_HAS_KERNING(face)   ((face)->kern_pairs != NULL)
+#endif
+
+FT_END_HEADER
+
+#endif /* __TTKERN_H__ */
+
+
+/* END */
--- a/src/sfnt/ttload.c
+++ b/src/sfnt/ttload.c
@@ -800,6 +800,7 @@
   /* <Return>                                                              */
   /*    FreeType error code.  0 means success.                             */
   /*                                                                       */
+#ifdef FT_OPTIMIZE_MEMORY
   static FT_Error
   tt_face_load_metrics( TT_Face    face,
                         FT_Stream  stream,
@@ -807,7 +808,82 @@
   {
     FT_Error   error;
     FT_Memory  memory = stream->memory;
+    FT_ULong   table_size;
+    FT_Byte**  ptable;
+    FT_ULong*  ptable_size;
+    
+    
+    FT_TRACE2(( "TT_Load_%s_Metrics: %08p\n", vertical ? "Vertical"
+                                                       : "Horizontal",
+                                              face ));
 
+    if ( vertical )
+    {
+      ptable      = &face->vert_metrics;
+      ptable_size = &face->vert_metrics_size;
+      
+      /* 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_size );
+      if ( error )
+      {
+        /* Set number_Of_VMetrics to 0! */
+        FT_TRACE2(( "  no vertical header in file.\n" ));
+        error = SFNT_Err_Ok;
+        goto Exit;
+      }
+    }
+    else
+    {
+      ptable      = &face->horz_metrics;
+      ptable_size = &face->horz_metrics_size;
+
+      error = face->goto_table( face, TTAG_hmtx, stream, &table_size );
+      if ( error )
+      {
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+        /* If this is an incrementally loaded font and there are */
+        /* overriding metrics tolerate a missing 'hmtx' table.   */
+        if ( face->root.internal->incremental_interface &&
+             face->root.internal->incremental_interface->funcs->
+               get_glyph_metrics )
+        {
+          face->horizontal.number_Of_HMetrics = 0;
+          error = SFNT_Err_Ok;
+          goto Exit;
+        }
+#endif
+
+        FT_ERROR(( " no horizontal metrics in file!\n" ));
+        error = SFNT_Err_Hmtx_Table_Missing;
+        goto Exit;
+      }
+    }
+    
+    if ( FT_FRAME_EXTRACT( table_size, *ptable ) )
+      goto Exit;
+      
+    *ptable_size = table_size;
+    
+  Exit:
+    return error;
+  }
+
+#else /* !OPTIMIZE_MEMORY */
+  static FT_Error
+  tt_face_load_metrics( TT_Face    face,
+                        FT_Stream  stream,
+                        FT_Bool    vertical )
+  {
+    FT_Error   error;
+    FT_Memory  memory = stream->memory;
+
     FT_ULong   table_len;
     FT_Long    num_shorts, num_longs, num_shorts_checked;
 
@@ -938,8 +1014,8 @@
   Exit:
     return error;
   }
+#endif /* !FT_OPTIMIZE_METRICS */
 
-
   /*************************************************************************/
   /*                                                                       */
   /* <Function>                                                            */
@@ -1611,206 +1687,91 @@
   }
 
 
-  FT_CALLBACK_DEF( int )
-  tt_kern_pair_compare( const void*  a,
-                        const void*  b );
-
-
   /*************************************************************************/
   /*                                                                       */
   /* <Function>                                                            */
-  /*    tt_face_load_kern                                                  */
+  /*    tt_face_load_hdmx                                                  */
   /*                                                                       */
   /* <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.                                      */
+  /*    Loads the horizontal device metrics table.                         */
   /*                                                                       */
   /* <Input>                                                               */
   /*    face   :: A handle to the target face object.                      */
   /*                                                                       */
-  /*    stream :: The input stream.                                        */
+  /*    stream :: A handle to the input stream.                            */
   /*                                                                       */
   /* <Return>                                                              */
   /*    FreeType error code.  0 means success.                             */
   /*                                                                       */
-  
-#undef  TT_KERN_INDEX
-#define TT_KERN_INDEX( g1, g2 )  ( ( (FT_ULong)g1 << 16 ) | g2 )
-
-
+#ifdef FT_OPTIMIZE_MEMORY
   FT_LOCAL_DEF( FT_Error )
-  tt_face_load_kern( TT_Face    face,
+  tt_face_load_hdmx( TT_Face    face,
                      FT_Stream  stream )
   {
     FT_Error   error;
     FT_Memory  memory = stream->memory;
+    FT_UInt    version, nn, num_records;
+    FT_ULong   table_size, record_size;
+    FT_Byte*   p;
+    FT_Byte*   limit;
 
-    FT_UInt    n, num_tables;
-
-
-    /* the kern table is optional; exit silently if it is missing */
-    error = face->goto_table( face, TTAG_kern, stream, 0 );
-    if ( error )
+    /* this table is optional */
+    error = face->goto_table( face, TTAG_hdmx, stream, &table_size );
+    if ( error || table_size < 8 )
       return SFNT_Err_Ok;
 
-    if ( FT_FRAME_ENTER( 4L ) )
+    if ( FT_FRAME_EXTRACT( table_size, face->hdmx_table ) )
       goto Exit;
 
-    (void)FT_GET_USHORT();         /* version */
-    num_tables = FT_GET_USHORT();
+    p     = face->hdmx_table;
+    limit = p + table_size;
 
-    FT_FRAME_EXIT();
+    version     = FT_NEXT_USHORT(p);
+    num_records = FT_NEXT_USHORT(p);
+    record_size = FT_NEXT_ULONG(p);
 
-    for ( n = 0; n < num_tables; n++ )
+    if ( version != 0 || num_records > 255 || record_size > 0x40000 )
     {
-      FT_UInt  coverage;
-      FT_UInt  length;
+      error = SFNT_Err_Invalid_File_Format;
+      goto Fail;
+    }
 
+    if ( FT_NEW_ARRAY( face->hdmx_record_sizes, num_records ) )
+      goto Fail;
 
-      if ( FT_FRAME_ENTER( 6L ) )
-        goto Exit;
-
-      (void)FT_GET_USHORT();           /* version                 */
-      length   = FT_GET_USHORT() - 6;  /* substract header length */
-      coverage = FT_GET_USHORT();
-
-      FT_FRAME_EXIT();
-
-      if ( coverage == 0x0001 )
-      {
-        FT_UInt        num_pairs;
-        TT_Kern0_Pair  pair;
-        TT_Kern0_Pair  limit;
-
-
-        /* found a horizontal format 0 kerning table! */
-        if ( FT_FRAME_ENTER( 8L ) )
-          goto Exit;
-
-        num_pairs = FT_GET_USHORT();
-
-        /* skip the rest */
-
-        FT_FRAME_EXIT();
-
-        /* allocate array of kerning pairs */
-        if ( FT_QNEW_ARRAY( face->kern_pairs, num_pairs ) ||
-             FT_FRAME_ENTER( 6L * num_pairs )             )
-          goto Exit;
-
-        pair  = face->kern_pairs;
-        limit = pair + num_pairs;
-        for ( ; pair < limit; pair++ )
-        {
-          pair->left  = FT_GET_USHORT();
-          pair->right = FT_GET_USHORT();
-          pair->value = FT_GET_USHORT();
-        }
-
-        FT_FRAME_EXIT();
-
-        face->num_kern_pairs   = num_pairs;
-        face->kern_table_index = n;
-
-        /* ensure that the kerning pair table is sorted (yes, some */
-        /* fonts have unsorted tables!)                            */
-
-#if 1
-        if ( num_pairs > 0 )     
-        {
-          TT_Kern0_Pair  pair0 = face->kern_pairs;
-          FT_ULong       prev  = TT_KERN_INDEX( pair0->left, pair0->right );
-          
-
-          for ( pair0++; pair0 < limit; pair0++ )
-          {
-            FT_ULong  next = TT_KERN_INDEX( pair0->left, pair0->right );
-            
-
-            if ( next < prev )
-              goto SortIt;
-              
-            prev = next;
-          }
-          goto Exit;
-          
-        SortIt:
-          ft_qsort( (void*)face->kern_pairs, (int)num_pairs,
-                    sizeof ( TT_Kern0_PairRec ), tt_kern_pair_compare );
-        }
-#else        
-        {
-          TT_Kern0_Pair  pair0    = face->kern_pairs;
-          FT_UInt        i;
-          
-
-          for ( i = 1; i < num_pairs; i++, pair0++ )
-          {
-            if ( tt_kern_pair_compare( pair0, pair0 + 1 ) != -1 )
-            {
-              ft_qsort( (void*)face->kern_pairs, (int)num_pairs,
-                        sizeof ( TT_Kern0_PairRec ), tt_kern_pair_compare );
-              break;
-            }
-          }
-        }
-#endif
-
-        goto Exit;
-      }
-
-      if ( FT_STREAM_SKIP( length ) )
-        goto Exit;
+    for ( nn = 0; nn < num_records; nn++ )
+    {
+      if ( p+record_size > limit )
+        break;
+        
+      face->hdmx_record_sizes[nn] = p[0];
+      p                          += record_size;
     }
+    
+    face->hdmx_record_count = nn;
+    face->hdmx_table_size   = table_size;
 
-    /* no kern table found -- doesn't matter */
-    face->kern_table_index = -1;
-    face->num_kern_pairs   = 0;
-    face->kern_pairs       = NULL;
-
   Exit:
     return error;
+    
+  Fail:
+    FT_FRAME_RELEASE( face->hdmx_table );
+    face->hdmx_table_size = 0;
+    goto Exit;
   }
 
 
-  FT_CALLBACK_DEF( int )
-  tt_kern_pair_compare( const void*  a,
-                        const void*  b )
+  FT_LOCAL_DEF( void )
+  tt_face_free_hdmx( TT_Face  face )
   {
-    TT_Kern0_Pair  pair1 = (TT_Kern0_Pair)a;
-    TT_Kern0_Pair  pair2 = (TT_Kern0_Pair)b;
-
-    FT_ULong  index1 = TT_KERN_INDEX( pair1->left, pair1->right );
-    FT_ULong  index2 = TT_KERN_INDEX( pair2->left, pair2->right );
-
-
-    return ( index1 < index2 ? -1 :
-           ( index1 > index2 ?  1 : 0 ));
+    FT_Stream  stream = face->root.stream;
+    FT_Memory  memory = stream->memory;
+    
+    FT_FREE( face->hdmx_record_sizes );
+    FT_FRAME_RELEASE( face->hdmx_table );
   }
 
-
-#undef TT_KERN_INDEX
-  
-
-
-  /*************************************************************************/
-  /*                                                                       */
-  /* <Function>                                                            */
-  /*    tt_face_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>                                                              */
-  /*    FreeType error code.  0 means success.                             */
-  /*                                                                       */
+#else /* !FT_OPTIMIZE_MEMORY */
   FT_LOCAL_DEF( FT_Error )
   tt_face_load_hdmx( TT_Face    face,
                      FT_Stream  stream )
@@ -1885,17 +1846,6 @@
   }
 
 
-  /*************************************************************************/
-  /*                                                                       */
-  /* <Function>                                                            */
-  /*    tt_face_free_hdmx                                                  */
-  /*                                                                       */
-  /* <Description>                                                         */
-  /*    Frees the horizontal device metrics table.                         */
-  /*                                                                       */
-  /* <Input>                                                               */
-  /*    face :: A handle to the target face object.                        */
-  /*                                                                       */
   FT_LOCAL_DEF( void )
   tt_face_free_hdmx( TT_Face  face )
   {
@@ -1912,6 +1862,8 @@
       face->hdmx.num_records = 0;
     }
   }
+
+#endif /* !OPTIMIZE_MEMORY */
 
 
 /* END */
--- a/src/sfnt/ttload.h
+++ b/src/sfnt/ttload.h
@@ -112,11 +112,6 @@
 
 
   FT_LOCAL( FT_Error )
-  tt_face_load_kern( TT_Face    face,
-                     FT_Stream  stream );
-
-
-  FT_LOCAL( FT_Error )
   tt_face_load_gasp( TT_Face    face,
                      FT_Stream  stream );
 
--- a/src/sfnt/ttsbit.c
+++ b/src/sfnt/ttsbit.c
@@ -185,7 +185,7 @@
   }
 
 
-  const FT_Frame_Field  sbit_metrics_fields[] =
+  static const FT_Frame_Field  sbit_metrics_fields[] =
   {
 #undef  FT_STRUCTURE
 #define FT_STRUCTURE  TT_SBit_MetricsRec
--- a/src/truetype/ttdriver.c
+++ b/src/truetype/ttdriver.c
@@ -69,7 +69,7 @@
   /*************************************************************************/
   /*                                                                       */
   /* <Function>                                                            */
-  /*    Get_Kerning                                                        */
+  /*    tt_get_kerning                                                     */
   /*                                                                       */
   /* <Description>                                                         */
   /*    A driver method used to return the kerning vector between two      */
@@ -99,56 +99,21 @@
   /*    They can be implemented by format-specific interfaces.             */
   /*                                                                       */
   static FT_Error
-  Get_Kerning( FT_Face     ttface,          /* TT_Face */
+  tt_get_kerning( FT_Face     ttface,          /* TT_Face */
                FT_UInt     left_glyph,
                FT_UInt     right_glyph,
                FT_Vector*  kerning )
   {
-    TT_Face        face = (TT_Face)ttface;
-    TT_Kern0_Pair  pair;
+    TT_Face       face = (TT_Face)ttface;
+    SFNT_Service  sfnt = (SFNT_Service) face->sfnt;
 
-
-    if ( !face )
-      return TT_Err_Invalid_Face_Handle;
-
     kerning->x = 0;
     kerning->y = 0;
 
-    if ( face->kern_pairs )
-    {
-      /* there are some kerning pairs in this font file! */
-      FT_ULong  search_tag = PAIR_TAG( left_glyph, right_glyph );
-      FT_Long   left, right;
-
-
-      left  = 0;
-      right = face->num_kern_pairs - 1;
-
-      while ( left <= right )
-      {
-        FT_Long   middle = left + ( ( right - left ) >> 1 );
-        FT_ULong  cur_pair;
-
-
-        pair     = face->kern_pairs + middle;
-        cur_pair = PAIR_TAG( pair->left, pair->right );
-
-        if ( cur_pair == search_tag )
-          goto Found;
-
-        if ( cur_pair < search_tag )
-          left = middle + 1;
-        else
-          right = middle - 1;
-      }
-    }
-
-  Exit:
-    return TT_Err_Ok;
-
-  Found:
-    kerning->x = pair->value;
-    goto Exit;
+    if ( sfnt )
+      kerning->x = sfnt->get_kerning( face, left_glyph, right_glyph );
+      
+    return 0;
   }
 
 
@@ -456,7 +421,7 @@
     Set_Pixel_Sizes,
     Load_Glyph,
 
-    Get_Kerning,
+    tt_get_kerning,
     0,                      /* FT_Face_AttachFunc      */
     0                       /* FT_Face_GetAdvancesFunc */
   };
--- a/src/truetype/ttgload.c
+++ b/src/truetype/ttgload.c
@@ -96,34 +96,99 @@
   /*    This function will much probably move to another component in the  */
   /*    near future, but I haven't decided which yet.                      */
   /*                                                                       */
-  FT_LOCAL_DEF( void )
-  TT_Get_Metrics( TT_HoriHeader*  header,
-                  FT_UInt         idx,
-                  FT_Short*       bearing,
-                  FT_UShort*      advance )
+#ifdef FT_OPTIMIZE_MEMORY
+  static void
+  tt_face_get_metrics( TT_Face       face,
+                       FT_Bool       vertical,
+                       FT_UInt       idx,
+                       FT_Short      *abearing,
+                       FT_UShort     *aadvance )
   {
+    TT_HoriHeader*  header;
+    FT_Byte*        p;
+    FT_Byte*        limit;
+    FT_UShort       k;
+
+    if ( vertical )
+    {
+      header = (TT_HoriHeader*)&face->vertical;
+      p      = face->vert_metrics;
+      limit  = p + face->vert_metrics_size;
+    }
+    else
+    {
+      header = &face->horizontal;
+      p      = face->horz_metrics;
+      limit  = p + face->horz_metrics_size;
+    }
+    
+    k = header->number_Of_HMetrics;
+    
+    if ( k > 0 )
+    {
+      if ( idx < (FT_UInt)k )
+      {
+        p += 4*idx;
+        if ( p+4 >= limit )
+          goto NoData;
+          
+        *aadvance = FT_NEXT_USHORT(p);
+        *abearing = FT_NEXT_SHORT(p);
+      }
+      else
+      {
+        p += 4*(k-1);
+        if ( p+4 > limit )
+          goto NoData;
+          
+        *aadvance = FT_NEXT_USHORT(p);
+        p += 2 + 2*(idx-k);
+        if ( p+2 > limit )
+          *abearing = 0;
+        else
+          *abearing = FT_PEEK_SHORT(p);
+      }
+    }
+    else
+    {
+    NoData:
+      *abearing = 0;
+      *aadvance = 0;
+    }
+  }
+#else
+  static void
+  tt_face_get_metrics( TT_Face       face,
+                       FT_Bool       vertical,
+                       FT_UInt       idx,
+                       FT_Short      *abearing,
+                       FT_UShort     *aadvance )
+  {
+    TT_HoriHeader*  header = (vertical ? (TT_HoriHeader*)&face->vertical
+                                       :                 &face->horizontal);
     TT_LongMetrics  longs_m;
-    FT_UShort       k = header->number_Of_HMetrics;
+    FT_UShort       k     = header->number_Of_HMetrics;
 
 
     if ( k == 0 )
     {
-      *bearing = *advance = 0;
+      *abearing = *aadvance = 0;
       return;
     }
 
     if ( idx < (FT_UInt)k )
     {
-      longs_m  = (TT_LongMetrics )header->long_metrics + idx;
-      *bearing = longs_m->bearing;
-      *advance = longs_m->advance;
+      longs_m   = (TT_LongMetrics )header->long_metrics + idx;
+      *abearing = longs_m->bearing;
+      *aadvance = longs_m->advance;
     }
     else
     {
-      *bearing = ((TT_ShortMetrics*)header->short_metrics)[idx - k];
-      *advance = ((TT_LongMetrics )header->long_metrics)[k - 1].advance;
+      *abearing = ((TT_ShortMetrics*)header->short_metrics)[idx - k];
+      *aadvance = ((TT_LongMetrics )header->long_metrics)[k - 1].advance;
     }
   }
+#endif
 
 
   /*************************************************************************/
@@ -139,7 +204,7 @@
                 FT_Short*   lsb,
                 FT_UShort*  aw )
   {
-    TT_Get_Metrics( &face->horizontal, idx, lsb, aw );
+    tt_face_get_metrics( face, 0, idx, lsb, aw );
 
     if ( check && face->postscript.isFixedPitch )
       *aw = face->horizontal.advance_Width_Max;
@@ -152,17 +217,36 @@
   /* in the font's `hdmx' table (if any).                                  */
   /*                                                                       */
   static FT_Byte*
-  Get_Advance_Widths( TT_Face    face,
-                      FT_UShort  ppem )
+  Get_Advance_WidthPtr( TT_Face  face,
+                        FT_Int   ppem,
+                        FT_UInt  gindex )
   {
+#ifdef FT_OPTIMIZE_MEMORY
+    FT_UInt   nn;
+    FT_Byte*  result = NULL;
+    FT_ULong  record_size = face->hdmx_record_size;
+    FT_Byte*  record      = face->hdmx_table + 8;
+    
+    for ( nn = 0; nn < face->hdmx_record_count; nn++ )
+      if ( face->hdmx_record_sizes[nn] == ppem )
+      {
+        gindex += 2;
+        if ( gindex < record_size )
+          result = record + gindex;
+        break;
+      }
+
+    return result;
+#else
     FT_UShort  n;
 
 
     for ( n = 0; n < face->hdmx.num_records; n++ )
       if ( face->hdmx.records[n].ppem == ppem )
-        return face->hdmx.records[n].widths;
+        return &face->hdmx.records[n].widths[gindex];
 
     return NULL;
+#endif
   }
 
 
@@ -189,7 +273,7 @@
     FT_UNUSED( check );
 
     if ( face->vertical_info )
-      TT_Get_Metrics( (TT_HoriHeader *)&face->vertical, idx, tsb, ah );
+      tt_face_get_metrics( face, 1, idx, tsb, ah );
 
 #if 1             /* Emperically determined, at variance with what MS said */
 
@@ -1782,12 +1866,13 @@
     if ( !face->postscript.isFixedPitch && size &&
          IS_HINTED( loader->load_flags )        )
     {
-      FT_Byte*  widths = Get_Advance_Widths( face,
-                                             size->root.metrics.x_ppem );
+      FT_Byte*  widthp = Get_Advance_WidthPtr( face,
+                                               size->root.metrics.x_ppem,
+                                               glyph_index );
 
 
-      if ( widths )
-        glyph->metrics.horiAdvance = widths[glyph_index] << 6;
+      if ( widthp )
+        glyph->metrics.horiAdvance = *widthp << 6;
     }
 
     /* set glyph dimensions */
--- a/src/truetype/ttgload.h
+++ b/src/truetype/ttgload.h
@@ -32,12 +32,6 @@
 
 
   FT_LOCAL( void )
-  TT_Get_Metrics( TT_HoriHeader*  header,
-                  FT_UInt         idx,
-                  FT_Short*       bearing,
-                  FT_UShort*      advance );
-
-  FT_LOCAL( void )
   TT_Init_Glyph_Loading( TT_Face  face );
 
   FT_LOCAL( FT_Error )