• 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  #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