CFF2 subroutine flattner

Factored out CFF1 & CFF2 common subsetting code in hb-subset-cff-common.hh
diff --git a/src/hb-cff2-interp-cs.hh b/src/hb-cff2-interp-cs.hh
index aaff5ac..20a5f5e 100644
--- a/src/hb-cff2-interp-cs.hh
+++ b/src/hb-cff2-interp-cs.hh
@@ -54,6 +54,15 @@
     return true;
   }
 
+  inline bool process_vsindex (void)
+  {
+    unsigned int  index;
+    if (unlikely (!argStack.check_pop_uint (index)))
+      return false;
+    set_ivs (index);
+    return true;
+  }
+
   inline unsigned int get_ivs (void) const { return ivs; }
   inline void         set_ivs (unsigned int ivs_) { ivs = ivs_; }
 
@@ -69,16 +78,11 @@
     switch (op) {
 
       case OpCode_blendcs:
-        //env.flush_stack (); // XXX: TODO
-        break;
+        return OPSET::process_blend (env, param);
+
       case OpCode_vsindexcs:
-        {
-          unsigned int ivs;
-          if (unlikely (!env.argStack.check_pop_uint (ivs))) return false;
-          env.set_ivs (ivs);
-          //env.flush_stack ();
-        }
-        break;
+        return OPSET::process_vsindex (env, param);
+
       default:
         typedef CSOpSet<OPSET, CFF2CSInterpEnv, PARAM>  SUPER;
         if (unlikely (!SUPER::process_op (op, env, param)))
@@ -87,6 +91,18 @@
     }
     return true;
   }
+
+  static inline bool process_blend (CFF2CSInterpEnv &env, PARAM& param)
+  {
+    // XXX: TODO leave default values?
+    OPSET::flush_stack (env, param);
+    return true;
+  }
+
+  static inline bool process_vsindex (CFF2CSInterpEnv &env, PARAM& param)
+  {
+    return env.process_vsindex ();
+  }
 };
 
 template <typename OPSET, typename PARAM>
diff --git a/src/hb-subset-cff-common.hh b/src/hb-subset-cff-common.hh
index 4ed8248..f7294cc 100644
--- a/src/hb-subset-cff-common.hh
+++ b/src/hb-subset-cff-common.hh
@@ -98,6 +98,160 @@
   }
 };
 
