shithub: freetype+ttf2subf

Download patch

ref: 108fdbbbd3ab7ea18b512762abd01ab4a051ca34
parent: ea1e8d3a53e29d5ba00fff0ca2c468bf4e16e9af
author: Wu, Chia-I (吳佳一) <[email protected]>
date: Mon Jan 16 10:35:56 EST 2006

* src/psaux/afmparse.c, src/psaux/afmparse.h: New files which
implement an AFM parser.  It is used to parse an AFM file.

* src/psaux/psconv.c, src/psaux/psconv.h: New files to provide
conversion functions (e.g, PS real number => FT_Fixed) for the
PS_Parser and AFM_Parser.  Some of the functions are taken, with some
modifications, from the psobjs.c

* src/psaux/psobjs.c: Use functions from psconv.c.

* include/freetype/internal/psaux.h, src/psaux/psauxmod.c:: Add
`AFM_Parser' to the `psaux' service.

* src/psaux/psaux.c, src/psaux/rules.mk: Include those new files.

* src/tools/test_afm.c: A test program for AFM parser.

* include/freetype/internal/services/svkern.h,
include/freetype/internal/ftserv.h: New service `Kerning'.  It is
currently only used to get the track kerning information.

* src/type1/t1driver.c, src/type1/t1objs.c, src/type1/t1afm.c,
src/type1/t1afm.h: Update to use the AFM parser.
Provide the `Kerning' service.

