shithub: freetype+ttf2subf

ref: fbf3ce2890605f5fd78c357634f8bca08ebcfaf5
dir: /src/type1/t1load.c/

View raw version
#include <ftconfig.h>
#include <ftdebug.h>

#include <t1types.h>
#include <t1tokens.h>
#include <t1parse.h>

#include <stdio.h>

#undef  FT_COMPONENT
#define FT_COMPONENT  trace_t1load

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


/*************************************************************************/
/*                                                                       */
/* <Function> Init_T1_Parser                                             */
/*                                                                       */
/* <Description>                                                         */
/*    Initialise a given parser object to build a given T1_Face          */
/*                                                                       */
/* <Input>                                                               */
/*    parser  :: handle to the newly built parser object                 */
/*    face    :: handle to target T1 face object                         */
/*                                                                       */
  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.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  :: handle to source parser                                 */
/*                                                                       */
/* <Output>                                                              */
/*    token   :: the extracted token descriptor                          */
/*                                                                       */
/* <Return>                                                              */
/*    Error code. 0 means success                                        */
/*                                                                       */
  LOCAL_FUNC
  T1_Error  Next_T1_Token( T1_Parser*  parser,
                           T1_Token*   token )
  {
    T1_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:
          ;
      }

    /* Dump the token when requested. This feature is only available */
    /* in the 'error' and 'trace' debug levels..                     */
