Edit

kc3-lang/freetype/src/psaux/psft.c

Branch :

  • Show log

    Commit

  • Author : Skef Iterum
    Date : 2023-12-14 06:59:05
    Hash : 8eab5110
    Message : [CFF] Extract `BlueValues` as `Fixed` rather than `Int`. This is a follow-up to commit 26a7f047, [cff] Make blend operator work with floats in private dicts. which addressed the 'party baseline' bug. However, the reporting user indicated that the default location and some other points in design space rendered OK, but other points in design space still had problems. The most obvious issue being that the x-heights of lower-case letters did not align; see https://github.com/adobe-fonts/source-serif/issues/121#issuecomment-1773794136 After some analysis we determined that this was due to an interaction between `BlueValue` rounding and the zone-based algorithm. In short, for a point to be considered in a zone it must fall within the bounds of the zone. (There is a slop factor in some cases, but only a very small one.) In the Adobe-contributed side of the code, point values are not integer-rounded, instead they're kept as (some form of) fixed. Rounding just the `BlueValues` means that points that need to be considered within a zone will fall outside of it at some points in design space. The majority of this patch changes the storage and parsing of `BlueValues` to keep them as `FT_Fixed`. No significant code changes were needed because the values are converted to `Fixed` anyway when stored in `CF_BlueRec`. No attempt was made to address problems in the older pshinter code beyond converting the values from `FT_Fixed` to `FT_Short` when copying the private dictionary. (However, as the point values are also rounded in that code, the problem is much less likely to occur, although inconsistency between rounding and truncation could cause an analogous problem.) * include/freetype/internal/cfftypes.h (CFF_PrivateRec): Use `FT_Fixed` for `blue_values`, `other_blues`, `family_blues`, and `family_other_blues`. * src/cff/cffload.c (cff_blend_doBlend): Updated. * src/cff/cffobjs.c (CFF_fixedToInt): New macro. (cff_make_private_dict): Use it. * src/cff/cffparse.h (cff_kind_delta_fixed): New enum value. * src/cff/cffparse.c (do_fixed): Updated. (CFF_FIELD_DELTA, CFF_FIELD_DELTA_FIXED, CFF_DELTA_KIND): New set of macros, replacing `CFF_FIELD_DELTA`. (cff_parser_run): Updated to handle fixed-float deltas. * src/cff/cfftoken.h: Updated to use `CFF_FIELD_DELTA_FIXED` for blue values. * src/psaux/psblues.c (cf2_blueToFixed): Removed, no longer needed. (cf2_blues_init): Updated. * src/pxaux/psft.c, src/pxaux/psft.h (cf2_getBlueValues, cf2_getOtherBlues, cf2_getFamilyBlues, cf2_getFamilyOtherBlues): Updated signatures. * src/psaux/psobjs.c (t1_make_subfont): Updated.

  • src/psaux/psft.c
  • /****************************************************************************
     *
     * psft.c
     *
     *   FreeType Glue Component to Adobe's Interpreter (body).
     *
     * Copyright 2013-2014 Adobe Systems Incorporated.
     *
     * This software, and all works of authorship, whether in source or
     * object code form as indicated by the copyright notice(s) included
     * herein (collectively, the "Work") is made available, and may only be
     * used, modified, and distributed under the FreeType Project License,
     * LICENSE.TXT.  Additionally, subject to the terms and conditions of the
     * FreeType Project License, each contributor to the Work hereby grants
     * to any individual or legal entity exercising permissions granted by
     * the FreeType Project License and this section (hereafter, "You" or
     * "Your") a perpetual, worldwide, non-exclusive, no-charge,
     * royalty-free, irrevocable (except as stated in this section) patent
     * license to make, have made, use, offer to sell, sell, import, and
     * otherwise transfer the Work, where such license applies only to those
     * patent claims licensable by such contributor that are necessarily
     * infringed by their contribution(s) alone or by combination of their
     * contribution(s) with the Work to which such contribution(s) was
     * submitted.  If You institute patent litigation against any entity
     * (including a cross-claim or counterclaim in a lawsuit) alleging that
     * the Work or a contribution incorporated within the Work constitutes
     * direct or contributory patent infringement, then any patent licenses
     * granted to You under this License for that Work shall terminate as of
     * the date such litigation is filed.
     *
     * By using, modifying, or distributing the Work you indicate that you
     * have read and understood the terms and conditions of the
     * FreeType Project License as well as those provided in this section,
     * and you accept them fully.
     *
     */
    
    
    #include "psft.h"
    #include <freetype/internal/ftdebug.h>
    
    #include "psfont.h"
    #include "pserror.h"
    #include "psobjs.h"
    #include "cffdecode.h"
    
    #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
    #include <freetype/ftmm.h>
    #include <freetype/internal/services/svmm.h>
    #endif
    
    #include <freetype/internal/services/svcfftl.h>
    
    
    #define CF2_MAX_SIZE  cf2_intToFixed( 2000 )    /* max ppem */
    
    
      /*
       * This check should avoid most internal overflow cases.  Clients should
       * generally respond to `Glyph_Too_Big' by getting a glyph outline
       * at EM size, scaling it and filling it as a graphics operation.
       *
       */
      static FT_Error
      cf2_checkTransform( const CF2_Matrix*  transform,
                          CF2_Int            unitsPerEm )
      {
        CF2_Fixed  maxScale;
    
    
        if ( transform->a <= 0 || transform->d <= 0 )
          return FT_THROW( Invalid_Size_Handle );
    
        FT_ASSERT( unitsPerEm > 0 );
        FT_ASSERT( transform->b == 0 && transform->c == 0 );
        FT_ASSERT( transform->tx == 0 && transform->ty == 0 );
    
        if ( unitsPerEm > 0x7FFF )
          return FT_THROW( Glyph_Too_Big );
    
        maxScale = FT_DivFix( CF2_MAX_SIZE, cf2_intToFixed( unitsPerEm ) );
    
        if ( transform->a > maxScale || transform->d > maxScale )
          return FT_THROW( Glyph_Too_Big );
    
        return FT_Err_Ok;
      }
    
    
      static void
      cf2_setGlyphWidth( CF2_Outline  outline,
                         CF2_Fixed    width )
      {
        PS_Decoder*  decoder = outline->decoder;
    
    
        FT_ASSERT( decoder );
    
        if ( !decoder->builder.is_t1 )
          *decoder->glyph_width = cf2_fixedToInt( width );
      }
    
    
      /* Clean up font instance. */
      static void
      cf2_free_instance( void*  ptr )
      {
        CF2_Font  font = (CF2_Font)ptr;
    
    
        if ( font )
        {
          FT_Memory  memory = font->memory;
    
    
          FT_FREE( font->blend.lastNDV );
          FT_FREE( font->blend.BV );
        }
      }
    
    
      /*********************************************
       *
       * functions for handling client outline;
       * FreeType uses coordinates in 26.6 format
       *
       */
    
      static void
      cf2_builder_moveTo( CF2_OutlineCallbacks      callbacks,
                          const CF2_CallbackParams  params )
      {
        /* downcast the object pointer */
        CF2_Outline  outline = (CF2_Outline)callbacks;
        PS_Builder*  builder;
    
        (void)params;        /* only used in debug mode */
    
    
        FT_ASSERT( outline && outline->decoder );
        FT_ASSERT( params->op == CF2_PathOpMoveTo );
    
        builder = &outline->decoder->builder;
    
        /* note: two successive moves simply close the contour twice */
        ps_builder_close_contour( builder );
        builder->path_begun = 0;
      }
    
    
      static void
      cf2_builder_lineTo( CF2_OutlineCallbacks      callbacks,
                          const CF2_CallbackParams  params )
      {
        FT_Error  error;
    
        /* downcast the object pointer */
        CF2_Outline  outline = (CF2_Outline)callbacks;
        PS_Builder*  builder;
    
    
        FT_ASSERT( outline && outline->decoder );
        FT_ASSERT( params->op == CF2_PathOpLineTo );
    
        builder = &outline->decoder->builder;
    
        if ( !builder->path_begun )
        {
          /* record the move before the line; also check points and set */
          /* `path_begun'                                               */
          error = ps_builder_start_point( builder,
                                          params->pt0.x,
                                          params->pt0.y );
          if ( error )
          {
            if ( !*callbacks->error )
              *callbacks->error =  error;
            return;
          }
        }
    
        /* `ps_builder_add_point1' includes a check_points call for one point */
        error = ps_builder_add_point1( builder,
                                       params->pt1.x,
                                       params->pt1.y );
        if ( error )
        {
          if ( !*callbacks->error )
            *callbacks->error =  error;
          return;
        }
      }
    
    
      static void
      cf2_builder_cubeTo( CF2_OutlineCallbacks      callbacks,
                          const CF2_CallbackParams  params )
      {
        FT_Error  error;
    
        /* downcast the object pointer */
        CF2_Outline  outline = (CF2_Outline)callbacks;
        PS_Builder*  builder;
    
    
        FT_ASSERT( outline && outline->decoder );
        FT_ASSERT( params->op == CF2_PathOpCubeTo );
    
        builder = &outline->decoder->builder;
    
        if ( !builder->path_begun )
        {
          /* record the move before the line; also check points and set */
          /* `path_begun'                                               */
          error = ps_builder_start_point( builder,
                                          params->pt0.x,
                                          params->pt0.y );
          if ( error )
          {
            if ( !*callbacks->error )
              *callbacks->error =  error;
            return;
          }
        }
    
        /* prepare room for 3 points: 2 off-curve, 1 on-curve */
        error = ps_builder_check_points( builder, 3 );
        if ( error )
        {
          if ( !*callbacks->error )
            *callbacks->error =  error;
          return;
        }
    
        ps_builder_add_point( builder,
                              params->pt1.x,
                              params->pt1.y, 0 );
        ps_builder_add_point( builder,
                              params->pt2.x,
                              params->pt2.y, 0 );
        ps_builder_add_point( builder,
                              params->pt3.x,
                              params->pt3.y, 1 );
      }
    
    
      static void
      cf2_outline_init( CF2_Outline  outline,
                        FT_Memory    memory,
                        FT_Error*    error )
      {
        FT_ZERO( outline );
    
        outline->root.memory = memory;
        outline->root.error  = error;
    
        outline->root.moveTo = cf2_builder_moveTo;
        outline->root.lineTo = cf2_builder_lineTo;
        outline->root.cubeTo = cf2_builder_cubeTo;
      }
    
    
      /* get scaling and hint flag from GlyphSlot */
      static void
      cf2_getScaleAndHintFlag( PS_Decoder*  decoder,
                               CF2_Fixed*   x_scale,
                               CF2_Fixed*   y_scale,
                               FT_Bool*     hinted,
                               FT_Bool*     scaled )
      {
        FT_ASSERT( decoder && decoder->builder.glyph );
    
        /* note: FreeType scale includes a factor of 64 */
        *hinted = decoder->builder.glyph->hint;
        *scaled = decoder->builder.glyph->scaled;
    
        if ( *hinted )
        {
          *x_scale = ADD_INT32( decoder->builder.glyph->x_scale, 32 ) / 64;
          *y_scale = ADD_INT32( decoder->builder.glyph->y_scale, 32 ) / 64;
        }
        else
        {
          /* for unhinted outlines, `cff_slot_load' does the scaling, */
          /* thus render at `unity' scale                             */
    
          *x_scale = 0x0400;   /* 1/64 as 16.16 */
          *y_scale = 0x0400;
        }
      }
    
    
      /* get units per em from `FT_Face' */
      /* TODO: should handle font matrix concatenation? */
      static FT_UShort
      cf2_getUnitsPerEm( PS_Decoder*  decoder )
      {
        FT_ASSERT( decoder && decoder->builder.face );
    
        return decoder->builder.face->units_per_EM;
      }
    
    
      /* Main entry point: Render one glyph. */
      FT_LOCAL_DEF( FT_Error )
      cf2_decoder_parse_charstrings( PS_Decoder*  decoder,
                                     FT_Byte*     charstring_base,
                                     FT_ULong     charstring_len )
      {
        FT_Memory  memory;
        FT_Error   error = FT_Err_Ok;
        CF2_Font   font;
    
        FT_Bool  is_t1 = decoder->builder.is_t1;
    
    
        FT_ASSERT( decoder &&
                   ( is_t1 || decoder->cff ) );
    
        if ( is_t1 && !decoder->current_subfont )
        {
          FT_ERROR(( "cf2_decoder_parse_charstrings (Type 1): "
                     "SubFont missing. Use `t1_make_subfont' first\n" ));
          return FT_THROW( Invalid_Table );
        }
    
        memory = decoder->builder.memory;
    
        /* CF2 data is saved here across glyphs */
        font = (CF2_Font)decoder->cf2_instance->data;
    
        /* on first glyph, allocate instance structure */
        if ( !decoder->cf2_instance->data )
        {
          decoder->cf2_instance->finalizer =
            (FT_Generic_Finalizer)cf2_free_instance;
    
          if ( FT_ALLOC( decoder->cf2_instance->data,
                         sizeof ( CF2_FontRec ) ) )
            return FT_THROW( Out_Of_Memory );
    
          font = (CF2_Font)decoder->cf2_instance->data;
    
          font->memory = memory;
    
          if ( !is_t1 )
            font->cffload = (FT_Service_CFFLoad)decoder->cff->cffload;
    
          /* initialize a client outline, to be shared by each glyph rendered */
          cf2_outline_init( &font->outline, font->memory, &font->error );
        }
    
        /* save decoder; it is a stack variable and will be different on each */
        /* call                                                               */
        font->decoder         = decoder;
        font->outline.decoder = decoder;
    
        {
          /* build parameters for Adobe engine */
    
          PS_Builder*  builder = &decoder->builder;
          PS_Driver    driver  = (PS_Driver)FT_FACE_DRIVER( builder->face );
    
          FT_Bool  no_stem_darkening_driver =
                     driver->no_stem_darkening;
          FT_Char  no_stem_darkening_font =
                     builder->face->internal->no_stem_darkening;
    
          /* local error */
          FT_Error       error2 = FT_Err_Ok;
          CF2_BufferRec  buf;
          CF2_Matrix     transform;
          CF2_F16Dot16   glyphWidth;
    
          FT_Bool  hinted;
          FT_Bool  scaled;
    
    
          /* FreeType has already looked up the GID; convert to         */
          /* `RegionBuffer', assuming that the input has been validated */
          FT_ASSERT( charstring_base + charstring_len >= charstring_base );
    
          FT_ZERO( &buf );
          buf.start =
          buf.ptr   = charstring_base;
          buf.end   = FT_OFFSET( charstring_base, charstring_len );
    
          FT_ZERO( &transform );
    
          cf2_getScaleAndHintFlag( decoder,
                                   &transform.a,
                                   &transform.d,
                                   &hinted,
                                   &scaled );
    
          if ( is_t1 )
            font->isCFF2 = FALSE;
          else
          {
            /* copy isCFF2 boolean from TT_Face to CF2_Font */
            font->isCFF2 = ((TT_Face)builder->face)->is_cff2;
          }
          font->isT1 = is_t1;
    
          font->renderingFlags = 0;
          if ( hinted )
            font->renderingFlags |= CF2_FlagsHinted;
          if ( scaled && ( !no_stem_darkening_font        ||
                           ( no_stem_darkening_font < 0 &&
                             !no_stem_darkening_driver  ) ) )
            font->renderingFlags |= CF2_FlagsDarkened;
    
          font->darkenParams[0] = driver->darken_params[0];
          font->darkenParams[1] = driver->darken_params[1];
          font->darkenParams[2] = driver->darken_params[2];
          font->darkenParams[3] = driver->darken_params[3];
          font->darkenParams[4] = driver->darken_params[4];
          font->darkenParams[5] = driver->darken_params[5];
          font->darkenParams[6] = driver->darken_params[6];
          font->darkenParams[7] = driver->darken_params[7];
    
          /* now get an outline for this glyph;      */
          /* also get units per em to validate scale */
          font->unitsPerEm = (CF2_Int)cf2_getUnitsPerEm( decoder );
    
          if ( scaled )
          {
            error2 = cf2_checkTransform( &transform, font->unitsPerEm );
            if ( error2 )
              return error2;
          }
    
          error2 = cf2_getGlyphOutline( font, &buf, &transform, &glyphWidth );
          if ( error2 )
            return FT_ERR( Invalid_File_Format );
    
          cf2_setGlyphWidth( &font->outline, glyphWidth );
    
          return FT_Err_Ok;
        }
      }
    
    
      /* get pointer to current FreeType subfont (based on current glyphID) */
      FT_LOCAL_DEF( CFF_SubFont )
      cf2_getSubfont( PS_Decoder*  decoder )
      {
        FT_ASSERT( decoder && decoder->current_subfont );
    
        return decoder->current_subfont;
      }
    
    
      /* get pointer to VStore structure */
      FT_LOCAL_DEF( CFF_VStore )
      cf2_getVStore( PS_Decoder*  decoder )
      {
        FT_ASSERT( decoder && decoder->cff );
    
        return &decoder->cff->vstore;
      }
    
    
      /* get maxstack value from CFF2 Top DICT */
      FT_LOCAL_DEF( FT_UInt )
      cf2_getMaxstack( PS_Decoder*  decoder )
      {
        FT_ASSERT( decoder && decoder->cff );
    
        return decoder->cff->top_font.font_dict.maxstack;
      }
    
    
    #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
      /* Get normalized design vector for current render request; */
      /* return pointer and length.                               */
      /*                                                          */
      /* Note: Uses FT_Fixed not CF2_Fixed for the vector.        */
      FT_LOCAL_DEF( FT_Error )
      cf2_getNormalizedVector( PS_Decoder*  decoder,
                               CF2_UInt     *len,
                               FT_Fixed*    *vec )
      {
        TT_Face                  face;
        FT_Service_MultiMasters  mm;
    
    
        FT_ASSERT( decoder && decoder->builder.face );
        FT_ASSERT( vec && len );
        FT_ASSERT( !decoder->builder.is_t1 );
    
        face = (TT_Face)decoder->builder.face;
        mm   = (FT_Service_MultiMasters)face->mm;
    
        return mm->get_var_blend( FT_FACE( face ), len, NULL, vec, NULL );
      }
    #endif
    
    
      /* get `y_ppem' from `CFF_Size' */
      FT_LOCAL_DEF( CF2_Fixed )
      cf2_getPpemY( PS_Decoder*  decoder )
      {
        FT_ASSERT( decoder                     &&
                   decoder->builder.face       &&
                   decoder->builder.face->size );
    
        /*
         * Note that `y_ppem' can be zero if there wasn't a call to
         * `FT_Set_Char_Size' or something similar.  However, this isn't a
         * problem since we come to this place in the code only if
         * FT_LOAD_NO_SCALE is set (the other case gets caught by
         * `cf2_checkTransform').  The ppem value is needed to compute the stem
         * darkening, which is disabled for getting the unscaled outline.
         *
         */
        return cf2_intToFixed(
                 decoder->builder.face->size->metrics.y_ppem );
      }
    
    
      /* get standard stem widths for the current subfont; */
      /* FreeType stores these as integer font units       */
      /* (note: variable names seem swapped)               */
      FT_LOCAL_DEF( CF2_Fixed )
      cf2_getStdVW( PS_Decoder*  decoder )
      {
        FT_ASSERT( decoder && decoder->current_subfont );
    
        return cf2_intToFixed(
                 decoder->current_subfont->private_dict.standard_height );
      }
    
    
      FT_LOCAL_DEF( CF2_Fixed )
      cf2_getStdHW( PS_Decoder*  decoder )
      {
        FT_ASSERT( decoder && decoder->current_subfont );
    
        return cf2_intToFixed(
                 decoder->current_subfont->private_dict.standard_width );
      }
    
    
      /* note: FreeType stores 1000 times the actual value for `BlueScale' */
      FT_LOCAL_DEF( void )
      cf2_getBlueMetrics( PS_Decoder*  decoder,
                          CF2_Fixed*   blueScale,
                          CF2_Fixed*   blueShift,
                          CF2_Fixed*   blueFuzz )
      {
        FT_ASSERT( decoder && decoder->current_subfont );
    
        *blueScale = FT_DivFix(
                       decoder->current_subfont->private_dict.blue_scale,
                       cf2_intToFixed( 1000 ) );
        *blueShift = cf2_intToFixed(
                       decoder->current_subfont->private_dict.blue_shift );
        *blueFuzz  = cf2_intToFixed(
                       decoder->current_subfont->private_dict.blue_fuzz );
      }
    
    
      /* get blue values counts and arrays; the FreeType parser has validated */
      /* the counts and verified that each is an even number                  */
      FT_LOCAL_DEF( void )
      cf2_getBlueValues( PS_Decoder*  decoder,
                         size_t*      count,
                         FT_Fixed*   *data )
      {
        FT_ASSERT( decoder && decoder->current_subfont );
    
        *count = decoder->current_subfont->private_dict.num_blue_values;
        *data  = (FT_Fixed*)
                   &decoder->current_subfont->private_dict.blue_values;
      }
    
    
      FT_LOCAL_DEF( void )
      cf2_getOtherBlues( PS_Decoder*  decoder,
                         size_t*      count,
                         FT_Fixed*   *data )
      {
        FT_ASSERT( decoder && decoder->current_subfont );
    
        *count = decoder->current_subfont->private_dict.num_other_blues;
        *data  = (FT_Fixed*)
                   &decoder->current_subfont->private_dict.other_blues;
      }
    
    
      FT_LOCAL_DEF( void )
      cf2_getFamilyBlues( PS_Decoder*  decoder,
                          size_t*      count,
                          FT_Fixed*   *data )
      {
        FT_ASSERT( decoder && decoder->current_subfont );
    
        *count = decoder->current_subfont->private_dict.num_family_blues;
        *data  = (FT_Fixed*)
                   &decoder->current_subfont->private_dict.family_blues;
      }
    
    
      FT_LOCAL_DEF( void )
      cf2_getFamilyOtherBlues( PS_Decoder*  decoder,
                               size_t*      count,
                               FT_Fixed*   *data )
      {
        FT_ASSERT( decoder && decoder->current_subfont );
    
        *count = decoder->current_subfont->private_dict.num_family_other_blues;
        *data  = (FT_Fixed*)
                   &decoder->current_subfont->private_dict.family_other_blues;
      }
    
    
      FT_LOCAL_DEF( CF2_Int )
      cf2_getLanguageGroup( PS_Decoder*  decoder )
      {
        FT_ASSERT( decoder && decoder->current_subfont );
    
        return decoder->current_subfont->private_dict.language_group;
      }
    
    
      /* convert unbiased subroutine index to `CF2_Buffer' and */
      /* return 0 on success                                   */
      FT_LOCAL_DEF( CF2_Int )
      cf2_initGlobalRegionBuffer( PS_Decoder*  decoder,
                                  CF2_Int      subrNum,
                                  CF2_Buffer   buf )
      {
        CF2_UInt  idx;
    
    
        FT_ASSERT( decoder );
    
        FT_ZERO( buf );
    
        idx = (CF2_UInt)( subrNum + decoder->globals_bias );
        if ( idx >= decoder->num_globals )
          return TRUE;     /* error */
    
        FT_ASSERT( decoder->globals );
    
        buf->start =
        buf->ptr   = decoder->globals[idx];
        buf->end   = decoder->globals[idx + 1];
    
        return FALSE;      /* success */
      }
    
    
      /* convert AdobeStandardEncoding code to CF2_Buffer; */
      /* used for seac component                           */
      FT_LOCAL_DEF( FT_Error )
      cf2_getSeacComponent( PS_Decoder*  decoder,
                            CF2_Int      code,
                            CF2_Buffer   buf )
      {
        CF2_Int   gid;
        FT_Byte*  charstring;
        FT_ULong  len;
        FT_Error  error;
    
    
        FT_ASSERT( decoder );
        FT_ASSERT( !decoder->builder.is_t1 );
    
        FT_ZERO( buf );
    
    #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 ( decoder->builder.face->internal->incremental_interface )
          gid = code;
        else
    #endif /* FT_CONFIG_OPTION_INCREMENTAL */
        {
          gid = cff_lookup_glyph_by_stdcharcode( decoder->cff, code );
          if ( gid < 0 )
            return FT_THROW( Invalid_Glyph_Format );
        }
    
        error = decoder->get_glyph_callback( (TT_Face)decoder->builder.face,
                                             (CF2_UInt)gid,
                                             &charstring,
                                             &len );
        /* TODO: for now, just pass the FreeType error through */
        if ( error )
          return error;
    
        /* assume input has been validated */
        FT_ASSERT( charstring + len >= charstring );
    
        buf->start = charstring;
        buf->end   = FT_OFFSET( charstring, len );
        buf->ptr   = buf->start;
    
        return FT_Err_Ok;
      }
    
    
      FT_LOCAL_DEF( void )
      cf2_freeSeacComponent( PS_Decoder*  decoder,
                             CF2_Buffer   buf )
      {
        FT_ASSERT( decoder );
        FT_ASSERT( !decoder->builder.is_t1 );
    
        decoder->free_glyph_callback( (TT_Face)decoder->builder.face,
                                      (FT_Byte**)&buf->start,
                                      (FT_ULong)( buf->end - buf->start ) );
      }
    
    
      FT_LOCAL_DEF( FT_Error )
      cf2_getT1SeacComponent( PS_Decoder*  decoder,
                              FT_UInt      glyph_index,
                              CF2_Buffer   buf )
      {
        FT_Data   glyph_data;
        FT_Error  error = FT_Err_Ok;
        T1_Face   face  = (T1_Face)decoder->builder.face;
        T1_Font   type1 = &face->type1;
    
    #ifdef FT_CONFIG_OPTION_INCREMENTAL
        FT_Incremental_InterfaceRec  *inc =
          face->root.internal->incremental_interface;
    
    
        /* For incremental fonts get the character data using the */
        /* callback function.                                     */
        if ( inc )
          error = inc->funcs->get_glyph_data( inc->object,
                                              glyph_index, &glyph_data );
        else
    #endif
        /* For ordinary fonts get the character data stored in the face record. */
        {
          glyph_data.pointer = type1->charstrings[glyph_index];
          glyph_data.length  = type1->charstrings_len[glyph_index];
        }
    
        if ( !error )
        {
          FT_Byte*  charstring_base = (FT_Byte*)glyph_data.pointer;
          FT_ULong  charstring_len  = glyph_data.length;
    
    
          FT_ASSERT( charstring_base + charstring_len >= charstring_base );
    
          FT_ZERO( buf );
          buf->start =
          buf->ptr   = charstring_base;
          buf->end   = charstring_base + charstring_len;
        }
    
        return error;
      }
    
    
      FT_LOCAL_DEF( void )
      cf2_freeT1SeacComponent( PS_Decoder*  decoder,
                               CF2_Buffer   buf )
      {
    #ifdef FT_CONFIG_OPTION_INCREMENTAL
    
        T1_Face  face;
        FT_Data  data;
    
    
        FT_ASSERT( decoder );
    
        face = (T1_Face)decoder->builder.face;
    
        data.pointer = buf->start;
        data.length  = (FT_UInt)( buf->end - buf->start );
    
        if ( face->root.internal->incremental_interface )
          face->root.internal->incremental_interface->funcs->free_glyph_data(
            face->root.internal->incremental_interface->object,
            &data );
    
    #else /* !FT_CONFIG_OPTION_INCREMENTAL */
    
        FT_UNUSED( decoder );
        FT_UNUSED( buf );
    
    #endif /* !FT_CONFIG_OPTION_INCREMENTAL */
      }
    
    
      FT_LOCAL_DEF( CF2_Int )
      cf2_initLocalRegionBuffer( PS_Decoder*  decoder,
                                 CF2_Int      subrNum,
                                 CF2_Buffer   buf )
      {
        CF2_UInt  idx;
    
    
        FT_ASSERT( decoder );
    
        FT_ZERO( buf );
    
        idx = (CF2_UInt)( subrNum + decoder->locals_bias );
        if ( idx >= decoder->num_locals )
          return TRUE;     /* error */
    
        FT_ASSERT( decoder->locals );
    
        buf->start = decoder->locals[idx];
    
        if ( decoder->builder.is_t1 )
        {
          /* 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.          */
          if ( decoder->locals_len )
            buf->end = FT_OFFSET( buf->start, decoder->locals_len[idx] );
          else
          {
            /* We are using subroutines from a CID font.  We must adjust */
            /* for the seed bytes.                                       */
            buf->start += ( decoder->lenIV >= 0 ? decoder->lenIV : 0 );
            buf->end    = decoder->locals[idx + 1];
          }
    
          if ( !buf->start )
          {
            FT_ERROR(( "cf2_initLocalRegionBuffer (Type 1 mode):"
                       " invoking empty subrs\n" ));
          }
        }
        else
        {
          buf->end = decoder->locals[idx + 1];
        }
    
        buf->ptr = buf->start;
    
        return FALSE;      /* success */
      }
    
    
      FT_LOCAL_DEF( CF2_Fixed )
      cf2_getDefaultWidthX( PS_Decoder*  decoder )
      {
        FT_ASSERT( decoder && decoder->current_subfont );
    
        return cf2_intToFixed(
                 decoder->current_subfont->private_dict.default_width );
      }
    
    
      FT_LOCAL_DEF( CF2_Fixed )
      cf2_getNominalWidthX( PS_Decoder*  decoder )
      {
        FT_ASSERT( decoder && decoder->current_subfont );
    
        return cf2_intToFixed(
                 decoder->current_subfont->private_dict.nominal_width );
      }
    
    
      FT_LOCAL_DEF( void )
      cf2_outline_reset( CF2_Outline  outline )
      {
        PS_Decoder*  decoder = outline->decoder;
    
    
        FT_ASSERT( decoder );
    
        outline->root.windingMomentum = 0;
    
        FT_GlyphLoader_Rewind( decoder->builder.loader );
      }
    
    
      FT_LOCAL_DEF( void )
      cf2_outline_close( CF2_Outline  outline )
      {
        PS_Decoder*  decoder = outline->decoder;
    
    
        FT_ASSERT( decoder );
    
        ps_builder_close_contour( &decoder->builder );
    
        FT_GlyphLoader_Add( decoder->builder.loader );
      }
    
    
    /* END */