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 #include "hb.hh"
28 
29 #ifndef HB_NO_SUBSET_CFF
30 
31 #include "hb-open-type.hh"
32 #include "hb-ot-cff2-table.hh"
33 #include "hb-set.h"
34 #include "hb-subset-cff2.hh"
35 #include "hb-subset-plan.hh"
36 #include "hb-subset-cff-common.hh"
37 #include "hb-cff2-interp-cs.hh"
38 
39 using namespace CFF;
40 
41 struct cff2_sub_table_offsets_t : cff_sub_table_offsets_t
42 {
cff2_sub_table_offsets_tcff2_sub_table_offsets_t43   cff2_sub_table_offsets_t ()
44     : cff_sub_table_offsets_t (),
45       varStoreOffset (0)
46   {}
47 
48   unsigned int  varStoreOffset;
49 };
50 
51 struct cff2_top_dict_op_serializer_t : cff_top_dict_op_serializer_t<>
52 {
serializecff2_top_dict_op_serializer_t53   bool serialize (hb_serialize_context_t *c,
54 		  const op_str_t &opstr,
55 		  const cff2_sub_table_offsets_t &offsets) const
56   {
57     TRACE_SERIALIZE (this);
58 
59     switch (opstr.op)
60     {
61       case OpCode_vstore:
62 	return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.varStoreOffset));
63 
64       default:
65 	return_trace (cff_top_dict_op_serializer_t<>::serialize (c, opstr, offsets));
66     }
67   }
68 
calculate_serialized_sizecff2_top_dict_op_serializer_t69   unsigned int calculate_serialized_size (const op_str_t &opstr) const
70   {
71     switch (opstr.op)
72     {
73       case OpCode_vstore:
74 	return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (opstr.op);
75 
76       default:
77 	return cff_top_dict_op_serializer_t<>::calculate_serialized_size (opstr);
78     }
79   }
80 };
81 
82 struct cff2_cs_opset_flatten_t : cff2_cs_opset_t<cff2_cs_opset_flatten_t, flatten_param_t>
83 {
flush_args_and_opcff2_cs_opset_flatten_t84   static void flush_args_and_op (op_code_t op, cff2_cs_interp_env_t &env, flatten_param_t& param)
85   {
86     switch (op)
87     {
88       case OpCode_return:
89       case OpCode_endchar:
90 	/* dummy opcodes in CFF2. ignore */
91 	break;
92 
93       case OpCode_hstem:
94       case OpCode_hstemhm:
95       case OpCode_vstem:
96       case OpCode_vstemhm:
97       case OpCode_hintmask:
98       case OpCode_cntrmask:
99 	if (param.drop_hints)
100 	{
101 	  env.clear_args ();
102 	  return;
103 	}
104 	HB_FALLTHROUGH;
105 
106       default:
107 	SUPER::flush_args_and_op (op, env, param);
108 	break;
109     }
110   }
111 
flush_argscff2_cs_opset_flatten_t112   static void flush_args (cff2_cs_interp_env_t &env, flatten_param_t& param)
113   {
114     for (unsigned int i = 0; i < env.argStack.get_count ();)
115     {
116       const blend_arg_t &arg = env.argStack[i];
117       if (arg.blending ())
118       {
119       	if (unlikely (!((arg.numValues > 0) && (env.argStack.get_count () >= arg.numValues))))
120       	{
121 	  env.set_error ();
122 	  return;
123 	}
124 	flatten_blends (arg, i, env, param);
125 	i += arg.numValues;
126       }
127       else
128       {
129 	str_encoder_t  encoder (param.flatStr);
130 	encoder.encode_num (arg);
131 	i++;
132       }
133     }
134     SUPER::flush_args (env, param);
135   }
136 
flatten_blendscff2_cs_opset_flatten_t137   static void flatten_blends (const blend_arg_t &arg, unsigned int i, cff2_cs_interp_env_t &env, flatten_param_t& param)
138   {
139     /* flatten the default values */
140     str_encoder_t  encoder (param.flatStr);
141     for (unsigned int j = 0; j < arg.numValues; j++)
142     {
143       const blend_arg_t &arg1 = env.argStack[i + j];
144       if (unlikely (!((arg1.blending () && (arg.numValues == arg1.numValues) && (arg1.valueIndex == j) &&
145 	      (arg1.deltas.length == env.get_region_count ())))))
146       {
147       	env.set_error ();
148       	return;
149       }
150       encoder.encode_num (arg1);
151     }
152     /* flatten deltas for each value */
153     for (unsigned int j = 0; j < arg.numValues; j++)
154     {
155       const blend_arg_t &arg1 = env.argStack[i + j];
156       for (unsigned int k = 0; k < arg1.deltas.length; k++)
157 	encoder.encode_num (arg1.deltas[k]);
158     }
159     /* flatten the number of values followed by blend operator */
160     encoder.encode_int (arg.numValues);
161     encoder.encode_op (OpCode_blendcs);
162   }
163 
flush_opcff2_cs_opset_flatten_t164   static void flush_op (op_code_t op, cff2_cs_interp_env_t &env, flatten_param_t& param)
165   {
166     switch (op)
167     {
168       case OpCode_return:
169       case OpCode_endchar:
170 	return;
171       default:
172 	str_encoder_t  encoder (param.flatStr);
173 	encoder.encode_op (op);
174     }
175   }
176 
177   private:
178   typedef cff2_cs_opset_t<cff2_cs_opset_flatten_t, flatten_param_t> SUPER;
179   typedef cs_opset_t<blend_arg_t, cff2_cs_opset_flatten_t, cff2_cs_opset_flatten_t, cff2_cs_interp_env_t, flatten_param_t> CSOPSET;
180 };
181 
182 struct cff2_cs_opset_subr_subset_t : cff2_cs_opset_t<cff2_cs_opset_subr_subset_t, subr_subset_param_t>
183 {
process_opcff2_cs_opset_subr_subset_t184   static void process_op (op_code_t op, cff2_cs_interp_env_t &env, subr_subset_param_t& param)
185   {
186     switch (op) {
187 
188       case OpCode_return:
189 	param.current_parsed_str->set_parsed ();
190 	env.return_from_subr ();
191 	param.set_current_str (env, false);
192 	break;
193 
194       case OpCode_endchar:
195 	param.current_parsed_str->set_parsed ();
196 	SUPER::process_op (op, env, param);
197 	break;
198 
199       case OpCode_callsubr:
200 	process_call_subr (op, CSType_LocalSubr, env, param, env.localSubrs, param.local_closure);
201 	break;
202 
203       case OpCode_callgsubr:
204 	process_call_subr (op, CSType_GlobalSubr, env, param, env.globalSubrs, param.global_closure);
205 	break;
206 
207       default:
208 	SUPER::process_op (op, env, param);
209 	param.current_parsed_str->add_op (op, env.str_ref);
210 	break;
211     }
212   }
213 
214   protected:
process_call_subrcff2_cs_opset_subr_subset_t215   static void process_call_subr (op_code_t op, cs_type_t type,
216 				 cff2_cs_interp_env_t &env, subr_subset_param_t& param,
217 				 cff2_biased_subrs_t& subrs, hb_set_t *closure)
218   {
219     byte_str_ref_t    str_ref = env.str_ref;
220     env.call_subr (subrs, type);
221     param.current_parsed_str->add_call_op (op, str_ref, env.context.subr_num);
222     closure->add (env.context.subr_num);
223     param.set_current_str (env, true);
224   }
225 
226   private:
227   typedef cff2_cs_opset_t<cff2_cs_opset_subr_subset_t, subr_subset_param_t> SUPER;
228 };
229 
230 struct cff2_subr_subsetter_t : subr_subsetter_t<cff2_subr_subsetter_t, CFF2Subrs, const OT::cff2::accelerator_subset_t, cff2_cs_interp_env_t, cff2_cs_opset_subr_subset_t>
231 {
cff2_subr_subsetter_tcff2_subr_subsetter_t232   cff2_subr_subsetter_t (const OT::cff2::accelerator_subset_t &acc_, const hb_subset_plan_t *plan_)
233     : subr_subsetter_t (acc_, plan_) {}
234 
finalize_parsed_strcff2_subr_subsetter_t235   static void finalize_parsed_str (cff2_cs_interp_env_t &env, subr_subset_param_t& param, parsed_cs_str_t &charstring)
236   {
237     /* vsindex is inserted at the beginning of the charstring as necessary */
238     if (env.seen_vsindex ())
239     {
240       number_t  ivs;
241       ivs.set_int ((int)env.get_ivs ());
242       charstring.set_prefix (ivs, OpCode_vsindexcs);
243     }
244   }
245 };
246 
247 struct cff2_subset_plan {
cff2_subset_plancff2_subset_plan248   cff2_subset_plan ()
249     : final_size (0),
250       orig_fdcount (0),
251       subset_fdcount(1),
252       subset_fdselect_format (0),
253       drop_hints (false),
254       desubroutinize (false)
255   {
256     subset_fdselect_ranges.init ();
257     fdmap.init ();
258     subset_charstrings.init ();
259     subset_globalsubrs.init ();
260     subset_localsubrs.init ();
261     privateDictInfos.init ();
262   }
263 
~cff2_subset_plancff2_subset_plan264   ~cff2_subset_plan ()
265   {
266     subset_fdselect_ranges.fini ();
267     fdmap.fini ();
268     subset_charstrings.fini_deep ();
269     subset_globalsubrs.fini_deep ();
270     subset_localsubrs.fini_deep ();
271     privateDictInfos.fini ();
272   }
273 
createcff2_subset_plan274   bool create (const OT::cff2::accelerator_subset_t &acc,
275 	      hb_subset_plan_t *plan)
276   {
277     final_size = 0;
278     orig_fdcount = acc.fdArray->count;
279 
280     drop_hints = plan->drop_hints;
281     desubroutinize = plan->desubroutinize;
282 
283     /* CFF2 header */
284     final_size += OT::cff2::static_size;
285 
286     /* top dict */
287     {
288       cff2_top_dict_op_serializer_t topSzr;
289       offsets.topDictInfo.size = TopDict::calculate_serialized_size (acc.topDict, topSzr);
290       final_size += offsets.topDictInfo.size;
291     }
292 
293     if (desubroutinize)
294     {
295       /* Flatten global & local subrs */
296       subr_flattener_t<const OT::cff2::accelerator_subset_t, cff2_cs_interp_env_t, cff2_cs_opset_flatten_t>
297 		    flattener(acc, plan);
298       if (!flattener.flatten (subset_charstrings))
299 	return false;
300 
301       /* no global/local subroutines */
302       offsets.globalSubrsInfo.size = CFF2Subrs::calculate_serialized_size (1, 0, 0);
303     }
304     else
305     {
306       cff2_subr_subsetter_t	subr_subsetter (acc, plan);
307 
308       /* Subset subrs: collect used subroutines, leaving all unused ones behind */
309       if (!subr_subsetter.subset ())
310 	return false;
311 
312       /* encode charstrings, global subrs, local subrs with new subroutine numbers */
313       if (!subr_subsetter.encode_charstrings (subset_charstrings))
314 	return false;
315 
316       if (!subr_subsetter.encode_globalsubrs (subset_globalsubrs))
317 	return false;
318 
319       /* global subrs */
320       unsigned int dataSize = subset_globalsubrs.total_size ();
321       offsets.globalSubrsInfo.offSize = calcOffSize (dataSize);
322       offsets.globalSubrsInfo.size = CFF2Subrs::calculate_serialized_size (offsets.globalSubrsInfo.offSize, subset_globalsubrs.length, dataSize);
323 
324       /* local subrs */
325       if (!offsets.localSubrsInfos.resize (orig_fdcount))
326 	return false;
327       if (!subset_localsubrs.resize (orig_fdcount))
328 	return false;
329       for (unsigned int fd = 0; fd < orig_fdcount; fd++)
330       {
331 	subset_localsubrs[fd].init ();
332 	offsets.localSubrsInfos[fd].init ();
333 	if (!subr_subsetter.encode_localsubrs (fd, subset_localsubrs[fd]))
334 	  return false;
335 
336 	unsigned int dataSize = subset_localsubrs[fd].total_size ();
337 	if (dataSize > 0)
338 	{
339 	  offsets.localSubrsInfos[fd].offset = final_size;
340 	  offsets.localSubrsInfos[fd].offSize = calcOffSize (dataSize);
341 	  offsets.localSubrsInfos[fd].size = CFF2Subrs::calculate_serialized_size (offsets.localSubrsInfos[fd].offSize, subset_localsubrs[fd].length, dataSize);
342 	}
343       }
344     }
345 
346     /* global subrs */
347     offsets.globalSubrsInfo.offset = final_size;
348     final_size += offsets.globalSubrsInfo.size;
349 
350     /* variation store */
351     if (acc.varStore != &Null(CFF2VariationStore))
352     {
353       offsets.varStoreOffset = final_size;
354       final_size += acc.varStore->get_size ();
355     }
356 
357     /* FDSelect */
358     if (acc.fdSelect != &Null(CFF2FDSelect))
359     {
360       offsets.FDSelectInfo.offset = final_size;
361       if (unlikely (!hb_plan_subset_cff_fdselect (plan,
362 				  orig_fdcount,
363 				  *(const FDSelect *)acc.fdSelect,
364 				  subset_fdcount,
365 				  offsets.FDSelectInfo.size,
366 				  subset_fdselect_format,
367 				  subset_fdselect_ranges,
368 				  fdmap)))
369 	return false;
370 
371       final_size += offsets.FDSelectInfo.size;
372     }
373     else
374       fdmap.identity (1);
375 
376     /* FDArray (FDIndex) */
377     {
378       offsets.FDArrayInfo.offset = final_size;
379       cff_font_dict_op_serializer_t fontSzr;
380       unsigned int dictsSize = 0;
381       for (unsigned int i = 0; i < acc.fontDicts.length; i++)
382 	if (fdmap.has (i))
383 	  dictsSize += FontDict::calculate_serialized_size (acc.fontDicts[i], fontSzr);
384 
385       offsets.FDArrayInfo.offSize = calcOffSize (dictsSize);
386       final_size += CFF2Index::calculate_serialized_size (offsets.FDArrayInfo.offSize, subset_fdcount, dictsSize);
387     }
388 
389     /* CharStrings */
390     {
391       offsets.charStringsInfo.offset = final_size;
392       unsigned int dataSize = subset_charstrings.total_size ();
393       offsets.charStringsInfo.offSize = calcOffSize (dataSize);
394       final_size += CFF2CharStrings::calculate_serialized_size (offsets.charStringsInfo.offSize, plan->num_output_glyphs (), dataSize);
395     }
396 
397     /* private dicts & local subrs */
398     offsets.privateDictsOffset = final_size;
399     for (unsigned int i = 0; i < orig_fdcount; i++)
400     {
401       if (fdmap.has (i))
402       {
403 	bool  has_localsubrs = offsets.localSubrsInfos[i].size > 0;
404 	cff_private_dict_op_serializer_t privSzr (desubroutinize, drop_hints);
405 	unsigned int  priv_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr, has_localsubrs);
406 	table_info_t  privInfo = { final_size, priv_size, 0 };
407 	privateDictInfos.push (privInfo);
408 	final_size += privInfo.size;
409 
410 	if (!plan->desubroutinize && has_localsubrs)
411 	{
412 	  offsets.localSubrsInfos[i].offset = final_size;
413 	  final_size += offsets.localSubrsInfos[i].size;
414 	}
415       }
416     }
417 
418     return true;
419   }
420 
get_final_sizecff2_subset_plan421   unsigned int get_final_size () const  { return final_size; }
422 
423   unsigned int	final_size;
424   cff2_sub_table_offsets_t offsets;
425 
426   unsigned int    orig_fdcount;
427   unsigned int    subset_fdcount;
428   unsigned int    subset_fdselect_format;
429   hb_vector_t<code_pair_t>   subset_fdselect_ranges;
430 
431   hb_inc_bimap_t   fdmap;
432 
433   str_buff_vec_t	    subset_charstrings;
434   str_buff_vec_t	    subset_globalsubrs;
435   hb_vector_t<str_buff_vec_t> subset_localsubrs;
436   hb_vector_t<table_info_t>  privateDictInfos;
437 
438   bool	    drop_hints;
439   bool	    desubroutinize;
440 };
441 
_write_cff2(const cff2_subset_plan & plan,const OT::cff2::accelerator_subset_t & acc,unsigned int num_glyphs,unsigned int dest_sz,void * dest)442 static inline bool _write_cff2 (const cff2_subset_plan &plan,
443 				const OT::cff2::accelerator_subset_t  &acc,
444 				unsigned int num_glyphs,
445 				unsigned int dest_sz,
446 				void *dest)
447 {
448   hb_serialize_context_t c (dest, dest_sz);
449 
450   OT::cff2 *cff2 = c.start_serialize<OT::cff2> ();
451   if (unlikely (!c.extend_min (*cff2)))
452     return false;
453 
454   /* header */
455   cff2->version.major = 0x02;
456   cff2->version.minor = 0x00;
457   cff2->topDict = OT::cff2::static_size;
458 
459   /* top dict */
460   {
461     assert (cff2->topDict == (unsigned) (c.head - c.start));
462     cff2->topDictSize = plan.offsets.topDictInfo.size;
463     TopDict &dict = cff2 + cff2->topDict;
464     cff2_top_dict_op_serializer_t topSzr;
465     if (unlikely (!dict.serialize (&c, acc.topDict, topSzr, plan.offsets)))
466     {
467       DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 top dict");
468       return false;
469     }
470   }
471 
472   /* global subrs */
473   {
474     assert (cff2->topDict + plan.offsets.topDictInfo.size == (unsigned) (c.head - c.start));
475     CFF2Subrs *dest = c.start_embed <CFF2Subrs> ();
476     if (unlikely (dest == nullptr)) return false;
477     if (unlikely (!dest->serialize (&c, plan.offsets.globalSubrsInfo.offSize, plan.subset_globalsubrs)))
478     {
479       DEBUG_MSG (SUBSET, nullptr, "failed to serialize global subroutines");
480       return false;
481     }
482   }
483 
484   /* variation store */
485   if (acc.varStore != &Null(CFF2VariationStore))
486   {
487     assert (plan.offsets.varStoreOffset == (unsigned) (c.head - c.start));
488     CFF2VariationStore *dest = c.start_embed<CFF2VariationStore> ();
489     if (unlikely (!dest->serialize (&c, acc.varStore)))
490     {
491       DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 Variation Store");
492       return false;
493     }
494   }
495 
496   /* FDSelect */
497   if (acc.fdSelect != &Null(CFF2FDSelect))
498   {
499     assert (plan.offsets.FDSelectInfo.offset == (unsigned) (c.head - c.start));
500 
501     if (unlikely (!hb_serialize_cff_fdselect (&c, num_glyphs, *(const FDSelect *)acc.fdSelect, acc.fdArray->count,
502 					      plan.subset_fdselect_format, plan.offsets.FDSelectInfo.size,
503 					      plan.subset_fdselect_ranges)))
504     {
505       DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 subset FDSelect");
506       return false;
507     }
508   }
509 
510   /* FDArray (FD Index) */
511   {
512     assert (plan.offsets.FDArrayInfo.offset == (unsigned) (c.head - c.start));
513     CFF2FDArray  *fda = c.start_embed<CFF2FDArray> ();
514     if (unlikely (fda == nullptr)) return false;
515     cff_font_dict_op_serializer_t  fontSzr;
516     if (unlikely (!fda->serialize (&c, plan.offsets.FDArrayInfo.offSize,
517 				   acc.fontDicts, plan.subset_fdcount, plan.fdmap,
518 				   fontSzr, plan.privateDictInfos)))
519     {
520       DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 FDArray");
521       return false;
522     }
523   }
524 
525   /* CharStrings */
526   {
527     assert (plan.offsets.charStringsInfo.offset == (unsigned) (c.head - c.start));
528     CFF2CharStrings  *cs = c.start_embed<CFF2CharStrings> ();
529     if (unlikely (cs == nullptr)) return false;
530     if (unlikely (!cs->serialize (&c, plan.offsets.charStringsInfo.offSize, plan.subset_charstrings)))
531     {
532       DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 CharStrings");
533       return false;
534     }
535   }
536 
537   /* private dicts & local subrs */
538   assert (plan.offsets.privateDictsOffset == (unsigned) (c.head - c.start));
539   for (unsigned int i = 0; i < acc.privateDicts.length; i++)
540   {
541     if (plan.fdmap.has (i))
542     {
543       PrivateDict  *pd = c.start_embed<PrivateDict> ();
544       if (unlikely (pd == nullptr)) return false;
545       unsigned int priv_size = plan.privateDictInfos[plan.fdmap[i]].size;
546       bool result;
547       cff_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints);
548       /* N.B. local subrs immediately follows its corresponding private dict. i.e., subr offset == private dict size */
549       unsigned int subroffset = (plan.offsets.localSubrsInfos[i].size > 0) ? priv_size : 0;
550       result = pd->serialize (&c, acc.privateDicts[i], privSzr, subroffset);
551       if (unlikely (!result))
552       {
553 	DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF Private Dict[%d]", i);
554 	return false;
555       }
556       if (plan.offsets.localSubrsInfos[i].size > 0)
557       {
558 	CFF2Subrs *dest = c.start_embed <CFF2Subrs> ();
559 	if (unlikely (dest == nullptr)) return false;
560 	if (unlikely (!dest->serialize (&c, plan.offsets.localSubrsInfos[i].offSize, plan.subset_localsubrs[i])))
561 	{
562 	  DEBUG_MSG (SUBSET, nullptr, "failed to serialize local subroutines");
563 	  return false;
564 	}
565       }
566     }
567   }
568 
569   assert (c.head == c.end);
570   c.end_serialize ();
571 
572   return true;
573 }
574 
575 static inline bool
_hb_subset_cff2(const OT::cff2::accelerator_subset_t & acc,const char * data,hb_subset_plan_t * plan,hb_blob_t ** prime)576 _hb_subset_cff2 (const OT::cff2::accelerator_subset_t  &acc,
577 		const char		      *data,
578 		hb_subset_plan_t		*plan,
579 		hb_blob_t		       **prime /* OUT */)
580 {
581   cff2_subset_plan cff2_plan;
582 
583   if (unlikely (!cff2_plan.create (acc, plan)))
584   {
585     DEBUG_MSG(SUBSET, nullptr, "Failed to generate a cff2 subsetting plan.");
586     return false;
587   }
588 
589   unsigned int  cff2_prime_size = cff2_plan.get_final_size ();
590   char *cff2_prime_data = (char *) calloc (1, cff2_prime_size);
591 
592   if (unlikely (!_write_cff2 (cff2_plan, acc, plan->num_output_glyphs (),
593 			      cff2_prime_size, cff2_prime_data))) {
594     DEBUG_MSG(SUBSET, nullptr, "Failed to write a subset cff2.");
595     free (cff2_prime_data);
596     return false;
597   }
598 
599   *prime = hb_blob_create (cff2_prime_data,
600 			   cff2_prime_size,
601 			   HB_MEMORY_MODE_READONLY,
602 			   cff2_prime_data,
603 			   free);
604   return true;
605 }
606 
607 /**
608  * hb_subset_cff2:
609  * Subsets the CFF2 table according to a provided plan.
610  *
611  * Return value: subsetted cff2 table.
612  **/
613 bool
hb_subset_cff2(hb_subset_plan_t * plan,hb_blob_t ** prime)614 hb_subset_cff2 (hb_subset_plan_t *plan,
615 		hb_blob_t       **prime /* OUT */)
616 {
617   hb_blob_t *cff2_blob = hb_sanitize_context_t().reference_table<CFF::cff2> (plan->source);
618   const char *data = hb_blob_get_data(cff2_blob, nullptr);
619 
620   OT::cff2::accelerator_subset_t acc;
621   acc.init(plan->source);
622   bool result = likely (acc.is_valid ()) &&
623 		_hb_subset_cff2 (acc, data, plan, prime);
624 
625   hb_blob_destroy (cff2_blob);
626   acc.fini ();
627 
628   return result;
629 }
630 
631 
632 #endif
633