+struct CFFSubTableOffsets {
+  inline CFFSubTableOffsets (void)
+    : privateDictsOffset (0)
+  
+  {
+    topDictInfo.init ();
+    FDSelectInfo.init ();
+    FDArrayInfo.init ();
+    charStringsInfo.init ();
+    globalSubrsInfo.init ();
+    localSubrsInfos.init ();
+  }
+
+  inline ~CFFSubTableOffsets (void)
+  {
+    localSubrsInfos.fini ();
+  }
+
+  TableInfo     topDictInfo;
+  TableInfo     FDSelectInfo;
+  TableInfo     FDArrayInfo;
+  TableInfo     charStringsInfo;
+  unsigned int  privateDictsOffset;
+  TableInfo     globalSubrsInfo;
+  hb_vector_t<TableInfo>  localSubrsInfos;
+};
+
+struct CFFTopDict_OpSerializer : OpSerializer
+{
+  inline bool serialize (hb_serialize_context_t *c,
+                         const OpStr &opstr,
+                         const CFFSubTableOffsets &offsets) const
+  {
+    TRACE_SERIALIZE (this);
+
+    switch (opstr.op)
+    {
+      case OpCode_CharStrings:
+        return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.charStringsInfo.offset));
+
+      case OpCode_FDArray:
+        return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.FDArrayInfo.offset));
+
+      case OpCode_FDSelect:
+        return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.FDSelectInfo.offset));
+
+      default:
+        return_trace (copy_opstr (c, opstr));
+    }
+    return_trace (true);
+  }
+
+  inline unsigned int calculate_serialized_size (const OpStr &opstr) const
+  {
+    switch (opstr.op)
+    {
+      case OpCode_CharStrings:
+      case OpCode_FDArray:
+      case OpCode_FDSelect:
+        return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (opstr.op);
+    
+      default:
+        return opstr.str.len;
+    }
+  }
+};
+
+struct CFFFontDict_OpSerializer : OpSerializer
+{
+  inline bool serialize (hb_serialize_context_t *c,
+                         const OpStr &opstr,
+                         const TableInfo &privateDictInfo) const
+  {
+    TRACE_SERIALIZE (this);
+
+    if (opstr.op == OpCode_Private)
+    {
+      /* serialize the private dict size as a 2-byte integer */
+      if (unlikely (!UnsizedByteStr::serialize_int2 (c, privateDictInfo.size)))
+        return_trace (false);
+
+      /* serialize the private dict offset as a 4-byte integer */
+      if (unlikely (!UnsizedByteStr::serialize_int4 (c, privateDictInfo.offset)))
+        return_trace (false);
+
+      /* serialize the opcode */
+      HBUINT8 *p = c->allocate_size<HBUINT8> (1);
+      if (unlikely (p == nullptr)) return_trace (false);
+      p->set (OpCode_Private);
+
+      return_trace (true);
+    }
+    else
+    {
+      HBUINT8 *d = c->allocate_size<HBUINT8> (opstr.str.len);
+      if (unlikely (d == nullptr)) return_trace (false);
+      memcpy (d, &opstr.str.str[0], opstr.str.len);
+    }
+    return_trace (true);
+  }
+
+  inline unsigned int calculate_serialized_size (const OpStr &opstr) const
+  {
+    if (opstr.op == OpCode_Private)
+      return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Private);
+    else
+      return opstr.str.len;
+  }
+};
+
+struct CFFPrivateDict_OpSerializer : OpSerializer
+{
+  inline CFFPrivateDict_OpSerializer (bool drop_hints_=false, bool flatten_subrs_=false)
+    : drop_hints (drop_hints_), flatten_subrs (flatten_subrs_) {}
+
+  inline bool serialize (hb_serialize_context_t *c,
+                         const OpStr &opstr,
+                         const unsigned int subrsOffset) const
+  {
+    TRACE_SERIALIZE (this);
+
+    if (drop_hints && DictOpSet::is_hint_op (opstr.op))
+      return true;
+    if (opstr.op == OpCode_Subrs)
+    {
+      if (flatten_subrs)
+        return_trace (true);
+      else
+        return_trace (FontDict::serialize_offset2_op(c, OpCode_Subrs, subrsOffset));
+    }
+    else
+      return_trace (copy_opstr (c, opstr));
+  }
+
+  inline unsigned int calculate_serialized_size (const OpStr &opstr) const
+  {
+    if (drop_hints && DictOpSet::is_hint_op (opstr.op))
+      return 0;
+    if (opstr.op == OpCode_Subrs)
+    {
+      if (flatten_subrs)
+        return 0;
+      else
+        return OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs);
+    }
+    else
+      return opstr.str.len;
+  }
+
+  protected:
+  const bool  drop_hints;
+  const bool  flatten_subrs;
+};
+
 
 template <typename ACCESSOR, typename ENV, typename OPSET>
 struct SubrFlattener
diff --git a/src/hb-subset-cff1.cc b/src/hb-subset-cff1.cc
index 9e8f090..769eb5e 100644
--- a/src/hb-subset-cff1.cc
+++ b/src/hb-subset-cff1.cc
@@ -34,32 +34,26 @@
 
 using namespace CFF;
 
-struct CFF1SubTableOffsets {
+struct CFF1SubTableOffsets : CFFSubTableOffsets
+{
   inline CFF1SubTableOffsets (void)
+    : CFFSubTableOffsets (),
+      nameIndexOffset (0),
+      stringIndexOffset (0),
+      encodingOffset (0),
+      charsetOffset (0)
   {
-    memset (this, 0, sizeof(*this));
-    localSubrsInfos.init ();
-  }
-
-  inline ~CFF1SubTableOffsets (void)
-  {
-    localSubrsInfos.fini ();
+    privateDictInfo.init ();
   }
 
   unsigned int  nameIndexOffset;
-  TableInfo     topDictInfo;
   unsigned int  stringIndexOffset;
-  TableInfo     globalSubrsInfo;
   unsigned int  encodingOffset;
   unsigned int  charsetOffset;
-  TableInfo     FDSelectInfo;
-  TableInfo     FDArrayInfo;
-  TableInfo     charStringsInfo;
   TableInfo     privateDictInfo;
-  hb_vector_t<TableInfo>  localSubrsInfos;
 };
 
-struct CFF1TopDict_OpSerializer : OpSerializer
+struct CFF1TopDict_OpSerializer : CFFTopDict_OpSerializer
 {
   inline bool serialize (hb_serialize_context_t *c,
                          const OpStr &opstr,
@@ -75,15 +69,6 @@
       case OpCode_Encoding:
         return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.encodingOffset));
 
-      case OpCode_CharStrings:
-        return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.charStringsInfo.offset));
-
-      case OpCode_FDArray:
-        return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.FDArrayInfo.offset));
-
-      case OpCode_FDSelect:
-        return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.FDSelectInfo.offset));
-
       case OpCode_Private:
         {
           if (unlikely (!UnsizedByteStr::serialize_int2 (c, offsets.privateDictInfo.size)))
@@ -97,7 +82,7 @@
         break;
 
       default:
-        return_trace (copy_opstr (c, opstr));
+        return_trace (CFFTopDict_OpSerializer::serialize (c, opstr, offsets));
     }
     return_trace (true);
   }
