Edit

kc3-lang/freetype/src/cff/cffgload.c

Branch :

  • Show log

    Commit

  • Author : Wu, Chia-I (吳佳一)
    Date : 2006-02-14 06:40:10
    Hash : f5aa47be
    Message : Clean up the SFNT_Interface. Table loading functions are now named after the tables' tags; `hdmx' is TrueType-specific and thus the code is moved to the truetype module; `get_metrics' is moved here from the truetype module so that the code can be shared with the cff module. This pass involves no real changes. That is, the code is moved verbatim mostly. The only exception is the return value of `tt_face_get_metrics'. * include/freetype/internal/sfnt.h, src/sfnt/rules.mk, src/sfnt/sfdriver.c, src/sfnt/sfnt.c, src/sfnt/sfobjs.c, src/sfnt/ttload.c, src/sfnt/ttload.h, src/sfnt/ttsbit.c, src/sfnt/ttsbit.h, src/sfnt/ttsbit0.c: Clean up the SFNT_Interface. * src/sfnt/ttmtx.c, src/sfnt/ttmtx.h: Metrics-related tables' loading and parsing code is moved here. Move `tt_face_get_metrics' here from the truetype module. The return value is changed from `void' to `FT_Error'. * include/freetype/internal/fttrace.h: New trace: ttmtx. * src/truetype/ttpload.c, src/truetype/ttpload.h: `hdmx' loading and parsing code is moved here. New function `tt_face_load_prep' splitted from `tt_face_load_fpgm'. `tt_face_load_fpgm' returns `FT_Err_Ok' if `fpgm' doesn't exist. * src/cff/cffgload.c, src/cff/cffobjs.c: Update. * src/truetype/ttgload.c, src/truetype/ttobjs.c: Update.

  • src/cff/cffgload.c
  • /***************************************************************************/
    /*                                                                         */
    /*  cffgload.c                                                             */
    /*                                                                         */
    /*    OpenType Glyph Loader (body).                                        */
    /*                                                                         */
    /*  Copyright 1996-2001, 2002, 2003, 2004, 2005, 2006 by                   */
    /*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
    /*                                                                         */
    /*  This file is part of the FreeType project, and may only be used,       */
    /*  modified, and distributed under the terms of the FreeType project      */
    /*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
    /*  this file you indicate that you have read the license and              */
    /*  understand and accept it fully.                                        */
    /*                                                                         */
    /***************************************************************************/
    
    
    #include <ft2build.h>
    #include FT_INTERNAL_DEBUG_H
    #include FT_INTERNAL_CALC_H
    #include FT_INTERNAL_STREAM_H
    #include FT_INTERNAL_SFNT_H
    #include FT_OUTLINE_H
    #include FT_TRUETYPE_TAGS_H
    #include FT_INTERNAL_POSTSCRIPT_HINTS_H
    
    #include "cffobjs.h"
    #include "cffload.h"
    #include "cffgload.h"
    
    #include "cfferrs.h"
    
    
      /*************************************************************************/
      /*                                                                       */
      /* The macro FT_COMPONENT is used in trace mode.  It is an implicit      */
      /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log  */
      /* messages during execution.                                            */
      /*                                                                       */
    #undef  FT_COMPONENT
    #define FT_COMPONENT  trace_cffgload
    
    
      typedef enum  CFF_Operator_
      {
        cff_op_unknown = 0,
    
        cff_op_rmoveto,
        cff_op_hmoveto,
        cff_op_vmoveto,
    
        cff_op_rlineto,
        cff_op_hlineto,
        cff_op_vlineto,
    
        cff_op_rrcurveto,
        cff_op_hhcurveto,
        cff_op_hvcurveto,
        cff_op_rcurveline,
        cff_op_rlinecurve,
        cff_op_vhcurveto,
        cff_op_vvcurveto,
    
        cff_op_flex,
        cff_op_hflex,
        cff_op_hflex1,
        cff_op_flex1,
    
        cff_op_endchar,
    
        cff_op_hstem,
        cff_op_vstem,
        cff_op_hstemhm,
        cff_op_vstemhm,
    
        cff_op_hintmask,
        cff_op_cntrmask,
        cff_op_dotsection,  /* deprecated, acts as no-op */
    
        cff_op_abs,
        cff_op_add,
        cff_op_sub,
        cff_op_div,
        cff_op_neg,
        cff_op_random,
        cff_op_mul,
        cff_op_sqrt,
    
        cff_op_blend,
    
        cff_op_drop,
        cff_op_exch,
        cff_op_index,
        cff_op_roll,
        cff_op_dup,
    
        cff_op_put,
        cff_op_get,
        cff_op_store,
        cff_op_load,
    
        cff_op_and,
        cff_op_or,
        cff_op_not,
        cff_op_eq,
        cff_op_ifelse,
    
        cff_op_callsubr,
        cff_op_callgsubr,
        cff_op_return,
    
        /* do not remove */
        cff_op_max
    
      } CFF_Operator;
    
    
    #define CFF_COUNT_CHECK_WIDTH  0x80
    #define CFF_COUNT_EXACT        0x40
    #define CFF_COUNT_CLEAR_STACK  0x20
    
    
      static const FT_Byte  cff_argument_counts[] =
      {
        0,  /* unknown */
    
        2 | CFF_COUNT_CHECK_WIDTH | CFF_COUNT_EXACT, /* rmoveto */
        1 | CFF_COUNT_CHECK_WIDTH | CFF_COUNT_EXACT,
        1 | CFF_COUNT_CHECK_WIDTH | CFF_COUNT_EXACT,
    
        0 | CFF_COUNT_CLEAR_STACK, /* rlineto */
        0 | CFF_COUNT_CLEAR_STACK,
        0 | CFF_COUNT_CLEAR_STACK,
    
        0 | CFF_COUNT_CLEAR_STACK, /* rrcurveto */
        0 | CFF_COUNT_CLEAR_STACK,
        0 | CFF_COUNT_CLEAR_STACK,
        0 | CFF_COUNT_CLEAR_STACK,
        0 | CFF_COUNT_CLEAR_STACK,
        0 | CFF_COUNT_CLEAR_STACK,
        0 | CFF_COUNT_CLEAR_STACK,
    
        13, /* flex */
        7,
        9,
        11,
    
        0 | CFF_COUNT_CHECK_WIDTH, /* endchar */
    
        2 | CFF_COUNT_CHECK_WIDTH, /* hstem */
        2 | CFF_COUNT_CHECK_WIDTH,
        2 | CFF_COUNT_CHECK_WIDTH,
        2 | CFF_COUNT_CHECK_WIDTH,
    
        0 | CFF_COUNT_CHECK_WIDTH, /* hintmask */
        0 | CFF_COUNT_CHECK_WIDTH, /* cntrmask */
        0, /* dotsection */
    
        1, /* abs */
        2,
        2,
        2,
        1,
        0,
        2,
        1,
    
        1, /* blend */
    
        1, /* drop */
        2,
        1,
        2,
        1,
    
        2, /* put */
        1,
        4,
        3,
    
        2, /* and */
        2,
        1,
        2,
        4,
    
        1, /* callsubr */
        1,
        0
      };
    
    
      /*************************************************************************/
      /*************************************************************************/
      /*************************************************************************/
      /**********                                                      *********/
      /**********                                                      *********/
      /**********             GENERIC CHARSTRING PARSING               *********/
      /**********                                                      *********/
      /**********                                                      *********/
      /*************************************************************************/
      /*************************************************************************/
      /*************************************************************************/
    
    
      /*************************************************************************/
      /*                                                                       */
      /* <Function>                                                            */
      /*    cff_builder_init                                                   */
      /*                                                                       */
      /* <Description>                                                         */
      /*    Initializes a given glyph builder.                                 */
      /*                                                                       */
      /* <InOut>                                                               */
      /*    builder :: A pointer to the glyph builder to initialize.           */
      /*                                                                       */
      /* <Input>                                                               */
      /*    face    :: The current face object.                                */
      /*                                                                       */
      /*    size    :: The current size object.                                */
      /*                                                                       */
      /*    glyph   :: The current glyph object.                               */
      /*                                                                       */
      static void
      cff_builder_init( CFF_Builder*   builder,
                        TT_Face        face,
                        CFF_Size       size,
                        CFF_GlyphSlot  glyph,
                        FT_Bool        hinting )
      {
        builder->path_begun  = 0;
        builder->load_points = 1;
    
        builder->face   = face;
        builder->glyph  = glyph;
        builder->memory = face->root.memory;
    
        if ( glyph )
        {
          FT_GlyphLoader  loader = glyph->root.internal->loader;
    
    
          builder->loader  = loader;
          builder->base    = &loader->base.outline;
          builder->current = &loader->current.outline;
          FT_GlyphLoader_Rewind( loader );
    
          builder->hints_globals = 0;
          builder->hints_funcs   = 0;
    
          if ( hinting && size )
          {
            builder->hints_globals = size->root.internal;
            builder->hints_funcs   = glyph->root.internal->glyph_hints;
          }
        }
    
        if ( size )
        {
          builder->scale_x = size->root.metrics.x_scale;
          builder->scale_y = size->root.metrics.y_scale;
        }
    
        builder->pos_x = 0;
        builder->pos_y = 0;
    
        builder->left_bearing.x = 0;
        builder->left_bearing.y = 0;
        builder->advance.x      = 0;
        builder->advance.y      = 0;
      }
    
    
      /*************************************************************************/
      /*                                                                       */
      /* <Function>                                                            */
      /*    cff_builder_done                                                   */
      /*                                                                       */
      /* <Description>                                                         */
      /*    Finalizes a given glyph builder.  Its contents can still be used   */
      /*    after the call, but the function saves important information       */
      /*    within the corresponding glyph slot.                               */
      /*                                                                       */
      /* <Input>                                                               */
      /*    builder :: A pointer to the glyph builder to finalize.             */
      /*                                                                       */
      static void
      cff_builder_done( CFF_Builder*  builder )
      {
        CFF_GlyphSlot  glyph = builder->glyph;
    
    
        if ( glyph )
          glyph->root.outline = *builder->base;
      }
    
    
      /*************************************************************************/
      /*                                                                       */
      /* <Function>                                                            */
      /*    cff_compute_bias                                                   */
      /*                                                                       */
      /* <Description>                                                         */
      /*    Computes the bias value in dependence of the number of glyph       */
      /*    subroutines.                                                       */
      /*                                                                       */
      /* <Input>                                                               */
      /*    num_subrs :: The number of glyph subroutines.                      */
      /*                                                                       */
      /* <Return>                                                              */
      /*    The bias value.                                                    */
      static FT_Int
      cff_compute_bias( FT_UInt  num_subrs )
      {
        FT_Int  result;
    
    
        if ( num_subrs < 1240 )
          result = 107;
        else if ( num_subrs < 33900U )
          result = 1131;
        else
          result = 32768U;
    
        return result;
      }
    
    
      /*************************************************************************/
      /*                                                                       */
      /* <Function>                                                            */
      /*    cff_decoder_init                                                   */
      /*                                                                       */
      /* <Description>                                                         */
      /*    Initializes a given glyph decoder.                                 */
      /*                                                                       */
      /* <InOut>                                                               */
      /*    decoder :: A pointer to the glyph builder to initialize.           */
      /*                                                                       */
      /* <Input>                                                               */
      /*    face    :: The current face object.                                */
      /*                                                                       */
      /*    size    :: The current size object.                                */
      /*                                                                       */
      /*    slot    :: The current glyph object.                               */
      /*                                                                       */
      FT_LOCAL_DEF( void )
      cff_decoder_init( CFF_Decoder*    decoder,
                        TT_Face         face,
                        CFF_Size        size,
                        CFF_GlyphSlot   slot,
                        FT_Bool         hinting,
                        FT_Render_Mode  hint_mode )
      {
        CFF_Font  cff = (CFF_Font)face->extra.data;
    
    
        /* clear everything */
        FT_MEM_ZERO( decoder, sizeof ( *decoder ) );
    
        /* initialize builder */
        cff_builder_init( &decoder->builder, face, size, slot, hinting );
    
        /* initialize Type2 decoder */
        decoder->num_globals  = cff->num_global_subrs;
        decoder->globals      = cff->global_subrs;
        decoder->globals_bias = cff_compute_bias( decoder->num_globals );
    
        decoder->hint_mode    = hint_mode;
      }
    
    
      /* this function is used to select the locals subrs array */
      FT_LOCAL_DEF( void )
      cff_decoder_prepare( CFF_Decoder*  decoder,
                           FT_UInt       glyph_index )
      {
        CFF_Font     cff = (CFF_Font)decoder->builder.face->extra.data;
        CFF_SubFont  sub = &cff->top_font;
    
    
        /* manage CID fonts */
        if ( cff->num_subfonts >= 1 )
        {
          FT_Byte  fd_index = cff_fd_select_get( &cff->fd_select, glyph_index );
    
    
          sub = cff->subfonts[fd_index];
        }
    
        decoder->num_locals    = sub->num_local_subrs;
        decoder->locals        = sub->local_subrs;
        decoder->locals_bias   = cff_compute_bias( decoder->num_locals );
    
        decoder->glyph_width   = sub->private_dict.default_width;
        decoder->nominal_width = sub->private_dict.nominal_width;
      }
    
    
      /* check that there is enough space for `count' more points */
      static FT_Error
      check_points( CFF_Builder*  builder,
                    FT_Int        count )
      {
        return FT_GLYPHLOADER_CHECK_POINTS( builder->loader, count, 0 );
      }
    
    
      /* add a new point, do not check space */
      static void
      cff_builder_add_point( CFF_Builder*  builder,
                             FT_Pos        x,
                             FT_Pos        y,
                             FT_Byte       flag )
      {
        FT_Outline*  outline = builder->current;
    
    
        if ( builder->load_points )
        {
          FT_Vector*  point   = outline->points + outline->n_points;
          FT_Byte*    control = (FT_Byte*)outline->tags + outline->n_points;
    
    
          point->x = x >> 16;
          point->y = y >> 16;
          *control = (FT_Byte)( flag ? FT_CURVE_TAG_ON : FT_CURVE_TAG_CUBIC );
    
          builder->last = *point;
        }
    
        outline->n_points++;
      }
    
    
      /* check space for a new on-curve point, then add it */
      static FT_Error
      cff_builder_add_point1( CFF_Builder*  builder,
                              FT_Pos        x,
                              FT_Pos        y )
      {
        FT_Error  error;
    
    
        error = check_points( builder, 1 );
        if ( !error )
          cff_builder_add_point( builder, x, y, 1 );
    
        return error;
      }
    
    
      /* check space for a new contour, then add it */
      static FT_Error
      cff_builder_add_contour( CFF_Builder*  builder )
      {
        FT_Outline*  outline = builder->current;
        FT_Error     error;
    
    
        if ( !builder->load_points )
        {
          outline->n_contours++;
          return CFF_Err_Ok;
        }
    
        error = FT_GLYPHLOADER_CHECK_POINTS( builder->loader, 0, 1 );
        if ( !error )
        {
          if ( outline->n_contours > 0 )
            outline->contours[outline->n_contours - 1] =
              (short)( outline->n_points - 1 );
    
          outline->n_contours++;
        }
    
        return error;
      }
    
    
      /* if a path was begun, add its first on-curve point */
      static FT_Error
      cff_builder_start_point( CFF_Builder*  builder,
                               FT_Pos        x,
                               FT_Pos        y )
      {
        FT_Error  error = CFF_Err_Ok;
    
    
        /* test whether we are building a new contour */
        if ( !builder->path_begun )
        {
          builder->path_begun = 1;
          error = cff_builder_add_contour( builder );
          if ( !error )
            error = cff_builder_add_point1( builder, x, y );
        }
    
        return error;
      }
    
    
      /* close the current contour */
      static void
      cff_builder_close_contour( CFF_Builder*  builder )
      {
        FT_Outline*  outline = builder->current;
    
    
        if ( !outline )
          return;
    
        /* XXXX: We must not include the last point in the path if it */
        /*       is located on the first point.                       */
        if ( outline->n_points > 1 )
        {
          FT_Int      first   = 0;
          FT_Vector*  p1      = outline->points + first;
          FT_Vector*  p2      = outline->points + outline->n_points - 1;
          FT_Byte*    control = (FT_Byte*)outline->tags + outline->n_points - 1;
    
    
          if ( outline->n_contours > 1 )
          {
            first = outline->contours[outline->n_contours - 2] + 1;
            p1    = outline->points + first;
          }
    
          /* `delete' last point only if it coincides with the first    */
          /* point and if it is not a control point (which can happen). */
          if ( p1->x == p2->x && p1->y == p2->y )
            if ( *control == FT_CURVE_TAG_ON )
              outline->n_points--;
        }
    
        if ( outline->n_contours > 0 )
          outline->contours[outline->n_contours - 1] =
            (short)( outline->n_points - 1 );
      }
    
    
      static FT_Int
      cff_lookup_glyph_by_stdcharcode( CFF_Font  cff,
                                       FT_Int    charcode )
      {
        FT_UInt    n;
        FT_UShort  glyph_sid;
    
    
        /* CID-keyed fonts don't have glyph names */
        if ( !cff->charset.sids )
          return -1;
    
        /* check range of standard char code */
        if ( charcode < 0 || charcode > 255 )
          return -1;
    
        /* Get code to SID mapping from `cff_standard_encoding'. */
        glyph_sid = cff_get_standard_encoding( (FT_UInt)charcode );
    
        for ( n = 0; n < cff->num_glyphs; n++ )
        {
          if ( cff->charset.sids[n] == glyph_sid )
            return n;
        }
    
        return -1;
      }
    
    
      static FT_Error
      cff_get_glyph_data( TT_Face    face,
                          FT_UInt    glyph_index,
                          FT_Byte**  pointer,
                          FT_ULong*  length )
      {
    #ifdef FT_CONFIG_OPTION_INCREMENTAL
        /* For incremental fonts get the character data using the */
        /* callback function.                                     */
        if ( face->root.internal->incremental_interface )
        {
          FT_Data   data;
          FT_Error  error =
                      face->root.internal->incremental_interface->funcs->get_glyph_data(
                        face->root.internal->incremental_interface->object,
                        glyph_index, &data );
    
    
          *pointer = (FT_Byte*)data.pointer;
          *length = data.length;
    
          return error;
        }
        else
    #endif /* FT_CONFIG_OPTION_INCREMENTAL */
    
        {
          CFF_Font  cff  = (CFF_Font)(face->extra.data);
    
    
          return cff_index_access_element( &cff->charstrings_index, glyph_index,
                                           pointer, length );
        }
      }
    
    
      static void
      cff_free_glyph_data( TT_Face    face,
                           FT_Byte**  pointer,
                           FT_ULong   length )
      {
    #ifndef FT_CONFIG_OPTION_INCREMENTAL
        FT_UNUSED( length );
    #endif
    
    #ifdef FT_CONFIG_OPTION_INCREMENTAL
        /* For incremental fonts get the character data using the */
        /* callback function.                                     */
        if ( face->root.internal->incremental_interface )
        {
          FT_Data data;
    
    
          data.pointer = *pointer;
          data.length  = length;
    
          face->root.internal->incremental_interface->funcs->free_glyph_data(
            face->root.internal->incremental_interface->object,&data );
        }
        else
    #endif /* FT_CONFIG_OPTION_INCREMENTAL */
    
        {
          CFF_Font  cff = (CFF_Font)(face->extra.data);
    
    
          cff_index_forget_element( &cff->charstrings_index, pointer );
        }
      }
    
    
      static FT_Error
      cff_operator_seac( CFF_Decoder*  decoder,
                         FT_Pos        adx,
                         FT_Pos        ady,
                         FT_Int        bchar,
                         FT_Int        achar )
      {
        FT_Error      error;
        CFF_Builder*  builder = &decoder->builder;
        FT_Int        bchar_index, achar_index;
        TT_Face       face = decoder->builder.face;
        FT_Vector     left_bearing, advance;
        FT_Byte*      charstring;
        FT_ULong      charstring_len;
    
    
    #ifdef FT_CONFIG_OPTION_INCREMENTAL
        /* Incremental fonts don't necessarily have valid charsets.        */
        /* They use the character code, not the glyph index, in this case. */
        if ( face->root.internal->incremental_interface )
        {
          bchar_index = bchar;
          achar_index = achar;
        }
        else
    #endif /* FT_CONFIG_OPTION_INCREMENTAL */
        {
          CFF_Font cff = (CFF_Font)(face->extra.data);
    
    
          bchar_index = cff_lookup_glyph_by_stdcharcode( cff, bchar );
          achar_index = cff_lookup_glyph_by_stdcharcode( cff, achar );
        }
    
        if ( bchar_index < 0 || achar_index < 0 )
        {
          FT_ERROR(( "cff_operator_seac:" ));
          FT_ERROR(( " invalid seac character code arguments\n" ));
          return CFF_Err_Syntax_Error;
        }
    
        /* If we are trying to load a composite glyph, do not load the */
        /* accent character and return the array of subglyphs.         */
        if ( builder->no_recurse )
        {
          FT_GlyphSlot    glyph  = (FT_GlyphSlot)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)( adx >> 16 );
          subg->arg2  = (FT_Int)( ady >> 16 );
    
          /* 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;
        }
    
        FT_GlyphLoader_Prepare( builder->loader );
    
        /* First load `bchar' in builder */
        error = cff_get_glyph_data( face, bchar_index,
                                    &charstring, &charstring_len );
        if ( !error )
        {
          error = cff_decoder_parse_charstrings( decoder, charstring,
                                                 charstring_len );
    
          if ( error )
            goto Exit;
    
          cff_free_glyph_data( face, &charstring, charstring_len );
        }
    
        /* Save the left bearing and width of the base character */
        /* as they will be erased by the next load.              */
    
        left_bearing = builder->left_bearing;
        advance      = builder->advance;
    
        builder->left_bearing.x = 0;
        builder->left_bearing.y = 0;
    
        builder->pos_x = adx;
        builder->pos_y = ady;
    
        /* Now load `achar' on top of the base outline. */
        error = cff_get_glyph_data( face, achar_index,
                                    &charstring, &charstring_len );
        if ( !error )
        {
          error = cff_decoder_parse_charstrings( decoder, charstring,
                                                 charstring_len );
    
          if ( error )
            goto Exit;
    
          cff_free_glyph_data( face, &charstring, charstring_len );
        }
    
        /* Restore the left side bearing and advance width */
        /* of the base character.                          */
        builder->left_bearing = left_bearing;
        builder->advance      = advance;
    
        builder->pos_x = 0;
        builder->pos_y = 0;
    
      Exit:
        return error;
      }
    
    
      /*************************************************************************/
      /*                                                                       */
      /* <Function>                                                            */
      /*    cff_decoder_parse_charstrings                                      */
      /*                                                                       */
      /* <Description>                                                         */
      /*    Parses a given Type 2 charstrings program.                         */
      /*                                                                       */
      /* <InOut>                                                               */
      /*    decoder         :: The current Type 1 decoder.                     */
      /*                                                                       */
      /* <Input>                                                               */
      /*    charstring_base :: The base 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 )
      cff_decoder_parse_charstrings( CFF_Decoder*  decoder,
                                     FT_Byte*      charstring_base,
                                     FT_ULong      charstring_len )
      {
        FT_Error           error;
        CFF_Decoder_Zone*  zone;
        FT_Byte*           ip;
        FT_Byte*           limit;
        CFF_Builder*       builder = &decoder->builder;
        FT_Pos             x, y;
        FT_Fixed           seed;
        FT_Fixed*          stack;
    
        T2_Hints_Funcs     hinter;
    
    
        /* set default width */
        decoder->num_hints  = 0;
        decoder->read_width = 1;
    
        /* compute random seed from stack address of parameter */
        seed = (FT_Fixed)(char*)&seed           ^
               (FT_Fixed)(char*)&decoder        ^
               (FT_Fixed)(char*)&charstring_base;
        seed = ( seed ^ ( seed >> 10 ) ^ ( seed >> 20 ) ) & 0xFFFFL;
        if ( seed == 0 )
          seed = 0x7384;
    
        /* initialize the decoder */
        decoder->top  = decoder->stack;
        decoder->zone = decoder->zones;
        zone          = decoder->zones;
        stack         = decoder->top;
    
        hinter = (T2_Hints_Funcs)builder->hints_funcs;
    
        builder->path_begun = 0;
    
        zone->base           = charstring_base;
        limit = zone->limit  = charstring_base + charstring_len;
        ip    = zone->cursor = zone->base;
    
        error = CFF_Err_Ok;
    
        x = builder->pos_x;
        y = builder->pos_y;
    
        /* begin hints recording session, if any */
        if ( hinter )
          hinter->open( hinter->hints );
    
        /* now execute loop */
        while ( ip < limit )
        {
          CFF_Operator  op;
          FT_Byte       v;
    
    
          /********************************************************************/
          /*                                                                  */
          /* Decode operator or operand                                       */
          /*                                                                  */
          v = *ip++;
          if ( v >= 32 || v == 28 )
          {
            FT_Int    shift = 16;
            FT_Int32  val;
    
    
            /* this is an operand, push it on the stack */
            if ( v == 28 )
            {
              if ( ip + 1 >= limit )
                goto Syntax_Error;
              val = (FT_Short)( ( (FT_Short)ip[0] << 8 ) | ip[1] );
              ip += 2;
            }
            else if ( v < 247 )
              val = (FT_Long)v - 139;
            else if ( v < 251 )
            {
              if ( ip >= limit )
                goto Syntax_Error;
              val = ( (FT_Long)v - 247 ) * 256 + *ip++ + 108;
            }
            else if ( v < 255 )
            {
              if ( ip >= limit )
                goto Syntax_Error;
              val = -( (FT_Long)v - 251 ) * 256 - *ip++ - 108;
            }
            else
            {
              if ( ip + 3 >= limit )
                goto Syntax_Error;
              val = ( (FT_Int32)ip[0] << 24 ) |
                    ( (FT_Int32)ip[1] << 16 ) |
                    ( (FT_Int32)ip[2] <<  8 ) |
                                ip[3];
              ip    += 4;
              shift  = 0;
            }
            if ( decoder->top - stack >= CFF_MAX_OPERANDS )
              goto Stack_Overflow;
    
            val           <<= shift;
            *decoder->top++ = val;
    
    #ifdef FT_DEBUG_LEVEL_TRACE
            if ( !( val & 0xFFFFL ) )
              FT_TRACE4(( " %ld", (FT_Int32)( val >> 16 ) ));
            else
              FT_TRACE4(( " %.2f", val / 65536.0 ));
    #endif
    
          }
          else
          {
            FT_Fixed*  args     = decoder->top;
            FT_Int     num_args = (FT_Int)( args - decoder->stack );
            FT_Int     req_args;
    
    
            /* find operator */
            op = cff_op_unknown;
    
            switch ( v )
            {
            case 1:
              op = cff_op_hstem;
              break;
            case 3:
              op = cff_op_vstem;
              break;
            case 4:
              op = cff_op_vmoveto;
              break;
            case 5:
              op = cff_op_rlineto;
              break;
            case 6:
              op = cff_op_hlineto;
              break;
            case 7:
              op = cff_op_vlineto;
              break;
            case 8:
              op = cff_op_rrcurveto;
              break;
            case 10:
              op = cff_op_callsubr;
              break;
            case 11:
              op = cff_op_return;
              break;
            case 12:
              {
                if ( ip >= limit )
                  goto Syntax_Error;
                v = *ip++;
    
                switch ( v )
                {
                case 0:
                  op = cff_op_dotsection;
                  break;
                case 3:
                  op = cff_op_and;
                  break;
                case 4:
                  op = cff_op_or;
                  break;
                case 5:
                  op = cff_op_not;
                  break;
                case 8:
                  op = cff_op_store;
                  break;
                case 9:
                  op = cff_op_abs;
                  break;
                case 10:
                  op = cff_op_add;
                  break;
                case 11:
                  op = cff_op_sub;
                  break;
                case 12:
                  op = cff_op_div;
                  break;
                case 13:
                  op = cff_op_load;
                  break;
                case 14:
                  op = cff_op_neg;
                  break;
                case 15:
                  op = cff_op_eq;
                  break;
                case 18:
                  op = cff_op_drop;
                  break;
                case 20:
                  op = cff_op_put;
                  break;
                case 21:
                  op = cff_op_get;
                  break;
                case 22:
                  op = cff_op_ifelse;
                  break;
                case 23:
                  op = cff_op_random;
                  break;
                case 24:
                  op = cff_op_mul;
                  break;
                case 26:
                  op = cff_op_sqrt;
                  break;
                case 27:
                  op = cff_op_dup;
                  break;
                case 28:
                  op = cff_op_exch;
                  break;
                case 29:
                  op = cff_op_index;
                  break;
                case 30:
                  op = cff_op_roll;
                  break;
                case 34:
                  op = cff_op_hflex;
                  break;
                case 35:
                  op = cff_op_flex;
                  break;
                case 36:
                  op = cff_op_hflex1;
                  break;
                case 37:
                  op = cff_op_flex1;
                  break;
                default:
                  /* decrement ip for syntax error message */
                  ip--;
                }
              }
              break;
            case 14:
              op = cff_op_endchar;
              break;
            case 16:
              op = cff_op_blend;
              break;
            case 18:
              op = cff_op_hstemhm;
              break;
            case 19:
              op = cff_op_hintmask;
              break;
            case 20:
              op = cff_op_cntrmask;
              break;
            case 21:
              op = cff_op_rmoveto;
              break;
            case 22:
              op = cff_op_hmoveto;
              break;
            case 23:
              op = cff_op_vstemhm;
              break;
            case 24:
              op = cff_op_rcurveline;
              break;
            case 25:
              op = cff_op_rlinecurve;
              break;
            case 26:
              op = cff_op_vvcurveto;
              break;
            case 27:
              op = cff_op_hhcurveto;
              break;
            case 29:
              op = cff_op_callgsubr;
              break;
            case 30:
              op = cff_op_vhcurveto;
              break;
            case 31:
              op = cff_op_hvcurveto;
              break;
            default:
              ;
            }
            if ( op == cff_op_unknown )
              goto Syntax_Error;
    
            /* check arguments */
            req_args = cff_argument_counts[op];
            if ( req_args & CFF_COUNT_CHECK_WIDTH )
            {
              args = stack;
    
              if ( num_args > 0 && decoder->read_width )
              {
                /* If `nominal_width' is non-zero, the number is really a      */
                /* difference against `nominal_width'.  Else, the number here  */
                /* is truly a width, not a difference against `nominal_width'. */
                /* If the font does not set `nominal_width', then              */
                /* `nominal_width' defaults to zero, and so we can set         */
                /* `glyph_width' to `nominal_width' plus number on the stack   */
                /* -- for either case.                                         */
    
                FT_Int  set_width_ok;
    
    
                switch ( op )
                {
                case cff_op_hmoveto:
                case cff_op_vmoveto:
                  set_width_ok = num_args & 2;
                  break;
    
                case cff_op_hstem:
                case cff_op_vstem:
                case cff_op_hstemhm:
                case cff_op_vstemhm:
                case cff_op_rmoveto:
                case cff_op_hintmask:
                case cff_op_cntrmask:
                  set_width_ok = num_args & 1;
                  break;
    
                case cff_op_endchar:
                  /* If there is a width specified for endchar, we either have */
                  /* 1 argument or 5 arguments.  We like to argue.             */
                  set_width_ok = ( ( num_args == 5 ) || ( num_args == 1 ) );
                  break;
    
                default:
                  set_width_ok = 0;
                  break;
                }
    
                if ( set_width_ok )
                {
                  decoder->glyph_width = decoder->nominal_width +
                                           ( stack[0] >> 16 );
    
                  /* Consumed an argument. */
                  num_args--;
                  args++;
                }
              }
    
              decoder->read_width = 0;
              req_args            = 0;
            }
    
            req_args &= 15;
            if ( num_args < req_args )
              goto Stack_Underflow;
            args     -= req_args;
            num_args -= req_args;
    
            switch ( op )
            {
            case cff_op_hstem:
            case cff_op_vstem:
            case cff_op_hstemhm:
            case cff_op_vstemhm:
              /* the number of arguments is always even here */
              FT_TRACE4(( op == cff_op_hstem   ? " hstem"   :
                        ( op == cff_op_vstem   ? " vstem"   :
                        ( op == cff_op_hstemhm ? " hstemhm" : " vstemhm" ) ) ));
    
              if ( hinter )
                hinter->stems( hinter->hints,
                               ( op == cff_op_hstem || op == cff_op_hstemhm ),
                               num_args / 2,
                               args );
    
              decoder->num_hints += num_args / 2;
              args = stack;
              break;
    
            case cff_op_hintmask:
            case cff_op_cntrmask:
              FT_TRACE4(( op == cff_op_hintmask ? " hintmask" : " cntrmask" ));
    
              /* implement vstem when needed --                        */
              /* the specification doesn't say it, but this also works */
              /* with the 'cntrmask' operator                          */
              /*                                                       */
              if ( num_args > 0 )
              {
                if ( hinter )
                  hinter->stems( hinter->hints,
                                 0,
                                 num_args / 2,
                                 args );
    
                decoder->num_hints += num_args / 2;
              }
    
              if ( hinter )
              {
                if ( op == cff_op_hintmask )
                  hinter->hintmask( hinter->hints,
                                    builder->current->n_points,
                                    decoder->num_hints,
                                    ip );
                else
                  hinter->counter( hinter->hints,
                                   decoder->num_hints,
                                   ip );
              }
    
    #ifdef FT_DEBUG_LEVEL_TRACE
              {
                FT_UInt maskbyte;
    
    
                FT_TRACE4(( " " ));
    
                for ( maskbyte = 0;
                      maskbyte < (FT_UInt)(( decoder->num_hints + 7 ) >> 3);
                      maskbyte++, ip++ )
                  FT_TRACE4(( "0x%02X", *ip ));
              }
    #else
              ip += ( decoder->num_hints + 7 ) >> 3;
    #endif
              if ( ip >= limit )
                goto Syntax_Error;
              args = stack;
              break;
    
            case cff_op_rmoveto:
              FT_TRACE4(( " rmoveto" ));
    
              cff_builder_close_contour( builder );
              builder->path_begun = 0;
              x   += args[0];
              y   += args[1];
              args = stack;
              break;
    
            case cff_op_vmoveto:
              FT_TRACE4(( " vmoveto" ));
    
              cff_builder_close_contour( builder );
              builder->path_begun = 0;
              y   += args[0];
              args = stack;
              break;
    
            case cff_op_hmoveto:
              FT_TRACE4(( " hmoveto" ));
    
              cff_builder_close_contour( builder );
              builder->path_begun = 0;
              x   += args[0];
              args = stack;
              break;
    
            case cff_op_rlineto:
              FT_TRACE4(( " rlineto" ));
    
              if ( cff_builder_start_point ( builder, x, y ) ||
                   check_points( builder, num_args / 2 )     )
                goto Fail;
    
              if ( num_args < 2 || num_args & 1 )
                goto Stack_Underflow;
    
              args = stack;
              while ( args < decoder->top )
              {
                x += args[0];
                y += args[1];
                cff_builder_add_point( builder, x, y, 1 );
                args += 2;
              }
              args = stack;
              break;
    
            case cff_op_hlineto:
            case cff_op_vlineto:
              {
                FT_Int  phase = ( op == cff_op_hlineto );
    
    
                FT_TRACE4(( op == cff_op_hlineto ? " hlineto"
                                                 : " vlineto" ));
    
                if ( cff_builder_start_point ( builder, x, y ) ||
                     check_points( builder, num_args )         )
                  goto Fail;
    
                args = stack;
                while ( args < decoder->top )
                {
                  if ( phase )
                    x += args[0];
                  else
                    y += args[0];
    
                  if ( cff_builder_add_point1( builder, x, y ) )
                    goto Fail;
    
                  args++;
                  phase ^= 1;
                }
                args = stack;
              }
              break;
    
            case cff_op_rrcurveto:
              FT_TRACE4(( " rrcurveto" ));
    
              /* check number of arguments; must be a multiple of 6 */
              if ( num_args % 6 != 0 )
                goto Stack_Underflow;
    
              if ( cff_builder_start_point ( builder, x, y ) ||
                   check_points( builder, num_args / 2 )     )
                goto Fail;
    
              args = stack;
              while ( args < decoder->top )
              {
                x += args[0];
                y += args[1];
                cff_builder_add_point( builder, x, y, 0 );
                x += args[2];
                y += args[3];
                cff_builder_add_point( builder, x, y, 0 );
                x += args[4];
                y += args[5];
                cff_builder_add_point( builder, x, y, 1 );
                args += 6;
              }
              args = stack;
              break;
    
            case cff_op_vvcurveto:
              FT_TRACE4(( " vvcurveto" ));
    
              if ( cff_builder_start_point( builder, x, y ) )
                goto Fail;
    
              args = stack;
              if ( num_args & 1 )
              {
                x += args[0];
                args++;
                num_args--;
              }
    
              if ( num_args % 4 != 0 )
                goto Stack_Underflow;
    
              if ( check_points( builder, 3 * ( num_args / 4 ) ) )
                goto Fail;
    
              while ( args < decoder->top )
              {
                y += args[0];
                cff_builder_add_point( builder, x, y, 0 );
                x += args[1];
                y += args[2];
                cff_builder_add_point( builder, x, y, 0 );
                y += args[3];
                cff_builder_add_point( builder, x, y, 1 );
                args += 4;
              }
              args = stack;
              break;
    
            case cff_op_hhcurveto:
              FT_TRACE4(( " hhcurveto" ));
    
              if ( cff_builder_start_point( builder, x, y ) )
                goto Fail;
    
              args = stack;
              if ( num_args & 1 )
              {
                y += args[0];
                args++;
                num_args--;
              }
    
              if ( num_args % 4 != 0 )
                goto Stack_Underflow;
    
              if ( check_points( builder, 3 * ( num_args / 4 ) ) )
                goto Fail;
    
              while ( args < decoder->top )
              {
                x += args[0];
                cff_builder_add_point( builder, x, y, 0 );
                x += args[1];
                y += args[2];
                cff_builder_add_point( builder, x, y, 0 );
                x += args[3];
                cff_builder_add_point( builder, x, y, 1 );
                args += 4;
              }
              args = stack;
              break;
    
            case cff_op_vhcurveto:
            case cff_op_hvcurveto:
              {
                FT_Int  phase;
    
    
                FT_TRACE4(( op == cff_op_vhcurveto ? " vhcurveto"
                                                   : " hvcurveto" ));
    
                if ( cff_builder_start_point( builder, x, y ) )
                  goto Fail;
    
                args = stack;
                if ( num_args < 4 || ( num_args % 4 ) > 1 )
                  goto Stack_Underflow;
    
                if ( check_points( builder, ( num_args / 4 ) * 3 ) )
                  goto Stack_Underflow;
    
                phase = ( op == cff_op_hvcurveto );
    
                while ( num_args >= 4 )
                {
                  num_args -= 4;
                  if ( phase )
                  {
                    x += args[0];
                    cff_builder_add_point( builder, x, y, 0 );
                    x += args[1];
                    y += args[2];
                    cff_builder_add_point( builder, x, y, 0 );
                    y += args[3];
                    if ( num_args == 1 )
                      x += args[4];
                    cff_builder_add_point( builder, x, y, 1 );
                  }
                  else
                  {
                    y += args[0];
                    cff_builder_add_point( builder, x, y, 0 );
                    x += args[1];
                    y += args[2];
                    cff_builder_add_point( builder, x, y, 0 );
                    x += args[3];
                    if ( num_args == 1 )
                      y += args[4];
                    cff_builder_add_point( builder, x, y, 1 );
                  }
                  args  += 4;
                  phase ^= 1;
                }
                args = stack;
              }
              break;
    
            case cff_op_rlinecurve:
              {
                FT_Int  num_lines = ( num_args - 6 ) / 2;
    
    
                FT_TRACE4(( " rlinecurve" ));
    
                if ( num_args < 8 || ( num_args - 6 ) & 1 )
                  goto Stack_Underflow;
    
                if ( cff_builder_start_point( builder, x, y ) ||
                     check_points( builder, num_lines + 3 )   )
                  goto Fail;
    
                args = stack;
    
                /* first, add the line segments */
                while ( num_lines > 0 )
                {
                  x += args[0];
                  y += args[1];
                  cff_builder_add_point( builder, x, y, 1 );
                  args += 2;
                  num_lines--;
                }
    
                /* then the curve */
                x += args[0];
                y += args[1];
                cff_builder_add_point( builder, x, y, 0 );
                x += args[2];
                y += args[3];
                cff_builder_add_point( builder, x, y, 0 );
                x += args[4];
                y += args[5];
                cff_builder_add_point( builder, x, y, 1 );
                args = stack;
              }
              break;
    
            case cff_op_rcurveline:
              {
                FT_Int  num_curves = ( num_args - 2 ) / 6;
    
    
                FT_TRACE4(( " rcurveline" ));
    
                if ( num_args < 8 || ( num_args - 2 ) % 6 )
                  goto Stack_Underflow;
    
                if ( cff_builder_start_point ( builder, x, y ) ||
                     check_points( builder, num_curves*3 + 2 ) )
                  goto Fail;
    
                args = stack;
    
                /* first, add the curves */
                while ( num_curves > 0 )
                {
                  x += args[0];
                  y += args[1];
                  cff_builder_add_point( builder, x, y, 0 );
                  x += args[2];
                  y += args[3];
                  cff_builder_add_point( builder, x, y, 0 );
                  x += args[4];
                  y += args[5];
                  cff_builder_add_point( builder, x, y, 1 );
                  args += 6;
                  num_curves--;
                }
    
                /* then the final line */
                x += args[0];
                y += args[1];
                cff_builder_add_point( builder, x, y, 1 );
                args = stack;
              }
              break;
    
            case cff_op_hflex1:
              {
                FT_Pos start_y;
    
    
                FT_TRACE4(( " hflex1" ));
    
                args = stack;
    
                /* adding five more points; 4 control points, 1 on-curve point */
                /* make sure we have enough space for the start point if it    */
                /* needs to be added                                           */
                if ( cff_builder_start_point( builder, x, y ) ||
                     check_points( builder, 6 )               )
                  goto Fail;
    
                /* Record the starting point's y postion for later use */
                start_y = y;
    
                /* first control point */
                x += args[0];
                y += args[1];
                cff_builder_add_point( builder, x, y, 0 );
    
                /* second control point */
                x += args[2];
                y += args[3];
                cff_builder_add_point( builder, x, y, 0 );
    
                /* join point; on curve, with y-value the same as the last */
                /* control point's y-value                                 */
                x += args[4];
                cff_builder_add_point( builder, x, y, 1 );
    
                /* third control point, with y-value the same as the join */
                /* point's y-value                                        */
                x += args[5];
                cff_builder_add_point( builder, x, y, 0 );
    
                /* fourth control point */
                x += args[6];
                y += args[7];
                cff_builder_add_point( builder, x, y, 0 );
    
                /* ending point, with y-value the same as the start   */
                x += args[8];
                y  = start_y;
                cff_builder_add_point( builder, x, y, 1 );
    
                args = stack;
                break;
              }
    
            case cff_op_hflex:
              {
                FT_Pos start_y;
    
    
                FT_TRACE4(( " hflex" ));
    
                args = stack;
    
                /* adding six more points; 4 control points, 2 on-curve points */
                if ( cff_builder_start_point( builder, x, y ) ||
                     check_points( builder, 6 )               )
                  goto Fail;
    
                /* record the starting point's y-position for later use */
                start_y = y;
    
                /* first control point */
                x += args[0];
                cff_builder_add_point( builder, x, y, 0 );
    
                /* second control point */
                x += args[1];
                y += args[2];
                cff_builder_add_point( builder, x, y, 0 );
    
                /* join point; on curve, with y-value the same as the last */
                /* control point's y-value                                 */
                x += args[3];
                cff_builder_add_point( builder, x, y, 1 );
    
                /* third control point, with y-value the same as the join */
                /* point's y-value                                        */
                x += args[4];
                cff_builder_add_point( builder, x, y, 0 );
    
                /* fourth control point */
                x += args[5];
                y  = start_y;
                cff_builder_add_point( builder, x, y, 0 );
    
                /* ending point, with y-value the same as the start point's */
                /* y-value -- we don't add this point, though               */
                x += args[6];
                cff_builder_add_point( builder, x, y, 1 );
    
                args = stack;
                break;
              }
    
            case cff_op_flex1:
              {
                FT_Pos    start_x, start_y; /* record start x, y values for */
                                            /* alter use                                */
                FT_Fixed  dx = 0, dy = 0;   /* used in horizontal/vertical  */
                                            /* algorithm below              */
                FT_Int    horizontal, count;
    
    
                FT_TRACE4(( " flex1" ));
    
                /* adding six more points; 4 control points, 2 on-curve points */
                if ( cff_builder_start_point( builder, x, y ) ||
                     check_points( builder, 6 )               )
                  goto Fail;
    
                /* record the starting point's x, y postion for later use */
                start_x = x;
                start_y = y;
    
                /* XXX: figure out whether this is supposed to be a horizontal */
                /*      or vertical flex; the Type 2 specification is vague... */
    
                args = stack;
    
                /* grab up to the last argument */
                for ( count = 5; count > 0; count-- )
                {
                  dx += args[0];
                  dy += args[1];
                  args += 2;
                }
    
                /* rewind */
                args = stack;
    
                if ( dx < 0 ) dx = -dx;
                if ( dy < 0 ) dy = -dy;
    
                /* strange test, but here it is... */
                horizontal = ( dx > dy );
    
                for ( count = 5; count > 0; count-- )
                {
                  x += args[0];
                  y += args[1];
                  cff_builder_add_point( builder, x, y, (FT_Bool)( count == 3 ) );
                  args += 2;
                }
    
                /* is last operand an x- or y-delta? */
                if ( horizontal )
                {
                  x += args[0];
                  y  = start_y;
                }
                else
                {
                  x  = start_x;
                  y += args[0];
                }
    
                cff_builder_add_point( builder, x, y, 1 );
    
                args = stack;
                break;
               }
    
            case cff_op_flex:
              {
                FT_UInt  count;
    
    
                FT_TRACE4(( " flex" ));
    
                if ( cff_builder_start_point( builder, x, y ) ||
                     check_points( builder, 6 )               )
                  goto Fail;
    
                args = stack;
                for ( count = 6; count > 0; count-- )
                {
                  x += args[0];
                  y += args[1];
                  cff_builder_add_point( builder, x, y,
                                         (FT_Bool)( count == 4 || count == 1 ) );
                  args += 2;
                }
    
                args = stack;
              }
              break;
    
            case cff_op_endchar:
              FT_TRACE4(( " endchar" ));
    
              /* We are going to emulate the seac operator. */
              if ( num_args == 4 )
              {
                /* Save glyph width so that the subglyphs don't overwrite it. */
                FT_Pos  glyph_width = decoder->glyph_width;
    
    
                error = cff_operator_seac( decoder,
                                           args[0],
                                           args[1],
                                           (FT_Int)( args[2] >> 16 ),
                                           (FT_Int)( args[3] >> 16 ) );
                args += 4;
    
                decoder->glyph_width = glyph_width;
              }
              else
              {
                if ( !error )
                  error = CFF_Err_Ok;
    
                cff_builder_close_contour( builder );
    
                /* close hints recording session */
                if ( hinter )
                {
                  if ( hinter->close( hinter->hints,
                                      builder->current->n_points ) )
                    goto Syntax_Error;
    
                  /* apply hints to the loaded glyph outline now */
                  hinter->apply( hinter->hints,
                                 builder->current,
                                 (PSH_Globals)builder->hints_globals,
                                 decoder->hint_mode );
                }
    
                /* add current outline to the glyph slot */
                FT_GlyphLoader_Add( builder->loader );
              }
    
              /* return now! */
              FT_TRACE4(( "\n\n" ));
              return error;
    
            case cff_op_abs:
              FT_TRACE4(( " abs" ));
    
              if ( args[0] < 0 )
                args[0] = -args[0];
              args++;
              break;
    
            case cff_op_add:
              FT_TRACE4(( " add" ));
    
              args[0] += args[1];
              args++;
              break;
    
            case cff_op_sub:
              FT_TRACE4(( " sub" ));
    
              args[0] -= args[1];
              args++;
              break;
    
            case cff_op_div:
              FT_TRACE4(( " div" ));
    
              args[0] = FT_DivFix( args[0], args[1] );
              args++;
              break;
    
            case cff_op_neg:
              FT_TRACE4(( " neg" ));
    
              args[0] = -args[0];
              args++;
              break;
    
            case cff_op_random:
              {
                FT_Fixed  Rand;
    
    
                FT_TRACE4(( " rand" ));
    
                Rand = seed;
                if ( Rand >= 0x8000L )
                  Rand++;
    
                args[0] = Rand;
                seed    = FT_MulFix( seed, 0x10000L - seed );
                if ( seed == 0 )
                  seed += 0x2873;
                args++;
              }
              break;
    
            case cff_op_mul:
              FT_TRACE4(( " mul" ));
    
              args[0] = FT_MulFix( args[0], args[1] );
              args++;
              break;
    
            case cff_op_sqrt:
              FT_TRACE4(( " sqrt" ));
    
              if ( args[0] > 0 )
              {
                FT_Int    count = 9;
                FT_Fixed  root  = args[0];
                FT_Fixed  new_root;
    
    
                for (;;)
                {
                  new_root = ( root + FT_DivFix( args[0], root ) + 1 ) >> 1;
                  if ( new_root == root || count <= 0 )
                    break;
                  root = new_root;
                }
                args[0] = new_root;
              }
              else
                args[0] = 0;
              args++;
              break;
    
            case cff_op_drop:
              /* nothing */
              FT_TRACE4(( " drop" ));
    
              break;
    
            case cff_op_exch:
              {
                FT_Fixed  tmp;
    
    
                FT_TRACE4(( " exch" ));
    
                tmp     = args[0];
                args[0] = args[1];
                args[1] = tmp;
                args   += 2;
              }
              break;
    
            case cff_op_index:
              {
                FT_Int  idx = (FT_Int)( args[0] >> 16 );
    
    
                FT_TRACE4(( " index" ));
    
                if ( idx < 0 )
                  idx = 0;
                else if ( idx > num_args - 2 )
                  idx = num_args - 2;
                args[0] = args[-( idx + 1 )];
                args++;
              }
              break;
    
            case cff_op_roll:
              {
                FT_Int  count = (FT_Int)( args[0] >> 16 );
                FT_Int  idx   = (FT_Int)( args[1] >> 16 );
    
    
                FT_TRACE4(( " roll" ));
    
                if ( count <= 0 )
                  count = 1;
    
                args -= count;
                if ( args < stack )
                  goto Stack_Underflow;
    
                if ( idx >= 0 )
                {
                  while ( idx > 0 )
                  {
                    FT_Fixed  tmp = args[count - 1];
                    FT_Int    i;
    
    
                    for ( i = count - 2; i >= 0; i-- )
                      args[i + 1] = args[i];
                    args[0] = tmp;
                    idx--;
                  }
                }
                else
                {
                  while ( idx < 0 )
                  {
                    FT_Fixed  tmp = args[0];
                    FT_Int    i;
    
    
                    for ( i = 0; i < count - 1; i++ )
                      args[i] = args[i + 1];
                    args[count - 1] = tmp;
                    idx++;
                  }
                }
                args += count;
              }
              break;
    
            case cff_op_dup:
              FT_TRACE4(( " dup" ));
    
              args[1] = args[0];
              args++;
              break;
    
            case cff_op_put:
              {
                FT_Fixed  val = args[0];
                FT_Int    idx = (FT_Int)( args[1] >> 16 );
    
    
                FT_TRACE4(( " put" ));
    
                if ( idx >= 0 && idx < decoder->len_buildchar )
                  decoder->buildchar[idx] = val;
              }
              break;
    
            case cff_op_get:
              {
                FT_Int    idx = (FT_Int)( args[0] >> 16 );
                FT_Fixed  val = 0;
    
    
                FT_TRACE4(( " get" ));
    
                if ( idx >= 0 && idx < decoder->len_buildchar )
                  val = decoder->buildchar[idx];
    
                args[0] = val;
                args++;
              }
              break;
    
            case cff_op_store:
              FT_TRACE4(( " store "));
    
              goto Unimplemented;
    
            case cff_op_load:
              FT_TRACE4(( " load" ));
    
              goto Unimplemented;
    
            case cff_op_dotsection:
              /* this operator is deprecated and ignored by the parser */
              FT_TRACE4(( " dotsection" ));
              break;
    
            case cff_op_and:
              {
                FT_Fixed  cond = args[0] && args[1];
    
    
                FT_TRACE4(( " and" ));
    
                args[0] = cond ? 0x10000L : 0;
                args++;
              }
              break;
    
            case cff_op_or:
              {
                FT_Fixed  cond = args[0] || args[1];
    
    
                FT_TRACE4(( " or" ));
    
                args[0] = cond ? 0x10000L : 0;
                args++;
              }
              break;
    
            case cff_op_eq:
              {
                FT_Fixed  cond = !args[0];
    
    
                FT_TRACE4(( " eq" ));
    
                args[0] = cond ? 0x10000L : 0;
                args++;
              }
              break;
    
            case cff_op_ifelse:
              {
                FT_Fixed  cond = ( args[2] <= args[3] );
    
    
                FT_TRACE4(( " ifelse" ));
    
                if ( !cond )
                  args[0] = args[1];
                args++;
              }
              break;
    
            case cff_op_callsubr:
              {
                FT_UInt  idx = (FT_UInt)( ( args[0] >> 16 ) +
                                          decoder->locals_bias );
    
    
                FT_TRACE4(( " callsubr(%d)", idx ));
    
                if ( idx >= decoder->num_locals )
                {
                  FT_ERROR(( "cff_decoder_parse_charstrings:" ));
                  FT_ERROR(( "  invalid local subr index\n" ));
                  goto Syntax_Error;
                }
    
                if ( zone - decoder->zones >= CFF_MAX_SUBRS_CALLS )
                {
                  FT_ERROR(( "cff_decoder_parse_charstrings:"
                             " too many nested subrs\n" ));
                  goto Syntax_Error;
                }
    
                zone->cursor = ip;  /* save current instruction pointer */
    
                zone++;
                zone->base   = decoder->locals[idx];
                zone->limit  = decoder->locals[idx + 1];
                zone->cursor = zone->base;
    
                if ( !zone->base )
                {
                  FT_ERROR(( "cff_decoder_parse_charstrings:"
                             " invoking empty subrs!\n" ));
                  goto Syntax_Error;
                }
    
                decoder->zone = zone;
                ip            = zone->base;
                limit         = zone->limit;
              }
              break;
    
            case cff_op_callgsubr:
              {
                FT_UInt  idx = (FT_UInt)( ( args[0] >> 16 ) +
                                          decoder->globals_bias );
    
    
                FT_TRACE4(( " callgsubr(%d)", idx ));
    
                if ( idx >= decoder->num_globals )
                {
                  FT_ERROR(( "cff_decoder_parse_charstrings:" ));
                  FT_ERROR(( " invalid global subr index\n" ));
                  goto Syntax_Error;
                }
    
                if ( zone - decoder->zones >= CFF_MAX_SUBRS_CALLS )
                {
                  FT_ERROR(( "cff_decoder_parse_charstrings:"
                             " too many nested subrs\n" ));
                  goto Syntax_Error;
                }
    
                zone->cursor = ip;  /* save current instruction pointer */
    
                zone++;
                zone->base   = decoder->globals[idx];
                zone->limit  = decoder->globals[idx + 1];
                zone->cursor = zone->base;
    
                if ( !zone->base )
                {
                  FT_ERROR(( "cff_decoder_parse_charstrings:"
                             " invoking empty subrs!\n" ));
                  goto Syntax_Error;
                }
    
                decoder->zone = zone;
                ip            = zone->base;
                limit         = zone->limit;
              }
              break;
    
            case cff_op_return:
              FT_TRACE4(( " return" ));
    
              if ( decoder->zone <= decoder->zones )
              {
                FT_ERROR(( "cff_decoder_parse_charstrings:"
                           " unexpected return\n" ));
                goto Syntax_Error;
              }
    
              decoder->zone--;
              zone  = decoder->zone;
              ip    = zone->cursor;
              limit = zone->limit;
              break;
    
            default:
            Unimplemented:
              FT_ERROR(( "Unimplemented opcode: %d", ip[-1] ));
    
              if ( ip[-1] == 12 )
                FT_ERROR(( " %d", ip[0] ));
              FT_ERROR(( "\n" ));
    
              return CFF_Err_Unimplemented_Feature;
            }
    
          decoder->top = args;
    
          } /* general operator processing */
    
        } /* while ip < limit */
    
        FT_TRACE4(( "..end..\n\n" ));
    
      Fail:
        return error;
    
      Syntax_Error:
        FT_TRACE4(( "cff_decoder_parse_charstrings: syntax error!" ));
        return CFF_Err_Invalid_File_Format;
    
      Stack_Underflow:
        FT_TRACE4(( "cff_decoder_parse_charstrings: stack underflow!" ));
        return CFF_Err_Too_Few_Arguments;
    
      Stack_Overflow:
        FT_TRACE4(( "cff_decoder_parse_charstrings: stack overflow!" ));
        return CFF_Err_Stack_Overflow;
      }
    
    
      /*************************************************************************/
      /*************************************************************************/
      /*************************************************************************/
      /**********                                                      *********/
      /**********                                                      *********/
      /**********            COMPUTE THE MAXIMUM ADVANCE WIDTH         *********/
      /**********                                                      *********/
      /**********    The following code is in charge of computing      *********/
      /**********    the maximum advance width of the font.  It        *********/
      /**********    quickly processes each glyph charstring to        *********/
      /**********    extract the value from either a `sbw' or `seac'   *********/
      /**********    operator.                                         *********/
      /**********                                                      *********/
      /*************************************************************************/
      /*************************************************************************/
      /*************************************************************************/
    
    
    #if 0 /* unused until we support pure CFF fonts */
    
    
      FT_LOCAL_DEF( FT_Error )
      cff_compute_max_advance( TT_Face  face,
                               FT_Int*  max_advance )
      {
        FT_Error     error = CFF_Err_Ok;
        CFF_Decoder  decoder;
        FT_Int       glyph_index;
        CFF_Font     cff = (CFF_Font)face->other;
    
    
        *max_advance = 0;
    
        /* Initialize load decoder */
        cff_decoder_init( &decoder, face, 0, 0, 0, 0 );
    
        decoder.builder.metrics_only = 1;
        decoder.builder.load_points  = 0;
    
        /* For each glyph, parse the glyph charstring and extract */
        /* the advance width.                                     */
        for ( glyph_index = 0; glyph_index < face->root.num_glyphs;
              glyph_index++ )
        {
          FT_Byte*  charstring;
          FT_ULong  charstring_len;
    
    
          /* now get load the unscaled outline */
          error = cff_get_glyph_data( face, glyph_index,
                                      &charstring, &charstring_len );
          if ( !error )
          {
            cff_decoder_prepare( &decoder, glyph_index );
            error = cff_decoder_parse_charstrings( &decoder,
                                                   charstring, charstring_len );
    
            cff_free_glyph_data( face, &charstring, &charstring_len );
          }
    
          /* ignore the error if one has occurred -- skip to next glyph */
          error = CFF_Err_Ok;
        }
    
        *max_advance = decoder.builder.advance.x;
    
        return CFF_Err_Ok;
      }
    
    
    #endif /* 0 */
    
    
      FT_LOCAL_DEF( FT_Error )
      cff_slot_load( CFF_GlyphSlot  glyph,
                     CFF_Size       size,
                     FT_Int         glyph_index,
                     FT_Int32       load_flags )
      {
        FT_Error      error;
        CFF_Decoder   decoder;
        TT_Face       face     = (TT_Face)glyph->root.face;
        FT_Bool       hinting;
        CFF_Font      cff      = (CFF_Font)face->extra.data;
    
        FT_Matrix     font_matrix;
        FT_Vector     font_offset;
    
    
        if ( load_flags & FT_LOAD_NO_RECURSE )
          load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING;
    
        glyph->x_scale = 0x10000L;
        glyph->y_scale = 0x10000L;
        if ( size )
        {
          glyph->x_scale = size->root.metrics.x_scale;
          glyph->y_scale = size->root.metrics.y_scale;
        }
    
    #ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
    
        /* try to load embedded bitmap if any              */
        /*                                                 */
        /* XXX: The convention should be emphasized in     */
        /*      the documents because it can be confusing. */
        if ( size )
        {
          CFF_Face      cff_face = (CFF_Face)size->root.face;
          SFNT_Service  sfnt     = (SFNT_Service)cff_face->sfnt;
          FT_Stream     stream   = cff_face->root.stream;
    
    
          if ( size->strike_index != 0xFFFFFFFFUL      &&
               sfnt->load_eblc                         &&
               ( load_flags & FT_LOAD_NO_BITMAP ) == 0 )
          {
            TT_SBit_MetricsRec  metrics;
    
    
            error = sfnt->load_sbit_image( face,
                                           size->strike_index,
                                           (FT_UInt)glyph_index,
                                           (FT_Int)load_flags,
                                           stream,
                                           &glyph->root.bitmap,
                                           &metrics );
    
            if ( !error )
            {
              glyph->root.outline.n_points   = 0;
              glyph->root.outline.n_contours = 0;
    
              glyph->root.metrics.width  = (FT_Pos)metrics.width  << 6;
              glyph->root.metrics.height = (FT_Pos)metrics.height << 6;
    
              glyph->root.metrics.horiBearingX = (FT_Pos)metrics.horiBearingX << 6;
              glyph->root.metrics.horiBearingY = (FT_Pos)metrics.horiBearingY << 6;
              glyph->root.metrics.horiAdvance  = (FT_Pos)metrics.horiAdvance  << 6;
    
              glyph->root.metrics.vertBearingX = (FT_Pos)metrics.vertBearingX << 6;
              glyph->root.metrics.vertBearingY = (FT_Pos)metrics.vertBearingY << 6;
              glyph->root.metrics.vertAdvance  = (FT_Pos)metrics.vertAdvance  << 6;
    
              glyph->root.format = FT_GLYPH_FORMAT_BITMAP;
    
              if ( load_flags & FT_LOAD_VERTICAL_LAYOUT )
              {
                glyph->root.bitmap_left = metrics.vertBearingX;
                glyph->root.bitmap_top  = metrics.vertBearingY;
              }
              else
              {
                glyph->root.bitmap_left = metrics.horiBearingX;
                glyph->root.bitmap_top  = metrics.horiBearingY;
              }
              return error;
            }
          }
        }
    
    #endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */
    
        /* return immediately if we only want the embedded bitmaps */
        if ( load_flags & FT_LOAD_SBITS_ONLY )
          return CFF_Err_Invalid_Argument;
    
        glyph->root.outline.n_points   = 0;
        glyph->root.outline.n_contours = 0;
    
        hinting = FT_BOOL( ( load_flags & FT_LOAD_NO_SCALE   ) == 0 &&
                           ( load_flags & FT_LOAD_NO_HINTING ) == 0 );
    
        glyph->root.format = FT_GLYPH_FORMAT_OUTLINE;  /* by default */
    
        {
          FT_Byte*  charstring;
          FT_ULong  charstring_len;
    
    
          /* in a CID-keyed font, consider `glyph_index' as a CID and map */
          /* it immediately to the real glyph_index -- if it isn't a      */
          /* subsetted font, glyph_indices and CIDs are identical, though */
          if ( cff->top_font.font_dict.cid_registry != 0xFFFFU &&
               cff->charset.cids )
            glyph_index = cff->charset.cids[glyph_index];
    
          cff_decoder_init( &decoder, face, size, glyph, hinting,
                            FT_LOAD_TARGET_MODE( load_flags ) );
    
          decoder.builder.no_recurse =
            (FT_Bool)( ( load_flags & FT_LOAD_NO_RECURSE ) != 0 );
    
          /* now load the unscaled outline */
          error = cff_get_glyph_data( face, glyph_index,
                                      &charstring, &charstring_len );
          if ( !error )
          {
            cff_decoder_prepare( &decoder, glyph_index );
            error = cff_decoder_parse_charstrings( &decoder,
                                                   charstring, charstring_len );
    
            cff_free_glyph_data( face, &charstring, charstring_len );
    
    
    #ifdef FT_CONFIG_OPTION_INCREMENTAL
            /* Control data and length may not be available for incremental   */
            /* fonts.                                                         */
            if ( face->root.internal->incremental_interface )
            {
              glyph->root.control_data = 0;
              glyph->root.control_len = 0;
            }
            else
    #endif /* FT_CONFIG_OPTION_INCREMENTAL */
    
            /* We set control_data and control_len if charstrings is loaded.  */
            /* See how charstring loads at cff_index_access_element() in      */
            /* cffload.c.                                                     */
            {
              CFF_IndexRec csindex = cff->charstrings_index;
    
    
              glyph->root.control_data =
                csindex.bytes + csindex.offsets[glyph_index] - 1;
              glyph->root.control_len =
                charstring_len;
            }
          }
    
          /* save new glyph tables */
          cff_builder_done( &decoder.builder );
        }
    
    #ifdef FT_CONFIG_OPTION_INCREMENTAL
    
        /* Incremental fonts can optionally override the metrics. */
        if ( !error                                                              &&
             face->root.internal->incremental_interface                          &&
             face->root.internal->incremental_interface->funcs->get_glyph_metrics )
        {
          FT_Incremental_MetricsRec  metrics;
    
    
          metrics.bearing_x = decoder.builder.left_bearing.x;
          metrics.bearing_y = decoder.builder.left_bearing.y;
          metrics.advance   = decoder.builder.advance.x;
          error = face->root.internal->incremental_interface->funcs->get_glyph_metrics(
                    face->root.internal->incremental_interface->object,
                    glyph_index, FALSE, &metrics );
          decoder.builder.left_bearing.x = metrics.bearing_x;
          decoder.builder.left_bearing.y = metrics.bearing_y;
          decoder.builder.advance.x      = metrics.advance;
          decoder.builder.advance.y      = 0;
        }
    
    #endif /* FT_CONFIG_OPTION_INCREMENTAL */
    
        if ( cff->num_subfonts >= 1 )
        {
          FT_Byte  fd_index = cff_fd_select_get( &cff->fd_select, glyph_index );
    
    
          font_matrix = cff->subfonts[fd_index]->font_dict.font_matrix;
          font_offset = cff->subfonts[fd_index]->font_dict.font_offset;
        }
        else
        {
          font_matrix = cff->top_font.font_dict.font_matrix;
          font_offset = cff->top_font.font_dict.font_offset;
        }
    
        /* Now, set the metrics -- this is rather simple, as   */
        /* the left side bearing is the xMin, and the top side */
        /* bearing the yMax.                                   */
        if ( !error )
        {
          /* For composite glyphs, return only left side bearing and */
          /* advance width.                                          */
          if ( load_flags & FT_LOAD_NO_RECURSE )
          {
            FT_Slot_Internal  internal = glyph->root.internal;
    
    
            glyph->root.metrics.horiBearingX = decoder.builder.left_bearing.x;
            glyph->root.metrics.horiAdvance  = decoder.glyph_width;
            internal->glyph_matrix           = font_matrix;
            internal->glyph_delta            = font_offset;
            internal->glyph_transformed      = 1;
          }
          else
          {
            FT_BBox            cbox;
            FT_Glyph_Metrics*  metrics = &glyph->root.metrics;
            FT_Vector          advance;
            FT_Bool            has_vertical_info;
    
    
            /* copy the _unscaled_ advance width */
            metrics->horiAdvance                    = decoder.glyph_width;
            glyph->root.linearHoriAdvance           = decoder.glyph_width;
            glyph->root.internal->glyph_transformed = 0;
    
            has_vertical_info = FT_BOOL( face->vertical_info                   &&
                                         face->vertical.number_Of_VMetrics > 0 &&
                                         face->vertical.long_metrics != 0 );
    
            /* get the vertical metrics from the vtmx table if we have one */
            if ( has_vertical_info )
            {
              FT_Short   vertBearingY = 0;
              FT_UShort  vertAdvance  = 0;
    
    
              ( (SFNT_Service)face->sfnt )->get_metrics( face, 1,
                                                         glyph_index,
                                                         &vertBearingY,
                                                         &vertAdvance );
              metrics->vertBearingY = vertBearingY;
              metrics->vertAdvance  = vertAdvance;
            }
            else
            {
              /* make up vertical ones */
              if ( face->os2.version != 0xFFFFU )
                metrics->vertAdvance = (FT_Pos)( face->os2.sTypoAscender -
                                                 face->os2.sTypoDescender );
              else
                metrics->vertAdvance = (FT_Pos)( face->horizontal.Ascender -
                                                 face->horizontal.Descender );
            }
    
            glyph->root.linearVertAdvance = metrics->vertAdvance;
    
            glyph->root.format = FT_GLYPH_FORMAT_OUTLINE;
    
            glyph->root.outline.flags = 0;
            if ( size && size->root.metrics.y_ppem < 24 )
              glyph->root.outline.flags |= FT_OUTLINE_HIGH_PRECISION;
    
            glyph->root.outline.flags |= FT_OUTLINE_REVERSE_FILL;
    
            /* apply the font matrix */
            FT_Outline_Transform( &glyph->root.outline, &font_matrix );
    
            FT_Outline_Translate( &glyph->root.outline,
                                  font_offset.x,
                                  font_offset.y );
    
            advance.x = metrics->horiAdvance;
            advance.y = 0;
            FT_Vector_Transform( &advance, &font_matrix );
            metrics->horiAdvance = advance.x + font_offset.x;
    
            advance.x = 0;
            advance.y = metrics->vertAdvance;
            FT_Vector_Transform( &advance, &font_matrix );
            metrics->vertAdvance = advance.y + font_offset.y;
    
            if ( ( load_flags & FT_LOAD_NO_SCALE ) == 0 )
            {
              /* scale the outline and the metrics */
              FT_Int       n;
              FT_Outline*  cur     = &glyph->root.outline;
              FT_Vector*   vec     = cur->points;
              FT_Fixed     x_scale = glyph->x_scale;
              FT_Fixed     y_scale = glyph->y_scale;
    
    
              /* First of all, scale the points */
              if ( !hinting || !decoder.builder.hints_funcs )
                for ( n = cur->n_points; n > 0; n--, vec++ )
                {
                  vec->x = FT_MulFix( vec->x, x_scale );
                  vec->y = FT_MulFix( vec->y, y_scale );
                }
    
              /* Then scale the metrics */
              metrics->horiAdvance = FT_MulFix( metrics->horiAdvance, x_scale );
              metrics->vertAdvance = FT_MulFix( metrics->vertAdvance, y_scale );
            }
    
            /* compute the other metrics */
            FT_Outline_Get_CBox( &glyph->root.outline, &cbox );
    
            metrics->width  = cbox.xMax - cbox.xMin;
            metrics->height = cbox.yMax - cbox.yMin;
    
            metrics->horiBearingX = cbox.xMin;
            metrics->horiBearingY = cbox.yMax;
    
            if ( has_vertical_info )
              metrics->vertBearingX = -metrics->width / 2;
            else
              ft_synthesize_vertical_metrics( metrics,
                                              metrics->vertAdvance );
          }
        }
    
        return error;
      }
    
    
    /* END */