/***************************************************************************/
/*                                                                         */
/*  t1load.c                                                               */
/*                                                                         */
/*    Type 1 font loader (body).                                           */
/*                                                                         */
/*  Copyright 1996-2000 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 <freetype/config/ftconfig.h>
#include <freetype/internal/ftdebug.h>
#include <freetype/internal/t1types.h>


#ifdef FT_FLAT_COMPILE

#include "t1tokens.h"
#include "t1parse.h"

#else

#include <freetype/src/type1/t1tokens.h>
#include <freetype/src/type1/t1parse.h>

#endif


#include <stdio.h>

#include <string.h>     /* for strncpy(), strncmp(), strlen() */


  /*************************************************************************/
  /*                                                                       */
  /* The macro FT_COMPONENT is used in trace mode.  It is an implicit      */
  /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log  */
  /* messages during execution.                                            */
  /*                                                                       */
#undef  FT_COMPONENT
#define FT_COMPONENT  trace_t1load


  typedef FT_Error  (*T1_Parse_Func)( T1_Parser*  parser );


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    Init_T1_Parser                                                     */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Initializes a given parser object to build a given T1_Face.        */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    parser    :: A handle to the newly built parser object.            */
  /*                                                                       */
  /* <Input>                                                               */
  /*    face      :: A handle to the target Type 1 face object.            */
  /*                                                                       */
  /*    tokenizer :: A handle to the target Type 1 token manager.          */
  /*                                                                       */
  LOCAL_FUNC
  void  Init_T1_Parser( T1_Parser*    parser,
                        T1_Face       face,
                        T1_Tokenizer  tokenizer )
  {
    parser->error     = 0;
    parser->face      = face;
    parser->tokenizer = tokenizer;
    parser->top       = parser->stack;
    parser->limit     = parser->stack + T1_MAX_STACK_DEPTH;

    parser->state_index    = 0;
    parser->state_stack[0] = dict_none;

    parser->encoding_type    = t1_encoding_none;
    parser->encoding_names   = 0;
    parser->encoding_offsets = 0;
    parser->encoding_lengths = 0;

    parser->dump_tokens            = 0;
    face->type1.private_dict.lenIV = 4;  /* XXX : is it sure? */
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    Next_T1_Token                                                      */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Grabs the next significant token from a parser's input stream.     */
  /*    This function ignores a number of tokens, and translates           */
  /*    alternate forms into their common ones.                            */
  /*                                                                       */
  /* <Input>                                                               */
  /*    parser :: A handle to the source parser.                           */
  /*                                                                       */
  /* <Output>                                                              */
  /*    token  :: The extracted token descriptor.                          */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeTyoe error code.  0 means success.                             */
  /*                                                                       */
  LOCAL_FUNC
  FT_Error  Next_T1_Token( T1_Parser*  parser,
                           T1_Token*   token )
  {
    FT_Error      error;
    T1_Tokenizer  tokzer = parser->tokenizer;


  L1:
    error = Read_Token( tokzer );
    if ( error )
      return error;

    /* we now must ignore a number of tokens like `dup', `executeonly', */
    /* `readonly', etc.                                                 */
    *token = tokzer->token;
    if ( token->kind == tok_keyword )
      switch( token->kind2 )
      {
      case key_dup:
      case key_execonly:
      case key_readonly:
      case key_noaccess:
      case key_userdict:
        /* do nothing - loop */
        goto L1;

        /* we also translate some other keywords from their alternative */
        /* to their `normal' form                                       */

      case key_NP_alternate:
        token->kind2 = key_NP;
        break;

      case key_RD_alternate:
        token->kind2 = key_RD;
        break;

      case key_ND_alternate:
        token->kind2 = key_ND;
        break;

      default:
        ;
      }

#if defined( FT_DEBUG_LEVEL_ERROR ) || defined( FT_DEBUG_LEVEL_TRACE )

    /* Dump the token when requested.  This feature is only available */
    /* in the `error' and `trace' debug levels.                       */
    if ( parser->dump_tokens )
    {
      FT_String  temp_string[128];
      FT_Int     len;


      len = token->len;
      if ( len > 127 )
        len = 127;
      strncpy( temp_string,
               (FT_String*)tokzer->base + token->start,
               len );
      temp_string[len] = '\0';
      FT_ERROR(( "%s\n", temp_string ));
    }

#endif /* FT_DEBUG_LEVEL_ERROR or FT_DEBUG_LEVEL_TRACE */

    return T1_Err_Ok;
  }


  static
  FT_Error  Expect_Keyword( T1_Parser*    parser,
                            T1_TokenType  keyword )
  {
    T1_Token  token;
    FT_Error  error;


    error = Next_T1_Token( parser, &token );
    if ( error )
      goto Exit;

    if ( token.kind  != tok_keyword ||
         token.kind2 != keyword     )
    {
      error = T1_Err_Syntax_Error;
      FT_ERROR(( "Expect_Keyword: keyword `%s' expected.\n",
                 t1_keywords[keyword - key_first_] ));
    }

  Exit:
    return error;
  }


  static
  FT_Error  Expect_Keyword2( T1_Parser*    parser,
                             T1_TokenType  keyword1,
                             T1_TokenType  keyword2 )
  {
    T1_Token  token;
    FT_Error  error;


    error = Next_T1_Token( parser, &token );
    if ( error )
      goto Exit;

    if ( token.kind  != tok_keyword  ||
         ( token.kind2 != keyword1 &&
           token.kind2 != keyword2 ) )
    {
      error = T1_Err_Syntax_Error;
      FT_ERROR(( "Expect_Keyword2: keyword `%s' or `%s' expected.\n",
                 t1_keywords[keyword1 - key_first_],
                 t1_keywords[keyword2 - key_first_] ));
    }

  Exit:
    return error;
  }


  static
  void  Parse_Encoding( T1_Parser*  parser )
  {
    T1_Token*     token  = parser->top+1;
    FT_Memory     memory = parser->face->root.memory;
    T1_Encoding*  encode = &parser->face->type1.encoding;
    FT_Error      error  = 0;


    if ( token->kind  == tok_keyword              &&
         ( token->kind2 == key_StandardEncoding ||
           token->kind2 == key_ExpertEncoding   ) )
    {
      encode->num_chars  = 256;
      encode->code_first = 32;
      encode->code_last  = 255;

      if ( ALLOC_ARRAY( encode->char_index, 256, FT_Short ) )
        goto Exit;

      encode->char_name = 0;  /* no need to store glyph names */

      /* Now copy the encoding */
      switch ( token->kind2 )
      {
      case key_ExpertEncoding:
        parser->encoding_type = t1_encoding_expert;
        break;

      default:
        parser->encoding_type = t1_encoding_standard;
        break;
      }
    }
    else
    {
      FT_ERROR(( "Parse_Encoding: invalid encoding type\n" ));
      error = T1_Err_Syntax_Error;
    }

  Exit:
    parser->error = error;
  }


  /*************************************************************************/
  /*                                                                       */
  /*                                                                       */
  /*           IMPLEMENTATION OF THE `DEF' KEYWORD DEPENDING ON            */
  /*                        CURRENT DICTIONARY STATE                       */
  /*                                                                       */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    Do_Def_Font                                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This function performs a `def' if in the Font dictionary.  Its     */
  /*    purpose is to build the T1_Face attributes directly from the       */
  /*    stream.                                                            */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    parser :: A handle to the current parser.                          */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0 means success.                             */
  /*                                                                       */
  static
  FT_Error  Do_Def_Font( T1_Parser*  parser )
  {
    T1_Token*  top   = parser->top;
    T1_Face    face  = parser->face;
    T1_Font*   type1 = &face->type1;


    switch ( top[0].kind2 )
    {
    case imm_FontName:
      /* in some cases, the /FontName is an immediate like */
      /* /TimesNewRoman.  In this case, we simply copy the */
      /* token string (without the /).                     */
      if ( top[1].kind == tok_immediate )
      {
        FT_Memory  memory = parser->tokenizer->memory;
        FT_Error   error;
        FT_Int     len = top[1].len;


        if ( ALLOC( type1->font_name, len + 1 ) )
        {
          parser->error = error;
          return error;
        }

        MEM_Copy( type1->font_name,
                  parser->tokenizer->base + top[1].start,
                  len );
        type1->font_name[len] = '\0';
      }
      else
        type1->font_name = CopyString( parser );
      break;

    case imm_Encoding:
      Parse_Encoding( parser );
      break;

    case imm_PaintType:
      type1->paint_type = (FT_Byte)CopyInteger( parser );
      break;

    case imm_FontType:
      type1->font_type = (FT_Byte)CopyInteger( parser );
      break;

    case imm_FontMatrix:
      CopyMatrix( parser, &type1->font_matrix );
      break;

    case imm_FontBBox:
      CopyBBox( parser, &type1->font_bbox );
      break;

    case imm_UniqueID:
      type1->private_dict.unique_id = CopyInteger( parser );
      break;

    case imm_StrokeWidth:
      type1->stroke_width = CopyInteger( parser );
      break;

    case imm_FontID:
      type1->font_id = CopyInteger( parser );
      break;

    default:
      /* ignore all other things */
      parser->error = T1_Err_Ok;
    }

    return parser->error;
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    Do_Def_FontInfo                                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This function performs a `def' if in the FontInfo dictionary. Its  */
  /*    purpose is to build the T1_FontInfo structure directly from the    */
  /*    stream.                                                            */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    parser :: A handle to the current parser.                          */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeTyoe error code.  0 means success.                             */
  /*                                                                       */
  static
  FT_Error  Do_Def_FontInfo( T1_Parser*  parser )
  {
    T1_Token*     top  = parser->top;
    T1_FontInfo*  info = &parser->face->type1.font_info;


    switch ( top[0].kind2 )
    {
    case imm_version:
      info->version = CopyString( parser );
      break;

    case imm_Notice:
      info->notice = CopyString( parser );
      break;

    case imm_FullName:
      info->full_name = CopyString( parser );
      break;

    case imm_FamilyName:
      info->family_name = CopyString( parser );
      break;

    case imm_Weight:
      info->weight = CopyString( parser );
      break;

    case imm_ItalicAngle:
      info->italic_angle = CopyInteger( parser );
      break;

    case imm_isFixedPitch:
      info->is_fixed_pitch = CopyBoolean( parser );
      break;

    case imm_UnderlinePosition:
      info->underline_position = (FT_Short)CopyInteger( parser );
      break;

    case imm_UnderlineThickness:
      info->underline_thickness = (FT_Short)CopyInteger( parser );
      break;

    default:
      /* ignore all other things */
      parser->error = T1_Err_Ok;
    }

    return parser->error;
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    Do_Def_Private                                                     */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This function performs a `def' if in the Private dictionary.  Its  */
  /*    purpose is to build the T1_Private structure directly from the     */
  /*    stream.                                                            */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    parser :: A handle to the current parser.                          */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeTyoe error code.  0 means success.                             */
  /*                                                                       */
  static
  FT_Error  Do_Def_Private( T1_Parser*  parser )
  {
    T1_Token*   top   = parser->top;
    T1_Private* priv  = &parser->face->type1.private_dict;


    switch ( top[0].kind2 )
    {
      /* Ignore the definitions of RD, NP, ND, and their alternate forms */
    case imm_RD:
    case imm_RD_alternate:
    case imm_ND:
    case imm_ND_alternate:
    case imm_NP:
    case imm_NP_alternate:
      parser->error = T1_Err_Ok;
      break;

    case imm_BlueValues:
      CopyArray( parser, &priv->num_blue_values,
                 priv->blue_values, 14 );
      break;

    case imm_OtherBlues:
      CopyArray( parser, &priv->num_other_blues,
                 priv->other_blues, 10 );
      break;

    case imm_FamilyBlues:
      CopyArray( parser, &priv->num_family_blues,
                 priv->family_blues, 14 );
      break;

    case imm_FamilyOtherBlues:
      CopyArray( parser, &priv->num_family_other_blues,
                 priv->family_other_blues, 10 );
      break;

    case imm_BlueScale:
      priv->blue_scale = CopyFloat( parser, 0x10000L );
      break;

    case imm_BlueShift:
      priv->blue_shift = CopyInteger( parser );
      break;

    case imm_BlueFuzz:
      priv->blue_fuzz = CopyInteger( parser );
      break;

    case imm_StdHW:
      CopyArray( parser, 0, (FT_Short*)&priv->standard_width, 1 );
      break;

    case imm_StdVW:
      CopyArray( parser, 0, (FT_Short*)&priv->standard_height, 1 );
      break;

    case imm_StemSnapH:
      CopyArray( parser, &priv->num_snap_widths,
                 priv->snap_widths, 12 );
      break;

    case imm_StemSnapV:
      CopyArray( parser, &priv->num_snap_heights,
                 priv->snap_heights, 12 );
      break;

    case imm_ForceBold:
      priv->force_bold = CopyBoolean( parser );
      break;

    case imm_LanguageGroup:
      priv->language_group = CopyInteger( parser );
      break;

    case imm_password:
      priv->password = CopyInteger( parser );
      break;

    case imm_UniqueID:
      priv->unique_id = CopyInteger( parser );
      break;

    case imm_lenIV:
      priv->lenIV = CopyInteger( parser );
      break;

    case imm_MinFeature:
      CopyArray( parser, 0, priv->min_feature, 2 );
      break;

    default:
      /* ignore all other things */
      parser->error = T1_Err_Ok;
    }

    return parser->error;
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    Do_Def_Error                                                       */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This function returns a simple syntax error when invoked.  It is   */
  /*    used for the `def' keyword if in the `encoding', `subrs',          */
  /*    `othersubrs', and `charstrings' dictionary states.                 */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    parser :: A handle to the current parser.                          */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0 means success.                             */
  /*                                                                       */
  static
  FT_Error  Do_Def_Error( T1_Parser*  parser )
  {
    FT_ERROR(( "Do_Def_Error:" ));
    FT_ERROR(( " `def' keyword encountered in bad dictionary/array\n" ));

    parser->error = T1_Err_Syntax_Error;

    return parser->error;
  }


  static
  FT_Error  Do_Def_Ignore( T1_Parser*  parser )
  {
    FT_UNUSED( parser );
    return T1_Err_Ok;
  }


  static
  T1_Parse_Func  def_funcs[dict_max] =
  {
    Do_Def_Error,
    Do_Def_Font,
    Do_Def_FontInfo,
    Do_Def_Ignore,
    Do_Def_Private,
    Do_Def_Ignore,
    Do_Def_Ignore,
    Do_Def_Ignore,
    Do_Def_Ignore,
    Do_Def_Ignore,
    Do_Def_Ignore,
  };


  /*************************************************************************/
  /*                                                                       */
  /*                                                                       */
  /*           IMPLEMENTATION OF THE `PUT' KEYWORD DEPENDING ON            */
  /*                        CURRENT DICTIONARY STATE                       */
  /*                                                                       */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    Do_Put_Encoding                                                    */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This function performs a `put' if in the Encoding array.  The      */
  /*    glyph name is copied into the T1 recorder, and the charcode and    */
  /*    glyph name pointer are written into the face object encoding.      */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    parser :: A handle to the current parser.                          */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0 means success.                             */
  /*                                                                       */
  static
  FT_Error  Do_Put_Encoding( T1_Parser*  parser )
  {
    FT_Error      error  = T1_Err_Ok;
    T1_Face       face   = parser->face;
    T1_Token*     top    = parser->top;
    T1_Encoding*  encode = &face->type1.encoding;
    FT_Int        index;


    /* record and check the character code */
    if ( top[0].kind != tok_number )
    {
      FT_TRACE4(( "Do_Put_Encoding: number expected\n" ));
      goto Syntax_Error;
    }
    index = (FT_Int)CopyInteger( parser );
    if ( parser->error )
      return parser->error;

    if ( index < 0 || index >= encode->num_chars )
    {
      FT_TRACE4(( "Do_Put_Encoding: invalid character code\n" ));
      goto Syntax_Error;
    }

    /* record the immediate name */
    if ( top[1].kind != tok_immediate )
    {
      FT_TRACE4(( "Do_Put_Encoding: immediate name expected\n" ));
      goto Syntax_Error;
    }

    /* if the glyph name is `.notdef', store a NULL char name; */
    /* otherwise, record the glyph name                        */
    if ( top[1].kind == imm_notdef )
    {
      parser->table.elements[index] = 0;
      parser->table.lengths [index] = 0;
    }
    else
    {
      FT_String  temp_name[128];
      T1_Token*  token = top + 1;
      FT_Int     len   = token->len - 1;


      /* copy immediate name */
      if ( len > 127 )
        len = 127;
      MEM_Copy( temp_name, parser->tokenizer->base + token->start + 1, len );
      temp_name[len] = '\0';

      error = T1_Add_Table( &parser->table, index,
                            (FT_Byte*)temp_name, len + 1 );

      /* adjust code_first and code_last */
      if ( index < encode->code_first ) encode->code_first = index;
      if ( index > encode->code_last  ) encode->code_last  = index;
    }
    return error;

  Syntax_Error:
    /* ignore the error, and simply clear the stack */
    FT_TRACE4(( "Do_Put_Encoding: invalid syntax encountered\n" ));
    parser->top = parser->stack;

    return T1_Err_Ok;
  }


  /*************************************************************************/
  /*                                                                       */
  /*                                                                       */
  /*           IMPLEMENTATION OF THE "RD" KEYWORD DEPENDING ON             */
  /*                        CURRENT DICTIONARY STATE                       */
  /*                                                                       */
  /*                                                                       */
  /*************************************************************************/


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    Do_RD_Subrs                                                        */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This function performs an `RD' if in the Subrs dictionary.  It     */
  /*    simply records the array of bytecodes/charstrings corresponding to */
  /*    the sub-routine.                                                   */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    parser :: A handle to the current parser.                          */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0 means success.                             */
  /*                                                                       */
  static
  FT_Error  Do_RD_Subrs( T1_Parser*  parser )
  {
    FT_Error      error  = T1_Err_Ok;
    T1_Face       face   = parser->face;
    T1_Token*     top    = parser->top;
    T1_Tokenizer  tokzer = parser->tokenizer;
    FT_Int        index, count;


    /* record and check the character code */
    if ( top[0].kind != tok_number ||
         top[1].kind != tok_number )
    {
      FT_ERROR(( "Do_RD_Subrs: number expected\n" ));
      goto Syntax_Error;
    }
    index = (FT_Int)CopyInteger( parser );
    error = parser->error;
    if ( error )
      goto Exit;

    count = (FT_Int)CopyInteger( parser );
    error = parser->error;
    if ( error )
      goto Exit;

    if ( index < 0 || index >= face->type1.num_subrs )
    {
      FT_ERROR(( "Do_RD_Subrs: invalid character code\n" ));
      goto Syntax_Error;
    }

    /* decrypt charstring and skip it */
    {
      FT_Byte*  base = tokzer->base + tokzer->cursor;


      tokzer->cursor += count;

      /* some fonts use a value of -1 for lenIV to indicate that */
      /* the charstrings are unencoded.                          */
      /*                                                         */
      /* Thanks to Tom Kacvinsky for pointing this out.          */
      /*                                                         */
      if ( face->type1.private_dict.lenIV >= 0 )
      {
        t1_decrypt( base, count, 4330 );

        base  += face->type1.private_dict.lenIV;
        count -= face->type1.private_dict.lenIV;
      }

      error = T1_Add_Table( &parser->table, index, base, count );
    }

    /* consume the closing NP or `put' */
    error = Expect_Keyword2( parser, key_NP, key_put );

  Exit:
    return error;

  Syntax_Error:
    return T1_Err_Syntax_Error;
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    Do_RD_CharStrings                                                  */
  /*                                                                       */
  /* <Description>                                                         */
  /*    This function performs an `RD' if in the CharStrings dictionary.   */
  /*    It simply records the array of bytecodes/charstrings corresponding */
  /*    to the glyph program string.                                       */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    parser :: A handle to the current parser.                          */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0 means success.                             */
  /*                                                                       */
  static
  FT_Error  Do_RD_Charstrings( T1_Parser*  parser )
  {
    FT_Error      error = T1_Err_Ok;
    T1_Face       face  = parser->face;
    T1_Token*     top   = parser->top;
    T1_Tokenizer  tokzer = parser->tokenizer;
    FT_Int        index, count;


    /* check the character name argument */
    if ( top[0].kind != tok_immediate )
    {
      FT_ERROR(( "Do_RD_Charstrings: immediate character name expected\n" ));
      goto Syntax_Error;
    }

    /* check the count argument */
    if ( top[1].kind != tok_number )
    {
      FT_ERROR(( "Do_RD_Charstrings: number expected\n" ));
      goto Syntax_Error;
    }

    parser->args++;
    count = (FT_Int)CopyInteger( parser );
    error = parser->error;
    if ( error )
      goto Exit;

    /* record the glyph name and get the corresponding glyph index */
    if ( top[0].kind2 == imm_notdef )
      index = 0;
    else
    {
      FT_String  temp_name[128];
      T1_Token*  token = top;
      FT_Int     len   = token->len - 1;


      /* copy immediate name */
      if ( len > 127 )
        len = 127;
      MEM_Copy( temp_name, parser->tokenizer->base + token->start + 1, len );
      temp_name[len] = '\0';

      index = parser->cur_name++;
      error = T1_Add_Table( &parser->table, index * 2,
                            (FT_Byte*)temp_name, len + 1 );
      if ( error )
        goto Exit;
    }

    /* decrypt and record charstring, then skip them */
    {
      FT_Byte*  base = tokzer->base + tokzer->cursor;


      tokzer->cursor += count;  /* skip */

      if ( face->type1.private_dict.lenIV >= 0 )
      {
        t1_decrypt( base, count, 4330 );

        base  += face->type1.private_dict.lenIV;
        count -= face->type1.private_dict.lenIV;
      }

      error = T1_Add_Table( &parser->table, index * 2 + 1, base, count );
    }

    /* consume the closing `ND' */
    if ( !error )
      error = Expect_Keyword( parser, key_ND );

  Exit:
    return error;

  Syntax_Error:
    return T1_Err_Syntax_Error;
  }


  static
  FT_Error  Expect_Dict_Arguments( T1_Parser*    parser,
                                   FT_Int        num_args,
                                   T1_TokenType  immediate,
                                   T1_DictState  new_state,
                                   FT_Int*       count )
  {
    /* check that we have enough arguments in the stack, including */
    /* the `dict' keyword                                          */
    if ( parser->top - parser->stack < num_args )
    {
      FT_ERROR(( "Expect_Dict_Arguments: expecting at least %d arguments",
                 num_args ));
      goto Syntax_Error;
    }

    /* check that we have the correct immediate, if needed */
    if ( num_args == 2 )
    {
      if ( parser->top[-2].kind  != tok_immediate ||
           parser->top[-2].kind2 != immediate     )
      {
        FT_ERROR(( "Expect_Dict_Arguments: expecting `/%s' dictionary\n",
                   t1_immediates[immediate - imm_first_] ));
        goto Syntax_Error;
      }
    }

    parser->args = parser->top-1;

    /* check that the count argument is a number */
    if ( parser->args->kind != tok_number )
    {
      FT_ERROR(( "Expect_Dict_Arguments:" ));
      FT_ERROR(( " expecting numerical count argument for `dict'\n" ));
      goto Syntax_Error;
    }

    if ( count )
    {
      *count = CopyInteger( parser );
      if ( parser->error )
        return parser->error;
    }

    /* save the dictionary state */
    parser->state_stack[++parser->state_index] = new_state;

    /* consume the `begin' keyword and clear the stack */
    parser->top -= num_args;
    return Expect_Keyword( parser, key_begin );

  Syntax_Error:
    return T1_Err_Syntax_Error;
  }


  static
  FT_Error  Expect_Array_Arguments( T1_Parser*  parser )
  {
    T1_Token*     top   = parser->top;
    FT_Error      error = T1_Err_Ok;
    T1_DictState  new_state;
    FT_Int        count;
    T1_Face       face   = parser->face;
    FT_Memory     memory = face->root.memory;


    /* Check arguments format */
    if ( top - parser->stack < 2 )
    {
      FT_ERROR(( "Expect_Array_Arguments: two arguments expected\n" ));
      error = T1_Err_Stack_Underflow;
      goto Exit;
    }

    parser->top -= 2;
    top         -= 2;
    parser->args = top + 1;

    if ( top[0].kind != tok_immediate )
    {
      FT_ERROR(( "Expect_Array_Arguments:" ));
      FT_ERROR(( " first argument must be an immediate name\n" ));
      goto Syntax_Error;
    }

    if ( top[1].kind != tok_number )
    {
      FT_ERROR(( "Expect_Array_Arguments:" ));
      FT_ERROR(( " second argument must be a number\n" ));
      goto Syntax_Error;
    }

    count = (FT_Int)CopyInteger( parser );

    /* Is this an array we know about? */
    switch ( top[0].kind2 )
    {
    case imm_Encoding:
      {
        T1_Encoding*  encode = &face->type1.encoding;


        new_state = dict_encoding;

        encode->code_first = count;
        encode->code_last  = 0;
        encode->num_chars  = count;

        /* Allocate the table of character indices.  The table of   */
        /* character names is allocated through init_t1_recorder(). */
        if ( ALLOC_ARRAY( encode->char_index, count, FT_Short ) )
          return error;

        error = T1_New_Table( &parser->table, count, memory );
        if ( error )
          goto Exit;

        parser->encoding_type = t1_encoding_array;
      }
      break;

    case imm_Subrs:
      new_state             = dict_subrs;
      face->type1.num_subrs = count;

      error = T1_New_Table( &parser->table, count, memory );
      if ( error )
        goto Exit;
      break;

    case imm_CharStrings:
      new_state = dict_charstrings;
      break;

    default:
      new_state = dict_unknown_array;
    }

    parser->state_stack[++parser->state_index] = new_state;

  Exit:
    return error;

  Syntax_Error:
    return T1_Err_Syntax_Error;
  }


  static
  FT_Error  Finalize_Parsing( T1_Parser*  parser )
  {
    T1_Face             face    = parser->face;
    T1_Font*            type1   = &face->type1;
    FT_Memory           memory  = face->root.memory;
    T1_Table*           strings = &parser->table;
    PSNames_Interface*  psnames = (PSNames_Interface*)face->psnames;

    FT_Int              num_glyphs;
    FT_Int              n;
    FT_Error            error;


    num_glyphs = type1->num_glyphs = parser->cur_name;

    /* allocate glyph names and charstrings arrays */
    if ( ALLOC_ARRAY( type1->glyph_names,     num_glyphs, FT_String* ) ||
         ALLOC_ARRAY( type1->charstrings,     num_glyphs, FT_Byte* )   ||
         ALLOC_ARRAY( type1->charstrings_len, num_glyphs, FT_Int*  )   )
      return error;

    /* copy glyph names and charstrings offsets and lengths */
    type1->charstrings_block = strings->block;
    for ( n = 0; n < num_glyphs; n++ )
    {
      type1->glyph_names[n]     = (FT_String*)strings->elements[2 * n];
      type1->charstrings[n]     = strings->elements[2 * n + 1];
      type1->charstrings_len[n] = strings->lengths [2 * n + 1];
    }

    /* now free the old tables */
    FREE( strings->elements );
    FREE( strings->lengths );

    if ( !psnames )
    {
      FT_ERROR(( "Finalize_Parsing: `PSNames' module missing!\n" ));
      return T1_Err_Unimplemented_Feature;
    }

    /* compute encoding if required */
    if ( parser->encoding_type == t1_encoding_none )
    {
      FT_ERROR(( "Finalize_Parsing: no encoding specified in font file\n" ));
      return T1_Err_Syntax_Error;
    }

    {
      FT_Int        n;
      T1_Encoding*  encode = &type1->encoding;


      encode->code_first = encode->num_chars - 1;
      encode->code_last  = 0;

      for ( n = 0; n < encode->num_chars; n++ )
      {
        FT_String** names;
        FT_Int      index;
        FT_Int      m;


        switch ( parser->encoding_type )
        {
        case t1_encoding_standard:
          index = psnames->adobe_std_encoding[n];
          names = 0;
          break;

        case t1_encoding_expert:
          index = psnames->adobe_expert_encoding[n];
          names = 0;
          break;

        default:
          index = n;
          names = (FT_String**)parser->encoding_offsets;
        }

        encode->char_index[n] = 0;

        if ( index )
        {
          FT_String*  name;


          if ( names )
            name = names[index];
          else
            name = (FT_String*)psnames->adobe_std_strings(index);

          if ( name )
          {
            FT_Int  len = strlen( name );


            /* lookup glyph index from name */
            for ( m = 0; m < num_glyphs; m++ )
            {
              if ( strncmp( type1->glyph_names[m], name, len ) == 0 )
              {
                encode->char_index[n] = m;
                break;
              }
            }

            if ( n < encode->code_first ) encode->code_first = n;
            if ( n > encode->code_last  ) encode->code_last  = n;
          }
        }
      }

      parser->encoding_type = t1_encoding_none;

      FREE( parser->encoding_names );
      FREE( parser->encoding_lengths );
      FREE( parser->encoding_offsets );
    }

    return T1_Err_Ok;
  }


  /*************************************************************************/
  /*                                                                       */
  /* <Function>                                                            */
  /*    Parse_T1_FontProgram                                               */
  /*                                                                       */
  /* <Description>                                                         */
  /*    Parses a given Type 1 font file and builds its face object.        */
  /*                                                                       */
  /* <InOut>                                                               */
  /*    parser :: A handle to the target parser object.                    */
  /*                                                                       */
  /* <Return>                                                              */
  /*    FreeType error code.  0 means success.                             */
  /*                                                                       */
  /* <Note>                                                                */
  /*    The parser contains a handle to the target face object.            */
  /*                                                                       */
  LOCAL_FUNC
  FT_Error  Parse_T1_FontProgram( T1_Parser*  parser )
  {
    FT_Error  error;
    T1_Font*  type1 = &parser->face->type1;


    for (;;)
    {
      T1_Token      token;
      T1_Token*     top;
      T1_DictState  dict_state;
      FT_Int        dict_index;


      error      = Next_T1_Token( parser, &token );
      top        = parser->top;
      dict_index = parser->state_index;
      dict_state = parser->state_stack[dict_index];

      switch ( token.kind )
      {
        /* a keyword has been detected */
      case tok_keyword:
        switch ( token.kind2 )
        {
        case key_dict:
          switch ( dict_state )
          {
          case dict_none:
            /* All right, we are beginning the font dictionary.  */
            /* Check that we only have one number argument, then */
            /* consume the `begin' and change to `dict_font'     */
            /* state.                                            */
            error = Expect_Dict_Arguments( parser, 1, tok_error,
                                           dict_font, 0 );
            if ( error )
              goto Exit;

            /* clear stack from all the previous content.  This */
            /* could be some stupid Postscript code.            */
            parser->top = parser->stack;
            break;

          case dict_font:
            /* This must be the /FontInfo dictionary, so check */
            /* that we have at least two arguments, that they  */
            /* are `/FontInfo' and a number, then change the   */
            /* dictionary state.                               */
            error = Expect_Dict_Arguments( parser, 2, imm_FontInfo,
                                           dict_fontinfo, 0 );
            if ( error )
              goto Exit;
            break;

          case dict_none2:
            error = Expect_Dict_Arguments( parser, 2, imm_Private,
                                           dict_private, 0 );
            if ( error )
              goto Exit;
            break;

          case dict_private:
            {
              T1_Face  face = parser->face;
              FT_Int   count;


              error = Expect_Dict_Arguments( parser, 2, imm_CharStrings,
                                             dict_charstrings, &count );
              if ( error )
                goto Exit;

              type1->num_glyphs = count;
              error = T1_New_Table( &parser->table, count * 2,
                                    face->root.memory );
              if ( error )
                goto Exit;

              /* record `.notdef' as the first glyph in the font */
              error = T1_Add_Table( &parser->table, 0,
                                    (FT_Byte*)".notdef", 8 );
              parser->cur_name = 1;
              /* XXX: DO SOMETHING HERE */
            }
            break;

          default:
            /* All other uses are invalid */
            FT_ERROR(( "Parse_T1_FontProgram:" ));
            FT_ERROR(( " invalid use of `dict' keyword\n" ));
            goto Syntax_Error;
          }
          break;

        case key_array:
          /* Are we in an array yet?  If so, raise an error */
          switch ( dict_state )
          {
          case dict_encoding:
          case dict_subrs:
          case dict_othersubrs:
          case dict_charstrings:
          case dict_unknown_array:
            FT_ERROR(( "Parse_T1_FontProgram: nested array definitions\n" ));
            goto Syntax_Error;

          default:
            ;
          }
          error = Expect_Array_Arguments( parser );
          if ( error )
            goto Exit;
          break;

        case key_ND:
        case key_NP:
        case key_def:
          /* Are we in an array? If so, finalize it. */
          switch ( dict_state )
          {
          case dict_encoding:    /* finish encoding array */
            /* copy table names to the face object */
            T1_Done_Table( &parser->table );

            parser->encoding_names   = parser->table.block;
            parser->encoding_lengths = parser->table.lengths;
            parser->encoding_offsets = parser->table.elements;

            parser->state_index--;
            break;

          case dict_subrs:
            /* copy recorder sub-routines */
            T1_Done_Table( &parser->table );

            parser->subrs      = parser->table.block;
            type1->subrs       = parser->table.elements;
            type1->subrs_len   = parser->table.lengths;
            type1->subrs_block = parser->table.block;

            parser->state_index--;
            break;

          case dict_charstrings:
          case dict_othersubrs:
          case dict_unknown_array:
            FT_ERROR(( "Parse_T1_FontProgram: unsupported array\n" ));
            goto Syntax_Error;
            break;

          default:   /* normal `def' processing */
            /* Check that we have sufficient operands in the stack */
            if ( top >= parser->stack + 2 )
            {
              /* Now check that the first operand is an immediate. */
              /* If so, call the appropriate `def' routine based   */
              /* on the current parser state.                      */
              if ( top[-2].kind == tok_immediate )
              {
                parser->top -= 2;
                parser->args = parser->top + 1;
                error = def_funcs[dict_state](parser);
              }
              else
              {
                /* This is an error, but some fonts contain  */
                /* stupid Postscript code.  We simply ignore */
                /* an invalid `def' by clearing the stack.   */
#if 0
                FT_ERROR(( "Parse_T1_FontProgram: immediate expected\n" ));
                goto Syntax_Error;
#else
                parser->top = parser->stack;
#endif
              }
            }
            else
            {
              FT_ERROR(( "Parse_T1_FontProgram: not enough arguments\n" ));
              goto Stack_Underflow;
            }
          }
          break;

        case key_index:
          if ( top <= parser->stack )
          {
            FT_ERROR(( "Parse_T1_FontProgram: not enough arguments\n" ));
            goto Stack_Underflow;
          }

          /* simply ignore? */
          parser->top --;
          break;

        case key_put:
          /* Check that we have sufficient operands in stack */
          if ( top < parser->stack + 2 )
          {
            FT_ERROR(( "Parse_T1_FontProgram: not enough arguments\n" ));
            goto Stack_Underflow;
          }

          parser->top -= 2;
          parser->args = parser->top;

          switch ( dict_state )
          {
          case dict_encoding:
            error = Do_Put_Encoding( parser );
            if ( error )
              goto Exit;
            break;

          case dict_unknown_array:   /* ignore the `put' */
            break;

          default:
#if 0
            FT_ERROR(( "Parse_T1_FontProgram: invalid context\n" ));
            goto Syntax_Error;
#else
            /* invalid context; simply ignore the `put' and */
            /* clear the stack (stupid Postscript code)     */
            FT_TRACE4(( "Parse_T1_FontProgram: invalid context ignored.\n" ));
            parser->top = parser->stack;
#endif
          }
          break;

        case key_RD:
          /* Check that we have sufficient operands in stack */
          if ( top < parser->stack + 2 )
          {
            FT_ERROR(( "Parse_T1_FontProgram: not enough arguments\n" ));
            goto Stack_Underflow;
          }

          parser->top -= 2;
          parser->args = parser->top;
          switch ( dict_state )
          {
          case dict_subrs:
            error = Do_RD_Subrs( parser );
            if ( error )
              goto Exit;
            break;

          case dict_charstrings:
            error = Do_RD_Charstrings( parser );
            if ( error )
              goto Exit;
            break;

          default:
            FT_ERROR(( "Parse_T1_FontProgram: invalid context\n" ));
            goto Syntax_Error;
          }
          break;

        case key_end:
          /* Were we in a dictionary or in an array? */
          if ( dict_index <= 0 )
          {
            FT_ERROR(( "Parse_T1_FontProgram: no dictionary defined\n" ));
            goto Syntax_Error;
          }

          switch ( dict_state )
          {
            /* jump to the private dictionary if we are closing the */
            /* `/Font' dictionary                                   */
          case dict_font:
            goto Open_Private;

            /* exit the parser when closing the CharStrings dictionary */
          case dict_charstrings:
            return Finalize_Parsing( parser );

          default:
            /* Pop the current dictionary state and return to previous */
            /* one.  Consume the `def'.                                */

            /* Because some buggy fonts (BitStream) have incorrect     */
            /* syntax, we never escape from the private dictionary     */
            if ( dict_state != dict_private )
              parser->state_index--;

            /* many fonts use `NP' instead of `def' or `put', so */
            /* we simply ignore the next token                   */
#if 0
            error = Expect_Keyword2( parser, key_def, key_put );
            if ( error )
              goto Exit;
#else
            (void)Expect_Keyword2( parser, key_def, key_put );
#endif
          }
          break;

        case key_for:
          /* check that we have four arguments and simply */
          /* ignore them                                  */
          if ( top - parser->stack < 4 )
          {
            FT_ERROR(( "Parse_T1_FontProgram: not enough arguments\n" ));
            goto Stack_Underflow;
          }

          parser->top -= 4;
          break;

        case key_currentdict:
        Open_Private:
          parser->state_index    = 0;
          parser->state_stack[0] = dict_none2;
          error = Open_PrivateDict( parser->tokenizer );
          if ( error )
            goto Exit;
          break;

        case key_true:
        case key_false:
        case key_StandardEncoding:
        case key_ExpertEncoding:
          goto Push_Element;

        default:
          FT_ERROR(( "Parse_T1_FontProgram:" ));
          FT_ERROR(( " invalid keyword in context\n" ));
          error = T1_Err_Syntax_Error;
        }
        break;

        /* check for the presence of `/BlendAxisTypes' -- we cannot deal */
        /* with multiple master fonts, so we must return a correct error */
        /* code to allow another driver to load them                     */
      case tok_immediate:
        if ( token.kind2 == imm_BlendAxisTypes )
        {
          error = FT_Err_Unknown_File_Format;
          goto Exit;
        }
        /* fallthrough */

        /* A number was detected */
      case tok_string:
      case tok_program:
      case tok_array:
      case tok_hexarray:
      case tok_any:
      case tok_number:                        /* push number on stack */

      Push_Element:
        if ( top >= parser->limit )
        {
          error = T1_Err_Stack_Overflow;
          goto Exit;
        }
        else
          *parser->top++ = token;
        break;

        /* anything else is an error per se the spec, but we     */
        /* frequently encounter stupid postscript code in fonts, */
        /* so just ignore them                                   */
      default:
        error = T1_Err_Ok;  /* ignore token */
      }

      if ( error )
        return error;
    }

  Exit:
    return error;

  Syntax_Error:
    return T1_Err_Syntax_Error;

  Stack_Underflow:
    return T1_Err_Stack_Underflow;
  }


/* END */
