Imported Upstream version 3.4.0
[platform/upstream/harfbuzz.git] / src / hb-subset-cff-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
27 #ifndef HB_SUBSET_CFF_COMMON_HH
28 #define HB_SUBSET_CFF_COMMON_HH
29
30 #include "hb.hh"
31
32 #include "hb-subset-plan.hh"
33 #include "hb-cff-interp-cs-common.hh"
34
35 namespace CFF {
36
37 /* Used for writing a temporary charstring */
38 struct str_encoder_t
39 {
40   str_encoder_t (str_buff_t &buff_)
41     : buff (buff_), error (false) {}
42
43   void reset () { buff.resize (0); }
44
45   void encode_byte (unsigned char b)
46   {
47     if (unlikely (buff.push (b) == &Crap (unsigned char)))
48       set_error ();
49   }
50
51   void encode_int (int v)
52   {
53     if ((-1131 <= v) && (v <= 1131))
54     {
55       if ((-107 <= v) && (v <= 107))
56         encode_byte (v + 139);
57       else if (v > 0)
58       {
59         v -= 108;
60         encode_byte ((v >> 8) + OpCode_TwoBytePosInt0);
61         encode_byte (v & 0xFF);
62       }
63       else
64       {
65         v = -v - 108;
66         encode_byte ((v >> 8) + OpCode_TwoByteNegInt0);
67         encode_byte (v & 0xFF);
68       }
69     }
70     else
71     {
72       if (unlikely (v < -32768))
73         v = -32768;
74       else if (unlikely (v > 32767))
75         v = 32767;
76       encode_byte (OpCode_shortint);
77       encode_byte ((v >> 8) & 0xFF);
78       encode_byte (v & 0xFF);
79     }
80   }
81
82   void encode_num (const number_t& n)
83   {
84     if (n.in_int_range ())
85     {
86       encode_int (n.to_int ());
87     }
88     else
89     {
90       int32_t v = n.to_fixed ();
91       encode_byte (OpCode_fixedcs);
92       encode_byte ((v >> 24) & 0xFF);
93       encode_byte ((v >> 16) & 0xFF);
94       encode_byte ((v >> 8) & 0xFF);
95       encode_byte (v & 0xFF);
96     }
97   }
98
99   void encode_op (op_code_t op)
100   {
101     if (Is_OpCode_ESC (op))
102     {
103       encode_byte (OpCode_escape);
104       encode_byte (Unmake_OpCode_ESC (op));
105     }
106     else
107       encode_byte (op);
108   }
109
110   void copy_str (const byte_str_t &str)
111   {
112     unsigned int  offset = buff.length;
113     if (unlikely (!buff.resize (offset + str.length)))
114     {
115       set_error ();
116       return;
117     }
118     if (unlikely (buff.length < offset + str.length))
119     {
120       set_error ();
121       return;
122     }
123     memcpy (&buff[offset], &str[0], str.length);
124   }
125
126   bool is_error () const { return error; }
127
128   protected:
129   void set_error () { error = true; }
130
131   str_buff_t &buff;
132   bool    error;
133 };
134
135 struct cff_sub_table_info_t {
136   cff_sub_table_info_t ()
137     : fd_array_link (0),
138       char_strings_link (0)
139   {
140     fd_select.init ();
141   }
142
143   table_info_t     fd_select;
144   objidx_t         fd_array_link;
145   objidx_t         char_strings_link;
146 };
147
148 template <typename OPSTR=op_str_t>
149 struct cff_top_dict_op_serializer_t : op_serializer_t
150 {
151   bool serialize (hb_serialize_context_t *c,
152                   const OPSTR &opstr,
153                   const cff_sub_table_info_t &info) const
154   {
155     TRACE_SERIALIZE (this);
156
157     switch (opstr.op)
158     {
159       case OpCode_CharStrings:
160         return_trace (FontDict::serialize_link4_op(c, opstr.op, info.char_strings_link, whence_t::Absolute));
161
162       case OpCode_FDArray:
163         return_trace (FontDict::serialize_link4_op(c, opstr.op, info.fd_array_link, whence_t::Absolute));
164
165       case OpCode_FDSelect:
166         return_trace (FontDict::serialize_link4_op(c, opstr.op, info.fd_select.link, whence_t::Absolute));
167
168       default:
169         return_trace (copy_opstr (c, opstr));
170     }
171     return_trace (true);
172   }
173 };
174
175 struct cff_font_dict_op_serializer_t : op_serializer_t
176 {
177   bool serialize (hb_serialize_context_t *c,
178                   const op_str_t &opstr,
179                   const table_info_t &privateDictInfo) const
180   {
181     TRACE_SERIALIZE (this);
182
183     if (opstr.op == OpCode_Private)
184     {
185       /* serialize the private dict size & offset as 2-byte & 4-byte integers */
186       return_trace (UnsizedByteStr::serialize_int2 (c, privateDictInfo.size) &&
187                     Dict::serialize_link4_op (c, opstr.op, privateDictInfo.link, whence_t::Absolute));
188     }
189     else
190     {
191       HBUINT8 *d = c->allocate_size<HBUINT8> (opstr.str.length);
192       if (unlikely (!d)) return_trace (false);
193       memcpy (d, &opstr.str[0], opstr.str.length);
194     }
195     return_trace (true);
196   }
197 };
198
199 struct cff_private_dict_op_serializer_t : op_serializer_t
200 {
201   cff_private_dict_op_serializer_t (bool desubroutinize_, bool drop_hints_)
202     : desubroutinize (desubroutinize_), drop_hints (drop_hints_) {}
203
204   bool serialize (hb_serialize_context_t *c,
205                   const op_str_t &opstr,
206                   objidx_t subrs_link) const
207   {
208     TRACE_SERIALIZE (this);
209
210     if (drop_hints && dict_opset_t::is_hint_op (opstr.op))
211       return true;
212     if (opstr.op == OpCode_Subrs)
213     {
214       if (desubroutinize || !subrs_link)
215         return_trace (true);
216       else
217         return_trace (FontDict::serialize_link2_op (c, opstr.op, subrs_link));
218     }
219     else
220       return_trace (copy_opstr (c, opstr));
221   }
222
223   protected:
224   const bool  desubroutinize;
225   const bool  drop_hints;
226 };
227
228 struct flatten_param_t
229 {
230   str_buff_t     &flatStr;
231   bool  drop_hints;
232 };
233
234 template <typename ACC, typename ENV, typename OPSET, op_code_t endchar_op=OpCode_Invalid>
235 struct subr_flattener_t
236 {
237   subr_flattener_t (const ACC &acc_,
238                     const hb_subset_plan_t *plan_)
239                    : acc (acc_), plan (plan_) {}
240
241   bool flatten (str_buff_vec_t &flat_charstrings)
242   {
243     if (!flat_charstrings.resize (plan->num_output_glyphs ()))
244       return false;
245     for (unsigned int i = 0; i < plan->num_output_glyphs (); i++)
246       flat_charstrings[i].init ();
247     for (unsigned int i = 0; i < plan->num_output_glyphs (); i++)
248     {
249       hb_codepoint_t  glyph;
250       if (!plan->old_gid_for_new_gid (i, &glyph))
251       {
252         /* add an endchar only charstring for a missing glyph if CFF1 */
253         if (endchar_op != OpCode_Invalid) flat_charstrings[i].push (endchar_op);
254         continue;
255       }
256       const byte_str_t str = (*acc.charStrings)[glyph];
257       unsigned int fd = acc.fdSelect->get_fd (glyph);
258       if (unlikely (fd >= acc.fdCount))
259         return false;
260       cs_interpreter_t<ENV, OPSET, flatten_param_t> interp;
261       interp.env.init (str, acc, fd);
262       flatten_param_t  param = {
263         flat_charstrings[i],
264         (bool) (plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
265       };
266       if (unlikely (!interp.interpret (param)))
267         return false;
268     }
269     return true;
270   }
271
272   const ACC &acc;
273   const hb_subset_plan_t *plan;
274 };
275
276 struct subr_closures_t
277 {
278   subr_closures_t (unsigned int fd_count) : valid (false), global_closure (), local_closures ()
279   {
280     valid = true;
281     if (!local_closures.resize (fd_count))
282       valid = false;
283   }
284
285   void reset ()
286   {
287     global_closure.clear();
288     for (unsigned int i = 0; i < local_closures.length; i++)
289       local_closures[i].clear();
290   }
291
292   bool is_valid () const { return valid; }
293   bool  valid;
294   hb_set_t  global_closure;
295   hb_vector_t<hb_set_t> local_closures;
296 };
297
298 struct parsed_cs_op_t : op_str_t
299 {
300   void init (unsigned int subr_num_ = 0)
301   {
302     subr_num = subr_num_;
303     drop_flag = false;
304     keep_flag = false;
305     skip_flag = false;
306   }
307
308   bool for_drop () const { return drop_flag; }
309   void set_drop ()       { if (!for_keep ()) drop_flag = true; }
310
311   bool for_keep () const { return keep_flag; }
312   void set_keep ()       { keep_flag = true; }
313
314   bool for_skip () const { return skip_flag; }
315   void set_skip ()       { skip_flag = true; }
316
317   unsigned int  subr_num;
318
319   protected:
320   bool    drop_flag : 1;
321   bool    keep_flag : 1;
322   bool    skip_flag : 1;
323 };
324
325 struct parsed_cs_str_t : parsed_values_t<parsed_cs_op_t>
326 {
327   void init ()
328   {
329     SUPER::init ();
330     parsed = false;
331     hint_dropped = false;
332     has_prefix_ = false;
333   }
334
335   void add_op (op_code_t op, const byte_str_ref_t& str_ref)
336   {
337     if (!is_parsed ())
338       SUPER::add_op (op, str_ref);
339   }
340
341   void add_call_op (op_code_t op, const byte_str_ref_t& str_ref, unsigned int subr_num)
342   {
343     if (!is_parsed ())
344     {
345       unsigned int parsed_len = get_count ();
346       if (likely (parsed_len > 0))
347         values[parsed_len-1].set_skip ();
348
349       parsed_cs_op_t val;
350       val.init (subr_num);
351       SUPER::add_op (op, str_ref, val);
352     }
353   }
354
355   void set_prefix (const number_t &num, op_code_t op = OpCode_Invalid)
356   {
357     has_prefix_ = true;
358     prefix_op_ = op;
359     prefix_num_ = num;
360   }
361
362   bool at_end (unsigned int pos) const
363   {
364     return ((pos + 1 >= values.length) /* CFF2 */
365         || (values[pos + 1].op == OpCode_return));
366   }
367
368   bool is_parsed () const { return parsed; }
369   void set_parsed ()      { parsed = true; }
370
371   bool is_hint_dropped () const { return hint_dropped; }
372   void set_hint_dropped ()      { hint_dropped = true; }
373
374   bool is_vsindex_dropped () const { return vsindex_dropped; }
375   void set_vsindex_dropped ()      { vsindex_dropped = true; }
376
377   bool has_prefix () const          { return has_prefix_; }
378   op_code_t prefix_op () const         { return prefix_op_; }
379   const number_t &prefix_num () const { return prefix_num_; }
380
381   protected:
382   bool    parsed;
383   bool    hint_dropped;
384   bool    vsindex_dropped;
385   bool    has_prefix_;
386   op_code_t     prefix_op_;
387   number_t      prefix_num_;
388
389   private:
390   typedef parsed_values_t<parsed_cs_op_t> SUPER;
391 };
392
393 struct parsed_cs_str_vec_t : hb_vector_t<parsed_cs_str_t>
394 {
395   private:
396   typedef hb_vector_t<parsed_cs_str_t> SUPER;
397 };
398
399 struct subr_subset_param_t
400 {
401   void init (parsed_cs_str_t *parsed_charstring_,
402              parsed_cs_str_vec_t *parsed_global_subrs_, parsed_cs_str_vec_t *parsed_local_subrs_,
403              hb_set_t *global_closure_, hb_set_t *local_closure_,
404              bool drop_hints_)
405   {
406     parsed_charstring = parsed_charstring_;
407     current_parsed_str = parsed_charstring;
408     parsed_global_subrs = parsed_global_subrs_;
409     parsed_local_subrs = parsed_local_subrs_;
410     global_closure = global_closure_;
411     local_closure = local_closure_;
412     drop_hints = drop_hints_;
413   }
414
415   parsed_cs_str_t *get_parsed_str_for_context (call_context_t &context)
416   {
417     switch (context.type)
418     {
419       case CSType_CharString:
420         return parsed_charstring;
421
422       case CSType_LocalSubr:
423         if (likely (context.subr_num < parsed_local_subrs->length))
424           return &(*parsed_local_subrs)[context.subr_num];
425         break;
426
427       case CSType_GlobalSubr:
428         if (likely (context.subr_num < parsed_global_subrs->length))
429           return &(*parsed_global_subrs)[context.subr_num];
430         break;
431     }
432     return nullptr;
433   }
434
435   template <typename ENV>
436   void set_current_str (ENV &env, bool calling)
437   {
438     parsed_cs_str_t *parsed_str = get_parsed_str_for_context (env.context);
439     if (unlikely (!parsed_str))
440     {
441       env.set_error ();
442       return;
443     }
444     /* If the called subroutine is parsed partially but not completely yet,
445      * it must be because we are calling it recursively.
446      * Handle it as an error. */
447     if (unlikely (calling && !parsed_str->is_parsed () && (parsed_str->values.length > 0)))
448       env.set_error ();
449     else
450       current_parsed_str = parsed_str;
451   }
452
453   parsed_cs_str_t       *current_parsed_str;
454
455   parsed_cs_str_t       *parsed_charstring;
456   parsed_cs_str_vec_t   *parsed_global_subrs;
457   parsed_cs_str_vec_t   *parsed_local_subrs;
458   hb_set_t      *global_closure;
459   hb_set_t      *local_closure;
460   bool    drop_hints;
461 };
462
463 struct subr_remap_t : hb_inc_bimap_t
464 {
465   void create (const hb_set_t *closure)
466   {
467     /* create a remapping of subroutine numbers from old to new.
468      * no optimization based on usage counts. fonttools doesn't appear doing that either.
469      */
470
471     hb_codepoint_t old_num = HB_SET_VALUE_INVALID;
472     while (hb_set_next (closure, &old_num))
473       add (old_num);
474
475     if (get_population () < 1240)
476       bias = 107;
477     else if (get_population () < 33900)
478       bias = 1131;
479     else
480       bias = 32768;
481   }
482
483   int biased_num (unsigned int old_num) const
484   {
485     hb_codepoint_t new_num = get (old_num);
486     return (int)new_num - bias;
487   }
488
489   protected:
490   int bias;
491 };
492
493 struct subr_remaps_t
494 {
495   subr_remaps_t (unsigned int fdCount)
496   {
497     local_remaps.resize (fdCount);
498   }
499
500   bool in_error()
501   {
502     return local_remaps.in_error ();
503   }
504
505   void create (subr_closures_t& closures)
506   {
507     global_remap.create (&closures.global_closure);
508     for (unsigned int i = 0; i < local_remaps.length; i++)
509       local_remaps[i].create (&closures.local_closures[i]);
510   }
511
512   subr_remap_t         global_remap;
513   hb_vector_t<subr_remap_t>  local_remaps;
514 };
515
516 template <typename SUBSETTER, typename SUBRS, typename ACC, typename ENV, typename OPSET, op_code_t endchar_op=OpCode_Invalid>
517 struct subr_subsetter_t
518 {
519   subr_subsetter_t (ACC &acc_, const hb_subset_plan_t *plan_)
520       : acc (acc_), plan (plan_), closures(acc_.fdCount), remaps(acc_.fdCount)
521   {}
522
523   /* Subroutine subsetting with --no-desubroutinize runs in phases:
524    *
525    * 1. execute charstrings/subroutines to determine subroutine closures
526    * 2. parse out all operators and numbers
527    * 3. mark hint operators and operands for removal if --no-hinting
528    * 4. re-encode all charstrings and subroutines with new subroutine numbers
529    *
530    * Phases #1 and #2 are done at the same time in collect_subrs ().
531    * Phase #3 walks charstrings/subroutines forward then backward (hence parsing required),
532    * because we can't tell if a number belongs to a hint op until we see the first moveto.
533    *
534    * Assumption: a callsubr/callgsubr operator must immediately follow a (biased) subroutine number
535    * within the same charstring/subroutine, e.g., not split across a charstring and a subroutine.
536    */
537   bool subset (void)
538   {
539     parsed_charstrings.resize (plan->num_output_glyphs ());
540     parsed_global_subrs.resize (acc.globalSubrs->count);
541
542     if (unlikely (remaps.in_error()
543                   || parsed_charstrings.in_error ()
544                   || parsed_global_subrs.in_error ())) {
545       return false;
546     }
547
548     if (unlikely (!parsed_local_subrs.resize (acc.fdCount))) return false;
549
550     for (unsigned int i = 0; i < acc.fdCount; i++)
551     {
552       parsed_local_subrs[i].resize (acc.privateDicts[i].localSubrs->count);
553       if (unlikely (parsed_local_subrs[i].in_error ())) return false;
554     }
555     if (unlikely (!closures.valid))
556       return false;
557
558     /* phase 1 & 2 */
559     for (unsigned int i = 0; i < plan->num_output_glyphs (); i++)
560     {
561       hb_codepoint_t  glyph;
562       if (!plan->old_gid_for_new_gid (i, &glyph))
563         continue;
564       const byte_str_t str = (*acc.charStrings)[glyph];
565       unsigned int fd = acc.fdSelect->get_fd (glyph);
566       if (unlikely (fd >= acc.fdCount))
567         return false;
568
569       cs_interpreter_t<ENV, OPSET, subr_subset_param_t> interp;
570       interp.env.init (str, acc, fd);
571
572       subr_subset_param_t  param;
573       param.init (&parsed_charstrings[i],
574                   &parsed_global_subrs,  &parsed_local_subrs[fd],
575                   &closures.global_closure, &closures.local_closures[fd],
576                   plan->flags & HB_SUBSET_FLAGS_NO_HINTING);
577
578       if (unlikely (!interp.interpret (param)))
579         return false;
580
581       /* complete parsed string esp. copy CFF1 width or CFF2 vsindex to the parsed charstring for encoding */
582       SUBSETTER::complete_parsed_str (interp.env, param, parsed_charstrings[i]);
583     }
584
585     if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
586     {
587       /* mark hint ops and arguments for drop */
588       for (unsigned int i = 0; i < plan->num_output_glyphs (); i++)
589       {
590         hb_codepoint_t  glyph;
591         if (!plan->old_gid_for_new_gid (i, &glyph))
592           continue;
593         unsigned int fd = acc.fdSelect->get_fd (glyph);
594         if (unlikely (fd >= acc.fdCount))
595           return false;
596         subr_subset_param_t  param;
597         param.init (&parsed_charstrings[i],
598                     &parsed_global_subrs,  &parsed_local_subrs[fd],
599                     &closures.global_closure, &closures.local_closures[fd],
600                     plan->flags & HB_SUBSET_FLAGS_NO_HINTING);
601
602         drop_hints_param_t  drop;
603         if (drop_hints_in_str (parsed_charstrings[i], param, drop))
604         {
605           parsed_charstrings[i].set_hint_dropped ();
606           if (drop.vsindex_dropped)
607             parsed_charstrings[i].set_vsindex_dropped ();
608         }
609       }
610
611       /* after dropping hints recreate closures of actually used subrs */
612       closures.reset ();
613       for (unsigned int i = 0; i < plan->num_output_glyphs (); i++)
614       {
615         hb_codepoint_t  glyph;
616         if (!plan->old_gid_for_new_gid (i, &glyph))
617           continue;
618         unsigned int fd = acc.fdSelect->get_fd (glyph);
619         if (unlikely (fd >= acc.fdCount))
620           return false;
621         subr_subset_param_t  param;
622         param.init (&parsed_charstrings[i],
623                     &parsed_global_subrs,  &parsed_local_subrs[fd],
624                     &closures.global_closure, &closures.local_closures[fd],
625                     plan->flags & HB_SUBSET_FLAGS_NO_HINTING);
626         collect_subr_refs_in_str (parsed_charstrings[i], param);
627       }
628     }
629
630     remaps.create (closures);
631
632     return true;
633   }
634
635   bool encode_charstrings (str_buff_vec_t &buffArray) const
636   {
637     if (unlikely (!buffArray.resize (plan->num_output_glyphs ())))
638       return false;
639     for (unsigned int i = 0; i < plan->num_output_glyphs (); i++)
640     {
641       hb_codepoint_t  glyph;
642       if (!plan->old_gid_for_new_gid (i, &glyph))
643       {
644         /* add an endchar only charstring for a missing glyph if CFF1 */
645         if (endchar_op != OpCode_Invalid) buffArray[i].push (endchar_op);
646         continue;
647       }
648       unsigned int  fd = acc.fdSelect->get_fd (glyph);
649       if (unlikely (fd >= acc.fdCount))
650         return false;
651       if (unlikely (!encode_str (parsed_charstrings[i], fd, buffArray[i])))
652         return false;
653     }
654     return true;
655   }
656
657   bool encode_subrs (const parsed_cs_str_vec_t &subrs, const subr_remap_t& remap, unsigned int fd, str_buff_vec_t &buffArray) const
658   {
659     unsigned int  count = remap.get_population ();
660
661     if (unlikely (!buffArray.resize (count)))
662       return false;
663     for (unsigned int old_num = 0; old_num < subrs.length; old_num++)
664     {
665       hb_codepoint_t new_num = remap[old_num];
666       if (new_num != CFF_UNDEF_CODE)
667       {
668         if (unlikely (!encode_str (subrs[old_num], fd, buffArray[new_num])))
669           return false;
670       }
671     }
672     return true;
673   }
674
675   bool encode_globalsubrs (str_buff_vec_t &buffArray)
676   {
677     return encode_subrs (parsed_global_subrs, remaps.global_remap, 0, buffArray);
678   }
679
680   bool encode_localsubrs (unsigned int fd, str_buff_vec_t &buffArray) const
681   {
682     return encode_subrs (parsed_local_subrs[fd], remaps.local_remaps[fd], fd, buffArray);
683   }
684
685   protected:
686   struct drop_hints_param_t
687   {
688     drop_hints_param_t ()
689       : seen_moveto (false),
690         ends_in_hint (false),
691         all_dropped (false),
692         vsindex_dropped (false) {}
693
694     bool  seen_moveto;
695     bool  ends_in_hint;
696     bool  all_dropped;
697     bool  vsindex_dropped;
698   };
699
700   bool drop_hints_in_subr (parsed_cs_str_t &str, unsigned int pos,
701                            parsed_cs_str_vec_t &subrs, unsigned int subr_num,
702                            const subr_subset_param_t &param, drop_hints_param_t &drop)
703   {
704     drop.ends_in_hint = false;
705     bool has_hint = drop_hints_in_str (subrs[subr_num], param, drop);
706
707     /* if this subr ends with a stem hint (i.e., not a number; potential argument for moveto),
708      * then this entire subroutine must be a hint. drop its call. */
709     if (drop.ends_in_hint)
710     {
711       str.values[pos].set_drop ();
712       /* if this subr call is at the end of the parent subr, propagate the flag
713        * otherwise reset the flag */
714       if (!str.at_end (pos))
715         drop.ends_in_hint = false;
716     }
717     else if (drop.all_dropped)
718     {
719       str.values[pos].set_drop ();
720     }
721
722     return has_hint;
723   }
724
725   /* returns true if it sees a hint op before the first moveto */
726   bool drop_hints_in_str (parsed_cs_str_t &str, const subr_subset_param_t &param, drop_hints_param_t &drop)
727   {
728     bool  seen_hint = false;
729
730     for (unsigned int pos = 0; pos < str.values.length; pos++)
731     {
732       bool  has_hint = false;
733       switch (str.values[pos].op)
734       {
735         case OpCode_callsubr:
736           has_hint = drop_hints_in_subr (str, pos,
737                                         *param.parsed_local_subrs, str.values[pos].subr_num,
738                                         param, drop);
739           break;
740
741         case OpCode_callgsubr:
742           has_hint = drop_hints_in_subr (str, pos,
743                                         *param.parsed_global_subrs, str.values[pos].subr_num,
744                                         param, drop);
745           break;
746
747         case OpCode_rmoveto:
748         case OpCode_hmoveto:
749         case OpCode_vmoveto:
750           drop.seen_moveto = true;
751           break;
752
753         case OpCode_hintmask:
754         case OpCode_cntrmask:
755           if (drop.seen_moveto)
756           {
757             str.values[pos].set_drop ();
758             break;
759           }
760           HB_FALLTHROUGH;
761
762         case OpCode_hstemhm:
763         case OpCode_vstemhm:
764         case OpCode_hstem:
765         case OpCode_vstem:
766           has_hint = true;
767           str.values[pos].set_drop ();
768           if (str.at_end (pos))
769             drop.ends_in_hint = true;
770           break;
771
772         case OpCode_dotsection:
773           str.values[pos].set_drop ();
774           break;
775
776         default:
777           /* NONE */
778           break;
779       }
780       if (has_hint)
781       {
782         for (int i = pos - 1; i >= 0; i--)
783         {
784           parsed_cs_op_t  &csop = str.values[(unsigned)i];
785           if (csop.for_drop ())
786             break;
787           csop.set_drop ();
788           if (csop.op == OpCode_vsindexcs)
789             drop.vsindex_dropped = true;
790         }
791         seen_hint |= has_hint;
792       }
793     }
794
795     /* Raise all_dropped flag if all operators except return are dropped from a subr.
796      * It may happen even after seeing the first moveto if a subr contains
797      * only (usually one) hintmask operator, then calls to this subr can be dropped.
798      */
799     drop.all_dropped = true;
800     for (unsigned int pos = 0; pos < str.values.length; pos++)
801     {
802       parsed_cs_op_t  &csop = str.values[pos];
803       if (csop.op == OpCode_return)
804         break;
805       if (!csop.for_drop ())
806       {
807         drop.all_dropped = false;
808         break;
809       }
810     }
811
812     return seen_hint;
813   }
814
815   void collect_subr_refs_in_subr (parsed_cs_str_t &str, unsigned int pos,
816                                   unsigned int subr_num, parsed_cs_str_vec_t &subrs,
817                                   hb_set_t *closure,
818                                   const subr_subset_param_t &param)
819   {
820     closure->add (subr_num);
821     collect_subr_refs_in_str (subrs[subr_num], param);
822   }
823
824   void collect_subr_refs_in_str (parsed_cs_str_t &str, const subr_subset_param_t &param)
825   {
826     for (unsigned int pos = 0; pos < str.values.length; pos++)
827     {
828       if (!str.values[pos].for_drop ())
829       {
830         switch (str.values[pos].op)
831         {
832           case OpCode_callsubr:
833             collect_subr_refs_in_subr (str, pos,
834                                        str.values[pos].subr_num, *param.parsed_local_subrs,
835                                        param.local_closure, param);
836             break;
837
838           case OpCode_callgsubr:
839             collect_subr_refs_in_subr (str, pos,
840                                        str.values[pos].subr_num, *param.parsed_global_subrs,
841                                        param.global_closure, param);
842             break;
843
844           default: break;
845         }
846       }
847     }
848   }
849
850   bool encode_str (const parsed_cs_str_t &str, const unsigned int fd, str_buff_t &buff) const
851   {
852     buff.init ();
853     str_encoder_t  encoder (buff);
854     encoder.reset ();
855     /* if a prefix (CFF1 width or CFF2 vsindex) has been removed along with hints,
856      * re-insert it at the beginning of charstreing */
857     if (str.has_prefix () && str.is_hint_dropped ())
858     {
859       encoder.encode_num (str.prefix_num ());
860       if (str.prefix_op () != OpCode_Invalid)
861         encoder.encode_op (str.prefix_op ());
862     }
863     for (unsigned int i = 0; i < str.get_count(); i++)
864     {
865       const parsed_cs_op_t  &opstr = str.values[i];
866       if (!opstr.for_drop () && !opstr.for_skip ())
867       {
868         switch (opstr.op)
869         {
870           case OpCode_callsubr:
871             encoder.encode_int (remaps.local_remaps[fd].biased_num (opstr.subr_num));
872             encoder.encode_op (OpCode_callsubr);
873             break;
874
875           case OpCode_callgsubr:
876             encoder.encode_int (remaps.global_remap.biased_num (opstr.subr_num));
877             encoder.encode_op (OpCode_callgsubr);
878             break;
879
880           default:
881             encoder.copy_str (opstr.str);
882             break;
883         }
884       }
885     }
886     return !encoder.is_error ();
887   }
888
889   protected:
890   const ACC                     &acc;
891   const hb_subset_plan_t        *plan;
892
893   subr_closures_t               closures;
894
895   parsed_cs_str_vec_t           parsed_charstrings;
896   parsed_cs_str_vec_t           parsed_global_subrs;
897   hb_vector_t<parsed_cs_str_vec_t>  parsed_local_subrs;
898
899   subr_remaps_t                 remaps;
900
901   private:
902   typedef typename SUBRS::count_type subr_count_type;
903 };
904
905 } /* namespace CFF */
906
907 HB_INTERNAL bool
908 hb_plan_subset_cff_fdselect (const hb_subset_plan_t *plan,
909                             unsigned int fdCount,
910                             const CFF::FDSelect &src, /* IN */
911                             unsigned int &subset_fd_count /* OUT */,
912                             unsigned int &subset_fdselect_size /* OUT */,
913                             unsigned int &subset_fdselect_format /* OUT */,
914                             hb_vector_t<CFF::code_pair_t> &fdselect_ranges /* OUT */,
915                             hb_inc_bimap_t &fdmap /* OUT */);
916
917 HB_INTERNAL bool
918 hb_serialize_cff_fdselect (hb_serialize_context_t *c,
919                           unsigned int num_glyphs,
920                           const CFF::FDSelect &src,
921                           unsigned int fd_count,
922                           unsigned int fdselect_format,
923                           unsigned int size,
924                           const hb_vector_t<CFF::code_pair_t> &fdselect_ranges);
925
926 #endif /* HB_SUBSET_CFF_COMMON_HH */