Imported Upstream version 2.6.7
[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 ? subrs->count : 0; }
80   unsigned int get_bias () const  { return bias; }
81
82   byte_str_t operator [] (unsigned int index) const
83   {
84     if (unlikely (!subrs || 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 arg_count = env.argStack.get_count ();
555     if (unlikely (arg_count < 8))
556       return;
557
558     unsigned int i = 0;
559     unsigned int curve_limit = arg_count - 2;
560     for (; i + 6 <= curve_limit; i += 6)
561     {
562       point_t pt1 = env.get_pt ();
563       pt1.move (env.eval_arg (i), env.eval_arg (i+1));
564       point_t pt2 = pt1;
565       pt2.move (env.eval_arg (i+2), env.eval_arg (i+3));
566       point_t pt3 = pt2;
567       pt3.move (env.eval_arg (i+4), env.eval_arg (i+5));
568       PATH::curve (env, param, pt1, pt2, pt3);
569     }
570
571     point_t pt1 = env.get_pt ();
572     pt1.move (env.eval_arg (i), env.eval_arg (i+1));
573     PATH::line (env, param, pt1);
574   }
575
576   static void rlinecurve (ENV &env, PARAM& param)
577   {
578     unsigned int arg_count = env.argStack.get_count ();
579     if (unlikely (arg_count < 8))
580       return;
581
582     unsigned int i = 0;
583     unsigned int line_limit = arg_count - 6;
584     for (; i + 2 <= line_limit; i += 2)
585     {
586       point_t pt1 = env.get_pt ();
587       pt1.move (env.eval_arg (i), env.eval_arg (i+1));
588       PATH::line (env, param, pt1);
589     }
590
591     point_t pt1 = env.get_pt ();
592     pt1.move (env.eval_arg (i), env.eval_arg (i+1));
593     point_t pt2 = pt1;
594     pt2.move (env.eval_arg (i+2), env.eval_arg (i+3));
595     point_t pt3 = pt2;
596     pt3.move (env.eval_arg (i+4), env.eval_arg (i+5));
597     PATH::curve (env, param, pt1, pt2, pt3);
598   }
599
600   static void vvcurveto (ENV &env, PARAM& param)
601   {
602     unsigned int i = 0;
603     point_t pt1 = env.get_pt ();
604     if ((env.argStack.get_count () & 1) != 0)
605       pt1.move_x (env.eval_arg (i++));
606     for (; i + 4 <= env.argStack.get_count (); i += 4)
607     {
608       pt1.move_y (env.eval_arg (i));
609       point_t pt2 = pt1;
610       pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
611       point_t pt3 = pt2;
612       pt3.move_y (env.eval_arg (i+3));
613       PATH::curve (env, param, pt1, pt2, pt3);
614       pt1 = env.get_pt ();
615     }
616   }
617
618   static void hhcurveto (ENV &env, PARAM& param)
619   {
620     unsigned int i = 0;
621     point_t pt1 = env.get_pt ();
622     if ((env.argStack.get_count () & 1) != 0)
623       pt1.move_y (env.eval_arg (i++));
624     for (; i + 4 <= env.argStack.get_count (); i += 4)
625     {
626       pt1.move_x (env.eval_arg (i));
627       point_t pt2 = pt1;
628       pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
629       point_t pt3 = pt2;
630       pt3.move_x (env.eval_arg (i+3));
631       PATH::curve (env, param, pt1, pt2, pt3);
632       pt1 = env.get_pt ();
633     }
634   }
635
636   static void vhcurveto (ENV &env, PARAM& param)
637   {
638     point_t pt1, pt2, pt3;
639     unsigned int i = 0;
640     if ((env.argStack.get_count () % 8) >= 4)
641     {
642       point_t pt1 = env.get_pt ();
643       pt1.move_y (env.eval_arg (i));
644       point_t pt2 = pt1;
645       pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
646       point_t pt3 = pt2;
647       pt3.move_x (env.eval_arg (i+3));
648       i += 4;
649
650       for (; i + 8 <= env.argStack.get_count (); i += 8)
651       {
652         PATH::curve (env, param, pt1, pt2, pt3);
653         pt1 = env.get_pt ();
654         pt1.move_x (env.eval_arg (i));
655         pt2 = pt1;
656         pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
657         pt3 = pt2;
658         pt3.move_y (env.eval_arg (i+3));
659         PATH::curve (env, param, pt1, pt2, pt3);
660
661         pt1 = pt3;
662         pt1.move_y (env.eval_arg (i+4));
663         pt2 = pt1;
664         pt2.move (env.eval_arg (i+5), env.eval_arg (i+6));
665         pt3 = pt2;
666         pt3.move_x (env.eval_arg (i+7));
667       }
668       if (i < env.argStack.get_count ())
669         pt3.move_y (env.eval_arg (i));
670       PATH::curve (env, param, pt1, pt2, pt3);
671     }
672     else
673     {
674       for (; i + 8 <= env.argStack.get_count (); i += 8)
675       {
676         pt1 = env.get_pt ();
677         pt1.move_y (env.eval_arg (i));
678         pt2 = pt1;
679         pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
680         pt3 = pt2;
681         pt3.move_x (env.eval_arg (i+3));
682         PATH::curve (env, param, pt1, pt2, pt3);
683
684         pt1 = pt3;
685         pt1.move_x (env.eval_arg (i+4));
686         pt2 = pt1;
687         pt2.move (env.eval_arg (i+5), env.eval_arg (i+6));
688         pt3 = pt2;
689         pt3.move_y (env.eval_arg (i+7));
690         if ((env.argStack.get_count () - i < 16) && ((env.argStack.get_count () & 1) != 0))
691           pt3.move_x (env.eval_arg (i+8));
692         PATH::curve (env, param, pt1, pt2, pt3);
693       }
694     }
695   }
696
697   static void hvcurveto (ENV &env, PARAM& param)
698   {
699     point_t pt1, pt2, pt3;
700     unsigned int i = 0;
701     if ((env.argStack.get_count () % 8) >= 4)
702     {
703       point_t pt1 = env.get_pt ();
704       pt1.move_x (env.eval_arg (i));
705       point_t pt2 = pt1;
706       pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
707       point_t pt3 = pt2;
708       pt3.move_y (env.eval_arg (i+3));
709       i += 4;
710
711       for (; i + 8 <= env.argStack.get_count (); i += 8)
712       {
713         PATH::curve (env, param, pt1, pt2, pt3);
714         pt1 = env.get_pt ();
715         pt1.move_y (env.eval_arg (i));
716         pt2 = pt1;
717         pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
718         pt3 = pt2;
719         pt3.move_x (env.eval_arg (i+3));
720         PATH::curve (env, param, pt1, pt2, pt3);
721
722         pt1 = pt3;
723         pt1.move_x (env.eval_arg (i+4));
724         pt2 = pt1;
725         pt2.move (env.eval_arg (i+5), env.eval_arg (i+6));
726         pt3 = pt2;
727         pt3.move_y (env.eval_arg (i+7));
728       }
729       if (i < env.argStack.get_count ())
730         pt3.move_x (env.eval_arg (i));
731       PATH::curve (env, param, pt1, pt2, pt3);
732     }
733     else
734     {
735       for (; i + 8 <= env.argStack.get_count (); i += 8)
736       {
737         pt1 = env.get_pt ();
738         pt1.move_x (env.eval_arg (i));
739         pt2 = pt1;
740         pt2.move (env.eval_arg (i+1), env.eval_arg (i+2));
741         pt3 = pt2;
742         pt3.move_y (env.eval_arg (i+3));
743         PATH::curve (env, param, pt1, pt2, pt3);
744
745         pt1 = pt3;
746         pt1.move_y (env.eval_arg (i+4));
747         pt2 = pt1;
748         pt2.move (env.eval_arg (i+5), env.eval_arg (i+6));
749         pt3 = pt2;
750         pt3.move_x (env.eval_arg (i+7));
751         if ((env.argStack.get_count () - i < 16) && ((env.argStack.get_count () & 1) != 0))
752           pt3.move_y (env.eval_arg (i+8));
753         PATH::curve (env, param, pt1, pt2, pt3);
754       }
755     }
756   }
757
758   /* default actions to be overridden */
759   static void moveto (ENV &env, PARAM& param, const point_t &pt)
760   { env.moveto (pt); }
761
762   static void line (ENV &env, PARAM& param, const point_t &pt1)
763   { PATH::moveto (env, param, pt1); }
764
765   static void curve (ENV &env, PARAM& param, const point_t &pt1, const point_t &pt2, const point_t &pt3)
766   { PATH::moveto (env, param, pt3); }
767
768   static void hflex (ENV &env, PARAM& param)
769   {
770     if (likely (env.argStack.get_count () == 7))
771     {
772       point_t pt1 = env.get_pt ();
773       pt1.move_x (env.eval_arg (0));
774       point_t pt2 = pt1;
775       pt2.move (env.eval_arg (1), env.eval_arg (2));
776       point_t pt3 = pt2;
777       pt3.move_x (env.eval_arg (3));
778       point_t pt4 = pt3;
779       pt4.move_x (env.eval_arg (4));
780       point_t pt5 = pt4;
781       pt5.move_x (env.eval_arg (5));
782       pt5.y = pt1.y;
783       point_t pt6 = pt5;
784       pt6.move_x (env.eval_arg (6));
785
786       curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6);
787     }
788     else
789       env.set_error ();
790   }
791
792   static void flex (ENV &env, PARAM& param)
793   {
794     if (likely (env.argStack.get_count () == 13))
795     {
796       point_t pt1 = env.get_pt ();
797       pt1.move (env.eval_arg (0), env.eval_arg (1));
798       point_t pt2 = pt1;
799       pt2.move (env.eval_arg (2), env.eval_arg (3));
800       point_t pt3 = pt2;
801       pt3.move (env.eval_arg (4), env.eval_arg (5));
802       point_t pt4 = pt3;
803       pt4.move (env.eval_arg (6), env.eval_arg (7));
804       point_t pt5 = pt4;
805       pt5.move (env.eval_arg (8), env.eval_arg (9));
806       point_t pt6 = pt5;
807       pt6.move (env.eval_arg (10), env.eval_arg (11));
808
809       curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6);
810     }
811     else
812       env.set_error ();
813   }
814
815   static void hflex1 (ENV &env, PARAM& param)
816   {
817     if (likely (env.argStack.get_count () == 9))
818     {
819       point_t pt1 = env.get_pt ();
820       pt1.move (env.eval_arg (0), env.eval_arg (1));
821       point_t pt2 = pt1;
822       pt2.move (env.eval_arg (2), env.eval_arg (3));
823       point_t pt3 = pt2;
824       pt3.move_x (env.eval_arg (4));
825       point_t pt4 = pt3;
826       pt4.move_x (env.eval_arg (5));
827       point_t pt5 = pt4;
828       pt5.move (env.eval_arg (6), env.eval_arg (7));
829       point_t pt6 = pt5;
830       pt6.move_x (env.eval_arg (8));
831       pt6.y = env.get_pt ().y;
832
833       curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6);
834     }
835     else
836       env.set_error ();
837   }
838
839   static void flex1 (ENV &env, PARAM& param)
840   {
841     if (likely (env.argStack.get_count () == 11))
842     {
843       point_t d;
844       d.init ();
845       for (unsigned int i = 0; i < 10; i += 2)
846         d.move (env.eval_arg (i), env.eval_arg (i+1));
847
848       point_t pt1 = env.get_pt ();
849       pt1.move (env.eval_arg (0), env.eval_arg (1));
850       point_t pt2 = pt1;
851       pt2.move (env.eval_arg (2), env.eval_arg (3));
852       point_t pt3 = pt2;
853       pt3.move (env.eval_arg (4), env.eval_arg (5));
854       point_t pt4 = pt3;
855       pt4.move (env.eval_arg (6), env.eval_arg (7));
856       point_t pt5 = pt4;
857       pt5.move (env.eval_arg (8), env.eval_arg (9));
858       point_t pt6 = pt5;
859
860       if (fabs (d.x.to_real ()) > fabs (d.y.to_real ()))
861       {
862         pt6.move_x (env.eval_arg (10));
863         pt6.y = env.get_pt ().y;
864       }
865       else
866       {
867         pt6.x = env.get_pt ().x;
868         pt6.move_y (env.eval_arg (10));
869       }
870
871       curve2 (env, param, pt1, pt2, pt3, pt4, pt5, pt6);
872     }
873     else
874       env.set_error ();
875   }
876
877   protected:
878   static void curve2 (ENV &env, PARAM& param,
879                       const point_t &pt1, const point_t &pt2, const point_t &pt3,
880                       const point_t &pt4, const point_t &pt5, const point_t &pt6)
881   {
882     PATH::curve (env, param, pt1, pt2, pt3);
883     PATH::curve (env, param, pt4, pt5, pt6);
884   }
885 };
886
887 template <typename ENV, typename OPSET, typename PARAM>
888 struct cs_interpreter_t : interpreter_t<ENV>
889 {
890   bool interpret (PARAM& param)
891   {
892     SUPER::env.set_endchar (false);
893
894     for (;;) {
895       OPSET::process_op (SUPER::env.fetch_op (), SUPER::env, param);
896       if (unlikely (SUPER::env.in_error ()))
897         return false;
898       if (SUPER::env.is_endchar ())
899         break;
900     }
901
902     return true;
903   }
904
905   private:
906   typedef interpreter_t<ENV> SUPER;
907 };
908
909 } /* namespace CFF */
910
911 #endif /* HB_CFF_INTERP_CS_COMMON_HH */