• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 StrEncoder
39 {
StrEncoderCFF::StrEncoder40   StrEncoder (StrBuff &buff_)
41     : buff (buff_), error (false) {}
42 
resetCFF::StrEncoder43   void reset () { buff.resize (0); }
44 
encode_byteCFF::StrEncoder45   void encode_byte (unsigned char b)
46   {
47     if (unlikely (buff.push ((const char)b) == &Crap(char)))
48       set_error ();
49   }
50 
encode_intCFF::StrEncoder51   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 
encode_numCFF::StrEncoder82   void encode_num (const Number& 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 
encode_opCFF::StrEncoder99   void encode_op (OpCode 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 
copy_strCFF::StrEncoder110   void copy_str (const ByteStr &str)
111   {
112     unsigned int  offset = buff.len;
113     buff.resize (offset + str.len);
114     if (unlikely (buff.len < offset + str.len))
115     {
116       set_error ();
117       return;
118     }
119     memcpy (&buff[offset], &str.str[0], str.len);
120   }
121 
is_errorCFF::StrEncoder122   bool is_error () const { return error; }
123 
124   protected:
set_errorCFF::StrEncoder125   void set_error () { error = true; }
126 
127   StrBuff &buff;
128   bool    error;
129 };
130 
131 struct CFFSubTableOffsets {
CFFSubTableOffsetsCFF::CFFSubTableOffsets132   CFFSubTableOffsets () : privateDictsOffset (0)
133   {
134     topDictInfo.init ();
135     FDSelectInfo.init ();
136     FDArrayInfo.init ();
137     charStringsInfo.init ();
138     globalSubrsInfo.init ();
139     localSubrsInfos.init ();
140   }
141 
~CFFSubTableOffsetsCFF::CFFSubTableOffsets142   ~CFFSubTableOffsets () { localSubrsInfos.fini (); }
143 
144   TableInfo     topDictInfo;
145   TableInfo     FDSelectInfo;
146   TableInfo     FDArrayInfo;
147   TableInfo     charStringsInfo;
148   unsigned int  privateDictsOffset;
149   TableInfo     globalSubrsInfo;
150   hb_vector_t<TableInfo>  localSubrsInfos;
151 };
152 
153 template <typename OPSTR=OpStr>
154 struct CFFTopDict_OpSerializer : OpSerializer
155 {
serializeCFF::CFFTopDict_OpSerializer156   bool serialize (hb_serialize_context_t *c,
157 		  const OPSTR &opstr,
158 		  const CFFSubTableOffsets &offsets) const
159   {
160     TRACE_SERIALIZE (this);
161 
162     switch (opstr.op)
163     {
164       case OpCode_CharStrings:
165 	return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.charStringsInfo.offset));
166 
167       case OpCode_FDArray:
168 	return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.FDArrayInfo.offset));
169 
170       case OpCode_FDSelect:
171 	return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.FDSelectInfo.offset));
172 
173       default:
174 	return_trace (copy_opstr (c, opstr));
175     }
176     return_trace (true);
177   }
178 
calculate_serialized_sizeCFF::CFFTopDict_OpSerializer179   unsigned int calculate_serialized_size (const OPSTR &opstr) const
180   {
181     switch (opstr.op)
182     {
183       case OpCode_CharStrings:
184       case OpCode_FDArray:
185       case OpCode_FDSelect:
186 	return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (opstr.op);
187 
188       default:
189 	return opstr.str.len;
190     }
191   }
192 };
193 
194 struct CFFFontDict_OpSerializer : OpSerializer
195 {
serializeCFF::CFFFontDict_OpSerializer196   bool serialize (hb_serialize_context_t *c,
197 		  const OpStr &opstr,
198 		  const TableInfo &privateDictInfo) const
199   {
200     TRACE_SERIALIZE (this);
201 
202     if (opstr.op == OpCode_Private)
203     {
204       /* serialize the private dict size & offset as 2-byte & 4-byte integers */
205       if (unlikely (!UnsizedByteStr::serialize_int2 (c, privateDictInfo.size) ||
206 		    !UnsizedByteStr::serialize_int4 (c, privateDictInfo.offset)))
207 	return_trace (false);
208 
209       /* serialize the opcode */
210       HBUINT8 *p = c->allocate_size<HBUINT8> (1);
211       if (unlikely (p == nullptr)) return_trace (false);
212       p->set (OpCode_Private);
213 
214       return_trace (true);
215     }
216     else
217     {
218       HBUINT8 *d = c->allocate_size<HBUINT8> (opstr.str.len);
219       if (unlikely (d == nullptr)) return_trace (false);
220       memcpy (d, &opstr.str.str[0], opstr.str.len);
221     }
222     return_trace (true);
223   }
224 
calculate_serialized_sizeCFF::CFFFontDict_OpSerializer225   unsigned int calculate_serialized_size (const OpStr &opstr) const
226   {
227     if (opstr.op == OpCode_Private)
228       return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Private);
229     else
230       return opstr.str.len;
231   }
232 };
233 
234 struct CFFPrivateDict_OpSerializer : OpSerializer
235 {
CFFPrivateDict_OpSerializerCFF::CFFPrivateDict_OpSerializer236   CFFPrivateDict_OpSerializer (bool desubroutinize_, bool drop_hints_)
237     : desubroutinize (desubroutinize_), drop_hints (drop_hints_) {}
238 
serializeCFF::CFFPrivateDict_OpSerializer239   bool serialize (hb_serialize_context_t *c,
240 		  const OpStr &opstr,
241 		  const unsigned int subrsOffset) const
242   {
243     TRACE_SERIALIZE (this);
244 
245     if (drop_hints && DictOpSet::is_hint_op (opstr.op))
246       return true;
247     if (opstr.op == OpCode_Subrs)
248     {
249       if (desubroutinize || (subrsOffset == 0))
250 	return_trace (true);
251       else
252 	return_trace (FontDict::serialize_offset2_op (c, opstr.op, subrsOffset));
253     }
254     else
255       return_trace (copy_opstr (c, opstr));
256   }
257 
calculate_serialized_sizeCFF::CFFPrivateDict_OpSerializer258   unsigned int calculate_serialized_size (const OpStr &opstr,
259 					  bool has_localsubr=true) const
260   {
261     if (drop_hints && DictOpSet::is_hint_op (opstr.op))
262       return 0;
263     if (opstr.op == OpCode_Subrs)
264     {
265       if (desubroutinize || !has_localsubr)
266 	return 0;
267       else
268 	return OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (opstr.op);
269     }
270     else
271       return opstr.str.len;
272   }
273 
274   protected:
275   const bool  desubroutinize;
276   const bool  drop_hints;
277 };
278 
279 struct FlattenParam
280 {
281   StrBuff     &flatStr;
282   bool	drop_hints;
283 };
284 
285 template <typename ACC, typename ENV, typename OPSET>
286 struct SubrFlattener
287 {
SubrFlattenerCFF::SubrFlattener288   SubrFlattener (const ACC &acc_,
289 			const hb_vector_t<hb_codepoint_t> &glyphs_,
290 			bool drop_hints_)
291     : acc (acc_),
292       glyphs (glyphs_),
293       drop_hints (drop_hints_)
294   {}
295 
flattenCFF::SubrFlattener296   bool flatten (StrBuffArray &flat_charstrings)
297   {
298     if (!flat_charstrings.resize (glyphs.len))
299       return false;
300     for (unsigned int i = 0; i < glyphs.len; i++)
301       flat_charstrings[i].init ();
302     for (unsigned int i = 0; i < glyphs.len; i++)
303     {
304       hb_codepoint_t  glyph = glyphs[i];
305       const ByteStr str = (*acc.charStrings)[glyph];
306       unsigned int fd = acc.fdSelect->get_fd (glyph);
307       if (unlikely (fd >= acc.fdCount))
308       	return false;
309       CSInterpreter<ENV, OPSET, FlattenParam> interp;
310       interp.env.init (str, acc, fd);
311       FlattenParam  param = { flat_charstrings[i], drop_hints };
312       if (unlikely (!interp.interpret (param)))
313 	return false;
314     }
315     return true;
316   }
317 
318   const ACC &acc;
319   const hb_vector_t<hb_codepoint_t> &glyphs;
320   bool  drop_hints;
321 };
322 
323 struct SubrClosures
324 {
SubrClosuresCFF::SubrClosures325   SubrClosures () : valid (false), global_closure (nullptr)
326   { local_closures.init (); }
327 
initCFF::SubrClosures328   void init (unsigned int fd_count)
329   {
330     valid = true;
331     global_closure = hb_set_create ();
332     if (global_closure == hb_set_get_empty ())
333       valid = false;
334     if (!local_closures.resize (fd_count))
335       valid = false;
336 
337     for (unsigned int i = 0; i < local_closures.len; i++)
338     {
339       local_closures[i] = hb_set_create ();
340       if (local_closures[i] == hb_set_get_empty ())
341 	valid = false;
342     }
343   }
344 
finiCFF::SubrClosures345   void fini ()
346   {
347     hb_set_destroy (global_closure);
348     for (unsigned int i = 0; i < local_closures.len; i++)
349       hb_set_destroy (local_closures[i]);
350     local_closures.fini ();
351   }
352 
resetCFF::SubrClosures353   void reset ()
354   {
355     hb_set_clear (global_closure);
356     for (unsigned int i = 0; i < local_closures.len; i++)
357       hb_set_clear (local_closures[i]);
358   }
359 
is_validCFF::SubrClosures360   bool is_valid () const { return valid; }
361   bool  valid;
362   hb_set_t  *global_closure;
363   hb_vector_t<hb_set_t *> local_closures;
364 };
365 
366 struct ParsedCSOp : OpStr
367 {
initCFF::ParsedCSOp368   void init (unsigned int subr_num_ = 0)
369   {
370     OpStr::init ();
371     subr_num = subr_num_;
372     drop_flag = false;
373     keep_flag = false;
374     skip_flag = false;
375   }
376 
finiCFF::ParsedCSOp377   void fini () { OpStr::fini (); }
378 
for_dropCFF::ParsedCSOp379   bool for_drop () const { return drop_flag; }
set_dropCFF::ParsedCSOp380   void set_drop ()       { if (!for_keep ()) drop_flag = true; }
381 
for_keepCFF::ParsedCSOp382   bool for_keep () const { return keep_flag; }
set_keepCFF::ParsedCSOp383   void set_keep ()       { keep_flag = true; }
384 
for_skipCFF::ParsedCSOp385   bool for_skip () const { return skip_flag; }
set_skipCFF::ParsedCSOp386   void set_skip ()       { skip_flag = true; }
387 
388   unsigned int  subr_num;
389 
390   protected:
391   bool	  drop_flag : 1;
392   bool	  keep_flag : 1;
393   bool	  skip_flag : 1;
394 };
395 
396 struct ParsedCStr : ParsedValues<ParsedCSOp>
397 {
initCFF::ParsedCStr398   void init ()
399   {
400     SUPER::init ();
401     parsed = false;
402     hint_dropped = false;
403     has_prefix_ = false;
404   }
405 
add_opCFF::ParsedCStr406   void add_op (OpCode op, const SubByteStr& substr)
407   {
408     if (!is_parsed ())
409       SUPER::add_op (op, substr);
410   }
411 
add_call_opCFF::ParsedCStr412   void add_call_op (OpCode op, const SubByteStr& substr, unsigned int subr_num)
413   {
414     if (!is_parsed ())
415     {
416       unsigned int parsed_len = get_count ();
417       if (likely (parsed_len > 0))
418 	values[parsed_len-1].set_skip ();
419 
420       ParsedCSOp val;
421       val.init (subr_num);
422       SUPER::add_op (op, substr, val);
423     }
424   }
425 
set_prefixCFF::ParsedCStr426   void set_prefix (const Number &num, OpCode op = OpCode_Invalid)
427   {
428     has_prefix_ = true;
429     prefix_op_ = op;
430     prefix_num_ = num;
431   }
432 
at_endCFF::ParsedCStr433   bool at_end (unsigned int pos) const
434   {
435     return ((pos + 1 >= values.len) /* CFF2 */
436 	|| (values[pos + 1].op == OpCode_return));
437   }
438 
is_parsedCFF::ParsedCStr439   bool is_parsed () const { return parsed; }
set_parsedCFF::ParsedCStr440   void set_parsed ()      { parsed = true; }
441 
is_hint_droppedCFF::ParsedCStr442   bool is_hint_dropped () const { return hint_dropped; }
set_hint_droppedCFF::ParsedCStr443   void set_hint_dropped ()      { hint_dropped = true; }
444 
is_vsindex_droppedCFF::ParsedCStr445   bool is_vsindex_dropped () const { return vsindex_dropped; }
set_vsindex_droppedCFF::ParsedCStr446   void set_vsindex_dropped ()      { vsindex_dropped = true; }
447 
has_prefixCFF::ParsedCStr448   bool has_prefix () const          { return has_prefix_; }
prefix_opCFF::ParsedCStr449   OpCode prefix_op () const         { return prefix_op_; }
prefix_numCFF::ParsedCStr450   const Number &prefix_num () const { return prefix_num_; }
451 
452   protected:
453   bool    parsed;
454   bool    hint_dropped;
455   bool    vsindex_dropped;
456   bool    has_prefix_;
457   OpCode  prefix_op_;
458   Number  prefix_num_;
459 
460   private:
461   typedef ParsedValues<ParsedCSOp> SUPER;
462 };
463 
464 struct ParsedCStrs : hb_vector_t<ParsedCStr>
465 {
initCFF::ParsedCStrs466   void init (unsigned int len_ = 0)
467   {
468     SUPER::init ();
469     resize (len_);
470     for (unsigned int i = 0; i < len; i++)
471       (*this)[i].init ();
472   }
finiCFF::ParsedCStrs473   void fini () { SUPER::fini_deep (); }
474 
475   private:
476   typedef hb_vector_t<ParsedCStr> SUPER;
477 };
478 
479 struct SubrSubsetParam
480 {
initCFF::SubrSubsetParam481   void init (ParsedCStr *parsed_charstring_,
482 		    ParsedCStrs *parsed_global_subrs_, ParsedCStrs *parsed_local_subrs_,
483 		    hb_set_t *global_closure_, hb_set_t *local_closure_,
484 		    bool drop_hints_)
485   {
486     parsed_charstring = parsed_charstring_;
487     current_parsed_str = parsed_charstring;
488     parsed_global_subrs = parsed_global_subrs_;
489     parsed_local_subrs = parsed_local_subrs_;
490     global_closure = global_closure_;
491     local_closure = local_closure_;
492     drop_hints = drop_hints_;
493   }
494 
get_parsed_str_for_contextCFF::SubrSubsetParam495   ParsedCStr *get_parsed_str_for_context (CallContext &context)
496   {
497     switch (context.type)
498     {
499       case CSType_CharString:
500 	return parsed_charstring;
501 
502       case CSType_LocalSubr:
503 	if (likely (context.subr_num < parsed_local_subrs->len))
504 	  return &(*parsed_local_subrs)[context.subr_num];
505 	break;
506 
507       case CSType_GlobalSubr:
508 	if (likely (context.subr_num < parsed_global_subrs->len))
509 	  return &(*parsed_global_subrs)[context.subr_num];
510 	break;
511     }
512     return nullptr;
513   }
514 
515   template <typename ENV>
set_current_strCFF::SubrSubsetParam516   void set_current_str (ENV &env, bool calling)
517   {
518     ParsedCStr  *parsed_str = get_parsed_str_for_context (env.context);
519     if (likely (parsed_str != nullptr))
520     {
521       /* If the called subroutine is parsed partially but not completely yet,
522        * it must be because we are calling it recursively.
523        * Handle it as an error. */
524       if (unlikely (calling && !parsed_str->is_parsed () && (parsed_str->values.len > 0)))
525       	env.set_error ();
526       else
527       	current_parsed_str = parsed_str;
528     }
529     else
530       env.set_error ();
531   }
532 
533   ParsedCStr    *current_parsed_str;
534 
535   ParsedCStr    *parsed_charstring;
536   ParsedCStrs   *parsed_global_subrs;
537   ParsedCStrs   *parsed_local_subrs;
538   hb_set_t      *global_closure;
539   hb_set_t      *local_closure;
540   bool	  drop_hints;
541 };
542 
543 struct SubrRemap : Remap
544 {
createCFF::SubrRemap545   void create (hb_set_t *closure)
546   {
547     /* create a remapping of subroutine numbers from old to new.
548      * no optimization based on usage counts. fonttools doesn't appear doing that either.
549      */
550     reset (closure->get_max () + 1);
551     for (hb_codepoint_t old_num = 0; old_num < len; old_num++)
552     {
553       if (hb_set_has (closure, old_num))
554 	add (old_num);
555     }
556 
557     if (get_count () < 1240)
558       bias = 107;
559     else if (get_count () < 33900)
560       bias = 1131;
561     else
562       bias = 32768;
563   }
564 
operator []CFF::SubrRemap565   hb_codepoint_t operator[] (unsigned int old_num) const
566   {
567     if (old_num >= len)
568       return CFF_UNDEF_CODE;
569     else
570       return Remap::operator[] (old_num);
571   }
572 
biased_numCFF::SubrRemap573   int biased_num (unsigned int old_num) const
574   {
575     hb_codepoint_t new_num = (*this)[old_num];
576     return (int)new_num - bias;
577   }
578 
579   protected:
580   int bias;
581 };
582 
583 struct SubrRemaps
584 {
SubrRemapsCFF::SubrRemaps585   SubrRemaps ()
586   {
587     global_remap.init ();
588     local_remaps.init ();
589   }
590 
~SubrRemapsCFF::SubrRemaps591   ~SubrRemaps () { fini (); }
592 
initCFF::SubrRemaps593   void init (unsigned int fdCount)
594   {
595     local_remaps.resize (fdCount);
596     for (unsigned int i = 0; i < fdCount; i++)
597       local_remaps[i].init ();
598   }
599 
createCFF::SubrRemaps600   void create (SubrClosures& closures)
601   {
602     global_remap.create (closures.global_closure);
603     for (unsigned int i = 0; i < local_remaps.len; i++)
604       local_remaps[i].create (closures.local_closures[i]);
605   }
606 
finiCFF::SubrRemaps607   void fini ()
608   {
609     global_remap.fini ();
610     local_remaps.fini_deep ();
611   }
612 
613   SubrRemap	       global_remap;
614   hb_vector_t<SubrRemap>  local_remaps;
615 };
616 
617 template <typename SUBSETTER, typename SUBRS, typename ACC, typename ENV, typename OPSET>
618 struct SubrSubsetter
619 {
SubrSubsetterCFF::SubrSubsetter620   SubrSubsetter ()
621   {
622     parsed_charstrings.init ();
623     parsed_global_subrs.init ();
624     parsed_local_subrs.init ();
625   }
626 
~SubrSubsetterCFF::SubrSubsetter627   ~SubrSubsetter ()
628   {
629     closures.fini ();
630     remaps.fini ();
631     parsed_charstrings.fini_deep ();
632     parsed_global_subrs.fini_deep ();
633     parsed_local_subrs.fini_deep ();
634   }
635 
636   /* Subroutine subsetting with --no-desubroutinize runs in phases:
637    *
638    * 1. execute charstrings/subroutines to determine subroutine closures
639    * 2. parse out all operators and numbers
640    * 3. mark hint operators and operands for removal if --no-hinting
641    * 4. re-encode all charstrings and subroutines with new subroutine numbers
642    *
643    * Phases #1 and #2 are done at the same time in collect_subrs ().
644    * Phase #3 walks charstrings/subroutines forward then backward (hence parsing required),
645    * because we can't tell if a number belongs to a hint op until we see the first moveto.
646    *
647    * Assumption: a callsubr/callgsubr operator must immediately follow a (biased) subroutine number
648    * within the same charstring/subroutine, e.g., not split across a charstring and a subroutine.
649    */
subsetCFF::SubrSubsetter650   bool subset (ACC &acc, const hb_vector_t<hb_codepoint_t> &glyphs, bool drop_hints)
651   {
652     closures.init (acc.fdCount);
653     remaps.init (acc.fdCount);
654 
655     parsed_charstrings.init (glyphs.len);
656     parsed_global_subrs.init (acc.globalSubrs->count);
657     parsed_local_subrs.resize (acc.fdCount);
658     for (unsigned int i = 0; i < acc.fdCount; i++)
659     {
660       parsed_local_subrs[i].init (acc.privateDicts[i].localSubrs->count);
661     }
662     if (unlikely (!closures.valid))
663       return false;
664 
665     /* phase 1 & 2 */
666     for (unsigned int i = 0; i < glyphs.len; i++)
667     {
668       hb_codepoint_t  glyph = glyphs[i];
669       const ByteStr str = (*acc.charStrings)[glyph];
670       unsigned int fd = acc.fdSelect->get_fd (glyph);
671       if (unlikely (fd >= acc.fdCount))
672       	return false;
673 
674       CSInterpreter<ENV, OPSET, SubrSubsetParam> interp;
675       interp.env.init (str, acc, fd);
676 
677       SubrSubsetParam  param;
678       param.init (&parsed_charstrings[i],
679 		  &parsed_global_subrs,  &parsed_local_subrs[fd],
680 		  closures.global_closure, closures.local_closures[fd],
681 		  drop_hints);
682 
683       if (unlikely (!interp.interpret (param)))
684 	return false;
685 
686       /* finalize parsed string esp. copy CFF1 width or CFF2 vsindex to the parsed charstring for encoding */
687       SUBSETTER::finalize_parsed_str (interp.env, param, parsed_charstrings[i]);
688     }
689 
690     if (drop_hints)
691     {
692       /* mark hint ops and arguments for drop */
693       for (unsigned int i = 0; i < glyphs.len; i++)
694       {
695 	unsigned int fd = acc.fdSelect->get_fd (glyphs[i]);
696 	if (unlikely (fd >= acc.fdCount))
697 	  return false;
698 	SubrSubsetParam  param;
699 	param.init (&parsed_charstrings[i],
700 		    &parsed_global_subrs,  &parsed_local_subrs[fd],
701 		    closures.global_closure, closures.local_closures[fd],
702 		    drop_hints);
703 
704 	DropHintsParam  drop;
705 	if (drop_hints_in_str (parsed_charstrings[i], param, drop))
706 	{
707 	  parsed_charstrings[i].set_hint_dropped ();
708 	  if (drop.vsindex_dropped)
709 	    parsed_charstrings[i].set_vsindex_dropped ();
710 	}
711       }
712 
713       /* after dropping hints recreate closures of actually used subrs */
714       closures.reset ();
715       for (unsigned int i = 0; i < glyphs.len; i++)
716       {
717 	unsigned int fd = acc.fdSelect->get_fd (glyphs[i]);
718 	if (unlikely (fd >= acc.fdCount))
719 	  return false;
720 	SubrSubsetParam  param;
721 	param.init (&parsed_charstrings[i],
722 		    &parsed_global_subrs,  &parsed_local_subrs[fd],
723 		    closures.global_closure, closures.local_closures[fd],
724 		    drop_hints);
725 	collect_subr_refs_in_str (parsed_charstrings[i], param);
726       }
727     }
728 
729     remaps.create (closures);
730 
731     return true;
732   }
733 
encode_charstringsCFF::SubrSubsetter734   bool encode_charstrings (ACC &acc, const hb_vector_t<hb_codepoint_t> &glyphs, StrBuffArray &buffArray) const
735   {
736     if (unlikely (!buffArray.resize (glyphs.len)))
737       return false;
738     for (unsigned int i = 0; i < glyphs.len; i++)
739     {
740       unsigned int  fd = acc.fdSelect->get_fd (glyphs[i]);
741       if (unlikely (fd >= acc.fdCount))
742       	return false;
743       if (unlikely (!encode_str (parsed_charstrings[i], fd, buffArray[i])))
744 	return false;
745     }
746     return true;
747   }
748 
encode_subrsCFF::SubrSubsetter749   bool encode_subrs (const ParsedCStrs &subrs, const SubrRemap& remap, unsigned int fd, StrBuffArray &buffArray) const
750   {
751     unsigned int  count = remap.get_count ();
752 
753     if (unlikely (!buffArray.resize (count)))
754       return false;
755     for (unsigned int old_num = 0; old_num < subrs.len; old_num++)
756     {
757       hb_codepoint_t new_num = remap[old_num];
758       if (new_num != CFF_UNDEF_CODE)
759       {
760 	if (unlikely (!encode_str (subrs[old_num], fd, buffArray[new_num])))
761 	  return false;
762       }
763     }
764     return true;
765   }
766 
encode_globalsubrsCFF::SubrSubsetter767   bool encode_globalsubrs (StrBuffArray &buffArray)
768   {
769     return encode_subrs (parsed_global_subrs, remaps.global_remap, 0, buffArray);
770   }
771 
encode_localsubrsCFF::SubrSubsetter772   bool encode_localsubrs (unsigned int fd, StrBuffArray &buffArray) const
773   {
774     return encode_subrs (parsed_local_subrs[fd], remaps.local_remaps[fd], fd, buffArray);
775   }
776 
777   protected:
778   struct DropHintsParam
779   {
DropHintsParamCFF::SubrSubsetter::DropHintsParam780     DropHintsParam ()
781       : seen_moveto (false),
782 	ends_in_hint (false),
783 	vsindex_dropped (false) {}
784 
785     bool  seen_moveto;
786     bool  ends_in_hint;
787     bool  vsindex_dropped;
788   };
789 
drop_hints_in_subrCFF::SubrSubsetter790   bool drop_hints_in_subr (ParsedCStr &str, unsigned int pos,
791 			   ParsedCStrs &subrs, unsigned int subr_num,
792 			   const SubrSubsetParam &param, DropHintsParam &drop)
793   {
794     drop.ends_in_hint = false;
795     bool has_hint = drop_hints_in_str (subrs[subr_num], param, drop);
796 
797     /* if this subr ends with a stem hint (i.e., not a number a potential argument for moveto),
798      * then this entire subroutine must be a hint. drop its call. */
799     if (drop.ends_in_hint)
800     {
801       str.values[pos].set_drop ();
802       /* if this subr call is at the end of the parent subr, propagate the flag
803        * otherwise reset the flag */
804       if (!str.at_end (pos))
805 	drop.ends_in_hint = false;
806     }
807 
808     return has_hint;
809   }
810 
811   /* returns true if it sees a hint op before the first moveto */
drop_hints_in_strCFF::SubrSubsetter812   bool drop_hints_in_str (ParsedCStr &str, const SubrSubsetParam &param, DropHintsParam &drop)
813   {
814     bool  seen_hint = false;
815 
816     for (unsigned int pos = 0; pos < str.values.len; pos++)
817     {
818       bool  has_hint = false;
819       switch (str.values[pos].op)
820       {
821 	case OpCode_callsubr:
822 	  has_hint = drop_hints_in_subr (str, pos,
823 					*param.parsed_local_subrs, str.values[pos].subr_num,
824 					param, drop);
825 
826 	  break;
827 
828 	case OpCode_callgsubr:
829 	  has_hint = drop_hints_in_subr (str, pos,
830 					*param.parsed_global_subrs, str.values[pos].subr_num,
831 					param, drop);
832 	  break;
833 
834 	case OpCode_rmoveto:
835 	case OpCode_hmoveto:
836 	case OpCode_vmoveto:
837 	  drop.seen_moveto = true;
838 	  break;
839 
840 	case OpCode_hintmask:
841 	case OpCode_cntrmask:
842 	  if (drop.seen_moveto)
843 	  {
844 	    str.values[pos].set_drop ();
845 	    break;
846 	  }
847 	  HB_FALLTHROUGH;
848 
849 	case OpCode_hstemhm:
850 	case OpCode_vstemhm:
851 	case OpCode_hstem:
852 	case OpCode_vstem:
853 	  has_hint = true;
854 	  str.values[pos].set_drop ();
855 	  if (str.at_end (pos))
856 	    drop.ends_in_hint = true;
857 	  break;
858 
859 	case OpCode_dotsection:
860 	  str.values[pos].set_drop ();
861 	  break;
862 
863 	default:
864 	  /* NONE */
865 	  break;
866       }
867       if (has_hint)
868       {
869 	for (int i = pos - 1; i >= 0; i--)
870 	{
871 	  ParsedCSOp  &csop = str.values[(unsigned)i];
872 	  if (csop.for_drop ())
873 	    break;
874 	  csop.set_drop ();
875 	  if (csop.op == OpCode_vsindexcs)
876 	    drop.vsindex_dropped = true;
877 	}
878 	seen_hint |= has_hint;
879       }
880     }
881 
882     return seen_hint;
883   }
884 
collect_subr_refs_in_subrCFF::SubrSubsetter885   void collect_subr_refs_in_subr (ParsedCStr &str, unsigned int pos,
886 				  unsigned int subr_num, ParsedCStrs &subrs,
887 				  hb_set_t *closure,
888 				  const SubrSubsetParam &param)
889   {
890     hb_set_add (closure, subr_num);
891     collect_subr_refs_in_str (subrs[subr_num], param);
892   }
893 
collect_subr_refs_in_strCFF::SubrSubsetter894   void collect_subr_refs_in_str (ParsedCStr &str, const SubrSubsetParam &param)
895   {
896     for (unsigned int pos = 0; pos < str.values.len; pos++)
897     {
898       if (!str.values[pos].for_drop ())
899       {
900 	switch (str.values[pos].op)
901 	{
902 	  case OpCode_callsubr:
903 	    collect_subr_refs_in_subr (str, pos,
904 				       str.values[pos].subr_num, *param.parsed_local_subrs,
905 				       param.local_closure, param);
906 	    break;
907 
908 	  case OpCode_callgsubr:
909 	    collect_subr_refs_in_subr (str, pos,
910 				       str.values[pos].subr_num, *param.parsed_global_subrs,
911 				       param.global_closure, param);
912 	    break;
913 
914 	  default: break;
915 	}
916       }
917     }
918   }
919 
encode_strCFF::SubrSubsetter920   bool encode_str (const ParsedCStr &str, const unsigned int fd, StrBuff &buff) const
921   {
922     buff.init ();
923     StrEncoder  encoder (buff);
924     encoder.reset ();
925     /* if a prefix (CFF1 width or CFF2 vsindex) has been removed along with hints,
926      * re-insert it at the beginning of charstreing */
927     if (str.has_prefix () && str.is_hint_dropped ())
928     {
929       encoder.encode_num (str.prefix_num ());
930       if (str.prefix_op () != OpCode_Invalid)
931 	encoder.encode_op (str.prefix_op ());
932     }
933     for (unsigned int i = 0; i < str.get_count(); i++)
934     {
935       const ParsedCSOp  &opstr = str.values[i];
936       if (!opstr.for_drop () && !opstr.for_skip ())
937       {
938 	switch (opstr.op)
939 	{
940 	  case OpCode_callsubr:
941 	    encoder.encode_int (remaps.local_remaps[fd].biased_num (opstr.subr_num));
942 	    encoder.encode_op (OpCode_callsubr);
943 	    break;
944 
945 	  case OpCode_callgsubr:
946 	    encoder.encode_int (remaps.global_remap.biased_num (opstr.subr_num));
947 	    encoder.encode_op (OpCode_callgsubr);
948 	    break;
949 
950 	  default:
951 	    encoder.copy_str (opstr.str);
952 	    break;
953 	}
954       }
955     }
956     return !encoder.is_error ();
957   }
958 
959   protected:
960   SubrClosures	      closures;
961 
962   ParsedCStrs	       parsed_charstrings;
963   ParsedCStrs	       parsed_global_subrs;
964   hb_vector_t<ParsedCStrs>  parsed_local_subrs;
965 
966   SubrRemaps		remaps;
967 
968   private:
969   typedef typename SUBRS::count_type subr_count_type;
970 };
971 };  /* namespace CFF */
972 
973 HB_INTERNAL bool
974 hb_plan_subset_cff_fdselect (const hb_vector_t<hb_codepoint_t> &glyphs,
975 			    unsigned int fdCount,
976 			    const CFF::FDSelect &src, /* IN */
977 			    unsigned int &subset_fd_count /* OUT */,
978 			    unsigned int &subset_fdselect_size /* OUT */,
979 			    unsigned int &subset_fdselect_format /* OUT */,
980 			    hb_vector_t<CFF::code_pair> &fdselect_ranges /* OUT */,
981 			    CFF::Remap &fdmap /* OUT */);
982 
983 HB_INTERNAL bool
984 hb_serialize_cff_fdselect (hb_serialize_context_t *c,
985 			  unsigned int num_glyphs,
986 			  const CFF::FDSelect &src,
987 			  unsigned int fd_count,
988 			  unsigned int fdselect_format,
989 			  unsigned int size,
990 			  const hb_vector_t<CFF::code_pair> &fdselect_ranges);
991 
992 #endif /* HB_SUBSET_CFF_COMMON_HH */
993