Implemented hb_subset_cff2
authorMichiharu Ariza <ariza@adobe.com>
Wed, 1 Aug 2018 18:30:38 +0000 (11:30 -0700)
committerMichiharu Ariza <ariza@adobe.com>
Wed, 1 Aug 2018 18:30:38 +0000 (11:30 -0700)
Added serialize functions to CFF2 structs
Fixed issues & bugs & code cleanup
Now subsetting CFF2 table outputs something

src/hb-ot-cff-common-private.hh
src/hb-ot-cff2-table.hh
src/hb-private.hh
src/hb-subset-cff2.cc

index 0e035a2..573e421 100644 (file)
@@ -28,6 +28,7 @@
 
 #include "hb-open-type-private.hh"
 #include "hb-ot-layout-common-private.hh"
+#include "hb-subset-plan.hh"
 
 namespace CFF {
 
@@ -132,7 +133,83 @@ enum OpCode {
     OpCode_reserved255 = 255
 };
 
-inline OpCode Make_OpCode_ESC(unsigned char byte2)  { return (OpCode)(OpCode_ESC_Base + byte2); }
+inline OpCode Make_OpCode_ESC (unsigned char byte2)  { return (OpCode)(OpCode_ESC_Base + byte2); }
+inline unsigned int OpCode_Size (OpCode op) { return (op >= OpCode_ESC_Base)? 2: 1; }
+
+/* pair of table offset and length */
+struct offset_size_pair {
+  unsigned int  offset;
+  unsigned int  size;
+};
+
+/* byte string */
+struct UnsizedByteStr : UnsizedArrayOf <HBUINT8>
+{
+  // encode 2-byte int (Dict/CharString) or 4-byte int (Dict)
+  template <typename INTTYPE, int minVal, int maxVal>
+  inline static bool serialize_int (hb_serialize_context_t *c, OpCode intOp, int value)
+  {
+    TRACE_SERIALIZE (this);
+
+    if (unlikely ((value < minVal || value > maxVal)))
+      return_trace (false);
+
+    HBUINT8 *p = c->allocate_size<HBUINT8> (1);
+    if (unlikely (p == nullptr)) return_trace (false);
+    p->set (intOp);
+
+    INTTYPE *ip = c->allocate_size<INTTYPE> (INTTYPE::static_size);
+    if (unlikely (ip == nullptr)) return_trace (false);
+    ip->set ((unsigned int)value);
+
+    return_trace (true);
+  }
+  
+  inline static bool serialize_int4 (hb_serialize_context_t *c, int value)
+  { return serialize_int<HBUINT32, 0, 0x7FFFFFFF> (c, OpCode_longint, value); }
+  
+  inline static bool serialize_int2 (hb_serialize_context_t *c, int value)
+  { return serialize_int<HBUINT16, 0, 0x7FFF> (c, OpCode_shortint, value); }
+};
+
+struct ByteStr
+{
+  ByteStr (const UnsizedByteStr& s, unsigned int l)
+    : str (&s), len (l) {}
+  ByteStr (const char *s=nullptr, unsigned int l=0)
+    : str ((const UnsizedByteStr *)s), len (l) {}
+  /* sub-string */
+  ByteStr (const ByteStr &bs, unsigned int offset, unsigned int l)
+  {  
+    str = (const UnsizedByteStr *)&bs.str[offset];
+    len = l;
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const { return str->sanitize (c, len); }
+
+  inline const HBUINT8& operator [] (unsigned int i) const {
+    assert (str && (i < len));
+    return (*str)[i];
+  }
+
+  inline bool check_limit (unsigned int offset, unsigned int count) const
+  { return (offset + count <= len); }
+
+  const UnsizedByteStr *str;
+  unsigned int len;
+};
+
+inline unsigned int calcOffSize(unsigned int offset)
+{
+  unsigned int size = 1;
+  while ((offset & ~0xFF) != 0)
+  {
+    size++;
+    offset >>= 8;
+  }
+  assert (size <= 4);
+  return size;
+}
 
 /* CFF INDEX */
 struct Index
@@ -145,9 +222,69 @@ struct Index
                           c->check_array (data_base (), 1, max_offset () - 1)));
   }
 
-  inline unsigned int offset_array_size (void) const
+  inline static unsigned int calculate_offset_array_size (unsigned int offSize, unsigned int count)
   { return offSize * (count + 1); }
 
+  inline unsigned int offset_array_size (void) const
+  { return calculate_offset_array_size (offSize, count); }
+
+  inline static unsigned int calculate_serialized_size (unsigned int offSize, unsigned int count, unsigned int dataSize)
+  { return min_size + calculate_offset_array_size (offSize, count) + dataSize; }
+
+  inline bool serialize (hb_serialize_context_t *c, const Index &src)
+  {
+    TRACE_SANITIZE (this);
+    unsigned int size = src.get_size ();
+    Index *dest = c->allocate_size<Index> (size);
+    if (unlikely (dest == nullptr)) return_trace (false);
+    memcpy (dest, &src, size);
+    return_trace (true);
+  }
+
+  inline bool serialize (hb_serialize_context_t *c,
+                         unsigned int offSize,
+                         const hb_vector_t<ByteStr> &bytesArray)
+  {
+    TRACE_SERIALIZE (this);
+
+    /* serialize Index header */
+    if (unlikely (!c->extend_min (*this))) return_trace (false);
+    this->count.set (bytesArray.len);
+    this->offSize.set (offSize);
+    if (!unlikely (c->allocate_size<HBUINT8> (offSize * (bytesArray.len + 1))))
+      return_trace (false);
+  
+    /* serialize indices */
+    unsigned int  offset = 1;
+    for (unsigned int i = 0; i < bytesArray.len; i++)
+    {
+      set_offset_at (i, offset);
+      offset += bytesArray[i].len;
+    }
+
+    /* serialize data */
+    for (unsigned int i = 0; i < bytesArray.len; i++)
+    {
+      HBUINT8 *dest = c->allocate_size<HBUINT8> (bytesArray[i].len);
+      if (dest == nullptr)
+        return_trace (false);
+      memcpy (dest, &bytesArray[i].str[0], bytesArray[i].len);
+    }
+    return_trace (true);
+  }
+
+  inline void set_offset_at (unsigned int index, unsigned int offset)
+  {
+    HBUINT8 *p = offsets + offSize * index + offSize;
+    unsigned int size = offSize;
+    for (; size; size--)
+    {
+      --p;
+      p->set (offset & 0xFF);
+      offset >>= 8;
+    }
+  }
+
   inline const unsigned int offset_at (unsigned int index) const
   {
     const HBUINT8 *p = offsets + offSize * index;
@@ -167,20 +304,21 @@ struct Index
   inline unsigned int data_size (void) const
   { return HBINT8::static_size; };
 
-  inline hb_bytes_t operator [] (unsigned int index) const
+  inline ByteStr operator [] (unsigned int index) const
   {
     if (likely (index < count))
-    {
-      hb_bytes_t  b;
-      b.bytes = data_base () + offset_at (index) - 1;
-      b.len = offset_at (index + 1) - offset_at (index);
-      return b;
-    }
-    return Null(hb_bytes_t);
+      return ByteStr (data_base () + offset_at (index) - 1, offset_at (index + 1) - offset_at (index));
+    else
+      return Null(ByteStr);
   }
 
   inline unsigned int get_size (void) const
-  { return count.static_size + offSize.static_size + offset_array_size () + (offset_at (count) - 1); }
+  {
+    if (this != &Null(Index))
+      return count.static_size + offSize.static_size + offset_array_size () + (offset_at (count) - 1);
+    else
+      return 0;
+  }
 
   protected:
   inline unsigned int max_offset (void) const
@@ -200,43 +338,185 @@ struct Index
   HBUINT8   offsets[VAR]; /* The array of (count + 1) offsets into objects array (1-base). */
   /* HBUINT8 data[VAR];      Object data */
   public:
-  DEFINE_SIZE_MIN (6);
+  DEFINE_SIZE_ARRAY (5, offsets);
 };
 
 template <typename Type>
 struct IndexOf : Index
 {
-  inline const Type& operator [] (unsigned int index) const
+  inline ByteStr operator [] (unsigned int index) const
   {
     if (likely (index < count))
-      return StructAtOffset<Type>(data_base (), offset_at (index) - 1);
-    return Null(Type);
+      return ByteStr (data_base () + offset_at (index) - 1, length_at (index));
+    return Null(ByteStr);
   }
 };
 
