tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

hb-subset-cff2.cc (19566B)


      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 {
     42  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 {
     52  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 {
     74  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 
    102  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 
    127  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 
    154  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 
    167  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 {
    185  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:
    216  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 {
    233  cff2_subr_subsetter_t (const OT::cff2::accelerator_subset_t &acc_, const hb_subset_plan_t *plan_)
    234    : subr_subsetter_t (acc_, plan_) {}
    235 
    236  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 {
    250  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 
    255  void init () {}
    256 
    257  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 
    269  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 {
    293  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 
    301  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 
    326  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 {
    380  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 
    386  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 {
    427  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 
    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
    547 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
    667 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