* include/freetype/freetype.h, src/base/ftobjs.c: New API
`FT_Get_Track_Kerning'.

git/fs: mount .git/fs: mount/attach disallowed
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,33 @@
+2006-01-16  Chia-I Wu  <[email protected]>
+
+	* src/psaux/afmparse.c, src/psaux/afmparse.h: New files which
+	implement an AFM parser.  It is used to parse an AFM file.
+
+	* src/psaux/psconv.c, src/psaux/psconv.h: New files to provide
+	conversion functions (e.g, PS real number => FT_Fixed) for the
+	PS_Parser and AFM_Parser.  Some of the functions are taken, with some
+	modifications, from the psobjs.c
+
+	* src/psaux/psobjs.c: Use functions from psconv.c.
+
+	* include/freetype/internal/psaux.h, src/psaux/psauxmod.c:: Add
+	`AFM_Parser' to the `psaux' service.
+
+	* src/psaux/psaux.c, src/psaux/rules.mk: Include those new files.
+
+	* src/tools/test_afm.c: A test program for AFM parser.
+
+	* include/freetype/internal/services/svkern.h,
+	include/freetype/internal/ftserv.h: New service `Kerning'.  It is
+	currently only used to get the track kerning information.
+
+	* src/type1/t1driver.c, src/type1/t1objs.c, src/type1/t1afm.c,
+	src/type1/t1afm.h: Update to use the AFM parser.
+	Provide the `Kerning' service.
+
+	* include/freetype/freetype.h, src/base/ftobjs.c: New API
+	`FT_Get_Track_Kerning'.
+
 2006-01-15  Chia-I Wu  <[email protected]>
 
 	* include/freetype/internal/ftobjs.h, src/base/ftobjs.c,
--- a/include/freetype/freetype.h
+++ b/include/freetype/freetype.h
@@ -192,6 +192,7 @@
   /*    FT_Render_Mode                                                     */
   /*    FT_Get_Kerning                                                     */
   /*    FT_Kerning_Mode                                                    */
+  /*    FT_Get_Track_Kerning                                               */
   /*    FT_Get_Glyph_Name                                                  */
   /*    FT_Get_Postscript_Name                                             */
   /*                                                                       */
@@ -2709,6 +2710,34 @@
                   FT_UInt     right_glyph,
                   FT_UInt     kern_mode,
                   FT_Vector  *akerning );
+
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* <Function>                                                            */
+  /*    FT_Get_Track_Kerning                                               */
+  /*                                                                       */
+  /* <Description>                                                         */
+  /*    Return the track kerning for a given face object at a given size.  */
+  /*                                                                       */
+  /* <Input>                                                               */
+  /*    face        :: A handle to a source face object.                   */
+  /*                                                                       */
+  /*    point_size  :: The point size in 16.16 fractional points.          */
+  /*                                                                       */
+  /*    degree      :: The degree of tightness.                            */
+  /*                                                                       */
+  /* <Output>                                                              */
+  /*    akerning    :: The kerning in in 16.16 fractional points.          */
+  /*                                                                       */
+  /* <Return>                                                              */
+  /*    FreeType error code.  0 means success.                             */
+  /*                                                                       */
+  FT_EXPORT( FT_Error )
+  FT_Get_Track_Kerning( FT_Face    face,
+                        FT_Fixed   point_size,
+                        FT_Int     degree,
+                        FT_Fixed*  akerning );
 
 
   /*************************************************************************/
--- a/include/freetype/internal/ftserv.h
+++ b/include/freetype/internal/ftserv.h
@@ -313,6 +313,7 @@
 #define FT_SERVICE_TT_CMAP_H            <freetype/internal/services/svttcmap.h>
 #define FT_SERVICE_WINFNT_H             <freetype/internal/services/svwinfnt.h>
 #define FT_SERVICE_XFREE86_NAME_H       <freetype/internal/services/svxf86nm.h>
+#define FT_SERVICE_KERNING_H            <freetype/internal/services/svkern.h>
 
  /* */
 
--- a/include/freetype/internal/psaux.h
+++ b/include/freetype/internal/psaux.h
@@ -688,6 +688,96 @@
   /*************************************************************************/
   /*************************************************************************/
   /*****                                                               *****/
+  /*****                            AFM PARSER                         *****/
+  /*****                                                               *****/
+  /*************************************************************************/
+  /*************************************************************************/
+
+  typedef struct AFM_ParserRec_*  AFM_Parser;
+
+  typedef struct  AFM_TrackKernRec_
+  {
+    FT_Int    degree;
+    FT_Fixed  min_ptsize;
+    FT_Fixed  min_kern;
+    FT_Fixed  max_ptsize;
+    FT_Fixed  max_kern;
+  } AFM_TrackKernRec, *AFM_TrackKern;
+
+  typedef struct  AFM_KernPairRec_
+  {
+    FT_Int  index1;
+    FT_Int  index2;
+    FT_Int  x;
+    FT_Int  y;
+  } AFM_KernPairRec, *AFM_KernPair;
+
+  typedef struct  AFM_FontInfoRec_
+  {
+    FT_Bool        IsCIDFont;
+    AFM_TrackKern  TrackKerns;   /* free if non-NULL */
+    FT_Int         NumTrackKern;
+    AFM_KernPair   KernPairs;    /* free if non-NULL */
+    FT_Int         NumKernPair;
+  } AFM_FontInfoRec, *AFM_FontInfo;
+
+  typedef struct  AFM_Parser_FuncsRec_
+  {
+    FT_Error
+    (*init)( AFM_Parser  parser,
+             FT_Memory   memory,
+             FT_Byte*    base,
+             FT_Byte*    limit );
+
+    void
+    (*done)( AFM_Parser  parser );
+
+    FT_Error
+    (*parse)( AFM_Parser  parser );
+
+  } AFM_Parser_FuncsRec;
+
+  typedef struct AFM_StreamRec_*  AFM_Stream;
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* <Struct>                                                              */
+  /*    AFM_ParserRec                                                      */
+  /*                                                                       */
+  /* <Description>                                                         */
+  /*    An AFM_Parser is a parser for the AFM files.                       */
+  /*                                                                       */
+  /* <Fields>                                                              */
+  /*    memory    :: The object used for memory operations                 */
+  /*                   (alloc/realloc).                                    */
+  /*                                                                       */
+  /*    stream    :: This is an opaque object.                             */
+  /*                                                                       */
+  /*    FontInfo  :: The result will be stored here.                       */
+  /*                                                                       */
+  /*    get_index :: An user provided function to get glyph index by its   */
+  /*                 name.                                                 */
+  /*                                                                       */
+  typedef struct  AFM_ParserRec_
+  {
+    FT_Memory     memory;
+    AFM_Stream    stream;
+
+    AFM_FontInfo  FontInfo;
+
+    FT_Int
+    (*get_index)( const char*  name,
+                  FT_UInt      len,
+                  void*        user_data );
+
+    void*         user_data;
+
+  } AFM_ParserRec;
+
+
+  /*************************************************************************/
+  /*************************************************************************/
+  /*****                                                               *****/
   /*****                     TYPE1 CHARMAPS                            *****/
   /*****                                                               *****/
   /*************************************************************************/
@@ -720,6 +810,7 @@
     const PS_Parser_FuncsRec*   ps_parser_funcs;
     const T1_Builder_FuncsRec*  t1_builder_funcs;
     const T1_Decoder_FuncsRec*  t1_decoder_funcs;
+    const AFM_Parser_FuncsRec*  afm_parser_funcs;
 
     void
     (*t1_decrypt)( FT_Byte*   buffer,
--- /dev/null
+++ b/include/freetype/internal/services/svkern.h
@@ -1,0 +1,51 @@
+/***************************************************************************/
+/*                                                                         */
+/*  svkern.h                                                               */
+/*                                                                         */
+/*    The FreeType Kerning service (specification).                        */
+/*                                                                         */
+/*  Copyright 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.                                        */
+/*                                                                         */
+/***************************************************************************/
+
+
+#ifndef __SVKERN_H__
+#define __SVKERN_H__
+
+#include FT_INTERNAL_SERVICE_H
+#include FT_TRUETYPE_TABLES_H
+
+
+FT_BEGIN_HEADER
+
+#define FT_SERVICE_ID_KERNING  "kerning"
+
+
+  typedef FT_Error
+  (*FT_Kerning_TrackGetFunc)( FT_Face    face,
+                              FT_Fixed   point_size,
+                              FT_Int     degree,
+                              FT_Fixed*  akerning );
+
+  FT_DEFINE_SERVICE( Kerning )
+  {
+    FT_Kerning_TrackGetFunc  get_track;  
+  };
+
+  /* */
+
+ 
+FT_END_HEADER
+
+
+#endif /* __SVKERN_H__ */
+
+
+/* END */
--- a/src/base/ftobjs.c
+++ b/src/base/ftobjs.c
@@ -33,6 +33,7 @@
 #include FT_SERVICE_POSTSCRIPT_NAME_H
 #include FT_SERVICE_GLYPH_DICT_H
 #include FT_SERVICE_TT_CMAP_H
+#include FT_SERVICE_KERNING_H
 
 
   FT_BASE_DEF( FT_Pointer )
@@ -2371,6 +2372,40 @@
         }
       }
     }
+
+    return error;
+  }
+
+
+  /* documentation is in freetype.h */
+
+  FT_EXPORT_DEF( FT_Error )
+  FT_Get_Track_Kerning( FT_Face    face,
+                        FT_Fixed   point_size,
+                        FT_Int     degree,
+                        FT_Fixed*  akerning )
+  {
+    FT_Service_Kerning  service;
+    FT_Error            error = FT_Err_Ok;
+    FT_Driver           driver;
+
+
+    if ( !face )
+      return FT_Err_Invalid_Face_Handle;
+
+    if ( !akerning )
+      return FT_Err_Invalid_Argument;
+
+    driver = face->driver;
+
+    FT_FACE_FIND_SERVICE( face, service, KERNING );
+    if ( !service )
+      return FT_Err_Unimplemented_Feature;
+
+    error = service->get_track( face,
+                                point_size,
+                                degree,
+                                akerning );
 
     return error;
   }
--- /dev/null
+++ b/src/psaux/afmparse.c
@@ -1,0 +1,925 @@
+/***************************************************************************/
+/*                                                                         */
+/*  afmparse.c                                                             */
+/*                                                                         */
+/*    AFM parser (body).                                                   */
+/*                                                                         */
+/*  Copyright 2006 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 FT_INTERNAL_POSTSCRIPT_AUX_H
+#include FT_INTERNAL_DEBUG_H
+
+#include "afmparse.h"
+#include "psconv.h"
+
+#include "psauxerr.h"
+
+/***************************************************************************/
+/*                                                                         */
+/*    AFM_Stream                                                           */
+/*                                                                         */
+/* The use of AFM_Stream is largely inspired by parseAFM.[ch] from t1lib.  */
+/*                                                                         */
+/*                                                                         */
+
+  enum
+  {
+    AFM_STREAM_STATUS_NORMAL,
+    AFM_STREAM_STATUS_EOC,
+    AFM_STREAM_STATUS_EOL,
+    AFM_STREAM_STATUS_EOF
+  };
+
+
+  typedef struct  AFM_StreamRec_
+  {
+    FT_Byte*   cursor;
+    FT_Byte*   base;
+    FT_Byte*   limit;
+
+    FT_Int     status;
+
+  } AFM_StreamRec;
+
+
+#ifndef EOF
+#define EOF -1
+#endif
+
+/* this works because empty lines are ignored */
+#define AFM_IS_NEWLINE( ch ) ( ( ch ) == '\r' || ( ch ) == '\n' )
+
+#define AFM_IS_EOF( ch )     ( ( ch ) == EOF  || ( ch ) == '\x1a' )
+#define AFM_IS_SPACE( ch )   ( ( ch ) == ' '  || ( ch ) == '\t' )
+
+/* column separator; there is no `column' in the spec actually */
+#define AFM_IS_SEP( ch )     ( ( ch ) == ';' )
+
+#define AFM_GETC() \
+          ( ( ( stream )->cursor < ( stream )->limit ) \
+            ? *( stream )->cursor++                    \
+            : EOF )
+
+#define AFM_STREAM_KEY_BEGIN( stream ) \
+  (char*)( ( stream )->cursor - 1 )
+
+#define AFM_STREAM_KEY_LEN( stream, key ) \
+  ( (char*)( stream )->cursor - key - 1 )
+
+#define AFM_STATUS_EOC( stream ) \
+  ( ( stream )->status >= AFM_STREAM_STATUS_EOC )
+
+#define AFM_STATUS_EOL( stream ) \
+  ( ( stream )->status >= AFM_STREAM_STATUS_EOL )
+
+#define AFM_STATUS_EOF( stream ) \
+  ( ( stream )->status >= AFM_STREAM_STATUS_EOF )
+
+  static int
+  afm_stream_skip_spaces( AFM_Stream  stream )
+  {
+    int  ch;
+
+
+    if ( AFM_STATUS_EOC( stream ) )
+      return ';';
+
+    while ( 1 )
+    {
+      ch = AFM_GETC();
+      if ( !AFM_IS_SPACE( ch ) )
+        break;
+    }
+
+    if ( AFM_IS_NEWLINE( ch ) )
+      stream->status = AFM_STREAM_STATUS_EOL;
+    else if ( AFM_IS_SEP( ch ) )
+      stream->status = AFM_STREAM_STATUS_EOC;
+    else if ( AFM_IS_EOF( ch ) )
+      stream->status = AFM_STREAM_STATUS_EOF;
+
+    return ch;
+  }
+
+
+  /* read a key or val in current column */
+  static char*
+  afm_stream_read_one( AFM_Stream  stream )
+  {
+    char*  str;
+    int    ch;
+
+
+    afm_stream_skip_spaces( stream );
+    if ( AFM_STATUS_EOC( stream ) )
+      return NULL;
+
+    str = AFM_STREAM_KEY_BEGIN( stream );
+
+    while ( 1 )
+    {
+      ch = AFM_GETC();
+      if ( AFM_IS_SPACE( ch ) )
+        break;
+      else if ( AFM_IS_NEWLINE( ch ) )
+      {
+        stream->status = AFM_STREAM_STATUS_EOL;
+
+        break;
+      }
+      else if ( AFM_IS_SEP( ch ) )
+      {
+        stream->status = AFM_STREAM_STATUS_EOC;
+
+        break;
+      }
+      else if ( AFM_IS_EOF( ch ) )
+      {
+        stream->status = AFM_STREAM_STATUS_EOF;
+
+        break;
+      }
+    }
+
+    return str;
+  }
+
+
+  /* read a string (i.e., read to EOL) */
+  static char*
+  afm_stream_read_string( AFM_Stream  stream )
+  {
+    char*  str;
+    int    ch;
+
+
+    afm_stream_skip_spaces( stream );
+    if ( AFM_STATUS_EOL( stream ) )
+      return NULL;
+
+    str = AFM_STREAM_KEY_BEGIN( stream );
+
+    /* scan to eol */
+    while ( 1 )
+    {
+      ch = AFM_GETC();
+      if ( AFM_IS_NEWLINE( ch ) )
+      {
+        stream->status = AFM_STREAM_STATUS_EOL;
+
+        break;
+      }
+      else if ( AFM_IS_EOF( ch ) )
+      {
+        stream->status = AFM_STREAM_STATUS_EOF;
+
+        break;
+      }
+    }
+
+    return str;
+  }
+
+
+/***************************************************************************/
+/*                                                                         */
+/*    AFM_Parser                                                           */
+/*                                                                         */
+/*                                                                         */
+
+  /* all keys defined in Ch. 7-10 of 5004.AFM_Spec.pdf */
+  typedef enum
+  {
+    AFM_TOKEN_ASCENDER,
+    AFM_TOKEN_AXISLABEL,
+    AFM_TOKEN_AXISTYPE,
+    AFM_TOKEN_B,
+    AFM_TOKEN_BLENDAXISTYPES,
+    AFM_TOKEN_BLENDDESIGNMAP,
+    AFM_TOKEN_BLENDDESIGNPOSITIONS,
+    AFM_TOKEN_C,
+    AFM_TOKEN_CC,
+    AFM_TOKEN_CH,
+    AFM_TOKEN_CAPHEIGHT,
+    AFM_TOKEN_CHARWIDTH,
+    AFM_TOKEN_CHARACTERSET,
+    AFM_TOKEN_CHARACTERS,
+    AFM_TOKEN_DESCENDER,
+    AFM_TOKEN_ENCODINGSCHEME,
+    AFM_TOKEN_ENDAXIS,
+    AFM_TOKEN_ENDCHARMETRICS,
+    AFM_TOKEN_ENDCOMPOSITES,
+    AFM_TOKEN_ENDDIRECTION,
+    AFM_TOKEN_ENDFONTMETRICS,
+    AFM_TOKEN_ENDKERNDATA,
+    AFM_TOKEN_ENDKERNPAIRS,
+    AFM_TOKEN_ENDTRACKKERN,
+    AFM_TOKEN_ESCCHAR,
+    AFM_TOKEN_FAMILYNAME,
+    AFM_TOKEN_FONTBBOX,
+    AFM_TOKEN_FONTNAME,
+    AFM_TOKEN_FULLNAME,
+    AFM_TOKEN_ISBASEFONT,
+    AFM_TOKEN_ISCIDFONT,
+    AFM_TOKEN_ISFIXEDPITCH,
+    AFM_TOKEN_ISFIXEDV,
+    AFM_TOKEN_ITALICANGLE,
+    AFM_TOKEN_KP,
+    AFM_TOKEN_KPH,
+    AFM_TOKEN_KPX,
+    AFM_TOKEN_KPY,
+    AFM_TOKEN_L,
+    AFM_TOKEN_MAPPINGSCHEME,
+    AFM_TOKEN_METRICSSETS,
+    AFM_TOKEN_N,
+    AFM_TOKEN_NOTICE,
+    AFM_TOKEN_PCC,
+    AFM_TOKEN_STARTAXIS,
+    AFM_TOKEN_STARTCHARMETRICS,
+    AFM_TOKEN_STARTCOMPOSITES,
+    AFM_TOKEN_STARTDIRECTION,
+    AFM_TOKEN_STARTFONTMETRICS,
+    AFM_TOKEN_STARTKERNDATA,
+    AFM_TOKEN_STARTKERNPAIRS,
+    AFM_TOKEN_STARTKERNPAIRS0,
+    AFM_TOKEN_STARTKERNPAIRS1,
+    AFM_TOKEN_STARTTRACKKERN,
+    AFM_TOKEN_STDHW,
+    AFM_TOKEN_STDVW,
+    AFM_TOKEN_TRACKKERN,
+    AFM_TOKEN_UNDERLINEPOSITION,
+    AFM_TOKEN_UNDERLINETHICKNESS,
+    AFM_TOKEN_VV,
+    AFM_TOKEN_VVECTOR,
+    AFM_TOKEN_VERSION,
+    AFM_TOKEN_W,
+    AFM_TOKEN_W0,
+    AFM_TOKEN_W0X,
+    AFM_TOKEN_W0Y,
+    AFM_TOKEN_W1,
+    AFM_TOKEN_W1X,
+    AFM_TOKEN_W1Y,
+    AFM_TOKEN_WX,
+    AFM_TOKEN_WY,
+    AFM_TOKEN_WEIGHT,
+    AFM_TOKEN_WEIGHTVECTOR,
+    AFM_TOKEN_XHEIGHT,
+    N_AFM_TOKENS,
+    AFM_TOKEN_UNKNOWN
+  } AFM_Token;
+
+
+  static const char*  afm_key_table[N_AFM_TOKENS] =
+  {
+    "Ascender",
+    "AxisLabel",
+    "AxisType",
+    "B",
+    "BlendAxisTypes",
+    "BlendDesignMap",
+    "BlendDesignPositions",
+    "C",
+    "CC",
+    "CH",
+    "CapHeight",
+    "CharWidth",
+    "CharacterSet",
+    "Characters",
+    "Descender",
+    "EncodingScheme",
+    "EndAxis",
+    "EndCharMetrics",
+    "EndComposites",
+    "EndDirection",
+    "EndFontMetrics",
+    "EndKernData",
+    "EndKernPairs",
+    "EndTrackKern",
+    "EscChar",
+    "FamilyName",
+    "FontBBox",
+    "FontName",
+    "FullName",
+    "IsBaseFont",
+    "IsCIDFont",
+    "IsFixedPitch",
+    "IsFixedV",
+    "ItalicAngle",
+    "KP",
+    "KPH",
+    "KPX",
+    "KPY",
+    "L",
+    "MappingScheme",
+    "MetricsSets",
+    "N",
+    "Notice",
+    "PCC",
+    "StartAxis",
+    "StartCharMetrics",
+    "StartComposites",
+    "StartDirection",
+    "StartFontMetrics",
+    "StartKernData",
+    "StartKernPairs",
+    "StartKernPairs0",
+    "StartKernPairs1",
+    "StartTrackKern",
+    "StdHW",
+    "StdVW",
+    "TrackKern",
+    "UnderlinePosition",
+    "UnderlineThickness",
+    "VV",
+    "VVector",
+    "Version",
+    "W",
+    "W0",
+    "W0X",
+    "W0Y",
+    "W1",
+    "W1X",
+    "W1Y",
+    "WX",
+    "WY",
+    "Weight",
+    "WeightVector",
+    "XHeight"
+  };
+
+
+#define AFM_MAX_ARGUMENTS 5
+
+  static AFM_ValueRec  shared_vals[AFM_MAX_ARGUMENTS];
+
+
+  /*
+   * `afm_parser_read_vals' and `afm_parser_next_key' provides
+   * high-level operations to an AFM_Stream.  The rest of the
+   * parser functions should use them and should not access
+   * the AFM_Stream directly.
+   */
+
+  FT_LOCAL_DEF( FT_Int )
+  afm_parser_read_vals( AFM_Parser  parser,
+                        AFM_Value   vals,
+                        FT_Int      n )
+  {
+    AFM_Stream  stream = parser->stream;
+    char*       str;
+    FT_Int      i;
+
+
+    if ( n > AFM_MAX_ARGUMENTS )
+      return 0;
+
+    for ( i = 0; i < n; i++ )
+    {
+      FT_UInt  len;
+
+
+      if ( vals[i].type == AFM_VALUE_TYPE_STRING )
+        str = afm_stream_read_string( stream );
+      else
+        str = afm_stream_read_one( stream );
+
+      if ( !str )
+        break;
+
+      len = AFM_STREAM_KEY_LEN( stream, str );
+
+      switch ( vals[i].type )
+      {
+        case AFM_VALUE_TYPE_STRING:
+        case AFM_VALUE_TYPE_NAME:
+            if ( !FT_QAlloc( parser->memory, len + 1, (void**)&vals[i].u.s  ) )
+            {
+              ft_memcpy( vals[i].u.s, str, len );
+              vals[i].u.s[len] = '\0';
+            }
+            break;
+        case AFM_VALUE_TYPE_FIXED:
+          vals[i].u.f = PS_Conv_ToFixed( (FT_Byte**)&str,
+                                         (FT_Byte*)str + len,
+                                         0 );
+          break;
+        case AFM_VALUE_TYPE_INTEGER:
+          vals[i].u.i = PS_Conv_ToInt( (FT_Byte**)&str,
+                                       (FT_Byte*)str + len );
+          break;
+        case AFM_VALUE_TYPE_BOOL:
+          vals[i].u.b = ( len == 4                          &&
+                          ft_strncmp( str, "true", 4 ) == 0 );
+          break;
+        case AFM_VALUE_TYPE_INDEX:
+          if ( parser->get_index )
+            vals[i].u.i = parser->get_index( str,
+                                             len,
+                                             parser->user_data );
+          else
+            vals[i].u.i = 0;
+          break;
+      }
+    }
+
+    return i;
+  }
+
+
+  FT_LOCAL_DEF( char* )
+  afm_parser_next_key( AFM_Parser  parser,
+                       FT_Bool     line,
+                       FT_UInt*    len )
+  {
+    AFM_Stream  stream = parser->stream;
+    char*       key;
+
+
+    if ( line )
+    {
+      while ( 1 )
+      {
+        /* skip current line */
+        if ( !AFM_STATUS_EOL( stream ) )
+          afm_stream_read_string( stream );
+
+        stream->status = AFM_STREAM_STATUS_NORMAL;
+        key = afm_stream_read_one( stream );
+
+        /* skip empty line */
+        if ( !key                      &&
+             !AFM_STATUS_EOF( stream ) &&
+             AFM_STATUS_EOL( stream ) )
+          continue;
+
+        break;
+      }
+    }
+    else
+    {
+      while ( 1 )
+      {
+        /* skip current column */
+        while ( !AFM_STATUS_EOC( stream ) )
+          afm_stream_read_one( stream );
+
+        stream->status = AFM_STREAM_STATUS_NORMAL;
+        key = afm_stream_read_one( stream );
+
+        /* skip empty column */
+        if ( !key                      &&
+             !AFM_STATUS_EOF( stream ) &&
+             AFM_STATUS_EOC( stream ) )
+          continue;
+
+        break;
+      }
+    }
+
+    if ( len )
+      *len = ( key ) ? AFM_STREAM_KEY_LEN( stream, key )
+                     : 0;
+
+    return key;
+  }
+
+
+  static AFM_Token
+  afm_tokenize( const char*  key,
+                FT_UInt      len )
+  {
+    int  n;
+
+
+    for ( n = 0; n < N_AFM_TOKENS; n++ )
+    {
+      if ( *( afm_key_table[n] ) == *key )
+      {
+        for ( ; n < N_AFM_TOKENS; n++ )
+        {
+          if ( *( afm_key_table[n] ) != *key )
+            return AFM_TOKEN_UNKNOWN;
+
+          if ( ft_strncmp( afm_key_table[n], key, len ) == 0 )
+            return n;
+        }
+      }
+    }
+
+    return AFM_TOKEN_UNKNOWN;
+  }
+
+
+  FT_LOCAL_DEF( FT_Error )
+  afm_parser_init( AFM_Parser  parser,
+                   FT_Memory   memory,
+                   FT_Byte*    base,
+                   FT_Byte*    limit )
+  {
+    AFM_Stream  stream;
+    FT_Error    error;
+
+
+    if ( FT_NEW( stream ) )
+      return error;
+
+    stream->cursor  = stream->base = base;
+    stream->limit   = limit;
+
+    /* so that the first call won't skip the first line */
+    stream->status  = AFM_STREAM_STATUS_EOL;
+
+    parser->memory    = memory;
+    parser->stream    = stream;
+    parser->FontInfo  = NULL;
+    parser->get_index = NULL;
+
+    return PSaux_Err_Ok;
+  }
+
+
+  FT_LOCAL( void )
+  afm_parser_done( AFM_Parser  parser )
+  {
+    FT_Memory  memory = parser->memory;
+
+
+    FT_FREE( parser->stream );
+  }
+
+
+  FT_LOCAL_DEF( FT_Error )
+  afm_parser_read_int( AFM_Parser  parser,
+                       FT_Int*     aint )
+  {
+    AFM_ValueRec  val;
+
+
+    val.type = AFM_VALUE_TYPE_INTEGER;
+
+    if ( afm_parser_read_vals( parser, &val, 1 ) == 1 )
+    {
+      *aint = val.u.i;
+
+      return PSaux_Err_Ok;
+    }
+    else
+      return PSaux_Err_Syntax_Error;
+  }
+                       
+
+  static FT_Error
+  afm_parse_track_kern( AFM_Parser  parser )
+  {
+    AFM_FontInfo   fi = parser->FontInfo;
+    AFM_TrackKern  tk;
+    char*          key;
+    FT_UInt        len;
+    int            n = -1;
+
+
+    if ( afm_parser_read_int( parser, &fi->NumTrackKern ) )
+        goto Fail;
+
+    if ( fi->NumTrackKern )
+    {
+      FT_Memory  memory = parser->memory;
+      FT_Error   error;
+
+
+      FT_QNEW_ARRAY( fi->TrackKerns, fi->NumTrackKern );
+      if ( error )
+        return error;
+    }
+
+    while ( ( key = afm_parser_next_key( parser, 1, &len ) ) )
+    {
+      switch ( afm_tokenize( key, len ) )
+      {
+      case AFM_TOKEN_TRACKKERN:
+        n++;
+
+        if ( n >= fi->NumTrackKern )
+          goto Fail;
+
+        tk = fi->TrackKerns + n;
+
+        shared_vals[0].type = AFM_VALUE_TYPE_INTEGER;
+        shared_vals[1].type = AFM_VALUE_TYPE_FIXED;
+        shared_vals[2].type = AFM_VALUE_TYPE_FIXED;
+        shared_vals[3].type = AFM_VALUE_TYPE_FIXED;
+        shared_vals[4].type = AFM_VALUE_TYPE_FIXED;
+        if ( afm_parser_read_vals( parser, shared_vals, 5 ) != 5 )
+          goto Fail;
+
+        tk->degree     = shared_vals[0].u.i;
+        tk->min_ptsize = shared_vals[1].u.f;
+        tk->min_kern   = shared_vals[2].u.f;
+        tk->max_ptsize = shared_vals[3].u.f;
+        tk->max_kern   = shared_vals[4].u.f;
+
+        /* is this correct? */
+        if ( tk->degree < 0 && tk->min_kern > 0 )
+          tk->min_kern = -tk->min_kern;
+        break;
+
+      case AFM_TOKEN_ENDTRACKKERN:
+      case AFM_TOKEN_ENDKERNDATA:
+      case AFM_TOKEN_ENDFONTMETRICS:
+        fi->NumTrackKern = n + 1;
+        return PSaux_Err_Ok;
+        break;
+
+      case AFM_TOKEN_UNKNOWN:
+        break;
+
+      default:
+        goto Fail;
+        break;
+      }
+    }
+
+  Fail:
+    return PSaux_Err_Syntax_Error;
+  }
+
+
+#undef  KERN_INDEX
+#define KERN_INDEX( g1, g2 )  ( ( (FT_ULong)g1 << 16 ) | g2 )
+
+  /* compare two kerning pairs */
+  FT_CALLBACK_DEF( int )
+  afm_compare_kern_pairs( const void*  a,
+                          const void*  b )
+  {
+    AFM_KernPair  kp1 = (AFM_KernPair)a;
+    AFM_KernPair  kp2 = (AFM_KernPair)b;
+
+    FT_ULong  index1 = KERN_INDEX( kp1->index1, kp1->index2 );
+    FT_ULong  index2 = KERN_INDEX( kp2->index1, kp2->index2 );
+
+
+    return (int)( index1 - index2 );
+  }
+
+
+  static FT_Error
+  afm_parse_kern_pairs( AFM_Parser  parser )
+  {
+    AFM_FontInfo  fi = parser->FontInfo;
+    AFM_KernPair  kp;
+    char*         key;
+    FT_UInt       len;
+    int           n = -1;
+
+
+    if ( afm_parser_read_int( parser, &fi->NumKernPair ) )
+      goto Fail;
+
+    if ( fi->NumKernPair )
+    {
+      FT_Memory  memory = parser->memory;
+      FT_Error   error;
+
+
+      FT_QNEW_ARRAY( fi->KernPairs, fi->NumKernPair );
+      if ( error )
+        return error;
+    }
+
+    while ( ( key = afm_parser_next_key( parser, 1, &len ) ) )
+    {
+      AFM_Token  token = afm_tokenize( key, len );
+
+
+      switch ( token )
+      {
+      case AFM_TOKEN_KP:
+      case AFM_TOKEN_KPX:
+      case AFM_TOKEN_KPY:
+        {
+          FT_Int  r;
+
+
+          n++;
+
+          if ( n >= fi->NumKernPair )
+            goto Fail;
+
+          kp = fi->KernPairs + n;
+
+          shared_vals[0].type = AFM_VALUE_TYPE_INDEX;
+          shared_vals[1].type = AFM_VALUE_TYPE_INDEX;
+          shared_vals[2].type = AFM_VALUE_TYPE_INTEGER;
+          shared_vals[3].type = AFM_VALUE_TYPE_INTEGER;
+          r = afm_parser_read_vals( parser, shared_vals, 4 );
+          if ( r < 3 )
+            goto Fail;
+
+          kp->index1 = shared_vals[0].u.i;
+          kp->index2 = shared_vals[1].u.i;
+          if ( token == AFM_TOKEN_KPY )
+          {
+            kp->x = 0;
+            kp->y = shared_vals[2].u.i;
+          }
+          else
+          {
+            kp->x = shared_vals[2].u.i;
+            kp->y = ( token == AFM_TOKEN_KP && r == 4 )
+                      ? shared_vals[3].u.i : 0;
+          }
+        }
+        break;
+
+      case AFM_TOKEN_ENDKERNPAIRS:
+      case AFM_TOKEN_ENDKERNDATA:
+      case AFM_TOKEN_ENDFONTMETRICS:
+        fi->NumKernPair = n + 1;
+        ft_qsort( fi->KernPairs, fi->NumKernPair,
+                  sizeof( AFM_KernPairRec ),
+                  afm_compare_kern_pairs );
+        return PSaux_Err_Ok;
+        break;
+
+      case AFM_TOKEN_UNKNOWN:
+        break;
+
+      default:
+        goto Fail;
+        break;
+      }
+    }
+
+  Fail:
+    return PSaux_Err_Syntax_Error;
+  }
+
+
+  static FT_Error
+  afm_parse_kern_data( AFM_Parser  parser )
+  {
+    FT_Error  error;
+    char*     key;
+    FT_UInt   len;
+
+
+    while ( ( key = afm_parser_next_key( parser, 1, &len ) ) )
+    {
+      switch ( afm_tokenize( key, len ) )
+      {
+      case AFM_TOKEN_STARTTRACKKERN:
+        error = afm_parse_track_kern( parser );
+        if ( error )
+          return error;
+        break;
+
+      case AFM_TOKEN_STARTKERNPAIRS:
+      case AFM_TOKEN_STARTKERNPAIRS0:
+        error = afm_parse_kern_pairs( parser );
+        if ( error )
+          return error;
+        break;
+
+      case AFM_TOKEN_ENDKERNDATA:
+      case AFM_TOKEN_ENDFONTMETRICS: 
+        return PSaux_Err_Ok;
+        break;
+
+      case AFM_TOKEN_UNKNOWN:
+        break;
+
+      default:
+        goto Fail;
+        break;
+      }
+    }
+
+  Fail:
+    return PSaux_Err_Syntax_Error;
+  }
+
+
+  static FT_Error
+  afm_parser_skip_section( AFM_Parser  parser,
+                           FT_UInt     n,
+                           AFM_Token   end_section )
+  {
+    char*     key;
+    FT_UInt   len;
+
+
+    while ( n-- > 0 )
+    {
+      key = afm_parser_next_key( parser, 1, NULL );
+      if ( !key )
+        goto Fail;
+    }
+
+    while ( ( key = afm_parser_next_key( parser, 1, &len ) ) )
+    {
+      AFM_Token  token = afm_tokenize( key, len );
+
+
+      if ( token == end_section || token == AFM_TOKEN_ENDFONTMETRICS )
+        return PSaux_Err_Ok;
+    }
+
+  Fail:
+    return PSaux_Err_Syntax_Error;
+  }
+
+
+  FT_LOCAL_DEF( FT_Error )
+  afm_parser_parse( AFM_Parser  parser )
+  {
+    FT_Memory     memory = parser->memory;
+    AFM_FontInfo  fi     = parser->FontInfo;
+    FT_Error      error  = PSaux_Err_Syntax_Error;
+    char*         key;
+    FT_UInt       len;
+    FT_Int        metrics_sets = 0;
+
+
+    if ( !fi )
+      return PSaux_Err_Invalid_Argument;
+
+    key = afm_parser_next_key( parser, 1, &len );
+    if ( !key || len != 16                              ||
+         ft_strncmp( key, "StartFontMetrics", 16 ) != 0 )
+      return PSaux_Err_Unknown_File_Format;
+
+    while ( ( key = afm_parser_next_key( parser, 1, &len ) ) )
+    {
+      switch ( afm_tokenize( key, len ) )
+      {
+      case AFM_TOKEN_METRICSSETS:
+        if ( afm_parser_read_int( parser, &metrics_sets ) )
+          goto Fail;
+
+        if ( metrics_sets != 0 && metrics_sets != 2 )
+        {
+          error = PSaux_Err_Unimplemented_Feature;
+
+          goto Fail;
+        }
+        break;
+
+      case AFM_TOKEN_ISCIDFONT:
+        shared_vals[0].type = AFM_VALUE_TYPE_BOOL;
+        if ( afm_parser_read_vals( parser, shared_vals, 1 ) != 1 )
+          goto Fail;
+
+        fi->IsCIDFont = shared_vals[0].u.b;
+        break;
+
+      case AFM_TOKEN_STARTCHARMETRICS:
+        {
+          FT_Int  n;
+
+
+          if ( afm_parser_read_int( parser, &n ) )
+            goto Fail;
+
+          error = afm_parser_skip_section( parser, n,
+                                           AFM_TOKEN_ENDCHARMETRICS );
+          if ( error )
+            return error;
+        }
+        break;
+
+      case AFM_TOKEN_STARTKERNDATA:
+        error = afm_parse_kern_data( parser );
+        if ( error )
+          goto Fail;
+        /* no break since we only support kern data */
+
+      case AFM_TOKEN_ENDFONTMETRICS: 
+        return PSaux_Err_Ok;
+        break;
+
+      default:
+        break;
+      }
+    }
+
+  Fail:
+    FT_FREE( fi->TrackKerns );
+    fi->NumTrackKern = 0;
+
+    FT_FREE( fi->KernPairs );
+    fi->NumKernPair = 0;
+
+    fi->IsCIDFont = 0;
+
+    return error;
+  }
--- /dev/null
+++ b/src/psaux/afmparse.h
@@ -1,0 +1,85 @@
+/***************************************************************************/
+/*                                                                         */
+/*  afmparse.h                                                             */
+/*                                                                         */
+/*    AFM parser (specification).                                          */
+/*                                                                         */
+/*  Copyright 2006 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 __AFMPARSE_H__
+#define __AFMPARSE_H__
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_POSTSCRIPT_AUX_H
+
+
+FT_BEGIN_HEADER
+
+
+  FT_LOCAL( FT_Error )
+  afm_parser_init( AFM_Parser  parser,
+                   FT_Memory   memory,
+                   FT_Byte*    base,
+                   FT_Byte*    limit );
+
+
+  FT_LOCAL( void )
+  afm_parser_done( AFM_Parser  parser );
+
+
+  FT_LOCAL( FT_Error )
+  afm_parser_parse( AFM_Parser  parser );
+
+
+  enum  AFM_ValueType_
+  {
+    AFM_VALUE_TYPE_STRING,
+    AFM_VALUE_TYPE_NAME,
+    AFM_VALUE_TYPE_FIXED,   /* real number */
+    AFM_VALUE_TYPE_INTEGER,
+    AFM_VALUE_TYPE_BOOL,
+    AFM_VALUE_TYPE_INDEX    /* glyph index */
+  };
+
+
+  typedef struct
+  {
+    enum AFM_ValueType_  type;
+    union  {
+      char*        s;
+      FT_Fixed     f;
+      FT_Int       i;
+      FT_Bool      b;
+    } u;
+  } AFM_ValueRec, *AFM_Value;
+
+
+  FT_LOCAL( FT_Int )
+  afm_parser_read_vals( AFM_Parser  parser,
+                        AFM_Value   vals,
+                        FT_Int      n );
+
+
+  /* read the next key from the next line or column */
+  FT_LOCAL( char* )
+  afm_parser_next_key( AFM_Parser  parser,
+                       FT_Bool     line,
+                       FT_UInt*    len );
+
+FT_END_HEADER
+
+#endif /* __AFMPARSE_H__ */
+
+
+/* END */
--- a/src/psaux/psaux.c
+++ b/src/psaux/psaux.c
@@ -23,6 +23,8 @@
 #include "psauxmod.c"
 #include "t1decode.c"
 #include "t1cmap.c"
