Added CFF2 Subr nullifier
authorMichiharu Ariza <ariza@adobe.com>
Fri, 17 Aug 2018 20:13:18 +0000 (13:13 -0700)
committerMichiharu Ariza <ariza@adobe.com>
Fri, 17 Aug 2018 20:13:18 +0000 (13:13 -0700)
along with CFF2 charstring interpreter
factored out common code between CFF1 & CFF2 to CSInterpreter
moved fetch_op from Interpreter to InterpEnv
misc code clean up & bug fixes

src/Makefile.sources
src/hb-cff-interp-common-private.hh
src/hb-cff-interp-cs-common-private.hh
src/hb-cff-interp-dict-common-private.hh
src/hb-cff1-interp-cs.hh
src/hb-cff2-interp-cs.hh [new file with mode: 0644]
src/hb-ot-cff-common-private.hh
src/hb-ot-cff2-table.hh
src/hb-subset-cff1.cc
src/hb-subset-cff2.cc

index f5e1a6d..f7ae086 100644 (file)
@@ -155,6 +155,7 @@ HB_OT_sources = \
        hb-cff-interp-common-private.hh \
        hb-cff-interp-cs-common-private.hh \
        hb-cff1-interp-cs.hh \
+       hb-cff2-interp-cs.hh \
        hb-cff-interp-dict-common-private.hh \
        $(NULL)
 
