/***************************************************************************/

/*                                                                         */

/*  afmparse.c                                                             */

/*                                                                         */

/*    AFM parser (body).                                                   */

/*                                                                         */

/*  Copyright 2006, 2007, 2008, 2009, 2010 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_FREETYPE_H

#include FT_INTERNAL_POSTSCRIPT_AUX_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 = 0;  /* make stupid compiler happy */





    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 value 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_

  {

    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*  const 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"

  };





  /*

   * `afm_parser_read_vals' and `afm_parser_next_key' provide

   * high-level operations to an AFM_Stream.  The rest of the

   * parser functions should use them without accessing the

   * AFM_Stream directly.

   */



  FT_LOCAL_DEF( FT_Int )

  afm_parser_read_vals( AFM_Parser  parser,

                        AFM_Value   vals,

                        FT_UInt     n )

  {

    AFM_Stream  stream = parser->stream;

    char*       str;

    FT_UInt     i;





    if ( n > AFM_MAX_ARGUMENTS )

      return 0;



    for ( i = 0; i < n; i++ )

    {

      FT_Offset  len;

      AFM_Value  val = vals + i;





      if ( val->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 ( val->type )

      {

      case AFM_VALUE_TYPE_STRING:

      case AFM_VALUE_TYPE_NAME:

        {

          FT_Memory  memory = parser->memory;

          FT_Error   error;





          if ( !FT_QALLOC( val->u.s, len + 1 ) )

          {

            ft_memcpy( val->u.s, str, len );

            val->u.s[len] = '\0';

          }

        }

        break;



      case AFM_VALUE_TYPE_FIXED:

        val->u.f = PS_Conv_ToFixed( (FT_Byte**)(void*)&str,

                                    (FT_Byte*)str + len, 0 );

        break;



      case AFM_VALUE_TYPE_INTEGER:

        val->u.i = PS_Conv_ToInt( (FT_Byte**)(void*)&str,

                                  (FT_Byte*)str + len );

        break;



      case AFM_VALUE_TYPE_BOOL:

        val->u.b = FT_BOOL( len == 4                      &&

                            !ft_strncmp( str, "true", 4 ) );

        break;



      case AFM_VALUE_TYPE_INDEX:

        if ( parser->get_index )

          val->u.i = parser->get_index( str, len, parser->user_data );

        else

          val->u.i = 0;

        break;

      }

    }



    return i;

  }





  FT_LOCAL_DEF( char* )

  afm_parser_next_key( AFM_Parser  parser,

                       FT_Bool     line,

                       FT_Offset*  len )

  {

    AFM_Stream  stream = parser->stream;

    char*       key    = 0;  /* make stupid compiler happy */





    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 ) ? (FT_Offset)AFM_STREAM_KEY_LEN( stream, key )

                     : 0;



    return key;

  }





  static AFM_Token

  afm_tokenize( const char*  key,

                FT_Offset    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 (AFM_Token) 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 = NULL;

    FT_Error    error;





    if ( FT_NEW( stream ) )

      return error;



    stream->cursor = stream->base = base;

    stream->limit  = limit;



    /* don't skip the first line during the first call */

    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_Offset      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;





      if ( FT_QNEW_ARRAY( fi->TrackKerns, fi->NumTrackKern ) )

        return error;

    }



    while ( ( key = afm_parser_next_key( parser, 1, &len ) ) != 0 )

    {

      AFM_ValueRec  shared_vals[5];





      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;



      case AFM_TOKEN_UNKNOWN:

        break;



      default:

        goto Fail;

      }

    }



  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 );





    if ( index1 > index2 )

      return 1;

    else if ( index1 < index2 )

      return -1;

    else

      return 0;

  }





  static FT_Error

  afm_parse_kern_pairs( AFM_Parser  parser )

  {

    AFM_FontInfo  fi = parser->FontInfo;

    AFM_KernPair  kp;

    char*         key;

    FT_Offset     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;





      if ( FT_QNEW_ARRAY( fi->KernPairs, fi->NumKernPair ) )

        return error;

    }



    while ( ( key = afm_parser_next_key( parser, 1, &len ) ) != 0 )

    {

      AFM_Token  token = afm_tokenize( key, len );





      switch ( token )

      {

      case AFM_TOKEN_KP:

      case AFM_TOKEN_KPX:

      case AFM_TOKEN_KPY:

        {

          FT_Int        r;

          AFM_ValueRec  shared_vals[4];





          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;



      case AFM_TOKEN_UNKNOWN:

        break;



      default:

        goto Fail;

      }

    }



  Fail:

    return PSaux_Err_Syntax_Error;

  }





  static FT_Error

  afm_parse_kern_data( AFM_Parser  parser )

  {

    FT_Error   error;

    char*      key;

    FT_Offset  len;





    while ( ( key = afm_parser_next_key( parser, 1, &len ) ) != 0 )

    {

      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;



      case AFM_TOKEN_UNKNOWN:

        break;



      default:

        goto Fail;

      }

    }



  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_Offset  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 ) ) != 0 )

    {

      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_Offset     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 ) ) != 0 )

    {

      AFM_ValueRec  shared_vals[4];





      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_FONTBBOX:

        shared_vals[0].type = AFM_VALUE_TYPE_FIXED;

        shared_vals[1].type = AFM_VALUE_TYPE_FIXED;

        shared_vals[2].type = AFM_VALUE_TYPE_FIXED;

        shared_vals[3].type = AFM_VALUE_TYPE_FIXED;

        if ( afm_parser_read_vals( parser, shared_vals, 4 ) != 4 )

          goto Fail;



        fi->FontBBox.xMin = shared_vals[0].u.f;

        fi->FontBBox.yMin = shared_vals[1].u.f;

        fi->FontBBox.xMax = shared_vals[2].u.f;

        fi->FontBBox.yMax = shared_vals[3].u.f;

        break;



      case AFM_TOKEN_ASCENDER:

        shared_vals[0].type = AFM_VALUE_TYPE_FIXED;

        if ( afm_parser_read_vals( parser, shared_vals, 1 ) != 1 )

          goto Fail;



        fi->Ascender = shared_vals[0].u.f;

        break;



      case AFM_TOKEN_DESCENDER:

        shared_vals[0].type = AFM_VALUE_TYPE_FIXED;

        if ( afm_parser_read_vals( parser, shared_vals, 1 ) != 1 )

          goto Fail;



        fi->Descender = shared_vals[0].u.f;

        break;



      case AFM_TOKEN_STARTCHARMETRICS:

        {

          FT_Int  n = 0;





          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;

        /* fall through since we only support kern data */



      case AFM_TOKEN_ENDFONTMETRICS:

        return PSaux_Err_Ok;



      default:

        break;

      }

    }



  Fail:

    FT_FREE( fi->TrackKerns );

    fi->NumTrackKern = 0;



    FT_FREE( fi->KernPairs );

    fi->NumKernPair = 0;



    fi->IsCIDFont = 0;



    return error;

  }





/* END */