+#include "afmparse.c"
+#include "psconv.c"
 
 
 /* END */
--- a/src/psaux/psauxmod.c
+++ b/src/psaux/psauxmod.c
@@ -21,6 +21,7 @@
 #include "psobjs.h"
 #include "t1decode.h"
 #include "t1cmap.h"
+#include "afmparse.h"
 
 
   FT_CALLBACK_TABLE_DEF
@@ -76,6 +77,15 @@
 
 
   FT_CALLBACK_TABLE_DEF
+  const AFM_Parser_FuncsRec  afm_parser_funcs =
+  {
+    afm_parser_init,
+    afm_parser_done,
+    afm_parser_parse
+  };
+
+
+  FT_CALLBACK_TABLE_DEF
   const T1_CMap_ClassesRec  t1_cmap_classes =
   {
     &t1_cmap_standard_class_rec,
@@ -92,6 +102,7 @@
     &ps_parser_funcs,
     &t1_builder_funcs,
     &t1_decoder_funcs,
+    &afm_parser_funcs,
 
     t1_decrypt,
     
--- /dev/null
+++ b/src/psaux/psconv.c
@@ -1,0 +1,394 @@
+/***************************************************************************/
+/*                                                                         */
+/*  psconv.c                                                               */
+/*                                                                         */
+/*    Some convenient conversions (body).                                  */
+/*                                                                         */
+/*  Copyright 2006 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_POSTSCRIPT_AUX_H
+#include FT_INTERNAL_DEBUG_H
+
+#include "psobjs.h"
+#include "psauxerr.h"
+
+
+/* The following array is used by various functions to quickly convert */
+/* digits (both decimal and non-decimal) into numbers.                 */
+
+#if 'A' == 65
+  /* ASCII */
+
+  static const char  ft_char_table[128] =
+  {
+    /* 0x00 */
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+     0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1,
+    -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+    25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
+    -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+    25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
+  };
+
+  /* no character >= 0x80 can represent a valid number */
+#define OP  >=
+
+#endif /* 'A' == 65 */
+
+#if 'A' == 193
+  /* EBCDIC */
+
+  static const char  ft_char_table[128] =
+  {
+    /* 0x80 */
+    -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, -1, -1, -1, -1, -1, -1,
+    -1, 19, 20, 21, 22, 23, 24, 25, 26, 27, -1, -1, -1, -1, -1, -1,
+    -1, -1, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -1,
+    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, -1, -1, -1, -1, -1, -1,
+    -1, 19, 20, 21, 22, 23, 24, 25, 26, 27, -1, -1, -1, -1, -1, -1,
+    -1, -1, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -1,
+     0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1,
+  };
+
+  /* no character < 0x80 can represent a valid number */
+#define OP  <
+
+#endif /* 'A' == 193 */
+
+  FT_LOCAL_DEF( FT_Int )
+  PS_Conv_Strtol( FT_Byte**  cursor,
+                  FT_Byte*   limit,
+                  FT_Int     base )
+  {
+    FT_Byte*  p = *cursor;
+    FT_Int    num = 0;
+    FT_Bool   sign = 0;
+
+
+    if ( p == limit || base < 2 || base > 36 )
+      return 0;
+
+    if ( *p == '-' || *p == '+' )
+    {
+      sign = ( *p == '-' );
+
+      p++;
+      if ( p == limit )
+        return 0;
+    }
+
+    for ( ; p < limit; p++ )
+    {
+      char  c;
+
+
+      if ( IS_PS_SPACE( *p ) || *p OP 0x80 )
+        break;
+
+      c = ft_char_table[*p & 0x7f];
+
+      if ( c < 0 || c >= base )
+        break;
+
+      num = num * base + c;
+    }
+
+    if ( sign )
+      num = -num;
+
+    *cursor = p;
+
+    return num;
+  }
+
+
+  FT_LOCAL_DEF( FT_Int )
+  PS_Conv_ToInt( FT_Byte**  cursor,
+  	         FT_Byte*   limit )
+  	    
+  {
+    FT_Byte*  p;
+    FT_Int    num;
+
+
+    num = PS_Conv_Strtol( cursor, limit, 10 );
+    p   = *cursor;
+
+    if ( p < limit && *p == '#' )
+    {
+      *cursor = p + 1;
+
+      return PS_Conv_Strtol( cursor, limit, num );
+    }
+    else
+      return num;
+  }
+
+
+  FT_LOCAL_DEF( FT_Fixed )
+  PS_Conv_ToFixed( FT_Byte**  cursor,
+  	           FT_Byte*   limit,
+  		   FT_Int     power_ten )
+  {
+    FT_Byte*  p = *cursor;
+    FT_Fixed  integral;
+    FT_Long   decimal = 0, divider = 1;
+    FT_Bool   sign = 0;
+
+
+    if ( p == limit )
+      return 0;
+
+    if ( *p == '-' || *p == '+' )
+    {
+      sign = ( *p == '-' );
+
+      p++;
+      if ( p == limit )
+        return 0;
+    }
+
+    if ( *p != '.' )
+    {
+      integral = PS_Conv_ToInt( &p, limit ) << 16;
+
+      if ( p == limit )
+        goto Exit;
+    }
+    else
+      integral = 0;
+
+    /* read the decimal part */
+    if ( *p == '.' )
+    {
+      p++;
+
+      for ( ; p < limit; p++ )
+      {
+        char  c;
+
+
+        if ( IS_PS_SPACE( *p ) || *p OP 0x80 )
+          break;
+
+        c = ft_char_table[*p & 0x7f];
+
+        if ( c < 0 || c >= 10 )
+          break;
+
+        if ( divider < 10000000L )
+        {
+          decimal = decimal * 10 + c;
+          divider *= 10;
+        }
+      }
+    }
+
+    /* read exponent, if any */
+    if ( p + 1 < limit && ( *p == 'e' || *p == 'E' ) )
+    {
+      p++;
+      power_ten += PS_Conv_ToInt( &p, limit );
+    }
+
+Exit:
+    while ( power_ten > 0 )
+    {
+      integral *= 10;
+      decimal  *= 10;
+      power_ten--;
+    }
+
+    while ( power_ten < 0 )
+    {
+      integral /= 10;
+      divider  *= 10;
+      power_ten++;
+    }
+
+    if ( decimal )
+      integral += FT_DivFix( decimal, divider );
+
+    if ( sign )
+      integral = -integral;
+
+    *cursor = p;
+
+    return integral;
+  }
+
+
+#if 0
+  FT_LOCAL_DEF( FT_UInt )
+  PS_Conv_StringDecode( FT_Byte**  cursor,
+  		        FT_Byte*   limit,
+  		        FT_Byte*   buffer,
+  		        FT_UInt    n )
+  {
+    FT_Byte*  p;
+    FT_UInt   r = 0;
+
+
+    for ( p = *cursor; r < n && p < limit; p++ )
+    {
+      FT_Byte  b;
+
+
+      if ( *p != '\\' )
+      {
+        buffer[r++] = *p;
+
+        continue;
+      }
+
+      p++;
+
+      switch ( *p )
+      {
+      case 'n':
+        b = '\n';
+        break;
+      case 'r':
+        b = '\r';
+        break;
+      case 't':
+        b = '\t';
+        break;
+      case 'b':
+        b = '\b';
+        break;
+      case 'f':
+        b = '\f';
+        break;
+      case '\r':
+        p++;
+        if ( *p != '\n' )
+        {
+          b = *p;
+
+          break;
+        }
+        /* no break */
+      case '\n':
+        continue;
+        break;
+      default:
+        if ( IS_PS_DIGIT( *p ) )
+        {
+          b = *p - '0';
+
+          p++;
+
+          if ( IS_PS_DIGIT( *p ) )
+          {
+            b = b * 8 + *p - '0';
+
+            p++;
+
+            if ( IS_PS_DIGIT( *p ) )
+              b = b * 8 + *p - '0';
+            else
+            {
+              buffer[r++] = b;
+              b = *p;
+            }
+          }
+          else
+          {
+            buffer[r++] = b;
+            b = *p;
+          }
+        }
+        else
+          b = *p;
+        break;
+      }
+
+      buffer[r++] = b;
+    }
+
+    *cursor = p;
+
+    return r;
+  }
+#endif
+
+
+  FT_LOCAL_DEF( FT_UInt )
+  PS_Conv_ASCIIHexDecode( FT_Byte**  cursor,
+  		          FT_Byte*   limit,
+  			  FT_Byte*   buffer,
+  			  FT_UInt    n )
+  {
+    FT_Byte*  p;
+    FT_UInt   r = 0;
+
+
+    for ( p = *cursor; r < 2 * n && p < limit; p++ )
+    {
+      char  c;
+
+
+      if ( IS_PS_SPACE( *p ) )
+        continue;
+
+      if ( *p OP 0x80 )
+        break;
+
+      c = ft_char_table[*p & 0x7f];
+
+      if ( c < 0 || c >= 16 )
+        break;
+
+      if ( r % 2 )
+        *buffer++ += c;
+      else
+        *buffer = c << 4;
+
+      r++;
+    }
+
+    *cursor = p;
+
+    return ( r + 1 ) / 2;
+  }
+
+
+  FT_LOCAL_DEF( FT_UInt )
+  PS_Conv_EexecDecode(  FT_Byte**   cursor,
+  		        FT_Byte*    limit,
+  		        FT_Byte*    buffer,
+  		        FT_UInt     n,
+  		        FT_UShort*  seed )
+  {
+    FT_Byte*  p;
+    FT_UInt   r;
+
+
+    for ( r = 0, p = *cursor; r < n && p < limit; r++, p++ )
+    {
+      FT_Byte  b = ( *p ^ ( *seed >> 8 ) );
+
+
+      *seed = (FT_UShort)( ( *p + *seed ) * 52845U + 22719 );
+      *buffer++ = b;
+    }
+
+    *cursor = p;
+
+    return r;
+  }
--- /dev/null
+++ b/src/psaux/psconv.h
@@ -1,0 +1,106 @@
+/***************************************************************************/
+/*                                                                         */
+/*  psconv.h                                                               */
+/*                                                                         */
+/*    Some convenient conversions (specification).                         */
+/*                                                                         */
+/*  Copyright 2006 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 __PSCONV_H__
+#define __PSCONV_H__
+
+
+#include <ft2build.h>
+#include FT_INTERNAL_POSTSCRIPT_AUX_H
+
+FT_BEGIN_HEADER
+
+
+  FT_LOCAL_DEF( FT_Int )
+  PS_Conv_Strtol( FT_Byte**  cursor,
+                  FT_Byte*   limit,
+                  FT_Int     base );
+
+
+  FT_LOCAL( FT_Int )
+  PS_Conv_ToInt( FT_Byte**  cursor,
+                 FT_Byte*   limit );
+
+  FT_LOCAL( FT_Fixed )
+  PS_Conv_ToFixed( FT_Byte**  cursor,
+                   FT_Byte*   limit,
+                   FT_Int     power_ten );
+
+#if 0
+  FT_LOCAL( FT_UInt )
+  PS_Conv_StringDecode( FT_Byte**  cursor,
+                        FT_Byte*   limit,
+                        FT_Byte*   buffer,
+                        FT_UInt    n );
+#endif
+
+  FT_LOCAL( FT_UInt )
+  PS_Conv_ASCIIHexDecode( FT_Byte**  cursor,
+                          FT_Byte*   limit,
+                          FT_Byte*   buffer,
+                          FT_UInt    n );
+
+  FT_LOCAL( FT_UInt )
+  PS_Conv_EexecDecode(  FT_Byte**   cursor,
+                        FT_Byte*    limit,
+                        FT_Byte*    buffer,
+                        FT_UInt     n,
+                        FT_UShort*  seed );
+
+#define IS_PS_NEWLINE( ch ) \
+  ( ( ch ) == '\r'       || \
+    ( ch ) == '\n' )
+
+#define IS_PS_SPACE( ch )   \
+  ( ( ch ) == ' '        || \
+    IS_PS_NEWLINE( ch )  || \
+    ( ch ) == '\t'       || \
+    ( ch ) == '\f'       || \
+    ( ch ) == '\0' )
+
+#define IS_PS_SPECIAL( ch ) \
+  ( ( ch ) == '/'        || \
+    ( ch ) == '('        || \
+    ( ch ) == ')'        || \
+    ( ch ) == '<'        || \
+    ( ch ) == '>'        || \
+    ( ch ) == '['        || \
+    ( ch ) == ']'        || \
+    ( ch ) == '{'        || \
+    ( ch ) == '}'        || \
+    ( ch ) == '%' )
+
+#define IS_PS_DELIM( ch )   \
+  ( IS_PS_SPACE( ch )    || \
+    IS_PS_SPECIAL( ch ) )
+
+#define IS_PS_DIGIT( ch ) ( ( ch ) >= '0' && ( ch ) <= '9' )
+
+#define IS_PS_XDIGIT( ch )                \
+  ( IS_PS_DIGIT( ( ch ) )              || \
+    ( ( ch ) >= 'A' && ( ch ) <= 'F' ) || \
+    ( ( ch ) >= 'a' && ( ch ) <= 'f' ) )
+
+#define IS_PS_BASE85( ch ) ( ( ch ) >= '!' && ( ch ) <= 'u' )
+
+FT_END_HEADER
+
+#endif /* __PSCONV_H__ */
+
+
+/* END */
--- a/src/psaux/psobjs.c
+++ b/src/psaux/psobjs.c
@@ -21,6 +21,7 @@
 #include FT_INTERNAL_DEBUG_H
 
 #include "psobjs.h"
+#include "psconv.h"
 
 #include "psauxerr.h"
 
@@ -266,65 +267,7 @@
   /*************************************************************************/
   /*************************************************************************/
 
-  /* In the PostScript Language Reference Manual (PLRM) the following */
-  /* characters are called `whitespace characters'.                   */
-#define IS_T1_WHITESPACE( c )  ( (c) == ' '  || (c) == '\t' )
-#define IS_T1_LINESPACE( c )   ( (c) == '\r' || (c) == '\n' || (c) == '\f' )
-#define IS_T1_NULLSPACE( c )   ( (c) == '\0' )
 
-  /* According to the PLRM all whitespace characters are equivalent, */
-  /* except in comments and strings.                                 */
-#define IS_T1_SPACE( c )  ( IS_T1_WHITESPACE( c ) || \
-                            IS_T1_LINESPACE( c )  || \
-                            IS_T1_NULLSPACE( c )  )
-
-
-  /* The following array is used by various functions to quickly convert */
-  /* digits (both decimal and non-decimal) into numbers.                 */
-
-#if 'A' == 65
-  /* ASCII */
-
-  static const char  ft_char_table[128] =
-  {
-    /* 0x00 */
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-     0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1,
-    -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
-    25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
-    -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
-    25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
-  };
-
-  /* no character >= 0x80 can represent a valid number */
-#define OP  >=
-
-#endif /* 'A' == 65 */
-
-#if 'A' == 193
-  /* EBCDIC */
-
-  static const char  ft_char_table[128] =
-  {
-    /* 0x80 */
-    -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, -1, -1, -1, -1, -1, -1,
-    -1, 19, 20, 21, 22, 23, 24, 25, 26, 27, -1, -1, -1, -1, -1, -1,
-    -1, -1, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -1,
-    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-    -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, -1, -1, -1, -1, -1, -1,
-    -1, 19, 20, 21, 22, 23, 24, 25, 26, 27, -1, -1, -1, -1, -1, -1,
-    -1, -1, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -1,
-     0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1,
-  }
-
-  /* no character < 0x80 can represent a valid number */
-#define OP  <
-
-#endif /* 'A' == 193 */
-
-
   /* first character must be already part of the comment */
 
   static void
