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