@@ -108,107 +93,17 @@
     {
       case OpCode_charset:
       case OpCode_Encoding:
-      case OpCode_CharStrings:
-      case OpCode_FDArray:
-      case OpCode_FDSelect:
         return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (opstr.op);
     
       case OpCode_Private:
         return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Private);
     
       default:
-        return opstr.str.len;
+        return CFFTopDict_OpSerializer::calculate_serialized_size (opstr);
     }
   }
 };
 
-struct CFF1FontDict_OpSerializer : OpSerializer
-{
-  inline bool serialize (hb_serialize_context_t *c,
-                         const OpStr &opstr,
-                         const TableInfo &privateDictInfo) const
-  {
-    TRACE_SERIALIZE (this);
-
-    if (opstr.op == OpCode_Private)
-    {
-      /* serialize the private dict size as a 2-byte integer */
-      if (unlikely (!UnsizedByteStr::serialize_int2 (c, privateDictInfo.size)))
-        return_trace (false);
-
-      /* serialize the private dict offset as a 4-byte integer */
-      if (unlikely (!UnsizedByteStr::serialize_int4 (c, privateDictInfo.offset)))
-        return_trace (false);
-
-      /* serialize the opcode */
-      HBUINT8 *p = c->allocate_size<HBUINT8> (1);
-      if (unlikely (p == nullptr)) return_trace (false);
-      p->set (OpCode_Private);
-
-      return_trace (true);
-    }
-    else
-    {
-      HBUINT8 *d = c->allocate_size<HBUINT8> (opstr.str.len);
-      if (unlikely (d == nullptr)) return_trace (false);
-      memcpy (d, &opstr.str.str[0], opstr.str.len);
-    }
-    return_trace (true);
-  }
-
-  inline unsigned int calculate_serialized_size (const OpStr &opstr) const
-  {
-    if (opstr.op == OpCode_Private)
-      return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Private);
-    else
-      return opstr.str.len;
-  }
-};
-
-struct CFF1PrivateDict_OpSerializer : OpSerializer
-{
-  inline CFF1PrivateDict_OpSerializer (bool drop_hints_=false, bool flatten_subrs_=false)
-    : drop_hints (drop_hints_), flatten_subrs (flatten_subrs_) {}
-
-  inline bool serialize (hb_serialize_context_t *c,
-                         const OpStr &opstr,
-                         const unsigned int subrsOffset) const
-  {
-    TRACE_SERIALIZE (this);
-
-    if (drop_hints && DictOpSet::is_hint_op (opstr.op))
-      return true;
-    if (opstr.op == OpCode_Subrs)
-    {
-      if (flatten_subrs)
-        return_trace (true);
-      else
-        return_trace (FontDict::serialize_offset2_op(c, OpCode_Subrs, subrsOffset));
-    }
-    else
-      return_trace (copy_opstr (c, opstr));
-  }
-
-  inline unsigned int calculate_serialized_size (const OpStr &opstr) const
-  {
-    if (drop_hints && DictOpSet::is_hint_op (opstr.op))
-      return 0;
-    if (opstr.op == OpCode_Subrs)
-    {
-      if (flatten_subrs)
-        return 0;
-      else
-        return OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs);
-    }
-    else
-      return opstr.str.len;
-  }
-
-  protected:
-  const bool  drop_hints;
-  const bool  flatten_subrs;
-};
-
 struct CFF1CSOpSet_Flatten : CFF1CSOpSet<CFF1CSOpSet_Flatten, ByteStrBuff>
 {
   static inline bool process_op (OpCode op, CFF1CSInterpEnv &env, ByteStrBuff& flatStr)
@@ -387,7 +282,7 @@
     /* FDArray (FDIndex) */
     if (acc.fdArray != &Null(CFF1FDArray)) {
       offsets.FDArrayInfo.offset = final_size;
-      CFF1FontDict_OpSerializer fontSzr;
+      CFFFontDict_OpSerializer fontSzr;
       final_size += CFF1FDArray::calculate_serialized_size(offsets.FDArrayInfo.offSize/*OUT*/, acc.fontDicts, subst_fdcount, fdmap, fontSzr);
     }
 
@@ -422,7 +317,7 @@
       if (!fdmap.excludes (i))
       {
         unsigned int  priv_size;
-        CFF1PrivateDict_OpSerializer privSzr (plan->drop_hints, flatten_subrs);
+        CFFPrivateDict_OpSerializer privSzr (plan->drop_hints, flatten_subrs);
         priv_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr);
         TableInfo  privInfo = { final_size, priv_size, 0 };
         privateDictInfos.push (privInfo);
@@ -594,7 +489,7 @@
     assert (plan.offsets.FDArrayInfo.offset == c.head - c.start);
     CFF1FDArray  *fda = c.start_embed<CFF1FDArray> ();
     if (unlikely (fda == nullptr)) return false;
-    CFF1FontDict_OpSerializer  fontSzr;
+    CFFFontDict_OpSerializer  fontSzr;
     if (unlikely (!fda->serialize (&c, plan.offsets.FDArrayInfo.offSize,
                                    acc.fontDicts, plan.subst_fdcount, plan.fdmap,
                                    fontSzr, plan.privateDictInfos)))
@@ -626,7 +521,7 @@
       if (unlikely (pd == nullptr)) return false;
       unsigned int priv_size = plan.flatten_subrs? 0: plan.privateDictInfos[plan.fdmap[i]].size;
       bool result;
-      CFF1PrivateDict_OpSerializer privSzr (plan.drop_hints, plan.flatten_subrs);
+      CFFPrivateDict_OpSerializer privSzr (plan.drop_hints, plan.flatten_subrs);
       /* N.B. local subrs immediately follows its corresponding private dict. i.e., subr offset == private dict size */
       result = pd->serialize (&c, acc.privateDicts[i], privSzr, priv_size);
       if (unlikely (!result))
diff --git a/src/hb-subset-cff2.cc b/src/hb-subset-cff2.cc
index fee09d8..59535e6 100644
--- a/src/hb-subset-cff2.cc
+++ b/src/hb-subset-cff2.cc
@@ -34,29 +34,17 @@
 
 using namespace CFF;
 
-struct CFF2SubTableOffsets {
+struct CFF2SubTableOffsets : CFFSubTableOffsets
+{
   inline CFF2SubTableOffsets (void)
-  {
-    memset (this, 0, sizeof(*this));
-    localSubrsInfos.init ();
-  }
+    : CFFSubTableOffsets (),
+      varStoreOffset (0)
+  {}
 
-  inline ~CFF2SubTableOffsets (void)
-  {
-    localSubrsInfos.fini ();
-  }
-
-  unsigned int  topDictSize;
   unsigned int  varStoreOffset;
-  TableInfo     FDSelectInfo;
-  TableInfo     FDArrayInfo;
-  TableInfo     charStringsInfo;
-  unsigned int  privateDictsOffset;
-  TableInfo     globalSubrsInfo;
-  hb_vector_t<TableInfo>  localSubrsInfos;
 };
 
-struct CFF2TopDict_OpSerializer : OpSerializer
+struct CFF2TopDict_OpSerializer : CFFTopDict_OpSerializer
 {
   inline bool serialize (hb_serialize_context_t *c,
                          const OpStr &opstr,
@@ -69,19 +57,9 @@
       case OpCode_vstore:
         return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.varStoreOffset));
 
-      case OpCode_CharStrings:
-        return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.charStringsInfo.offset));
-
-      case OpCode_FDArray:
-        return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.FDArrayInfo.offset));
-
-      case OpCode_FDSelect:
-        return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.FDSelectInfo.offset));
-
       default:
