xref: /aosp_15_r20/external/harfbuzz_ng/src/hb-subset-cff2.cc (revision 2d1272b857b1f7575e6e246373e1cb218663db8a)
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-plan.hh"
35 #include "hb-subset-cff-common.hh"
36 #include "hb-cff2-interp-cs.hh"
37 
38 using namespace CFF;
39 
40 struct cff2_sub_table_info_t : cff_sub_table_info_t
41 {
cff2_sub_table_info_tcff2_sub_table_info_t42   cff2_sub_table_info_t ()
43     : cff_sub_table_info_t (),
44       var_store_link (0)
45   {}
46 
47   objidx_t  var_store_link;
48 };
49 
50 struct cff2_top_dict_op_serializer_t : cff_top_dict_op_serializer_t<>
51 {
serializecff2_top_dict_op_serializer_t52   bool serialize (hb_serialize_context_t *c,
53 		  const op_str_t &opstr,
54 		  const cff2_sub_table_info_t &info) const
55   {
56     TRACE_SERIALIZE (this);
57 
58     switch (opstr.op)
59     {
60       case OpCode_vstore:
61         if (info.var_store_link)
62 	  return_trace (FontDict::serialize_link4_op(c, opstr.op, info.var_store_link));
63 	else
64 	  return_trace (true);
65 
66       default:
67 	return_trace (cff_top_dict_op_serializer_t<>::serialize (c, opstr, info));
68     }
69   }
70 };
71 
72 struct cff2_cs_opset_flatten_t : cff2_cs_opset_t<cff2_cs_opset_flatten_t, flatten_param_t, blend_arg_t>
73 {
flush_args_and_opcff2_cs_opset_flatten_t74   static void flush_args_and_op (op_code_t op, cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param)
75   {
76     switch (op)
77     {
78       case OpCode_return:
79       case OpCode_endchar:
80 	/* dummy opcodes in CFF2. ignore */
81 	break;
82 
83       case OpCode_hstem:
84       case OpCode_hstemhm:
85       case OpCode_vstem:
86       case OpCode_vstemhm:
87       case OpCode_hintmask:
88       case OpCode_cntrmask:
89 	if (param.drop_hints)
90 	{
91 	  env.clear_args ();
92 	  return;
93 	}
94 	HB_FALLTHROUGH;
95 
96       default:
97 	SUPER::flush_args_and_op (op, env, param);
98 	break;
99     }
100   }
101 
flush_argscff2_cs_opset_flatten_t102   static void flush_args (cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param)
103   {
104     for (unsigned int i = 0; i < env.argStack.get_count ();)
105     {
106       const blend_arg_t &arg = env.argStack[i];
107       if (arg.blending ())
108       {
109 	if (unlikely (!((arg.numValues > 0) && (env.argStack.get_count () >= arg.numValues))))
110 	{
111 	  env.set_error ();
112 	  return;
113 	}
114 	flatten_blends (arg, i, env, param);
115 	i += arg.numValues;
116       }
117       else
118       {
119 	str_encoder_t  encoder (param.flatStr);
120 	encoder.encode_num_cs (arg);
121 	i++;
122       }
123     }
124     SUPER::flush_args (env, param);
125   }
126 
flatten_blendscff2_cs_opset_flatten_t127   static void flatten_blends (const blend_arg_t &arg, unsigned int i, cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param)
128   {
129     /* flatten the default values */
130     str_encoder_t  encoder (param.flatStr);
131     for (unsigned int j = 0; j < arg.numValues; j++)
132     {
133       const blend_arg_t &arg1 = env.argStack[i + j];
134       if (unlikely (!((arg1.blending () && (arg.numValues == arg1.numValues) && (arg1.valueIndex == j) &&
135 	      (arg1.deltas.length == env.get_region_count ())))))
136       {
137 	env.set_error ();
138 	return;
139       }
140       encoder.encode_num_cs (arg1);
141     }
142     /* flatten deltas for each value */
143     for (unsigned int j = 0; j < arg.numValues; j++)
144     {
145       const blend_arg_t &arg1 = env.argStack[i + j];
146       for (unsigned int k = 0; k < arg1.deltas.length; k++)
147 	encoder.encode_num_cs (arg1.deltas[k]);
148     }
149     /* flatten the number of values followed by blend operator */
150     encoder.encode_int (arg.numValues);
151     encoder.encode_op (OpCode_blendcs);
152   }
153 
flush_opcff2_cs_opset_flatten_t154   static void flush_op (op_code_t op, cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param)
155   {
156     switch (op)
157     {
158       case OpCode_return:
159       case OpCode_endchar:
160 	return;
161       default:
162 	str_encoder_t  encoder (param.flatStr);
163 	encoder.encode_op (op);
164     }
165   }
166 
flush_hintmaskcff2_cs_opset_flatten_t167   static void flush_hintmask (op_code_t op, cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param)
168   {
169     SUPER::flush_hintmask (op, env, param);
170     if (!param.drop_hints)
171     {
172       str_encoder_t  encoder (param.flatStr);
173       for (unsigned int i = 0; i < env.hintmask_size; i++)
174 	encoder.encode_byte (env.str_ref[i]);
175     }
176   }
177 
178   private:
179   typedef cff2_cs_opset_t<cff2_cs_opset_flatten_t, flatten_param_t, blend_arg_t> SUPER;
180   typedef cs_opset_t<blend_arg_t, cff2_cs_opset_flatten_t, cff2_cs_opset_flatten_t, cff2_cs_interp_env_t<blend_arg_t>, flatten_param_t> CSOPSET;
181 };
182 
183 struct cff2_cs_opset_subr_subset_t : cff2_cs_opset_t<cff2_cs_opset_subr_subset_t, subr_subset_param_t, blend_arg_t>
184 {
process_opcff2_cs_opset_subr_subset_t185   static void process_op (op_code_t op, cff2_cs_interp_env_t<blend_arg_t> &env, subr_subset_param_t& param)
186   {
187     switch (op) {
188 
189       case OpCode_return:
190 	param.current_parsed_str->set_parsed ();
191 	env.return_from_subr ();
192 	param.set_current_str (env, false);
193 	break;
194 
195       case OpCode_endchar:
196 	param.current_parsed_str->set_parsed ();
197 	SUPER::process_op (op, env, param);
198 	break;
199 
200       case OpCode_callsubr:
201 	process_call_subr (op, CSType_LocalSubr, env, param, env.localSubrs, param.local_closure);
202 	break;
203 
204       case OpCode_callgsubr:
205 	process_call_subr (op, CSType_GlobalSubr, env, param, env.globalSubrs, param.global_closure);
206 	break;
207 
208       default:
209 	SUPER::process_op (op, env, param);
210 	param.current_parsed_str->add_op (op, env.str_ref);
211 	break;
212     }
213   }
214 
215   protected:
process_call_subrcff2_cs_opset_subr_subset_t216   static void process_call_subr (op_code_t op, cs_type_t type,
217 				 cff2_cs_interp_env_t<blend_arg_t> &env, subr_subset_param_t& param,
218 				 cff2_biased_subrs_t& subrs, hb_set_t *closure)
219   {
220     byte_str_ref_t    str_ref = env.str_ref;
221     env.call_subr (subrs, type);
222     param.current_parsed_str->add_call_op (op, str_ref, env.context.subr_num);
223     closure->add (env.context.subr_num);
224     param.set_current_str (env, true);
225   }
226 
227   private:
228   typedef cff2_cs_opset_t<cff2_cs_opset_subr_subset_t, subr_subset_param_t, blend_arg_t> SUPER;
229 };
230 
231 struct cff2_subr_subsetter_t : subr_subsetter_t<cff2_subr_subsetter_t, CFF2Subrs, const OT::cff2::accelerator_subset_t, cff2_cs_interp_env_t<blend_arg_t>, cff2_cs_opset_subr_subset_t>
232 {
cff2_subr_subsetter_tcff2_subr_subsetter_t233   cff2_subr_subsetter_t (const OT::cff2::accelerator_subset_t &acc_, const hb_subset_plan_t *plan_)
234     : subr_subsetter_t (acc_, plan_) {}
235 
complete_parsed_strcff2_subr_subsetter_t236   static void complete_parsed_str (cff2_cs_interp_env_t<blend_arg_t> &env, subr_subset_param_t& param, parsed_cs_str_t &charstring)
237   {
238     /* vsindex is inserted at the beginning of the charstring as necessary */
239     if (env.seen_vsindex ())
240     {
241       number_t  ivs;
242       ivs.set_int ((int)env.get_ivs ());
243       charstring.set_prefix (ivs, OpCode_vsindexcs);
244     }
245   }
246 };
247 
248 struct cff2_private_blend_encoder_param_t
249 {
cff2_private_blend_encoder_param_tcff2_private_blend_encoder_param_t250   cff2_private_blend_encoder_param_t (hb_serialize_context_t *c,
251 				      const CFF2ItemVariationStore *varStore,
252 				      hb_array_t<int> normalized_coords) :
253     c (c), varStore (varStore), normalized_coords (normalized_coords) {}
254 
initcff2_private_blend_encoder_param_t255   void init () {}
256 
process_blendcff2_private_blend_encoder_param_t257   void process_blend ()
258   {
259     if (!seen_blend)
260     {
261       region_count = varStore->varStore.get_region_index_count (ivs);
262       scalars.resize_exact (region_count);
263       varStore->varStore.get_region_scalars (ivs, normalized_coords.arrayZ, normalized_coords.length,
264 					     &scalars[0], region_count);
265       seen_blend = true;
266     }
267   }
268 
blend_deltascff2_private_blend_encoder_param_t269   double blend_deltas (hb_array_t<const number_t> deltas) const
270   {
271     double v = 0;
272     if (likely (scalars.length == deltas.length))
273     {
274       unsigned count = scalars.length;
275       for (unsigned i = 0; i < count; i++)
276 	v += (double) scalars.arrayZ[i] * deltas.arrayZ[i].to_real ();
277     }
278     return v;
279   }
280 
281 
282   hb_serialize_context_t *c = nullptr;
283   bool seen_blend = false;
284   unsigned ivs = 0;
285   unsigned region_count = 0;
286   hb_vector_t<float> scalars;
287   const	 CFF2ItemVariationStore *varStore = nullptr;
288   hb_array_t<int> normalized_coords;
289 };
290 
291 struct cff2_private_dict_blend_opset_t : dict_opset_t
292 {
process_arg_blendcff2_private_dict_blend_opset_t293   static void process_arg_blend (cff2_private_blend_encoder_param_t& param,
294 				 number_t &arg,
295 				 const hb_array_t<const number_t> blends,
296 				 unsigned n, unsigned i)
297   {
298     arg.set_int (round (arg.to_real () + param.blend_deltas (blends)));
299   }
300 
process_blendcff2_private_dict_blend_opset_t301   static void process_blend (cff2_priv_dict_interp_env_t& env, cff2_private_blend_encoder_param_t& param)
302   {
303     unsigned int n, k;
304 
305     param.process_blend ();
306     k = param.region_count;
307     n = env.argStack.pop_uint ();
308     /* copy the blend values into blend array of the default values */
309     unsigned int start = env.argStack.get_count () - ((k+1) * n);
310     /* let an obvious error case fail, but note CFF2 spec doesn't forbid n==0 */
311     if (unlikely (start > env.argStack.get_count ()))
312     {
313       env.set_error ();
314       return;
315     }
316     for (unsigned int i = 0; i < n; i++)
317     {
318       const hb_array_t<const number_t> blends = env.argStack.sub_array (start + n + (i * k), k);
319       process_arg_blend (param, env.argStack[start + i], blends, n, i);
320     }
321 
322     /* pop off blend values leaving default values now adorned with blend values */
323     env.argStack.pop (k * n);
324   }
325 
process_opcff2_private_dict_blend_opset_t326   static void process_op (op_code_t op, cff2_priv_dict_interp_env_t& env, cff2_private_blend_encoder_param_t& param)
327   {
328     switch (op) {
329       case OpCode_StdHW:
330       case OpCode_StdVW:
331       case OpCode_BlueScale:
332       case OpCode_BlueShift:
333       case OpCode_BlueFuzz:
334       case OpCode_ExpansionFactor:
335       case OpCode_LanguageGroup:
336       case OpCode_BlueValues:
337       case OpCode_OtherBlues:
338       case OpCode_FamilyBlues:
339       case OpCode_FamilyOtherBlues:
340       case OpCode_StemSnapH:
341       case OpCode_StemSnapV:
342 	break;
343       case OpCode_vsindexdict:
344 	env.process_vsindex ();
345 	param.ivs = env.get_ivs ();
346 	env.clear_args ();
347 	return;
348       case OpCode_blenddict:
349 	process_blend (env, param);
350 	return;
351 
352       default:
353 	dict_opset_t::process_op (op, env);
354 	if (!env.argStack.is_empty ()) return;
355 	break;
356     }
357 
358     if (unlikely (env.in_error ())) return;
359 
360     // Write args then op
361 
362     str_buff_t str;
363     str_encoder_t encoder (str);
364 
365     unsigned count = env.argStack.get_count ();
366     for (unsigned i = 0; i < count; i++)
367       encoder.encode_num_tp (env.argStack[i]);
368 
369     encoder.encode_op (op);
370 
371     auto bytes = str.as_bytes ();
372     param.c->embed (&bytes, bytes.length);
373 
374     env.clear_args ();
375   }
376 };
377 
378 struct cff2_private_dict_op_serializer_t : op_serializer_t
379 {
cff2_private_dict_op_serializer_tcff2_private_dict_op_serializer_t380   cff2_private_dict_op_serializer_t (bool desubroutinize_, bool drop_hints_, bool pinned_,
381 				     const CFF::CFF2ItemVariationStore* varStore_,
382 				     hb_array_t<int> normalized_coords_)
383     : desubroutinize (desubroutinize_), drop_hints (drop_hints_), pinned (pinned_),
384       varStore (varStore_), normalized_coords (normalized_coords_) {}
385 
serializecff2_private_dict_op_serializer_t386   bool serialize (hb_serialize_context_t *c,
387 		  const op_str_t &opstr,
388 		  objidx_t subrs_link) const
389   {
390     TRACE_SERIALIZE (this);
391 
392     if (drop_hints && dict_opset_t::is_hint_op (opstr.op))
393       return_trace (true);
394 
395     if (opstr.op == OpCode_Subrs)
396     {
397       if (desubroutinize || !subrs_link)
398 	return_trace (true);
399       else
400 	return_trace (FontDict::serialize_link2_op (c, opstr.op, subrs_link));
401     }
402 
403     if (pinned)
404     {
405       // Reinterpret opstr and process blends.
406       cff2_priv_dict_interp_env_t env {hb_ubytes_t (opstr.ptr, opstr.length)};
407       cff2_private_blend_encoder_param_t param (c, varStore, normalized_coords);
408       dict_interpreter_t<cff2_private_dict_blend_opset_t, cff2_private_blend_encoder_param_t, cff2_priv_dict_interp_env_t> interp (env);
409       return_trace (interp.interpret (param));
410     }
411 
412     return_trace (copy_opstr (c, opstr));
413   }
414 
415   protected:
416   const bool desubroutinize;
417   const bool drop_hints;
418   const bool pinned;
419   const CFF::CFF2ItemVariationStore* varStore;
420   hb_array_t<int> normalized_coords;
421 };
422 
423 
424 namespace OT {
425 struct cff2_subset_plan
426 {
createOT::cff2_subset_plan427   bool create (const OT::cff2::accelerator_subset_t &acc,
428 	      hb_subset_plan_t *plan)
429   {
430     /* make sure notdef is first */
431     hb_codepoint_t old_glyph;
432     if (!plan->old_gid_for_new_gid (0, &old_glyph) || (old_glyph != 0)) return false;
433 
434     num_glyphs = plan->num_output_glyphs ();
435     orig_fdcount = acc.fdArray->count;
436 
437     drop_hints = plan->flags & HB_SUBSET_FLAGS_NO_HINTING;
438     pinned = (bool) plan->normalized_coords;
439     desubroutinize = plan->flags & HB_SUBSET_FLAGS_DESUBROUTINIZE ||
440 		     pinned; // For instancing we need this path
441 
442  #ifdef HB_EXPERIMENTAL_API
443     min_charstrings_off_size = (plan->flags & HB_SUBSET_FLAGS_IFTB_REQUIREMENTS) ? 4 : 0;
444  #else
445     min_charstrings_off_size = 0;
446  #endif
447 
448     if (desubroutinize)
449     {
450       /* Flatten global & local subrs */
451       subr_flattener_t<const OT::cff2::accelerator_subset_t, cff2_cs_interp_env_t<blend_arg_t>, cff2_cs_opset_flatten_t>
452 		    flattener(acc, plan);
453       if (!flattener.flatten (subset_charstrings))
454 	return false;
455     }
456     else
457     {
458       cff2_subr_subsetter_t	subr_subsetter (acc, plan);
459 
460       /* Subset subrs: collect used subroutines, leaving all unused ones behind */
461       if (!subr_subsetter.subset ())
462 	return false;
463 
464       /* encode charstrings, global subrs, local subrs with new subroutine numbers */
465       if (!subr_subsetter.encode_charstrings (subset_charstrings, !pinned))
466 	return false;
467 
468       if (!subr_subsetter.encode_globalsubrs (subset_globalsubrs))
469 	return false;
470 
471       /* local subrs */
472       if (!subset_localsubrs.resize (orig_fdcount))
473 	return false;
474       for (unsigned int fd = 0; fd < orig_fdcount; fd++)
475       {
476 	subset_localsubrs[fd].init ();
477 	if (!subr_subsetter.encode_localsubrs (fd, subset_localsubrs[fd]))
478 	  return false;
479       }
480     }
481 
482     /* FDSelect */
483     if (acc.fdSelect != &Null (CFF2FDSelect))
484     {
485       if (unlikely (!hb_plan_subset_cff_fdselect (plan,
486 						  orig_fdcount,
487 						  *(const FDSelect *)acc.fdSelect,
488 						  subset_fdcount,
489 						  subset_fdselect_size,
490 						  subset_fdselect_format,
491 						  subset_fdselect_ranges,
492 						  fdmap)))
493 	return false;
494     }
495     else
496       fdmap.identity (1);
497 
498     return true;
499   }
500 
501   cff2_sub_table_info_t info;
502 
503   unsigned int    num_glyphs;
504   unsigned int    orig_fdcount = 0;
505   unsigned int    subset_fdcount = 1;
506   unsigned int    subset_fdselect_size = 0;
507   unsigned int    subset_fdselect_format = 0;
508   bool            pinned = false;
509   hb_vector_t<code_pair_t>   subset_fdselect_ranges;
510 
511   hb_inc_bimap_t   fdmap;
512 
513   str_buff_vec_t	    subset_charstrings;
514   str_buff_vec_t	    subset_globalsubrs;
515   hb_vector_t<str_buff_vec_t> subset_localsubrs;
516 
517   bool	    drop_hints = false;
518   bool	    desubroutinize = false;
519 
520   unsigned  min_charstrings_off_size = 0;
521 };
522 } // namespace OT
523 
_serialize_cff2_charstrings(hb_serialize_context_t * c,cff2_subset_plan & plan,const OT::cff2::accelerator_subset_t & acc)524 static bool _serialize_cff2_charstrings (hb_serialize_context_t *c,
525 			     cff2_subset_plan &plan,
526 			     const OT::cff2::accelerator_subset_t  &acc)
527 {
528   c->push ();
529 
530   unsigned data_size = 0;
531   unsigned total_size = CFF2CharStrings::total_size (plan.subset_charstrings, &data_size, plan.min_charstrings_off_size);
532   if (unlikely (!c->start_zerocopy (total_size)))
533     return false;
534 
535   auto *cs = c->start_embed<CFF2CharStrings> ();
536   if (unlikely (!cs->serialize (c, plan.subset_charstrings, &data_size, plan.min_charstrings_off_size)))
537   {
538     c->pop_discard ();
539     return false;
540   }
541 
542   plan.info.char_strings_link = c->pop_pack (false);
543   return true;
544 }
545 
546 bool
serialize(hb_serialize_context_t * c,struct cff2_subset_plan & plan,hb_array_t<int> normalized_coords) const547 OT::cff2::accelerator_subset_t::serialize (hb_serialize_context_t *c,
548 					   struct cff2_subset_plan &plan,
549 					   hb_array_t<int> normalized_coords) const
550 {
551   /* push charstrings onto the object stack first which will ensure it packs as the last
552      object in the table. Keeping the chastrings last satisfies the requirements for patching
553      via IFTB. If this ordering needs to be changed in the future, charstrings should be left
554      at the end whenever HB_SUBSET_FLAGS_ITFB_REQUIREMENTS is enabled. */
555   if (!_serialize_cff2_charstrings(c, plan, *this))
556     return false;
557 
558   /* private dicts & local subrs */
559   hb_vector_t<table_info_t>  private_dict_infos;
560   if (unlikely (!private_dict_infos.resize (plan.subset_fdcount))) return false;
561 
562   for (int i = (int)privateDicts.length; --i >= 0 ;)
563   {
564     if (plan.fdmap.has (i))
565     {
566       objidx_t	subrs_link = 0;
567 
568       if (plan.subset_localsubrs[i].length > 0)
569       {
570 	auto *dest = c->push <CFF2Subrs> ();
571 	if (likely (dest->serialize (c, plan.subset_localsubrs[i])))
572 	  subrs_link = c->pop_pack (false);
573 	else
574 	{
575 	  c->pop_discard ();
576 	  return false;
577 	}
578       }
579       auto *pd = c->push<PrivateDict> ();
580       cff2_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints, plan.pinned,
581 						 varStore, normalized_coords);
582       if (likely (pd->serialize (c, privateDicts[i], privSzr, subrs_link)))
583       {
584 	unsigned fd = plan.fdmap[i];
585 	private_dict_infos[fd].size = c->length ();
586 	private_dict_infos[fd].link = c->pop_pack ();
587       }
588       else
589       {
590 	c->pop_discard ();
591 	return false;
592       }
593     }
594   }
595 
596   /* FDSelect */
597   if (fdSelect != &Null (CFF2FDSelect))
598   {
599     c->push ();
600     if (likely (hb_serialize_cff_fdselect (c, plan.num_glyphs, *(const FDSelect *)fdSelect,
601 					   plan.orig_fdcount,
602 					   plan.subset_fdselect_format, plan.subset_fdselect_size,
603 					   plan.subset_fdselect_ranges)))
604       plan.info.fd_select.link = c->pop_pack ();
605     else
606     {
607       c->pop_discard ();
608       return false;
609     }
610   }
611 
612   /* FDArray (FD Index) */
613   {
614     auto *fda = c->push<CFF2FDArray> ();
615     cff_font_dict_op_serializer_t fontSzr;
616     auto it =
617     + hb_zip (+ hb_iter (fontDicts)
618 	      | hb_filter ([&] (const cff2_font_dict_values_t &_)
619 		{ return plan.fdmap.has (&_ - &fontDicts[0]); }),
620 	      hb_iter (private_dict_infos))
621     ;
622     if (unlikely (!fda->serialize (c, it, fontSzr)))
623     {
624       c->pop_discard ();
625       return false;
626     }
627     plan.info.fd_array_link = c->pop_pack (false);
628   }
629 
630   /* variation store */
631   if (varStore != &Null (CFF2ItemVariationStore) &&
632       !plan.pinned)
633   {
634     auto *dest = c->push<CFF2ItemVariationStore> ();
635     if (unlikely (!dest->serialize (c, varStore)))
636     {
637       c->pop_discard ();
638       return false;
639     }
640     plan.info.var_store_link = c->pop_pack (false);
641   }
642 
643   OT::cff2 *cff2 = c->allocate_min<OT::cff2> ();
644   if (unlikely (!cff2)) return false;
645 
646   /* header */
647   cff2->version.major = 0x02;
648   cff2->version.minor = 0x00;
649   cff2->topDict = OT::cff2::static_size;
650 
651   /* top dict */
652   {
653     TopDict &dict = cff2 + cff2->topDict;
654     cff2_top_dict_op_serializer_t topSzr;
655     if (unlikely (!dict.serialize (c, topDict, topSzr, plan.info))) return false;
656     cff2->topDictSize = c->head - (const char *)&dict;
657   }
658 
659   /* global subrs */
660   {
661     auto *dest = c->start_embed <CFF2Subrs> ();
662     return dest->serialize (c, plan.subset_globalsubrs);
663   }
664 }
665 
666 bool
subset(hb_subset_context_t * c) const667 OT::cff2::accelerator_subset_t::subset (hb_subset_context_t *c) const
668 {
669   cff2_subset_plan cff2_plan;
670 
671   if (unlikely (!cff2_plan.create (*this, c->plan))) return false;
672   return serialize (c->serializer, cff2_plan,
673 		    c->plan->normalized_coords.as_array ());
674 }
675 
676 #endif
677