-struct UnsizedByteStr : UnsizedArrayOf <HBUINT8> {};
+/* an operator prefixed by its operands in a byte string */
+struct OpStr
+{
+  OpCode  op;
+  ByteStr str;
+  bool    update;
+};
 
-struct ByteStr {
-  ByteStr (const UnsizedByteStr& s, unsigned int l)
-    : str (s), len (l) {}
+typedef hb_vector_t <OpStr> OpStrs;
 
-  inline bool sanitize (hb_sanitize_context_t *c) const { return str.sanitize (c, len); }
+/* base param type for dict parsing */
+struct DictValues
+{
+  inline void init (void)
+  {
+    opStart = 0;
+    opStrs.init ();
+  }
 
-  inline const HBUINT8& operator [] (unsigned int i) const { return str[i]; }
+  inline void fini (void)
+  {
+    opStrs.fini ();
+  }
 
-  inline bool check_limit (unsigned int offset, unsigned int count) const
-  { return (offset + count <= len); }
+  void pushOpStr (OpCode op, const ByteStr& str, unsigned int offset, bool update)
+  {
+    OpStr *opstr = opStrs.push ();
+    opstr->op = op;
+    opstr->str = ByteStr (str, opStart, offset - opStart);
+    opstr->update = update;
+    opStart = offset;
+  }
 
-  const UnsizedByteStr& str;
-  const unsigned int len;
+  unsigned int  opStart;
+  OpStrs        opStrs;
+};
+
+/* base of OP_SERIALIZER */
+struct OpSerializer
+{
+  protected:
+  inline bool copy_opstr (hb_serialize_context_t *c, const OpStr& opstr) const
+  {
+    TRACE_SERIALIZE (this);
+
+    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);
+  }
 };
 
 /* Top Dict, Font Dict, Private Dict */
