text: /****************************************************************************
   *
   * t1decode.c
   *
   *   PostScript Type 1 decoding routines (body).
   *
   * Copyright (C) 2000-2019 by
   * David Turner, Robert Wilhelm, and Werner Lemberg.
   *
   * This file is part of the FreeType project, and may only be used,
   * modified, and distributed under the terms of the FreeType project
   * license, LICENSE.TXT.  By continuing to use, modify, or distribute
   * this file you indicate that you have read the license and
   * understand and accept it fully.
   *
   */
  
  
  #include <ft2build.h>
  #include FT_INTERNAL_CALC_H
  #include FT_INTERNAL_DEBUG_H
  #include FT_INTERNAL_POSTSCRIPT_HINTS_H
  #include FT_INTERNAL_HASH_H
  #include FT_OUTLINE_H
  
  #include "t1decode.h"
  #include "psobjs.h"
  
  #include "psauxerr.h"
  
  /* ensure proper sign extension */
  #define Fix2Int( f )  ( (FT_Int)(FT_Short)( (f) >> 16 ) )
  
    /**************************************************************************
     *
     * 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  t1decode
  
  
    typedef enum  T1_Operator_
    {
      op_none = 0,
      op_endchar,
      op_hsbw,
      op_seac,
      op_sbw,
      op_closepath,
      op_hlineto,
      op_hmoveto,
      op_hvcurveto,
      op_rlineto,
      op_rmoveto,
      op_rrcurveto,
      op_vhcurveto,
      op_vlineto,
      op_vmoveto,
      op_dotsection,
      op_hstem,
      op_hstem3,
      op_vstem,
      op_vstem3,
      op_div,
      op_callothersubr,
      op_callsubr,
      op_pop,
      op_return,
      op_setcurrentpoint,
      op_unknown15,
  
      op_max    /* never remove this one */
  
    } T1_Operator;
  
  
    static
    const FT_Int  t1_args_count[op_max] =
    {
      0, /* none */
      0, /* endchar */
      2, /* hsbw */
      5, /* seac */
      4, /* sbw */
      0, /* closepath */
      1, /* hlineto */
      1, /* hmoveto */
      4, /* hvcurveto */
      2, /* rlineto */
      2, /* rmoveto */
      6, /* rrcurveto */
      4, /* vhcurveto */
      1, /* vlineto */
      1, /* vmoveto */
      0, /* dotsection */
      2, /* hstem */
      6, /* hstem3 */
      2, /* vstem */
      6, /* vstem3 */
      2, /* div */
     -1, /* callothersubr */
      1, /* callsubr */
      0, /* pop */
      0, /* return */
      2, /* setcurrentpoint */
      2  /* opcode 15 (undocumented and obsolete) */
    };
  
  
    /**************************************************************************
     *
     * @Function:
     *   t1_lookup_glyph_by_stdcharcode_ps
     *
     * @Description:
     *   Looks up a given glyph by its StandardEncoding charcode.  Used to
     *   implement the SEAC Type 1 operator in the Adobe engine
     *
     * @Input:
     *   face ::
     *     The current face object.
     *
     *   charcode ::
     *     The character code to look for.
     *
     * @Return:
     *   A glyph index in the font face.  Returns -1 if the corresponding
     *   glyph wasn't found.
     */
    FT_LOCAL_DEF( FT_Int )
    t1_lookup_glyph_by_stdcharcode_ps( PS_Decoder*  decoder,
                                       FT_Int       charcode )
    {
      FT_UInt             n;
      const FT_String*    glyph_name;
      FT_Service_PsCMaps  psnames = decoder->psnames;
  
  
      /* check range of standard char code */
      if ( charcode < 0 || charcode > 255 )
        return -1;
  
      glyph_name = psnames->adobe_std_strings(
                     psnames->adobe_std_encoding[charcode]);
  
      for ( n = 0; n < decoder->num_glyphs; n++ )
      {
        FT_String*  name = (FT_String*)decoder->glyph_names[n];
  
  
        if ( name                               &&
             name[0] == glyph_name[0]           &&
             ft_strcmp( name, glyph_name ) == 0 )
          return (FT_Int)n;
      }
  
      return -1;
    }
  
  
  #ifdef T1_CONFIG_OPTION_OLD_ENGINE
  
    /**************************************************************************
     *
     * @Function:
     *   t1_lookup_glyph_by_stdcharcode
     *
     * @Description:
     *   Looks up a given glyph by its StandardEncoding charcode.  Used to
     *   implement the SEAC Type 1 operator.
     *
     * @Input:
     *   face ::
     *     The current face object.
     *
     *   charcode ::
     *     The character code to look for.
     *
     * @Return:
     *   A glyph index in the font face.  Returns -1 if the corresponding
     *   glyph wasn't found.
     */
    static FT_Int
    t1_lookup_glyph_by_stdcharcode( T1_Decoder  decoder,
                                    FT_Int      charcode )
    {
      FT_UInt             n;
      const FT_String*    glyph_name;
      FT_Service_PsCMaps  psnames = decoder->psnames;
  
  
      /* check range of standard char code */
      if ( charcode < 0 || charcode > 255 )
        return -1;
  
      glyph_name = psnames->adobe_std_strings(
                     psnames->adobe_std_encoding[charcode]);
  
      for ( n = 0; n < decoder->num_glyphs; n++ )
      {
        FT_String*  name = (FT_String*)decoder->glyph_names[n];
  
  
        if ( name                               &&
             name[0] == glyph_name[0]           &&
             ft_strcmp( name, glyph_name ) == 0 )
          return (FT_Int)n;
      }
  
      return -1;
    }
  
  
    /* parse a single Type 1 glyph */
    FT_LOCAL_DEF( FT_Error )
    t1_decoder_parse_glyph( T1_Decoder  decoder,
                            FT_UInt     glyph )
    {
      return decoder->parse_callback( decoder, glyph );
    }
  
  
    /**************************************************************************
     *
     * @Function:
     *   t1operator_seac
     *
     * @Description:
     *   Implements the `seac' Type 1 operator for a Type 1 decoder.
     *
     * @Input:
     *   decoder ::
     *     The current CID decoder.
     *
     *   asb ::
     *     The accent's side bearing.
     *
     *   adx ::
     *     The horizontal offset of the accent.
     *
     *   ady ::
     *     The vertical offset of the accent.
     *
     *   bchar ::
     *     The base character's StandardEncoding charcode.
     *
     *   achar ::
     *     The accent character's StandardEncoding charcode.
     *
     * @Return:
     *   FreeType error code.  0 means success.
     */
    static FT_Error
    t1operator_seac( T1_Decoder  decoder,
                     FT_Pos      asb,
                     FT_Pos      adx,
                     FT_Pos      ady,
                     FT_Int      bchar,
                     FT_Int      achar )
    {
      FT_Error     error;
      FT_Int       bchar_index, achar_index;
  #if 0
      FT_Int       n_base_points;
      FT_Outline*  base = decoder->builder.base;
  #endif
      FT_Vector    left_bearing, advance;
  
  #ifdef FT_CONFIG_OPTION_INCREMENTAL
      T1_Face      face  = (T1_Face)decoder->builder.face;
  #endif
  
  
      if ( decoder->seac )
      {
        FT_ERROR(( "t1operator_seac: invalid nested seac\n" ));
        return FT_THROW( Syntax_Error );
      }
  
      if ( decoder->builder.metrics_only )
      {
        FT_ERROR(( "t1operator_seac: unexpected seac\n" ));
        return FT_THROW( Syntax_Error );
      }
  
      /* seac weirdness */
      adx += decoder->builder.left_bearing.x;
  
      /* `glyph_names' is set to 0 for CID fonts which do not */
      /* include an encoding.  How can we deal with these?    */
  #ifdef FT_CONFIG_OPTION_INCREMENTAL
      if ( decoder->glyph_names == 0                   &&
           !face->root.internal->incremental_interface )
  #else
      if ( decoder->glyph_names == 0 )
  #endif /* FT_CONFIG_OPTION_INCREMENTAL */
      {
        FT_ERROR(( "t1operator_seac:"
                   " glyph names table not available in this font\n" ));
        return FT_THROW( Syntax_Error );
      }
  
  #ifdef FT_CONFIG_OPTION_INCREMENTAL
      if ( face->root.internal->incremental_interface )
      {
        /* the caller must handle the font encoding also */
        bchar_index = bchar;
        achar_index = achar;
      }
      else
  #endif
      {
        bchar_index = t1_lookup_glyph_by_stdcharcode( decoder, bchar );
        achar_index = t1_lookup_glyph_by_stdcharcode( decoder, achar );
      }
  
      if ( bchar_index < 0 || achar_index < 0 )
      {
        FT_ERROR(( "t1operator_seac:"
                   " invalid seac character code arguments\n" ));
        return FT_THROW( Syntax_Error );
      }
  
      /* if we are trying to load a composite glyph, do not load the */
      /* accent character and return the array of subglyphs.         */
      if ( decoder->builder.no_recurse )
      {
        FT_GlyphSlot    glyph  = (FT_GlyphSlot)decoder->builder.glyph;
        FT_GlyphLoader  loader = glyph->internal->loader;
        FT_SubGlyph     subg;
  
  
        /* reallocate subglyph array if necessary */
        error = FT_GlyphLoader_CheckSubGlyphs( loader, 2 );
        if ( error )
          goto Exit;
  
        subg = loader->current.subglyphs;
  
        /* subglyph 0 = base character */
        subg->index = bchar_index;
        subg->flags = FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES |
                      FT_SUBGLYPH_FLAG_USE_MY_METRICS;
        subg->arg1  = 0;
        subg->arg2  = 0;
        subg++;
  
        /* subglyph 1 = accent character */
        subg->index = achar_index;
        subg->flags = FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES;
        subg->arg1  = (FT_Int)FIXED_TO_INT( adx - asb );
        subg->arg2  = (FT_Int)FIXED_TO_INT( ady );
  
        /* set up remaining glyph fields */
        glyph->num_subglyphs = 2;
        glyph->subglyphs     = loader->base.subglyphs;
        glyph->format        = FT_GLYPH_FORMAT_COMPOSITE;
  
        loader->current.num_subglyphs = 2;
        goto Exit;
      }
  
      /* First load `bchar' in builder */
      /* now load the unscaled outline */
  
      FT_GlyphLoader_Prepare( decoder->builder.loader );  /* prepare loader */
  
      /* save the left bearing and width of the SEAC   */
      /* glyph as they will be erased by the next load */
  
      left_bearing = decoder->builder.left_bearing;
      advance      = decoder->builder.advance;
  
      /* the seac operator must not be nested */
      decoder->seac = TRUE;
      error = t1_decoder_parse_glyph( decoder, (FT_UInt)bchar_index );
      decoder->seac = FALSE;
      if ( error )
        goto Exit;
  
      /* If the SEAC glyph doesn't have a (H)SBW of its */
      /* own use the values from the base glyph.        */
  
      if ( decoder->builder.parse_state != T1_Parse_Have_Width )
      {
        left_bearing = decoder->builder.left_bearing;
        advance      = decoder->builder.advance;
      }
  
      decoder->builder.left_bearing.x = 0;
      decoder->builder.left_bearing.y = 0;
  
      decoder->builder.pos_x = adx - asb;
      decoder->builder.pos_y = ady;
  
      /* Now load `achar' on top of */
      /* the base outline           */
  
      /* the seac operator must not be nested */
      decoder->seac = TRUE;
      error = t1_decoder_parse_glyph( decoder, (FT_UInt)achar_index );
      decoder->seac = FALSE;
      if ( error )
        goto Exit;
  
      /* restore the left side bearing and advance width   */
      /* of the SEAC glyph or base character (saved above) */
  
      decoder->builder.left_bearing = left_bearing;
      decoder->builder.advance      = advance;
  
      decoder->builder.pos_x = 0;
      decoder->builder.pos_y = 0;
  
    Exit:
      return error;
    }
  
  
    /**************************************************************************
     *
     * @Function:
     *   t1_decoder_parse_charstrings
     *
     * @Description:
     *   Parses a given Type 1 charstrings program.
     *
     * @Input:
     *   decoder ::
     *     The current Type 1 decoder.
     *
     *   charstring_base ::
     *     The base address of the charstring stream.
     *
     *   charstring_len ::
     *     The length in bytes of the charstring stream.
     *
     * @Return:
     *   FreeType error code.  0 means success.
     */
    FT_LOCAL_DEF( FT_Error )
    t1_decoder_parse_charstrings( T1_Decoder  decoder,
                                  FT_Byte*    charstring_base,
                                  FT_UInt     charstring_len )
    {
      FT_Error         error;
      T1_Decoder_Zone  zone;
      FT_Byte*         ip;
      FT_Byte*         limit;
      T1_Builder       builder = &decoder->builder;
      FT_Pos           x, y, orig_x, orig_y;
      FT_Int           known_othersubr_result_cnt   = 0;
      FT_Int           unknown_othersubr_result_cnt = 0;
      FT_Bool          large_int;
      FT_Fixed         seed;
  
      T1_Hints_Funcs   hinter;
  
  #ifdef FT_DEBUG_LEVEL_TRACE
      FT_Bool          bol = TRUE;
  #endif
  
  
      /* compute random seed from stack address of parameter */
      seed = (FT_Fixed)( ( (FT_Offset)(char*)&seed            ^
                           (FT_Offset)(char*)&decoder         ^
                           (FT_Offset)(char*)&charstring_base ) &
                           FT_ULONG_MAX                         );
      seed = ( seed ^ ( seed >> 10 ) ^ ( seed >> 20 ) ) & 0xFFFFL;
      if ( seed == 0 )
        seed = 0x7384;
  
      /* First of all, initialize the decoder */
      decoder->top  = decoder->stack;
      decoder->zone = decoder->zones;
      zone          = decoder->zones;
  
      builder->parse_state = T1_Parse_Start;
  
      hinter = (T1_Hints_Funcs)builder->hints_funcs;
  
      /* a font that reads BuildCharArray without setting */
      /* its values first is buggy, but ...               */
      FT_ASSERT( ( decoder->len_buildchar == 0 ) ==
                 ( decoder->buildchar == NULL )  );
  
      if ( decoder->buildchar && decoder->len_buildchar > 0 )
        FT_ARRAY_ZERO( decoder->buildchar, decoder->len_buildchar );
  
      zone->base           = charstring_base;
      limit = zone->limit  = charstring_base + charstring_len;
      ip    = zone->cursor = zone->base;
  
      error = FT_Err_Ok;
  
      x = orig_x = builder->pos_x;
      y = orig_y = builder->pos_y;
  
      /* begin hints recording session, if any */
      if ( hinter )
        hinter->open( hinter->hints );
  
      large_int = FALSE;
  
      /* now, execute loop */
      while ( ip < limit )
      {
        FT_Long*     top   = decoder->top;
        T1_Operator  op    = op_none;
        FT_Int32     value = 0;
  
  
        FT_ASSERT( known_othersubr_result_cnt == 0   ||
                   unknown_othersubr_result_cnt == 0 );
  
  #ifdef FT_DEBUG_LEVEL_TRACE
        if ( bol )
        {
          FT_TRACE5(( " (%d)", decoder->top - decoder->stack ));
          bol = FALSE;
        }
  #endif
  
        /**********************************************************************
         *
         * Decode operator or operand
         *
         */
  
        /* first of all, decompress operator or value */
        switch ( *ip++ )
        {
        case 1:
          op = op_hstem;
          break;
  
        case 3:
          op = op_vstem;
          break;
        case 4:
          op = op_vmoveto;
          break;
        case 5:
          op = op_rlineto;
          break;
        case 6:
          op = op_hlineto;
          break;
        case 7:
          op = op_vlineto;
          break;
        case 8:
          op = op_rrcurveto;
          break;
        case 9:
          op = op_closepath;
          break;
        case 10:
          op = op_callsubr;
          break;
        case 11:
          op = op_return;
          break;
  
        case 13:
          op = op_hsbw;
          break;
        case 14:
          op = op_endchar;
          break;
  
        case 15:          /* undocumented, obsolete operator */
          op = op_unknown15;
          break;
  
        case 21:
          op = op_rmoveto;
          break;
        case 22:
          op = op_hmoveto;
          break;
  
        case 30:
          op = op_vhcurveto;
          break;
        case 31:
          op = op_hvcurveto;
          break;
  
        case 12:
          if ( ip >= limit )
          {
            FT_ERROR(( "t1_decoder_parse_charstrings:"
                       " invalid escape (12+EOF)\n" ));
            goto Syntax_Error;
          }
  
          switch ( *ip++ )
          {
          case 0:
            op = op_dotsection;
            break;
          case 1:
            op = op_vstem3;
            break;
          case 2:
            op = op_hstem3;
            break;
          case 6:
            op = op_seac;
            break;
          case 7:
            op = op_sbw;
            break;
          case 12:
            op = op_div;
            break;
          case 16:
            op = op_callothersubr;
            break;
          case 17:
            op = op_pop;
            break;
          case 33:
            op = op_setcurrentpoint;
            break;
  
          default:
            FT_ERROR(( "t1_decoder_parse_charstrings:"
                       " invalid escape (12+%d)\n",
                       ip[-1] ));
            goto Syntax_Error;
          }
          break;
  
        case 255:    /* four bytes integer */
          if ( ip + 4 > limit )
          {
            FT_ERROR(( "t1_decoder_parse_charstrings:"
                       " unexpected EOF in integer\n" ));
            goto Syntax_Error;
          }
  
          value = (FT_Int32)( ( (FT_UInt32)ip[0] << 24 ) |
                              ( (FT_UInt32)ip[1] << 16 ) |
                              ( (FT_UInt32)ip[2] << 8  ) |
                                (FT_UInt32)ip[3]         );
          ip += 4;
  
          /* According to the specification, values > 32000 or < -32000 must */
          /* be followed by a `div' operator to make the result be in the    */
          /* range [-32000;32000].  We expect that the second argument of    */
          /* `div' is not a large number.  Additionally, we don't handle     */
          /* stuff like `<large1> <large2> <num> div <num> div' or           */
          /* <large1> <large2> <num> div div'.  This is probably not allowed */
          /* anyway.                                                         */
          if ( value > 32000 || value < -32000 )
          {
            if ( large_int )
            {
              FT_ERROR(( "t1_decoder_parse_charstrings:"
                         " no `div' after large integer\n" ));
            }
            else
              large_int = TRUE;
          }
          else
          {
            if ( !large_int )
              value = (FT_Int32)( (FT_UInt32)value << 16 );
          }
  
          break;
  
        default:
          if ( ip[-1] >= 32 )
          {
            if ( ip[-1] < 247 )
              value = (FT_Int32)ip[-1] - 139;
            else
            {
              if ( ++ip > limit )
              {
                FT_ERROR(( "t1_decoder_parse_charstrings:"
                           " unexpected EOF in integer\n" ));
                goto Syntax_Error;
              }
  
              if ( ip[-2] < 251 )
                value =    ( ( ip[-2] - 247 ) * 256 ) + ip[-1] + 108;
              else
                value = -( ( ( ip[-2] - 251 ) * 256 ) + ip[-1] + 108 );
            }
  
            if ( !large_int )
              value = (FT_Int32)( (FT_UInt32)value << 16 );
          }
          else
          {
            FT_ERROR(( "t1_decoder_parse_charstrings:"
                       " invalid byte (%d)\n", ip[-1] ));
            goto Syntax_Error;
          }
        }
  
        if ( unknown_othersubr_result_cnt > 0 )
        {
          switch ( op )
          {
          case op_callsubr:
          case op_return:
          case op_none:
          case op_pop:
            break;
  
          default:
            /* all operands have been transferred by previous pops */
            unknown_othersubr_result_cnt = 0;
            break;
          }
        }
  
        if ( large_int && !( op == op_none || op == op_div ) )
        {
          FT_ERROR(( "t1_decoder_parse_charstrings:"
                     " no `div' after large integer\n" ));
  
          large_int = FALSE;
        }
  
        /**********************************************************************
         *
         * Push value on stack, or process operator
         *
         */
        if ( op == op_none )
        {
          if ( top - decoder->stack >= T1_MAX_CHARSTRINGS_OPERANDS )
          {
            FT_ERROR(( "t1_decoder_parse_charstrings: stack overflow\n" ));
            goto Syntax_Error;
          }
  
  #ifdef FT_DEBUG_LEVEL_TRACE
          if ( large_int )
            FT_TRACE4(( " %d", value ));
          else
            FT_TRACE4(( " %d", value / 65536 ));
  #endif
  
          *top++       = value;
          decoder->top = top;
        }
        else if ( op == op_callothersubr )  /* callothersubr */
        {
          FT_Int  subr_no;
          FT_Int  arg_cnt;
  
  
  #ifdef FT_DEBUG_LEVEL_TRACE
          FT_TRACE4(( " callothersubr\n" ));
          bol = TRUE;
  #endif
  
          if ( top - decoder->stack < 2 )
            goto Stack_Underflow;
  
          top -= 2;
  
          subr_no = Fix2Int( top[1] );
          arg_cnt = Fix2Int( top[0] );
  
          /************************************************************
           *
           * remove all operands to callothersubr from the stack
           *
           * for handled othersubrs, where we know the number of
           * arguments, we increase the stack by the value of
           * known_othersubr_result_cnt
           *
           * for unhandled othersubrs the following pops adjust the
           * stack pointer as necessary
           */
  
          if ( arg_cnt > top - decoder->stack )
            goto Stack_Underflow;
  
          top -= arg_cnt;
  
          known_othersubr_result_cnt   = 0;
          unknown_othersubr_result_cnt = 0;
  
          /* XXX TODO: The checks to `arg_count == <whatever>'       */
          /* might not be correct; an othersubr expects a certain    */
          /* number of operands on the PostScript stack (as opposed  */
          /* to the T1 stack) but it doesn't have to put them there  */
          /* by itself; previous othersubrs might have left the      */
          /* operands there if they were not followed by an          */
          /* appropriate number of pops                              */
          /*                                                         */
          /* On the other hand, Adobe Reader 7.0.8 for Linux doesn't */
          /* accept a font that contains charstrings like            */
          /*                                                         */
          /*     100 200 2 20 callothersubr                          */
          /*     300 1 20 callothersubr pop                          */
          /*                                                         */
          /* Perhaps this is the reason why BuildCharArray exists.   */
  
          switch ( subr_no )
          {
          case 0:                     /* end flex feature */
            if ( arg_cnt != 3 )
              goto Unexpected_OtherSubr;
  
            if ( !decoder->flex_state           ||
                 decoder->num_flex_vectors != 7 )
            {
              FT_ERROR(( "t1_decoder_parse_charstrings:"
                         " unexpected flex end\n" ));
              goto Syntax_Error;
            }
  
            /* the two `results' are popped by the following setcurrentpoint */
            top[0] = x;
            top[1] = y;
            known_othersubr_result_cnt = 2;
            break;
  
          case 1:                     /* start flex feature */
            if ( arg_cnt != 0 )
              goto Unexpected_OtherSubr;
  
            if ( FT_SET_ERROR( t1_builder_start_point( builder, x, y ) ) ||
                 FT_SET_ERROR( t1_builder_check_points( builder, 6 ) )   )
              goto Fail;
  
            decoder->flex_state        = 1;
            decoder->num_flex_vectors  = 0;
            break;
  
          case 2:                     /* add flex vectors */
            {
              FT_Int  idx;
  
  
              if ( arg_cnt != 0 )
                goto Unexpected_OtherSubr;
  
              if ( !decoder->flex_state )
              {
                FT_ERROR(( "t1_decoder_parse_charstrings:"
                           " missing flex start\n" ));
                goto Syntax_Error;
              }
  
              /* note that we should not add a point for index 0; */
              /* this will move our current position to the flex  */
              /* point without adding any point to the outline    */
              idx = decoder->num_flex_vectors++;
              if ( idx > 0 && idx < 7 )
              {
                /* in malformed fonts it is possible to have other */
                /* opcodes in the middle of a flex (which don't    */
                /* increase `num_flex_vectors'); we thus have to   */
                /* check whether we can add a point                */
                if ( FT_SET_ERROR( t1_builder_check_points( builder, 1 ) ) )
                  goto Syntax_Error;
  
                t1_builder_add_point( builder,
                                      x,
                                      y,
                                      (FT_Byte)( idx == 3 || idx == 6 ) );
              }
            }
            break;
  
          case 3:                     /* change hints */
            if ( arg_cnt != 1 )
              goto Unexpected_OtherSubr;
  
            known_othersubr_result_cnt = 1;
  
            if ( hinter )
              hinter->reset( hinter->hints,
                             (FT_UInt)builder->current->n_points );
            break;
  
          case 12:
          case 13:
            /* counter control hints, clear stack */
            top = decoder->stack;
            break;
  
          case 14:
          case 15:
          case 16:
          case 17:
          case 18:                    /* multiple masters */
            {
              PS_Blend  blend = decoder->blend;
              FT_UInt   num_points, nn, mm;
              FT_Long*  delta;
              FT_Long*  values;
  
  
              if ( !blend )
              {
                FT_ERROR(( "t1_decoder_parse_charstrings:"
                           " unexpected multiple masters operator\n" ));
                goto Syntax_Error;
              }
  
              num_points = (FT_UInt)subr_no - 13 + ( subr_no == 18 );
              if ( arg_cnt != (FT_Int)( num_points * blend->num_designs ) )
              {
                FT_ERROR(( "t1_decoder_parse_charstrings:"
                           " incorrect number of multiple masters arguments\n" ));
                goto Syntax_Error;
              }
  
              /* We want to compute                                    */
              /*                                                       */
              /*   a0*w0 + a1*w1 + ... + ak*wk                         */
              /*                                                       */
              /* but we only have a0, a1-a0, a2-a0, ..., ak-a0.        */
              /*                                                       */
              /* However, given that w0 + w1 + ... + wk == 1, we can   */
              /* rewrite it easily as                                  */
              /*                                                       */
              /*   a0 + (a1-a0)*w1 + (a2-a0)*w2 + ... + (ak-a0)*wk     */
              /*                                                       */
              /* where k == num_designs-1.                             */
              /*                                                       */
              /* I guess that's why it's written in this `compact'     */
              /* form.                                                 */
              /*                                                       */
              delta  = top + num_points;
              values = top;
              for ( nn = 0; nn < num_points; nn++ )
              {
                FT_Long  tmp = values[0];
  
  
                for ( mm = 1; mm < blend->num_designs; mm++ )
                  tmp = ADD_LONG( tmp,
                                  FT_MulFix( *delta++,
                                             blend->weight_vector[mm] ) );
  
                *values++ = tmp;
              }
  
              known_othersubr_result_cnt = (FT_Int)num_points;
              break;
            }
  
          case 19:
            /* <idx> 1 19 callothersubr                             */
            /* => replace elements starting from index cvi( <idx> ) */
            /*    of BuildCharArray with WeightVector               */
            {
              FT_Int    idx;
              PS_Blend  blend = decoder->blend;
  
  
              if ( arg_cnt != 1 || !blend )
                goto Unexpected_OtherSubr;
  
              idx = Fix2Int( top[0] );
  
              if ( idx < 0                                                    ||
                   (FT_UInt)idx + blend->num_designs > decoder->len_buildchar )
                goto Unexpected_OtherSubr;
  
              ft_memcpy( &decoder->buildchar[idx],
                         blend->weight_vector,
                         blend->num_designs *
                           sizeof ( blend->weight_vector[0] ) );
            }
            break;
  
          case 20:
            /* <arg1> <arg2> 2 20 callothersubr pop   */
            /* ==> push <arg1> + <arg2> onto T1 stack */
            if ( arg_cnt != 2 )
              goto Unexpected_OtherSubr;
  
            top[0] = ADD_LONG( top[0], top[1] );
  
            known_othersubr_result_cnt = 1;
            break;
  
          case 21:
            /* <arg1> <arg2> 2 21 callothersubr pop   */
            /* ==> push <arg1> - <arg2> onto T1 stack */
            if ( arg_cnt != 2 )
              goto Unexpected_OtherSubr;
  
            top[0] = SUB_LONG( top[0], top[1] );
  
            known_othersubr_result_cnt = 1;
            break;
  
          case 22:
            /* <arg1> <arg2> 2 22 callothersubr pop   */
            /* ==> push <arg1> * <arg2> onto T1 stack */
            if ( arg_cnt != 2 )
              goto Unexpected_OtherSubr;
  
            top[0] = FT_MulFix( top[0], top[1] );
  
            known_othersubr_result_cnt = 1;
            break;
  
          case 23:
            /* <arg1> <arg2> 2 23 callothersubr pop   */
            /* ==> push <arg1> / <arg2> onto T1 stack */
            if ( arg_cnt != 2 || top[1] == 0 )
              goto Unexpected_OtherSubr;
  
            top[0] = FT_DivFix( top[0], top[1] );
  
            known_othersubr_result_cnt = 1;
            break;
  
          case 24:
            /* <val> <idx> 2 24 callothersubr               */
            /* ==> set BuildCharArray[cvi( <idx> )] = <val> */
            {
              FT_Int    idx;
              PS_Blend  blend = decoder->blend;
  
  
              if ( arg_cnt != 2 || !blend )
                goto Unexpected_OtherSubr;
  
              idx = Fix2Int( top[1] );
  
              if ( idx < 0 || (FT_UInt) idx >= decoder->len_buildchar )
                goto Unexpected_OtherSubr;
  
              decoder->buildchar[idx] = top[0];
            }
            break;
  
          case 25:
            /* <idx> 1 25 callothersubr pop        */
            /* ==> push BuildCharArray[cvi( idx )] */
            /*     onto T1 stack                   */
            {
              FT_Int    idx;
              PS_Blend  blend = decoder->blend;
  
  
              if ( arg_cnt != 1 || !blend )
                goto Unexpected_OtherSubr;
  
              idx = Fix2Int( top[0] );
  
              if ( idx < 0 || (FT_UInt) idx >= decoder->len_buildchar )
                goto Unexpected_OtherSubr;
  
              top[0] = decoder->buildchar[idx];
            }
  
            known_othersubr_result_cnt = 1;
            break;
  
  #if 0
          case 26:
            /* <val> mark <idx> ==> set BuildCharArray[cvi( <idx> )] = <val>, */
            /*                      leave mark on T1 stack                    */
            /* <val> <idx>      ==> set BuildCharArray[cvi( <idx> )] = <val>  */
            XXX which routine has left its mark on the (PostScript) stack?;
            break;
  #endif
  
          case 27:
            /* <res1> <res2> <val1> <val2> 4 27 callothersubr pop */
            /* ==> push <res1> onto T1 stack if <val1> <= <val2>, */
            /*     otherwise push <res2>                          */
            if ( arg_cnt != 4 )
              goto Unexpected_OtherSubr;
  
            if ( top[2] > top[3] )
              top[0] = top[1];
  
            known_othersubr_result_cnt = 1;
            break;
  
          case 28:
            /* 0 28 callothersubr pop                               */
            /* => push random value from interval [0, 1) onto stack */
            if ( arg_cnt != 0 )
              goto Unexpected_OtherSubr;
  
            {
              FT_Fixed  Rand;
  
  
              Rand = seed;
              if ( Rand >= 0x8000L )
                Rand++;
  
              top[0] = Rand;
  
              seed = FT_MulFix( seed, 0x10000L - seed );
              if ( seed == 0 )
                seed += 0x2873;
            }
  
            known_othersubr_result_cnt = 1;
            break;
  
          default:
            if ( arg_cnt >= 0 && subr_no >= 0 )
            {
              FT_ERROR(( "t1_decoder_parse_charstrings:"
                         " unknown othersubr [%d %d], wish me luck\n",
                         arg_cnt, subr_no ));
              unknown_othersubr_result_cnt = arg_cnt;
              break;
            }
            /* fall through */
  
          Unexpected_OtherSubr:
            FT_ERROR(( "t1_decoder_parse_charstrings:"
                       " invalid othersubr [%d %d]\n", arg_cnt, subr_no ));
            goto Syntax_Error;
          }
  
          top += known_othersubr_result_cnt;
  
          decoder->top = top;
        }
        else  /* general operator */
        {
          FT_Int  num_args = t1_args_count[op];
  
  
          FT_ASSERT( num_args >= 0 );
  
          if ( top - decoder->stack < num_args )
            goto Stack_Underflow;
  
          /* XXX Operators usually take their operands from the        */
          /*     bottom of the stack, i.e., the operands are           */
          /*     decoder->stack[0], ..., decoder->stack[num_args - 1]; */
          /*     only div, callsubr, and callothersubr are different.  */
          /*     In practice it doesn't matter (?).                    */
  
  #ifdef FT_DEBUG_LEVEL_TRACE
  
          switch ( op )
          {
          case op_callsubr:
          case op_div:
          case op_callothersubr:
          case op_pop:
          case op_return:
            break;
  
          default:
            if ( top - decoder->stack != num_args )
              FT_TRACE0(( "t1_decoder_parse_charstrings:"
                          " too much operands on the stack"
                          " (seen %d, expected %d)\n",
                          top - decoder->stack, num_args ));
              break;
          }
  
  #endif /* FT_DEBUG_LEVEL_TRACE */
  
          top -= num_args;
  
          switch ( op )
          {
          case op_endchar:
            FT_TRACE4(( " endchar\n" ));
  
            t1_builder_close_contour( builder );
  
            /* close hints recording session */
            if ( hinter )
            {
              if ( hinter->close( hinter->hints,
                                  (FT_UInt)builder->current->n_points ) )
                goto Syntax_Error;
  
              /* apply hints to the loaded glyph outline now */
              error = hinter->apply( hinter->hints,
                                     builder->current,
                                     (PSH_Globals)builder->hints_globals,
                                     decoder->hint_mode );
              if ( error )
                goto Fail;
            }
  
            /* add current outline to the glyph slot */
            FT_GlyphLoader_Add( builder->loader );
  
            /* the compiler should optimize away this empty loop but ... */
  
  #ifdef FT_DEBUG_LEVEL_TRACE
  
            if ( decoder->len_buildchar > 0 )
            {
              FT_UInt  i;
  
  
              FT_TRACE4(( "BuildCharArray = [ " ));
  
              for ( i = 0; i < decoder->len_buildchar; i++ )
                FT_TRACE4(( "%d ", decoder->buildchar[i] ));
  
              FT_TRACE4(( "]\n" ));
            }
  
  #endif /* FT_DEBUG_LEVEL_TRACE */
  
            FT_TRACE4(( "\n" ));
  
            /* return now! */
            return FT_Err_Ok;
  
          case op_hsbw:
            FT_TRACE4(( " hsbw" ));
  
            builder->parse_state = T1_Parse_Have_Width;
  
            builder->left_bearing.x = ADD_LONG( builder->left_bearing.x,
                                                top[0] );
  
            builder->advance.x = top[1];
            builder->advance.y = 0;
  
            orig_x = x = ADD_LONG( builder->pos_x, top[0] );
            orig_y = y = builder->pos_y;
  
            FT_UNUSED( orig_y );
  
            /* the `metrics_only' indicates that we only want to compute */
            /* the glyph's metrics (lsb + advance width), not load the   */
            /* rest of it; so exit immediately                           */
            if ( builder->metrics_only )
            {
              FT_TRACE4(( "\n" ));
              return FT_Err_Ok;
            }
  
            break;
  
          case op_seac:
            return t1operator_seac( decoder,
                                    top[0],
                                    top[1],
                                    top[2],
                                    Fix2Int( top[3] ),
                                    Fix2Int( top[4] ) );
  
          case op_sbw:
            FT_TRACE4(( " sbw" ));
  
            builder->parse_state = T1_Parse_Have_Width;
  
            builder->left_bearing.x = ADD_LONG( builder->left_bearing.x,
                                                top[0] );
            builder->left_bearing.y = ADD_LONG( builder->left_bearing.y,
                                                top[1] );
  
            builder->advance.x = top[2];
            builder->advance.y = top[3];
  
            x = ADD_LONG( builder->pos_x, top[0] );
            y = ADD_LONG( builder->pos_y, top[1] );
  
            /* the `metrics_only' indicates that we only want to compute */
            /* the glyph's metrics (lsb + advance width), not load the   */
            /* rest of it; so exit immediately                           */
            if ( builder->metrics_only )
            {
              FT_TRACE4(( "\n" ));
              return FT_Err_Ok;
            }
  
            break;
  
          case op_closepath:
            FT_TRACE4(( " closepath" ));
  
            /* if there is no path, `closepath' is a no-op */
            if ( builder->parse_state == T1_Parse_Have_Path   ||
                 builder->parse_state == T1_Parse_Have_Moveto )
              t1_builder_close_contour( builder );
  
            builder->parse_state = T1_Parse_Have_Width;
            break;
  
          case op_hlineto:
            FT_TRACE4(( " hlineto" ));
  
            if ( FT_SET_ERROR( t1_builder_start_point( builder, x, y ) ) )
              goto Fail;
  
            x = ADD_LONG( x, top[0] );
            goto Add_Line;
  
          case op_hmoveto:
            FT_TRACE4(( " hmoveto" ));
  
            x = ADD_LONG( x, top[0] );
  
            if ( !decoder->flex_state )
            {
              if ( builder->parse_state == T1_Parse_Start )
                goto Syntax_Error;
              builder->parse_state = T1_Parse_Have_Moveto;
            }
            break;
  
          case op_hvcurveto:
            FT_TRACE4(( " hvcurveto" ));
  
            if ( FT_SET_ERROR( t1_builder_start_point( builder, x, y ) ) ||
                 FT_SET_ERROR( t1_builder_check_points( builder, 3 ) )   )
              goto Fail;
  
            x = ADD_LONG( x, top[0] );
            t1_builder_add_point( builder, x, y, 0 );
  
            x = ADD_LONG( x, top[1] );
            y = ADD_LONG( y, top[2] );
            t1_builder_add_point( builder, x, y, 0 );
  
            y = ADD_LONG( y, top[3] );
            t1_builder_add_point( builder, x, y, 1 );
            break;
  
          case op_rlineto:
            FT_TRACE4(( " rlineto" ));
  
            if ( FT_SET_ERROR( t1_builder_start_point( builder, x, y ) ) )
              goto Fail;
  
            x = ADD_LONG( x, top[0] );
            y = ADD_LONG( y, top[1] );
  
          Add_Line:
            if ( FT_SET_ERROR( t1_builder_add_point1( builder, x, y ) ) )
              goto Fail;
            break;
  
          case op_rmoveto:
            FT_TRACE4(( " rmoveto" ));
  
            x = ADD_LONG( x, top[0] );
            y = ADD_LONG( y, top[1] );
  
            if ( !decoder->flex_state )
            {
              if ( builder->parse_state == T1_Parse_Start )
                goto Syntax_Error;
              builder->parse_state = T1_Parse_Have_Moveto;
            }
            break;
  
          case op_rrcurveto:
            FT_TRACE4(( " rrcurveto" ));
  
            if ( FT_SET_ERROR( t1_builder_start_point( builder, x, y ) ) ||
                 FT_SET_ERROR( t1_builder_check_points( builder, 3 ) )   )
              goto Fail;
  
            x = ADD_LONG( x, top[0] );
            y = ADD_LONG( y, top[1] );
            t1_builder_add_point( builder, x, y, 0 );
  
            x = ADD_LONG( x, top[2] );
            y = ADD_LONG( y, top[3] );
            t1_builder_add_point( builder, x, y, 0 );
  
            x = ADD_LONG( x, top[4] );
            y = ADD_LONG( y, top[5] );
            t1_builder_add_point( builder, x, y, 1 );
            break;
  
          case op_vhcurveto:
            FT_TRACE4(( " vhcurveto" ));
  
            if ( FT_SET_ERROR( t1_builder_start_point( builder, x, y ) ) ||
                 FT_SET_ERROR( t1_builder_check_points( builder, 3 ) )   )
              goto Fail;
  
            y = ADD_LONG( y, top[0] );
            t1_builder_add_point( builder, x, y, 0 );
  
            x = ADD_LONG( x, top[1] );
            y = ADD_LONG( y, top[2] );
            t1_builder_add_point( builder, x, y, 0 );
  
            x = ADD_LONG( x, top[3] );
            t1_builder_add_point( builder, x, y, 1 );
            break;
  
          case op_vlineto:
            FT_TRACE4(( " vlineto" ));
  
            if ( FT_SET_ERROR( t1_builder_start_point( builder, x, y ) ) )
              goto Fail;
  
            y = ADD_LONG( y, top[0] );
            goto Add_Line;
  
          case op_vmoveto:
            FT_TRACE4(( " vmoveto" ));
  
            y = ADD_LONG( y, top[0] );
  
            if ( !decoder->flex_state )
            {
              if ( builder->parse_state == T1_Parse_Start )
                goto Syntax_Error;
              builder->parse_state = T1_Parse_Have_Moveto;
            }
            break;
  
          case op_div:
            FT_TRACE4(( " div" ));
  
            /* if `large_int' is set, we divide unscaled numbers; */
            /* otherwise, we divide numbers in 16.16 format --    */
            /* in both cases, it is the same operation            */
            *top = FT_DivFix( top[0], top[1] );
            top++;
  
            large_int = FALSE;
            break;
  
          case op_callsubr:
            {
              FT_Int  idx;
  
  
              FT_TRACE4(( " callsubr" ));
  
              idx = Fix2Int( top[0] );
  
              if ( decoder->subrs_hash )
              {
                size_t*  val = ft_hash_num_lookup( idx,
                                                   decoder->subrs_hash );
  
  
                if ( val )
                  idx = *val;
                else
                  idx = -1;
              }
  
              if ( idx < 0 || idx >= decoder->num_subrs )
              {
                FT_ERROR(( "t1_decoder_parse_charstrings:"
                           " invalid subrs index\n" ));
                goto Syntax_Error;
              }
  
              if ( zone - decoder->zones >= T1_MAX_SUBRS_CALLS )
              {
                FT_ERROR(( "t1_decoder_parse_charstrings:"
                           " too many nested subrs\n" ));
                goto Syntax_Error;
              }
  
              zone->cursor = ip;  /* save current instruction pointer */
  
              zone++;
  
              /* The Type 1 driver stores subroutines without the seed bytes. */
              /* The CID driver stores subroutines with seed bytes.  This     */
              /* case is taken care of when decoder->subrs_len == 0.          */
              zone->base = decoder->subrs[idx];
  
              if ( decoder->subrs_len )
                zone->limit = zone->base + decoder->subrs_len[idx];
              else
              {
                /* We are using subroutines from a CID font.  We must adjust */
                /* for the seed bytes.                                       */
                zone->base  += ( decoder->lenIV >= 0 ? decoder->lenIV : 0 );
                zone->limit  = decoder->subrs[idx + 1];
              }
  
              zone->cursor = zone->base;
  
              if ( !zone->base )
              {
                FT_ERROR(( "t1_decoder_parse_charstrings:"
                           " invoking empty subrs\n" ));
                goto Syntax_Error;
              }
  
              decoder->zone = zone;
              ip            = zone->base;
              limit         = zone->limit;
              break;
            }
  
          case op_pop:
            FT_TRACE4(( " pop" ));
  
            if ( known_othersubr_result_cnt > 0 )
            {
              known_othersubr_result_cnt--;
              /* ignore, we pushed the operands ourselves */
              break;
            }
  
            if ( unknown_othersubr_result_cnt == 0 )
            {
              FT_ERROR(( "t1_decoder_parse_charstrings:"
                         " no more operands for othersubr\n" ));
              goto Syntax_Error;
            }
  
            unknown_othersubr_result_cnt--;
            top++;   /* `push' the operand to callothersubr onto the stack */
            break;
  
          case op_return:
            FT_TRACE4(( " return" ));
  
            if ( zone <= decoder->zones )
            {
              FT_ERROR(( "t1_decoder_parse_charstrings:"
                         " unexpected return\n" ));
              goto Syntax_Error;
            }
  
            zone--;
            ip            = zone->cursor;
            limit         = zone->limit;
            decoder->zone = zone;
            break;
  
          case op_dotsection:
            FT_TRACE4(( " dotsection" ));
  
            break;
  
          case op_hstem:
            FT_TRACE4(( " hstem" ));
  
            /* record horizontal hint */
            if ( hinter )
            {
              /* top[0] += builder->left_bearing.y; */
              hinter->stem( hinter->hints, 1, top );
            }
            break;
  
          case op_hstem3:
            FT_TRACE4(( " hstem3" ));
  
            /* record horizontal counter-controlled hints */
            if ( hinter )
              hinter->stem3( hinter->hints, 1, top );
            break;
  
          case op_vstem:
            FT_TRACE4(( " vstem" ));
  
            /* record vertical hint */
            if ( hinter )
            {
              top[0] = ADD_LONG( top[0], orig_x );
              hinter->stem( hinter->hints, 0, top );
            }
            break;
  
          case op_vstem3:
            FT_TRACE4(( " vstem3" ));
  
            /* record vertical counter-controlled hints */
            if ( hinter )
            {
              FT_Pos  dx = orig_x;
  
  
              top[0] = ADD_LONG( top[0], dx );
              top[2] = ADD_LONG( top[2], dx );
              top[4] = ADD_LONG( top[4], dx );
              hinter->stem3( hinter->hints, 0, top );
            }
            break;
  
          case op_setcurrentpoint:
            FT_TRACE4(( " setcurrentpoint" ));
  
            /* From the T1 specification, section 6.4:                */
            /*                                                        */
            /*   The setcurrentpoint command is used only in          */
            /*   conjunction with results from OtherSubrs procedures. */
  
            /* known_othersubr_result_cnt != 0 is already handled     */
            /* above.                                                 */
  
            /* Note, however, that both Ghostscript and Adobe         */
            /* Distiller handle this situation by silently ignoring   */
            /* the inappropriate `setcurrentpoint' instruction.  So   */
            /* we do the same.                                        */
  #if 0
  
            if ( decoder->flex_state != 1 )
            {
              FT_ERROR(( "t1_decoder_parse_charstrings:"
                         " unexpected `setcurrentpoint'\n" ));
              goto Syntax_Error;
            }
            else
              ...
  #endif
  
            x = top[0];
            y = top[1];
            decoder->flex_state = 0;
            break;
  
          case op_unknown15:
            FT_TRACE4(( " opcode_15" ));
            /* nothing to do except to pop the two arguments */
            break;
  
          default:
            FT_ERROR(( "t1_decoder_parse_charstrings:"
                       " unhandled opcode %d\n", op ));
            goto Syntax_Error;
          }
  
          /* XXX Operators usually clear the operand stack;  */
          /*     only div, callsubr, callothersubr, pop, and */
          /*     return are different.                       */
          /*     In practice it doesn't matter (?).          */
  
          decoder->top = top;
  
  #ifdef FT_DEBUG_LEVEL_TRACE
          FT_TRACE4(( "\n" ));
          bol = TRUE;
  #endif
  
        } /* general operator processing */
  
      } /* while ip < limit */
  
      FT_TRACE4(( "..end..\n\n" ));
  
    Fail:
      return error;
  
    Syntax_Error:
      return FT_THROW( Syntax_Error );
  
    Stack_Underflow:
      return FT_THROW( Stack_Underflow );
    }
  
  
  #else /* !T1_CONFIG_OPTION_OLD_ENGINE */
  
  
    /**************************************************************************
     *
     * @Function:
     *   t1_decoder_parse_metrics
     *
     * @Description:
     *   Parses a given Type 1 charstrings program to extract width
     *
     * @Input:
     *   decoder ::
     *     The current Type 1 decoder.
     *
     *   charstring_base ::
     *     The base address of the charstring stream.
     *
     *   charstring_len ::
     *     The length in bytes of the charstring stream.
     *
     * @Return:
     *   FreeType error code.  0 means success.
     */
    FT_LOCAL_DEF( FT_Error )
    t1_decoder_parse_metrics( T1_Decoder  decoder,
                              FT_Byte*    charstring_base,
                              FT_UInt     charstring_len )
    {
      T1_Decoder_Zone  zone;
      FT_Byte*         ip;
      FT_Byte*         limit;
      T1_Builder       builder = &decoder->builder;
  
  #ifdef FT_DEBUG_LEVEL_TRACE
      FT_Bool          bol = TRUE;
  #endif
  
  
      /* First of all, initialize the decoder */
      decoder->top  = decoder->stack;
      decoder->zone = decoder->zones;
      zone          = decoder->zones;
  
      builder->parse_state = T1_Parse_Start;
  
      zone->base           = charstring_base;
      limit = zone->limit  = charstring_base + charstring_len;
      ip    = zone->cursor = zone->base;
  
      /* now, execute loop */
      while ( ip < limit )
      {
        FT_Long*     top   = decoder->top;
        T1_Operator  op    = op_none;
        FT_Int32     value = 0;
  
  
  #ifdef FT_DEBUG_LEVEL_TRACE
        if ( bol )
        {
          FT_TRACE5(( " (%d)", decoder->top - decoder->stack ));
          bol = FALSE;
        }
  #endif
  
        /**********************************************************************
         *
         * Decode operator or operand
         *
         */
  
        /* first of all, decompress operator or value */
        switch ( *ip++ )
        {
        case 1:
        case 3:
        case 4:
        case 5:
        case 6:
        case 7:
        case 8:
        case 9:
        case 10:
        case 11:
        case 14:
        case 15:
        case 21:
        case 22:
        case 30:
        case 31:
          goto No_Width;
  
        case 13:
          op = op_hsbw;
          break;
  
        case 12:
          if ( ip >= limit )
          {
            FT_ERROR(( "t1_decoder_parse_metrics:"
                       " invalid escape (12+EOF)\n" ));
            goto Syntax_Error;
          }
  
          switch ( *ip++ )
          {
          case 7:
            op = op_sbw;
            break;
  
          default:
            goto No_Width;
          }
          break;
  
        case 255:    /* four bytes integer */
          if ( ip + 4 > limit )
          {
            FT_ERROR(( "t1_decoder_parse_metrics:"
                       " unexpected EOF in integer\n" ));
            goto Syntax_Error;
          }
  
          value = (FT_Int32)( ( (FT_UInt32)ip[0] << 24 ) |
                              ( (FT_UInt32)ip[1] << 16 ) |
                              ( (FT_UInt32)ip[2] << 8  ) |
                                (FT_UInt32)ip[3]         );
          ip += 4;
  
          /* According to the specification, values > 32000 or < -32000 must */
          /* be followed by a `div' operator to make the result be in the    */
          /* range [-32000;32000].  We expect that the second argument of    */
          /* `div' is not a large number.  Additionally, we don't handle     */
          /* stuff like `<large1> <large2> <num> div <num> div' or           */
          /* <large1> <large2> <num> div div'.  This is probably not allowed */
          /* anyway.                                                         */
          if ( value > 32000 || value < -32000 )
          {
            FT_ERROR(( "t1_decoder_parse_metrics:"
                       " large integer found for width\n" ));
            goto Syntax_Error;
          }
          else
          {
            value = (FT_Int32)( (FT_UInt32)value << 16 );
          }
  
          break;
  
        default:
          if ( ip[-1] >= 32 )
          {
            if ( ip[-1] < 247 )
              value = (FT_Int32)ip[-1] - 139;
            else
            {
              if ( ++ip > limit )
              {
                FT_ERROR(( "t1_decoder_parse_metrics:"
                           " unexpected EOF in integer\n" ));
                goto Syntax_Error;
              }
  
              if ( ip[-2] < 251 )
                value =    ( ( ip[-2] - 247 ) * 256 ) + ip[-1] + 108;
              else
                value = -( ( ( ip[-2] - 251 ) * 256 ) + ip[-1] + 108 );
            }
  
            value = (FT_Int32)( (FT_UInt32)value << 16 );
          }
          else
          {
            FT_ERROR(( "t1_decoder_parse_metrics:"
                       " invalid byte (%d)\n", ip[-1] ));
            goto Syntax_Error;
          }
        }
  
        /**********************************************************************
         *
         * Push value on stack, or process operator
         *
         */
        if ( op == op_none )
        {
          if ( top - decoder->stack >= T1_MAX_CHARSTRINGS_OPERANDS )
          {
            FT_ERROR(( "t1_decoder_parse_metrics: stack overflow\n" ));
            goto Syntax_Error;
          }
  
  #ifdef FT_DEBUG_LEVEL_TRACE
            FT_TRACE4(( " %d", value / 65536 ));
  #endif
  
          *top++       = value;
          decoder->top = top;
        }
        else  /* general operator */
        {
          FT_Int  num_args = t1_args_count[op];
  
  
          FT_ASSERT( num_args >= 0 );
  
          if ( top - decoder->stack < num_args )
            goto Stack_Underflow;
  
  #ifdef FT_DEBUG_LEVEL_TRACE
  
          if ( top - decoder->stack != num_args )
            FT_TRACE0(( "t1_decoder_parse_metrics:"
                        " too much operands on the stack"
                        " (seen %d, expected %d)\n",
                        top - decoder->stack, num_args ));
  
  #endif /* FT_DEBUG_LEVEL_TRACE */
  
          top -= num_args;
  
          switch ( op )
          {
          case op_hsbw:
            FT_TRACE4(( " hsbw" ));
  
            builder->parse_state = T1_Parse_Have_Width;
  
            builder->left_bearing.x = ADD_LONG( builder->left_bearing.x,
                                                top[0] );
  
            builder->advance.x = top[1];
            builder->advance.y = 0;
  
            /* we only want to compute the glyph's metrics */
            /* (lsb + advance width), not load the rest of */
            /* it; so exit immediately                     */
            FT_TRACE4(( "\n" ));
            return FT_Err_Ok;
  
          case op_sbw:
            FT_TRACE4(( " sbw" ));
  
            builder->parse_state = T1_Parse_Have_Width;
  
            builder->left_bearing.x = ADD_LONG( builder->left_bearing.x,
                                                top[0] );
            builder->left_bearing.y = ADD_LONG( builder->left_bearing.y,
                                                top[1] );
  
            builder->advance.x = top[2];
            builder->advance.y = top[3];
  
            /* we only want to compute the glyph's metrics */
            /* (lsb + advance width), not load the rest of */
            /* it; so exit immediately                     */
            FT_TRACE4(( "\n" ));
            return FT_Err_Ok;
  
          default:
            FT_ERROR(( "t1_decoder_parse_metrics:"
                       " unhandled opcode %d\n", op ));
            goto Syntax_Error;
          }
  
        } /* general operator processing */
  
      } /* while ip < limit */
  
      FT_TRACE4(( "..end..\n\n" ));
  
    No_Width:
      FT_ERROR(( "t1_decoder_parse_metrics:"
                 " no width, found op %d instead\n",
                 ip[-1] ));
    Syntax_Error:
      return FT_THROW( Syntax_Error );
  
    Stack_Underflow:
      return FT_THROW( Stack_Underflow );
    }
  
  #endif /* !T1_CONFIG_OPTION_OLD_ENGINE */
  
  
    /* initialize T1 decoder */
    FT_LOCAL_DEF( FT_Error )
    t1_decoder_init( T1_Decoder           decoder,
                     FT_Face              face,
                     FT_Size              size,
                     FT_GlyphSlot         slot,
                     FT_Byte**            glyph_names,
                     PS_Blend             blend,
                     FT_Bool              hinting,
                     FT_Render_Mode       hint_mode,
                     T1_Decoder_Callback  parse_callback )
    {
      FT_ZERO( decoder );
  
      /* retrieve `psnames' interface from list of current modules */
      {
        FT_Service_PsCMaps  psnames;
  
  
        FT_FACE_FIND_GLOBAL_SERVICE( face, psnames, POSTSCRIPT_CMAPS );
        if ( !psnames )
        {
          FT_ERROR(( "t1_decoder_init:"
                     " the `psnames' module is not available\n" ));
          return FT_THROW( Unimplemented_Feature );
        }
  
        decoder->psnames = psnames;
      }
  
      t1_builder_init( &decoder->builder, face, size, slot, hinting );
  
      /* decoder->buildchar and decoder->len_buildchar have to be  */
      /* initialized by the caller since we cannot know the length */
      /* of the BuildCharArray                                     */
  
      decoder->num_glyphs     = (FT_UInt)face->num_glyphs;
      decoder->glyph_names    = glyph_names;
      decoder->hint_mode      = hint_mode;
      decoder->blend          = blend;
      decoder->parse_callback = parse_callback;
  
      decoder->funcs          = t1_decoder_funcs;
  
      return FT_Err_Ok;
    }
  
  
    /* finalize T1 decoder */
    FT_LOCAL_DEF( void )
    t1_decoder_done( T1_Decoder  decoder )
    {
      FT_Memory  memory = decoder->builder.memory;
  
  
      t1_builder_done( &decoder->builder );
  
      if ( decoder->cf2_instance.finalizer )
      {
        decoder->cf2_instance.finalizer( decoder->cf2_instance.data );
        FT_FREE( decoder->cf2_instance.data );
      }
    }
  
  
  /* END */
  