-        return_trace (copy_opstr (c, opstr));
+        return_trace (CFFTopDict_OpSerializer::serialize (c, opstr, offsets));
     }
-    return_trace (true);
   }
 
   inline unsigned int calculate_serialized_size (const OpStr &opstr) const
@@ -89,102 +67,64 @@
     switch (opstr.op)
     {
       case OpCode_vstore:
-      case OpCode_CharStrings:
-      case OpCode_FDArray:
-      case OpCode_FDSelect:
         return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (opstr.op);
     
       default:
-        return opstr.str.len;
+        return CFFTopDict_OpSerializer::calculate_serialized_size (opstr);
     }
   }
 };
 
-struct CFF2FontDict_OpSerializer : OpSerializer
+struct CFF2CSOpSet_Flatten : CFF2CSOpSet<CFF2CSOpSet_Flatten, ByteStrBuff>
 {
-  inline bool serialize (hb_serialize_context_t *c,
-                         const OpStr &opstr,
-                         const TableInfo& privDictInfo) const
+  static inline bool process_op (OpCode op, CFF2CSInterpEnv &env, ByteStrBuff& flatStr)
   {
-    TRACE_SERIALIZE (this);
-
-    if (opstr.op == OpCode_Private)
+    if (unlikely (!SUPER::process_op (op, env, flatStr)))
+      return false;
+    switch (op)
     {
-      /* serialize the private dict size as a 2-byte integer */
-      if (unlikely (!UnsizedByteStr::serialize_int2 (c, privDictInfo.size)))
-        return_trace (false);
-
-      /* serialize the private dict offset as a 4-byte integer */
-      if (unlikely (!UnsizedByteStr::serialize_int4 (c, privDictInfo.offset)))
-        return_trace (false);
-
-      /* serialize the opcode */
-      HBUINT8 *p = c->allocate_size<HBUINT8> (1);
-      if (unlikely (p == nullptr)) return_trace (false);
-      p->set (OpCode_Private);
-
-      return_trace (true);
+      case OpCode_hintmask:
+      case OpCode_cntrmask:
+        if (unlikely (!flatStr.encode_op (op)))
+          return false;
+        for (int i = -env.hintmask_size; i < 0; i++)
+          if (unlikely (!flatStr.encode_byte (env.substr[i])))
+            return false;
+        break;
+      case OpCode_return:
+      case OpCode_endchar:
+        /* dummy opcodes in CFF2. ignore */
+        break;
+      default:
+        if (!CSOPSET::is_subr_op (op) &&
+            !CSOPSET::is_arg_op (op))
+          return flatStr.encode_op (op);
     }
-    else
-    {
-      HBUINT8 *d = c->allocate_size<HBUINT8> (opstr.str.len);
-      if (unlikely (d == nullptr)) return_trace (false);
-      memcpy (d, &opstr.str.str[0], opstr.str.len);
-    }
-    return_trace (true);
+    return true;
   }
 
-  inline unsigned int calculate_serialized_size (const OpStr &opstr) const
+  static inline bool process_blend (CFF2CSInterpEnv &env, ByteStrBuff& flatStr)
   {
-    if (opstr.op == OpCode_Private)
-      return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Private);
-    else
-      return opstr.str.len;
+    flush_stack (env, flatStr);
+    return true;
   }