index 0f81b48..9b4d488 100644 (file)
@@ -56,7 +56,7 @@ enum OpCode {
     OpCode_Subrs,                  /* 19  CFF Private, CFF2 Private */
     OpCode_defaultWidthX,          /* 20  CFF Private (0) */
     OpCode_nominalWidthX,          /* 21  CFF Private (0) */
-    OpCode_vsindex,                /* 22  CFF2 Private/CS */
+    OpCode_vsindexdict,            /* 22  CFF2 Private/CS */
     OpCode_blenddict,              /* 23  CFF2 Private/CS  */
     OpCode_vstore,                 /* 24  CFF2 Top */
     OpCode_reserved25,             /* 25 */
@@ -142,8 +142,8 @@ enum OpCode {
  // OpCode_escape,                 /* 12 CFF, CFF2 */
     OpCode_Reserved13 = 13,
     OpCode_endchar,                /* 14 CFF */
// OpCode_vsindex,                /* 15 CFF2 */
-    OpCode_blendcs = 16,           /* 16 CFF2 */
   OpCode_vsindexcs,              /* 15 CFF2 */
+    OpCode_blendcs,                /* 16 CFF2 */
     OpCode_Reserved17,
     OpCode_hstemhm,                /* 18 CFF, CFF2 */
     OpCode_hintmask,               /* 19 CFF, CFF2 */
@@ -365,8 +365,8 @@ struct Stack
 
   inline void clear (void) { size = 0; }
 
-  inline bool check_overflow (unsigned int count) const { return (count <= kSizeLimit) && (count + size <= kSizeLimit); }
-  inline bool check_underflow (unsigned int count) const { return (count <= size); }
+  inline bool check_overflow (unsigned int count=1) const { return (count <= kSizeLimit) && (count + size <= kSizeLimit); }
+  inline bool check_underflow (unsigned int count=1) const { return (count <= size); }
 
   inline unsigned int get_size (void) const { return size; }
   inline bool is_empty (void) const { return size == 0; }
@@ -396,7 +396,7 @@ struct ArgStack : Stack<Number, 513>
 
   inline bool check_pop_num (Number& n)
   {
-    if (unlikely (!this->check_underflow (1)))
+    if (unlikely (!this->check_underflow ()))
       return false;
     n = this->pop ();
     return true;
@@ -413,7 +413,7 @@ struct ArgStack : Stack<Number, 513>
 
   inline bool check_pop_int (int& v)
   {
-    if (unlikely (!this->check_underflow (1)))
+    if (unlikely (!this->check_underflow ()))
       return false;
     v = this->pop ().to_int ();
     return true;
@@ -501,6 +501,21 @@ struct InterpEnv
     argStack.fini ();
   }
 
+  inline bool fetch_op (OpCode &op)
+  {
+    if (unlikely (!substr.avail ()))
+      return false;
+    op = (OpCode)(unsigned char)substr[0];
+    if (op == OpCode_escape) {
+      if (unlikely (!substr.avail ()))
+        return false;
+      op = Make_OpCode_ESC (substr[1]);
+      substr.inc ();
+    }
+    substr.inc ();
+    return true;
+  }
+
   SubByteStr    substr;
   ArgStack      argStack;
 };
@@ -558,21 +573,6 @@ struct Interpreter {
 
   inline void fini (void) { env.fini (); }
 
-  inline bool fetch_op (OpCode &op)
-  {
-    if (unlikely (!env.substr.avail ()))
-      return false;
-    op = (OpCode)(unsigned char)env.substr[0];
-    if (op == OpCode_escape) {
-      if (unlikely (!env.substr.avail ()))
-        return false;
-      op = Make_OpCode_ESC (env.substr[1]);
-      env.substr.inc ();
-    }
-    env.substr.inc ();
-    return true;
-  }
-
   ENV env;
 };
 
index cf98e0f..3172535 100644 (file)
@@ -64,6 +64,11 @@ struct CSInterpEnv : InterpEnv
   {
     InterpEnv::init (str);
 
+    stack_cleared = false;
+    seen_moveto = true;
+    seen_hintmask = false;
+    hstem_count = 0;
+    vstem_count = 0;
     callStack.init ();
     globalSubrs.init (globalSubrs_);
     localSubrs.init (localSubrs_);
@@ -105,20 +110,55 @@ struct CSInterpEnv : InterpEnv
 
   inline bool returnFromSubr (void)
   {
-    if (unlikely (!callStack.check_underflow (1)))
+    if (unlikely (!callStack.check_underflow ()))
       return false;
 
     substr = callStack.pop ();
     return true;
   }
 
+  inline void determine_hintmask_size (void)
+  {
+    if (!seen_hintmask)
+    {
+      vstem_count += argStack.size / 2;
+      hintmask_size = (hstem_count + vstem_count + 7) >> 3;
+      seen_hintmask = true;
+    }
+    clear_stack ();
+  }
+
+  inline void process_moveto (void)
+  {
+    clear_stack ();
+
+    if (!seen_moveto)
+    {
+      determine_hintmask_size ();
+      seen_moveto = true;
+    }
+  }
+
+  inline void clear_stack (void)
+  {
+    stack_cleared = true;
+    argStack.clear ();
+  }
+
   inline void set_endchar (bool endchar_flag_) { endchar_flag = endchar_flag_; }
   inline bool is_endchar (void) const { return endchar_flag; }
+  inline bool is_stack_cleared (void) const { return stack_cleared; }
 
   protected:
-  bool              endchar_flag;
+  bool          endchar_flag;
+  bool          stack_cleared;
+  bool          seen_moveto;
+  bool          seen_hintmask;
 
   public:
+  unsigned int  hstem_count;
+  unsigned int  vstem_count;
+  unsigned int  hintmask_size;
   CallStack            callStack;
   BiasedSubrs<SUBRS>   globalSubrs;
   BiasedSubrs<SUBRS>   localSubrs;
@@ -131,6 +171,12 @@ struct CSOpSet : OpSet
   {
     switch (op) {
 
+      case OpCode_return:
+        return env.returnFromSubr ();
+      case OpCode_endchar:
+        env.set_endchar (true);
+        return true;
+
       case OpCode_longintcs:
         return env.argStack.push_longint_from_substr (env.substr);
 
@@ -140,9 +186,50 @@ struct CSOpSet : OpSet
       case OpCode_callgsubr:
         return env.callSubr (env.globalSubrs);
 
+      case OpCode_hstem:
+      case OpCode_hstemhm:
+        env.hstem_count += env.argStack.size / 2;
+        env.clear_stack ();
+        break;
+      case OpCode_vstem:
+      case OpCode_vstemhm:
+        env.vstem_count += env.argStack.size / 2;
+        env.clear_stack ();
+        break;
+      case OpCode_hintmask:
+      case OpCode_cntrmask:
+        env.determine_hintmask_size ();
+        if (unlikely (!env.substr.avail (env.hintmask_size)))
+          return false;
+        env.substr.inc (env.hintmask_size);
+        break;
+      
+      case OpCode_vmoveto:
+      case OpCode_rlineto:
+      case OpCode_hlineto:
+      case OpCode_vlineto:
+      case OpCode_rmoveto:
+      case OpCode_hmoveto:
+        env.process_moveto ();
+        break;
+      case OpCode_rrcurveto:
+      case OpCode_rcurveline:
+      case OpCode_rlinecurve:
+      case OpCode_vvcurveto:
+      case OpCode_hhcurveto:
+      case OpCode_vhcurveto:
+      case OpCode_hvcurveto:
+      case OpCode_hflex:
+      case OpCode_flex:
+      case OpCode_hflex1:
+      case OpCode_flex1:
+        env.clear_stack ();
+        break;
+
       default:
         return OpSet::process_op (op, env);
     }
+    return true;
   }
 };
 
@@ -157,13 +244,11 @@ struct CSInterpreter : Interpreter<ENV>
 
     for (;;) {
       OpCode op;
-      if (unlikely (!super.fetch_op (op) ||
+      if (unlikely (!super.env.fetch_op (op) ||
                     !OPSET::process_op (op, super.env, param)))
         return false;
       if (super.env.is_endchar ())
         break;
-      if (!super.env.substr.avail ())
-        return false;
     }
     
     return true;
index 60764fd..62041c9 100644 (file)
@@ -170,7 +170,8 @@ struct DictInterpreter : Interpreter<InterpEnv>
     do
     {
       OpCode op;
-      if (unlikely (!super.fetch_op (op) || !OPSET::process_op (op, super.env, param)))
+      if (unlikely (!super.env.fetch_op (op) ||
+                    !OPSET::process_op (op, super.env, param)))
         return false;
     } while (super.env.substr.avail ());
     
index 6767d0f..c76a788 100644 (file)
@@ -38,11 +38,6 @@ struct CFF1CSInterpEnv : CSInterpEnv<CFF1Subrs>
   inline void init (const ByteStr &str, const CFF1Subrs &globalSubrs, const CFF1Subrs &localSubrs)
   {
     CSInterpEnv<CFF1Subrs>::init (str, globalSubrs, localSubrs);
-    seen_width = false;
-    seen_moveto = true;
-    seen_hintmask = false;
-    hstem_count = 0;
-    vstem_count = 0;
     for (unsigned int i = 0; i < kTransientArraySize; i++)
       transient_array[i].set_int (0);
   }
@@ -50,34 +45,6 @@ struct CFF1CSInterpEnv : CSInterpEnv<CFF1Subrs>
   bool check_transient_array_index (unsigned int i) const
   { return i < kTransientArraySize; }
 
-  inline void determine_hintmask_size (void)
-  {
-    if (!seen_hintmask)
-    {
-      vstem_count += argStack.size / 2;
-      hintmask_size = (hstem_count + vstem_count + 7) >> 3;
-      seen_hintmask = true;
-    }
-    clear_stack ();
-  }
-
-  inline void process_moveto (void)
-  {
-    clear_stack ();
-
-    if (!seen_moveto)
-    {
-      determine_hintmask_size ();
-      seen_moveto = true;
-    }
-  }
-
-  inline void clear_stack (void)
-  {
-    seen_width = true;
-    argStack.clear ();
-  }
-
   inline void process_width (void)
   {
     if (!seen_width && (argStack.size > 0))
@@ -90,11 +57,6 @@ struct CFF1CSInterpEnv : CSInterpEnv<CFF1Subrs>
 
   bool          seen_width;
   Number        width;
-  bool          seen_moveto;
-  bool          seen_hintmask;
-  unsigned int  hintmask_size;
-  unsigned int  hstem_count;
-  unsigned int  vstem_count;
 
   static const unsigned int kTransientArraySize = 32;
   Number  transient_array[kTransientArraySize];
@@ -109,11 +71,6 @@ struct CFF1CSOpSet : CSOpSet<CFF1Subrs, PARAM>
 
     switch (op) {
 
-      case OpCode_return:
-        return env.returnFromSubr ();
-      case OpCode_endchar:
-        env.set_endchar (true);
-        return true;
       case OpCode_and:
         if (unlikely (!env.argStack.check_pop_num2 (n1, n2))) return false;
         env.argStack.push_int ((n1.to_real() != 0.0f) && (n2.to_real() != 0.0f));
@@ -223,45 +180,6 @@ struct CFF1CSOpSet : CSOpSet<CFF1Subrs, PARAM>
           }
         }
         break;
-      case OpCode_hstem:
-      case OpCode_hstemhm:
-        env.hstem_count += env.argStack.size / 2;
-        env.clear_stack ();
-        break;
-      case OpCode_vstem:
-      case OpCode_vstemhm:
-        env.vstem_count += env.argStack.size / 2;
-        env.clear_stack ();
-        break;
-      case OpCode_hintmask:
-      case OpCode_cntrmask:
-        env.determine_hintmask_size ();
-        if (unlikely (!env.substr.avail (env.hintmask_size)))
-          return false;
-        env.substr.inc (env.hintmask_size);
-        break;
-      
-      case OpCode_vmoveto:
-      case OpCode_rlineto:
-      case OpCode_hlineto:
-      case OpCode_vlineto:
-      case OpCode_rmoveto:
-      case OpCode_hmoveto:
-        env.process_moveto ();
-        break;
-      case OpCode_rrcurveto:
-      case OpCode_rcurveline:
-      case OpCode_rlinecurve:
-      case OpCode_vvcurveto:
-      case OpCode_hhcurveto:
-      case OpCode_vhcurveto:
-      case OpCode_hvcurveto:
-      case OpCode_hflex:
-      case OpCode_flex:
-      case OpCode_hflex1:
-      case OpCode_flex1:
-        env.clear_stack ();
-        break;
       default:
         typedef CSOpSet<CFF1Subrs, PARAM>  SUPER;
         if (unlikely (!SUPER::process_op (op, env, param)))
diff --git a/src/hb-cff2-interp-cs.hh b/src/hb-cff2-interp-cs.hh
new file mode 100644 (file)
index 0000000..519b5fc
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright © 2018 Adobe Systems Incorporated.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Adobe Author(s): Michiharu Ariza
+ */
+#ifndef HB_CFF2_INTERP_CS_HH
+#define HB_CFF2_INTERP_CS_HH
+
+#include "hb-private.hh"
+#include "hb-cff-interp-cs-common-private.hh"
+
+namespace CFF {
+
+using namespace OT;
+
+struct CFF2CSInterpEnv : CSInterpEnv<CFF2Subrs>
+{
+  inline void init (const ByteStr &str, const CFF2Subrs &globalSubrs_, const CFF2Subrs &localSubrs_)
+  {
+    CSInterpEnv<CFF2Subrs>::init (str, globalSubrs_, localSubrs_);
+    ivs = 0;
+  }
+
+  inline bool fetch_op (OpCode &op)
+  {
+    if (unlikely (substr.avail ()))
+      return CSInterpEnv<CFF2Subrs>::fetch_op (op);
+
+    /* make up return or endchar op */
+    if (callStack.check_underflow ())
+      op = OpCode_return;
+    else
+      op = OpCode_endchar;
+    return true;
+  }
+
+  inline unsigned int get_ivs (void) const { return ivs; }
+  inline void         set_ivs (unsigned int ivs_) { ivs = ivs_; }
+
+  protected:
+  unsigned int  ivs;
+};
+
+template <typename PARAM>
+struct CFF2CSOpSet : CSOpSet<CFF2Subrs, PARAM>
+{
+  static inline bool process_op (OpCode op, CFF2CSInterpEnv &env, PARAM& param)
+  {
+    switch (op) {
+
+      case OpCode_blendcs:
+        env.clear_stack (); // XXX: TODO
+        break;
+      case OpCode_vsindexcs:
+        {
+          unsigned int ivs;
+          if (unlikely (!env.argStack.check_pop_uint (ivs))) return false;
+          env.set_ivs (ivs);
+          env.clear_stack ();
+        }
+        break;
+      default:
+        typedef CSOpSet<CFF2Subrs, PARAM>  SUPER;
+        if (unlikely (!SUPER::process_op (op, env, param)))
+          return false;
+        break;
+    }
+    return true;
+  }
+};
+
+template <typename OPSET, typename PARAM>
+struct CFF2CSInterpreter : CSInterpreter<CFF2CSInterpEnv, OPSET, PARAM> {};
+
+} /* namespace CFF */
+
+#endif /* HB_CFF2_INTERP_CS_HH */
index e22ee87..3f9f865 100644 (file)
@@ -557,6 +557,8 @@ struct Subrs : CFFIndex<COUNT>
   inline bool serialize (hb_serialize_context_t *c, const Subrs<COUNT> &subrs, unsigned int offSize, const hb_set_t *set, const ByteStr& nullStr = ByteStr())
   {
     TRACE_SERIALIZE (this);
+    if (&subrs == &Null(Subrs<COUNT>))
+      return_trace (true);
     if ((subrs.count == 0) || (hb_set_get_population (set) == 0))
     {
       if (!unlikely (c->allocate_size<COUNT> (COUNT::static_size)))
@@ -580,6 +582,8 @@ struct Subrs : CFFIndex<COUNT>
   /* in parallel to above */
   inline unsigned int calculate_serialized_size (unsigned int &offSize /*OUT*/, const hb_set_t *set, unsigned int nullStrSize = 0) const
   {
+    if (this == &Null(Subrs<COUNT>))
+      return 0;
     unsigned int  count_ = CFFIndex<COUNT>::count;
     offSize = 0;
     if ((count_ == 0) || (hb_set_get_population (set) == 0))
index f5d356d..da4acf4 100644 (file)
@@ -318,6 +318,7 @@ struct CFF2PrivateDictOpSet : DictOpSet
           return false;
         env.argStack.clear ();
         break;
+      case OpCode_vsindexdict:
       case OpCode_blenddict:
         // XXX: TODO
         return true;
@@ -442,10 +443,11 @@ struct cff2
       if (num_glyphs != sc.get_num_glyphs ())
       { fini (); return; }
 
-      privateDicts.resize (fdArray->count);
+      fdCount = fdArray->count;
+      privateDicts.resize (fdCount);
 
       /* parse font dicts and gather private dicts */
-      for (unsigned int i = 0; i < fdArray->count; i++)
+      for (unsigned int i = 0; i < fdCount; i++)
       {
         const ByteStr fontDictStr = (*fdArray)[i];
         if (unlikely (!fontDictStr.sanitize (&sc))) { fini (); return; }
@@ -500,6 +502,7 @@ struct cff2
     const CFF2CharStrings     *charStrings;
     const CFF2FDArray         *fdArray;
     const CFF2FDSelect        *fdSelect;
+    unsigned int              fdCount;
 
     hb_vector_t<CFF2FontDictValues>     fontDicts;
     hb_vector_t<PrivDictVal>  privateDicts;
index 5e54349..921b1df 100644 (file)
@@ -555,6 +555,7 @@ static inline bool _write_cff1 (const cff_subset_plan &plan,
     }
   }
 
+  assert (c.head == c.end);
   c.end_serialize ();
 
   return true;
index 7835523..ab876c9 100644 (file)
@@ -30,6 +30,7 @@
 #include "hb-subset-cff2.hh"
 #include "hb-subset-plan.hh"
 #include "hb-subset-cff-common-private.hh"
+#include "hb-cff2-interp-cs.hh"
 
 using namespace CFF;
 
@@ -37,6 +38,12 @@ struct CFF2SubTableOffsets {
   inline CFF2SubTableOffsets (void)
   {
     memset (this, 0, sizeof(*this));
+    localSubrsInfos.init ();
+  }
+
+  inline ~CFF2SubTableOffsets (void)
+  {
+    localSubrsInfos.fini ();
   }
 
   unsigned int  topDictSize;
@@ -45,6 +52,8 @@ struct CFF2SubTableOffsets {
   TableInfo     FDArrayInfo;
   TableInfo     charStringsInfo;
   unsigned int  privateDictsOffset;
+  TableInfo     globalSubrsInfo;
+  hb_vector_t<TableInfo>  localSubrsInfos;
 };
 
 struct CFF2TopDict_OpSerializer : OpSerializer
@@ -157,6 +166,31 @@ struct CFF2PrivateDict_OpSerializer : OpSerializer
   }
 };
 
+struct CFF2CSOpSet_SubrSubset : CFF2CSOpSet<SubrRefMapPair>
+{
+  static inline bool process_op (OpCode op, CFF2CSInterpEnv &env, SubrRefMapPair& refMapPair)
+  {
+    unsigned int  subr_num;
+    switch (op) {
+      case OpCode_callsubr:
+        if (!unlikely (env.popSubrNum(env.localSubrs, subr_num)))
+          return false;
+        env.argStack.unpop ();
+        refMapPair.local_map->add (subr_num);
+        break;
+      case OpCode_callgsubr:
+        if (!unlikely (env.popSubrNum(env.globalSubrs, subr_num)))
+          return false;
+        env.argStack.unpop ();
+        refMapPair.global_map->add (subr_num);
+        break;
+      default:
+        break;
+    }
+    return CFF2CSOpSet<SubrRefMapPair>::process_op (op, env, refMapPair);
+  }
+};
+
 struct cff2_subset_plan {
   inline cff2_subset_plan (void)
     : final_size (0),
@@ -176,6 +210,7 @@ struct cff2_subset_plan {
     fdmap.fini ();
     subset_charstrings.fini ();
     privateDictInfos.fini ();
+    subrRefMaps.fini ();
   }
 
   inline bool create (const OT::cff2::accelerator_subset_t &acc,
@@ -194,8 +229,22 @@ struct cff2_subset_plan {
       final_size += offsets.topDictSize;
     }
 
+    /* Subset global & local subrs */
+    {
+      SubrSubsetter<const OT::cff2::accelerator_subset_t, CFF2CSInterpEnv, CFF2CSOpSet_SubrSubset> subsetter(acc, plan->glyphs);
+      if (!subsetter.collect_refs (subrRefMaps))
+        return false;
+      
+      offsets.globalSubrsInfo.size = acc.globalSubrs->calculate_serialized_size (offsets.globalSubrsInfo.offSize, subrRefMaps.global_map);
+      if (!offsets.localSubrsInfos.resize (orig_fdcount))
+        return false;
+      for (unsigned int i = 0; i < orig_fdcount; i++)
+        offsets.localSubrsInfos[i].size = acc.privateDicts[i].localSubrs->calculate_serialized_size (offsets.localSubrsInfos[i].offSize, subrRefMaps.local_maps[i]);
+    }
+    
     /* global subrs */
-    final_size += acc.globalSubrs->get_size ();
+    offsets.globalSubrsInfo.offset = final_size;
+    final_size += offsets.globalSubrsInfo.size;
 
     /* variation store */
     if (acc.varStore != &Null(CFF2VariationStore))
@@ -253,7 +302,7 @@ struct cff2_subset_plan {
         CFF2PrivateDict_OpSerializer privSzr;
         TableInfo  privInfo = { final_size, PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr), 0 };
         privateDictInfos.push (privInfo);
-        final_size += privInfo.size + acc.privateDicts[i].localSubrs->get_size ();
+        final_size += privInfo.size + offsets.localSubrsInfos[i].size;
       }
     }
 
@@ -275,6 +324,8 @@ struct cff2_subset_plan {
 
   hb_vector_t<ByteStr> subset_charstrings;
   hb_vector_t<TableInfo> privateDictInfos;
+
+  SubrRefMaps             subrRefMaps;
 };
 
 static inline bool _write_cff2 (const cff2_subset_plan &plan,
@@ -312,8 +363,7 @@ static inline bool _write_cff2 (const cff2_subset_plan &plan,
     assert (cff2->topDict + plan.offsets.topDictSize == c.head - c.start);
     CFF2Subrs *dest = c.start_embed<CFF2Subrs> ();
     if (unlikely (dest == nullptr)) return false;
-    CFFIndex<HBUINT32> *super = dest;
-    if (unlikely (!super->serialize (&c, *acc.globalSubrs)))
+    if (unlikely (!dest->serialize (&c, *acc.globalSubrs, plan.offsets.globalSubrsInfo.offSize, plan.subrRefMaps.global_map)))
     {
       DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 global subrs");
       return false;
@@ -409,8 +459,7 @@ static inline bool _write_cff2 (const cff2_subset_plan &plan,
           DEBUG_MSG (SUBSET, nullptr, "CFF2 subset: local subrs unexpectedly null [%d]", i);
           return false;
         }
-        CFFIndex<HBUINT32> *super = subrs;
-        if (unlikely (!super->serialize (&c, *acc.privateDicts[i].localSubrs)))
+        if (unlikely (!subrs->serialize (&c, *acc.privateDicts[i].localSubrs, plan.offsets.localSubrsInfos[i].offSize, plan.subrRefMaps.local_maps[i])))
         {
           DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 local subrs [%d]", i);
           return false;
@@ -419,6 +468,7 @@ static inline bool _write_cff2 (const cff2_subset_plan &plan,
     }
   }
 
+  assert (c.head == c.end);
   c.end_serialize ();
 
   return true;