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