@@ -336,7 +279,7 @@
 
     while ( cur < limit )
     {
-      if ( IS_T1_LINESPACE( *cur ) )
+      if ( IS_PS_NEWLINE( *cur ) )
         break;
       cur++;
     }
@@ -354,7 +297,7 @@
 
     while ( cur < limit )
     {
-      if ( !IS_T1_SPACE( *cur ) )
+      if ( !IS_PS_SPACE( *cur ) )
       {
         if ( *cur == '%' )
           /* According to the PLRM, a comment is equal to a space. */
@@ -412,20 +355,13 @@
 
     while ( ++cur < limit )
     {
-      int  d;
-
-
       /* All whitespace characters are ignored. */
       skip_spaces( &cur, limit );
       if ( cur >= limit )
         break;
 
-      if ( *cur OP 0x80 )
+      if ( !IS_PS_XDIGIT( *cur ) )
         break;
-
-      d = ft_char_table[*cur & 0x7F];
-      if ( d < 0 || d >= 16 )
-        break;
     }
 
     if ( cur < limit && *cur != '>' )
@@ -510,15 +446,6 @@
     /* anything else */
     while ( cur < limit )
     {
-      if ( IS_T1_SPACE( *cur )        ||
-           *cur == '('                ||
-           *cur == '/'                ||
-           *cur == '%'                ||
-           *cur == '[' || *cur == ']' ||
-           *cur == '{' || *cur == '}' ||
-           *cur == '<' || *cur == '>' )
-        break;
-
       if ( *cur == ')' )
       {
         FT_ERROR(( "ps_parser_skip_PS_token: "
@@ -526,6 +453,8 @@
         parser->error = PSaux_Err_Invalid_File_Format;
         goto Exit;
       }
+      else if ( IS_PS_DELIM( *cur ) )
+        break;
 
       cur++;
     }
@@ -692,270 +621,6 @@
   }
 
 
-  /* first character must be already part of the number */
-
-  static FT_Long
-  ps_radix( FT_Long    radixBase,
-            FT_Byte*  *acur,
-            FT_Byte*   limit )
-  {
-    FT_Long   result = 0;
-    FT_Byte*  cur    = *acur;
-
-
-    if ( radixBase < 2 || radixBase > 36 )
-      return 0;
-
-    while ( cur < limit )
-    {
-      int  d;
-
-
-      if ( *cur OP 0x80 )
-        break;
-
-      d = ft_char_table[*cur & 0x7F];
-      if ( d < 0 || d >= radixBase )
-        break;
-
-      result = result * radixBase + d;
-
-      cur++;
-    }
-
-    *acur = cur;
-
-    return result;
-  }
-
-
-  /* first character must be already part of the number */
-
-  static FT_Long
-  ps_toint( FT_Byte*  *acur,
-            FT_Byte*   limit )
-  {
-    FT_Long   result = 0;
-    FT_Byte*  cur    = *acur;
-    FT_Byte   c;
-
-
-    if ( cur >= limit )
-      goto Exit;
-
-    c = *cur;
-    if ( c == '-' )
-      cur++;
-
-    while ( cur < limit )
-    {
-      int  d;
-
-
-      if ( *cur == '#' )
-      {
-        cur++;
-        result = ps_radix( result, &cur, limit );
-        break;
-      }
-
-      if ( *cur OP 0x80 )
-        break;
-
-      d = ft_char_table[*cur & 0x7F];
-      if ( d < 0 || d >= 10 )
-        break;
-      result = result * 10 + d;
-
-      cur++;
-    };
-
-    if ( c == '-' )
-      result = -result;
-
-  Exit:
-    *acur = cur;
-    return result;
-  }
-
-
-  /* first character must be `<' if `delimiters' is non-zero */
-
-  static FT_Error
-  ps_tobytes( FT_Byte*  *acur,
-              FT_Byte*   limit,
-              FT_Long    max_bytes,
-              FT_Byte*   bytes,
-              FT_Long*   pnum_bytes,
-              FT_Bool    delimiters )
-  {
-    FT_Error  error = PSaux_Err_Ok;
-
-    FT_Byte*  cur = *acur;
-    FT_Long   n   = 0;
-
-
-    if ( cur >= limit )
-      goto Exit;
-
-    if ( delimiters )
-    {
-      if ( *cur != '<' )
-      {
-        FT_ERROR(( "ps_tobytes: Missing starting delimiter `<'\n" ));
-        error = PSaux_Err_Invalid_File_Format;
-        goto Exit;
-      }
-
-      cur++;
-    }
-
-    max_bytes = max_bytes * 2;
-
-    for ( n = 0; cur < limit; n++, cur++ )
-    {
-      int  d;
-
-
-      if ( n >= max_bytes )
-        /* buffer is full */
-        goto Exit;
-
-      /* All whitespace characters are ignored. */
-      skip_spaces( &cur, limit );
-      if ( cur >= limit )
-        break;
-
-      if ( *cur OP 0x80 )
-        break;
-
-      d = ft_char_table[*cur & 0x7F];
-      if ( d < 0 || d >= 16 )
-        break;
-
-      /* <f> == <f0> != <0f> */
-      bytes[n / 2] = (FT_Byte)( ( n % 2 ) ? bytes[n / 2] + d
-                                          : d * 16 );
-    }
-
-    if ( delimiters )
-    {
-      if ( cur < limit && *cur != '>' )
-      {
-        FT_ERROR(( "ps_tobytes: Missing closing delimiter `>'\n" ));
-        error = PSaux_Err_Invalid_File_Format;
-        goto Exit;
-      }
-
-      cur++;
-    }
-
-    *acur = cur;
-
-  Exit:
-    *pnum_bytes = ( n + 1 ) / 2;
-
-    return error;
-  }
-
-
-  /* first character must be already part of the number */
-
-  static FT_Long
-  ps_tofixed( FT_Byte*  *acur,
-              FT_Byte*   limit,
-              FT_Long    power_ten )
-  {
-    FT_Byte*  cur  = *acur;
-    FT_Long   num, divider, result;
-    FT_Int    sign = 0;
-
-
-    if ( cur >= limit )
-      return 0;
-
-    /* first of all, check the sign */
-    if ( *cur == '-' && cur + 1 < limit )
-    {
-      sign = 1;
-      cur++;
-    }
-
-    /* then, read the integer part, if any */
-    if ( *cur != '.' )
-      result = ps_toint( &cur, limit ) << 16;
-    else
-      result = 0;
-
-    num     = 0;
-    divider = 1;
-
-    if ( cur >= limit )
-      goto Exit;
-
-    /* read decimal part, if any */
-    if ( *cur == '.' && cur + 1 < limit )
-    {
-      cur++;
-
-      for (;;)
-      {
-        int  d;
-
-
-        if ( *cur OP 0x80 )
-          break;
-
-        d = ft_char_table[*cur & 0x7F];
-        if ( d < 0 || d >= 10 )
-          break;
-
-        if ( divider < 10000000L )
-        {
-          num      = num * 10 + d;
-          divider *= 10;
-        }
-
-        cur++;
-        if ( cur >= limit )
-          break;
-      }
-    }
-
-    /* read exponent, if any */
-    if ( cur + 1 < limit && ( *cur == 'e' || *cur == 'E' ) )
-    {
-      cur++;
-      power_ten += ps_toint( &cur, limit );
-    }
-
-  Exit:
-    /* raise to power of ten if needed */
-    while ( power_ten > 0 )
-    {
-      result = result * 10;
-      num    = num * 10;
-      power_ten--;
-    }
-
-    while ( power_ten < 0 )
-    {
-      result  = result / 10;
-      divider = divider * 10;
-      power_ten++;
-    }
-
-    if ( num )
-      result += FT_DivFix( num, divider );
-
-    if ( sign )
-      result = -result;
-
-    *acur = cur;
-    return result;
-  }
-
-
   /* first character must be a delimiter or a part of a number */
 
   static FT_Int
@@ -1003,7 +668,8 @@
         break;
       }
 
-      coords[count] = (FT_Short)( ps_tofixed( &cur, limit, 0 ) >> 16 );
+      coords[count] =
+        (FT_Short)( PS_Conv_ToFixed( &cur, limit, 0 ) >> 16 );
       count++;
 
       if ( !ender )
@@ -1064,7 +730,7 @@
         break;
       }
 
-      values[count] = ps_tofixed( &cur, limit, power_ten );
+      values[count] = PS_Conv_ToFixed( &cur, limit, power_ten );
       count++;
 
       if ( !ender )
@@ -1250,15 +916,15 @@
         goto Store_Integer;
 
       case T1_FIELD_TYPE_FIXED:
-        val = ps_tofixed( &cur, limit, 0 );
+        val = PS_Conv_ToFixed( &cur, limit, 0 );
         goto Store_Integer;
 
       case T1_FIELD_TYPE_FIXED_1000:
-        val = ps_tofixed( &cur, limit, 3 );
+        val = PS_Conv_ToFixed( &cur, limit, 3 );
         goto Store_Integer;
 
       case T1_FIELD_TYPE_INTEGER:
-        val = ps_toint( &cur, limit );
+        val = PS_Conv_ToInt( &cur, limit );
         /* fall through */
 
       Store_Integer:
@@ -1424,10 +1090,12 @@
   ps_parser_to_int( PS_Parser  parser )
   {
     ps_parser_skip_spaces( parser );
-    return ps_toint( &parser->cursor, parser->limit );
+    return PS_Conv_ToInt( &parser->cursor, parser->limit );
   }
 
 
+  /* first character must be `<' if `delimiters' is non-zero */
+
   FT_LOCAL_DEF( FT_Error )
   ps_parser_to_bytes( PS_Parser  parser,
                       FT_Byte*   bytes,
@@ -1435,13 +1103,49 @@
                       FT_Long*   pnum_bytes,
                       FT_Bool    delimiters )
   {
+    FT_Error  error = PSaux_Err_Ok;
+    FT_Byte*  cur;
+    
+    
     ps_parser_skip_spaces( parser );
-    return ps_tobytes( &parser->cursor,
-                       parser->limit,
-                       max_bytes,
-                       bytes,
-                       pnum_bytes,
-                       delimiters );
+    cur = parser->cursor;
+
+    if ( cur >= parser->limit )
+      goto Exit;
+
+    if ( delimiters )
+    {
+      if ( *cur != '<' )
+      {
+        FT_ERROR(( "ps_tobytes: Missing starting delimiter `<'\n" ));
+        error = PSaux_Err_Invalid_File_Format;
+        goto Exit;
+      }
+
+      cur++;
+    }
+
+    *pnum_bytes = PS_Conv_ASCIIHexDecode( &cur,
+                                          parser->limit,
+                                          bytes,
+                                          max_bytes );
+
+    if ( delimiters )
+    {
+      if ( cur < parser->limit && *cur != '>' )
+      {
+        FT_ERROR(( "ps_tobytes: Missing closing delimiter `>'\n" ));
+        error = PSaux_Err_Invalid_File_Format;
+        goto Exit;
+      }
+
+      cur++;
+    }
+
+    parser->cursor = cur;
+
+  Exit:
+    return error;
   }
 
 
@@ -1450,7 +1154,7 @@
                       FT_Int     power_ten )
   {
     ps_parser_skip_spaces( parser );
-    return ps_tofixed( &parser->cursor, parser->limit, power_ten );
+    return PS_Conv_ToFixed( &parser->cursor, parser->limit, power_ten );
   }
 
 
