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