-};
 
-struct CFF2PrivateDict_OpSerializer : OpSerializer
-{
-  inline bool serialize (hb_serialize_context_t *c,
-                         const OpStr &opstr,
-                         const unsigned int subrsOffset) const
+  static inline bool process_vsindex (CFF2CSInterpEnv &env, ByteStrBuff& flatStr)
   {
-    TRACE_SERIALIZE (this);
-
-    if (opstr.op == OpCode_Subrs)
-      return_trace (FontDict::serialize_offset2_op(c, OpCode_Subrs, subrsOffset));
-    else
-      return_trace (copy_opstr (c, opstr));
+    flush_stack (env, flatStr);
+    return true;
   }
 
-  inline unsigned int calculate_serialized_size (const OpStr &opstr) const
+  static inline void flush_stack (CFF2CSInterpEnv &env, ByteStrBuff& flatStr)
   {
-    if (opstr.op == OpCode_Subrs)
-      return OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs);
-    else
-      return opstr.str.len;
-  }
-};
-
-struct CFF2PrivateDict_OpSerializer_DropHints : CFF2PrivateDict_OpSerializer
-{
-  inline bool serialize (hb_serialize_context_t *c,
-                         const OpStr &opstr,
-                         const unsigned int subrsOffset) const
-  {
-    if (DictOpSet::is_hint_op (opstr.op))
-      return true;
-    else
-      return CFF2PrivateDict_OpSerializer::serialize (c, opstr, subrsOffset);
+    for (unsigned int i = 0; i < env.argStack.size; i++)
+      flatStr.encode_num (env.argStack.elements[i]);
+    SUPER::flush_stack (env, flatStr);
   }
 
-  inline unsigned int calculate_serialized_size (const OpStr &opstr) const
-  {
-    if (DictOpSet::is_hint_op (opstr.op))
-      return 0;
-    else
-      return CFF2PrivateDict_OpSerializer::calculate_serialized_size (opstr);
-  }
+  private:
+  typedef CFF2CSOpSet<CFF2CSOpSet_Flatten, ByteStrBuff> SUPER;
+  typedef CSOpSet<CFF2CSOpSet_Flatten, CFF2CSInterpEnv, ByteStrBuff> CSOPSET;
 };
 
 struct CFF2CSOpSet_SubsetSubrs : CFF2CSOpSet<CFF2CSOpSet_SubsetSubrs, SubrRefMapPair>