-typedef UnsizedByteStr Dict;
+struct Dict : UnsizedByteStr
+{
+  template <typename OP_SERIALIZER, typename PARAM>
+  inline bool serialize (hb_serialize_context_t *c,
+                        const DictValues &values,
+                        OP_SERIALIZER& opszr,
+                        PARAM& param)
+  {
+    TRACE_SERIALIZE (this);
+    for (unsigned int i = 0; i < values.opStrs.len; i++)
+    {
+      if (unlikely (!opszr.serialize (c, values.opStrs[i], param)))
+        return_trace (false);
+    }
+    return_trace (true);
+  }
 
-typedef Index CharStrings;
-typedef Index Subrs;
-typedef IndexOf<Dict> FDArray;
+  /* in parallel to above */
+  template <typename OP_SERIALIZER>
+  inline static unsigned int calculate_serialized_size (const DictValues &values,
+                                                        OP_SERIALIZER& opszr)
+  {
+    unsigned int size = 0;
+    for (unsigned int i = 0; i < values.opStrs.len; i++)
+      size += opszr.calculate_serialized_size (values.opStrs[i]);
+    return size;
+  }
+
+  template <typename INTTYPE, int minVal, int maxVal>
+  inline static bool serialize_offset_op (hb_serialize_context_t *c, OpCode op, int value, OpCode intOp)
+  {
+    if (value == 0)
+      return true;
+    // XXX: not sure why but LLVM fails to compile the following 'unlikely' macro invocation
+    if (/*unlikely*/ (!serialize_int<INTTYPE, minVal, maxVal> (c, intOp, value)))
+      return false;
+
+    TRACE_SERIALIZE (this);
+    /* serialize the opcode */
+    HBUINT8 *p = c->allocate_size<HBUINT8> ((op >= OpCode_ESC_Base)? 2: 1);
+    if (unlikely (p == nullptr)) return_trace (false);
+    if (op >= OpCode_ESC_Base)
+    {
+      p->set (OpCode_escape);
+      op = (OpCode)(op - OpCode_ESC_Base);
+      p++;
+    }
+    p->set (op);
+    return_trace (true);
+  }
+
+  inline static bool serialize_offset4_op (hb_serialize_context_t *c, OpCode op, int value)
+  { return serialize_offset_op<HBUINT32, 0, 0x7FFFFFFF> (c, op, value, OpCode_longint); }
+
+  inline static bool serialize_offset2_op (hb_serialize_context_t *c, OpCode op, int value)
+  { return serialize_offset_op<HBUINT16, 0, 0x7FFF> (c, op, value, OpCode_shortint); }
+};
+
+struct TopDict : Dict {};
+struct FontDict : Dict {};
+struct PrivateDict : Dict {};
+
+struct FDArray : IndexOf<FontDict>
+{
+  template <typename DICTVAL, typename OP_SERIALIZER>
+  inline bool serialize (hb_serialize_context_t *c,
+                        unsigned int offSize,
+                        const hb_vector_t<DICTVAL> &fontDicts,
+                        OP_SERIALIZER& opszr,
+                        const hb_vector_t<offset_size_pair> &privatePairs)
+  {
+    TRACE_SERIALIZE (this);
+    if (unlikely (!c->extend_min (*this))) return_trace (false);
+    this->count.set (fontDicts.len);
+    this->offSize.set (offSize);
+    if (!unlikely (c->allocate_size<HBUINT8> (offSize * (fontDicts.len + 1))))
+      return_trace (false);
+    
+    /* serialize font dict offsets */
+    unsigned int  offset = 1;
+    unsigned int  i;
+    for (i = 0; i < fontDicts.len; i++)
+    {
+      set_offset_at (i, offset);
+      offset += FontDict::calculate_serialized_size (fontDicts[i], opszr);
+    }
+    set_offset_at (i, offset);
+
+    /* serialize font dicts */
+    for (unsigned int i = 0; i < fontDicts.len; i++)
+    {
+      FontDict *dict = c->start_embed<FontDict> ();
+      if (unlikely (!dict->serialize (c, fontDicts[i], opszr, privatePairs[i])))
+        return_trace (false);
+    }
+    return_trace (true);
+  }
+  
+  /* in parallel to above */
+  template <typename OP_SERIALIZER, typename DICTVAL>
+  inline static unsigned int calculate_serialized_size (unsigned int &offSize /* OUT */,
+                                                        const hb_vector_t<DICTVAL> &fontDicts,
+                                                        OP_SERIALIZER& opszr)
+  {
+    unsigned int dictsSize = 0;
+    for (unsigned int i = 0; i < fontDicts.len; i++)
+      dictsSize += FontDict::calculate_serialized_size (fontDicts[i], opszr);
+
+    offSize = calcOffSize (dictsSize + 1);
+    return Index::calculate_serialized_size (offSize, fontDicts.len, dictsSize);
+  }
+};
 
 /* FDSelect */
 struct FDSelect0 {
@@ -246,6 +526,9 @@ struct FDSelect0 {
     return_trace (likely (c->check_struct (this) && fds[c->get_num_glyphs () - 1].sanitize (c)));
   }
 
+  inline unsigned int get_size (unsigned int num_glyphs) const
+  { return HBUINT8::static_size * num_glyphs; }
+
   HBUINT8     fds[VAR];
 
   DEFINE_SIZE_MIN (1);
@@ -265,6 +548,9 @@ struct FDSelect3_Range {
 };
 
 struct FDSelect3 {
+  inline unsigned int get_size (void) const
+  { return HBUINT16::static_size * 2 + FDSelect3_Range::static_size * nRanges; }
+
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -283,10 +569,34 @@ struct FDSelect {
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
+
     return_trace (likely (c->check_struct (this) && (format == 0 || format == 3) &&
                           (format == 0)? u.format0.sanitize (c): u.format3.sanitize (c)));
   }
 
+  inline bool serialize (hb_serialize_context_t *c, const FDSelect &src, unsigned int num_glyphs)
+  {
+    TRACE_SANITIZE (this);
+    unsigned int size = src.get_size (num_glyphs);
+    FDSelect *dest = c->allocate_size<FDSelect> (size);
+    if (unlikely (dest == nullptr)) return_trace (false);
+    memcpy (dest, &src, size);
+    return_trace (true);
+  }
+
+  inline unsigned int calculate_serialized_size (unsigned int num_glyphs) const
+  { return get_size (num_glyphs); }
+
+  inline unsigned int get_size (unsigned int num_glyphs) const
+  {
+    unsigned int size = format.static_size;
+    if (format == 0)
+      size += u.format0.get_size (num_glyphs);
+    else if (likely (format == 3))
+      size += u.format3.get_size ();
+    return size;
+  }
+
   HBUINT8       format;
   union {
     FDSelect0   format0;
@@ -296,6 +606,9 @@ struct FDSelect {
   DEFINE_SIZE_MIN (2);
 };
 
+typedef Index CharStrings;
+typedef Index Subrs;
+
 inline float parse_bcd (const ByteStr& str, unsigned int& offset, float& v)
 {
   // XXX: TODO
@@ -430,71 +743,75 @@ inline bool check_pop_offset (Stack& stack, Offset& offset)
 template <typename OpSet, typename Param>
 struct Interpreter {
 
-  inline void init ()
+  inline Interpreter (void)
   {
     stack.init ();
   }
   
-  inline void fini ()
+  inline ~Interpreter (void)
   {
     stack.fini ();
   }
 
   inline bool interpret (const ByteStr& str, Param& param)
   {
-    param.init();
+    param.init ();
 
     for (unsigned int i = 0; i < str.len; i++)
     {
-      unsigned char byte = str[i];
-      if ((OpCode_shortint == byte) ||
-          (OpCode_OneByteIntFirst <= byte && OpCode_TwoByteNegInt3 >= byte))
+      OpCode op = (OpCode)(unsigned char)str[i];
+      if ((OpCode_shortint == op) ||
+          (OpCode_OneByteIntFirst <= op && OpCode_TwoByteNegInt3 >= op))
       {
-        if (unlikely (!process_intop (str, i, byte)))
+        if (unlikely (!process_intop (str, i, op)))
           return false;
       } else {
-        if (byte == OpCode_escape) {
+        if (op == OpCode_escape) {
           if (unlikely (!str.check_limit (i, 1)))
             return false;
-          byte = Make_OpCode_ESC(str[++i]);
+          op = Make_OpCode_ESC(str[++i]);
         }
 
-        if (unlikely (!OpSet::process_op (str, i, byte, stack, param)))
+        if (unlikely (!OpSet::process_op (str, i, op, stack, param)))
           return false;
       }
     }
+    
     return true;
   }
 
-  inline bool process_intop (const ByteStr& str, unsigned int& offset, unsigned char byte)
+  inline bool process_intop (const ByteStr& str, unsigned int& offset, OpCode op)
   {
-    switch (byte) {
+    switch (op) {
       case OpCode_TwoBytePosInt0: case OpCode_TwoBytePosInt1:
       case OpCode_TwoBytePosInt2: case OpCode_TwoBytePosInt3:
-        if (unlikely (str.check_limit (offset, 2) || !stack.check_overflow (1)))
+        if (unlikely (!str.check_limit (offset, 2) || !stack.check_overflow (1)))
           return false;
-        stack.push ((int16_t)((byte - OpCode_TwoBytePosInt0) * 256 + str[offset + 1] + 108));
+        stack.push ((int16_t)((op - OpCode_TwoBytePosInt0) * 256 + str[offset + 1] + 108));
+        offset++;
         break;
       
       case OpCode_TwoByteNegInt0: case OpCode_TwoByteNegInt1:
       case OpCode_TwoByteNegInt2: case OpCode_TwoByteNegInt3:
         if (unlikely (!str.check_limit (offset, 2) || !stack.check_overflow (1)))
           return false;
-        stack.push ((int16_t)(-(byte - OpCode_TwoByteNegInt0) * 256 - str[offset + 1] - 108));
+        stack.push ((int16_t)(-(op - OpCode_TwoByteNegInt0) * 256 - str[offset + 1] - 108));
+        offset++;
         break;
       
       case OpCode_shortint: /* 3-byte integer */
         if (unlikely (!str.check_limit (offset, 3) || !stack.check_overflow (1)))
           return false;
-        stack.push ((int16_t)((str[offset + 1] << 8) | str[offset + 2]));
+        stack.push ((int16_t)*(const HBUINT16*)&str[offset + 1]);
+        offset += 2;
         break;
       
       default:
         /* 1-byte integer */
-        if (likely ((OpCode_OneByteIntFirst <= byte) && (byte <= OpCode_OneByteIntLast)) &&
+        if (likely ((OpCode_OneByteIntFirst <= op) && (op <= OpCode_OneByteIntLast)) &&
             likely (stack.check_overflow (1)))
         {
-          stack.push ((int)byte - 139);
+          stack.push ((int)op - 139);
         } else {
           return false;
         }
@@ -507,6 +824,23 @@ struct Interpreter {
   Stack stack;
 };
 
+/* used by subsettter */
+struct SubTableOffsets {
+  inline SubTableOffsets (void)
+  {
+    memset (this, 0, sizeof(*this));
+  }
+
+  unsigned int  topDictSize;
+  unsigned int  varStoreOffset;
+  unsigned int  FDSelectOffset;
+  unsigned int  FDArrayOffset;
+  unsigned int  FDArrayOffSize;
+  unsigned int  charStringsOffset;
+  unsigned int  charStringsOffSize;
+  unsigned int  privateDictsOffset;
+};
+
 } /* namespace CFF */
 
 #endif /* HB_OT_CFF_COMMON_PRIVATE_HH */
index 5abfc78..fadd66f 100644 (file)
@@ -38,56 +38,106 @@ namespace CFF {
  */
 #define HB_OT_TAG_cff2 HB_TAG('C','F','F','2')
 
-struct CFF2TopDictValues
+struct CFF2VariationStore
 {
-  inline void init ()
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this)) && varStore.sanitize (c));
+  }
+
+  inline bool serialize (hb_serialize_context_t *c, const CFF2VariationStore *varStore)
   {
+    TRACE_SANITIZE (this);
+    CFF2VariationStore *dest = c->allocate_size<CFF2VariationStore> (varStore->size);
+    if (unlikely (dest == nullptr)) return_trace (false);
+    memcpy (dest, varStore, size);
+    return_trace (true);
+  }
+
+  inline unsigned int get_size (void) const { return size; }
+
+  HBUINT16        size;
+  VariationStore  varStore;
+
+  DEFINE_SIZE_MIN (2 + VariationStore::min_size);
+};
+
+struct CFF2TopDictValues : DictValues
+{
+  inline void init (void)
+  {
+    DictValues::init ();
     charStringsOffset.set (0);
     vstoreOffset.set (0);
     FDArrayOffset.set (0);
     FDSelectOffset.set (0);
+  }
+
+  inline void fini (void)
+  {
+    DictValues::fini ();
     FontMatrix[0] = FontMatrix[3] = 0.001f;
     FontMatrix[1] = FontMatrix[2] = FontMatrix[4] = FontMatrix[5] = 0.0f;
   }
 
-  LOffsetTo<CharStrings>    charStringsOffset;
-  LOffsetTo<VariationStore> vstoreOffset;
-  LOffsetTo<FDArray>        FDArrayOffset;
-  LOffsetTo<FDSelect>       FDSelectOffset;
+  inline unsigned int calculate_serialized_size (void) const
+  {
+    unsigned int size = 0;
+    for (unsigned int i = 0; i < opStrs.len; i++)
+    {
+      OpCode op = opStrs[i].op;
+      if (op == OpCode_FontMatrix)
+        size += opStrs[i].str.len;
+      else
+        size += OpCode_Size (OpCode_longint) + 4 + OpCode_Size (op);
+    }
+    return size;
+  }
+
   float     FontMatrix[6];
+  LOffsetTo<CharStrings>        charStringsOffset;
+  LOffsetTo<CFF2VariationStore> vstoreOffset;
+  LOffsetTo<FDArray>            FDArrayOffset;
+  LOffsetTo<FDSelect>           FDSelectOffset;
 };
 
 struct CFF2TopDictOpSet
 {
-  static inline bool process_op (const ByteStr& str, unsigned int& offset, unsigned char byte, Stack& stack, CFF2TopDictValues& val)
+  static inline bool process_op (const ByteStr& str, unsigned int& offset, OpCode op, Stack& stack, CFF2TopDictValues& val)
   {
-    switch (byte) {
+    switch (op) {
       case OpCode_CharStrings:
         if (unlikely (!check_pop_offset (stack, val.charStringsOffset)))
           return false;
+        val.pushOpStr (op, str, offset + 1, true);
         break;
       case OpCode_vstore:
         if (unlikely (!check_pop_offset (stack, val.vstoreOffset)))
           return false;
+        val.pushOpStr (op, str, offset + 1, true);
         break;
       case OpCode_FDArray:
         if (unlikely (!check_pop_offset (stack, val.FDArrayOffset)))
           return false;
+        val.pushOpStr (op, str, offset + 1, true);
         break;
       case OpCode_FDSelect:
         if (unlikely (!check_pop_offset (stack, val.FDSelectOffset)))
           return false;
+        val.pushOpStr (op, str, offset + 1, true);
         break;
       case OpCode_FontMatrix:
         if (unlikely (!stack.check_underflow (6)))
           return false;
         for (int i = 0; i < 6; i++)
           val.FontMatrix[i] = stack.pop ().to_real ();
+        val.pushOpStr (op, str, offset + 1, false);
         break;
       case OpCode_longint:  /* 5-byte integer */
         if (unlikely (!str.check_limit (offset, 5) || !stack.check_overflow (1)))
           return false;
-        stack.push ((int32_t)((str[offset + 1] << 24) | (str[offset + 2] << 16) | (str[offset + 3] << 8) | str[offset + 4]));
+        stack.push ((int32_t)*(const HBUINT32*)&str[offset + 1]);
         offset += 4;
         break;
       
@@ -103,37 +153,46 @@ struct CFF2TopDictOpSet
         stack.clear ();
         return false;
     }
+
     return true;
   }
 };
 
-struct CFF2FontDictValues
+struct CFF2FontDictValues : DictValues
 {
-  inline void init ()
+  inline void init (void)
   {
+    DictValues::init ();
     privateDictSize = 0;
     privateDictOffset.set (0);
   }
 
-  unsigned int    privateDictSize;
-  OffsetTo<Dict>  privateDictOffset;
+  inline void fini (void)
+  {
+    DictValues::fini ();
+  }
+
+  unsigned int            privateDictSize;
+  LOffsetTo<PrivateDict>  privateDictOffset;
 };
 
 struct CFF2FontDictOpSet
 {
-  static inline bool process_op (const ByteStr& str, unsigned int& offset, unsigned char byte, Stack& stack, CFF2FontDictValues& val)
+  static inline bool process_op (const ByteStr& str, unsigned int& offset, OpCode op, Stack& stack, CFF2FontDictValues& val)
   {
-    switch (byte) {
+    switch (op) {
       case OpCode_Private:
-        if (unlikely (!stack.check_pop_uint (val.privateDictSize)))
-          return false;
         if (unlikely (!check_pop_offset (stack, val.privateDictOffset)))
           return false;
+        if (unlikely (!stack.check_pop_uint (val.privateDictSize)))
+          return false;
+        val.pushOpStr (op, str, offset + 1, true);
         break;
       case OpCode_longint:  /* 5-byte integer */
         if (unlikely (!str.check_limit (offset, 5) || !stack.check_overflow (1)))
           return false;
-        stack.push ((int32_t)((str[offset + 1] << 24) | (str[offset + 2] << 16) || (str[offset + 3] << 8) || str[offset + 4]));
+        stack.push ((int32_t)((str[offset + 1] << 24) | ((uint32_t)str[offset + 2] << 16) | ((uint32_t)str[offset + 3] << 8) | str[offset + 4]));
+        offset += 4;
         break;
       case OpCode_BCD:  /* real number */
         float v;
@@ -147,14 +206,17 @@ struct CFF2FontDictOpSet
         stack.clear ();
         return false;
     }
+
     return true;
   }
 };
 
-struct CFF2PrivateDictValues
+struct CFF2PrivateDictValues : DictValues
 {
-  inline void init ()
+  inline void init (void)
   {
+    DictValues::init ();
+
     languageGroup = 0;
     expansionFactor = 0.06f;
     vsIndex = 0;
@@ -164,15 +226,18 @@ struct CFF2PrivateDictValues
     blueFuzz = 1.0f;
     stdHW = UNSET_REAL_VALUE;
     stdVW = UNSET_REAL_VALUE;
+    subrsOffset.set (0);
     blueValues.init ();
     otherBlues.init ();
     familyBlues.init ();
     familyOtherBlues.init ();
     stemSnapH.init ();
     stemSnapV.init ();
+
+    localSubrs = &Null(Subrs);
   }
 
-  inline void fini ()
+  inline void fini (void)
   {
     blueValues.fini ();
     otherBlues.fini ();
@@ -180,6 +245,19 @@ struct CFF2PrivateDictValues
     familyOtherBlues.fini ();
     stemSnapH.fini ();
     stemSnapV.fini ();
+
+    DictValues::fini ();
+  }
+
+  inline unsigned int calculate_serialized_size (void) const
+  {
+    unsigned int size = 0;
+    for (unsigned int i = 0; i < opStrs.len; i++)
+      if (opStrs[i].op == OpCode_Subrs)
+        size += OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs);
+      else
+        size += opStrs[i].str.len;
+    return size;
   }
 
   int       languageGroup;
@@ -197,13 +275,15 @@ struct CFF2PrivateDictValues
   hb_vector_t <float> familyOtherBlues;
   hb_vector_t <float> stemSnapH;
   hb_vector_t <float> stemSnapV;
+
+  const Subrs *localSubrs;
 };
 
 struct CFF2PrivateDictOpSet
 {
-  static inline bool process_op (const ByteStr& str, unsigned int& offset, unsigned char byte, Stack& stack, CFF2PrivateDictValues& val)
+  static inline bool process_op (const ByteStr& str, unsigned int& offset, OpCode op, Stack& stack, CFF2PrivateDictValues& val)
   {
-    switch (byte) {
+    switch (op) {
       case OpCode_BlueValues:
         if (unlikely (!stack.check_pop_delta (val.blueValues, true)))
           return false;
@@ -263,10 +343,26 @@ struct CFF2PrivateDictOpSet
       case OpCode_blend:
         // XXX: TODO
         break;
+      case OpCode_longint:  /* 5-byte integer */
+        if (unlikely (!str.check_limit (offset, 5) || !stack.check_overflow (1)))
+          return false;
+        stack.push ((int32_t)((str[offset + 1] << 24) | (str[offset + 2] << 16) || (str[offset + 3] << 8) || str[offset + 4]));
+        offset += 4;
+        break;
+      case OpCode_BCD:  /* real number */
+        float v;
+        if (unlikely (!stack.check_overflow (1) || !parse_bcd (str, offset, v)))
+          return false;
+        stack.push (v);
+        break;
 
       default:
         return false;
     }
+
+    if (op != OpCode_blend)
+      val.pushOpStr (op, str, offset + 1, op == OpCode_Subrs);
+
     return true;
   }
 };
@@ -296,8 +392,15 @@ struct cff2
   {
     inline void init (hb_face_t *face)
     {
-      hb_sanitize_context_t c;
-      this->blob = c.reference_table<cff2> (face);
+      fontDicts.init ();
+      privateDicts.init ();
+      
+      this->blob = sc.reference_table<cff2> (face);
+
+      /* setup for run-time santization */
+      sc.init (this->blob);
+      sc.start_processing ();
+      
       const OT::cff2 *cff2 = this->blob->as<OT::cff2> ();
 
       if (cff2 == &Null(OT::cff2))
@@ -308,45 +411,77 @@ struct cff2
 
       { /* parse top dict */
         ByteStr topDictStr (cff2 + cff2->topDict, cff2->topDictSize);
-        if (unlikely (!topDictStr.sanitize (&c)))
+        CFF2TopDict_Interpreter top_interp;
+        if (unlikely (!topDictStr.sanitize (&sc) ||
+                      !top_interp.interpret (topDictStr, top)))
         {
           fini ();
           return;
         }
-        CFF2TopDict_Interpreter top_interp;
-        top_interp.init ();
-        top_interp.interpret (topDictStr, top);
-        top_interp.fini ();
       }
       
-      varStore = &OT::StructAtOffset<VariationStore>(cff2, top.vstoreOffset);
-      charStrings = &OT::StructAtOffset<CharStrings>(cff2, top.charStringsOffset);
-      fdArray = &OT::StructAtOffset<FDArray>(cff2, top.FDArrayOffset);
-      fdSelect = &OT::StructAtOffset<FDSelect>(cff2, top.FDSelectOffset);
+      globalSubrs = &OT::StructAtOffset<Subrs> (cff2, cff2->topDict + cff2->topDictSize);
+      varStore = &top.vstoreOffset (cff2);
+      charStrings = &top.charStringsOffset (cff2);
+      fdArray = &top.FDArrayOffset (cff2);
+      fdSelect = &top.FDSelectOffset (cff2);
       
-      if (((varStore != &Null(VariationStore)) && unlikely (!varStore->sanitize (&c))) ||
-          ((charStrings == &Null(CharStrings)) || unlikely (!charStrings->sanitize (&c))) ||
-          ((fdArray == &Null(FDArray)) || unlikely (!fdArray->sanitize (&c))) ||
-          ((fdSelect == &Null(FDSelect)) || unlikely (!fdSelect->sanitize (&c))))
+      if (((varStore != &Null(CFF2VariationStore)) && unlikely (!varStore->sanitize (&sc))) ||
+          ((charStrings == &Null(CharStrings)) || unlikely (!charStrings->sanitize (&sc))) ||
+          ((fdArray == &Null(FDArray)) || unlikely (!fdArray->sanitize (&sc))) ||
+          ((fdSelect != &Null(FDSelect)) && unlikely (!fdSelect->sanitize (&sc))))
       {
         fini ();
         return;
       }
 
       num_glyphs = charStrings->count;
-      if (num_glyphs != c.get_num_glyphs ())
+      if (num_glyphs != sc.get_num_glyphs ())
       {
         fini ();
         return;
       }
+
+      privateDicts.resize (fdArray->count);
+
+      // parse font dicts and gather private dicts
+      for (unsigned int i = 0; i < fdArray->count; i++)
+      {
+        const ByteStr fontDictStr = (*fdArray)[i];
+        CFF2FontDictValues  *font;
+        CFF2FontDict_Interpreter font_interp;
+        font = fontDicts.push ();
+        if (unlikely (!fontDictStr.sanitize (&sc) ||
+                      !font_interp.interpret (fontDictStr, *font)))
+        {
+          fini ();
+          return;
+        }
+
+        const ByteStr privDictStr (font->privateDictOffset (cff2), font->privateDictSize);
+        CFF2PrivateDict_Interpreter priv_interp;
+        if (unlikely (!privDictStr.sanitize (&sc) ||
+                      !priv_interp.interpret (privDictStr, privateDicts[i])))
+        {
+          fini ();
+          return;
+        }
+
+        privateDicts[i].localSubrs = &privateDicts[i].subrsOffset (cff2);
+      }
     }
 
     inline void fini (void)
     {
+      sc.end_processing ();
+      fontDicts.fini ();
+      privateDicts.fini ();
       hb_blob_destroy (blob);
       blob = nullptr;
     }
 
+    inline bool is_valid (void) const { return blob != nullptr; }
+
     inline bool get_extents (hb_codepoint_t glyph,
            hb_glyph_extents_t *extents) const
     {
@@ -359,12 +494,18 @@ struct cff2
 
     private:
     hb_blob_t               *blob;
+    hb_sanitize_context_t   sc;
 
-    CFF2TopDictValues       top;
-    const VariationStore    *varStore;
-    const CharStrings       *charStrings;
-    const FDArray           *fdArray;
-    const FDSelect          *fdSelect;
+    public:
+    CFF2TopDictValues         top;
+    const Subrs               *globalSubrs;
+    const CFF2VariationStore  *varStore;
+    const CharStrings         *charStrings;
+    const FDArray             *fdArray;
+    const FDSelect            *fdSelect;
+
+    hb_vector_t<CFF2FontDictValues>     fontDicts;
+    hb_vector_t<CFF2PrivateDictValues>  privateDicts;
 
     unsigned int            num_glyphs;
   };
@@ -384,10 +525,10 @@ struct cff2
     return success;
   }
 
-  protected:
-  FixedVersion<HBUINT8> version;    /* Version of CFF2 table. set to 0x0200u */
-  OffsetTo<Dict, HBUINT8> topDict;  /* headerSize = Offset to Top DICT. */
-  HBUINT16       topDictSize;       /* Top DICT size */
+  public:
+  FixedVersion<HBUINT8> version;        /* Version of CFF2 table. set to 0x0200u */
+  OffsetTo<TopDict, HBUINT8> topDict;   /* headerSize = Offset to Top DICT. */
+  HBUINT16       topDictSize;           /* Top DICT size */
 
   public:
   DEFINE_SIZE_STATIC (5);
index a0bd994..683eb12 100644 (file)
@@ -377,7 +377,7 @@ typedef uint64_t hb_vector_size_impl_t;
 
 /* Global nul-content Null pool.  Enlarge as necessary. */
 
-#define HB_NULL_POOL_SIZE 264
+#define HB_NULL_POOL_SIZE 800
 
 extern HB_INTERNAL
 hb_vector_size_impl_t const _hb_NullPool[(HB_NULL_POOL_SIZE + sizeof (hb_vector_size_impl_t) - 1) / sizeof (hb_vector_size_impl_t)];
index 66681e9..8c79ac2 100644 (file)
 #include "hb-subset-cff2.hh"
 #include "hb-subset-plan.hh"
 
-static bool
-_calculate_cff2_prime_size (const OT::cff2::accelerator_t &cff2,
-                                     hb_vector_t<hb_codepoint_t> &glyph_ids,
-                                     hb_bool_t drop_hints,
-                                     unsigned int *cff2_size /* OUT */,
-                                     hb_vector_t<unsigned int> *instruction_ranges /* OUT */)
+using namespace CFF;
+
+struct CFF2TopDict_OpSerializer : OpSerializer
 {
-  // XXX: TODO
-  *cff2_size = 0;
-  return true;
-}
+  inline bool serialize (hb_serialize_context_t *c,
+                         const OpStr &opstr,
+                         const SubTableOffsets &offsets) const
+  {
+    TRACE_SERIALIZE (this);
 
-static bool
-_write_cff2_prime (hb_subset_plan_t              *plan,
-          const OT::cff2::accelerator_t &cff2,
-                            const char                    *cff2_data,
-                            hb_vector_t<unsigned int> &instruction_ranges,
-                            unsigned int                   cff2_prime_size,
-                            char                          *cff2_prime_data /* OUT */)
+    switch (opstr.op)
+    {
+      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.charStringsOffset));
+
+      case OpCode_FDArray:
+        return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.FDArrayOffset));
+
+      case OpCode_FDSelect:
+        return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.FDSelectOffset));
+
+      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_vstore:
+      case OpCode_CharStrings:
+      case OpCode_FDArray:
+      case OpCode_FDSelect:
+        return OpCode_Size (OpCode_longint) + 4 + OpCode_Size (opstr.op);
+    
+      default:
+        return opstr.str.len;
+    }
+  }
+};
+
+struct CFF2FontDict_OpSerializer : OpSerializer
+{
+  inline bool serialize (hb_serialize_context_t *c,
+                         const OpStr &opstr,
+                         const offset_size_pair& privOffSize) 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, privOffSize.size)))
+        return_trace (false);
+
+      /* serialize the private dict offset as a 4-byte integer */
+      if (unlikely (!UnsizedByteStr::serialize_int4 (c, privOffSize.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_longint) + 4 + OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Private);
+    else
+      return opstr.str.len;
+  }
+};
+
+struct CFF2PrivateDict_OpSerializer : OpSerializer
+{
+  inline bool serialize (hb_serialize_context_t *c,
+                         const OpStr &opstr,
+                         const unsigned int subrsOffset) const
+  {
+    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));
+  }
+
+  inline unsigned int calculate_serialized_size (const OpStr &opstr) const
+  {
+    if (opstr.op == OpCode_Subrs)
+      return OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs);
+    else
+      return opstr.str.len;
+  }
+};
+
+struct subset_plan {
+  inline subset_plan (void)
+  {
+    subset_charstrings.init ();
+    private_off_and_size_pairs.init ();
+  }
+
+  inline ~subset_plan (void)
+  {
+    subset_charstrings.fini ();
+    private_off_and_size_pairs.fini ();
+  }
+
+  inline bool create (const OT::cff2::accelerator_t &acc,
+              hb_subset_plan_t *plan)
+  {
+    final_size = 0;
+
+    /* CFF2 header */
+    final_size += OT::cff2::static_size;
+    
+    /* top dict */
+    {
+      CFF2TopDict_OpSerializer topSzr;
+      offsets.topDictSize = TopDict::calculate_serialized_size<CFF2TopDict_OpSerializer> (acc.top, topSzr);
+      final_size += offsets.topDictSize;
+    }
+
+    /* global subrs */
+    final_size += acc.globalSubrs->get_size ();
+
+    /* variation store */
+    if (acc.varStore != &Null(CFF2VariationStore))
+    {
+      offsets.varStoreOffset = final_size;
+      final_size += acc.varStore->get_size ();
+    }
+
+    /* FDSelect */
+    if (acc.fdSelect != &Null(FDSelect))
+    {
+      offsets.FDSelectOffset = final_size;
+      final_size += acc.fdSelect->calculate_serialized_size (acc.num_glyphs);
+    }
+
+    /* FDArray (FDIndex) */
+    {
+      offsets.FDArrayOffset = final_size;
+      CFF2FontDict_OpSerializer fontSzr;
+      final_size += FDArray::calculate_serialized_size(offsets.FDArrayOffSize/*OUT*/, acc.fontDicts, fontSzr);
+    }
+
+    /* CharStrings */
+    {
+      offsets.charStringsOffset = final_size;
+      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;
+      }
+      offsets.charStringsOffSize = calcOffSize (dataSize + 1);
+      final_size += CharStrings::calculate_serialized_size (offsets.charStringsOffSize, plan->glyphs.len, dataSize);
+    }
+
+    /* private dicts & local subrs */
+    offsets.privateDictsOffset = final_size;
+    for (unsigned int i = 0; i < acc.fdArray->count; i++)
+    {
+      CFF2PrivateDict_OpSerializer privSzr;
+      unsigned int private_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr);
+      offset_size_pair  pair = { final_size, private_size };
+      private_off_and_size_pairs.push (pair);
+      final_size += private_size + acc.privateDicts[i].localSubrs->get_size ();
+    }
+
+    return true;
+  }
+
+  inline unsigned int get_final_size (void) const  { return final_size; }
+
+  unsigned int    final_size;
+  SubTableOffsets offsets;
+
+  hb_vector_t<ByteStr> subset_charstrings;
+  hb_vector_t<offset_size_pair> private_off_and_size_pairs;
+};
+
+static inline bool _write_cff2 (const subset_plan &plan,
+                                const OT::cff2::accelerator_t  &acc,
+                                unsigned int dest_sz,
+                                void *dest)
 {
-  // XXX: TODO
+  hb_serialize_context_t c (dest, dest_sz);
+
+  TRACE_SERIALIZE (this);
+  OT::cff2 *cff2 = c.start_serialize<OT::cff2> ();
+  if (unlikely (!c.extend_min (*cff2)))
+    return_trace (false);
+
+  /* header */
+  cff2->version.major.set (0x02);
+  cff2->version.minor.set (0x00);
+  cff2->topDict.set (OT::cff2::static_size);
+
+  /* top dict */
+  {
+    assert (cff2->topDict == c.head - c.start);
+    cff2->topDictSize.set (plan.offsets.topDictSize);
+    TopDict &dict = cff2 + cff2->topDict;
+    CFF2TopDict_OpSerializer topSzr;
+    if (unlikely (!dict.serialize (&c, acc.top, topSzr, plan.offsets)))
+    {
+      DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 top dict");
+      return_trace (false);
+    }
+  }
+
+  /* global subrs */
+  {
+    assert (cff2->topDict + plan.offsets.topDictSize == c.head - c.start);
+    Subrs *dest = c.start_embed<Subrs> ();
+    if (unlikely (dest == nullptr)) return_trace (false);;
+    if (unlikely (!dest->serialize (&c, *acc.globalSubrs)))
+    {
+      DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 global subrs");
+      return_trace (false);
+    }
+  }
+  
+  /* variation store */
+  if (acc.varStore != &Null(CFF2VariationStore))
+  {
+    assert (plan.offsets.varStoreOffset == c.head - c.start);
+    CFF2VariationStore *dest = c.start_embed<CFF2VariationStore> ();
+    if (unlikely (!dest->serialize (&c, acc.varStore)))
+    {
+      DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 Variation Store");
+      return_trace (false);
+    }
+  }
+
+  /* FDSelect */
+  if (acc.fdSelect != &Null(FDSelect))
+  {
+    assert (plan.offsets.FDSelectOffset == c.head - c.start);
+    FDSelect *dest = c.start_embed<FDSelect> ();
+    if (unlikely (!dest->serialize (&c, *acc.fdSelect, acc.num_glyphs)))
+    {
+      DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 FDSelect");
+      return_trace (false);
+    }
+  }
+
+  /* FDArray (FD Index) */
+  {
+    assert (plan.offsets.FDArrayOffset == c.head - c.start);
+    FDArray  *fda = c.start_embed<FDArray> ();
+    if (unlikely (fda == nullptr)) return_trace (false);
+    CFF2FontDict_OpSerializer  fontSzr;
+    if (unlikely (!fda->serialize (&c, plan.offsets.FDArrayOffSize, acc.fontDicts, fontSzr, plan.private_off_and_size_pairs)))
+    {
+      DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 FDArray");
+      return_trace (false);
+    }
+  }
+
+  /* CharStrings */
+  {
+    assert (plan.offsets.charStringsOffset == c.head - c.start);
+    CharStrings  *cs = c.start_embed<CharStrings> ();
+    if (unlikely (cs == nullptr)) return_trace (false);
+    if (unlikely (!cs->serialize (&c, plan.offsets.charStringsOffSize, plan.subset_charstrings)))
+    {
+      DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 CharStrings");
+      return_trace (false);
+    }
+  }
+
+  /* private dicts & local subrs */
+  assert (plan.offsets.privateDictsOffset == c.head - c.start);
+  for (unsigned int i = 0; i < acc.privateDicts.len; i++)
+  {
+    PrivateDict  *pd = c.start_embed<PrivateDict> ();
+    if (unlikely (pd == nullptr)) return_trace (false);
+    CFF2PrivateDict_OpSerializer privSzr;
+    if (unlikely (!pd->serialize (&c, acc.privateDicts[i], privSzr, acc.privateDicts[i].subrsOffset)))
+    {
+      DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 Private Dict[%d]", i);
+      return_trace (false);
+    }
+    if (acc.privateDicts[i].subrsOffset != 0)
+    {
+      Subrs *subrs = c.allocate_size<Subrs> (acc.privateDicts[i].localSubrs->get_size ());
+      if (unlikely (subrs == nullptr) || acc.privateDicts[i].localSubrs == &Null(Subrs))
+      {
+        DEBUG_MSG (SUBSET, nullptr, "CFF2 subset: local subrs unexpectedly null [%d]", i);
+        return_trace (false);
+      }
+      if (unlikely (!subrs->serialize (&c, *acc.privateDicts[i].localSubrs)))
+      {
+        DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 local subrs [%d]", i);
+        return_trace (false);
+      }
+    }
+  }
+
+  c.end_serialize ();
+
   return true;
 }
 
 static bool
