Home | History | Annotate | Download | only in cff
      1 /***************************************************************************/
      2 /*                                                                         */
      3 /*  cf2intrp.c                                                             */
      4 /*                                                                         */
      5 /*    Adobe's CFF Interpreter (body).                                      */
      6 /*                                                                         */
      7 /*  Copyright 2007-2013 Adobe Systems Incorporated.                        */
      8 /*                                                                         */
      9 /*  This software, and all works of authorship, whether in source or       */
     10 /*  object code form as indicated by the copyright notice(s) included      */
     11 /*  herein (collectively, the "Work") is made available, and may only be   */
     12 /*  used, modified, and distributed under the FreeType Project License,    */
     13 /*  LICENSE.TXT.  Additionally, subject to the terms and conditions of the */
     14 /*  FreeType Project License, each contributor to the Work hereby grants   */
     15 /*  to any individual or legal entity exercising permissions granted by    */
     16 /*  the FreeType Project License and this section (hereafter, "You" or     */
     17 /*  "Your") a perpetual, worldwide, non-exclusive, no-charge,              */
     18 /*  royalty-free, irrevocable (except as stated in this section) patent    */
     19 /*  license to make, have made, use, offer to sell, sell, import, and      */
     20 /*  otherwise transfer the Work, where such license applies only to those  */
     21 /*  patent claims licensable by such contributor that are necessarily      */
     22 /*  infringed by their contribution(s) alone or by combination of their    */
     23 /*  contribution(s) with the Work to which such contribution(s) was        */
     24 /*  submitted.  If You institute patent litigation against any entity      */
     25 /*  (including a cross-claim or counterclaim in a lawsuit) alleging that   */
     26 /*  the Work or a contribution incorporated within the Work constitutes    */
     27 /*  direct or contributory patent infringement, then any patent licenses   */
     28 /*  granted to You under this License for that Work shall terminate as of  */
     29 /*  the date such litigation is filed.                                     */
     30 /*                                                                         */
     31 /*  By using, modifying, or distributing the Work you indicate that you    */
     32 /*  have read and understood the terms and conditions of the               */
     33 /*  FreeType Project License as well as those provided in this section,    */
     34 /*  and you accept them fully.                                             */
     35 /*                                                                         */
     36 /***************************************************************************/
     37 
     38 
     39 #include "cf2ft.h"
     40 #include FT_INTERNAL_DEBUG_H
     41 
     42 #include "cf2glue.h"
     43 #include "cf2font.h"
     44 #include "cf2stack.h"
     45 #include "cf2hints.h"
     46 
     47 #include "cf2error.h"
     48 
     49 
     50   /*************************************************************************/
     51   /*                                                                       */
     52   /* The macro FT_COMPONENT is used in trace mode.  It is an implicit      */
     53   /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log  */
     54   /* messages during execution.                                            */
     55   /*                                                                       */
     56 #undef  FT_COMPONENT
     57 #define FT_COMPONENT  trace_cf2interp
     58 
     59 
     60   /* some operators are not implemented yet */
     61 #define CF2_FIXME  FT_TRACE4(( "cf2_interpT2CharString:"            \
     62                                " operator not implemented yet\n" ))
     63 
     64 
     65 
     66   FT_LOCAL_DEF( void )
     67   cf2_hintmask_init( CF2_HintMask  hintmask,
     68                      FT_Error*     error )
     69   {
     70     FT_ZERO( hintmask );
     71 
     72     hintmask->error = error;
     73   }
     74 
     75 
     76   FT_LOCAL_DEF( FT_Bool )
     77   cf2_hintmask_isValid( const CF2_HintMask  hintmask )
     78   {
     79     return hintmask->isValid;
     80   }
     81 
     82 
     83   FT_LOCAL_DEF( FT_Bool )
     84   cf2_hintmask_isNew( const CF2_HintMask  hintmask )
     85   {
     86     return hintmask->isNew;
     87   }
     88 
     89 
     90   FT_LOCAL_DEF( void )
     91   cf2_hintmask_setNew( CF2_HintMask  hintmask,
     92                        FT_Bool       val )
     93   {
     94     hintmask->isNew = val;
     95   }
     96 
     97 
     98   /* clients call `getMaskPtr' in order to iterate */
     99   /* through hint mask                             */
    100 
    101   FT_LOCAL_DEF( FT_Byte* )
    102   cf2_hintmask_getMaskPtr( CF2_HintMask  hintmask )
    103   {
    104     return hintmask->mask;
    105   }
    106 
    107 
    108   static size_t
    109   cf2_hintmask_setCounts( CF2_HintMask  hintmask,
    110                           size_t        bitCount )
    111   {
    112     if ( bitCount > CF2_MAX_HINTS )
    113     {
    114       /* total of h and v stems must be <= 96 */
    115       CF2_SET_ERROR( hintmask->error, Invalid_Glyph_Format );
    116       return 0;
    117     }
    118 
    119     hintmask->bitCount  = bitCount;
    120     hintmask->byteCount = ( hintmask->bitCount + 7 ) / 8;
    121 
    122     hintmask->isValid = TRUE;
    123     hintmask->isNew   = TRUE;
    124 
    125     return bitCount;
    126   }
    127 
    128 
    129   /* consume the hintmask bytes from the charstring, advancing the src */
    130   /* pointer                                                           */
    131   static void
    132   cf2_hintmask_read( CF2_HintMask  hintmask,
    133                      CF2_Buffer    charstring,
    134                      size_t        bitCount )
    135   {
    136     size_t  i;
    137 
    138 #ifndef CF2_NDEBUG
    139     /* these are the bits in the final mask byte that should be zero  */
    140     /* Note: this variable is only used in an assert expression below */
    141     /* and then only if CF2_NDEBUG is not defined                     */
    142     CF2_UInt  mask = ( 1 << ( -(CF2_Int)bitCount & 7 ) ) - 1;
    143 #endif
    144 
    145 
    146     /* initialize counts and isValid */
    147     if ( cf2_hintmask_setCounts( hintmask, bitCount ) == 0 )
    148       return;
    149 
    150     FT_ASSERT( hintmask->byteCount > 0 );
    151 
    152     FT_TRACE4(( " (maskbytes:" ));
    153 
    154     /* set mask and advance interpreter's charstring pointer */
    155     for ( i = 0; i < hintmask->byteCount; i++ )
    156     {
    157       hintmask->mask[i] = (FT_Byte)cf2_buf_readByte( charstring );
    158       FT_TRACE4(( " 0x%02X", hintmask->mask[i] ));
    159     }
    160 
    161     FT_TRACE4(( ")\n" ));
    162 
    163     /* assert any unused bits in last byte are zero unless there's a prior */
    164     /* error                                                               */
    165     /* bitCount -> mask, 0 -> 0, 1 -> 7f, 2 -> 3f, ... 6 -> 3, 7 -> 1      */
    166 #ifndef CF2_NDEBUG
    167     FT_ASSERT( ( hintmask->mask[hintmask->byteCount - 1] & mask ) == 0 ||
    168                *hintmask->error                                        );
    169 #endif
    170   }
    171 
    172 
    173   FT_LOCAL_DEF( void )
    174   cf2_hintmask_setAll( CF2_HintMask  hintmask,
    175                        size_t        bitCount )
    176   {
    177     size_t    i;
    178     CF2_UInt  mask = ( 1 << ( -(CF2_Int)bitCount & 7 ) ) - 1;
    179 
    180 
    181     /* initialize counts and isValid */
    182     if ( cf2_hintmask_setCounts( hintmask, bitCount ) == 0 )
    183       return;
    184 
    185     FT_ASSERT( hintmask->byteCount > 0 );
    186     FT_ASSERT( hintmask->byteCount <
    187                  sizeof ( hintmask->mask ) / sizeof ( hintmask->mask[0] ) );
    188 
    189     /* set mask to all ones */
    190     for ( i = 0; i < hintmask->byteCount; i++ )
    191       hintmask->mask[i] = 0xFF;
    192 
    193     /* clear unused bits                                              */
    194     /* bitCount -> mask, 0 -> 0, 1 -> 7f, 2 -> 3f, ... 6 -> 3, 7 -> 1 */
    195     hintmask->mask[hintmask->byteCount - 1] &= ~mask;
    196   }
    197 
    198 
    199   /* Type2 charstring opcodes */
    200   enum
    201   {
    202     cf2_cmdRESERVED_0,   /* 0 */
    203     cf2_cmdHSTEM,        /* 1 */
    204     cf2_cmdRESERVED_2,   /* 2 */
    205     cf2_cmdVSTEM,        /* 3 */
    206     cf2_cmdVMOVETO,      /* 4 */
    207     cf2_cmdRLINETO,      /* 5 */
    208     cf2_cmdHLINETO,      /* 6 */
    209     cf2_cmdVLINETO,      /* 7 */
    210     cf2_cmdRRCURVETO,    /* 8 */
    211     cf2_cmdRESERVED_9,   /* 9 */
    212     cf2_cmdCALLSUBR,     /* 10 */
    213     cf2_cmdRETURN,       /* 11 */
    214     cf2_cmdESC,          /* 12 */
    215     cf2_cmdRESERVED_13,  /* 13 */
    216     cf2_cmdENDCHAR,      /* 14 */
    217     cf2_cmdRESERVED_15,  /* 15 */
    218     cf2_cmdRESERVED_16,  /* 16 */
    219     cf2_cmdRESERVED_17,  /* 17 */
    220     cf2_cmdHSTEMHM,      /* 18 */
    221     cf2_cmdHINTMASK,     /* 19 */
    222     cf2_cmdCNTRMASK,     /* 20 */
    223     cf2_cmdRMOVETO,      /* 21 */
    224     cf2_cmdHMOVETO,      /* 22 */
    225     cf2_cmdVSTEMHM,      /* 23 */
    226     cf2_cmdRCURVELINE,   /* 24 */
    227     cf2_cmdRLINECURVE,   /* 25 */
    228     cf2_cmdVVCURVETO,    /* 26 */
    229     cf2_cmdHHCURVETO,    /* 27 */
    230     cf2_cmdEXTENDEDNMBR, /* 28 */
    231     cf2_cmdCALLGSUBR,    /* 29 */
    232     cf2_cmdVHCURVETO,    /* 30 */
    233     cf2_cmdHVCURVETO     /* 31 */
    234   };
    235 
    236   enum
    237   {
    238     cf2_escDOTSECTION,   /* 0 */
    239     cf2_escRESERVED_1,   /* 1 */
    240     cf2_escRESERVED_2,   /* 2 */
    241     cf2_escAND,          /* 3 */
    242     cf2_escOR,           /* 4 */
    243     cf2_escNOT,          /* 5 */
    244     cf2_escRESERVED_6,   /* 6 */
    245     cf2_escRESERVED_7,   /* 7 */
    246     cf2_escRESERVED_8,   /* 8 */
    247     cf2_escABS,          /* 9 */
    248     cf2_escADD,          /* 10     like otherADD */
    249     cf2_escSUB,          /* 11     like otherSUB */
    250     cf2_escDIV,          /* 12 */
    251     cf2_escRESERVED_13,  /* 13 */
    252     cf2_escNEG,          /* 14 */
    253     cf2_escEQ,           /* 15 */
    254     cf2_escRESERVED_16,  /* 16 */
    255     cf2_escRESERVED_17,  /* 17 */
    256     cf2_escDROP,         /* 18 */
    257     cf2_escRESERVED_19,  /* 19 */
    258     cf2_escPUT,          /* 20     like otherPUT    */
    259     cf2_escGET,          /* 21     like otherGET    */
    260     cf2_escIFELSE,       /* 22     like otherIFELSE */
    261     cf2_escRANDOM,       /* 23     like otherRANDOM */
    262     cf2_escMUL,          /* 24     like otherMUL    */
    263     cf2_escRESERVED_25,  /* 25 */
    264     cf2_escSQRT,         /* 26 */
    265     cf2_escDUP,          /* 27     like otherDUP    */
    266     cf2_escEXCH,         /* 28     like otherEXCH   */
    267     cf2_escINDEX,        /* 29 */
    268     cf2_escROLL,         /* 30 */
    269     cf2_escRESERVED_31,  /* 31 */
    270     cf2_escRESERVED_32,  /* 32 */
    271     cf2_escRESERVED_33,  /* 33 */
    272     cf2_escHFLEX,        /* 34 */
    273     cf2_escFLEX,         /* 35 */
    274     cf2_escHFLEX1,       /* 36 */
    275     cf2_escFLEX1         /* 37 */
    276   };
    277 
    278 
    279   /* `stemHintArray' does not change once we start drawing the outline. */
    280   static void
    281   cf2_doStems( const CF2_Font  font,
    282                CF2_Stack       opStack,
    283                CF2_ArrStack    stemHintArray,
    284                CF2_Fixed*      width,
    285                FT_Bool*        haveWidth,
    286                CF2_Fixed       hintOffset )
    287   {
    288     CF2_UInt  i;
    289     CF2_UInt  count       = cf2_stack_count( opStack );
    290     FT_Bool   hasWidthArg = count & 1;
    291 
    292     /* variable accumulates delta values from operand stack */
    293     CF2_Fixed  position = hintOffset;
    294 
    295     if ( hasWidthArg && ! *haveWidth )
    296       *width = cf2_stack_getReal( opStack, 0 ) +
    297                  cf2_getNominalWidthX( font->decoder );
    298 
    299     if ( font->decoder->width_only )
    300       goto exit;
    301 
    302     for ( i = hasWidthArg ? 1 : 0; i < count; i += 2 )
    303     {
    304       /* construct a CF2_StemHint and push it onto the list */
    305       CF2_StemHintRec  stemhint;
    306 
    307 
    308       stemhint.min  =
    309         position   += cf2_stack_getReal( opStack, i );
    310       stemhint.max  =
    311         position   += cf2_stack_getReal( opStack, i + 1 );
    312 
    313       stemhint.used  = FALSE;
    314       stemhint.maxDS =
    315       stemhint.minDS = 0;
    316 
    317       cf2_arrstack_push( stemHintArray, &stemhint ); /* defer error check */
    318     }
    319 
    320     cf2_stack_clear( opStack );
    321 
    322   exit:
    323     /* cf2_doStems must define a width (may be default) */
    324     *haveWidth = TRUE;
    325   }
    326 
    327 
    328   static void
    329   cf2_doFlex( CF2_Stack       opStack,
    330               CF2_Fixed*      curX,
    331               CF2_Fixed*      curY,
    332               CF2_GlyphPath   glyphPath,
    333               const FT_Bool*  readFromStack,
    334               FT_Bool         doConditionalLastRead )
    335   {
    336     CF2_Fixed  vals[14];
    337     CF2_UInt   index;
    338     FT_Bool    isHFlex;
    339     CF2_Int    top, i, j;
    340 
    341 
    342     vals[0] = *curX;
    343     vals[1] = *curY;
    344     index   = 0;
    345     isHFlex = readFromStack[9] == FALSE;
    346     top     = isHFlex ? 9 : 10;
    347 
    348     for ( i = 0; i < top; i++ )
    349     {
    350       vals[i + 2] = vals[i];
    351       if ( readFromStack[i] )
    352         vals[i + 2] += cf2_stack_getReal( opStack, index++ );
    353     }
    354 
    355     if ( isHFlex )
    356       vals[9 + 2] = *curY;
    357 
    358     if ( doConditionalLastRead )
    359     {
    360       FT_Bool    lastIsX = cf2_fixedAbs( vals[10] - *curX ) >
    361                              cf2_fixedAbs( vals[11] - *curY );
    362       CF2_Fixed  lastVal = cf2_stack_getReal( opStack, index );
    363 
    364 
    365       if ( lastIsX )
    366       {
    367         vals[12] = vals[10] + lastVal;
    368         vals[13] = *curY;
    369       }
    370       else
    371       {
    372         vals[12] = *curX;
    373         vals[13] = vals[11] + lastVal;
    374       }
    375     }
    376     else
    377     {
    378       if ( readFromStack[10] )
    379         vals[12] = vals[10] + cf2_stack_getReal( opStack, index++ );
    380       else
    381         vals[12] = *curX;
    382 
    383       if ( readFromStack[11] )
    384         vals[13] = vals[11] + cf2_stack_getReal( opStack, index );
    385       else
    386         vals[13] = *curY;
    387     }
    388 
    389     for ( j = 0; j < 2; j++ )
    390       cf2_glyphpath_curveTo( glyphPath, vals[j * 6 + 2],
    391                                         vals[j * 6 + 3],
    392                                         vals[j * 6 + 4],
    393                                         vals[j * 6 + 5],
    394                                         vals[j * 6 + 6],
    395                                         vals[j * 6 + 7] );
    396 
    397     cf2_stack_clear( opStack );
    398 
    399     *curX = vals[12];
    400     *curY = vals[13];
    401   }
    402 
    403 
    404   /*
    405    * `error' is a shared error code used by many objects in this
    406    * routine.  Before the code continues from an error, it must check and
    407    * record the error in `*error'.  The idea is that this shared
    408    * error code will record the first error encountered.  If testing
    409    * for an error anyway, the cost of `goto exit' is small, so we do it,
    410    * even if continuing would be safe.  In this case, `lastError' is
    411    * set, so the testing and storing can be done in one place, at `exit'.
    412    *
    413    * Continuing after an error is intended for objects which do their own
    414    * testing of `*error', e.g., array stack functions.  This allows us to
    415    * avoid an extra test after the call.
    416    *
    417    * Unimplemented opcodes are ignored.
    418    *
    419    */
    420   FT_LOCAL_DEF( void )
    421   cf2_interpT2CharString( CF2_Font              font,
    422                           CF2_Buffer            buf,
    423                           CF2_OutlineCallbacks  callbacks,
    424                           const FT_Vector*      translation,
    425                           FT_Bool               doingSeac,
    426                           CF2_Fixed             curX,
    427                           CF2_Fixed             curY,
    428                           CF2_Fixed*            width )
    429   {
    430     /* lastError is used for errors that are immediately tested */
    431     FT_Error  lastError = FT_Err_Ok;
    432 
    433     /* pointer to parsed font object */
    434     CFF_Decoder*  decoder = font->decoder;
    435 
    436     FT_Error*  error  = &font->error;
    437     FT_Memory  memory = font->memory;
    438 
    439     CF2_Fixed  scaleY        = font->innerTransform.d;
    440     CF2_Fixed  nominalWidthX = cf2_getNominalWidthX( decoder );
    441 
    442     /* save this for hinting seac accents */
    443     CF2_Fixed  hintOriginY = curY;
    444 
    445     CF2_Stack  opStack = NULL;
    446     FT_Byte    op1;                       /* first opcode byte */
    447 
    448     /* instruction limit; 20,000,000 matches Avalon */
    449     FT_UInt32  instructionLimit = 20000000UL;
    450 
    451     CF2_ArrStackRec  subrStack;
    452 
    453     FT_Bool     haveWidth;
    454     CF2_Buffer  charstring = NULL;
    455 
    456     CF2_Int  charstringIndex = -1;       /* initialize to empty */
    457 
    458     /* TODO: placeholders for hint structures */
    459 
    460     /* objects used for hinting */
    461     CF2_ArrStackRec  hStemHintArray;
    462     CF2_ArrStackRec  vStemHintArray;
    463 
    464     CF2_HintMaskRec   hintMask;
    465     CF2_GlyphPathRec  glyphPath;
    466 
    467 
    468     /* initialize the remaining objects */
    469     cf2_arrstack_init( &subrStack,
    470                        memory,
    471                        error,
    472                        sizeof ( CF2_BufferRec ) );
    473     cf2_arrstack_init( &hStemHintArray,
    474                        memory,
    475                        error,
    476                        sizeof ( CF2_StemHintRec ) );
    477     cf2_arrstack_init( &vStemHintArray,
    478                        memory,
    479                        error,
    480                        sizeof ( CF2_StemHintRec ) );
    481 
    482     /* initialize CF2_StemHint arrays */
    483     cf2_hintmask_init( &hintMask, error );
    484 
    485     /* initialize path map to manage drawing operations */
    486 
    487     /* Note: last 4 params are used to handle `MoveToPermissive', which */
    488     /*       may need to call `hintMap.Build'                           */
    489     /* TODO: MoveToPermissive is gone; are these still needed?          */
    490     cf2_glyphpath_init( &glyphPath,
    491                         font,
    492                         callbacks,
    493                         scaleY,
    494                         /* hShift, */
    495                         &hStemHintArray,
    496                         &vStemHintArray,
    497                         &hintMask,
    498                         hintOriginY,
    499                         &font->blues,
    500                         translation );
    501 
    502     /*
    503      * Initialize state for width parsing.  From the CFF Spec:
    504      *
    505      *   The first stack-clearing operator, which must be one of hstem,
    506      *   hstemhm, vstem, vstemhm, cntrmask, hintmask, hmoveto, vmoveto,
    507      *   rmoveto, or endchar, takes an additional argument - the width (as
    508      *   described earlier), which may be expressed as zero or one numeric
    509      *   argument.
    510      *
    511      * What we implement here uses the first validly specified width, but
    512      * does not detect errors for specifying more than one width.
    513      *
    514      * If one of the above operators occurs without explicitly specifying
    515      * a width, we assume the default width.
    516      *
    517      */
    518     haveWidth = FALSE;
    519     *width    = cf2_getDefaultWidthX( decoder );
    520 
    521     /*
    522      * Note: at this point, all pointers to resources must be NULL
    523      * and all local objects must be initialized.
    524      * There must be no branches to exit: above this point.
    525      *
    526      */
    527 
    528     /* allocate an operand stack */
    529     opStack = cf2_stack_init( memory, error );
    530     if ( !opStack )
    531     {
    532       lastError = FT_THROW( Out_Of_Memory );
    533       goto exit;
    534     }
    535 
    536     /* initialize subroutine stack by placing top level charstring as */
    537     /* first element (max depth plus one for the charstring)          */
    538     /* Note: Caller owns and must finalize the first charstring.      */
    539     /*       Our copy of it does not change that requirement.         */
    540     cf2_arrstack_setCount( &subrStack, CF2_MAX_SUBR + 1 );
    541 
    542     charstring  = (CF2_Buffer)cf2_arrstack_getBuffer( &subrStack );
    543     *charstring = *buf;    /* structure copy */
    544 
    545     charstringIndex = 0;       /* entry is valid now */
    546 
    547     /* catch errors so far */
    548     if ( *error )
    549       goto exit;
    550 
    551     /* main interpreter loop */
    552     while ( 1 )
    553     {
    554       if ( cf2_buf_isEnd( charstring ) )
    555       {
    556         /* If we've reached the end of the charstring, simulate a */
    557         /* cf2_cmdRETURN or cf2_cmdENDCHAR.                       */
    558         if ( charstringIndex )
    559           op1 = cf2_cmdRETURN;  /* end of buffer for subroutine */
    560         else
    561           op1 = cf2_cmdENDCHAR; /* end of buffer for top level charstring */
    562       }
    563       else
    564         op1 = (FT_Byte)cf2_buf_readByte( charstring );
    565 
    566       /* check for errors once per loop */
    567       if ( *error )
    568         goto exit;
    569 
    570       instructionLimit--;
    571       if ( instructionLimit == 0 )
    572       {
    573         lastError = FT_THROW( Invalid_Glyph_Format );
    574         goto exit;
    575       }
    576 
    577       switch( op1 )
    578       {
    579       case cf2_cmdRESERVED_0:
    580       case cf2_cmdRESERVED_2:
    581       case cf2_cmdRESERVED_9:
    582       case cf2_cmdRESERVED_13:
    583       case cf2_cmdRESERVED_15:
    584       case cf2_cmdRESERVED_16:
    585       case cf2_cmdRESERVED_17:
    586         /* we may get here if we have a prior error */
    587         FT_TRACE4(( " unknown op (%d)\n", op1 ));
    588         break;
    589 
    590       case cf2_cmdHSTEMHM:
    591       case cf2_cmdHSTEM:
    592         FT_TRACE4(( op1 == cf2_cmdHSTEMHM ? " hstemhm\n" : " hstem\n" ));
    593 
    594         /* never add hints after the mask is computed */
    595         if ( cf2_hintmask_isValid( &hintMask ) )
    596           FT_TRACE4(( "cf2_interpT2CharString:"
    597                       " invalid horizontal hint mask\n" ));
    598 
    599         cf2_doStems( font,
    600                      opStack,
    601                      &hStemHintArray,
    602                      width,
    603                      &haveWidth,
    604                      0 );
    605 
    606         if ( font->decoder->width_only )
    607             goto exit;
    608 
    609         break;
    610 
    611       case cf2_cmdVSTEMHM:
    612       case cf2_cmdVSTEM:
    613         FT_TRACE4(( op1 == cf2_cmdVSTEMHM ? " vstemhm\n" : " vstem\n" ));
    614 
    615         /* never add hints after the mask is computed */
    616         if ( cf2_hintmask_isValid( &hintMask ) )
    617           FT_TRACE4(( "cf2_interpT2CharString:"
    618                       " invalid vertical hint mask\n" ));
    619 
    620         cf2_doStems( font,
    621                      opStack,
    622                      &vStemHintArray,
    623                      width,
    624                      &haveWidth,
    625                      0 );
    626 
    627         if ( font->decoder->width_only )
    628             goto exit;
    629 
    630         break;
    631 
    632       case cf2_cmdVMOVETO:
    633         FT_TRACE4(( " vmoveto\n" ));
    634 
    635         if ( cf2_stack_count( opStack ) > 1 && !haveWidth )
    636           *width = cf2_stack_getReal( opStack, 0 ) + nominalWidthX;
    637 
    638         /* width is defined or default after this */
    639         haveWidth = TRUE;
    640 
    641         if ( font->decoder->width_only )
    642             goto exit;
    643 
    644         curY += cf2_stack_popFixed( opStack );
    645 
    646         cf2_glyphpath_moveTo( &glyphPath, curX, curY );
    647 
    648         break;
    649 
    650       case cf2_cmdRLINETO:
    651         {
    652           CF2_UInt  index;
    653           CF2_UInt  count = cf2_stack_count( opStack );
    654 
    655 
    656           FT_TRACE4(( " rlineto\n" ));
    657 
    658           for ( index = 0; index < count; index += 2 )
    659           {
    660             curX += cf2_stack_getReal( opStack, index + 0 );
    661             curY += cf2_stack_getReal( opStack, index + 1 );
    662 
    663             cf2_glyphpath_lineTo( &glyphPath, curX, curY );
    664           }
    665 
    666           cf2_stack_clear( opStack );
    667         }
    668         continue; /* no need to clear stack again */
    669 
    670       case cf2_cmdHLINETO:
    671       case cf2_cmdVLINETO:
    672         {
    673           CF2_UInt  index;
    674           CF2_UInt  count = cf2_stack_count( opStack );
    675 
    676           FT_Bool  isX = op1 == cf2_cmdHLINETO;
    677 
    678 
    679           FT_TRACE4(( isX ? " hlineto\n" : " vlineto\n" ));
    680 
    681           for ( index = 0; index < count; index++ )
    682           {
    683             CF2_Fixed  v = cf2_stack_getReal( opStack, index );
    684 
    685 
    686             if ( isX )
    687               curX += v;
    688             else
    689               curY += v;
    690 
    691             isX = !isX;
    692 
    693             cf2_glyphpath_lineTo( &glyphPath, curX, curY );
    694           }
    695 
    696           cf2_stack_clear( opStack );
    697         }
    698         continue;
    699 
    700       case cf2_cmdRCURVELINE:
    701       case cf2_cmdRRCURVETO:
    702         {
    703           CF2_UInt  count = cf2_stack_count( opStack );
    704           CF2_UInt  index = 0;
    705 
    706 
    707           FT_TRACE4(( op1 == cf2_cmdRCURVELINE ? " rcurveline\n"
    708                                                : " rrcurveto\n" ));
    709 
    710           while ( index + 6 <= count )
    711           {
    712             CF2_Fixed  x1 = cf2_stack_getReal( opStack, index + 0 ) + curX;
    713             CF2_Fixed  y1 = cf2_stack_getReal( opStack, index + 1 ) + curY;
    714             CF2_Fixed  x2 = cf2_stack_getReal( opStack, index + 2 ) + x1;
    715             CF2_Fixed  y2 = cf2_stack_getReal( opStack, index + 3 ) + y1;
    716             CF2_Fixed  x3 = cf2_stack_getReal( opStack, index + 4 ) + x2;
    717             CF2_Fixed  y3 = cf2_stack_getReal( opStack, index + 5 ) + y2;
    718 
    719 
    720             cf2_glyphpath_curveTo( &glyphPath, x1, y1, x2, y2, x3, y3 );
    721 
    722             curX   = x3;
    723             curY   = y3;
    724             index += 6;
    725           }
    726 
    727           if ( op1 == cf2_cmdRCURVELINE )
    728           {
    729             curX += cf2_stack_getReal( opStack, index + 0 );
    730             curY += cf2_stack_getReal( opStack, index + 1 );
    731 
    732             cf2_glyphpath_lineTo( &glyphPath, curX, curY );
    733           }
    734 
    735           cf2_stack_clear( opStack );
    736         }
    737         continue; /* no need to clear stack again */
    738 
    739       case cf2_cmdCALLGSUBR:
    740       case cf2_cmdCALLSUBR:
    741         {
    742           CF2_UInt  subrIndex;
    743 
    744 
    745           FT_TRACE4(( op1 == cf2_cmdCALLGSUBR ? " callgsubr"
    746                                               : " callsubr" ));
    747 
    748           if ( charstringIndex > CF2_MAX_SUBR )
    749           {
    750             /* max subr plus one for charstring */
    751             lastError = FT_THROW( Invalid_Glyph_Format );
    752             goto exit;                      /* overflow of stack */
    753           }
    754 
    755           /* push our current CFF charstring region on subrStack */
    756           charstring = (CF2_Buffer)
    757                          cf2_arrstack_getPointer( &subrStack,
    758                                                   charstringIndex + 1 );
    759 
    760           /* set up the new CFF region and pointer */
    761           subrIndex = cf2_stack_popInt( opStack );
    762 
    763           switch ( op1 )
    764           {
    765           case cf2_cmdCALLGSUBR:
    766             FT_TRACE4(( "(%d)\n", subrIndex + decoder->globals_bias ));
    767 
    768             if ( cf2_initGlobalRegionBuffer( decoder,
    769                                              subrIndex,
    770                                              charstring ) )
    771             {
    772               lastError = FT_THROW( Invalid_Glyph_Format );
    773               goto exit;  /* subroutine lookup or stream error */
    774             }
    775             break;
    776 
    777           default:
    778             /* cf2_cmdCALLSUBR */
    779             FT_TRACE4(( "(%d)\n", subrIndex + decoder->locals_bias ));
    780 
    781             if ( cf2_initLocalRegionBuffer( decoder,
    782                                             subrIndex,
    783                                             charstring ) )
    784             {
    785               lastError = FT_THROW( Invalid_Glyph_Format );
    786               goto exit;  /* subroutine lookup or stream error */
    787             }
    788           }
    789 
    790           charstringIndex += 1;       /* entry is valid now */
    791         }
    792         continue; /* do not clear the stack */
    793 
    794       case cf2_cmdRETURN:
    795         FT_TRACE4(( " return\n" ));
    796 
    797         if ( charstringIndex < 1 )
    798         {
    799           /* Note: cannot return from top charstring */
    800           lastError = FT_THROW( Invalid_Glyph_Format );
    801           goto exit;                      /* underflow of stack */
    802         }
    803 
    804         /* restore position in previous charstring */
    805         charstring = (CF2_Buffer)
    806                        cf2_arrstack_getPointer( &subrStack,
    807                                                 --charstringIndex );
    808         continue;     /* do not clear the stack */
    809 
    810       case cf2_cmdESC:
    811         {
    812           FT_Byte  op2 = (FT_Byte)cf2_buf_readByte( charstring );
    813 
    814 
    815           switch ( op2 )
    816           {
    817           case cf2_escDOTSECTION:
    818             /* something about `flip type of locking' -- ignore it */
    819             FT_TRACE4(( " dotsection\n" ));
    820 
    821             break;
    822 
    823           /* TODO: should these operators be supported? */
    824           case cf2_escAND: /* in spec */
    825             FT_TRACE4(( " and\n" ));
    826 
    827             CF2_FIXME;
    828             break;
    829 
    830           case cf2_escOR: /* in spec */
    831             FT_TRACE4(( " or\n" ));
    832 
    833             CF2_FIXME;
    834             break;
    835 
    836           case cf2_escNOT: /* in spec */
    837             FT_TRACE4(( " not\n" ));
    838 
    839             CF2_FIXME;
    840             break;
    841 
    842           case cf2_escABS: /* in spec */
    843             FT_TRACE4(( " abs\n" ));
    844 
    845             CF2_FIXME;
    846             break;
    847 
    848           case cf2_escADD: /* in spec */
    849             FT_TRACE4(( " add\n" ));
    850 
    851             CF2_FIXME;
    852             break;
    853 
    854           case cf2_escSUB: /* in spec */
    855             FT_TRACE4(( " sub\n" ));
    856 
    857             CF2_FIXME;
    858             break;
    859 
    860           case cf2_escDIV: /* in spec */
    861             FT_TRACE4(( " div\n" ));
    862 
    863             CF2_FIXME;
    864             break;
    865 
    866           case cf2_escNEG: /* in spec */
    867             FT_TRACE4(( " neg\n" ));
    868 
    869             CF2_FIXME;
    870             break;
    871 
    872           case cf2_escEQ: /* in spec */
    873             FT_TRACE4(( " eq\n" ));
    874 
    875             CF2_FIXME;
    876             break;
    877 
    878           case cf2_escDROP: /* in spec */
    879             FT_TRACE4(( " drop\n" ));
    880 
    881             CF2_FIXME;
    882             break;
    883 
    884           case cf2_escPUT: /* in spec */
    885             FT_TRACE4(( " put\n" ));
    886 
    887             CF2_FIXME;
    888             break;
    889 
    890           case cf2_escGET: /* in spec */
    891             FT_TRACE4(( " get\n" ));
    892 
    893             CF2_FIXME;
    894             break;
    895 
    896           case cf2_escIFELSE: /* in spec */
    897             FT_TRACE4(( " ifelse\n" ));
    898 
    899             CF2_FIXME;
    900             break;
    901 
    902           case cf2_escRANDOM: /* in spec */
    903             FT_TRACE4(( " random\n" ));
    904 
    905             CF2_FIXME;
    906             break;
    907 
    908           case cf2_escMUL: /* in spec */
    909             FT_TRACE4(( " mul\n" ));
    910 
    911             CF2_FIXME;
    912             break;
    913 
    914           case cf2_escSQRT: /* in spec */
    915             FT_TRACE4(( " sqrt\n" ));
    916 
    917             CF2_FIXME;
    918             break;
    919 
    920           case cf2_escDUP: /* in spec */
    921             FT_TRACE4(( " dup\n" ));
    922 
    923             CF2_FIXME;
    924             break;
    925 
    926           case cf2_escEXCH: /* in spec */
    927             FT_TRACE4(( " exch\n" ));
    928 
    929             CF2_FIXME;
    930             break;
    931 
    932           case cf2_escINDEX: /* in spec */
    933             FT_TRACE4(( " index\n" ));
    934 
    935             CF2_FIXME;
    936             break;
    937 
    938           case cf2_escROLL: /* in spec */
    939             FT_TRACE4(( " roll\n" ));
    940 
    941             CF2_FIXME;
    942             break;
    943 
    944           case cf2_escHFLEX:
    945             {
    946               static const FT_Bool  readFromStack[12] =
    947               {
    948                 TRUE /* dx1 */, FALSE /* dy1 */,
    949                 TRUE /* dx2 */, TRUE  /* dy2 */,
    950                 TRUE /* dx3 */, FALSE /* dy3 */,
    951                 TRUE /* dx4 */, FALSE /* dy4 */,
    952                 TRUE /* dx5 */, FALSE /* dy5 */,
    953                 TRUE /* dx6 */, FALSE /* dy6 */
    954               };
    955 
    956 
    957               FT_TRACE4(( " hflex\n" ));
    958 
    959               cf2_doFlex( opStack,
    960                           &curX,
    961                           &curY,
    962                           &glyphPath,
    963                           readFromStack,
    964                           FALSE /* doConditionalLastRead */ );
    965             }
    966             continue;
    967 
    968           case cf2_escFLEX:
    969             {
    970               static const FT_Bool  readFromStack[12] =
    971               {
    972                 TRUE /* dx1 */, TRUE /* dy1 */,
    973                 TRUE /* dx2 */, TRUE /* dy2 */,
    974                 TRUE /* dx3 */, TRUE /* dy3 */,
    975                 TRUE /* dx4 */, TRUE /* dy4 */,
    976                 TRUE /* dx5 */, TRUE /* dy5 */,
    977                 TRUE /* dx6 */, TRUE /* dy6 */
    978               };
    979 
    980 
    981               FT_TRACE4(( " flex\n" ));
    982 
    983               cf2_doFlex( opStack,
    984                           &curX,
    985                           &curY,
    986                           &glyphPath,
    987                           readFromStack,
    988                           FALSE /* doConditionalLastRead */ );
    989             }
    990             break;      /* TODO: why is this not a continue? */
    991 
    992           case cf2_escHFLEX1:
    993             {
    994               static const FT_Bool  readFromStack[12] =
    995               {
    996                 TRUE /* dx1 */, TRUE  /* dy1 */,
    997                 TRUE /* dx2 */, TRUE  /* dy2 */,
    998                 TRUE /* dx3 */, FALSE /* dy3 */,
    999                 TRUE /* dx4 */, FALSE /* dy4 */,
   1000                 TRUE /* dx5 */, TRUE  /* dy5 */,
   1001                 TRUE /* dx6 */, FALSE /* dy6 */
   1002               };
   1003 
   1004 
   1005               FT_TRACE4(( " hflex1\n" ));
   1006 
   1007               cf2_doFlex( opStack,
   1008                           &curX,
   1009                           &curY,
   1010                           &glyphPath,
   1011                           readFromStack,
   1012                           FALSE /* doConditionalLastRead */ );
   1013             }
   1014             continue;
   1015 
   1016           case cf2_escFLEX1:
   1017             {
   1018               static const FT_Bool  readFromStack[12] =
   1019               {
   1020                 TRUE  /* dx1 */, TRUE  /* dy1 */,
   1021                 TRUE  /* dx2 */, TRUE  /* dy2 */,
   1022                 TRUE  /* dx3 */, TRUE  /* dy3 */,
   1023                 TRUE  /* dx4 */, TRUE  /* dy4 */,
   1024                 TRUE  /* dx5 */, TRUE  /* dy5 */,
   1025                 FALSE /* dx6 */, FALSE /* dy6 */
   1026               };
   1027 
   1028 
   1029               FT_TRACE4(( " flex1\n" ));
   1030 
   1031               cf2_doFlex( opStack,
   1032                           &curX,
   1033                           &curY,
   1034                           &glyphPath,
   1035                           readFromStack,
   1036                           TRUE /* doConditionalLastRead */ );
   1037             }
   1038             continue;
   1039 
   1040           case cf2_escRESERVED_1:
   1041           case cf2_escRESERVED_2:
   1042           case cf2_escRESERVED_6:
   1043           case cf2_escRESERVED_7:
   1044           case cf2_escRESERVED_8:
   1045           case cf2_escRESERVED_13:
   1046           case cf2_escRESERVED_16:
   1047           case cf2_escRESERVED_17:
   1048           case cf2_escRESERVED_19:
   1049           case cf2_escRESERVED_25:
   1050           case cf2_escRESERVED_31:
   1051           case cf2_escRESERVED_32:
   1052           case cf2_escRESERVED_33:
   1053           default:
   1054             FT_TRACE4(( " unknown op (12, %d)\n", op2 ));
   1055 
   1056           }; /* end of switch statement checking `op2' */
   1057 
   1058         } /* case cf2_cmdESC */
   1059         break;
   1060 
   1061       case cf2_cmdENDCHAR:
   1062         FT_TRACE4(( " endchar\n" ));
   1063 
   1064         if ( cf2_stack_count( opStack ) == 1 ||
   1065              cf2_stack_count( opStack ) == 5 )
   1066         {
   1067           if ( !haveWidth )
   1068             *width = cf2_stack_getReal( opStack, 0 ) + nominalWidthX;
   1069         }
   1070 
   1071         /* width is defined or default after this */
   1072         haveWidth = TRUE;
   1073 
   1074         if ( font->decoder->width_only )
   1075             goto exit;
   1076 
   1077         /* close path if still open */
   1078         cf2_glyphpath_closeOpenPath( &glyphPath );
   1079 
   1080         if ( cf2_stack_count( opStack ) > 1 )
   1081         {
   1082           /* must be either 4 or 5 --                       */
   1083           /* this is a (deprecated) implied `seac' operator */
   1084 
   1085           CF2_UInt       achar;
   1086           CF2_UInt       bchar;
   1087           CF2_BufferRec  component;
   1088           CF2_Fixed      dummyWidth;   /* ignore component width */
   1089           FT_Error       error2;
   1090 
   1091 
   1092           if ( doingSeac )
   1093           {
   1094             lastError = FT_THROW( Invalid_Glyph_Format );
   1095             goto exit;      /* nested seac */
   1096           }
   1097 
   1098           achar = cf2_stack_popInt( opStack );
   1099           bchar = cf2_stack_popInt( opStack );
   1100 
   1101           curY = cf2_stack_popFixed( opStack );
   1102           curX = cf2_stack_popFixed( opStack );
   1103 
   1104           error2 = cf2_getSeacComponent( decoder, achar, &component );
   1105           if ( error2 )
   1106           {
   1107              lastError = error2;      /* pass FreeType error through */
   1108              goto exit;
   1109           }
   1110           cf2_interpT2CharString( font,
   1111                                   &component,
   1112                                   callbacks,
   1113                                   translation,
   1114                                   TRUE,
   1115                                   curX,
   1116                                   curY,
   1117                                   &dummyWidth );
   1118           cf2_freeSeacComponent( decoder, &component );
   1119 
   1120           error2 = cf2_getSeacComponent( decoder, bchar, &component );
   1121           if ( error2 )
   1122           {
   1123             lastError = error2;      /* pass FreeType error through */
   1124             goto exit;
   1125           }
   1126           cf2_interpT2CharString( font,
   1127                                   &component,
   1128                                   callbacks,
   1129                                   translation,
   1130                                   TRUE,
   1131                                   0,
   1132                                   0,
   1133                                   &dummyWidth );
   1134           cf2_freeSeacComponent( decoder, &component );
   1135         }
   1136         goto exit;
   1137 
   1138       case cf2_cmdCNTRMASK:
   1139       case cf2_cmdHINTMASK:
   1140         /* the final \n in the tracing message gets added in      */
   1141         /* `cf2_hintmask_read' (which also traces the mask bytes) */
   1142         FT_TRACE4(( op1 == cf2_cmdCNTRMASK ? " cntrmask" : " hintmask" ));
   1143 
   1144         /* if there are arguments on the stack, there this is an */
   1145         /* implied cf2_cmdVSTEMHM                                */
   1146         if ( cf2_stack_count( opStack ) != 0 )
   1147         {
   1148           /* never add hints after the mask is computed */
   1149           if ( cf2_hintmask_isValid( &hintMask ) )
   1150             FT_TRACE4(( "cf2_interpT2CharString: invalid hint mask\n" ));
   1151         }
   1152 
   1153         cf2_doStems( font,
   1154                      opStack,
   1155                      &vStemHintArray,
   1156                      width,
   1157                      &haveWidth,
   1158                      0 );
   1159 
   1160         if ( font->decoder->width_only )
   1161             goto exit;
   1162 
   1163         if ( op1 == cf2_cmdHINTMASK )
   1164         {
   1165           /* consume the hint mask bytes which follow the operator */
   1166           cf2_hintmask_read( &hintMask,
   1167                              charstring,
   1168                              cf2_arrstack_size( &hStemHintArray ) +
   1169                                cf2_arrstack_size( &vStemHintArray ) );
   1170         }
   1171         else
   1172         {
   1173           /*
   1174            * Consume the counter mask bytes which follow the operator:
   1175            * Build a temporary hint map, just to place and lock those
   1176            * stems participating in the counter mask.  These are most
   1177            * likely the dominant hstems, and are grouped together in a
   1178            * few counter groups, not necessarily in correspondence
   1179            * with the hint groups.  This reduces the chances of
   1180            * conflicts between hstems that are initially placed in
   1181            * separate hint groups and then brought together.  The
   1182            * positions are copied back to `hStemHintArray', so we can
   1183            * discard `counterMask' and `counterHintMap'.
   1184            *
   1185            */
   1186           CF2_HintMapRec   counterHintMap;
   1187           CF2_HintMaskRec  counterMask;
   1188 
   1189 
   1190           cf2_hintmap_init( &counterHintMap,
   1191                             font,
   1192                             &glyphPath.initialHintMap,
   1193                             &glyphPath.hintMoves,
   1194                             scaleY );
   1195           cf2_hintmask_init( &counterMask, error );
   1196 
   1197           cf2_hintmask_read( &counterMask,
   1198                              charstring,
   1199                              cf2_arrstack_size( &hStemHintArray ) +
   1200                                cf2_arrstack_size( &vStemHintArray ) );
   1201           cf2_hintmap_build( &counterHintMap,
   1202                              &hStemHintArray,
   1203                              &vStemHintArray,
   1204                              &counterMask,
   1205                              0,
   1206                              FALSE );
   1207         }
   1208         break;
   1209 
   1210       case cf2_cmdRMOVETO:
   1211         FT_TRACE4(( " rmoveto\n" ));
   1212 
   1213         if ( cf2_stack_count( opStack ) > 2 && !haveWidth )
   1214           *width = cf2_stack_getReal( opStack, 0 ) + nominalWidthX;
   1215 
   1216         /* width is defined or default after this */
   1217         haveWidth = TRUE;
   1218 
   1219         if ( font->decoder->width_only )
   1220             goto exit;
   1221 
   1222         curY += cf2_stack_popFixed( opStack );
   1223         curX += cf2_stack_popFixed( opStack );
   1224 
   1225         cf2_glyphpath_moveTo( &glyphPath, curX, curY );
   1226 
   1227         break;
   1228 
   1229       case cf2_cmdHMOVETO:
   1230         FT_TRACE4(( " hmoveto\n" ));
   1231 
   1232         if ( cf2_stack_count( opStack ) > 1 && !haveWidth )
   1233           *width = cf2_stack_getReal( opStack, 0 ) + nominalWidthX;
   1234 
   1235         /* width is defined or default after this */
   1236         haveWidth = TRUE;
   1237 
   1238         if ( font->decoder->width_only )
   1239             goto exit;
   1240 
   1241         curX += cf2_stack_popFixed( opStack );
   1242 
   1243         cf2_glyphpath_moveTo( &glyphPath, curX, curY );
   1244 
   1245         break;
   1246 
   1247       case cf2_cmdRLINECURVE:
   1248         {
   1249           CF2_UInt  count = cf2_stack_count( opStack );
   1250           CF2_UInt  index = 0;
   1251 
   1252 
   1253           FT_TRACE4(( " rlinecurve\n" ));
   1254 
   1255           while ( index + 6 < count )
   1256           {
   1257             curX += cf2_stack_getReal( opStack, index + 0 );
   1258             curY += cf2_stack_getReal( opStack, index + 1 );
   1259 
   1260             cf2_glyphpath_lineTo( &glyphPath, curX, curY );
   1261             index += 2;
   1262           }
   1263 
   1264           while ( index < count )
   1265           {
   1266             CF2_Fixed  x1 = cf2_stack_getReal( opStack, index + 0 ) + curX;
   1267             CF2_Fixed  y1 = cf2_stack_getReal( opStack, index + 1 ) + curY;
   1268             CF2_Fixed  x2 = cf2_stack_getReal( opStack, index + 2 ) + x1;
   1269             CF2_Fixed  y2 = cf2_stack_getReal( opStack, index + 3 ) + y1;
   1270             CF2_Fixed  x3 = cf2_stack_getReal( opStack, index + 4 ) + x2;
   1271             CF2_Fixed  y3 = cf2_stack_getReal( opStack, index + 5 ) + y2;
   1272 
   1273 
   1274             cf2_glyphpath_curveTo( &glyphPath, x1, y1, x2, y2, x3, y3 );
   1275 
   1276             curX   = x3;
   1277             curY   = y3;
   1278             index += 6;
   1279           }
   1280 
   1281           cf2_stack_clear( opStack );
   1282         }
   1283         continue; /* no need to clear stack again */
   1284 
   1285       case cf2_cmdVVCURVETO:
   1286         {
   1287           CF2_UInt  count = cf2_stack_count( opStack );
   1288           CF2_UInt  index = 0;
   1289 
   1290 
   1291           FT_TRACE4(( " vvcurveto\n" ));
   1292 
   1293           while ( index < count )
   1294           {
   1295             CF2_Fixed  x1, y1, x2, y2, x3, y3;
   1296 
   1297 
   1298             if ( ( count - index ) & 1 )
   1299             {
   1300               x1 = cf2_stack_getReal( opStack, index ) + curX;
   1301 
   1302               ++index;
   1303             }
   1304             else
   1305               x1 = curX;
   1306 
   1307             y1 = cf2_stack_getReal( opStack, index + 0 ) + curY;
   1308             x2 = cf2_stack_getReal( opStack, index + 1 ) + x1;
   1309             y2 = cf2_stack_getReal( opStack, index + 2 ) + y1;
   1310             x3 = x2;
   1311             y3 = cf2_stack_getReal( opStack, index + 3 ) + y2;
   1312 
   1313             cf2_glyphpath_curveTo( &glyphPath, x1, y1, x2, y2, x3, y3 );
   1314 
   1315             curX   = x3;
   1316             curY   = y3;
   1317             index += 4;
   1318           }
   1319 
   1320           cf2_stack_clear( opStack );
   1321         }
   1322         continue; /* no need to clear stack again */
   1323 
   1324       case cf2_cmdHHCURVETO:
   1325         {
   1326           CF2_UInt  count = cf2_stack_count( opStack );
   1327           CF2_UInt  index = 0;
   1328 
   1329 
   1330           FT_TRACE4(( " hhcurveto\n" ));
   1331 
   1332           while ( index < count )
   1333           {
   1334             CF2_Fixed  x1, y1, x2, y2, x3, y3;
   1335 
   1336 
   1337             if ( ( count - index ) & 1 )
   1338             {
   1339               y1 = cf2_stack_getReal( opStack, index ) + curY;
   1340 
   1341               ++index;
   1342             }
   1343             else
   1344               y1 = curY;
   1345 
   1346             x1 = cf2_stack_getReal( opStack, index + 0 ) + curX;
   1347             x2 = cf2_stack_getReal( opStack, index + 1 ) + x1;
   1348             y2 = cf2_stack_getReal( opStack, index + 2 ) + y1;
   1349             x3 = cf2_stack_getReal( opStack, index + 3 ) + x2;
   1350             y3 = y2;
   1351 
   1352             cf2_glyphpath_curveTo( &glyphPath, x1, y1, x2, y2, x3, y3 );
   1353 
   1354             curX   = x3;
   1355             curY   = y3;
   1356             index += 4;
   1357           }
   1358 
   1359           cf2_stack_clear( opStack );
   1360         }
   1361         continue; /* no need to clear stack again */
   1362 
   1363       case cf2_cmdVHCURVETO:
   1364       case cf2_cmdHVCURVETO:
   1365         {
   1366           CF2_UInt  count = cf2_stack_count( opStack );
   1367           CF2_UInt  index = 0;
   1368 
   1369           FT_Bool  alternate = op1 == cf2_cmdHVCURVETO;
   1370 
   1371 
   1372           FT_TRACE4(( alternate ? " hvcurveto\n" : " vhcurveto\n" ));
   1373 
   1374           while ( index < count )
   1375           {
   1376             CF2_Fixed x1, x2, x3, y1, y2, y3;
   1377 
   1378 
   1379             if ( alternate )
   1380             {
   1381               x1 = cf2_stack_getReal( opStack, index + 0 ) + curX;
   1382               y1 = curY;
   1383               x2 = cf2_stack_getReal( opStack, index + 1 ) + x1;
   1384               y2 = cf2_stack_getReal( opStack, index + 2 ) + y1;
   1385               y3 = cf2_stack_getReal( opStack, index + 3 ) + y2;
   1386 
   1387               if ( count - index == 5 )
   1388               {
   1389                 x3 = cf2_stack_getReal( opStack, index + 4 ) + x2;
   1390 
   1391                 ++index;
   1392               }
   1393               else
   1394                 x3 = x2;
   1395 
   1396               alternate = FALSE;
   1397             }
   1398             else
   1399             {
   1400               x1 = curX;
   1401               y1 = cf2_stack_getReal( opStack, index + 0 ) + curY;
   1402               x2 = cf2_stack_getReal( opStack, index + 1 ) + x1;
   1403               y2 = cf2_stack_getReal( opStack, index + 2 ) + y1;
   1404               x3 = cf2_stack_getReal( opStack, index + 3 ) + x2;
   1405 
   1406               if ( count - index == 5 )
   1407               {
   1408                 y3 = cf2_stack_getReal( opStack, index + 4 ) + y2;
   1409 
   1410                 ++index;
   1411               }
   1412               else
   1413                 y3 = y2;
   1414 
   1415               alternate = TRUE;
   1416             }
   1417 
   1418             cf2_glyphpath_curveTo( &glyphPath, x1, y1, x2, y2, x3, y3 );
   1419 
   1420             curX   = x3;
   1421             curY   = y3;
   1422             index += 4;
   1423           }
   1424 
   1425           cf2_stack_clear( opStack );
   1426         }
   1427         continue;     /* no need to clear stack again */
   1428 
   1429       case cf2_cmdEXTENDEDNMBR:
   1430         {
   1431           CF2_Int  v;
   1432 
   1433 
   1434           v = (FT_Short)( ( cf2_buf_readByte( charstring ) << 8 ) |
   1435                             cf2_buf_readByte( charstring )        );
   1436 
   1437           FT_TRACE4(( " %d", v ));
   1438 
   1439           cf2_stack_pushInt( opStack, v );
   1440         }
   1441         continue;
   1442 
   1443       default:
   1444         /* numbers */
   1445         {
   1446           if ( /* op1 >= 32 && */ op1 <= 246 )
   1447           {
   1448             CF2_Int  v;
   1449 
   1450 
   1451             v = op1 - 139;
   1452 
   1453             FT_TRACE4(( " %d", v ));
   1454 
   1455             /* -107 .. 107 */
   1456             cf2_stack_pushInt( opStack, v );
   1457           }
   1458 
   1459           else if ( /* op1 >= 247 && */ op1 <= 250 )
   1460           {
   1461             CF2_Int  v;
   1462 
   1463 
   1464             v  = op1;
   1465             v -= 247;
   1466             v *= 256;
   1467             v += cf2_buf_readByte( charstring );
   1468             v += 108;
   1469 
   1470             FT_TRACE4(( " %d", v ));
   1471 
   1472             /* 108 .. 1131 */
   1473             cf2_stack_pushInt( opStack, v );
   1474           }
   1475 
   1476           else if ( /* op1 >= 251 && */ op1 <= 254 )
   1477           {
   1478             CF2_Int  v;
   1479 
   1480 
   1481             v  = op1;
   1482             v -= 251;
   1483             v *= 256;
   1484             v += cf2_buf_readByte( charstring );
   1485             v  = -v - 108;
   1486 
   1487             FT_TRACE4(( " %d", v ));
   1488 
   1489             /* -1131 .. -108 */
   1490             cf2_stack_pushInt( opStack, v );
   1491           }
   1492 
   1493           else /* op1 == 255 */
   1494           {
   1495             CF2_Fixed  v;
   1496 
   1497 
   1498             v = (CF2_Fixed)
   1499                   ( ( (FT_UInt32)cf2_buf_readByte( charstring ) << 24 ) |
   1500                     ( (FT_UInt32)cf2_buf_readByte( charstring ) << 16 ) |
   1501                     ( (FT_UInt32)cf2_buf_readByte( charstring ) <<  8 ) |
   1502                       (FT_UInt32)cf2_buf_readByte( charstring )         );
   1503 
   1504             FT_TRACE4(( " %.2f", v / 65536.0 ));
   1505 
   1506             cf2_stack_pushFixed( opStack, v );
   1507           }
   1508         }
   1509         continue;   /* don't clear stack */
   1510 
   1511       } /* end of switch statement checking `op1' */
   1512 
   1513       cf2_stack_clear( opStack );
   1514 
   1515     } /* end of main interpreter loop */
   1516 
   1517     /* we get here if the charstring ends without cf2_cmdENDCHAR */
   1518     FT_TRACE4(( "cf2_interpT2CharString:"
   1519                 "  charstring ends without ENDCHAR\n" ));
   1520 
   1521   exit:
   1522     /* check whether last error seen is also the first one */
   1523     cf2_setError( error, lastError );
   1524 
   1525     /* free resources from objects we've used */
   1526     cf2_glyphpath_finalize( &glyphPath );
   1527     cf2_arrstack_finalize( &vStemHintArray );
   1528     cf2_arrstack_finalize( &hStemHintArray );
   1529     cf2_arrstack_finalize( &subrStack );
   1530     cf2_stack_free( opStack );
   1531 
   1532     FT_TRACE4(( "\n" ));
   1533 
   1534     return;
   1535   }
   1536 
   1537 
   1538 /* END */
   1539