@@ -217,11 +157,14 @@
     : final_size (0),
       orig_fdcount (0),
       subst_fdcount(1),
-      subst_fdselect_format (0)
+      subst_fdselect_format (0),
+      flatten_subrs (true),
+      drop_hints (false)
   {
     subst_fdselect_first_glyphs.init ();
     fdmap.init ();
     subset_charstrings.init ();
+    flat_charstrings.init ();
     privateDictInfos.init ();
     subrRefMaps.init ();
   }
@@ -231,6 +174,7 @@
     subst_fdselect_first_glyphs.fini ();
     fdmap.fini ();
     subset_charstrings.fini ();
+    flat_charstrings.fini ();
     privateDictInfos.fini ();
     subrRefMaps.fini ();
   }
@@ -249,12 +193,23 @@
     /* top dict */
     {
       CFF2TopDict_OpSerializer topSzr;
-      offsets.topDictSize = TopDict::calculate_serialized_size (acc.topDict, topSzr);
-      final_size += offsets.topDictSize;
+      offsets.topDictInfo.size = TopDict::calculate_serialized_size (acc.topDict, topSzr);
+      final_size += offsets.topDictInfo.size;
     }
 
-    /* Subset global & local subrs */
+    if (flatten_subrs)
     {
+      /* Flatten global & local subrs */
+      SubrFlattener<const OT::cff2::accelerator_subset_t, CFF2CSInterpEnv, CFF2CSOpSet_Flatten> flattener(acc, plan->glyphs);
+      if (!flattener.flatten (flat_charstrings))
+        return false;
+      
+      /* no global/local subroutines */
+      offsets.globalSubrsInfo.size = HBUINT32::static_size; /* count 0 only */
+    }
+    else
+    {
+      /* Subset global & local subrs */
       SubrSubsetter<const OT::cff2::accelerator_subset_t, CFF2CSInterpEnv, CFF2CSOpSet_SubsetSubrs> subsetter(acc, plan->glyphs);
       if (!subsetter.collect_refs (subrRefMaps))
         return false;
@@ -299,7 +254,7 @@
     /* FDArray (FDIndex) */
     {
       offsets.FDArrayInfo.offset = final_size;
-      CFF2FontDict_OpSerializer fontSzr;
+      CFFFontDict_OpSerializer fontSzr;
       final_size += CFF2FDArray::calculate_serialized_size(offsets.FDArrayInfo.offSize/*OUT*/, acc.fontDicts, subst_fdcount, fdmap, fontSzr);
     }
 
@@ -309,9 +264,19 @@
       unsigned int dataSize = 0;
       for (unsigned int i = 0; i < plan->glyphs.len; i++)
       {
-        const ByteStr str = (*acc.charStrings)[plan->glyphs[i]];
-        subset_charstrings.push (str);
-        dataSize += str.len;
+        if (flatten_subrs)
+        {
+          ByteStrBuff &flatstr = flat_charstrings[i];
+          ByteStr str (&flatstr[0], flatstr.len);
+          subset_charstrings.push (str);
+          dataSize += flatstr.len;
+        }
+        else
+        {
+          const ByteStr str = (*acc.charStrings)[plan->glyphs[i]];
+          subset_charstrings.push (str);
+          dataSize += str.len;
+        }
       }
       offsets.charStringsInfo.offSize = calcOffSize (dataSize + 1);
       final_size += CFF2CharStrings::calculate_serialized_size (offsets.charStringsInfo.offSize, plan->glyphs.len, dataSize);
@@ -324,19 +289,13 @@
       if (!fdmap.excludes (i))
       {
         unsigned int priv_size;
-        if (plan->drop_hints)
-        {
-          CFF2PrivateDict_OpSerializer_DropHints privSzr_drop;
-          priv_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr_drop);
-        }
-        else
-        {
-          CFF2PrivateDict_OpSerializer privSzr;
-          priv_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr);
-        }
+        CFFPrivateDict_OpSerializer privSzr (drop_hints, flatten_subrs);
+        priv_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr);
         TableInfo  privInfo = { final_size, priv_size, 0 };
         privateDictInfos.push (privInfo);