-_hb_subset_cff2 (const OT::cff2::accelerator_t  &cff2,
-                          const char                     *cff2_data,
-                          hb_subset_plan_t               *plan,
-                          hb_blob_t                     **cff2_prime /* OUT */)
+_hb_subset_cff2 (const OT::cff2::accelerator_t  &acc,
+                const char                      *data,
+                hb_subset_plan_t                *plan,
+                hb_blob_t                       **prime /* OUT */)
 {
-  hb_vector_t<hb_codepoint_t> &glyphs_to_retain = plan->glyphs;
-
-  unsigned int cff2_prime_size;
-  hb_vector_t<unsigned int> instruction_ranges;
-  instruction_ranges.init();
-
-  if (unlikely (!_calculate_cff2_prime_size (cff2,
-                                                      glyphs_to_retain,
-                                                      plan->drop_hints,
-                                                      &cff2_prime_size,
-                                                      &instruction_ranges))) {
-    instruction_ranges.fini();
+  subset_plan cff2_subset_plan;
+
+  if (unlikely (!cff2_subset_plan.create (acc, plan)))
+  {
+    DEBUG_MSG(SUBSET, nullptr, "Failed to generate a cff2 subsetting plan.");
     return false;
   }
 
+  unsigned int  cff2_prime_size = cff2_subset_plan.get_final_size ();
   char *cff2_prime_data = (char *) calloc (1, cff2_prime_size);
-  if (unlikely (!_write_cff2_prime (plan, cff2, cff2_data,
-                                             instruction_ranges,
-                                             cff2_prime_size, cff2_prime_data))) {
+
+  if (unlikely (!_write_cff2 (cff2_subset_plan, acc,
+                              cff2_prime_size, cff2_prime_data))) {
+    DEBUG_MSG(SUBSET, nullptr, "Failed to write a subset cff2.");
     free (cff2_prime_data);
-    instruction_ranges.fini();
     return false;
   }
-  instruction_ranges.fini();
 
-  *cff2_prime = hb_blob_create (cff2_prime_data,
+  *prime = hb_blob_create (cff2_prime_data,
                                 cff2_prime_size,
                                 HB_MEMORY_MODE_READONLY,
                                 cff2_prime_data,
                                 free);
-  // XXX: TODO
   return true;
 }
 
@@ -102,20 +395,21 @@ _hb_subset_cff2 (const OT::cff2::accelerator_t  &cff2,
  **/
 bool
 hb_subset_cff2 (hb_subset_plan_t *plan,
-                hb_blob_t       **cff2_prime /* OUT */)
+                hb_blob_t       **prime /* OUT */)
 {
   hb_blob_t *cff2_blob = OT::hb_sanitize_context_t().reference_table<CFF::cff2> (plan->source);
-  const char *cff2_data = hb_blob_get_data(cff2_blob, nullptr);
+  const char *data = hb_blob_get_data(cff2_blob, nullptr);
 
-  OT::cff2::accelerator_t cff2;
-  cff2.init(plan->source);
-  bool result = _hb_subset_cff2 (cff2,
-                                 cff2_data,
+  OT::cff2::accelerator_t acc;
+  acc.init(plan->source);
+  bool result = likely (acc.is_valid ()) &&
+                _hb_subset_cff2 (acc,
+                                 data,
                                  plan,
-                                 cff2_prime);
+                                 prime);
 
   hb_blob_destroy (cff2_blob);
-  cff2.fini();
+  acc.fini();
 
   return result;
 }