@@ -1780,16 +1484,11 @@
               FT_Offset  length,
               FT_UShort  seed )
   {
-    while ( length > 0 )
-    {
-      FT_Byte  plain;
-
-
-      plain     = (FT_Byte)( *buffer ^ ( seed >> 8 ) );
-      seed      = (FT_UShort)( ( *buffer + seed ) * 52845U + 22719 );
-      *buffer++ = plain;
-      length--;
-    }
+    PS_Conv_EexecDecode( &buffer,
+                         buffer + length,
+                         buffer,
+                         length,
+                         &seed );
   }
 
 
--- a/src/psaux/rules.mk
+++ b/src/psaux/rules.mk
@@ -28,6 +28,8 @@
 PSAUX_DRV_SRC := $(PSAUX_DIR)/psobjs.c   \
                  $(PSAUX_DIR)/t1decode.c \
                  $(PSAUX_DIR)/t1cmap.c   \
+                 $(PSAUX_DIR)/afmparse.c \
+                 $(PSAUX_DIR)/psconv.c   \
                  $(PSAUX_DIR)/psauxmod.c
 
 # PSAUX driver headers
--- /dev/null
+++ b/src/tools/test_afm.c
@@ -1,0 +1,146 @@
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_INTERNAL_STREAM_H
+#include FT_INTERNAL_POSTSCRIPT_AUX_H
+
+  void dump_fontinfo( AFM_FontInfo  fi )
+  {
+    FT_Int  i;
+
+
+    printf( "This AFM is for %sCID font.\n\n",
+            ( fi->IsCIDFont ) ? "" : "non-" );
+
+    if ( fi->NumTrackKern )
+      printf( "There are %d sets of track kernings:\n",
+              fi->NumTrackKern );
+    else
+      printf( "There is no track kerning.\n" );
+
+    for ( i = 0; i < fi->NumTrackKern; i++ )
+    {
+      AFM_TrackKern  tk = fi->TrackKerns + i;
+
+
+      printf( "\t%2d: %5.2f %5.2f %5.2f %5.2f\n", tk->degree,
+                                                  tk->min_ptsize / 65536.,
+                                                  tk->min_kern / 65536.,
+                                                  tk->max_ptsize / 65536.,
+                                                  tk->max_kern / 65536. );
+    }
+
+    printf( "\n" );
+
+    if ( fi->NumTrackKern )
+      printf( "There are %d kerning pairs:\n",
+              fi->NumKernPair );
+    else
+      printf( "There is no kerning pair.\n" );
+
+    for ( i = 0; i < fi->NumKernPair; i++ )
+    {
+      AFM_KernPair  kp = fi->KernPairs + i;
+
+      
+      printf( "\t%3d + %3d => (%4d, %4d)\n", kp->index1,
+                                             kp->index2,
+                                             kp->x,
+                                             kp->y );
+    }
+
+  }
+
+  int
+  dummy_get_index( const char*  name,
+                   FT_UInt      len,
+                   void*        user_data )
+  {
+    if ( len )
+      return name[0];
+    else
+      return 0;
+  }
+
+  FT_Error
+  parse_afm( FT_Library    library,
+             FT_Stream     stream,
+             AFM_FontInfo  fi )
+  {
+    PSAux_Service  psaux;
+    AFM_ParserRec  parser;
+    FT_Error       error = FT_Err_Ok;
+
+
+    psaux = (PSAux_Service)FT_Get_Module_Interface( library, "psaux" );
+    if ( !psaux || !psaux->afm_parser_funcs )
+      return -1;
+
+    error = FT_Stream_EnterFrame( stream, stream->size );
+    if ( error )
+      return error;
+
+    error = psaux->afm_parser_funcs->init( &parser,
+                                           library->memory,
+                                           stream->cursor,
+                                           stream->limit );
+    if ( error )
+      return error;
+
+    parser.FontInfo = fi;
+    parser.get_index = dummy_get_index;
+
+    error = psaux->afm_parser_funcs->parse( &parser );
+
+    psaux->afm_parser_funcs->done( &parser );
+
+    return error;
+  }
+
+
+  int main( int    argc,
+            char** argv )
+  {
+    FT_Library       library;
+    FT_StreamRec     stream;
+    FT_Error         error = FT_Err_Ok;
+    AFM_FontInfoRec  fi;
+
+
+    if ( argc < 2 )
+      return FT_Err_Invalid_Argument;
+
+    error = FT_Init_FreeType( &library );
+    if ( error )
+      return error;
+
+    FT_ZERO( &stream );
+    error = FT_Stream_Open( &stream, argv[1] );
+    if ( error )
+      goto Exit;
+    stream.memory = library->memory;
+
+    FT_ZERO( &fi );
+    error = parse_afm( library, &stream, &fi );
+
+    if ( !error )
+    {
+      FT_Memory  memory = library->memory;
+
+
+      dump_fontinfo( &fi );
+
+      if ( fi.KernPairs )
+        FT_FREE( fi.KernPairs );
+      if ( fi.TrackKerns )
+        FT_FREE( fi.TrackKerns );
+    }
+    else
+      printf( "parse error\n" );
+
+    FT_Stream_Close( &stream );
+
+  Exit:
+    FT_Done_FreeType( library );
+
+    return error;
+  }
--- a/src/type1/t1afm.c
+++ b/src/type1/t1afm.c
@@ -34,102 +34,41 @@
 
 
   FT_LOCAL_DEF( void )