-        final_size += privInfo.size + offsets.localSubrsInfos[i].size;
+        final_size += privInfo.size;
+        if (!flatten_subrs)
+          final_size += offsets.localSubrsInfos[i].size;
       }
     }
 
@@ -356,11 +315,13 @@
 
   FDMap   fdmap;
 
-  hb_vector_t<ByteStr> subset_charstrings;
-  hb_vector_t<TableInfo> privateDictInfos;
+  hb_vector_t<ByteStr>    subset_charstrings;
+  ByteStrBuffArray        flat_charstrings;
+  hb_vector_t<TableInfo>  privateDictInfos;
 
   SubrRefMaps             subrRefMaps;
 
+  bool            flatten_subrs;
   bool            drop_hints;
 };
 
@@ -384,7 +345,7 @@
   /* top dict */
   {
     assert (cff2->topDict == c.head - c.start);
-    cff2->topDictSize.set (plan.offsets.topDictSize);
+    cff2->topDictSize.set (plan.offsets.topDictInfo.size);
     TopDict &dict = cff2 + cff2->topDict;
     CFF2TopDict_OpSerializer topSzr;
     if (unlikely (!dict.serialize (&c, acc.topDict, topSzr, plan.offsets)))
@@ -396,7 +357,7 @@
 
   /* global subrs */
   {
-    assert (cff2->topDict + plan.offsets.topDictSize == c.head - c.start);
+    assert (cff2->topDict + plan.offsets.topDictInfo.size == c.head - c.start);
     CFF2Subrs *dest = c.start_embed<CFF2Subrs> ();
     if (unlikely (dest == nullptr)) return false;
     if (unlikely (!dest->serialize (&c, *acc.globalSubrs, plan.offsets.globalSubrsInfo.offSize, plan.subrRefMaps.global_map)))
@@ -450,7 +411,7 @@
     assert (plan.offsets.FDArrayInfo.offset == c.head - c.start);
     CFF2FDArray  *fda = c.start_embed<CFF2FDArray> ();
     if (unlikely (fda == nullptr)) return false;
-    CFF2FontDict_OpSerializer  fontSzr;
+    CFFFontDict_OpSerializer  fontSzr;
     if (unlikely (!fda->serialize (&c, plan.offsets.FDArrayInfo.offSize,
                                    acc.fontDicts, plan.subst_fdcount, plan.fdmap,
                                    fontSzr, plan.privateDictInfos)))
@@ -482,22 +443,14 @@
       if (unlikely (pd == nullptr)) return false;
       unsigned int priv_size = plan.privateDictInfos[plan.fdmap[i]].size;
       bool result;
-      if (plan.drop_hints)
-      {
-        CFF2PrivateDict_OpSerializer_DropHints privSzr_drop;
-        result = pd->serialize (&c, acc.privateDicts[i], privSzr_drop, priv_size);
-      }
-      else
-      {
-        CFF2PrivateDict_OpSerializer privSzr;
-        result = pd->serialize (&c, acc.privateDicts[i], privSzr, priv_size);
-      }
+      CFFPrivateDict_OpSerializer privSzr (plan.drop_hints, plan.flatten_subrs);
+      result = pd->serialize (&c, acc.privateDicts[i], privSzr, priv_size);
       if (unlikely (!result))
       {
         DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 Private Dict[%d]", i);
         return false;
       }
-      if (acc.privateDicts[i].subrsOffset != 0)
+      if (!plan.flatten_subrs && (acc.privateDicts[i].subrsOffset != 0))
       {
         CFF2Subrs *subrs = c.start_embed<CFF2Subrs> ();
         if (unlikely (subrs == nullptr) || acc.privateDicts[i].localSubrs == &Null(CFF2Subrs))