Home | History | Annotate | Download | only in src
      1 /*
      2  * Copyright  2018 Adobe Inc.
      3  *
      4  *  This is part of HarfBuzz, a text shaping library.
      5  *
      6  * Permission is hereby granted, without written agreement and without
      7  * license or royalty fees, to use, copy, modify, and distribute this
      8  * software and its documentation for any purpose, provided that the
      9  * above copyright notice and the following two paragraphs appear in
     10  * all copies of this software.
     11  *
     12  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
     13  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
     14  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
     15  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
     16  * DAMAGE.
     17  *
     18  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
     19  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
     20  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
     21  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
     22  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
     23  *
     24  * Adobe Author(s): Michiharu Ariza
     25  */
     26 #ifndef HB_CFF_INTERP_CS_COMMON_HH
     27 #define HB_CFF_INTERP_CS_COMMON_HH
     28 
     29 #include "hb.hh"
     30 #include "hb-cff-interp-common.hh"
     31 
     32 namespace CFF {
     33 
     34 using namespace OT;
     35 
     36 enum CSType {
     37   CSType_CharString,
     38   CSType_GlobalSubr,
     39   CSType_LocalSubr
     40 };
     41 
     42 struct CallContext
     43 {
     44   void init (const SubByteStr substr_=SubByteStr (), CSType type_=CSType_CharString, unsigned int subr_num_=0)
     45   {
     46     substr = substr_;
     47     type = type_;
     48     subr_num = subr_num_;
     49   }
     50 
     51   void fini () {}
     52 
     53   SubByteStr      substr;
     54   CSType	  type;
     55   unsigned int    subr_num;
     56 };
     57 
     58 /* call stack */
     59 const unsigned int kMaxCallLimit = 10;
     60 struct CallStack : Stack<CallContext, kMaxCallLimit> {};
     61 
     62 template <typename SUBRS>
     63 struct BiasedSubrs
     64 {
     65   void init (const SUBRS &subrs_)
     66   {
     67     subrs = &subrs_;
     68     unsigned int  nSubrs = get_count ();
     69     if (nSubrs < 1240)
     70       bias = 107;
     71     else if (nSubrs < 33900)
     72       bias = 1131;
     73     else
     74       bias = 32768;
     75   }
     76 
     77   void fini () {}
     78 
     79   unsigned int get_count () const { return (subrs == nullptr)? 0: subrs->count; }
     80   unsigned int get_bias () const { return bias; }
     81 
     82   ByteStr operator [] (unsigned int index) const
     83   {
     84     if (unlikely ((subrs == nullptr) || index >= subrs->count))
     85       return Null(ByteStr);
     86     else
     87       return (*subrs)[index];
     88   }
     89 
     90   protected:
     91   unsigned int  bias;
     92   const SUBRS   *subrs;
     93 };
     94 
     95 struct Point
     96 {
     97   void init ()
     98   {
     99     x.init ();
    100     y.init ();
    101   }
    102 
    103   void set_int (int _x, int _y)
    104   {
    105     x.set_int (_x);
    106     y.set_int (_y);
    107   }
    108 
    109   void move_x (const Number &dx) { x += dx; }
    110   void move_y (const Number &dy) { y += dy; }
    111   void move (const Number &dx, const Number &dy) { move_x (dx); move_y (dy); }
    112   void move (const Point &d) { move_x (d.x); move_y (d.y); }
    113 
    114   Number  x;
    115   Number  y;
    116 };
    117 
    118 template <typename ARG, typename SUBRS>
    119 struct CSInterpEnv : InterpEnv<ARG>
    120 {
    121   void init (const ByteStr &str, const SUBRS &globalSubrs_, const SUBRS &localSubrs_)
    122   {
    123     InterpEnv<ARG>::init (str);
    124 
    125     context.init (str, CSType_CharString);
    126     seen_moveto = true;
    127     seen_hintmask = false;
    128     hstem_count = 0;
    129     vstem_count = 0;
    130     pt.init ();
    131     callStack.init ();
    132     globalSubrs.init (globalSubrs_);
    133     localSubrs.init (localSubrs_);
    134   }
    135   void fini ()
    136   {
    137     InterpEnv<ARG>::fini ();
    138 
    139     callStack.fini ();
    140     globalSubrs.fini ();
    141     localSubrs.fini ();
    142   }
    143 
    144   bool in_error () const
    145   {
    146     return callStack.in_error () || SUPER::in_error ();
    147   }
    148 
    149   bool popSubrNum (const BiasedSubrs<SUBRS>& biasedSubrs, unsigned int &subr_num)
    150   {
    151     int n = SUPER::argStack.pop_int ();
    152     n += biasedSubrs.get_bias ();
    153     if (unlikely ((n < 0) || ((unsigned int)n >= biasedSubrs.get_count ())))
    154       return false;
    155 
    156     subr_num = (unsigned int)n;
    157     return true;
    158   }
    159 
    160   void callSubr (const BiasedSubrs<SUBRS>& biasedSubrs, CSType type)
    161   {
    162     unsigned int subr_num;
    163 
    164     if (unlikely (!popSubrNum (biasedSubrs, subr_num)
    165 		 || callStack.get_count () >= kMaxCallLimit))
    166     {
    167       SUPER::set_error ();
    168       return;
    169     }
    170     context.substr = SUPER::substr;
    171     callStack.push (context);
    172 
    173     context.init ( biasedSubrs[subr_num], type, subr_num);
    174     SUPER::substr = context.substr;
    175   }
    176 
    177   void returnFromSubr ()
    178   {
    179     if (unlikely (SUPER::substr.in_error ()))
    180       SUPER::set_error ();
    181     context = callStack.pop ();
    182     SUPER::substr = context.substr;
    183   }
    184 
    185   void determine_hintmask_size ()
    186   {
    187     if (!seen_hintmask)
    188     {
    189       vstem_count += SUPER::argStack.get_count() / 2;
    190       hintmask_size = (hstem_count + vstem_count + 7) >> 3;
    191       seen_hintmask = true;
    192     }
    193   }
    194 
    195   void set_endchar (bool endchar_flag_) { endchar_flag = endchar_flag_; }
    196   bool is_endchar () const { return endchar_flag; }
    197 
    198   const Number &get_x () const { return pt.x; }
    199   const Number &get_y () const { return pt.y; }
    200   const Point &get_pt () const { return pt; }
    201 
    202   void moveto (const Point &pt_ ) { pt = pt_; }
    203 
    204   public:
    205   CallContext   context;
    206   bool	  endchar_flag;
    207   bool	  seen_moveto;
    208   bool	  seen_hintmask;
    209 
    210   unsigned int  hstem_count;
    211   unsigned int  vstem_count;
    212   unsigned int  hintmask_size;
    213   CallStack	    callStack;
    214   BiasedSubrs<SUBRS>   globalSubrs;
    215   BiasedSubrs<SUBRS>   localSubrs;
    216 
    217   private:
    218   Point	 pt;
    219 
    220   typedef InterpEnv<ARG> SUPER;
    221 };
    222 
    223 template <typename ENV, typename PARAM>
    224 struct PathProcsNull
    225 {
    226   static void rmoveto (ENV &env, PARAM& param) {}
    227   static void hmoveto (ENV &env, PARAM& param) {}
    228   static void vmoveto (ENV &env, PARAM& param) {}
    229   static void rlineto (ENV &env, PARAM& param) {}
    230   static void hlineto (ENV &env, PARAM& param) {}
    231   static void vlineto (ENV &env, PARAM& param) {}
    232   static void rrcurveto (ENV &env, PARAM& param) {}
    233   static void rcurveline (ENV &env, PARAM& param) {}
    234   static void rlinecurve (ENV &env, PARAM& param) {}
    235   static void vvcurveto (ENV &env, PARAM& param) {}
    236   static void hhcurveto (ENV &env, PARAM& param) {}
    237   static void vhcurveto (ENV &env, PARAM& param) {}
    238   static void hvcurveto (ENV &env, PARAM& param) {}
    239   static void moveto (ENV &env, PARAM& param, const Point &pt) {}
    240   static void line (ENV &env, PARAM& param, const Point &pt1) {}
    241   static void curve (ENV &env, PARAM& param, const Point &pt1, const Point &pt2, const Point &pt3) {}
    242   static void hflex (ENV &env, PARAM& param) {}
    243   static void flex (ENV &env, PARAM& param) {}
    244   static void hflex1 (ENV &env, PARAM& param) {}
    245   static void flex1 (ENV &env, PARAM& param) {}
    246 };
    247 
    248 template <typename ARG, typename OPSET, typename ENV, typename PARAM, typename PATH=PathProcsNull<ENV, PARAM> >
    249 struct CSOpSet : OpSet<ARG>
    250 {
    251   static void process_op (OpCode op, ENV &env, PARAM& param)
    252   {
    253     switch (op) {
    254 
    255       case OpCode_return:
    256 	env.returnFromSubr ();
    257 	break;
    258       case OpCode_endchar:
    259 	OPSET::check_width (op, env, param);
    260 	env.set_endchar (true);
    261 	OPSET::flush_args_and_op (op, env, param);
    262 	break;
    263 
    264       case OpCode_fixedcs:
    265 	env.argStack.push_fixed_from_substr (env.substr);
    266 	break;
    267 
    268       case OpCode_callsubr:
    269 	env.callSubr (env.localSubrs, CSType_LocalSubr);
    270 	break;
    271 
    272       case OpCode_callgsubr:
    273 	env.callSubr (env.globalSubrs, CSType_GlobalSubr);
    274 	break;
    275 
    276       case OpCode_hstem:
    277       case OpCode_hstemhm:
    278 	OPSET::check_width (op, env, param);
    279 	OPSET::process_hstem (op, env, param);
    280 	break;
    281       case OpCode_vstem:
    282       case OpCode_vstemhm:
    283 	OPSET::check_width (op, env, param);
    284 	OPSET::process_vstem (op, env, param);
    285 	break;
    286       case OpCode_hintmask:
    287       case OpCode_cntrmask:
    288 	OPSET::check_width (op, env, param);
    289 	OPSET::process_hintmask (op, env, param);
    290 	break;
    291       case OpCode_rmoveto:
    292 	OPSET::check_width (op, env, param);
    293 	PATH::rmoveto (env, param);
    294 	OPSET::process_post_move (op, env, param);
    295 	break;
    296       case OpCode_hmoveto:
    297 	OPSET::check_width (op, env, param);
    298 	PATH::hmoveto (env, param);
    299 	OPSET::process_post_move (op, env, param);
    300 	break;
    301       case OpCode_vmoveto:
    302 	OPSET::check_width (op, env, param);
    303 	PATH::vmoveto (env, param);
    304 	OPSET::process_post_move (op, env, param);
    305 	break;
    306       case OpCode_rlineto:
    307 	PATH::rlineto (env, param);
    308 	process_post_path (op, env, param);
    309 	break;
    310       case OpCode_hlineto:
    311 	PATH::hlineto (env, param);
    312 	process_post_path (op, env, param);
    313 	break;
    314       case OpCode_vlineto:
    315 	PATH::vlineto (env, param);
    316 	process_post_path (op, env, param);
    317 	break;
    318       case OpCode_rrcurveto:
    319 	PATH::rrcurveto (env, param);
    320 	process_post_path (op, env, param);
    321 	break;
    322       case OpCode_rcurveline:
    323 	PATH::rcurveline (env, param);
    324 	process_post_path (op, env, param);
    325 	break;
    326       case OpCode_rlinecurve:
    327 	PATH::rlinecurve (env, param);
    328 	process_post_path (op, env, param);
    329 	break;
    330       case OpCode_vvcurveto:
    331 	PATH::vvcurveto (env, param);
    332 	process_post_path (op, env, param);
    333 	break;
    334       case OpCode_hhcurveto:
    335 	PATH::hhcurveto (env, param);
    336 	process_post_path (op, env, param);
    337 	break;
    338       case OpCode_vhcurveto:
    339 	PATH::vhcurveto (env, param);
    340 	process_post_path (op, env, param);
    341 	break;
    342       case OpCode_hvcurveto:
    343 	PATH::hvcurveto (env, param);
    344 	process_post_path (op, env, param);
    345 	break;
    346 
    347       case OpCode_hflex:
    348 	PATH::hflex (env, param);
    349 	OPSET::process_post_flex (op, env, param);
    350 	break;
    351 
    352       case OpCode_flex:
    353 	PATH::flex (env, param);
    354 	OPSET::process_post_flex (op, env, param);
    355 	break;
    356 
    357       case OpCode_hflex1:
    358 	PATH::hflex1 (env, param);
    359 	OPSET::process_post_flex (op, env, param);
    360 	break;
    361 
    362       case OpCode_flex1:
    363 	PATH::flex1 (env, param);
    364 	OPSET::process_post_flex (op, env, param);
    365 	break;
    366 
    367       default:
    368 	SUPER::process_op (op, env);
    369 	break;
    370     }
    371   }
    372 
    373   static void process_hstem (OpCode op, ENV &env, PARAM& param)
    374   {
    375     env.hstem_count += env.argStack.get_count () / 2;
    376     OPSET::flush_args_and_op (op, env, param);
    377   }
    378 
    379   static void process_vstem (OpCode op, ENV &env, PARAM& param)
    380   {
    381     env.vstem_count += env.argStack.get_count () / 2;
    382     OPSET::flush_args_and_op (op, env, param);
    383   }
    384 
    385   static void process_hintmask (OpCode op, ENV &env, PARAM& param)
    386   {
    387     env.determine_hintmask_size ();
    388     if (likely (env.substr.avail (env.hintmask_size)))
    389     {
    390       OPSET::flush_hintmask (op, env, param);
    391       env.substr.inc (env.hintmask_size);
    392     }
    393   }
    394 
    395   static void process_post_flex (OpCode op, ENV &env, PARAM& param)
    396   {
    397     OPSET::flush_args_and_op (op, env, param);
    398   }
    399 
    400   static void check_width (OpCode op, ENV &env, PARAM& param)
    401   {}
    402 
    403   static void process_post_move (OpCode op, ENV &env, PARAM& param)
    404   {
    405     if (!env.seen_moveto)
    406     {
    407       env.determine_hintmask_size ();
    408       env.seen_moveto = true;
    409     }
    410     OPSET::flush_args_and_op (op, env, param);
    411   }
    412 
    413   static void process_post_path (OpCode op, ENV &env, PARAM& param)
    414   {
    415     OPSET::flush_args_and_op (op, env, param);
    416   }
    417 
    418   static void flush_args_and_op (OpCode op, ENV &env, PARAM& param)
    419   {
    420     OPSET::flush_args (env, param);
    421     OPSET::flush_op (op, env, param);
    422   }
    423 
    424   static void flush_args (ENV &env, PARAM& param)
    425   {
    426     env.pop_n_args (env.argStack.get_count ());
    427   }
    428 
    429   static void flush_op (OpCode op, ENV &env, PARAM& param)
    430   {
    431   }
    432 
    433   static void flush_hintmask (OpCode op, ENV &env, PARAM& param)
    434   {
    435     OPSET::flush_args_and_op (op, env, param);
    436   }
    437 
    438   static bool is_number_op (OpCode op)
    439   {
    440     switch (op)
    441     {
    442       case OpCode_shortint:
    443       case OpCode_fixedcs:
    444       case OpCode_TwoBytePosInt0: case OpCode_TwoBytePosInt1:
    445       case OpCode_TwoBytePosInt2: case OpCode_TwoBytePosInt3:
    446       case OpCode_TwoByteNegInt0: case OpCode_TwoByteNegInt1:
    447       case OpCode_TwoByteNegInt2: case OpCode_TwoByteNegInt3:
    448 	return true;
    449 
    450       default:
    451 	/* 1-byte integer */
    452 	return (OpCode_OneByteIntFirst <= op) && (op <= OpCode_OneByteIntLast);
    453     }
    454   }
    455 
    456   protected:
    457   typedef OpSet<ARG>  SUPER;
    458 };
    459 
    460 template <typename PATH, typename ENV, typename PARAM>
    461 struct PathProcs
    462 {
    463   static void rmoveto (ENV &env, PARAM& param)
    464   {
    465     Point pt1 = env.get_pt ();
    466     const Number &dy = env.pop_arg ();
    467     const Number &dx = env.pop_arg ();
    468     pt1.move (dx, dy);
    469     PATH::moveto (env, param, pt1);
    470   }
    471 
    472   static void hmoveto (ENV &env, PARAM& param)
    473   {
    474     Point pt1 = env.get_pt ();
    475     pt1.move_x (env.pop_arg ());
    476     PATH::moveto (env, param, pt1);
    477   }
    478 
    479   static void vmoveto (ENV &env, PARAM& param)
    480   {
    481     Point pt1 = env.get_pt ();
    482     pt1.move_y (env.pop_arg ());
    483     PATH::moveto (env, param, pt1);
    484   }
    485 
    486   static void rlineto (ENV &env, PARAM& param)
    487   {
    488     for (unsigned int i = 0; i + 2 <= env.argStack.get_count (); i += 2)
    489     {
    490       Point pt1 = env.get_pt ();
    491       pt1.move (env.eval_arg (i), env.eval_arg (i+1));
    492       PATH::line (env, param, pt1);
    493     }
    494   }
    495 
    496   static void hlineto (ENV &env, PARAM& param)
    497   {
    498     Point pt1;
    499     unsigned int i = 0;
    500     for (; i + 2 <= env.argStack.get_count (); i += 2)
    501     {
    502       pt1 = env.get_pt ();
    503       pt1.move_x (env.eval_arg (i));
    504       PATH::line (env, param, pt1);
    505       pt1.move_y (env.eval_arg (i+1));
    506       PATH::line (env, param, pt1);
    507     }
    508     if (i < env.argStack.get_count ())
    509     {
    510       pt1 = env.get_pt ();
    511       pt1.move_x (env.eval_arg (i));
    512       PATH::line (env, param, pt1);
    513     }
    514   }
    515 
    516   static void vlineto (ENV &env, PARAM& param)
    517   {
    518     Point pt1;
    519     unsigned int i = 0;
    520     for (; i + 2 <= env.argStack.get_count (); i += 2)
    521     {
    522       pt1 = env.get_pt ();
    523       pt1.move_y (env.eval_arg (i));
    524       PATH::line (env, param, pt1);
    525       pt1.move_x (env.eval_arg (i+1));
    526       PATH::line (env, param, pt1);
    527     }
    528     if (i < env.argStack.get_count ())
    529     {
    530       pt1 = env.get_pt ();
    531       pt1.move_y (env.eval_arg (i));
    532       PATH::line (env, param, pt1);
    533     }
    534   }
    535 
    536   static void rrcurveto (ENV &env, PARAM& param)
    537   {
    538     for (unsigned int i = 0; i + 6 <= env.argStack.get_count (); i += 6)
    539     {
    540       Point pt1 = env.get_pt ();
    541       pt1.move (env.eval_arg (i), env.eval_arg (i+1));
    542       Point pt2 = pt1;
    543       pt2.move (env.eval_arg (i+2), env.eval_arg (i+3));
    544       Point pt3 = pt2;
    545       pt3.move (env.eval_arg (i+4), env.eval_arg (i+5));
    546       PATH::curve (env, param, pt1, pt2, pt3);
    547     }
    548   }
    549 
    550   static void rcurveline (ENV &env, PARAM& param)
    551   {
    552     unsigned int i = 0;
    553     for (; i + 6 <= env.argStack.get_count (); i += 6)
    554     {
    555       Point pt1 = env.get_pt ();
    556       pt1.move (env.eval_arg (i), env.eval_arg (i+1));
    557       Point pt2 = pt1;
    558       pt2.move (env.eval_arg (i+2), env.eval_arg (i+3));
    559       Point pt3 = pt2;
    560       pt3.move (env.eval_arg (i+4), env.eval_arg (i+5));
    561       PATH::curve (env, param, pt1, pt2, pt3);
    562     }
    563     for (; i + 2 <= env.argStack.get_count (); i += 2)
    564     {
    565       Point pt1 = env.get_pt ();
    566       pt1.move (env.eval_arg (i), env.eval_arg (i+1));
    567       PATH::line (env, param, pt1);
    568     }
    569   }
    570 
    571   static void rlinecurve (ENV &env, PARAM& param)
    572   {
    573     unsigned int i = 0;
    574     unsigned int line_limit = (env.argStack.get_count () % 6);
    575     for (; i + 2 <= line_limit; i += 2)
    576     {
    577       Point pt1 = env.get_pt ();
    578       pt1.move (env.eval_arg (i), env.eval_arg (i+1));
    579       PATH::line (env, param, pt1);
    580     }
    581     for (; i + 6 <= env.argStack.get_count (); i += 6)
    582     {
    583       Point pt1 = env.get_pt ();
    584       pt1.move (env.eval_arg (i), env.eval_arg (i+1));
    585       Point pt2 = pt1;
    586       pt2.move (env.eval_arg (i+2), env.eval_arg (i+3));
    587       Point pt3 = pt2;
    588       pt3.move (env.eval_arg (i+4), env.eval_arg (i+5));
    589       PATH::curve (env, param, pt1, pt2, pt3);
    590     }
    591   }
    592 
    593   static void vvcurveto (ENV &env, PARAM& param)
    594   {
    595     unsigned int i = 0;
    596     Point pt1 = env.get_pt ();
    597     if ((env.argStack.get_count () & 1) != 0)
    598       pt1.move_x (env.eval_arg (i++));
    599     for (; i + 4 <= env.argStack.get_count (); i += 4)
    600     {
    601       pt1.move_y (env.eval_arg (i));
    602       Point pt2 = pt1;
    603       pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
    604       Point pt3 = pt2;
    605       pt3.move_y (env.eval_arg (i+3));
    606       PATH::curve (env, param, pt1, pt2, pt3);
    607       pt1 = env.get_pt ();
    608     }
    609   }
    610 
    611   static void hhcurveto (ENV &env, PARAM& param)
    612   {
    613     unsigned int i = 0;
    614     Point pt1 = env.get_pt ();
    615     if ((env.argStack.get_count () & 1) != 0)
    616       pt1.move_y (env.eval_arg (i++));
    617     for (; i + 4 <= env.argStack.get_count (); i += 4)
    618     {
    619       pt1.move_x (env.eval_arg (i));
    620       Point pt2 = pt1;
    621       pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
    622       Point pt3 = pt2;
    623       pt3.move_x (env.eval_arg (i+3));
    624       PATH::curve (env, param, pt1, pt2, pt3);
    625       pt1 = env.get_pt ();
    626     }
    627   }
    628 
    629   static void vhcurveto (ENV &env, PARAM& param)
    630   {
    631     Point pt1, pt2, pt3;
    632     unsigned int i = 0;
    633     if ((env.argStack.get_count () % 8) >= 4)
    634     {
    635       Point pt1 = env.get_pt ();
    636       pt1.move_y (env.eval_arg (i));
    637       Point pt2 = pt1;
    638       pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
    639       Point pt3 = pt2;
    640       pt3.move_x (env.eval_arg (i+3));
    641       i += 4;
    642 
    643       for (; i + 8 <= env.argStack.get_count (); i += 8)
    644       {
    645 	PATH::curve (env, param, pt1, pt2, pt3);
    646 	pt1 = env.get_pt ();
    647 	pt1.move_x (env.eval_arg (i));
    648 	pt2 = pt1;
    649 	pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
    650 	pt3 = pt2;
    651 	pt3.move_y (env.eval_arg (i+3));
    652 	PATH::curve (env, param, pt1, pt2, pt3);
    653 
    654 	pt1 = pt3;
    655 	pt1.move_y (env.eval_arg (i+4));
    656 	pt2 = pt1;
    657 	pt2.move (env.eval_arg (i+5), env.eval_arg (i+6));
    658 	pt3 = pt2;
    659 	pt3.move_x (env.eval_arg (i+7));
    660       }
    661       if (i < env.argStack.get_count ())
    662 	pt3.move_y (env.eval_arg (i));
    663       PATH::curve (env, param, pt1, pt2, pt3);
    664     }
    665     else
    666     {
    667       for (; i + 8 <= env.argStack.get_count (); i += 8)
    668       {
    669 	pt1 = env.get_pt ();
    670 	pt1.move_y (env.eval_arg (i));
    671 	pt2 = pt1;
    672 	pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
    673 	pt3 = pt2;
    674 	pt3.move_x (env.eval_arg (i+3));
    675 	PATH::curve (env, param, pt1, pt2, pt3);
    676 
    677 	pt1 = pt3;
    678 	pt1.move_x (env.eval_arg (i+4));
    679 	pt2 = pt1;
    680 	pt2.move (env.eval_arg (i+5), env.eval_arg (i+6));
    681 	pt3 = pt2;
    682 	pt3.move_y (env.eval_arg (i+7));
    683 	if ((env.argStack.get_count () - i < 16) && ((env.argStack.get_count () & 1) != 0))
    684 	  pt3.move_x (env.eval_arg (i+8));
    685 	PATH::curve (env, param, pt1, pt2, pt3);
    686       }
    687     }
    688   }
    689 
    690   static void hvcurveto (ENV &env, PARAM& param)
    691   {
    692     Point pt1, pt2, pt3;
    693     unsigned int i = 0;
    694     if ((env.argStack.get_count () % 8) >= 4)
    695     {
    696       Point pt1 = env.get_pt ();
    697       pt1.move_x (env.eval_arg (i));
    698       Point pt2 = pt1;
    699       pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
    700       Point pt3 = pt2;
    701       pt3.move_y (env.eval_arg (i+3));
    702       i += 4;
    703 
    704       for (; i + 8 <= env.argStack.get_count (); i += 8)
    705       {
    706 	PATH::curve (env, param, pt1, pt2, pt3);
    707 	pt1 = env.get_pt ();
    708 	pt1.move_y (env.eval_arg (i));
    709 	pt2 = pt1;
    710 	pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
    711 	pt3 = pt2;
    712 	pt3.move_x (env.eval_arg (i+3));
    713 	PATH::curve (env, param, pt1, pt2, pt3);
    714 
    715 	pt1 = pt3;
    716 	pt1.move_x (env.eval_arg (i+4));
    717 	pt2 = pt1;
    718 	pt2.move (env.eval_arg (i+5), env.eval_arg (i+6));
    719 	pt3 = pt2;
    720 	pt3.move_y (env.eval_arg (i+7));
    721       }
    722       if (i < env.argStack.get_count ())
    723 	pt3.move_x (env.eval_arg (i));
    724       PATH::curve (env, param, pt1, pt2, pt3);
    725     }
    726     else
    727     {
    728       for (; i + 8 <= env.argStack.get_count (); i += 8)
    729       {
    730 	pt1 = env.get_pt ();
    731 	pt1.move_x (env.eval_arg (i));
    732 	pt2 = pt1;
    733 	pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
    734 	pt3 = pt2;
    735 	pt3.move_y (env.eval_arg (i+3));
    736 	PATH::curve (env, param, pt1, pt2, pt3);
    737 
    738 	pt1 = pt3;
    739 	pt1.move_y (env.eval_arg (i+4));
    740 	pt2 = pt1;
    741 	pt2.move (env.eval_arg (i+5), env.eval_arg (i+6));
    742 	pt3 = pt2;
    743 	pt3.move_x (env.eval_arg (i+7));
    744 	if ((env.argStack.get_count () - i < 16) && ((env.argStack.get_count () & 1) != 0))
    745 	  pt3.move_y (env.eval_arg (i+8));
    746 	PATH::curve (env, param, pt1, pt2, pt3);
    747       }
    748     }
    749   }
    750 
    751   /* default actions to be overridden */
    752   static void moveto (ENV &env, PARAM& param, const Point &pt)
    753   { env.moveto (pt); }
    754 
    755   static void line (ENV &env, PARAM& param, const Point &pt1)
    756   { PATH::moveto (env, param, pt1); }
    757 
    758   static void curve (ENV &env, PARAM& param, const Point &pt1, const Point &pt2, const Point &pt3)
    759   { PATH::moveto (env, param, pt3); }
    760 
    761   static void hflex (ENV &env, PARAM& param)
    762   {
    763     if (likely (env.argStack.get_count () == 7))
    764     {
    765       Point pt1 = env.get_pt ();
    766       pt1.move_x (env.eval_arg (0));
    767       Point pt2 = pt1;
    768       pt2.move (env.eval_arg (1), env.eval_arg (2));
    769       Point pt3 = pt2;
    770       pt3.move_x (env.eval_arg (3));
    771       Point pt4 = pt3;
    772       pt4.move_x (env.eval_arg (4));
    773       Point pt5 = pt4;
    774       pt5.move_x (env.eval_arg (5));
    775       pt5.y = pt1.y;
    776       Point pt6 = pt5;
    777       pt6.move_x (env.eval_arg (6));
    778 
    779       curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6);
    780     }
    781     else
    782       env.set_error ();
    783   }
    784 
    785   static void flex (ENV &env, PARAM& param)
    786   {
    787     if (likely (env.argStack.get_count () == 13))
    788     {
    789       Point pt1 = env.get_pt ();
    790       pt1.move (env.eval_arg (0), env.eval_arg (1));
    791       Point pt2 = pt1;
    792       pt2.move (env.eval_arg (2), env.eval_arg (3));
    793       Point pt3 = pt2;
    794       pt3.move (env.eval_arg (4), env.eval_arg (5));
    795       Point pt4 = pt3;
    796       pt4.move (env.eval_arg (6), env.eval_arg (7));
    797       Point pt5 = pt4;
    798       pt5.move (env.eval_arg (8), env.eval_arg (9));
    799       Point pt6 = pt5;
    800       pt6.move (env.eval_arg (10), env.eval_arg (11));
    801 
    802       curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6);
    803     }
    804     else
    805       env.set_error ();
    806   }
    807 
    808   static void hflex1 (ENV &env, PARAM& param)
    809   {
    810     if (likely (env.argStack.get_count () == 9))
    811     {
    812       Point pt1 = env.get_pt ();
    813       pt1.move (env.eval_arg (0), env.eval_arg (1));
    814       Point pt2 = pt1;
    815       pt2.move (env.eval_arg (2), env.eval_arg (3));
    816       Point pt3 = pt2;
    817       pt3.move_x (env.eval_arg (4));
    818       Point pt4 = pt3;
    819       pt4.move_x (env.eval_arg (5));
    820       Point pt5 = pt4;
    821       pt5.move (env.eval_arg (6), env.eval_arg (7));
    822       Point pt6 = pt5;
    823       pt6.move_x (env.eval_arg (8));
    824       pt6.y = env.get_pt ().y;
    825 
    826       curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6);
    827     }
    828     else
    829       env.set_error ();
    830   }
    831 
    832   static void flex1 (ENV &env, PARAM& param)
    833   {
    834     if (likely (env.argStack.get_count () == 11))
    835     {
    836       Point d;
    837       d.init ();
    838       for (unsigned int i = 0; i < 10; i += 2)
    839 	d.move (env.eval_arg (i), env.eval_arg (i+1));
    840 
    841       Point pt1 = env.get_pt ();
    842       pt1.move (env.eval_arg (0), env.eval_arg (1));
    843       Point pt2 = pt1;
    844       pt2.move (env.eval_arg (2), env.eval_arg (3));
    845       Point pt3 = pt2;
    846       pt3.move (env.eval_arg (4), env.eval_arg (5));
    847       Point pt4 = pt3;
    848       pt4.move (env.eval_arg (6), env.eval_arg (7));
    849       Point pt5 = pt4;
    850       pt5.move (env.eval_arg (8), env.eval_arg (9));
    851       Point pt6 = pt5;
    852 
    853       if (fabs (d.x.to_real ()) > fabs (d.y.to_real ()))
    854       {
    855 	pt6.move_x (env.eval_arg (10));
    856 	pt6.y = env.get_pt ().y;
    857       }
    858       else
    859       {
    860 	pt6.x = env.get_pt ().x;
    861 	pt6.move_y (env.eval_arg (10));
    862       }
    863 
    864       curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6);
    865     }
    866     else
    867       env.set_error ();
    868   }
    869 
    870   protected:
    871   static void curve2 (ENV &env, PARAM& param,
    872 		      const Point &pt1, const Point &pt2, const Point &pt3,
    873 		      const Point &pt4, const Point &pt5, const Point &pt6)
    874   {
    875     PATH::curve (env, param, pt1, pt2, pt3);
    876     PATH::curve (env, param, pt4, pt5, pt6);
    877   }
    878 };
    879 
    880 template <typename ENV, typename OPSET, typename PARAM>
    881 struct CSInterpreter : Interpreter<ENV>
    882 {
    883   bool interpret (PARAM& param)
    884   {
    885     SUPER::env.set_endchar (false);
    886 
    887     for (;;) {
    888       OPSET::process_op (SUPER::env.fetch_op (), SUPER::env, param);
    889       if (unlikely (SUPER::env.in_error ()))
    890 	return false;
    891       if (SUPER::env.is_endchar ())
    892 	break;
    893     }
    894 
    895     return true;
    896   }
    897 
    898   private:
    899   typedef Interpreter<ENV> SUPER;
    900 };
    901 
    902 } /* namespace CFF */
    903 
    904 #endif /* HB_CFF_INTERP_CS_COMMON_HH */
    905