-  T1_Done_Metrics( FT_Memory  memory,
-                   T1_AFM*    afm )
+  T1_Done_Metrics( FT_Memory     memory,
+                   AFM_FontInfo  fi )
   {
-    FT_FREE( afm->kern_pairs );
-    afm->num_pairs = 0;
-    FT_FREE( afm );
-  }
+    FT_FREE( fi->KernPairs );
+    fi->NumKernPair = 0;
 
+    FT_FREE( fi->TrackKerns );
+    fi->NumTrackKern = 0;
 
-#undef  IS_KERN_PAIR
-#define IS_KERN_PAIR( p )  ( p[0] == 'K' && p[1] == 'P' )
+    FT_FREE( fi );
+  }
 
-#define IS_ALPHANUM( c )  ( ft_isalnum( c ) || \
-                            c == '_'        || \
-                            c == '.'        )
 
-
   /* read a glyph name and return the equivalent glyph index */
-  static FT_UInt
-  afm_atoindex( FT_Byte**  start,
-                FT_Byte*   limit,
-                T1_Font    type1 )
+  static FT_Int
+  t1_get_index( const char*  name,
+                FT_UInt      len,
+                void*        user_data )
   {
-    FT_Byte*    p = *start;
-    FT_PtrDist  len;
-    FT_UInt     result = 0;
-    char        temp[64];
+    T1_Font  type1 = (T1_Font)user_data;
+    FT_Int   n;
 
 
-    /* skip whitespace */
-    while ( p < limit                                             &&
-            ( *p == ' ' || *p == '\t' || *p == ':' || *p == ';' ) )
-      p++;
-    *start = p;
-
-    /* now, read glyph name */
-    while ( p < limit && IS_ALPHANUM( *p ) )
-      p++;
-
-    len = p - *start;
-
-    if ( len > 0 && len < 64 )
+    for ( n = 0; n < type1->num_glyphs; n++ )
     {
-      FT_Int  n;
+      char*  gname = (char*)type1->glyph_names[n];
 
 
-      /* copy glyph name to intermediate array */
-      FT_MEM_COPY( temp, *start, len );
-      temp[len] = 0;
-
-      /* lookup glyph name in face array */
-      for ( n = 0; n < type1->num_glyphs; n++ )
-      {
-        char*  gname = (char*)type1->glyph_names[n];
-
-
-        if ( gname && gname[0] == temp[0] && ft_strcmp( gname, temp ) == 0 )
-        {
-          result = n;
-          break;
-        }
-      }
+      if ( gname && gname[0] == name[0]        &&
+           ft_strlen( gname ) == len           &&
+           ft_strncmp( gname, name, len ) == 0 )
+        return n;
     }
-    *start = p;
-    return result;
-  }
 
