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