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