-
-  /* read an integer */
-  static int
-  afm_atoi( FT_Byte**  start,
-            FT_Byte*   limit )
-  {
-    FT_Byte*  p    = *start;
-    int       sum  = 0;
-    int       sign = 1;
-
-
-    /* skip everything that is not a number */
-    while ( p < limit && !isdigit( *p ) )
-    {
-      sign = 1;
-      if ( *p == '-' )
-        sign = -1;
-
-      p++;
-    }
-
-    while ( p < limit && isdigit( *p ) )
-    {
-      sum = sum * 10 + ( *p - '0' );
-      p++;
-    }
-    *start = p;
-
-    return sum * sign;
+    return 0;
   }
 
 
@@ -142,11 +81,11 @@
   compare_kern_pairs( const void*  a,
                       const void*  b )
   {
-    T1_Kern_Pair*  pair1 = (T1_Kern_Pair*)a;
-    T1_Kern_Pair*  pair2 = (T1_Kern_Pair*)b;
+    AFM_KernPair  pair1 = (AFM_KernPair)a;
+    AFM_KernPair  pair2 = (AFM_KernPair)b;
 
-    FT_ULong  index1 = KERN_INDEX( pair1->glyph1, pair1->glyph2 );
-    FT_ULong  index2 = KERN_INDEX( pair2->glyph1, pair2->glyph2 );
+    FT_ULong  index1 = KERN_INDEX( pair1->index1, pair1->index2 );
+    FT_ULong  index2 = KERN_INDEX( pair2->index1, pair2->index2 );
 
 
     return (int)( index1 - index2 );
@@ -153,101 +92,11 @@
   }
 
 
-  /* parse an AFM file -- for now, only read the kerning pairs */
-  static FT_Error
-  T1_Read_AFM( FT_Face    t1_face,
-               FT_Stream  stream )
-  {
-    FT_Error       error = T1_Err_Ok;
-    FT_Memory      memory = stream->memory;
-    FT_Byte*       start;
-    FT_Byte*       limit;
-    FT_Byte*       p;
-    FT_Int         count = 0;
-    T1_Kern_Pair*  pair;
-    T1_Font        type1 = &((T1_Face)t1_face)->type1;
-    T1_AFM*        afm   = 0;
-
-
-    start = (FT_Byte*)stream->cursor;
-    limit = (FT_Byte*)stream->limit;
-    p     = start;
-
-    /* we are now going to count the occurences of `KP' or `KPX' in */
-    /* the AFM file                                                 */
-    count = 0;
-    for ( p = start; p < limit - 3; p++ )
-    {
-      if ( IS_KERN_PAIR( p ) )
-        count++;
-    }
-
-    /* Actually, kerning pairs are simply optional! */
-    if ( count == 0 )
-      goto Exit;
-
-    /* allocate the pairs */
-    if ( FT_NEW( afm ) || FT_NEW_ARRAY( afm->kern_pairs, count ) )
-      goto Exit;
-
-    /* now, read each kern pair */
-    pair           = afm->kern_pairs;
-    afm->num_pairs = count;
-
-    /* save in face object */
-    ((T1_Face)t1_face)->afm_data = afm;
-
-    t1_face->face_flags |= FT_FACE_FLAG_KERNING;
-
-    for ( p = start; p < limit - 3; p++ )
-    {
-      if ( IS_KERN_PAIR( p ) )
-      {
-        FT_Byte*  q;
-
-
-        /* skip keyword (KP or KPX) */
-        q = p + 2;
-        if ( *q == 'X' )
-          q++;
-
-        pair->glyph1    = afm_atoindex( &q, limit, type1 );
-        pair->glyph2    = afm_atoindex( &q, limit, type1 );
-        pair->kerning.x = afm_atoi( &q, limit );
-
-        pair->kerning.y = 0;
-        if ( p[2] != 'X' )
-          pair->kerning.y = afm_atoi( &q, limit );
-
-        pair++;
-      }
-    }
-
-    /* now, sort the kern pairs according to their glyph indices */
-    ft_qsort( afm->kern_pairs, count, sizeof ( T1_Kern_Pair ),
-              compare_kern_pairs );
-
-  Exit:
-    if ( error )
-      FT_FREE( afm );
-
-    return error;
-  }
-
-
-#define LITTLE_ENDIAN_USHORT( p )  (FT_UShort)( ( (p)[0]       ) | \
-                                                ( (p)[1] <<  8 ) )
-
-#define LITTLE_ENDIAN_UINT( p )  (FT_UInt)( ( (p)[0]       ) | \
-                                            ( (p)[1] <<  8 ) | \
-                                            ( (p)[2] << 16 ) | \
-                                            ( (p)[3] << 24 ) )
-
-
   /* parse a PFM file -- for now, only read the kerning pairs */
   static FT_Error
-  T1_Read_PFM( FT_Face    t1_face,
-               FT_Stream  stream )
+  T1_Read_PFM( FT_Face       t1_face,
+               FT_Stream     stream,
+               AFM_FontInfo  fi )
   {
     FT_Error       error = T1_Err_Ok;
     FT_Memory      memory = stream->memory;
@@ -254,9 +103,7 @@
     FT_Byte*       start;
     FT_Byte*       limit;
     FT_Byte*       p;
-    FT_Int         kern_count = 0;
-    T1_Kern_Pair*  pair;
-    T1_AFM*        afm = 0;
+    AFM_KernPair   kp;
     FT_Int         width_table_length;
     FT_CharMap     oldcharmap;
     FT_CharMap     charmap;
@@ -275,16 +122,16 @@
       error = T1_Err_Unknown_File_Format;
       goto Exit;
     }
-    width_table_length = LITTLE_ENDIAN_USHORT( p );
+    width_table_length = FT_PEEK_USHORT_LE( p );
 
     p += 18 + width_table_length;
-    if ( p + 0x12 > limit || LITTLE_ENDIAN_USHORT( p ) < 0x12 )
+    if ( p + 0x12 > limit || FT_PEEK_USHORT_LE( p ) < 0x12 )
       /* extension table is probably optional */
       goto Exit;
 
     /* Kerning offset is 14 bytes from start of extensions table. */
     p += 14;
-    p = start + LITTLE_ENDIAN_UINT( p );
+    p = start + FT_PEEK_ULONG_LE( p );
 
     if ( p == start )
       /* zero offset means no table */
@@ -296,9 +143,9 @@
       goto Exit;
     }
 