#if defined( FT_DEBUG_LEVEL_ERROR ) || defined( FT_DEBUG_LEVEL_TRACE )
    if ( parser->dump_tokens )
    {
      T1_String  temp_string[128];
      T1_Int     len;

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

    return T1_Err_Ok;
  }



  static
  T1_Error  Expect_Keyword( T1_Parser*    parser,
                            T1_TokenType  keyword )
  {
    T1_Token  token;
    T1_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(( "T1.Parse: keyword '%s' expected.\n",
               t1_keywords[ keyword - key_first_ ] ));
    }

  Exit:
    return error;
  }



  static
  T1_Error  Expect_Keyword2( T1_Parser*    parser,
                             T1_TokenType  keyword1,
                             T1_TokenType  keyword2 )
  {
    T1_Token  token;
    T1_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(( "T1.Parse: 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;
	T1_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, T1_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;
		  default                 : parser->encoding_type = t1_encoding_standard; break;
	  }
    }
	else
	{
	  FT_ERROR(( "T1.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' when in the Font dictionary          */
/*    Its purpose is to build the T1_Face attributes directly from        */
/*    the stream..                                                        */
/*                                                                        */
/* <Input>                                                                */
/*    parser :: handle to current parser.                                 */
/*                                                                        */
/* <Return>                                                               */
/*    Error code. 0 means success                                         */
/*                                                                        */
  static
  T1_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;
          T1_Error   error;
          T1_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 = (T1_Byte)CopyInteger( parser );
        break;

      case imm_FontType:
        type1->font_type = (T1_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->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' when in the FontInfo dictionary      */
/*    Its purpose is to build the T1_FontInfo structure directly from     */
/*    the stream..                                                        */
/*                                                                        */
/* <Input>                                                                */
/*    parser :: handle to current parser.                                 */
/*                                                                        */
/* <Return>                                                               */
/*    Error code. 0 means success                                         */
/*                                                                        */
  static
  T1_Error  Do_Def_FontInfo( T1_Parser*  parser )
  {
    T1_Token*  top   = parser->top;
    T1_Font*   info  = &parser->face->type1;

    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 = (T1_Short)CopyInteger( parser );
        break;

      case imm_UnderlineThickness:
        info->underline_thickness = (T1_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' when in the Private dictionary       */
/*    Its purpose is to build the T1_Private structure directly from      */
/*    the stream..                                                        */
/*                                                                        */
/* <Input>                                                                */
/*    parser :: handle to current parser.                                 */
/*                                                                        */
/* <Return>                                                               */
/*    Error code. 0 means success                                         */
/*                                                                        */
  static
  T1_Error  Do_Def_Private( T1_Parser*  parser )
  {
    T1_Token*   top   = parser->top;
    T1_Font*    priv  = &parser->face->type1;

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


      case imm_BlueValues:
        CopyArray( parser, &priv->num_blues,
                   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, 0x10000 );
        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, (T1_Short*)&priv->standard_width, 1 );
        break;


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


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


      case imm_StemSnapV:
        CopyArray( parser, &priv->num_snap_heights,
                   priv->stem_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     */
/*    ued for the "def" keyword when in the "encoding", "subrs",          */
/*    "othersubrs" and "charstrings" dictionary states..                  */
/*                                                                        */
/* <Input>                                                                */
/*    parser :: handle to current parser.                                 */
/*                                                                        */
/* <Return>                                                               */
/*    Error code. 0 means success                                         */
/*                                                                        */
  static
  T1_Error  Do_Def_Error( T1_Parser*  parser )
  {
    FT_ERROR(( "T1.Load : 'def' keyword encountered in bad dictionary/array\n" ));
    parser->error = T1_Err_Syntax_Error;
    return parser->error;
  }


  static
  T1_Error  Do_Def_Ignore( T1_Parser*  parser )
  {
    (void)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' when 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    */
/*                                                                        */
/* <Input>                                                                */
/*    parser :: handle to current parser.                                 */
/*                                                                        */
/* <Return>                                                               */
/*    Error code. 0 means success                                         */
/*                                                                        */
  static
  T1_Error  Do_Put_Encoding( T1_Parser*  parser )
  {
    T1_Error      error  = T1_Err_Ok;
    T1_Face       face   = parser->face;
    T1_Token*     top    = parser->top;
    T1_Encoding*  encode = &face->type1.encoding;
    T1_Int        index;

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

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

    /* record the immediate name */
    if ( top[1].kind != tok_immediate )
    {
      FT_TRACE4(( "T1.Parse.put: 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
    {
      T1_String  temp_name[128];
      T1_Token*  token = top+1;
      T1_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, (T1_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(( "T1.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 a 'RD' when in the Subrs dictionary          */
/*    It simply records the array of bytecodes/charstrings corresponding  */
/*    to the sub-routine..                                                */
/*                                                                        */
/* <Input>                                                                */
/*    parser :: handle to current parser.                                 */
/*                                                                        */
/* <Return>                                                               */
/*    Error code. 0 means success                                         */
/*                                                                        */
  static
  T1_Error  Do_RD_Subrs( T1_Parser*  parser )
  {
    T1_Error      error  = T1_Err_Ok;
    T1_Face       face   = parser->face;
    T1_Token*     top    = parser->top;
    T1_Tokenizer  tokzer = parser->tokenizer;
    T1_Int        index, count;

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

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

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

    /* decrypt charstring and skip them */
    {
      T1_Byte*  base = tokzer->base + tokzer->cursor;

      t1_decrypt( base, count, 4330 );
      tokzer->cursor += count;

      base  += face->type1.lenIV;
      count -= face->type1.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 a 'RD' when in the CharStrings dictionary    */
/*    It simply records the array of bytecodes/charstrings corresponding  */
/*    to the glyph program string.                                        */
/*                                                                        */
/* <Input>                                                                */
/*    parser :: handle to current parser.                                 */
/*                                                                        */
/* <Return>                                                               */
/*    Error code. 0 means success                                         */
/*                                                                        */
  static
  T1_Error  Do_RD_Charstrings( T1_Parser*  parser )
  {
    T1_Error      error = T1_Err_Ok;
    T1_Face       face  = parser->face;
    T1_Token*     top   = parser->top;
    T1_Tokenizer  tokzer = parser->tokenizer;
    T1_Int        index, count;

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

    /* check the count argument */
    if ( top[1].kind != tok_number )
    {
      FT_ERROR(( "T1.Parse.put: number expected\n" ));
      goto Syntax_Error;
    }
	parser->args++;
    count = (T1_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
    {
      T1_String  temp_name[128];
      T1_Token*  token = top;
      T1_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, (T1_Byte*)temp_name, len+1 );
      if (error) goto Exit;
    }

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

      t1_decrypt( base, count, 4330 );
      tokzer->cursor += count;  /* skip */

      base  += face->type1.lenIV;
      count -= face->type1.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
  T1_Error  Expect_Dict_Arguments( T1_Parser*    parser,
                                   T1_Int        num_args,
                                   T1_TokenType  immediate,
                                   T1_DictState  new_state,
                                   T1_Int       *count )
  {
    /* check that we have enough arguments in the stack, including */
    /* the 'dict' keyword..                                        */
    if ( parser->top - parser->stack < num_args )
    {
      FT_ERROR(( "T1.Parse.Dict : 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(( "T1.Parse.Dict : 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(( "T1.Parse.Dict : 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
  T1_Error  Expect_Array_Arguments( T1_Parser*  parser )
  {
    T1_Token*     top   = parser->top;
    T1_Error      error = T1_Err_Ok;
    T1_DictState  new_state;
    T1_Int        count;
    T1_Face       face   = parser->face;
    FT_Memory     memory = face->root.memory;

    /* Check arguments format */
    if ( top - parser->stack < 2 )
    {
      FT_ERROR(( "T1.Parse.array: 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(( "T1.Parse.array: first argument must be an immediate name\n" ));
      goto Syntax_Error;
    }

    if ( top[1].kind != tok_number )
    {
      FT_ERROR(( "T1.Parse.array: second argument must be a number\n" ));
      goto Syntax_Error;
    }
    count = (T1_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 indexes. The table of */
          /* character names is allocated through init_t1_recorder */
          if ( ALLOC_ARRAY( encode->char_index, count, T1_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
  T1_Error  Finalise_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;
	T1_Int     num_glyphs;
	T1_Int     n;
	T1_Error   error;

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

	/* allocate glyph names and charstrings arrays */
	if ( ALLOC_ARRAY( type1->glyph_names    , num_glyphs, T1_String* ) ||
		 ALLOC_ARRAY( type1->charstrings    , num_glyphs, T1_Byte* )   ||
	     ALLOC_ARRAY( type1->charstrings_len, num_glyphs, T1_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]     = (T1_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(( "T1.Parse.Finalise : PSNames module missing !!\n" ));
      return T1_Err_Unimplemented_Feature;
    }

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

	{
	  T1_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++ )
	  {
		T1_String** names;
		T1_Int      index;
		T1_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 = (T1_String**)parser->encoding_offsets;
		}
		encode->char_index[n] = 0;
		if (index)
		{
		  T1_String*  name;

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

		  if ( name )
		  {
            T1_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;
  }





  LOCAL_FUNC
  T1_Error  Parse_T1_FontProgram( T1_Parser*  parser )
  {
    T1_Error  error;
    T1_Font*  type1 = &parser->face->type1;

    for (;;)
    {
      T1_Token      token;
      T1_Token*     top;
      T1_DictState  dict_state;
      T1_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 was detected */
        case tok_keyword:
          switch (token.kind2)
          {
            case key_dict:

              switch (dict_state)
              {
                case dict_none:
                   /* All right, we're beggining 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;
                    T1_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, (T1_Byte*)".notdef", 8 );
                    parser->cur_name = 1;
                    /* XXXXX : DO SOMETHING HERE */
                  }
                  break;

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


            case key_array:
              /* Are we in an array yet ? Is 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(( "T1.Parse.array: 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, finalise 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;

                    parser->state_index--;
                  }
                  break;

                case dict_charstrings:
                case dict_othersubrs:
                case dict_unknown_array:
                  FT_ERROR(( "T1.Parser.def: 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 some */
                        /* stupid Postscript code. We simply ignore      */
                        /* an invalid 'def' by clearing the stack        */
#if 0
                        FT_ERROR(( "T1.Parse.def: immediate expected\n" ));
                        goto Syntax_Error;
#else
                        parser->top = parser->stack;
#endif
                      }
                    }
                    else
                    {
                      FT_ERROR(( "T1.Parse.def: not enough arguments\n" ));
                      goto Stack_Underflow;
                    }
                  }
              }
              break;



            case key_index:
              if ( top <= parser->stack )
              {
                FT_ERROR(( "T1.Parse.index: 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(( "T1.Parse.put: 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(( "T1.Parse.put: invalid context\n" ));
                  goto Syntax_Error;
#else
                  /* invalid context, simply ignore the put and */
                  /* clear the stack (stupid Postscript code..) */
                  FT_TRACE4(( "T1.Parse.put: 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(( "T1.Parse.RD: 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(( "T1.Parse.RD: 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(( "T1.Parse.end: no dictionary defined\n" ));
                goto Syntax_Error;
              }

              switch (dict_state)
              {
                /* Jump to the private dictionary if we're closing the */
                /* /Font dictionary..                                  */
                case dict_font:
                  goto Open_Private;

                /* Exit the parser when closing the CharStrings dictionary */
                case dict_charstrings:
                  return Finalise_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 a NP instead of def or put, so */
                  /* we simply ignore the nest 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(( "T1.Parse.for: 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(( "T1.Parser: invalid keyword in context\n" ));
              error = T1_Err_Syntax_Error;
          }
          break;

        /* A number was detected */
        case tok_string:
        case tok_program:
        case tok_immediate:
        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 encountre 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;
  }