-    kern_count = LITTLE_ENDIAN_USHORT( p );
+    fi->NumKernPair = FT_PEEK_USHORT_LE( p );
     p += 2;
-    if ( p + 4 * kern_count > limit )
+    if ( p + 4 * fi->NumKernPair > limit )
     {
       error = T1_Err_Unknown_File_Format;
       goto Exit;
@@ -305,22 +152,16 @@
     }
 
     /* Actually, kerning pairs are simply optional! */
-    if ( kern_count == 0 )
+    if ( fi->NumKernPair == 0 )
       goto Exit;
 
     /* allocate the pairs */
-    if ( FT_NEW( afm ) || FT_NEW_ARRAY( afm->kern_pairs, kern_count ) )
+    if ( FT_QNEW_ARRAY( fi->KernPairs, fi->NumKernPair ) )
       goto Exit;
 
-    /* save in face object */
-    ((T1_Face)t1_face)->afm_data = afm;
-
-    t1_face->face_flags |= FT_FACE_FLAG_KERNING;
-
     /* now, read each kern pair */
-    pair           = afm->kern_pairs;
-    afm->num_pairs = kern_count;
-    limit          = p + 4 * kern_count;
+    kp             = fi->KernPairs;
+    limit          = p + 4 * fi->NumKernPair;
 
     /* PFM kerning data are stored by encoding rather than glyph index, */
     /* so find the PostScript charmap of this font and install it       */
@@ -347,15 +188,15 @@
     /*   encoding of first glyph (1 byte)     */
     /*   encoding of second glyph (1 byte)    */
     /*   offset (little-endian short)         */
-    for ( ; p < limit ; p+=4 )
+    for ( ; p < limit ; p += 4 )
     {
-      pair->glyph1 = FT_Get_Char_Index( t1_face, p[0] );
-      pair->glyph2 = FT_Get_Char_Index( t1_face, p[1] );
+      kp->index1 = FT_Get_Char_Index( t1_face, p[0] );
+      kp->index2 = FT_Get_Char_Index( t1_face, p[1] );
 
-      pair->kerning.x = (FT_Short)LITTLE_ENDIAN_USHORT(p + 2);
-      pair->kerning.y = 0;
+      kp->x = (FT_Int)FT_PEEK_USHORT_LE(p + 2);
+      kp->y = 0;
 
-      pair++;
+      kp++;
     }
 
     if ( oldcharmap != NULL )
@@ -364,12 +205,15 @@
       goto Exit;
 
     /* now, sort the kern pairs according to their glyph indices */
-    ft_qsort( afm->kern_pairs, kern_count, sizeof ( T1_Kern_Pair ),
+    ft_qsort( fi->KernPairs, fi->NumKernPair, sizeof ( AFM_KernPairRec ),
               compare_kern_pairs );
 
   Exit:
     if ( error )
-      FT_FREE( afm );
+    {
+      FT_FREE( fi->KernPairs );
+      fi->NumKernPair = 0;
+    }
 
     return error;
   }
@@ -381,28 +225,56 @@
   T1_Read_Metrics( FT_Face    t1_face,
                    FT_Stream  stream )
   {
-    FT_Error  error;
-    FT_Byte*  start;
+    PSAux_Service    psaux;
+    FT_Memory        memory = stream->memory;
+    AFM_ParserRec    parser;
+    AFM_FontInfo     fi;
+    FT_Error         error = T1_Err_Unknown_File_Format;
 
 
     if ( FT_FRAME_ENTER( stream->size ) )
       return error;
 
-    start = (FT_Byte*)stream->cursor;
+    FT_NEW( fi );
+    if ( error )
+      return error;
 
-    if ( stream->size >= ft_strlen( "StartFontMetrics" )    &&
-         ft_strncmp( (const char*)start, "StartFontMetrics",
-                     ft_strlen( "StartFontMetrics" ) ) == 0 )
-      error = T1_Read_AFM( t1_face, stream );
+    psaux = (PSAux_Service)( (T1_Face)t1_face )->psaux;
+    if ( psaux && psaux->afm_parser_funcs )
+    {
+      error = psaux->afm_parser_funcs->init( &parser,
+                                             stream->memory,
+                                             stream->cursor,
+                                             stream->limit );
 
-    else if ( stream->size > 6                                &&
-              start[0] == 0x00 && start[1] == 0x01            &&
-              LITTLE_ENDIAN_UINT( start + 2 ) == stream->size )
-      error = T1_Read_PFM( t1_face, stream );
+      if ( !error )
+      {
+        parser.FontInfo  = fi;
+        parser.get_index = t1_get_index;
+        parser.user_data = &( (T1_Face)t1_face )->type1;
 
-    else
-      error = T1_Err_Unknown_File_Format;
+        error = psaux->afm_parser_funcs->parse( &parser );
+        psaux->afm_parser_funcs->done( &parser );
+      }
+    }
 
+    if ( error == T1_Err_Unknown_File_Format )
+    {
+      FT_Byte*  start = stream->cursor;
+
+
+      if ( stream->size > 6                              &&
+           start[0] == 0x00 && start[1] == 0x01          &&
+           FT_PEEK_ULONG_LE( start + 2 ) == stream->size )
+        error = T1_Read_PFM( t1_face, stream, fi );
+    }
+
+    if ( !error && fi->NumKernPair )
+    {
+      t1_face->face_flags |= FT_FACE_FLAG_KERNING;
+      ( (T1_Face)t1_face )->afm_data = fi;
+    }
+
     FT_FRAME_EXIT();
 
     return error;
@@ -411,18 +283,18 @@
 
   /* find the kerning for a given glyph pair */
   FT_LOCAL_DEF( void )
-  T1_Get_Kerning( T1_AFM*     afm,
-                  FT_UInt     glyph1,
-                  FT_UInt     glyph2,
-                  FT_Vector*  kerning )
+  T1_Get_Kerning( AFM_FontInfo  fi,
+                  FT_UInt       glyph1,
+                  FT_UInt       glyph2,
+                  FT_Vector*    kerning )
   {
-    T1_Kern_Pair  *min, *mid, *max;
+    AFM_KernPair  min, mid, max;
     FT_ULong      idx = KERN_INDEX( glyph1, glyph2 );
 
 
     /* simple binary search */
-    min = afm->kern_pairs;
-    max = min + afm->num_pairs - 1;
+    min = fi->KernPairs;
+    max = min + fi->NumKernPair - 1;
 
     while ( min <= max )
     {
@@ -430,11 +302,13 @@
 
 
       mid  = min + ( max - min ) / 2;
-      midi = KERN_INDEX( mid->glyph1, mid->glyph2 );
+      midi = KERN_INDEX( mid->index1, mid->index2 );
 
       if ( midi == idx )
       {
-        *kerning = mid->kerning;
+        kerning->x = mid->x;
+        kerning->y = mid->y;
+
         return;
       }
 
@@ -446,6 +320,44 @@
 
     kerning->x = 0;
     kerning->y = 0;
+  }
+
+
+  FT_LOCAL_DEF( FT_Error )
+  T1_Get_Track_Kerning( FT_Face    face,
+                        FT_Fixed   ptsize,
+                        FT_Int     degree,
+                        FT_Fixed*  kerning )
+  {
+    AFM_FontInfo  fi = (AFM_FontInfo)( (T1_Face)face )->afm_data;
+    FT_Int        i;
+
+
+    if ( !fi )
+      return T1_Err_Invalid_Argument;
+
+    for ( i = 0; i < fi->NumTrackKern; i++ )
+    {
+      AFM_TrackKern  tk = fi->TrackKerns + i;
+
+
+      if ( tk->degree != degree )
+        continue;
+
+      if ( ptsize < tk->min_ptsize )
+        *kerning = tk->min_kern;
+      else if ( ptsize > tk->max_ptsize )
+        *kerning = tk->max_kern;
+      else
+      {
+        *kerning = FT_MulDiv( ptsize - tk->min_ptsize,
+                              tk->max_kern - tk->min_kern,
+                              tk->max_ptsize - tk->min_ptsize ) +
+                   tk->min_kern;
+      }
+    }
+
+    return T1_Err_Ok;
   }
 
 
--- a/src/type1/t1afm.h
+++ b/src/type1/t1afm.h
@@ -26,37 +26,25 @@
 FT_BEGIN_HEADER
 
 
-  typedef struct  T1_Kern_Pair_
-  {
-    FT_UInt    glyph1;
-    FT_UInt    glyph2;
-    FT_Vector  kerning;
-
-  } T1_Kern_Pair;
-
-
-  typedef struct  T1_AFM_
-  {
-    FT_Int         num_pairs;
-    T1_Kern_Pair*  kern_pairs;
-
-  } T1_AFM;
-
-
   FT_LOCAL( FT_Error )
   T1_Read_Metrics( FT_Face    face,
                    FT_Stream  stream );
 
   FT_LOCAL( void )
-  T1_Done_Metrics( FT_Memory  memory,
-                   T1_AFM*    afm );
+  T1_Done_Metrics( FT_Memory     memory,
+                   AFM_FontInfo  fi );
 
   FT_LOCAL( void )
-  T1_Get_Kerning( T1_AFM*     afm,
-                  FT_UInt     glyph1,
-                  FT_UInt     glyph2,
-                  FT_Vector*  kerning );
+  T1_Get_Kerning( AFM_FontInfo  fi,
+                  FT_UInt       glyph1,
+                  FT_UInt       glyph2,
+                  FT_Vector*    kerning );
 
+  FT_LOCAL( FT_Error )
+  T1_Get_Track_Kerning( FT_Face    face,
+                        FT_Fixed   ptsize,
+                        FT_Int     degree,
+                        FT_Fixed*  kerning );
 
 FT_END_HEADER
 
--- a/src/type1/t1driver.c
+++ b/src/type1/t1driver.c
@@ -36,6 +36,7 @@
 #include FT_SERVICE_POSTSCRIPT_NAME_H
 #include FT_SERVICE_POSTSCRIPT_CMAPS_H
 #include FT_SERVICE_POSTSCRIPT_INFO_H
+#include FT_SERVICE_KERNING_H
 
   /*************************************************************************/
   /*                                                                       */
@@ -176,6 +177,10 @@
     (PS_GetFontPrivateFunc)t1_ps_get_font_private,
   };
 
+  static const FT_Service_KerningRec  t1_service_kerning =
+  {
+    T1_Get_Track_Kerning,
+  };
 
  /*
   *  SERVICE LIST
@@ -188,6 +193,7 @@
     { FT_SERVICE_ID_GLYPH_DICT,           &t1_service_glyph_dict },
     { FT_SERVICE_ID_XF86_NAME,            FT_XF86_FORMAT_TYPE_1 },
     { FT_SERVICE_ID_POSTSCRIPT_INFO,      &t1_service_ps_info },
+    { FT_SERVICE_ID_KERNING,              &t1_service_kerning },
 
 #ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT
     { FT_SERVICE_ID_MULTI_MASTERS,        &t1_service_multi_masters },
@@ -246,15 +252,14 @@
                FT_UInt     right_glyph,
                FT_Vector*  kerning )
   {
-    T1_AFM*  afm;
-
-
     kerning->x = 0;
     kerning->y = 0;
 
-    afm = (T1_AFM*)face->afm_data;
-    if ( afm )
-      T1_Get_Kerning( afm, left_glyph, right_glyph, kerning );
+    if ( face->afm_data )
+      T1_Get_Kerning( (AFM_FontInfo)face->afm_data,
+                      left_glyph,
+                      right_glyph,
+                      kerning );
 
     return T1_Err_Ok;
   }
--- a/src/type1/t1objs.c
+++ b/src/type1/t1objs.c
@@ -233,7 +233,7 @@
 #ifndef T1_CONFIG_OPTION_NO_AFM
       /* release afm data if present */
       if ( face->afm_data )
-        T1_Done_Metrics( memory, (T1_AFM*)face->afm_data );
+        T1_Done_Metrics( memory, (AFM_FontInfo)face->afm_data );
 #endif
 
       /* release unicode map, if any */