Merge branch 'master' into cff-subset
diff --git a/src/Makefile.sources b/src/Makefile.sources
index 46af3ae..1e651d3 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -27,6 +27,9 @@
 	hb-ot-color-cbdt-table.hh \
 	hb-ot-cmap-table.hh \
 	hb-ot-glyf-table.hh \
+	hb-ot-cff1-table.hh \
+	hb-ot-cff1-table.cc \
+	hb-ot-cff2-table.hh \
 	hb-ot-hdmx-table.hh \
 	hb-ot-head-table.hh \
 	hb-ot-hhea-table.hh \
@@ -152,6 +155,12 @@
 	hb-ot-var-fvar-table.hh \
 	hb-ot-var-hvar-table.hh \
 	hb-ot-var-mvar-table.hh \
+	hb-ot-cff-common.hh \
+	hb-cff-interp-common.hh \
+	hb-cff-interp-cs-common.hh \
+	hb-cff1-interp-cs.hh \
+	hb-cff2-interp-cs.hh \
+	hb-cff-interp-dict-common.hh \
 	$(NULL)
 
 HB_OT_RAGEL_GENERATED_sources = \
@@ -213,6 +222,10 @@
 	hb-subset.hh \
 	hb-subset-glyf.cc \
 	hb-subset-glyf.hh \
+	hb-subset-cff1.cc \
+	hb-subset-cff2.cc \
+	hb-subset-cff-common.cc \
+	hb-ot-cff1-table.cc \
 	hb-subset-input.cc \
 	hb-subset-input.hh \
 	hb-subset-plan.cc \
@@ -221,6 +234,12 @@
 
 HB_SUBSET_headers = \
 	hb-subset.h \
+	hb-subset-glyf.hh \
+	hb-subset-cff1.hh \
+	hb-subset-cff2.hh \
+	hb-subset-cff-common.hh \
+	hb-subset-plan.hh \
+	hb-subset.hh \
 	$(NULL)
 
 HB_GOBJECT_DIST_sources = hb-gobject-structs.cc
diff --git a/src/hb-cff-interp-common.hh b/src/hb-cff-interp-common.hh
new file mode 100644
index 0000000..5b3477e
--- /dev/null
+++ b/src/hb-cff-interp-common.hh
@@ -0,0 +1,689 @@
+/*
+ * 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_CFF_INTERP_COMMON_HH
+#define HB_CFF_INTERP_COMMON_HH
+
+namespace CFF {
+
+using namespace OT;
+
+enum OpCode {
+    /* === Dict operators === */
+
+    /* One byte operators (0-31) */
+    OpCode_version,                /* 0   CFF Top */
+    OpCode_Notice,                 /* 1   CFF Top */
+    OpCode_FullName,               /* 2   CFF Top */
+    OpCode_FamilyName,             /* 3   CFF Top */
+    OpCode_Weight,                 /* 4   CFF Top */
+    OpCode_FontBBox,               /* 5   CFF Top */
+    OpCode_BlueValues,             /* 6   CFF Private, CFF2 Private */
+    OpCode_OtherBlues,             /* 7   CFF Private, CFF2 Private */
+    OpCode_FamilyBlues,            /* 8   CFF Private, CFF2 Private */
+    OpCode_FamilyOtherBlues,       /* 9   CFF Private, CFF2 Private */
+    OpCode_StdHW,                  /* 10  CFF Private, CFF2 Private */
+    OpCode_StdVW,                  /* 11  CFF Private, CFF2 Private */
+    OpCode_escape,                 /* 12  All. Shared with CS */
+    OpCode_UniqueID,               /* 13  CFF Top */
+    OpCode_XUID,                   /* 14  CFF Top */
+    OpCode_charset,                /* 15  CFF Top (0) */
+    OpCode_Encoding,               /* 16  CFF Top (0) */
+    OpCode_CharStrings,            /* 17  CFF Top, CFF2 Top */
+    OpCode_Private,                /* 18  CFF Top, CFF2 FD */
+    OpCode_Subrs,                  /* 19  CFF Private, CFF2 Private */
+    OpCode_defaultWidthX,          /* 20  CFF Private (0) */
+    OpCode_nominalWidthX,          /* 21  CFF Private (0) */
+    OpCode_vsindexdict,            /* 22  CFF2 Private/CS */
+    OpCode_blenddict,              /* 23  CFF2 Private/CS  */
+    OpCode_vstore,                 /* 24  CFF2 Top */
+    OpCode_reserved25,             /* 25 */
+    OpCode_reserved26,             /* 26 */
+    OpCode_reserved27,             /* 27 */
+
+    /* Numbers */
+    OpCode_shortint,               /* 28  16-bit integer, All */
+    OpCode_longintdict,            /* 29  32-bit integer, All */
+    OpCode_BCD,                    /* 30  real number, CFF2 Top/FD */
+    OpCode_reserved31,             /* 31 */
+
+    /* 1-byte integers */
+    OpCode_OneByteIntFirst = 32,   /* All. beginning of the range of first byte ints */
+    OpCode_OneByteIntLast = 246,   /* All. ending of the range of first byte int */
+
+    /* 2-byte integers */
+    OpCode_TwoBytePosInt0,         /* 247  All. first byte of two byte positive int (+108 to +1131) */
+    OpCode_TwoBytePosInt1,
+    OpCode_TwoBytePosInt2,
+    OpCode_TwoBytePosInt3,
+
+    OpCode_TwoByteNegInt0,         /* 251  All. first byte of two byte negative int (-1131 to -108) */
+    OpCode_TwoByteNegInt1,
+    OpCode_TwoByteNegInt2,
+    OpCode_TwoByteNegInt3,
+
+    /* Two byte escape operators 12, (0-41) */
+    OpCode_ESC_Base = 256,
+    OpCode_Copyright = OpCode_ESC_Base, /* Make_OpCode_ESC (0) CFF Top */
+    OpCode_isFixedPitch,           /* Make_OpCode_ESC (1)  CFF Top (false) */
+    OpCode_ItalicAngle,            /* Make_OpCode_ESC (2)  CFF Top (0) */
+    OpCode_UnderlinePosition,      /* Make_OpCode_ESC (3)  CFF Top (-100) */
+    OpCode_UnderlineThickness,     /* Make_OpCode_ESC (4)  CFF Top (50) */
+    OpCode_PaintType,              /* Make_OpCode_ESC (5)  CFF Top (0) */
+    OpCode_CharstringType,         /* Make_OpCode_ESC (6)  CFF Top (2) */
+    OpCode_FontMatrix,             /* Make_OpCode_ESC (7)  CFF Top, CFF2 Top (.001 0 0 .001 0 0)*/
+    OpCode_StrokeWidth,            /* Make_OpCode_ESC (8)  CFF Top (0) */
+    OpCode_BlueScale,              /* Make_OpCode_ESC (9)  CFF Private, CFF2 Private (0.039625) */
+    OpCode_BlueShift,              /* Make_OpCode_ESC (10) CFF Private, CFF2 Private (7) */
+    OpCode_BlueFuzz,               /* Make_OpCode_ESC (11) CFF Private, CFF2 Private (1) */
+    OpCode_StemSnapH,              /* Make_OpCode_ESC (12) CFF Private, CFF2 Private */
+    OpCode_StemSnapV,              /* Make_OpCode_ESC (13) CFF Private, CFF2 Private */
+    OpCode_ForceBold,              /* Make_OpCode_ESC (14) CFF Private (false) */
+    OpCode_reservedESC15,          /* Make_OpCode_ESC (15) */
+    OpCode_reservedESC16,          /* Make_OpCode_ESC (16) */
+    OpCode_LanguageGroup,          /* Make_OpCode_ESC (17) CFF Private, CFF2 Private (0) */
+    OpCode_ExpansionFactor,        /* Make_OpCode_ESC (18) CFF Private, CFF2 Private (0.06) */
+    OpCode_initialRandomSeed,      /* Make_OpCode_ESC (19) CFF Private (0) */
+    OpCode_SyntheticBase,          /* Make_OpCode_ESC (20) CFF Top */
+    OpCode_PostScript,             /* Make_OpCode_ESC (21) CFF Top */
+    OpCode_BaseFontName,           /* Make_OpCode_ESC (22) CFF Top */
+    OpCode_BaseFontBlend,          /* Make_OpCode_ESC (23) CFF Top */
+    OpCode_reservedESC24,          /* Make_OpCode_ESC (24) */
+    OpCode_reservedESC25,          /* Make_OpCode_ESC (25) */
+    OpCode_reservedESC26,          /* Make_OpCode_ESC (26) */
+    OpCode_reservedESC27,          /* Make_OpCode_ESC (27) */
+    OpCode_reservedESC28,          /* Make_OpCode_ESC (28) */
+    OpCode_reservedESC29,          /* Make_OpCode_ESC (29) */
+    OpCode_ROS,                    /* Make_OpCode_ESC (30) CFF Top_CID */
+    OpCode_CIDFontVersion,         /* Make_OpCode_ESC (31) CFF Top_CID (0) */
+    OpCode_CIDFontRevision,        /* Make_OpCode_ESC (32) CFF Top_CID (0) */
+    OpCode_CIDFontType,            /* Make_OpCode_ESC (33) CFF Top_CID (0) */
+    OpCode_CIDCount,               /* Make_OpCode_ESC (34) CFF Top_CID (8720) */
+    OpCode_UIDBase,                /* Make_OpCode_ESC (35) CFF Top_CID */
+    OpCode_FDArray,                /* Make_OpCode_ESC (36) CFF Top_CID, CFF2 Top */
+    OpCode_FDSelect,               /* Make_OpCode_ESC (37) CFF Top_CID, CFF2 Top */
+    OpCode_FontName,               /* Make_OpCode_ESC (38) CFF Top_CID */
+
+    /* === CharString operators === */
+
+    OpCode_hstem = 1,              /* 1 CFF, CFF2 */
+    OpCode_Reserved2,
+    OpCode_vstem,                  /* 3 CFF, CFF2 */
+    OpCode_vmoveto,                /* 4 CFF, CFF2 */
+    OpCode_rlineto,                /* 5 CFF, CFF2 */
+    OpCode_hlineto,                /* 6 CFF, CFF2 */
+    OpCode_vlineto,                /* 7 CFF, CFF2 */
+    OpCode_rrcurveto,              /* 8 CFF, CFF2 */
+    OpCode_Reserved9,
+    OpCode_callsubr,               /* 10 CFF, CFF2 */
+    OpCode_return,                 /* 11 CFF */
+ // OpCode_escape,                 /* 12 CFF, CFF2 */
+    OpCode_Reserved13 = 13,
+    OpCode_endchar,                /* 14 CFF */
+    OpCode_vsindexcs,              /* 15 CFF2 */
+    OpCode_blendcs,                /* 16 CFF2 */
+    OpCode_Reserved17,
+    OpCode_hstemhm,                /* 18 CFF, CFF2 */
+    OpCode_hintmask,               /* 19 CFF, CFF2 */
+    OpCode_cntrmask,               /* 20 CFF, CFF2 */
+    OpCode_rmoveto,                /* 21 CFF, CFF2 */
+    OpCode_hmoveto,                /* 22 CFF, CFF2 */
+    OpCode_vstemhm,                /* 23 CFF, CFF2 */
+    OpCode_rcurveline,             /* 24 CFF, CFF2 */
+    OpCode_rlinecurve,             /* 25 CFF, CFF2 */
+    OpCode_vvcurveto,              /* 26 CFF, CFF2 */
+    OpCode_hhcurveto,              /* 27 CFF, CFF2 */
+ // OpCode_shortint,               /* 28 CFF, CFF2 */
+    OpCode_callgsubr = 29,         /* 29 CFF, CFF2 */
+    OpCode_vhcurveto,              /* 30 CFF, CFF2 */
+    OpCode_hvcurveto,              /* 31 CFF, CFF2 */
+
+    OpCode_fixedcs = 255,          /* 32-bit fixed */
+
+    /* Two byte escape operators 12, (0-41) */
+    OpCode_ReservedESC0 = OpCode_ESC_Base, /* Make_OpCode_ESC (0) */
+    OpCode_ReservedESC1,           /* Make_OpCode_ESC (1) */
+    OpCode_ReservedESC2,           /* Make_OpCode_ESC (2) */
+    OpCode_and,                    /* Make_OpCode_ESC (3) CFF */
+    OpCode_or,                     /* Make_OpCode_ESC (4) CFF */
+    OpCode_not,                    /* Make_OpCode_ESC (5) CFF */
+    OpCode_ReservedESC6,           /* Make_OpCode_ESC (6) */
+    OpCode_ReservedESC7,           /* Make_OpCode_ESC (7) */
+    OpCode_ReservedESC8,           /* Make_OpCode_ESC (8) */
+    OpCode_abs,                    /* Make_OpCode_ESC (9) CFF */
+    OpCode_add,                    /* Make_OpCode_ESC (10) CFF */
+    OpCode_sub,                    /* Make_OpCode_ESC (11) CFF */
+    OpCode_div,                    /* Make_OpCode_ESC (12) CFF */
+    OpCode_ReservedESC13,          /* Make_OpCode_ESC (13) */
+    OpCode_neg,                    /* Make_OpCode_ESC (14) CFF */
+    OpCode_eq,                     /* Make_OpCode_ESC (15) CFF */
+    OpCode_ReservedESC16,          /* Make_OpCode_ESC (16) */
+    OpCode_ReservedESC17,          /* Make_OpCode_ESC (17) */
+    OpCode_drop,                   /* Make_OpCode_ESC (18) CFF */
+    OpCode_ReservedESC19,          /* Make_OpCode_ESC (19) */
+    OpCode_put,                    /* Make_OpCode_ESC (20) CFF */
+    OpCode_get,                    /* Make_OpCode_ESC (21) CFF */
+    OpCode_ifelse,                 /* Make_OpCode_ESC (22) CFF */
+    OpCode_random,                 /* Make_OpCode_ESC (23) CFF */
+    OpCode_mul,                    /* Make_OpCode_ESC (24) CFF */
+ // OpCode_reservedESC25,          /* Make_OpCode_ESC (25) */
+    OpCode_sqrt = OpCode_mul+2,    /* Make_OpCode_ESC (26) CFF */
+    OpCode_dup,                    /* Make_OpCode_ESC (27) CFF */
+    OpCode_exch,                   /* Make_OpCode_ESC (28) CFF */
+    OpCode_index,                  /* Make_OpCode_ESC (29) CFF */
+    OpCode_roll,                   /* Make_OpCode_ESC (30) CFF */
+    OpCode_reservedESC31,          /* Make_OpCode_ESC (31) */
+    OpCode_reservedESC32,          /* Make_OpCode_ESC (32) */
+    OpCode_reservedESC33,          /* Make_OpCode_ESC (33) */
+    OpCode_hflex,                  /* Make_OpCode_ESC (34) CFF, CFF2 */
+    OpCode_flex,                   /* Make_OpCode_ESC (35) CFF, CFF2 */
+    OpCode_hflex1,                 /* Make_OpCode_ESC (36) CFF, CFF2 */
+    OpCode_flex1                   /* Make_OpCode_ESC (37) CFF, CFF2 */
+};
+
+inline OpCode Make_OpCode_ESC (unsigned char byte2)  { return (OpCode)(OpCode_ESC_Base + byte2); }
+inline OpCode Unmake_OpCode_ESC (OpCode op)  { return (OpCode)(op - OpCode_ESC_Base); }
+inline bool Is_OpCode_ESC (OpCode op) { return op >= OpCode_ESC_Base; }
+inline unsigned int OpCode_Size (OpCode op) { return Is_OpCode_ESC (op)? 2: 1; }
+
+struct Number
+{
+  inline void init (void)
+  { set_int (0); }
+  inline void fini (void)
+  {}
+
+  inline void set_int (int v)           { format = NumInt; u.int_val = v; };
+  inline int to_int (void) const        { return is_int ()? u.int_val: (int)to_real (); }
+  inline void set_fixed (int32_t v)     { format = NumFixed; u.fixed_val = v; };
+  inline int32_t to_fixed (void) const
+  {
+    if (is_fixed ())
+      return u.fixed_val;
+    else if (is_real ())
+      return (int32_t)(u.real_val * 65536.0);
+    else
+      return (int32_t)(u.int_val << 16);
+  }
+  inline void set_real (float v)        { format = NumReal; u.real_val = v; };
+  inline float to_real (void) const
+  {
+    if (is_real ())
+      return u.real_val;
+    if (is_fixed ())
+      return u.fixed_val / 65536.0;
+    else
+      return (float)u.int_val;
+  }
+  inline bool in_int_range (void) const
+  {
+    if (is_int ())
+      return true;
+    if (is_fixed () && ((u.fixed_val & 0xFFFF) == 0))
+      return true;
+    else
+      return ((float)(int16_t)to_int () == u.real_val);
+  }
+
+protected:
+  enum NumFormat {
+    NumInt,
+    NumFixed,
+    NumReal
+  };
+  NumFormat  format;
+  union {
+    int     int_val;
+    int32_t fixed_val;
+    float   real_val;
+  } u;
+
+  inline bool  is_int (void) const { return format == NumInt; }
+  inline bool  is_fixed (void) const { return format == NumFixed; }
+  inline bool  is_real (void) const { return format == NumReal; }
+};
+
+/* 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_longintdict, 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
+{
+  inline ByteStr (void)
+    : str (&Null(UnsizedByteStr)), len (0) {}
+  inline ByteStr (const UnsizedByteStr& s, unsigned int l)
+    : str (&s), len (l) {}
+  inline ByteStr (const char *s, unsigned int l=0)
+    : str ((const UnsizedByteStr *)s), len (l) {}
+  /* sub-string */
+  inline ByteStr (const ByteStr &bs, unsigned int offset, unsigned int len_)
+  {
+    str = (const UnsizedByteStr *)&bs.str[offset];
+    len = len_;
+  }
+
+  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 serialize (hb_serialize_context_t *c, const ByteStr &src)
+  {
+    TRACE_SERIALIZE (this);
+    HBUINT8 *dest = c->allocate_size<HBUINT8> (src.len);
+    if (unlikely (dest == nullptr))
+      return_trace (false);
+    memcpy (dest, src.str, src.len);
+    return_trace (true);
+  }
+
+  inline unsigned int get_size (void) const { return len; }
+
+  inline bool check_limit (unsigned int offset, unsigned int count) const
+  { return (offset + count <= len); }
+
+  const UnsizedByteStr *str;
+  unsigned int len;
+};
+
+struct SubByteStr
+{
+  inline SubByteStr (void)
+  { init (); }
+
+  inline void init (void)
+  {
+    str = ByteStr (0);
+    offset = 0;
+  }
+
+  inline void fini (void) {}
+
+  inline SubByteStr (const ByteStr &str_, unsigned int offset_ = 0)
+    : str (str_), offset (offset_) {}
+
+  inline void reset (const ByteStr &str_, unsigned int offset_ = 0)
+  {
+    str = str_;
+    offset = offset_;
+  }
+
+  inline const HBUINT8& operator [] (int i) const {
+    return str[offset + i];
+  }
+
+  inline operator ByteStr (void) const { return ByteStr (str, offset, str.len - offset); }
+
+  inline bool avail (unsigned int count=1) const { return str.check_limit (offset, count); }
+  inline void inc (unsigned int count=1) { offset += count; assert (count <= str.len); }
+
+  ByteStr       str;
+  unsigned int  offset; /* beginning of the sub-string within str */
+};
+
+inline float parse_bcd (SubByteStr& substr, float& v)
+{
+  // XXX: TODO
+  v = 0;
+  for (;;) {
+    if (!substr.avail ())
+      return false;
+    unsigned char byte = substr[0];
+    substr.inc ();
+    if (((byte & 0xF0) == 0xF0) || ((byte & 0x0F) == 0x0F))
+      break;
+  }
+  return true;
+}
+
+/* stack */
+template <typename ELEM, int LIMIT>
+struct Stack
+{
+  inline void init (void)
+  {
+    count = 0;
+    elements.init ();
+    elements.resize (kSizeLimit);
+    for (unsigned int i = 0; i < elements.len; i++)
+      elements[i].init ();
+  }
+
+  inline void fini (void)
+  {
+    for (unsigned int i = 0; i < elements.len; i++)
+      elements[i].fini ();
+  }
+
+  inline void push (const ELEM &v)
+  {
+    if (likely (count < elements.len))
+      elements[count++] = v;
+  }
+
+  inline ELEM &push (void)
+  {
+    if (likely (count < elements.len))
+      return elements[count++];
+    else
+      return Crap(ELEM);
+  }
+
+  inline const ELEM& pop (void)
+  {
+    if (likely (count > 0))
+      return elements[--count];
+    else
+      return Null(ELEM);
+  }
+
+  inline const ELEM& peek (void)
+  {
+    if (likely (count > 0))
+      return elements[count];
+    else
+      return Null(ELEM);
+  }
+
+  inline void unpop (void)
+  {
+    if (likely (count < elements.len))
+      count++;
+  }
+
+  inline void clear (void) { count = 0; }
+
+  inline bool check_overflow (unsigned int n=1) const { return (n <= kSizeLimit) && (n + count <= kSizeLimit); }
+  inline bool check_underflow (unsigned int n=1) const { return (n <= count); }
+
+  inline unsigned int get_count (void) const { return count; }
+  inline bool is_empty (void) const { return count == 0; }
+
+  static const unsigned int kSizeLimit = LIMIT;
+
+  unsigned int count;
+  hb_vector_t<ELEM, kSizeLimit> elements;
+};
+
+/* argument stack */
+template <typename ARG=Number>
+struct ArgStack : Stack<ARG, 513>
+{
+  inline void push_int (int v)
+  {
+    ARG &n = S::push ();
+    n.set_int (v);
+  }
+
+  inline void push_fixed (int32_t v)
+  {
+    ARG &n = S::push ();
+    n.set_fixed (v);
+  }
+
+  inline void push_real (float v)
+  {
+    ARG &n = S::push ();
+    n.set_real (v);
+  }
+
+  inline bool check_pop_num (ARG& n)
+  {
+    if (unlikely (!this->check_underflow ()))
+      return false;
+    n = this->pop ();
+    return true;
+  }
+
+  inline bool check_pop_num2 (ARG& n1, ARG& n2)
+  {
+    if (unlikely (!this->check_underflow (2)))
+      return false;
+    n2 = this->pop ();
+    n1 = this->pop ();
+    return true;
+  }
+
+  inline bool check_pop_int (int& v)
+  {
+    if (unlikely (!this->check_underflow ()))
+      return false;
+    v = this->pop ().to_int ();
+    return true;
+  }
+
+  inline bool check_pop_uint (unsigned int& v)
+  {
+    int  i;
+    if (unlikely (!check_pop_int (i) || i < 0))
+      return false;
+    v = (unsigned int)i;
+    return true;
+  }
+
+  inline bool check_pop_delta (hb_vector_t<ARG>& vec, bool even=false)
+  {
+    if (even && unlikely ((this->count & 1) != 0))
+      return false;
+
+    float val = 0.0f;
+    for (unsigned int i = 0; i < S::count; i++) {
+      val += S::elements[i].to_real ();
+      ARG *n = vec.push ();
+      n->set_real (val);
+    }
+    return true;
+  }
+
+  inline bool push_longint_from_substr (SubByteStr& substr)
+  {
+    if (unlikely (!substr.avail (4) || !S::check_overflow (1)))
+      return false;
+    push_int ((int32_t)*(const HBUINT32*)&substr[0]);
+    substr.inc (4);
+    return true;
+  }
+
+  inline bool push_fixed_from_substr (SubByteStr& substr)
+  {
+    if (unlikely (!substr.avail (4) || !S::check_overflow (1)))
+      return false;
+    push_fixed ((int32_t)*(const HBUINT32*)&substr[0]);
+    substr.inc (4);
+    return true;
+  }
+
+  inline void reverse_range (int i, int j)
+  {
+    assert (i >= 0 && i < j);
+    ARG  tmp;
+    while (i < j)
+    {
+      tmp = S::elements[i];
+      S::elements[i++] = S::elements[j];
+      S::elements[j++] = tmp;
+    }
+  }
+
+  private:
+  typedef Stack<ARG, 513> S;
+};
+
+/* an operator prefixed by its operands in a byte string */
+struct OpStr
+{
+  OpCode  op;
+  ByteStr str;
+};
+
+/* 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);
+  }
+};
+
+template <typename ARG=Number>
+struct InterpEnv
+{
+  inline void init (const ByteStr &str_)
+  {
+    substr.reset (str_);
+    argStack.init ();
+  }
+
+  inline void fini (void)
+  {
+    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;
+  }
+
+  inline void pop_n_args (unsigned int n)
+  {
+    assert (n <= argStack.count);
+    argStack.count -= n;
+  }
+
+  inline void clear_args (void)
+  {
+    pop_n_args (argStack.count);
+  }
+
+  SubByteStr    substr;
+  ArgStack<ARG> argStack;
+};
+
+typedef InterpEnv<> NumInterpEnv;
+
+template <typename ARG=Number>
+struct OpSet
+{
+  static inline bool process_op (OpCode op, InterpEnv<ARG>& env)
+  {
+    switch (op) {
+      case OpCode_shortint:
+        if (unlikely (!env.substr.avail (2) || !env.argStack.check_overflow (1)))
+          return false;
+        env.argStack.push_int ((int16_t)*(const HBUINT16*)&env.substr[0]);
+        env.substr.inc (2);
+        break;
+
+      case OpCode_TwoBytePosInt0: case OpCode_TwoBytePosInt1:
+      case OpCode_TwoBytePosInt2: case OpCode_TwoBytePosInt3:
+        if (unlikely (!env.substr.avail () || !env.argStack.check_overflow (1)))
+          return false;
+        env.argStack.push_int ((int16_t)((op - OpCode_TwoBytePosInt0) * 256 + env.substr[0] + 108));
+        env.substr.inc ();
+        break;
+      
+      case OpCode_TwoByteNegInt0: case OpCode_TwoByteNegInt1:
+      case OpCode_TwoByteNegInt2: case OpCode_TwoByteNegInt3:
+        if (unlikely (!env.substr.avail () || !env.argStack.check_overflow (1)))
+          return false;
+        env.argStack.push_int ((int16_t)(-(op - OpCode_TwoByteNegInt0) * 256 - env.substr[0] - 108));
+        env.substr.inc ();
+        break;
+
+      default:
+        /* 1-byte integer */
+        if (likely ((OpCode_OneByteIntFirst <= op) && (op <= OpCode_OneByteIntLast)) &&
+            likely (env.argStack.check_overflow (1)))
+        {
+          env.argStack.push_int ((int)op - 139);
+        } else {
+          /* invalid unknown operator */
+          env.clear_args ();
+          return false;
+        }
+        break;
+    }
+
+    return true;
+  }
+};
+
+template <typename ENV>
+struct Interpreter {
+
+  inline ~Interpreter(void) { fini (); }
+
+  inline void fini (void) { env.fini (); }
+
+  ENV env;
+};
+
+} /* namespace CFF */
+
+#endif /* HB_CFF_INTERP_COMMON_HH */
diff --git a/src/hb-cff-interp-cs-common.hh b/src/hb-cff-interp-cs-common.hh
new file mode 100644
index 0000000..ef7b056
--- /dev/null
+++ b/src/hb-cff-interp-cs-common.hh
@@ -0,0 +1,328 @@
+/*
+ * 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_CFF_INTERP_CS_COMMON_HH
+#define HB_CFF_INTERP_CS_COMMON_HH
+
+#include "hb.hh"
+#include "hb-cff-interp-common.hh"
+
+namespace CFF {
+
+using namespace OT;
+
+/* call stack */
+struct CallStack : Stack<SubByteStr, 10> {};
+
+template <typename SUBRS>
+struct BiasedSubrs
+{
+  inline void init (const SUBRS &subrs_)
+  {
+    subrs = &subrs_;
+    unsigned int  nSubrs = subrs_.count;
+    if (nSubrs < 1240)
+      bias = 107;
+    else if (nSubrs < 33900)
+      bias = 1131;
+    else
+      bias = 32768;
+  }
+
+  inline void fini (void) {}
+
+  const SUBRS   *subrs;
+  unsigned int  bias;
+};
+
+template <typename ARG, typename SUBRS>
+struct CSInterpEnv : InterpEnv<ARG>
+{
+  inline void init (const ByteStr &str, const SUBRS &globalSubrs_, const SUBRS &localSubrs_)
+  {
+    InterpEnv<ARG>::init (str);
+
+    seen_moveto = true;
+    seen_hintmask = false;
+    hstem_count = 0;
+    vstem_count = 0;
+    callStack.init ();
+    globalSubrs.init (globalSubrs_);
+    localSubrs.init (localSubrs_);
+  }
+  inline void fini (void)
+  {
+    InterpEnv<ARG>::fini ();
+
+    callStack.fini ();
+    globalSubrs.fini ();
+    localSubrs.fini ();
+  }
+
+  inline bool popSubrNum (const BiasedSubrs<SUBRS>& biasedSubrs, unsigned int &subr_num)
+  {
+    int n;
+    if (unlikely ((!callStack.check_overflow (1) ||
+                   !SUPER::argStack.check_pop_int (n))))
+      return false;
+    n += biasedSubrs.bias;
+    if (unlikely ((n < 0) || (n >= biasedSubrs.subrs->count)))
+      return false;
+
+    subr_num = (unsigned int)n;
+    return true;
+  }
+
+  inline bool callSubr (const BiasedSubrs<SUBRS>& biasedSubrs)
+  {
+    unsigned int subr_num;
+
+    if (unlikely (!popSubrNum (biasedSubrs, subr_num)))
+      return false;
+    callStack.push (SUPER::substr);
+    SUPER::substr = (*biasedSubrs.subrs)[subr_num];
+
+    return true;
+  }
+
+  inline bool returnFromSubr (void)
+  {
+    if (unlikely (!callStack.check_underflow ()))
+      return false;
+
+    SUPER::substr = callStack.pop ();
+    return true;
+  }
+
+  inline void determine_hintmask_size (void)
+  {
+    if (!seen_hintmask)
+    {
+      vstem_count += SUPER::argStack.get_count() / 2;
+      hintmask_size = (hstem_count + vstem_count + 7) >> 3;
+      seen_hintmask = true;
+    }
+  }
+
+  inline void set_endchar (bool endchar_flag_) { endchar_flag = endchar_flag_; }
+  inline bool is_endchar (void) const { return endchar_flag; }
+
+  public:
+  bool          endchar_flag;
+  bool          seen_moveto;
+  bool          seen_hintmask;
+
+  unsigned int  hstem_count;
+  unsigned int  vstem_count;
+  unsigned int  hintmask_size;
+  CallStack            callStack;
+  BiasedSubrs<SUBRS>   globalSubrs;
+  BiasedSubrs<SUBRS>   localSubrs;
+
+  private:
+  typedef InterpEnv<ARG> SUPER;
+};
+
+template <typename ARG, typename OPSET, typename ENV, typename PARAM>
+struct CSOpSet : OpSet<ARG>
+{
+  static inline bool process_op (OpCode op, ENV &env, PARAM& param)
+  {  
+    switch (op) {
+
+      case OpCode_return:
+        return env.returnFromSubr ();
+      case OpCode_endchar:
+        env.set_endchar (true);
+        OPSET::flush_op (op, env, param);
+        break;
+
+      case OpCode_fixedcs:
+        return env.argStack.push_fixed_from_substr (env.substr);
+
+      case OpCode_callsubr:
+        return env.callSubr (env.localSubrs);
+      
+      case OpCode_callgsubr:
+        return env.callSubr (env.globalSubrs);
+
+      case OpCode_hstem:
+      case OpCode_hstemhm:
+        OPSET::process_hstem (op, env, param);
+        break;
+      case OpCode_vstem:
+      case OpCode_vstemhm:
+        OPSET::process_vstem (op, env, param);
+        break;
+      case OpCode_hintmask:
+      case OpCode_cntrmask:
+        OPSET::process_hintmask (op, env, param);
+        break;
+      
+      case OpCode_vmoveto:
+      case OpCode_rlineto:
+      case OpCode_hlineto:
+      case OpCode_vlineto:
+      case OpCode_rmoveto:
+      case OpCode_hmoveto:
+        OPSET::process_moveto (op, env, param);
+        break;
+      case OpCode_rrcurveto:
+      case OpCode_rcurveline:
+      case OpCode_rlinecurve:
+      case OpCode_vvcurveto:
+      case OpCode_hhcurveto:
+      case OpCode_vhcurveto:
+      case OpCode_hvcurveto:
+        OPSET::process_path (op, env, param);
+        break;
+
+      case OpCode_hflex:
+      case OpCode_flex:
+      case OpCode_hflex1:
+      case OpCode_flex1:
+        OPSET::process_flex (op, env, param);
+        break;
+
+      default:
+        return SUPER::process_op (op, env);
+    }
+    return true;
+  }
+
+  static inline void process_hstem (OpCode op, ENV &env, PARAM& param)
+  {
+    env.hstem_count += env.argStack.count / 2;
+    OPSET::flush_args_and_op (op, env, param);
+  }
+
+  static inline void process_vstem (OpCode op, ENV &env, PARAM& param)
+  {
+    env.vstem_count += env.argStack.count / 2;
+    OPSET::flush_args_and_op (op, env, param);
+  }
+
+  static inline void process_hintmask (OpCode op, ENV &env, PARAM& param)
+  {
+    env.determine_hintmask_size ();
+    if (likely (env.substr.avail (env.hintmask_size)))
+    {
+      OPSET::flush_hintmask (op, env, param);
+      env.substr.inc (env.hintmask_size);
+    }
+  }
+
+  static inline void process_flex (OpCode op, ENV &env, PARAM& param)
+  {
+    OPSET::flush_args_and_op (op, env, param);
+  }
+
+  static inline void process_moveto (OpCode op, ENV &env, PARAM& param)
+  {
+    if (!env.seen_moveto)
+    {
+      env.determine_hintmask_size ();
+      env.seen_moveto = true;
+    }
+    OPSET::flush_args_and_op (op, env, param);
+  }
+
+  static inline void process_path (OpCode op, ENV &env, PARAM& param)
+  {
+    OPSET::flush_args_and_op (op, env, param);
+  }
+
+  static inline void flush_args_and_op (OpCode op, ENV &env, PARAM& param)
+  {
+    OPSET::flush_n_args_and_op (op, env.argStack.count, env, param);
+  }
+
+  static inline void flush_n_args_and_op (OpCode op, unsigned int n, ENV &env, PARAM& param)
+  {
+    OPSET::flush_n_args (n, env, param);
+    OPSET::flush_op (op, env, param);
+  }
+
+  static inline void flush_args (ENV &env, PARAM& param)
+  {
+    OPSET::flush_n_args (env.argStack.count, env, param);
+  }
+
+  static inline void flush_n_args (unsigned int n, ENV &env, PARAM& param)
+  {
+    env.pop_n_args (n);
+  }
+
+  static inline void flush_op (OpCode op, ENV &env, PARAM& param)
+  {
+  }
+
+  static inline void flush_hintmask (OpCode op, ENV &env, PARAM& param)
+  {
+    OPSET::flush_args_and_op (op, env, param);
+  }
+
+  static inline bool is_subr_op (OpCode op)
+  {
+    switch (op)
+    {
+      case OpCode_callsubr:
+      case OpCode_callgsubr:
+      case OpCode_return:
+        return true;
+      default:
+        return false;
+    }
+  }
+
+  typedef OpSet<ARG>  SUPER;
+};
+
+template <typename ENV, typename OPSET, typename PARAM>
+struct CSInterpreter : Interpreter<ENV>
+{
+  inline bool interpret (PARAM& param)
+  {
+    SUPER::env.set_endchar (false);
+
+    for (;;) {
+      OpCode op;
+      if (unlikely (!SUPER::env.fetch_op (op) ||
+                    !OPSET::process_op (op, SUPER::env, param)))
+        return false;
+      if (SUPER::env.is_endchar ())
+        break;
+    }
+    
+    return true;
+  }
+
+  private:
+  typedef Interpreter<ENV> SUPER;
+};
+
+} /* namespace CFF */
+
+#endif /* HB_CFF_INTERP_CS_COMMON_HH */
diff --git a/src/hb-cff-interp-dict-common.hh b/src/hb-cff-interp-dict-common.hh
new file mode 100644
index 0000000..f33ec9a
--- /dev/null
+++ b/src/hb-cff-interp-dict-common.hh
@@ -0,0 +1,225 @@
+/*
+ * 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_CFF_INTERP_DICT_COMMON_HH
+#define HB_CFF_INTERP_DICT_COMMON_HH
+
+#include "hb-cff-interp-common.hh"
+
+namespace CFF {
+
+using namespace OT;
+
+/* an opstr and the parsed out dict value(s) */
+struct DictVal : OpStr
+{
+  inline void init (void)
+  {
+    single_val.set_int (0);
+    multi_val.init ();
+  }
+
+  inline void fini (void)
+  {
+    multi_val.fini ();
+  }
+
+  Number              single_val;
+  hb_vector_t<Number> multi_val;
+};
+
+typedef DictVal NumDictVal;
+
+template <typename VAL>
+struct DictValues
+{
+  inline void init (void)
+  {
+    opStart = 0;
+    values.init ();
+  }
+
+  inline void fini (void)
+  {
+    values.fini ();
+  }
+
+  inline void addOp (OpCode op, const SubByteStr& substr = SubByteStr ())
+  {
+    VAL *val = values.push ();
+    val->op = op;
+    val->str = ByteStr (substr.str, opStart, substr.offset - opStart);
+    opStart = substr.offset;
+  }
+
+  inline void addOp (OpCode op, const SubByteStr& substr, const VAL &v)
+  {
+    VAL *val = values.push (v);
+    val->op = op;
+    val->str = ByteStr (substr.str, opStart, substr.offset - opStart);
+    opStart = substr.offset;
+  }
+
+  inline bool hasOp (OpCode op) const
+  {
+    for (unsigned int i = 0; i < getNumValues (); i++)
+      if (getValue (i).op == op) return true;
+    return false;
+  }
+
+  inline unsigned getNumValues (void) const { return values.len; }
+  inline const VAL &getValue (unsigned int i) const { return values[i]; }
+  inline const VAL &operator [] (unsigned int i) const { return getValue (i); }
+
+  unsigned int       opStart;
+  hb_vector_t<VAL>   values;
+};
+
+template <typename OPSTR=OpStr>
+struct TopDictValues : DictValues<OPSTR>
+{
+  inline void init (void)
+  {
+    DictValues<OPSTR>::init ();
+    charStringsOffset = 0;
+    FDArrayOffset = 0;
+  }
+
+  inline void fini (void)
+  {
+    DictValues<OPSTR>::fini ();
+  }
+
+  inline unsigned int calculate_serialized_op_size (const OPSTR& opstr) const
+  {
+    switch (opstr.op)
+    {
+      case OpCode_CharStrings:
+      case OpCode_FDArray:
+        return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (opstr.op);
+
+      default:
+        return opstr.str.len;
+    }
+  }
+
+  unsigned int  charStringsOffset;
+  unsigned int  FDArrayOffset;
+};
+
+struct DictOpSet : OpSet<Number>
+{
+  static inline bool process_op (OpCode op, InterpEnv<Number>& env)
+  {
+    switch (op) {
+      case OpCode_longintdict:  /* 5-byte integer */
+        return env.argStack.push_longint_from_substr (env.substr);
+      case OpCode_BCD:  /* real number */
+        float v;
+        if (unlikely (!env.argStack.check_overflow (1) || !parse_bcd (env.substr, v)))
+          return false;
+        env.argStack.push_real (v);
+        return true;
+
+      default:
+        return OpSet<Number>::process_op (op, env);
+    }
+
+    return true;
+  }
+
+  static inline bool is_hint_op (OpCode op)
+  {
+    switch (op)
+    {
+      case OpCode_BlueValues:
+      case OpCode_OtherBlues:
+      case OpCode_FamilyBlues:
+      case OpCode_FamilyOtherBlues:
+      case OpCode_StemSnapH:
+      case OpCode_StemSnapV:
+      case OpCode_StdHW:
+      case OpCode_StdVW:
+      case OpCode_BlueScale:
+      case OpCode_BlueShift:
+      case OpCode_BlueFuzz:
+      case OpCode_ForceBold:
+      case OpCode_LanguageGroup:
+      case OpCode_ExpansionFactor:
+        return true;
+      default:
+        return false;
+    }
+  }
+};
+
+template <typename VAL=OpStr>
+struct TopDictOpSet : DictOpSet
+{
+  static inline bool process_op (OpCode op, InterpEnv<Number>& env, TopDictValues<VAL> & dictval)
+  {
+    switch (op) {
+      case OpCode_CharStrings:
+        if (unlikely (!env.argStack.check_pop_uint (dictval.charStringsOffset)))
+          return false;
+        env.clear_args ();
+        break;
+      case OpCode_FDArray:
+        if (unlikely (!env.argStack.check_pop_uint (dictval.FDArrayOffset)))
+          return false;
+        env.clear_args ();
+        break;
+      default:
+        return DictOpSet::process_op (op, env);
+    }
+
+    return true;
+  }
+};
+
+template <typename OPSET, typename PARAM, typename ENV=NumInterpEnv>
+struct DictInterpreter : Interpreter<ENV>
+{
+  inline bool interpret (PARAM& param)
+  {
+    param.init ();
+    do
+    {
+      OpCode op;
+      if (unlikely (!SUPER::env.fetch_op (op) ||
+                    !OPSET::process_op (op, SUPER::env, param)))
+        return false;
+    } while (SUPER::env.substr.avail ());
+    
+    return true;
+  }
+
+  private:
+  typedef Interpreter<ENV> SUPER;
+};
+
+} /* namespace CFF */
+
+#endif /* HB_CFF_INTERP_DICT_COMMON_HH */
diff --git a/src/hb-cff1-interp-cs.hh b/src/hb-cff1-interp-cs.hh
new file mode 100644
index 0000000..01faa44
--- /dev/null
+++ b/src/hb-cff1-interp-cs.hh
@@ -0,0 +1,216 @@
+/*
+ * 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_CFF1_INTERP_CS_HH
+#define HB_CFF1_INTERP_CS_HH
+
+#include "hb.hh"
+#include "hb-cff-interp-cs-common.hh"
+
+namespace CFF {
+
+using namespace OT;
+
+struct CFF1CSInterpEnv : CSInterpEnv<Number, CFF1Subrs>
+{
+  template <typename ACC>
+  inline void init (const ByteStr &str, ACC &acc, unsigned int fd)
+  {
+    SUPER::init (str, *acc.globalSubrs, *acc.privateDicts[fd].localSubrs);
+    processed_width = false;
+    has_width = false;
+    for (unsigned int i = 0; i < kTransientArraySize; i++)
+      transient_array[i].set_int (0);
+  }
+
+  bool check_transient_array_index (unsigned int i) const
+  { return i < kTransientArraySize; }
+
+  inline void check_width (void)
+  {
+    if (!processed_width)
+    {
+      if ((SUPER::argStack.count & 1) != 0)
+      {
+        width = SUPER::argStack.elements[0];
+        has_width = true;
+      }
+      processed_width = true;
+    }
+  }
+
+  bool          processed_width;
+  bool          has_width;
+  Number        width;
+
+  static const unsigned int kTransientArraySize = 32;
+  Number  transient_array[kTransientArraySize];
+
+  private:
+  typedef CSInterpEnv<Number, CFF1Subrs> SUPER;
+};
+
+template <typename OPSET, typename PARAM>
+struct CFF1CSOpSet : CSOpSet<Number, OPSET, CFF1CSInterpEnv, PARAM>
+{
+  static inline bool process_op (OpCode op, CFF1CSInterpEnv &env, PARAM& param)
+  {
+    Number  n1, n2;
+
+    switch (op) {
+
+      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));
+        break;
+      case OpCode_or:
+        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));
+        break;
+      case OpCode_not:
+        if (unlikely (!env.argStack.check_pop_num (n1))) return false;
+        env.argStack.push_int (n1.to_real() == 0.0f);
+        break;
+      case OpCode_abs:
+        if (unlikely (!env.argStack.check_pop_num (n1)))  return false;
+        env.argStack.push_real (fabs(n1.to_real ()));
+        break;
+      case OpCode_add:
+        if (unlikely (!env.argStack.check_pop_num2 (n1, n2))) return false;
+        env.argStack.push_real (n1.to_real() + n2.to_real());
+        break;
+      case OpCode_sub:
+        if (unlikely (!env.argStack.check_pop_num2 (n1, n2))) return false;
+        env.argStack.push_real (n1.to_real() - n2.to_real());
+        break;
+      case OpCode_div:
+        if (unlikely (!env.argStack.check_pop_num2 (n1, n2))) return false;
+        if (unlikely (n2.to_real() == 0.0f))
+          env.argStack.push_int (0);
+        else
+          env.argStack.push_real (n1.to_real() / n2.to_real());
+        break;
+      case OpCode_neg:
+        if (unlikely (!env.argStack.check_pop_num (n1))) return false;
+        env.argStack.push_real (-n1.to_real ());
+        break;
+      case OpCode_eq:
+        if (unlikely (!env.argStack.check_pop_num2 (n1, n2))) return false;
+        env.argStack.push_int (n1.to_real() == n2.to_real());
+        break;
+      case OpCode_drop:
+        if (unlikely (!env.argStack.check_pop_num (n1))) return false;
+        break;
+      case OpCode_put:
+        if (unlikely (!env.argStack.check_pop_num2 (n1, n2) ||
+                      !env.check_transient_array_index (n2.to_int ()))) return false;
+        env.transient_array[n2.to_int ()] = n1;
+        break;
+      case OpCode_get:
+        if (unlikely (!env.argStack.check_pop_num (n1) ||
+                      !env.check_transient_array_index (n1.to_int ()))) return false;
+        env.argStack.push (env.transient_array[n1.to_int ()]);
+        break;
+      case OpCode_ifelse:
+        {
+          if (unlikely (!env.argStack.check_pop_num2 (n1, n2))) return false;
+          bool  test = n1.to_real () <= n2.to_real ();
+          if (unlikely (!env.argStack.check_pop_num2 (n1, n2))) return false;
+          env.argStack.push (test? n1: n2);
+        }
+        break;
+      case OpCode_random:
+          if (unlikely (!env.argStack.check_overflow (1))) return false;
+          env.argStack.push_int (1);  /* we can't deal with random behavior; make it constant */
+      case OpCode_mul:
+        if (unlikely (!env.argStack.check_pop_num2 (n1, n2))) return false;
+        env.argStack.push_real (n1.to_real() * n2.to_real());
+        break;
+      case OpCode_sqrt:
+        if (unlikely (!env.argStack.check_pop_num (n1))) return false;
+        env.argStack.push_real ((float)sqrt (n1.to_real ()));
+        break;
+      case OpCode_dup:
+        if (unlikely (!env.argStack.check_pop_num (n1))) return false;
+        env.argStack.push (n1);
+        env.argStack.push (n1);
+        break;
+      case OpCode_exch:
+        if (unlikely (!env.argStack.check_pop_num2 (n1, n2))) return false;
+        env.argStack.push (n2);
+        env.argStack.push (n1);
+        break;
+      case OpCode_index:
+        {
+          if (unlikely (!env.argStack.check_pop_num (n1))) return false;
+          int i = n1.to_int ();
+          if (i < 0) i = 0;
+          if (unlikely (i >= env.argStack.count || !env.argStack.check_overflow (1))) return false;
+          env.argStack.push (env.argStack.elements[env.argStack.count - i - 1]);
+        }
+        break;
+      case OpCode_roll:
+        {
+          if (unlikely (!env.argStack.check_pop_num2 (n1, n2))) return false;
+          int n = n1.to_int ();
+          int j = n2.to_int ();
+          if (unlikely (n < 0 || n > env.argStack.count)) return false;
+          if (likely (n > 0))
+          {
+            if (j < 0)
+              j = n - (-j % n);
+            j %= n;
+            unsigned int top = env.argStack.count - 1;
+            unsigned int bot = top - n + 1;
+            env.argStack.reverse_range (top - j + 1, top);
+            env.argStack.reverse_range (bot, top - j);
+            env.argStack.reverse_range (bot, top);
+          }
+        }
+        break;
+      default:
+        if (unlikely (!SUPER::process_op (op, env, param)))
+          return false;
+        break;
+    }
+    return true;
+  }
+
+  static inline void flush_args (CFF1CSInterpEnv &env, PARAM& param)
+  {
+    env.check_width ();
+    SUPER::flush_args (env, param);
+  }
+
+  private:
+  typedef CSOpSet<Number, OPSET, CFF1CSInterpEnv, PARAM>  SUPER;
+};
+
+template <typename OPSET, typename PARAM>
+struct CFF1CSInterpreter : CSInterpreter<CFF1CSInterpEnv, OPSET, PARAM> {};
+
+} /* namespace CFF */
+
+#endif /* HB_CFF1_INTERP_CS_HH */
diff --git a/src/hb-cff2-interp-cs.hh b/src/hb-cff2-interp-cs.hh
new file mode 100644
index 0000000..b5f1f8f
--- /dev/null
+++ b/src/hb-cff2-interp-cs.hh
@@ -0,0 +1,183 @@
+/*
+ * 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.hh"
+#include "hb-cff-interp-cs-common.hh"
+
+namespace CFF {
+
+using namespace OT;
+
+struct BlendArg : Number
+{
+  inline void init (void)
+  {
+    Number::init ();
+    deltas.init ();
+  }
+
+  inline void fini (void)
+  {
+    Number::fini ();
+
+    for (unsigned int i = 0; i < deltas.len; i++)
+      deltas[i].fini ();
+    deltas.fini ();
+  }
+
+  inline void set_int (int v) { reset_blends (); Number::set_int (v); }
+  inline void set_fixed (int32_t v) { reset_blends (); Number::set_fixed (v); }
+  inline void set_real (float v) { reset_blends (); Number::set_real (v); }
+
+  inline void set_blends (unsigned int numValues_, unsigned int valueIndex_,
+                          unsigned int numBlends, const BlendArg *blends_)
+  {
+    numValues = numValues_;
+    valueIndex = valueIndex_;
+    deltas.resize (numBlends);
+    for (unsigned int i = 0; i < numBlends; i++)
+      deltas[i] = blends_[i];
+  }
+
+  inline bool blended (void) const { return deltas.len > 0; }
+  inline void reset_blends (void)
+  {
+    numValues = valueIndex = 0;
+    deltas.resize (0);
+  }
+
+  unsigned int numValues;
+  unsigned int valueIndex;
+  hb_vector_t<Number> deltas;
+};
+
+typedef InterpEnv<BlendArg> BlendInterpEnv;
+
+struct CFF2CSInterpEnv : CSInterpEnv<BlendArg, CFF2Subrs>
+{
+  template <typename ACC>
+  inline void init (const ByteStr &str, ACC &acc, unsigned int fd)
+  {
+    SUPER::init (str, *acc.globalSubrs, *acc.privateDicts[fd].localSubrs);
+    set_region_count (acc.region_count);
+    set_vsindex (acc.privateDicts[fd].vsindex);
+  }
+
+  inline bool fetch_op (OpCode &op)
+  {
+    if (unlikely (this->substr.avail ()))
+      return SUPER::fetch_op (op);
+
+    /* make up return or endchar op */
+    if (this->callStack.check_underflow ())
+      op = OpCode_return;
+    else
+      op = OpCode_endchar;
+    return true;
+  }
+
+  inline void process_vsindex (void)
+  {
+    unsigned int  index;
+    if (likely (argStack.check_pop_uint (index)))
+      set_vsindex (argStack.check_pop_uint (index));
+  }
+
+  inline unsigned int get_region_count (void) const { return region_count; }
+  inline void         set_region_count (unsigned int region_count_) { region_count = region_count_; }
+  inline unsigned int get_vsindex (void) const { return vsindex; }
+  inline void         set_vsindex (unsigned int vsindex_) { vsindex = vsindex_; }
+
+  protected:
+  unsigned int  region_count;
+  unsigned int  vsindex;
+
+  typedef CSInterpEnv<BlendArg, CFF2Subrs> SUPER;
+};
+
+template <typename OPSET, typename PARAM>
+struct CFF2CSOpSet : CSOpSet<BlendArg, OPSET, CFF2CSInterpEnv, PARAM>
+{
+  static inline bool process_op (OpCode op, CFF2CSInterpEnv &env, PARAM& param)
+  {
+    switch (op) {
+      case OpCode_callsubr:
+      case OpCode_callgsubr:
+        /* a subroutine number shoudln't be a blended value */
+        return (!env.argStack.peek ().blended () &&
+                SUPER::process_op (op, env, param));
+
+      case OpCode_blendcs:
+        return OPSET::process_blend (env, param);
+
+      case OpCode_vsindexcs:
+        if (unlikely (env.argStack.peek ().blended ()))
+          return false;
+        OPSET::process_vsindex (env, param);
+        break;
+
+      default:
+        return SUPER::process_op (op, env, param);
+    }
+    return true;
+  }
+
+  static inline bool process_blend (CFF2CSInterpEnv &env, PARAM& param)
+  {
+    unsigned int n, k;
+
+    k = env.get_region_count ();
+    if (unlikely (!env.argStack.check_pop_uint (n) ||
+                  (k+1) * n > env.argStack.get_count ()))
+      return false;
+    /* copy the blend values into blend array of the default values */
+    unsigned int start = env.argStack.get_count () - ((k+1) * n);
+    for (unsigned int i = 0; i < n; i++)
+      env.argStack.elements[start + i].set_blends (n, i, k, &env.argStack.elements[start + n + (i * k)]);
+
+    /* pop off blend values leaving default values now adorned with blend values */
+    env.argStack.count -= k * n;
+    return true;
+  }
+
+  static inline void process_vsindex (CFF2CSInterpEnv &env, PARAM& param)
+  {
+    env.process_vsindex ();
+    OPSET::flush_n_args_and_op (OpCode_vsindexcs, 1, env, param);
+  }
+
+  private:
+  typedef CSOpSet<BlendArg, OPSET, CFF2CSInterpEnv, PARAM>  SUPER;
+};
+
+template <typename OPSET, typename PARAM>
+struct CFF2CSInterpreter : CSInterpreter<CFF2CSInterpEnv, OPSET, PARAM> {};
+
+} /* namespace CFF */
+
+#endif /* HB_CFF2_INTERP_CS_HH */
diff --git a/src/hb-null.hh b/src/hb-null.hh
index 7128f0a..b7f1c1d 100644
--- a/src/hb-null.hh
+++ b/src/hb-null.hh
@@ -36,7 +36,7 @@
 
 /* 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)];
diff --git a/src/hb-ot-cff-common.hh b/src/hb-ot-cff-common.hh
new file mode 100644
index 0000000..8b6b6b2
--- /dev/null
+++ b/src/hb-ot-cff-common.hh
@@ -0,0 +1,695 @@
+/*
+ * 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_OT_CFF_COMMON_HH
+#define HB_OT_CFF_COMMON_HH
+
+#include "hb-open-type.hh"
+#include "hb-ot-layout-common.hh"
+#include "hb-cff-interp-dict-common.hh"
+#include "hb-subset-plan.hh"
+
+namespace CFF {
+
+using namespace OT;
+
+#define CFF_UNDEF_CODE  0xFFFFFFFF
+
+/* utility macro */
+template<typename Type>
+static inline const Type& StructAtOffsetOrNull(const void *P, unsigned int offset)
+{ return offset? (* reinterpret_cast<const Type*> ((const char *) P + offset)): Null(Type); }
+
+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 */
+template <typename COUNT>
+struct CFFIndex
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely ((count.sanitize (c) && count == 0) || /* empty INDEX */
+                          (c->check_struct (this) && offSize >= 1 && offSize <= 4 &&
+                           c->check_array (offsets, offSize, count + 1) &&
+                           c->check_array (data_base (), 1, max_offset () - 1))));
+  }
+
+  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 CFFIndex &src)
+  {
+    TRACE_SERIALIZE (this);
+    unsigned int size = src.get_size ();
+    CFFIndex *dest = c->allocate_size<CFFIndex> (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> &byteArray)
+  {
+    TRACE_SERIALIZE (this);
+    /* serialize CFFIndex header */
+    if (unlikely (!c->extend_min (*this))) return_trace (false);
+    this->count.set (byteArray.len);
+    this->offSize.set (offSize_);
+    if (!unlikely (c->allocate_size<HBUINT8> (offSize_ * (byteArray.len + 1))))
+      return_trace (false);
+  
+    /* serialize indices */
+    unsigned int  offset = 1;
+    unsigned int  i = 0;
+    for (; i < byteArray.len; i++)
+    {
+      set_offset_at (i, offset);
+      offset += byteArray[i].get_size ();
+    }
+    set_offset_at (i, offset);
+
+    /* serialize data */
+    for (unsigned int i = 0; i < byteArray.len; i++)
+    {
+      ByteStr  *dest = c->start_embed<ByteStr> ();
+      if (unlikely (dest == nullptr ||
+                    !dest->serialize (c, byteArray[i])))
+        return_trace (false);
+    }
+    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
+  {
+    assert (index <= count);
+    const HBUINT8 *p = offsets + offSize * index;
+    unsigned int size = offSize;
+    unsigned int offset = 0;
+    for (; size; size--)
+      offset = (offset << 8) + *p++;
+    return offset;
+  }
+
+  inline const unsigned int length_at (unsigned int index) const
+  { return offset_at (index + 1) - offset_at (index); }
+
+  inline const char *data_base (void) const
+  { return (const char *)this + min_size + offset_array_size (); }
+
+  inline unsigned int data_size (void) const
+  { return HBINT8::static_size; };
+
+  ByteStr operator [] (unsigned int index) const
+  {
+    if (likely (index < count))
+      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
+  {
+    if (this != &Null(CFFIndex))
+    {
+      if (count > 0)
+        return min_size + offset_array_size () + (offset_at (count) - 1);
+      else
+        return count.static_size;  /* empty CFFIndex contains count only */
+    }
+    else
+      return 0;
+  }
+
+  protected:
+  inline unsigned int max_offset (void) const
+  {
+    unsigned int max = 0;
+    for (unsigned int i = 0; i <= count; i++)
+    {
+      unsigned int off = offset_at (i);
+      if (off > max) max = off;
+    }
+    return max;
+  }
+
+  public:
+  COUNT     count;        /* Number of object data. Note there are (count+1) offsets */
+  HBUINT8   offSize;      /* The byte size of each offset in the offsets array. */
+  HBUINT8   offsets[VAR]; /* The array of (count + 1) offsets into objects array (1-base). */
+  /* HBUINT8 data[VAR];      Object data */
+  public:
+  DEFINE_SIZE_ARRAY (COUNT::static_size + HBUINT8::static_size, offsets);
+};
+
+template <typename COUNT, typename TYPE>
+struct CFFIndexOf : CFFIndex<COUNT>
+{
+  inline const ByteStr operator [] (unsigned int index) const
+  {
+    if (likely (index < CFFIndex<COUNT>::count))
+      return ByteStr (CFFIndex<COUNT>::data_base () + CFFIndex<COUNT>::offset_at (index) - 1, CFFIndex<COUNT>::length_at (index));
+    return Null(ByteStr);
+  }
+
+  template <typename DATA, typename PARAM1, typename PARAM2>
+  inline bool serialize (hb_serialize_context_t *c,
+                         unsigned int offSize_,
+                         const DATA *dataArray,
+                         unsigned int dataArrayLen,
+                         const hb_vector_t<unsigned int> &dataSizeArray,
+                         const PARAM1 &param1,
+                         const PARAM2 &param2)
+  {
+    TRACE_SERIALIZE (this);
+    /* serialize CFFIndex header */
+    if (unlikely (!c->extend_min (*this))) return_trace (false);
+    this->count.set (dataArrayLen);
+    this->offSize.set (offSize_);
+    if (!unlikely (c->allocate_size<HBUINT8> (offSize_ * (dataArrayLen + 1))))
+      return_trace (false);
+  
+    /* serialize indices */
+    unsigned int  offset = 1;
+    unsigned int  i = 0;
+    for (; i < dataArrayLen; i++)
+    {
+      CFFIndex<COUNT>::set_offset_at (i, offset);
+      offset += dataSizeArray[i];
+    }
+    CFFIndex<COUNT>::set_offset_at (i, offset);
+
+    /* serialize data */
+    for (unsigned int i = 0; i < dataArrayLen; i++)
+    {
+      TYPE  *dest = c->start_embed<TYPE> ();
+      if (unlikely (dest == nullptr ||
+                    !dest->serialize (c, dataArray[i], param1, param2)))
+        return_trace (false);
+    }
+    return_trace (true);
+  }
+
+  /* in parallel to above */
+  template <typename DATA, typename PARAM>
+  inline static unsigned int calculate_serialized_size (unsigned int &offSize_ /* OUT */,
+                                                        const DATA *dataArray,
+                                                        unsigned int dataArrayLen,
+                                                        hb_vector_t<unsigned int> &dataSizeArray, /* OUT */
+                                                        const PARAM &param)
+  {
+    /* determine offset size */
+    unsigned int  totalDataSize = 0;
+    for (unsigned int i = 0; i < dataArrayLen; i++)
+    {
+      unsigned int dataSize = TYPE::calculate_serialized_size (dataArray[i], param);
+      dataSizeArray[i] = dataSize;
+      totalDataSize += dataSize;
+    }
+    offSize_ = calcOffSize (totalDataSize);
+
+    return CFFIndex<COUNT>::calculate_serialized_size (offSize_, dataArrayLen, totalDataSize);
+  }
+};
+
+/* Top Dict, Font Dict, Private Dict */
+struct Dict : UnsizedByteStr
+{
+  template <typename DICTVAL, typename OP_SERIALIZER, typename PARAM>
+  inline bool serialize (hb_serialize_context_t *c,
+                        const DICTVAL &dictval,
+                        OP_SERIALIZER& opszr,
+                        PARAM& param)
+  {
+    TRACE_SERIALIZE (this);
+    for (unsigned int i = 0; i < dictval.getNumValues (); i++)
+    {
+      if (unlikely (!opszr.serialize (c, dictval[i], param)))
+        return_trace (false);
+    }
+    return_trace (true);
+  }
+
+  /* in parallel to above */
+  template <typename DICTVAL, typename OP_SERIALIZER>
+  inline static unsigned int calculate_serialized_size (const DICTVAL &dictval,
+                                                        OP_SERIALIZER& opszr)
+  {
+    unsigned int size = 0;
+    for (unsigned int i = 0; i < dictval.getNumValues (); i++)
+      size += opszr.calculate_serialized_size (dictval[i]);
+    return size;
+  }
+
+  template <typename INTTYPE, int minVal, int maxVal>
+  inline static bool serialize_int_op (hb_serialize_context_t *c, OpCode op, int value, OpCode intOp)
+  {
+    // 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> (OpCode_Size (op));
+    if (unlikely (p == nullptr)) return_trace (false);
+    if (Is_OpCode_ESC (op))
+    {
+      p->set (OpCode_escape);
+      op = Unmake_OpCode_ESC (op);
+      p++;
+    }
+    p->set (op);
+    return_trace (true);
+  }
+
+  inline static bool serialize_uint4_op (hb_serialize_context_t *c, OpCode op, int value)
+  { return serialize_int_op<HBUINT32, 0, 0x7FFFFFFF> (c, op, value, OpCode_longintdict); }
+
+  inline static bool serialize_uint2_op (hb_serialize_context_t *c, OpCode op, int value)
+  { return serialize_int_op<HBUINT16, 0, 0x7FFF> (c, op, value, OpCode_shortint); }
+
+  inline static bool serialize_offset4_op (hb_serialize_context_t *c, OpCode op, int value)
+  {
+    if (value == 0) return true;
+    return serialize_uint4_op (c, op, value);
+  }
+
+  inline static bool serialize_offset2_op (hb_serialize_context_t *c, OpCode op, int value)
+  {
+    if (value == 0) return true;
+    return serialize_uint2_op (c, op, value);
+  }
+};
+
+struct TopDict : Dict {};
+struct FontDict : Dict {};
+struct PrivateDict : Dict {};
+
+struct TableInfo
+{
+  void init (void) { offSize = offset = size = 0; }
+
+  unsigned int    offset;
+  unsigned int    size;
+  unsigned int    offSize;
+};
+
+/* used to remap font index or SID from fullset to subset.
+ * set to CFF_UNDEF_CODE if excluded from subset */
+struct Remap : hb_vector_t<hb_codepoint_t>
+{
+  inline void init (void)
+  { hb_vector_t<hb_codepoint_t>::init (); }
+
+  inline void fini (void)
+  { hb_vector_t<hb_codepoint_t>::fini (); }
+
+  inline bool reset (unsigned int count)
+  {
+    if (unlikely (!hb_vector_t<hb_codepoint_t>::resize (count)))
+      return false;
+    for (unsigned int i = 0; i < len; i++)
+      (*this)[i] = CFF_UNDEF_CODE;
+    count = 0;
+    return true;
+  }
+
+  inline bool fullset (void) const
+  {
+    for (unsigned int i = 0; i < len; i++)
+      if (hb_vector_t<hb_codepoint_t>::operator[] (i) == CFF_UNDEF_CODE)
+        return false;
+    return true;
+  }
+
+  inline bool excludes (hb_codepoint_t id) const
+  { return (id < len) && ((*this)[id] == CFF_UNDEF_CODE); }
+
+  inline hb_codepoint_t operator[] (hb_codepoint_t i) const
+  {
+    if (fullset ())
+      return i;
+    else
+      return hb_vector_t<hb_codepoint_t>::operator[] (i);
+  }
+
+  inline hb_codepoint_t &operator[] (hb_codepoint_t i)
+  {
+    assert (i < len);
+    return hb_vector_t<hb_codepoint_t>::operator[] (i);
+  }
+
+  inline unsigned int add (unsigned int i)
+  {
+    if ((*this)[i] == CFF_UNDEF_CODE)
+      (*this)[i] = count++;
+    return (*this)[i];
+  }
+
+  inline hb_codepoint_t get_count (void) const
+  { return count; }
+
+  protected:
+  hb_codepoint_t  count;
+};
+
+template <typename COUNT>
+struct FDArray : CFFIndexOf<COUNT, FontDict>
+{
+  /* used by CFF1 */
+  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)
+  {
+    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 fid = 0;
+    for (; fid < fontDicts.len; fid++)
+    {
+      CFFIndexOf<COUNT, FontDict>::set_offset_at (fid, offset);
+      offset += FontDict::calculate_serialized_size (fontDicts[fid], opszr);
+    }
+    CFFIndexOf<COUNT, FontDict>::set_offset_at (fid, 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, fontDicts[i])))
+        return_trace (false);
+    }
+    return_trace (true);
+  }
+  
+  /* used by CFF2 */
+  template <typename DICTVAL, typename OP_SERIALIZER>
+  inline bool serialize (hb_serialize_context_t *c,
+                        unsigned int offSize_,
+                        const hb_vector_t<DICTVAL> &fontDicts,
+                        unsigned int fdCount,
+                        const Remap &fdmap,
+                        OP_SERIALIZER& opszr,
+                        const hb_vector_t<TableInfo> &privateInfos)
+  {
+    TRACE_SERIALIZE (this);
+    if (unlikely (!c->extend_min (*this))) return_trace (false);
+    this->count.set (fdCount);
+    this->offSize.set (offSize_);
+    if (!unlikely (c->allocate_size<HBUINT8> (offSize_ * (fdCount + 1))))
+      return_trace (false);
+    
+    /* serialize font dict offsets */
+    unsigned int  offset = 1;
+    unsigned int  fid = 0;
+    for (unsigned i = 0; i < fontDicts.len; i++)
+      if (!fdmap.excludes (i))
+      {
+        CFFIndexOf<COUNT, FontDict>::set_offset_at (fid++, offset);
+        offset += FontDict::calculate_serialized_size (fontDicts[i], opszr);
+      }
+    CFFIndexOf<COUNT, FontDict>::set_offset_at (fid, offset);
+
+    /* serialize font dicts */
+    for (unsigned int i = 0; i < fontDicts.len; i++)
+      if (!fdmap.excludes (i))
+      {
+        FontDict *dict = c->start_embed<FontDict> ();
+        if (unlikely (!dict->serialize (c, fontDicts[i], opszr, privateInfos[fdmap[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,
+                                                        unsigned int fdCount,
+                                                        const Remap &fdmap,
+                                                        OP_SERIALIZER& opszr)
+  {
+    unsigned int dictsSize = 0;
+    for (unsigned int i = 0; i < fontDicts.len; i++)
+      if (!fdmap.excludes (i))
+        dictsSize += FontDict::calculate_serialized_size (fontDicts[i], opszr);
+
+    offSize_ = calcOffSize (dictsSize + 1);
+    return CFFIndex<COUNT>::calculate_serialized_size (offSize_, fdCount, dictsSize);
+  }
+};
+
+/* FDSelect */
+struct FDSelect0 {
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
+  {
+    TRACE_SANITIZE (this);
+    if (unlikely (!(c->check_struct (this))))
+      return_trace (false);
+    for (unsigned int i = 0; i < c->get_num_glyphs (); i++)
+      if (unlikely (!fds[i].sanitize (c)))
+        return_trace (false);
+
+    return_trace (true);
+  }
+
+  inline hb_codepoint_t get_fd (hb_codepoint_t glyph) const
+  {
+    return (hb_codepoint_t)fds[glyph];
+  }
+
+  inline unsigned int get_size (unsigned int num_glyphs) const
+  { return HBUINT8::static_size * num_glyphs; }
+
+  HBUINT8     fds[VAR];
+
+  DEFINE_SIZE_MIN (1);
+};
+
+template <typename GID_TYPE, typename FD_TYPE>
+struct FDSelect3_4_Range {
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (likely (c->check_struct (this) && (first < c->get_num_glyphs ()) && (fd < fdcount)));
+  }
+
+  GID_TYPE    first;
+  FD_TYPE     fd;
+
+  DEFINE_SIZE_STATIC (GID_TYPE::static_size + FD_TYPE::static_size);
+};
+
+template <typename GID_TYPE, typename FD_TYPE>
+struct FDSelect3_4 {
+  inline unsigned int get_size (void) const
+  { return GID_TYPE::static_size * 2 + FDSelect3_4_Range<GID_TYPE, FD_TYPE>::static_size * nRanges; }
+
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
+  {
+    TRACE_SANITIZE (this);
+    if (unlikely (!(c->check_struct (this) && (nRanges > 0) && (ranges[0].first == 0))))
+      return_trace (false);
+
+    for (unsigned int i = 0; i < nRanges; i++)
+    {
+      if (unlikely (!ranges[i].sanitize (c, fdcount)))
+        return_trace (false);
+      if ((0 < i) && unlikely (ranges[i - 1].first >= ranges[i].first))
+        return_trace (false);
+    }
+    if (unlikely (!sentinel().sanitize (c) || (sentinel() != c->get_num_glyphs ())))
+      return_trace (false);
+
+    return_trace (true);
+  }
+
+  inline hb_codepoint_t get_fd (hb_codepoint_t glyph) const
+  {
+    for (unsigned int i = 1; i < nRanges; i++)
+      if (glyph < ranges[i].first)
+        return (hb_codepoint_t)ranges[i - 1].fd;
+
+    assert (false);
+  }
+
+  inline GID_TYPE &sentinel (void)  { return StructAfter<GID_TYPE> (ranges[nRanges - 1]); }
+  inline const GID_TYPE &sentinel (void) const  { return StructAfter<GID_TYPE> (ranges[nRanges - 1]); }
+
+  GID_TYPE         nRanges;
+  FDSelect3_4_Range<GID_TYPE, FD_TYPE>  ranges[VAR];
+  /* GID_TYPE sentinel */
+
+  DEFINE_SIZE_ARRAY (GID_TYPE::static_size * 2, ranges);
+};
+
+typedef FDSelect3_4<HBUINT16, HBUINT8> FDSelect3;
+typedef FDSelect3_4_Range<HBUINT16, HBUINT8> FDSelect3_Range;
+
+struct FDSelect {
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
+  {
+    TRACE_SANITIZE (this);
+
+    return_trace (likely (c->check_struct (this) && (format == 0 || format == 3) &&
+                          (format == 0)?
+                          u.format0.sanitize (c, fdcount):
+                          u.format3.sanitize (c, fdcount)));
+  }
+
+  inline bool serialize (hb_serialize_context_t *c, const FDSelect &src, unsigned int num_glyphs)
+  {
+    TRACE_SERIALIZE (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
+      size += u.format3.get_size ();
+    return size;
+  }
+
+  inline hb_codepoint_t get_fd (hb_codepoint_t glyph) const
+  {
+    if (this == &Null(FDSelect))
+      return 0;
+    if (format == 0)
+      return u.format0.get_fd (glyph);
+    else
+      return u.format3.get_fd (glyph);
+  }
+
+  HBUINT8       format;
+  union {
+    FDSelect0   format0;
+    FDSelect3   format3;
+  } u;
+
+  DEFINE_SIZE_MIN (1);
+};
+
+template <typename COUNT>
+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) || (set == nullptr) || (hb_set_is_empty (set)))
+    {
+      if (!unlikely (c->allocate_size<COUNT> (COUNT::static_size)))
+        return_trace (false);
+      CFFIndex<COUNT>::count.set (0);
+      return_trace (true);
+    }
+    
+    hb_vector_t<ByteStr> bytesArray;
+    bytesArray.init ();
+    if (!bytesArray.resize (subrs.count))
+      return_trace (false);
+    for (hb_codepoint_t i = 0; i < subrs.count; i++)
+      bytesArray[i] = (hb_set_has (set, i))? subrs[i]: nullStr;
+
+    bool result = CFFIndex<COUNT>::serialize (c, offSize, bytesArray);
+    bytesArray.fini ();
+    return_trace (result);
+  }
+  
+  /* 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))
+      return COUNT::static_size;
+
+    unsigned int dataSize = 0;
+    for (hb_codepoint_t i = 0; i < count_; i++)
+    {
+      if (hb_set_has (set, i))
+        dataSize += (*this)[i].len;
+      else
+        dataSize += nullStrSize;
+    }
+    offSize = calcOffSize(dataSize);
+    return CFFIndex<COUNT>::calculate_serialized_size (offSize, count_, dataSize);
+  }
+};
+
+} /* namespace CFF */
+
+#endif /* HB_OT_CFF_COMMON_HH */
+
diff --git a/src/hb-ot-cff1-table.cc b/src/hb-ot-cff1-table.cc
new file mode 100644
index 0000000..9ac2f15
--- /dev/null
+++ b/src/hb-ot-cff1-table.cc
@@ -0,0 +1,130 @@
+/*
+ * 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
+ */
+
+#include "hb-ot-cff1-table.hh"
+
+/* SID to code */
+static const uint8_t standard_encoding [] =
+{
+    0,   32,   33,   34,   35,   36,   37,   38,  39,   40,   41,   42,   43,   44,   45,   46,
+   47,   48,   49,   50,   51,   52,   53,   54,  55,   56,   57,   58,   59,   60,   61,   62,
+   63,   64,   65,   66,   67,   68,   69,   70,  71,   72,   73,   74,   75,   76,   77,   78,
+   79,   80,   81,   82,   83,   84,   85,   86,  87,   88,   89,   90,   91,   92,   93,   94,
+   95,   96,   97,   98,   99,  100,  101,  102, 103,  104,  105,  106,  107,  108,  109,  110,
+  111,  112,  113,  114,  115,  116,  117,  118, 119,  120,  121,  122,  123,  124,  125,  126,
+  161,  162,  163,  164,  165,  166,  167,  168, 169,  170,  171,  172,  173,  174,  175,  177,
+  178,  179,  180,  182,  183,  184,  185,  186, 187,  188,  189,  191,  193,  194,  195,  196,
+  197,  198,  199,  200,  202,  203,  205,  206, 207,  208,  225,  227,  232,  233,  234,  235,
+  241,  245,  248,  249,  250,  251
+};
+
+/* SID to code */
+static const uint8_t expert_encoding [] =
+{
+    0,   32,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,   44,   45,   46,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,   58,   59,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,   47,    0,    0,    0,    0,    0,    0,    0,    0,    0,   87,   88,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,  201,    0,    0,    0,    0,  189,    0,    0,  188,    0,
+    0,    0,    0,  190,  202,    0,    0,    0,    0,  203,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,   33,   34,   36,   37,   38,   39,   40,   41,   42,   43,   48,
+   49,   50,   51,   52,   53,   54,   55,   56,   57,   60,   61,   62,   63,   65,   66,   67,
+   68,   69,   73,   76,   77,   78,   79,   82,   83,   84,   86,   89,   90,   91,   93,   94,
+   95,   96,   97,   98,   99,  100,  101,  102,  103,  104,  105,  106,  107,  108,  109,  110,
+  111,  112,  113,  114,  115,  116,  117,  118,  119,  120,  121,  122,  123,  124,  125,  126,
+  161,  162,  163,  166,  167,  168,  169,  170,  172,  175,  178,  179,  182,  183,  184,  191,
+  192,  193,  194,  195,  196,  197,  200,  204,  205,  206,  207,  208,  209,  210,  211,  212,
+  213,  214,  215,  216,  217,  218,  219,  220,  221,  222,  223,  224,  225,  226,  227,  228,
+  229,  230,  231,  232,  233,  234,  235,  236,  237,  238,  239,  240,  241,  242,  243,  244,
+  245,  246,  247,  248,  249,  250,  251,  252,  253,  254,  255
+};
+
+/* glyph ID to SID */
+static const uint16_t expert_charset [] =
+{
+    0,    1,  229,  230,  231,  232,  233,  234,  235,  236,  237,  238,   13,   14,   15,   99,
+  239,  240,  241,  242,  243,  244,  245,  246,  247,  248,   27,   28,  249,  250,  251,  252,
+  253,  254,  255,  256,  257,  258,  259,  260,  261,  262,  263,  264,  265,  266,  109,  110,
+  267,  268,  269,  270,  271,  272,  273,  274,  275,  276,  277,  278,  279,  280,  281,  282,
+  283,  284,  285,  286,  287,  288,  289,  290,  291,  292,  293,  294,  295,  296,  297,  298,
+  299,  300,  301,  302,  303,  304,  305,  306,  307,  308,  309,  310,  311,  312,  313,  314,
+  315,  316,  317,  318,  158,  155,  163,  319,  320,  321,  322,  323,  324,  325,  326,  150,
+  164,  169,  327,  328,  329,  330,  331,  332,  333,  334,  335,  336,  337,  338,  339,  340,
+  341,  342,  343,  344,  345,  346,  347,  348,  349,  350,  351,  352,  353,  354,  355,  356,
+  357,  358,  359,  360,  361,  362,  363,  364,  365,  366,  367,  368,  369,  370,  371,  372,
+  373,  374,  375,  376,  377,  378
+};
+
+/* glyph ID to SID */
+static const uint16_t expert_subset_charset [] =
+{
+    0,    1,  231,  232,  235,  236,  237,  238,   13,   14,   15,   99,  239,  240,  241,  242,
+  243,  244,  245,  246,  247,  248,   27,   28,  249,  250,  251,  253,  254,  255,  256,  257,
+  258,  259,  260,  261,  262,  263,  264,  265,  266,  109,  110,  267,  268,  269,  270,  272,
+  300,  301,  302,  305,  314,  315,  158,  155,  163,  320,  321,  322,  323,  324,  325,  326,
+  150,  164,  169,  327,  328,  329,  330,  331,  332,  333,  334,  335,  336,  337,  338,  339,
+  340,  341,  342,  343,  344,  345,  346
+};
+
+hb_codepoint_t OT::cff1::lookup_standard_encoding (hb_codepoint_t sid)
+{
+  if (sid < ARRAY_LENGTH (standard_encoding))
+    return (hb_codepoint_t)standard_encoding[sid];
+  else
+    return 0;
+}
+
+hb_codepoint_t OT::cff1::lookup_expert_encoding (hb_codepoint_t sid)
+{
+  if (sid < ARRAY_LENGTH (expert_encoding))
+    return (hb_codepoint_t)expert_encoding[sid];
+  else
+    return 0;
+}
+
+hb_codepoint_t OT::cff1::lookup_expert_charset (hb_codepoint_t glyph)
+{
+  if (glyph < ARRAY_LENGTH (expert_charset))
+    return (hb_codepoint_t)expert_charset[glyph];
+  else
+    return 0;
+}
+
+hb_codepoint_t OT::cff1::lookup_expert_subset_charset (hb_codepoint_t glyph)
+{
+  if (glyph < ARRAY_LENGTH (expert_subset_charset))
+    return (hb_codepoint_t)expert_subset_charset[glyph];
+  else
+    return 0;
+}
diff --git a/src/hb-ot-cff1-table.hh b/src/hb-ot-cff1-table.hh
new file mode 100644
index 0000000..957a35f
--- /dev/null
+++ b/src/hb-ot-cff1-table.hh
@@ -0,0 +1,1240 @@
+/*
+ * 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_OT_CFF1_TABLE_HH
+#define HB_OT_CFF1_TABLE_HH
+
+#include "hb-ot-cff-common.hh"
+#include "hb-subset-cff1.hh"
+
+namespace CFF {
+
+/*
+ * CFF -- Compact Font Format (CFF)
+ * http://www.adobe.com/content/dam/acom/en/devnet/font/pdfs/5176.CFF.pdf
+ */
+#define HB_OT_TAG_cff1 HB_TAG('C','F','F',' ')
+
+#define CFF_UNDEF_SID   CFF_UNDEF_CODE
+
+enum EncodingID { StandardEncoding = 0, ExpertEncoding = 1 };
+enum CharsetID { ISOAdobeCharset = 0, ExpertCharset = 1, ExpertSubsetCharset = 2 };
+
+typedef CFFIndex<HBUINT16>  CFF1Index;
+template <typename Type> struct CFF1IndexOf : CFFIndexOf<HBUINT16, Type> {};
+
+typedef CFFIndex<HBUINT16>  CFF1Index;
+typedef CFF1Index         CFF1CharStrings;
+typedef FDArray<HBUINT16> CFF1FDArray;
+typedef Subrs<HBUINT16>   CFF1Subrs;
+
+struct CFF1FDSelect : FDSelect {};
+
+/* Encoding */
+struct Encoding0 {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) && codes[nCodes - 1].sanitize (c));
+  }
+
+  inline hb_codepoint_t get_code (hb_codepoint_t glyph) const
+  {
+    if (glyph < nCodes)
+    {
+      return (hb_codepoint_t)codes[glyph];
+    }
+    else
+      return CFF_UNDEF_CODE;
+  }
+
+  inline unsigned int get_size (void) const
+  { return HBUINT8::static_size * (nCodes + 1); }
+
+  HBUINT8     nCodes;
+  HBUINT8     codes[VAR];
+
+  DEFINE_SIZE_ARRAY(1, codes);
+};
+
+struct Encoding1_Range {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  HBUINT8   first;
+  HBUINT8   nLeft;
+
+  DEFINE_SIZE_STATIC (2);
+};
+
+struct Encoding1 {
+  inline unsigned int get_size (void) const
+  { return HBUINT8::static_size + Encoding1_Range::static_size * nRanges; }
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) && ((nRanges == 0) || (ranges[nRanges - 1]).sanitize (c)));
+  }
+
+  inline hb_codepoint_t get_code (hb_codepoint_t glyph) const
+  {
+    for (unsigned int i = 0; i < nRanges; i++)
+    {
+      if (glyph <= ranges[i].nLeft)
+      {
+        return (hb_codepoint_t)ranges[i].first + glyph;
+      }
+      glyph -= (ranges[i].nLeft + 1);
+    }
+    return CFF_UNDEF_CODE;
+  }
+
+  HBUINT8           nRanges;
+  Encoding1_Range   ranges[VAR];
+
+  DEFINE_SIZE_ARRAY (1, ranges);
+};
+
+struct SuppEncoding {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  HBUINT8   code;
+  HBUINT16  glyph;
+
+  DEFINE_SIZE_STATIC (3);
+};
+
+struct CFF1SuppEncData {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) && ((nSups == 0) || (supps[nSups - 1]).sanitize (c)));
+  }
+
+  inline void get_codes (hb_codepoint_t sid, hb_vector_t<hb_codepoint_t> &codes) const
+  {
+    for (unsigned int i = 0; i < nSups; i++)
+      if (sid == supps[i].glyph)
+        codes.push (supps[i].code);
+  }
+
+  inline unsigned int get_size (void) const
+  { return HBUINT8::static_size + SuppEncoding::static_size * nSups; }
+
+  HBUINT8         nSups;
+  SuppEncoding   supps[VAR];
+
+  DEFINE_SIZE_ARRAY (1, supps);
+};
+
+struct code_pair
+{
+  hb_codepoint_t  code;
+  hb_codepoint_t  glyph;
+};
+
+struct Encoding {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+
+    if (unlikely (!c->check_struct (this)))
+      return_trace (false);
+    unsigned int fmt = format & 0x7F;
+    if (unlikely (fmt > 1))
+      return_trace (false);
+    if (unlikely (!((fmt == 0)? u.format0.sanitize (c): u.format1.sanitize (c))))
+      return_trace (false);
+    return_trace (((format & 0x80) == 0) || suppEncData ().sanitize (c));
+  }
+
+  /* serialize a fullset Encoding */
+  inline bool serialize (hb_serialize_context_t *c, const Encoding &src)
+  {
+    TRACE_SERIALIZE (this);
+    unsigned int size = src.get_size ();
+    Encoding *dest = c->allocate_size<Encoding> (size);
+    if (unlikely (dest == nullptr)) return_trace (false);
+    memcpy (dest, &src, size);
+    return_trace (true);
+  }
+
+  /* serialize a subset Encoding */
+  inline bool serialize (hb_serialize_context_t *c,
+                         uint8_t format,
+                         unsigned int enc_count,
+                         const hb_vector_t<code_pair>& code_ranges,
+                         const hb_vector_t<code_pair>& supp_codes)
+  {
+    TRACE_SERIALIZE (this);
+    Encoding *dest = c->extend_min (*this);
+    if (unlikely (dest == nullptr)) return_trace (false);
+    dest->format.set (format | ((supp_codes.len > 0)? 0x80: 0));
+    if (format == 0)
+    {
+      Encoding0 *fmt0 = c->allocate_size<Encoding0> (Encoding0::min_size + HBUINT8::static_size * enc_count);
+    if (unlikely (fmt0 == nullptr)) return_trace (false);
+      fmt0->nCodes.set (enc_count);
+      unsigned int glyph = 0;
+      for (unsigned int i = 0; i < code_ranges.len; i++)
+      {
+        hb_codepoint_t code = code_ranges[i].code;
+        for (int left = (int)code_ranges[i].glyph; left >= 0; left--)
+          fmt0->codes[glyph++].set (code++);
+        assert ((glyph <= 0x100) && (code <= 0x100));
+      }
+    }
+    else
+    {
+      Encoding1 *fmt1 = c->allocate_size<Encoding1> (Encoding1::min_size + Encoding1_Range::static_size * code_ranges.len);
+      if (unlikely (fmt1 == nullptr)) return_trace (false);
+      fmt1->nRanges.set (code_ranges.len);
+      for (unsigned int i = 0; i < code_ranges.len; i++)
+      {
+        assert ((code_ranges[i].code <= 0xFF) && (code_ranges[i].glyph <= 0xFF));
+        fmt1->ranges[i].first.set (code_ranges[i].code);
+        fmt1->ranges[i].nLeft.set (code_ranges[i].glyph);
+      }
+    }
+    if (supp_codes.len > 0)
+    {
+      CFF1SuppEncData *suppData = c->allocate_size<CFF1SuppEncData> (CFF1SuppEncData::min_size + SuppEncoding::static_size * supp_codes.len);
+      if (unlikely (suppData == nullptr)) return_trace (false);
+      suppData->nSups.set (supp_codes.len);
+      for (unsigned int i = 0; i < supp_codes.len; i++)
+      {
+        suppData->supps[i].code.set (supp_codes[i].code);
+        suppData->supps[i].glyph.set (supp_codes[i].glyph); /* actually SID */
+      }
+    }
+    return_trace (true);
+  }
+
+  /* parallel to above: calculate the size of a subset Encoding */
+  static inline unsigned int calculate_serialized_size (
+                        uint8_t format,
+                        unsigned int enc_count,
+                        unsigned int supp_count)
+  {
+    unsigned int  size = min_size;
+    if (format == 0)
+      size += Encoding0::min_size + HBUINT8::static_size * enc_count;
+    else
+      size += Encoding1::min_size + Encoding1_Range::static_size * enc_count;
+    if (supp_count > 0)
+      size += CFF1SuppEncData::min_size + SuppEncoding::static_size * supp_count;
+    return size;
+  }
+
+  inline unsigned int get_size (void) const
+  {
+    unsigned int size = min_size;
+    if (table_format () == 0)
+      size += u.format0.get_size ();
+    else
+      size += u.format1.get_size ();
+    if (has_supplement ())
+      size += suppEncData ().get_size ();
+    return size;
+  }
+
+  inline hb_codepoint_t get_code (hb_codepoint_t glyph) const
+  {
+    if (table_format () == 0)
+      return u.format0.get_code (glyph);
+    else
+      return u.format1.get_code (glyph);
+  }
+
+  inline uint8_t table_format (void) const { return (format & 0x7F); }
+  inline bool  has_supplement (void) const { return (format & 0x80) != 0; }
+
+  inline void get_supplement_codes (hb_codepoint_t sid, hb_vector_t<hb_codepoint_t> &codes) const
+  {
+    codes.resize (0);
+    if (has_supplement ())
+      suppEncData().get_codes (sid, codes);
+  }
+
+  protected:
+  inline const CFF1SuppEncData &suppEncData (void) const
+  {
+    if ((format & 0x7F) == 0)
+      return StructAfter<CFF1SuppEncData> (u.format0.codes[u.format0.nCodes-1]);
+    else
+      return StructAfter<CFF1SuppEncData> (u.format1.ranges[u.format1.nRanges-1]);
+  }
+
+  public:
+  HBUINT8       format;
+
+  union {
+    Encoding0   format0;
+    Encoding1   format1;
+  } u;
+  /* CFF1SuppEncData  suppEncData; */
+
+  DEFINE_SIZE_MIN (1);
+};
+
+/* Charset */
+struct Charset0 {
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int num_glyphs) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) && sids[num_glyphs - 1].sanitize (c));
+  }
+
+  inline hb_codepoint_t get_sid (hb_codepoint_t glyph) const
+  {
+    if (glyph == 0)
+      return 0;
+    else
+      return sids[glyph - 1];
+  }
+
+  inline unsigned int get_size (unsigned int num_glyphs) const
+  {
+    assert (num_glyphs > 0);
+    return HBUINT16::static_size * (num_glyphs - 1);
+  }
+
+  HBUINT16  sids[VAR];
+
+  DEFINE_SIZE_ARRAY(0, sids);
+};
+
+template <typename TYPE>
+struct Charset_Range {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this));
+  }
+
+  HBUINT16  first;
+  TYPE      nLeft;
+
+  DEFINE_SIZE_STATIC (HBUINT16::static_size + TYPE::static_size);
+};
+
+template <typename TYPE>
+struct Charset1_2 {
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int num_glyphs) const
+  {
+    TRACE_SANITIZE (this);
+    if (unlikely (!c->check_struct (this)))
+      return_trace (false);
+    num_glyphs--;
+    for (unsigned int i = 0; num_glyphs > 0; i++)
+    {
+      if (unlikely (!ranges[i].sanitize (c) || (num_glyphs < ranges[i].nLeft + 1)))
+        return_trace (false);
+      num_glyphs -= (ranges[i].nLeft + 1);
+    }
+    return_trace (true);
+  }
+
+  inline hb_codepoint_t get_sid (hb_codepoint_t glyph) const
+  {
+    if (glyph == 0) return 0;
+    glyph--;
+    for (unsigned int i = 0;; i++)
+    {
+      if (glyph <= ranges[i].nLeft)
+        return (hb_codepoint_t)ranges[i].first + glyph;
+      glyph -= (ranges[i].nLeft + 1);
+    }
+  
+    return 0;
+  }
+
+  inline unsigned int get_size (unsigned int num_glyphs) const
+  {
+    unsigned int size = HBUINT8::static_size;
+    int glyph = (int)num_glyphs;
+  
+    assert (glyph > 0);
+    glyph--;
+    for (unsigned int i = 0; glyph > 0; i++)
+    {
+      glyph -= (ranges[i].nLeft + 1);
+      size += Charset_Range<TYPE>::static_size;
+    }
+
+    return size;
+  }
+
+  Charset_Range<TYPE>   ranges[VAR];
+
+  DEFINE_SIZE_ARRAY (0, ranges);
+};
+
+typedef Charset1_2<HBUINT8>     Charset1;
+typedef Charset1_2<HBUINT16>    Charset2;
+typedef Charset_Range<HBUINT8>  Charset1_Range;
+typedef Charset_Range<HBUINT16> Charset2_Range;
+
+struct Charset {
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+
+    if (unlikely (!c->check_struct (this)))
+      return_trace (false);
+    if (format == 0)
+      return_trace (u.format0.sanitize (c, c->get_num_glyphs ()));
+    else if (format == 1)
+      return_trace (u.format1.sanitize (c, c->get_num_glyphs ()));
+    else if (likely (format == 2))
+      return_trace (u.format2.sanitize (c, c->get_num_glyphs ()));
+    else
+      return_trace (false);
+  }
+
+  /* serialize a fullset Charset */
+  inline bool serialize (hb_serialize_context_t *c, const Charset &src, unsigned int num_glyphs)
+  {
+    TRACE_SERIALIZE (this);
+    unsigned int size = src.get_size (num_glyphs);
+    Charset *dest = c->allocate_size<Charset> (size);
+    if (unlikely (dest == nullptr)) return_trace (false);
+    memcpy (dest, &src, size);
+    return_trace (true);
+  }
+
+  /* serialize a subset Charset */
+  inline bool serialize (hb_serialize_context_t *c,
+                         uint8_t format,
+                         unsigned int num_glyphs,
+                         const hb_vector_t<code_pair>& sid_ranges)
+  {
+    TRACE_SERIALIZE (this);
+    Charset *dest = c->extend_min (*this);
+    if (unlikely (dest == nullptr)) return_trace (false);
+    dest->format.set (format);
+    if (format == 0)
+    {
+      Charset0 *fmt0 = c->allocate_size<Charset0> (Charset0::min_size + HBUINT8::static_size * (num_glyphs - 1));
+    if (unlikely (fmt0 == nullptr)) return_trace (false);
+      unsigned int glyph = 0;
+      for (unsigned int i = 0; i < sid_ranges.len; i++)
+      {
+        hb_codepoint_t sid = sid_ranges[i].code;
+        for (int left = (int)sid_ranges[i].glyph; left >= 0; left--)
+          fmt0->sids[glyph++].set (sid++);
+      }
+    }
+    else if (format == 1)
+    {
+      Charset1 *fmt1 = c->allocate_size<Charset1> (Charset1::min_size + Charset1_Range::static_size * sid_ranges.len);
+      if (unlikely (fmt1 == nullptr)) return_trace (false);
+      for (unsigned int i = 0; i < sid_ranges.len; i++)
+      {
+        assert (sid_ranges[i].glyph <= 0xFF);
+        fmt1->ranges[i].first.set (sid_ranges[i].code);
+        fmt1->ranges[i].nLeft.set (sid_ranges[i].glyph);
+      }
+    }
+    else /* format 2 */
+    {
+      Charset2 *fmt2 = c->allocate_size<Charset2> (Charset2::min_size + Charset2_Range::static_size * sid_ranges.len);
+      if (unlikely (fmt2 == nullptr)) return_trace (false);
+      for (unsigned int i = 0; i < sid_ranges.len; i++)
+      {
+        assert (sid_ranges[i].glyph <= 0xFFFF);
+        fmt2->ranges[i].first.set (sid_ranges[i].code);
+        fmt2->ranges[i].nLeft.set (sid_ranges[i].glyph);
+      }
+    }
+    return_trace (true);
+  }
+
+  /* parallel to above: calculate the size of a subset Charset */
+  static inline unsigned int calculate_serialized_size (
+                        uint8_t format,
+                        unsigned int count)
+  {
+    unsigned int  size = min_size;
+    if (format == 0)
+      size += Charset0::min_size + HBUINT8::static_size * (count - 1);
+    else if (format == 1)
+      size += Charset1::min_size + Charset1_Range::static_size * count;
+    else
+      size += Charset2::min_size + Charset2_Range::static_size * count;
+
+    return size;
+  }
+
+  inline unsigned int get_size (unsigned int num_glyphs) const
+  {
+    unsigned int size = min_size;
+    if (format == 0)
+      size += u.format0.get_size (num_glyphs);
+    else if (format == 1)
+      size += u.format1.get_size (num_glyphs);
+    else
+      size += u.format2.get_size (num_glyphs);
+    return size;
+  }
+
+  inline hb_codepoint_t get_sid (hb_codepoint_t glyph) const
+  {
+    if (format == 0)
+      return u.format0.get_sid (glyph);
+    else if (format == 1)
+      return u.format1.get_sid (glyph);
+    else
+      return u.format2.get_sid (glyph);
+  }
+
+  HBUINT8       format;
+  union {
+    Charset0    format0;
+    Charset1    format1;
+    Charset2    format2;
+  } u;
+
+  DEFINE_SIZE_MIN (1);
+};
+
+struct CFF1StringIndex : CFF1Index
+{
+  inline bool serialize (hb_serialize_context_t *c, const CFF1StringIndex &strings, unsigned int offSize_, const Remap &sidmap)
+  {
+    TRACE_SERIALIZE (this);
+    if (unlikely ((strings.count == 0) || (sidmap.get_count () == 0)))
+    {
+      if (!unlikely (c->extend_min (*this)))
+        return_trace (false);
+      count.set (0);
+      return_trace (true);
+    }
+    
+    hb_vector_t<ByteStr> bytesArray;
+    bytesArray.init ();
+    if (!bytesArray.resize (sidmap.get_count ()))
+      return_trace (false);
+    for (unsigned int i = 0; i < strings.count; i++)
+    {
+      hb_codepoint_t  j = sidmap[i];
+      if (j != CFF_UNDEF_CODE)
+        bytesArray[j] = strings[i];
+    }
+
+    bool result = CFF1Index::serialize (c, offSize_, bytesArray);
+    bytesArray.fini ();
+    return_trace (result);
+  }
+  
+  /* in parallel to above */
+  inline unsigned int calculate_serialized_size (unsigned int &offSize /*OUT*/, const Remap &sidmap) const
+  {
+    offSize = 0;
+    if ((count == 0) || (sidmap.get_count () == 0))
+      return count.static_size;
+
+    unsigned int dataSize = 0;
+    for (unsigned int i = 0; i < count; i++)
+      if (sidmap[i] != CFF_UNDEF_CODE)
+        dataSize += length_at (i);
+
+    offSize = calcOffSize(dataSize);
+    return CFF1Index::calculate_serialized_size (offSize, sidmap.get_count (), dataSize);
+  }
+};
+
+struct CFF1TopDictInterpEnv : NumInterpEnv
+{
+  inline CFF1TopDictInterpEnv (void)
+    : NumInterpEnv(), prev_offset(0), last_offset(0) {}
+
+  unsigned int prev_offset;
+  unsigned int last_offset;
+};
+
+struct NameDictValues
+{
+  enum NameDictValIndex
+  {
+      version,
+      notice,
+      copyright,
+      fullName,
+      familyName,
+      weight,
+      postscript,
+      fontName,
+      baseFontName,
+      registry,
+      ordering,
+
+      ValCount
+  };
+
+  inline void init (void)
+  {
+    for (unsigned int i = 0; i < ValCount; i++)
+      values[i] = CFF_UNDEF_SID;
+  }
+
+  inline unsigned int& operator[] (unsigned int i)
+  { assert (i < ValCount); return values[i]; }
+
+  inline unsigned int operator[] (unsigned int i) const
+  { assert (i < ValCount); return values[i]; }
+
+  static inline enum NameDictValIndex name_op_to_index (OpCode op)
+  {
+    switch (op) {
+      case OpCode_version:
+        return version;
+      case OpCode_Notice:
+        return notice;
+      case OpCode_Copyright:
+        return copyright;
+      case OpCode_FullName:
+        return fullName;
+      case OpCode_FamilyName:
+        return familyName;
+      case OpCode_Weight:
+        return weight;
+      case OpCode_PostScript:
+        return postscript;
+      case OpCode_FontName:
+        return fontName;
+      default:
+        assert (0);
+      }
+  }
+
+  unsigned int  values[ValCount];
+};
+
+struct CFF1TopDictVal : OpStr
+{
+  unsigned int  last_arg_offset;
+};
+
+struct CFF1TopDictValues : TopDictValues<CFF1TopDictVal>
+{
+  inline void init (void)
+  {
+    TopDictValues<CFF1TopDictVal>::init ();
+
+    nameSIDs.init ();
+    ros_supplement = 0;
+    cidCount = 8720;
+    EncodingOffset = 0;
+    CharsetOffset = 0;
+    FDSelectOffset = 0;
+    privateDictInfo.init ();
+  }
+
+  inline void fini (void)
+  {
+    TopDictValues<CFF1TopDictVal>::fini ();
+  }
+
+  inline bool is_CID (void) const
+  { return nameSIDs[NameDictValues::registry] != CFF_UNDEF_SID; }
+
+  NameDictValues  nameSIDs;
+  unsigned int    ros_supplement_offset;
+  unsigned int    ros_supplement;
+  unsigned int    cidCount;
+
+  unsigned int    EncodingOffset;
+  unsigned int    CharsetOffset;
+  unsigned int    FDSelectOffset;
+  TableInfo       privateDictInfo;
+};
+
+struct CFF1TopDictOpSet : TopDictOpSet<CFF1TopDictVal>
+{
+  static inline bool process_op (OpCode op, CFF1TopDictInterpEnv& env, CFF1TopDictValues& dictval)
+  {
+    CFF1TopDictVal  val;
+    val.last_arg_offset = (env.last_offset-1) - dictval.opStart;  /* offset to the last argument */
+
+    switch (op) {
+      case OpCode_version:
+      case OpCode_Notice:
+      case OpCode_Copyright:
+      case OpCode_FullName:
+      case OpCode_FamilyName:
+      case OpCode_Weight:
+      case OpCode_PostScript:
+      case OpCode_BaseFontName:
+        if (unlikely (!env.argStack.check_pop_uint (dictval.nameSIDs[NameDictValues::name_op_to_index (op)])))
+          return false;
+        env.clear_args ();
+        break;
+      case OpCode_isFixedPitch:
+      case OpCode_ItalicAngle:
+      case OpCode_UnderlinePosition:
+      case OpCode_UnderlineThickness:
+      case OpCode_PaintType:
+      case OpCode_CharstringType:
+      case OpCode_UniqueID:
+      case OpCode_StrokeWidth:
+      case OpCode_SyntheticBase:
+      case OpCode_CIDFontVersion:
+      case OpCode_CIDFontRevision:
+      case OpCode_CIDFontType:
+      case OpCode_UIDBase:
+      case OpCode_FontBBox:
+      case OpCode_XUID:
+      case OpCode_BaseFontBlend:
+        env.clear_args ();
+        break;
+        
+      case OpCode_CIDCount:
+        if (unlikely (!env.argStack.check_pop_uint (dictval.cidCount)))
+          return false;
+        env.clear_args ();
+        break;
+
+      case OpCode_ROS:
+        if (unlikely (!env.argStack.check_pop_uint (dictval.ros_supplement) ||
+                      !env.argStack.check_pop_uint (dictval.nameSIDs[NameDictValues::ordering]) ||
+                      !env.argStack.check_pop_uint (dictval.nameSIDs[NameDictValues::registry])))
+          return false;
+        env.clear_args ();
+        break;
+
+      case OpCode_Encoding:
+        if (unlikely (!env.argStack.check_pop_uint (dictval.EncodingOffset)))
+          return false;
+        env.clear_args ();
+        break;
+
+      case OpCode_charset:
+        if (unlikely (!env.argStack.check_pop_uint (dictval.CharsetOffset)))
+          return false;
+        env.clear_args ();
+        break;
+
+      case OpCode_FDSelect:
+        if (unlikely (!env.argStack.check_pop_uint (dictval.FDSelectOffset)))
+          return false;
+        env.clear_args ();
+        break;
+    
+      case OpCode_Private:
+        if (unlikely (!env.argStack.check_pop_uint (dictval.privateDictInfo.offset)))
+          return false;
+        if (unlikely (!env.argStack.check_pop_uint (dictval.privateDictInfo.size)))
+          return false;
+        env.clear_args ();
+        break;
+    
+      default:
+        env.last_offset = env.substr.offset;
+        if (unlikely (!TopDictOpSet<CFF1TopDictVal>::process_op (op, env, dictval)))
+          return false;
+        /* Record this operand below if stack is empty, otherwise done */
+        if (!env.argStack.is_empty ()) return true;
+        break;
+    }
+
+    dictval.addOp (op, env.substr, val);
+    return true;
+  }
+};
+
+struct CFF1FontDictValues : DictValues<OpStr>
+{
+  inline void init (void)
+  {
+    DictValues<OpStr>::init ();
+    privateDictInfo.init ();
+    fontName = CFF_UNDEF_SID;
+  }
+
+  inline void fini (void)
+  {
+    DictValues<OpStr>::fini ();
+  }
+
+  TableInfo       privateDictInfo;
+  unsigned int    fontName;
+};
+
+struct CFF1FontDictOpSet : DictOpSet
+{
+  static inline bool process_op (OpCode op, NumInterpEnv& env, CFF1FontDictValues& dictval)
+  {
+    switch (op) {
+      case OpCode_FontName:
+        if (unlikely (!env.argStack.check_pop_uint (dictval.fontName)))
+          return false;
+        env.clear_args ();
+        break;
+      case OpCode_FontMatrix:
+      case OpCode_PaintType:
+        env.clear_args ();
+        break;
+      case OpCode_Private:
+        if (unlikely (!env.argStack.check_pop_uint (dictval.privateDictInfo.offset)))
+          return false;
+        if (unlikely (!env.argStack.check_pop_uint (dictval.privateDictInfo.size)))
+          return false;
+        env.clear_args ();
+        break;
+    
+      default:
+        if (unlikely (!DictOpSet::process_op (op, env)))
+          return false;
+        if (!env.argStack.is_empty ()) return true;
+        break;
+    }
+
+    dictval.addOp (op, env.substr);
+    return true;
+  }
+};
+
+template <typename VAL>
+struct CFF1PrivateDictValues_Base : DictValues<VAL>
+{
+  inline void init (void)
+  {
+    DictValues<VAL>::init ();
+    subrsOffset = 0;
+    localSubrs = &Null(CFF1Subrs);
+  }
+
+  inline void fini (void)
+  {
+    DictValues<VAL>::fini ();
+  }
+
+  inline unsigned int calculate_serialized_size (void) const
+  {
+    unsigned int size = 0;
+    for (unsigned int i = 0; i < DictValues<VAL>::getNumValues; i++)
+      if (DictValues<VAL>::getValue (i).op == OpCode_Subrs)
+        size += OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs);
+      else
+        size += DictValues<VAL>::getValue (i).str.len;
+    return size;
+  }
+
+  unsigned int      subrsOffset;
+  const CFF1Subrs    *localSubrs;
+};
+
+typedef CFF1PrivateDictValues_Base<OpStr> CFF1PrivateDictValues_Subset;
+typedef CFF1PrivateDictValues_Base<NumDictVal> CFF1PrivateDictValues;
+
+struct CFF1PrivateDictOpSet : DictOpSet
+{
+  static inline bool process_op (OpCode op, NumInterpEnv& env, CFF1PrivateDictValues& dictval)
+  {
+    NumDictVal val;
+    val.init ();
+
+    switch (op) {
+      case OpCode_BlueValues:
+      case OpCode_OtherBlues:
+      case OpCode_FamilyBlues:
+      case OpCode_FamilyOtherBlues:
+      case OpCode_StemSnapH:
+      case OpCode_StemSnapV:
+        if (unlikely (!env.argStack.check_pop_delta (val.multi_val)))
+          return false;
+        break;
+      case OpCode_StdHW:
+      case OpCode_StdVW:
+      case OpCode_BlueScale:
+      case OpCode_BlueShift:
+      case OpCode_BlueFuzz:
+      case OpCode_ForceBold:
+      case OpCode_LanguageGroup:
+      case OpCode_ExpansionFactor:
+      case OpCode_initialRandomSeed:
+      case OpCode_defaultWidthX:
+      case OpCode_nominalWidthX:
+        if (unlikely (!env.argStack.check_pop_num (val.single_val)))
+          return false;
+        env.clear_args ();
+        break;
+      case OpCode_Subrs:
+        if (unlikely (!env.argStack.check_pop_uint (dictval.subrsOffset)))
+          return false;
+        env.clear_args ();
+        break;
+
+      default:
+        if (unlikely (!DictOpSet::process_op (op, env)))
+          return false;
+        if (!env.argStack.is_empty ()) return true;
+        break;
+    }
+
+    dictval.addOp (op, env.substr, val);
+    return true;
+  }
+};
+
+struct CFF1PrivateDictOpSet_Subset : DictOpSet
+{
+  static inline bool process_op (OpCode op, NumInterpEnv& env, CFF1PrivateDictValues_Subset& dictval)
+  {
+    switch (op) {
+      case OpCode_BlueValues:
+      case OpCode_OtherBlues:
+      case OpCode_FamilyBlues:
+      case OpCode_FamilyOtherBlues:
+      case OpCode_StemSnapH:
+      case OpCode_StemSnapV:
+      case OpCode_StdHW:
+      case OpCode_StdVW:
+      case OpCode_BlueScale:
+      case OpCode_BlueShift:
+      case OpCode_BlueFuzz:
+      case OpCode_ForceBold:
+      case OpCode_LanguageGroup:
+      case OpCode_ExpansionFactor:
+      case OpCode_initialRandomSeed:
+      case OpCode_defaultWidthX:
+      case OpCode_nominalWidthX:
+        env.clear_args ();
+        break;
+
+      case OpCode_Subrs:
+        if (unlikely (!env.argStack.check_pop_uint (dictval.subrsOffset)))
+          return false;
+        env.clear_args ();
+        break;
+
+      default:
+        if (unlikely (!DictOpSet::process_op (op, env)))
+          return false;
+        if (!env.argStack.is_empty ()) return true;
+        break;
+    }
+
+    dictval.addOp (op, env.substr);
+    return true;
+  }
+};
+
+typedef DictInterpreter<CFF1TopDictOpSet, CFF1TopDictValues, CFF1TopDictInterpEnv> CFF1TopDict_Interpreter;
+typedef DictInterpreter<CFF1FontDictOpSet, CFF1FontDictValues> CFF1FontDict_Interpreter;
+typedef DictInterpreter<CFF1PrivateDictOpSet, CFF1PrivateDictValues> CFF1PrivateDict_Interpreter;
+
+typedef CFF1Index CFF1NameIndex;
+typedef CFF1IndexOf<TopDict> CFF1TopDictIndex;
+
+}; /* namespace CFF */
+
+namespace OT {
+
+using namespace CFF;
+
+struct cff1
+{
+  static const hb_tag_t tableTag        = HB_OT_TAG_cff1;
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+                  likely (version.major == 1));
+  }
+
+  template <typename PRIVOPSET, typename PRIVDICTVAL>
+  struct accelerator_templ_t
+  {
+    inline void init (hb_face_t *face)
+    {
+      topDict.init ();
+      fontDicts.init ();
+      privateDicts.init ();
+      
+      this->blob = sc.reference_table<cff1> (face);
+
+      /* setup for run-time santization */
+      sc.init (this->blob);
+      sc.start_processing ();
+      
+      const OT::cff1 *cff = this->blob->template as<OT::cff1> ();
+
+      if (cff == &Null(OT::cff1))
+      { fini (); return; }
+
+      nameIndex = &cff->nameIndex (cff);
+      if ((nameIndex == &Null (CFF1NameIndex)) || !nameIndex->sanitize (&sc))
+      { fini (); return; }
+
+      topDictIndex = &StructAtOffset<CFF1TopDictIndex> (nameIndex, nameIndex->get_size ());
+      if ((topDictIndex == &Null (CFF1TopDictIndex)) || !topDictIndex->sanitize (&sc) || (topDictIndex->count == 0))
+      { fini (); return; }
+
+      { /* parse top dict */
+        const ByteStr topDictStr = (*topDictIndex)[0];
+        if (unlikely (!topDictStr.sanitize (&sc))) { fini (); return; }
+        CFF1TopDict_Interpreter top_interp;
+        top_interp.env.init (topDictStr);
+        if (unlikely (!top_interp.interpret (topDict))) { fini (); return; }
+      }
+      
+      encoding = &Null(Encoding);
+      charset = &StructAtOffsetOrNull<Charset> (cff, topDict.CharsetOffset);
+      if (unlikely (is_CID () && (charset == &Null(Charset))))
+      { fini (); return; }
+
+      fdCount = 1;
+      if (is_CID ())
+      {
+        fdArray = &StructAtOffsetOrNull<CFF1FDArray> (cff, topDict.FDArrayOffset);
+        fdSelect = &StructAtOffsetOrNull<CFF1FDSelect> (cff, topDict.FDSelectOffset);
+        if (unlikely ((fdArray == &Null(CFF1FDArray)) || !fdArray->sanitize (&sc) ||
+            (fdSelect == &Null(CFF1FDSelect)) || !fdSelect->sanitize (&sc, fdArray->count)))
+        { fini (); return; }
+
+        fdCount = fdArray->count;
+      }
+      else
+      {
+        fdArray = &Null(CFF1FDArray);
+        fdSelect = &Null(CFF1FDSelect);
+        if (!is_predef_encoding ())
+        {
+          encoding = &StructAtOffsetOrNull<Encoding> (cff, topDict.EncodingOffset);
+          if ((encoding == &Null (Encoding)) || !encoding->sanitize (&sc))
+          { fini (); return; }
+        }
+      }
+
+      stringIndex = &StructAtOffset<CFF1StringIndex> (topDictIndex, topDictIndex->get_size ());
+      if ((stringIndex == &Null (CFF1StringIndex)) || !stringIndex->sanitize (&sc))
+      { fini (); return; }
+
+      globalSubrs = &StructAtOffset<CFF1Subrs> (stringIndex, stringIndex->get_size ());
+      if ((globalSubrs != &Null (CFF1Subrs)) && !stringIndex->sanitize (&sc))
+      { fini (); return; }
+
+      charStrings = &StructAtOffsetOrNull<CFF1CharStrings> (cff, topDict.charStringsOffset);
+
+      if ((charStrings == &Null(CFF1CharStrings)) || unlikely (!charStrings->sanitize (&sc)))
+      { fini (); return; }
+
+      num_glyphs = charStrings->count;
+      if (num_glyphs != sc.get_num_glyphs ())
+      { fini (); return; }
+
+      privateDicts.resize (fdCount);
+      for (unsigned int i = 0; i < fdCount; i++)
+        privateDicts[i].init ();
+
+      // parse CID font dicts and gather private dicts
+      if (is_CID ())
+      {
+        for (unsigned int i = 0; i < fdCount; i++)
+        {
+          ByteStr fontDictStr = (*fdArray)[i];
+          if (unlikely (!fontDictStr.sanitize (&sc))) { fini (); return; }
+          CFF1FontDictValues  *font;
+          CFF1FontDict_Interpreter font_interp;
+          font_interp.env.init (fontDictStr);
+          font = fontDicts.push ();
+          if (unlikely (!font_interp.interpret (*font))) { fini (); return; }
+          PRIVDICTVAL  *priv = &privateDicts[i];
+          const ByteStr privDictStr (StructAtOffset<UnsizedByteStr> (cff, font->privateDictInfo.offset), font->privateDictInfo.size);
+          if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; }
+          DictInterpreter<PRIVOPSET, PRIVDICTVAL> priv_interp;
+          priv_interp.env.init (privDictStr);
+          if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; }
+
+          priv->localSubrs = &StructAtOffsetOrNull<CFF1Subrs> (privDictStr.str, priv->subrsOffset);
+          if (priv->localSubrs != &Null(CFF1Subrs) &&
+              unlikely (!priv->localSubrs->sanitize (&sc)))
+          { fini (); return; }
+        }
+      }
+      else  /* non-CID */
+      {
+        CFF1TopDictValues  *font = &topDict;
+        PRIVDICTVAL  *priv = &privateDicts[0];
+        
+        const ByteStr privDictStr (StructAtOffset<UnsizedByteStr> (cff, font->privateDictInfo.offset), font->privateDictInfo.size);
+        if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; }
+        DictInterpreter<PRIVOPSET, PRIVDICTVAL> priv_interp;
+        priv_interp.env.init (privDictStr);
+        if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; }
+
+        priv->localSubrs = &StructAtOffsetOrNull<CFF1Subrs> (privDictStr.str, priv->subrsOffset);
+        if (priv->localSubrs != &Null(CFF1Subrs) &&
+            unlikely (!priv->localSubrs->sanitize (&sc)))
+        { fini (); return; }
+      }
+    }
+
+    inline void fini (void)
+    {
+      sc.end_processing ();
+      topDict.fini ();
+      fontDicts.fini ();
+      privateDicts.fini ();
+      hb_blob_destroy (blob);
+      blob = nullptr;
+    }
+
+    inline bool is_valid (void) const { return blob != nullptr; }
+    inline bool is_CID (void) const { return topDict.is_CID (); }
+
+    inline bool is_predef_encoding (void) const { return topDict.EncodingOffset <= ExpertEncoding; }
+    inline bool is_predef_charset (void) const { return topDict.CharsetOffset <= ExpertSubsetCharset; }
+
+    inline hb_codepoint_t  glyph_to_code (hb_codepoint_t glyph) const
+    {
+      if (encoding != &Null(Encoding))
+        return encoding->get_code (glyph);
+      else
+      {
+        hb_codepoint_t  sid = glyph_to_sid (glyph);
+        if (sid == 0) return 0;
+        hb_codepoint_t  code = 0;
+        switch (topDict.EncodingOffset)
+        {
+          case  StandardEncoding:
+            code = lookup_standard_encoding (sid);
+            break;
+          case  ExpertEncoding:
+            code = lookup_expert_encoding (sid);
+            break;
+          default:
+            break;
+        }
+        return code;
+      }
+    }
+
+    inline hb_codepoint_t  glyph_to_sid (hb_codepoint_t glyph) const
+    {
+      if (charset != &Null(Charset))
+        return charset->get_sid (glyph);
+      else
+      {
+        hb_codepoint_t sid = 0;
+        switch (topDict.CharsetOffset)
+        {
+          case  ISOAdobeCharset:
+            if (glyph <= 228 /*zcaron*/) sid = glyph;
+            break;
+          case  ExpertCharset:
+            sid = lookup_expert_charset (glyph);
+            break;
+          case  ExpertSubsetCharset:
+              sid = lookup_expert_subset_charset (glyph);
+            break;
+          default:
+            break;
+        }
+        return sid;
+      }
+    }
+
+    inline bool get_extents (hb_codepoint_t glyph,
+           hb_glyph_extents_t *extents) const
+    {
+      // XXX: TODO
+      if (glyph >= num_glyphs)
+        return false;
+      
+      return true;
+    }
+
+    protected:
+    hb_blob_t               *blob;
+    hb_sanitize_context_t   sc;
+
+    public:
+    const CFF1NameIndex     *nameIndex;
+    const CFF1TopDictIndex  *topDictIndex;
+    const CFF1StringIndex   *stringIndex;
+    const Encoding          *encoding;
+    const Charset           *charset;
+    const CFF1Subrs         *globalSubrs;
+    const CFF1CharStrings   *charStrings;
+    const CFF1FDArray       *fdArray;
+    const CFF1FDSelect      *fdSelect;
+    unsigned int            fdCount;
+
+    CFF1TopDictValues       topDict;
+    hb_vector_t<CFF1FontDictValues>   fontDicts;
+    hb_vector_t<PRIVDICTVAL>          privateDicts;
+
+    unsigned int            num_glyphs;
+  };
+
+  typedef accelerator_templ_t<CFF1PrivateDictOpSet, CFF1PrivateDictValues> accelerator_t;
+  typedef accelerator_templ_t<CFF1PrivateDictOpSet_Subset, CFF1PrivateDictValues_Subset> accelerator_subset_t;
+
+  inline bool subset (hb_subset_plan_t *plan) const
+  {
+    hb_blob_t *cff_prime = nullptr;
+
+    bool success = true;
+    if (hb_subset_cff1 (plan, &cff_prime)) {
+      success = success && plan->add_table (HB_OT_TAG_cff1, cff_prime);
+    } else {
+      success = false;
+    }
+    hb_blob_destroy (cff_prime);
+
+    return success;
+  }
+
+  protected:
+  HB_INTERNAL static hb_codepoint_t lookup_standard_encoding (hb_codepoint_t sid);
+  HB_INTERNAL static hb_codepoint_t lookup_expert_encoding (hb_codepoint_t sid);
+  HB_INTERNAL static hb_codepoint_t lookup_expert_charset (hb_codepoint_t glyph);
+  HB_INTERNAL static hb_codepoint_t lookup_expert_subset_charset (hb_codepoint_t glyph);
+
+  public:
+  FixedVersion<HBUINT8> version;          /* Version of CFF table. set to 0x0100u */
+  OffsetTo<CFF1NameIndex, HBUINT8> nameIndex; /* headerSize = Offset to Name INDEX. */
+  HBUINT8               offSize;          /* offset size (unused?) */
+
+  public:
+  DEFINE_SIZE_STATIC (4);
+};
+
+} /* namespace OT */
+
+#endif /* HB_OT_CFF1_TABLE_HH */
diff --git a/src/hb-ot-cff2-table.hh b/src/hb-ot-cff2-table.hh
new file mode 100644
index 0000000..8e2ee34
--- /dev/null
+++ b/src/hb-ot-cff2-table.hh
@@ -0,0 +1,559 @@
+/*
+ * 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_OT_CFF2_TABLE_HH
+#define HB_OT_CFF2_TABLE_HH
+
+#include "hb-ot-cff-common.hh"
+#include "hb-subset-cff2.hh"
+
+namespace CFF {
+
+/*
+ * CFF2 -- Compact Font Format (CFF) Version 2
+ * https://docs.microsoft.com/en-us/typography/opentype/spec/cff2
+ */
+#define HB_OT_TAG_cff2 HB_TAG('C','F','F','2')
+
+typedef CFFIndex<HBUINT32>  CFF2Index;
+template <typename Type> struct CFF2IndexOf : CFFIndexOf<HBUINT32, Type> {};
+
+typedef CFF2Index         CFF2CharStrings;
+typedef FDArray<HBUINT32> CFF2FDArray;
+typedef Subrs<HBUINT32>   CFF2Subrs;
+
+typedef FDSelect3_4<HBUINT32, HBUINT16> FDSelect4;
+typedef FDSelect3_4_Range<HBUINT32, HBUINT16> FDSelect4_Range;
+
+struct CFF2FDSelect
+{
+  inline bool sanitize (hb_sanitize_context_t *c, unsigned int fdcount) const
+  {
+    TRACE_SANITIZE (this);
+
+    return_trace (likely (c->check_struct (this) && (format == 0 || format == 3 || format == 4) &&
+                          (format == 0)?
+                          u.format0.sanitize (c, fdcount):
+                            ((format == 3)?
+                            u.format3.sanitize (c, fdcount):
+                            u.format4.sanitize (c, fdcount))));
+  }
+
+  inline bool serialize (hb_serialize_context_t *c, const CFF2FDSelect &src, unsigned int num_glyphs)
+  {
+    TRACE_SERIALIZE (this);
+    unsigned int size = src.get_size (num_glyphs);
+    CFF2FDSelect *dest = c->allocate_size<CFF2FDSelect> (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 (format == 3)
+      size += u.format3.get_size ();
+    else
+      size += u.format4.get_size ();
+    return size;
+  }
+
+  inline hb_codepoint_t get_fd (hb_codepoint_t glyph) const
+  {
+    if (this == &Null(CFF2FDSelect))
+      return 0;
+    if (format == 0)
+      return u.format0.get_fd (glyph);
+    else if (format == 3)
+      return u.format3.get_fd (glyph);
+    else
+      return u.format4.get_fd (glyph);
+  }
+
+  HBUINT8       format;
+  union {
+    FDSelect0   format0;
+    FDSelect3   format3;
+    FDSelect4   format4;
+  } u;
+
+  DEFINE_SIZE_MIN (2);
+};
+
+struct CFF2VariationStore
+{
+  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_SERIALIZE (this);
+    unsigned int size_ = varStore->get_size ();
+    CFF2VariationStore *dest = c->allocate_size<CFF2VariationStore> (size_);
+    if (unlikely (dest == nullptr)) return_trace (false);
+    memcpy (dest, varStore, size_);
+    return_trace (true);
+  }
+
+  inline unsigned int get_size (void) const { return HBUINT16::static_size + size; }
+
+  HBUINT16        size;
+  VariationStore  varStore;
+
+  DEFINE_SIZE_MIN (2 + VariationStore::min_size);
+};
+
+struct CFF2TopDictValues : TopDictValues<>
+{
+  inline void init (void)
+  {
+    TopDictValues<>::init ();
+    vstoreOffset = 0;
+    FDSelectOffset = 0;
+  }
+
+  inline void fini (void)
+  {
+    TopDictValues<>::fini ();
+  }
+
+  inline unsigned int calculate_serialized_size (void) const
+  {
+    unsigned int size = 0;
+    for (unsigned int i = 0; i < getNumValues (); i++)
+    {
+      OpCode op = getValue (i).op;
+      switch (op)
+      {
+        case OpCode_vstore:
+        case OpCode_FDSelect:
+          size += OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (op);
+          break;
+        default:
+          size += TopDictValues<>::calculate_serialized_op_size (getValue (i));
+          break;
+      }
+    }
+    return size;
+  }
+
+  unsigned int  vstoreOffset;
+  unsigned int  FDSelectOffset;
+};
+
+struct CFF2TopDictOpSet : TopDictOpSet<>
+{
+  static inline bool process_op (OpCode op, NumInterpEnv& env, CFF2TopDictValues& dictval)
+  {
+    switch (op) {
+      case OpCode_FontMatrix:
+        {
+          DictVal val;
+          val.init ();
+          dictval.addOp (op, env.substr);
+          env.clear_args ();
+        }
+        break;
+
+      case OpCode_vstore:
+        if (unlikely (!env.argStack.check_pop_uint (dictval.vstoreOffset)))
+          return false;
+        env.clear_args ();
+        break;
+      case OpCode_FDSelect:
+        if (unlikely (!env.argStack.check_pop_uint (dictval.FDSelectOffset)))
+          return false;
+        env.clear_args ();
+        break;
+    
+      default:
+        if (unlikely (!SUPER::process_op (op, env, dictval)))
+          return false;
+        /* Record this operand below if stack is empty, otherwise done */
+        if (!env.argStack.is_empty ()) return true;
+    }
+
+    dictval.addOp (op, env.substr);
+    return true;
+  }
+
+  typedef TopDictOpSet<> SUPER;
+};
+
+struct CFF2FontDictValues : DictValues<OpStr>
+{
+  inline void init (void)
+  {
+    DictValues<OpStr>::init ();
+    privateDictInfo.init ();
+  }
+
+  inline void fini (void)
+  {
+    DictValues<OpStr>::fini ();
+  }
+
+  TableInfo    privateDictInfo;
+};
+
+struct CFF2FontDictOpSet : DictOpSet
+{
+  static inline bool process_op (OpCode op, NumInterpEnv& env, CFF2FontDictValues& dictval)
+  {
+    switch (op) {
+      case OpCode_Private:
+        if (unlikely (!env.argStack.check_pop_uint (dictval.privateDictInfo.offset)))
+          return false;
+        if (unlikely (!env.argStack.check_pop_uint (dictval.privateDictInfo.size)))
+          return false;
+        env.clear_args ();
+        break;
+    
+      default:
+        if (unlikely (!SUPER::process_op (op, env)))
+          return false;
+        if (!env.argStack.is_empty ())
+          return true;
+    }
+
+    dictval.addOp (op, env.substr);
+    return true;
+  }
+
+  private:
+  typedef DictOpSet SUPER;
+};
+
+template <typename VAL>
+struct CFF2PrivateDictValues_Base : DictValues<VAL>
+{
+  inline void init (void)
+  {
+    DictValues<VAL>::init ();
+    subrsOffset = 0;
+    localSubrs = &Null(CFF2Subrs);
+    vsindex = 0;
+  }
+
+  inline void fini (void)
+  {
+    DictValues<VAL>::fini ();
+  }
+
+  inline unsigned int calculate_serialized_size (void) const
+  {
+    unsigned int size = 0;
+    for (unsigned int i = 0; i < DictValues<VAL>::getNumValues; i++)
+      if (DictValues<VAL>::getValue (i).op == OpCode_Subrs)
+        size += OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs);
+      else
+        size += DictValues<VAL>::getValue (i).str.len;
+    return size;
+  }
+
+  unsigned int      subrsOffset;
+  const CFF2Subrs   *localSubrs;
+  unsigned int      vsindex;
+};
+
+typedef CFF2PrivateDictValues_Base<OpStr> CFF2PrivateDictValues_Subset;
+typedef CFF2PrivateDictValues_Base<NumDictVal> CFF2PrivateDictValues;
+
+struct CFF2PrivateDictOpSet : DictOpSet
+{
+  static inline bool process_op (OpCode op, NumInterpEnv& env, CFF2PrivateDictValues& dictval)
+  {
+    NumDictVal val;
+    val.init ();
+
+    switch (op) {
+      case OpCode_StdHW:
+      case OpCode_StdVW:
+      case OpCode_BlueScale:
+      case OpCode_BlueShift:
+      case OpCode_BlueFuzz:
+      case OpCode_ExpansionFactor:
+      case OpCode_LanguageGroup:
+        if (unlikely (!env.argStack.check_pop_num (val.single_val)))
+          return false;
+        env.clear_args ();
+        break;
+      case OpCode_BlueValues:
+      case OpCode_OtherBlues:
+      case OpCode_FamilyBlues:
+      case OpCode_FamilyOtherBlues:
+      case OpCode_StemSnapH:
+      case OpCode_StemSnapV:
+        if (unlikely (!env.argStack.check_pop_delta (val.multi_val)))
+          return false;
+        env.clear_args ();
+        break;
+      case OpCode_Subrs:
+        if (unlikely (!env.argStack.check_pop_uint (dictval.subrsOffset)))
+          return false;
+        env.clear_args ();
+        break;
+      case OpCode_vsindexdict:
+        if (unlikely (!env.argStack.check_pop_uint (dictval.vsindex)))
+          return false;
+        break;
+      case OpCode_blenddict:
+        break;
+
+      default:
+        if (unlikely (!DictOpSet::process_op (op, env)))
+          return false;
+        if (!env.argStack.is_empty ()) return true;
+        break;
+    }
+
+    dictval.addOp (op, env.substr, val);
+    return true;
+  }
+};
+
+struct CFF2PrivateDictOpSet_Subset : DictOpSet
+{
+  static inline bool process_op (OpCode op, NumInterpEnv& env, CFF2PrivateDictValues_Subset& dictval)
+  {
+    switch (op) {
+      case OpCode_BlueValues:
+      case OpCode_OtherBlues:
+      case OpCode_FamilyBlues:
+      case OpCode_FamilyOtherBlues:
+      case OpCode_StdHW:
+      case OpCode_StdVW:
+      case OpCode_BlueScale:
+      case OpCode_BlueShift:
+      case OpCode_BlueFuzz:
+      case OpCode_StemSnapH:
+      case OpCode_StemSnapV:
+      case OpCode_LanguageGroup:
+      case OpCode_ExpansionFactor:
+        env.clear_args ();
+        break;
+
+      case OpCode_blenddict:
+        env.clear_args ();
+        return true;
+
+      case OpCode_Subrs:
+        if (unlikely (!env.argStack.check_pop_uint (dictval.subrsOffset)))
+          return false;
+        env.clear_args ();
+        break;
+
+      default:
+        if (unlikely (!SUPER::process_op (op, env)))
+          return false;
+        if (!env.argStack.is_empty ()) return true;
+        break;
+    }
+
+    dictval.addOp (op, env.substr);
+    return true;
+  }
+
+  private:
+  typedef DictOpSet SUPER;
+};
+
+typedef DictInterpreter<CFF2TopDictOpSet, CFF2TopDictValues> CFF2TopDict_Interpreter;
+typedef DictInterpreter<CFF2FontDictOpSet, CFF2FontDictValues> CFF2FontDict_Interpreter;
+
+}; /* namespace CFF */
+
+namespace OT {
+
+using namespace CFF;
+
+struct cff2
+{
+  static const hb_tag_t tableTag        = HB_OT_TAG_cff2;
+
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) &&
+                  likely (version.major == 2));
+  }
+
+  template <typename PRIVOPSET, typename PRIVDICTVAL>
+  struct accelerator_templ_t
+  {
+    inline void init (hb_face_t *face)
+    {
+      topDict.init ();
+      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->template as<OT::cff2> ();
+
+      if (cff2 == &Null(OT::cff2))
+      { fini (); return; }
+
+      { /* parse top dict */
+        ByteStr topDictStr (cff2 + cff2->topDict, cff2->topDictSize);
+        if (unlikely (!topDictStr.sanitize (&sc))) { fini (); return; }
+        CFF2TopDict_Interpreter top_interp;
+        top_interp.env.init (topDictStr);
+        if (unlikely (!top_interp.interpret (topDict))) { fini (); return; }
+      }
+      
+      globalSubrs = &StructAtOffset<CFF2Subrs> (cff2, cff2->topDict + cff2->topDictSize);
+      varStore = &StructAtOffsetOrNull<CFF2VariationStore> (cff2, topDict.vstoreOffset);
+      charStrings = &StructAtOffsetOrNull<CFF2CharStrings> (cff2, topDict.charStringsOffset);
+      fdArray = &StructAtOffsetOrNull<CFF2FDArray> (cff2, topDict.FDArrayOffset);
+      fdSelect = &StructAtOffsetOrNull<CFF2FDSelect> (cff2, topDict.FDSelectOffset);
+      
+      if (((varStore != &Null(CFF2VariationStore)) && unlikely (!varStore->sanitize (&sc))) ||
+          (charStrings == &Null(CFF2CharStrings)) || unlikely (!charStrings->sanitize (&sc)) ||
+          (fdArray == &Null(CFF2FDArray)) || unlikely (!fdArray->sanitize (&sc)) ||
+          (((fdSelect != &Null(CFF2FDSelect)) && unlikely (!fdSelect->sanitize (&sc, fdArray->count)))))
+      { fini (); return; }
+
+      num_glyphs = charStrings->count;
+      if (num_glyphs != sc.get_num_glyphs ())
+      { fini (); return; }
+
+      if (varStore != &Null(CFF2VariationStore))
+        region_count = varStore->varStore.get_region_count ();
+      else
+        region_count = 0;
+
+      fdCount = fdArray->count;
+      privateDicts.resize (fdCount);
+
+      /* parse font dicts and gather private dicts */
+      for (unsigned int i = 0; i < fdCount; i++)
+      {
+        const ByteStr fontDictStr = (*fdArray)[i];
+        if (unlikely (!fontDictStr.sanitize (&sc))) { fini (); return; }
+        CFF2FontDictValues  *font;
+        CFF2FontDict_Interpreter font_interp;
+        font_interp.env.init (fontDictStr);
+        font = fontDicts.push ();
+        if (unlikely (!font_interp.interpret (*font))) { fini (); return; }
+
+        const ByteStr privDictStr (StructAtOffsetOrNull<UnsizedByteStr> (cff2, font->privateDictInfo.offset), font->privateDictInfo.size);
+        if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; }
+        DictInterpreter<PRIVOPSET, PRIVDICTVAL>  priv_interp;
+        priv_interp.env.init(privDictStr);
+        if (unlikely (!priv_interp.interpret (privateDicts[i]))) { fini (); return; }
+
+        privateDicts[i].localSubrs = &StructAtOffsetOrNull<CFF2Subrs> (privDictStr.str, privateDicts[i].subrsOffset);
+        if (privateDicts[i].localSubrs != &Null(CFF2Subrs) &&
+          unlikely (!privateDicts[i].localSubrs->sanitize (&sc)))
+        { fini (); return; }
+      }
+    }
+
+    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
+    {
+      // XXX: TODO
+      if (glyph >= num_glyphs)
+        return false;
+      
+      return true;
+    }
+
+    protected:
+    hb_blob_t               *blob;
+    hb_sanitize_context_t   sc;
+
+    public:
+    CFF2TopDictValues         topDict;
+    const CFF2Subrs           *globalSubrs;
+    const CFF2VariationStore  *varStore;
+    const CFF2CharStrings     *charStrings;
+    const CFF2FDArray         *fdArray;
+    const CFF2FDSelect        *fdSelect;
+    unsigned int              fdCount;
+
+    hb_vector_t<CFF2FontDictValues>     fontDicts;
+    hb_vector_t<PRIVDICTVAL>  privateDicts;
+
+    unsigned int            num_glyphs;
+    unsigned int            region_count;
+  };
+
+  typedef accelerator_templ_t<CFF2PrivateDictOpSet, CFF2PrivateDictValues> accelerator_t;
+  typedef accelerator_templ_t<CFF2PrivateDictOpSet_Subset, CFF2PrivateDictValues_Subset> accelerator_subset_t;
+
+  inline bool subset (hb_subset_plan_t *plan) const
+  {
+    hb_blob_t *cff2_prime = nullptr;
+
+    bool success = true;
+    if (hb_subset_cff2 (plan, &cff2_prime)) {
+      success = success && plan->add_table (HB_OT_TAG_cff2, cff2_prime);
+    } else {
+      success = false;
+    }
+    hb_blob_destroy (cff2_prime);
+
+    return success;
+  }
+
+  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);
+};
+
+} /* namespace OT */
+
+#endif /* HB_OT_CFF2_TABLE_HH */
diff --git a/src/hb-ot-font.cc b/src/hb-ot-font.cc
index faacecb..7921fb8 100644
--- a/src/hb-ot-font.cc
+++ b/src/hb-ot-font.cc
@@ -37,6 +37,8 @@
 #include "hb-ot-kern-table.hh"
 #include "hb-ot-post-table.hh"
 #include "hb-ot-glyf-table.hh"
+#include "hb-ot-cff1-table.hh"
+#include "hb-ot-cff2-table.hh"
 #include "hb-ot-color-cbdt-table.hh"
 
 
diff --git a/src/hb-ot-layout-common.hh b/src/hb-ot-layout-common.hh
index 8a3a703..539e35e 100644
--- a/src/hb-ot-layout-common.hh
+++ b/src/hb-ot-layout-common.hh
@@ -1486,6 +1486,9 @@
 		  axesZ.sanitize (c, (unsigned int) axisCount * (unsigned int) regionCount));
   }
 
+  inline unsigned int get_region_count (void) const
+  { return regionCount; }
+
   protected:
   HBUINT16	axisCount;
   HBUINT16	regionCount;
@@ -1584,6 +1587,9 @@
 		  dataSets.sanitize (c, this));
   }
 
+  inline unsigned int get_region_count (void) const
+  { return (this+regions).get_region_count (); }
+
   protected:
   HBUINT16				format;
   LOffsetTo<VarRegionList>		regions;
diff --git a/src/hb-subset-cff-common.cc b/src/hb-subset-cff-common.cc
new file mode 100644
index 0000000..c62f83a
--- /dev/null
+++ b/src/hb-subset-cff-common.cc
@@ -0,0 +1,204 @@
+/*
+ * 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
+ */
+
+#include "hb-ot-cff-common.hh"
+#include "hb-ot-cff2-table.hh"
+#include "hb-subset-cff-common.hh"
+
+using namespace CFF;
+
+/**
+ * hb_plan_subset_cff_fdselect
+ * Determine an optimal FDSelect format according to a provided plan.
+ *
+ * Return value: FDSelect format, size, and ranges for the most compact subset FDSelect
+ * along with a font index remapping table
+ **/
+
+bool
+hb_plan_subset_cff_fdselect (const hb_vector_t<hb_codepoint_t> &glyphs,
+                            unsigned int fdCount,
+                            const FDSelect &src, /* IN */
+                            unsigned int &subset_fd_count /* OUT */,
+                            unsigned int &subst_fdselect_size /* OUT */,
+                            unsigned int &subst_fdselect_format /* OUT */,
+                            hb_vector_t<hb_codepoint_t> &subst_first_glyphs /* OUT */,
+                            Remap &fdmap /* OUT */)
+{
+  subset_fd_count = 0;
+  subst_fdselect_size = 0;
+  subst_fdselect_format = 0;
+  unsigned int  num_ranges = 0;
+
+  unsigned int subset_num_glyphs = glyphs.len;
+  if (subset_num_glyphs == 0)
+    return true;
+
+  {
+    /* use hb_set to determine the subset of font dicts */
+    hb_set_t  *set = hb_set_create ();
+    if (set == &Null (hb_set_t))
+      return false;
+    hb_codepoint_t  prev_fd = CFF_UNDEF_CODE;
+    for (hb_codepoint_t i = 0; i < subset_num_glyphs; i++)
+    {
+      hb_codepoint_t  fd = src.get_fd (glyphs[i]);
+      set->add (fd);
+      
+      if (fd != prev_fd)
+      {
+        num_ranges++;
+        prev_fd = fd;
+        subst_first_glyphs.push (i);
+      }
+    }
+
+    subset_fd_count = set->get_population ();
+    if (subset_fd_count == fdCount)
+    {
+      /* all font dicts belong to the subset. no need to subset FDSelect & FDArray */
+      hb_set_destroy (set);
+      return true;
+    }
+
+    /* create a fdmap */
+    if (!fdmap.reset (fdCount))
+    {
+      hb_set_destroy (set);
+      return false;
+    }
+
+    hb_codepoint_t  fd = CFF_UNDEF_CODE;
+    while (set->next (&fd))
+      fdmap.add (fd);
+    assert (fdmap.get_count () == subset_fd_count);
+    hb_set_destroy (set);
+  }
+
+  /* determine which FDSelect format is most compact */
+  if (subset_fd_count > 0xFF)
+  {
+    assert (src.format == 4);
+    subst_fdselect_format = 4;
+    subst_fdselect_size = FDSelect4::min_size + FDSelect4_Range::static_size * num_ranges;
+  }
+  else
+  {
+    unsigned int format0_size = FDSelect0::min_size + HBUINT8::static_size * subset_num_glyphs;
+    unsigned int format3_size = FDSelect3::min_size + FDSelect3_Range::static_size * num_ranges;
+
+    if (format0_size <= format3_size)
+    {
+      // subst_fdselect_format = 0;
+      subst_fdselect_size = format0_size;
+      subst_first_glyphs.fini ();
+    }
+    else
+    {
+      subst_fdselect_format = 3;
+      subst_fdselect_size = format3_size;
+    }
+  }
+
+  return true;
+}
+
+template <typename FDSELECT3_4>
+static inline bool
+serialize_fdselect_3_4 (hb_serialize_context_t *c,
+                          unsigned int num_glyphs,
+                          const FDSelect &src,
+                          unsigned int size,
+                          const hb_vector_t<hb_codepoint_t> &first_glyphs,
+                          const Remap &fdmap)
+{
+  TRACE_SERIALIZE (this);
+  FDSELECT3_4 *p = c->allocate_size<FDSELECT3_4> (size);
+  if (unlikely (p == nullptr)) return_trace (false);
+  p->nRanges.set (first_glyphs.len);
+  for (unsigned int i = 0; i < first_glyphs.len; i++)
+  {
+    hb_codepoint_t  glyph = first_glyphs[i];
+    p->ranges[i].first.set (glyph);
+    p->ranges[i].fd.set (fdmap[src.get_fd (glyph)]);
+  }
+  p->sentinel().set (num_glyphs);
+  return_trace (true);
+}
+
+/**
+ * hb_serialize_cff_fdselect
+ * Serialize a subset FDSelect format planned above.
+ **/
+bool
+hb_serialize_cff_fdselect (hb_serialize_context_t *c,
+                          const hb_vector_t<hb_codepoint_t> &glyphs,
+                          const FDSelect &src,
+                          unsigned int fd_count,
+                          unsigned int fdselect_format,
+                          unsigned int size,
+                          const hb_vector_t<hb_codepoint_t> &first_glyphs,
+                          const Remap &fdmap)
+{
+  TRACE_SERIALIZE (this);
+  FDSelect  *p = c->allocate_min<FDSelect> ();
+  if (unlikely (p == nullptr)) return_trace (false);
+  p->format.set (fdselect_format);
+  size -= FDSelect::min_size;
+
+  switch (fdselect_format)
+  {
+    case 0:
+    {
+      FDSelect0 *p = c->allocate_size<FDSelect0> (size);
+      if (unlikely (p == nullptr)) return_trace (false);
+      for (unsigned int i = 0; i < glyphs.len; i++)
+        p->fds[i].set (fdmap[src.get_fd (glyphs[i])]);
+      break;
+    }
+    
+    case 3:
+      return serialize_fdselect_3_4<FDSelect3> (c,
+                                                glyphs.len,
+                                                src,
+                                                size,
+                                                first_glyphs,
+                                                fdmap);
+    
+    case 4:
+      return serialize_fdselect_3_4<FDSelect4> (c,
+                                                glyphs.len,
+                                                src,
+                                                size,
+                                                first_glyphs,
+                                                fdmap);
+
+    default:
+      assert(false);
+  }
+  
+  return_trace (true);
+}
diff --git a/src/hb-subset-cff-common.hh b/src/hb-subset-cff-common.hh
new file mode 100644
index 0000000..6f944d3
--- /dev/null
+++ b/src/hb-subset-cff-common.hh
@@ -0,0 +1,309 @@
+/*
+ * 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_SUBSET_CFF_COMMON_HH
+#define HB_SUBSET_CFF_COMMON_HH
+
+#include "hb.hh"
+
+#include "hb-subset-plan.hh"
+#include "hb-cff-interp-cs-common.hh"
+
+namespace CFF {
+
+/* Used for writing a temporary charstring */
+struct ByteStrBuff : hb_vector_t<char, 1>
+{
+  inline bool encode_byte (unsigned char b)
+  {
+    return (push ((const char)b) != &Crap(char));
+  }
+
+  inline bool encode_int (int v)
+  {
+    if ((-1131 <= v) && (v <= 1131))
+    {
+      if ((-107 <= v) && (v <= 107))
+        return encode_byte (v + 139);
+      else if (v > 0)
+      {
+        v -= 108;
+        return encode_byte ((v >> 8) + OpCode_TwoBytePosInt0) && encode_byte (v & 0xFF);
+      }
+      else
+      {
+        v = -v - 108;
+        return encode_byte ((v >> 8) + OpCode_TwoByteNegInt0) && encode_byte (v & 0xFF);
+      }
+    }
+    assert ((v & ~0xFFFF) == 0);
+    return encode_byte (OpCode_shortint) &&
+           encode_byte ((v >> 8) & 0xFF) &&
+           encode_byte (v & 0xFF);
+  }
+
+  inline bool encode_num (const Number& n)
+  {
+    if (n.in_int_range ())
+    {
+      return encode_int (n.to_int ());
+    }
+    else
+    {
+      int32_t v = n.to_fixed ();
+      return encode_byte (OpCode_fixedcs) &&
+             encode_byte ((v >> 24) & 0xFF) &&
+             encode_byte ((v >> 16) & 0xFF) &&
+             encode_byte ((v >> 8) & 0xFF) &&
+             encode_byte (v & 0xFF);
+    }
+  }
+
+  inline bool encode_op (OpCode op)
+  {
+    if (Is_OpCode_ESC (op))
+      return encode_byte (OpCode_escape) &&
+             encode_byte (Unmake_OpCode_ESC (op));
+    else
+      return encode_byte (op);
+  }
+};
+
+struct ByteStrBuffArray : hb_vector_t<ByteStrBuff, 1>
+{
+  inline void fini (void)
+  {
+    for (unsigned int i = 0; i < len; i++)
+      hb_vector_t<ByteStrBuff, 1>::operator[] (i).fini ();
+    hb_vector_t<ByteStrBuff, 1>::fini ();
+  }
+};
+
+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;
+};
+
+template <typename OPSTR=OpStr>
+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 & offset as 2-byte & 4-byte integers */
+      if (unlikely (!UnsizedByteStr::serialize_int2 (c, privateDictInfo.size) ||
+                    !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)
+    : drop_hints (drop_hints_) {}
+
+  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)
+      return_trace (true);
+    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)
+      return 0;
+    else
+      return opstr.str.len;
+  }
+
+  protected:
+  const bool  drop_hints;
+};
+
+struct FlattenParam
+{
+  ByteStrBuff &flatStr;
+  bool        drop_hints;
+};
+
+template <typename ACC, typename ENV, typename OPSET>
+struct SubrFlattener
+{
+  inline SubrFlattener (const ACC &acc_,
+                        const hb_vector_t<hb_codepoint_t> &glyphs_,
+                        bool drop_hints_)
+    : acc (acc_),
+      glyphs (glyphs_),
+      drop_hints (drop_hints_)
+  {}
+
+  inline bool flatten (ByteStrBuffArray &flat_charstrings)
+  {
+    if (!flat_charstrings.resize (glyphs.len))
+      return false;
+    for (unsigned int i = 0; i < glyphs.len; i++)
+      flat_charstrings[i].init ();
+    for (unsigned int i = 0; i < glyphs.len; i++)
+    {
+      hb_codepoint_t  glyph = glyphs[i];
+      const ByteStr str = (*acc.charStrings)[glyph];
+      unsigned int fd = acc.fdSelect->get_fd (glyph);
+      CSInterpreter<ENV, OPSET, FlattenParam> interp;
+      interp.env.init (str, acc, fd);
+      FlattenParam  param = { flat_charstrings[i], drop_hints };
+      if (unlikely (!interp.interpret (param)))
+        return false;
+    }
+    return true;
+  }
+  
+  const ACC &acc;
+  const hb_vector_t<hb_codepoint_t> &glyphs;
+  bool  drop_hints;
+};
+};  /* namespace CFF */
+
+HB_INTERNAL bool
+hb_plan_subset_cff_fdselect (const hb_vector_t<hb_codepoint_t> &glyphs,
+                            unsigned int fdCount,
+                            const CFF::FDSelect &src, /* IN */
+                            unsigned int &subset_fd_count /* OUT */,
+                            unsigned int &subst_fdselect_size /* OUT */,
+                            unsigned int &subst_fdselect_format /* OUT */,
+                            hb_vector_t<hb_codepoint_t> &subst_first_glyphs /* OUT */,
+                            CFF::Remap &fdmap /* OUT */);
+
+HB_INTERNAL bool
+hb_serialize_cff_fdselect (hb_serialize_context_t *c,
+                          const hb_vector_t<hb_codepoint_t> &glyphs,
+                          const CFF::FDSelect &src,
+                          unsigned int fd_count,
+                          unsigned int fdselect_format,
+                          unsigned int size,
+                          const hb_vector_t<hb_codepoint_t> &first_glyphs,
+                          const CFF::Remap &fdmap);
+
+#endif /* HB_SUBSET_CFF_COMMON_HH */
diff --git a/src/hb-subset-cff1.cc b/src/hb-subset-cff1.cc
new file mode 100644
index 0000000..7f1d9dc
--- /dev/null
+++ b/src/hb-subset-cff1.cc
@@ -0,0 +1,958 @@
+/*
+ * 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
+ */
+
+#include "hb-open-type.hh"
+#include "hb-ot-cff1-table.hh"
+#include "hb-set.h"
+#include "hb-subset-cff1.hh"
+#include "hb-subset-plan.hh"
+#include "hb-subset-cff-common.hh"
+#include "hb-cff1-interp-cs.hh"
+
+using namespace CFF;
+
+struct RemapSID : Remap
+{
+  inline unsigned int add (unsigned int sid)
+  {
+    if ((sid != CFF_UNDEF_SID) && !is_std_std (sid))
+      return offset_sid (Remap::add (unoffset_sid (sid)));
+    else
+      return sid;
+  }
+
+  inline unsigned int operator[] (unsigned int sid) const
+  {
+    if (is_std_std (sid))
+      return sid;
+    else
+      return offset_sid (Remap::operator [] (unoffset_sid (sid)));
+  }
+
+  static const unsigned int num_std_strings = 391;
+
+  static inline bool is_std_std (unsigned int sid) { return sid < num_std_strings; }
+  static inline unsigned int offset_sid (unsigned int sid) { return sid + num_std_strings; }
+  static inline unsigned int unoffset_sid (unsigned int sid) { return sid - num_std_strings; }
+};
+
+struct CFF1SubTableOffsets : CFFSubTableOffsets
+{
+  inline CFF1SubTableOffsets (void)
+    : CFFSubTableOffsets (),
+      nameIndexOffset (0),
+      encodingOffset (0)
+  {
+    stringIndexInfo.init ();
+    charsetInfo.init ();
+    privateDictInfo.init ();
+  }
+
+  unsigned int  nameIndexOffset;
+  TableInfo     stringIndexInfo;
+  unsigned int  encodingOffset;
+  TableInfo     charsetInfo;
+  TableInfo     privateDictInfo;
+};
+
+/* a copy of a parsed out CFF1TopDictValues augmented with additional operators */
+struct CFF1TopDictValuesMod : CFF1TopDictValues
+{
+  inline void init (const CFF1TopDictValues *base_= &Null(CFF1TopDictValues))
+  {
+    SUPER::init ();
+    base = base_;
+  }
+
+  inline void fini (void)
+  {
+    SUPER::fini ();
+  }
+
+  inline unsigned getNumValues (void) const
+  {
+    return base->getNumValues () + SUPER::getNumValues ();
+  }
+  inline const CFF1TopDictVal &getValue (unsigned int i) const
+  {
+    if (i < base->getNumValues ())
+      return (*base)[i];
+    else
+      return SUPER::values[i - base->getNumValues ()];
+  }
+  inline const CFF1TopDictVal &operator [] (unsigned int i) const { return getValue (i); }
+
+  inline void reassignSIDs (const RemapSID& sidmap)
+  {
+    for (unsigned int i = 0; i < NameDictValues::ValCount; i++)
+      nameSIDs[i] = sidmap[base->nameSIDs[i]];
+  }
+
+  protected:
+  typedef CFF1TopDictValues SUPER;
+  const CFF1TopDictValues *base;
+};
+
+struct TopDictModifiers
+{
+  inline TopDictModifiers (const CFF1SubTableOffsets &offsets_,
+                           const unsigned int (&nameSIDs_)[NameDictValues::ValCount])
+    : offsets (offsets_),
+      nameSIDs (nameSIDs_)
+  {}
+
+  const CFF1SubTableOffsets &offsets;
+  const unsigned int        (&nameSIDs)[NameDictValues::ValCount];
+};
+
+struct CFF1TopDict_OpSerializer : CFFTopDict_OpSerializer<CFF1TopDictVal>
+{
+  inline bool serialize (hb_serialize_context_t *c,
+                         const CFF1TopDictVal &opstr,
+                         const TopDictModifiers &mod) const
+  {
+    TRACE_SERIALIZE (this);
+
+    OpCode op = opstr.op;
+    switch (op)
+    {
+      case OpCode_charset:
+        return_trace (FontDict::serialize_offset4_op(c, op, mod.offsets.charsetInfo.offset));
+
+      case OpCode_Encoding:
+        return_trace (FontDict::serialize_offset4_op(c, op, mod.offsets.encodingOffset));
+
+      case OpCode_Private:
+        {
+          if (unlikely (!UnsizedByteStr::serialize_int2 (c, mod.offsets.privateDictInfo.size)))
+            return_trace (false);
+          if (unlikely (!UnsizedByteStr::serialize_int4 (c, mod.offsets.privateDictInfo.offset)))
+            return_trace (false);
+          HBUINT8 *p = c->allocate_size<HBUINT8> (1);
+          if (unlikely (p == nullptr)) return_trace (false);
+          p->set (OpCode_Private);
+        }
+        break;
+
+      case OpCode_version:
+      case OpCode_Notice:
+      case OpCode_Copyright:
+      case OpCode_FullName:
+      case OpCode_FamilyName:
+      case OpCode_Weight:
+      case OpCode_PostScript:
+      case OpCode_BaseFontName:
+      case OpCode_FontName:
+        return_trace (FontDict::serialize_offset2_op(c, op, mod.nameSIDs[NameDictValues::name_op_to_index (op)]));
+
+      case OpCode_ROS:
+        {
+          /* for registry & ordering, reassigned SIDs are serialized
+           * for supplement, the original byte string is copied along with the op code */
+          OpStr supp_op;
+          supp_op.op = op;
+          supp_op.str.str = opstr.str.str + opstr.last_arg_offset;
+          assert (opstr.str.len >= opstr.last_arg_offset + 3);
+          supp_op.str.len = opstr.str.len - opstr.last_arg_offset;
+        return_trace (UnsizedByteStr::serialize_int2 (c, mod.nameSIDs[NameDictValues::registry]) &&
+                      UnsizedByteStr::serialize_int2 (c, mod.nameSIDs[NameDictValues::ordering]) &&
+                      copy_opstr (c, supp_op));
+        }
+      default:
+        return_trace (CFFTopDict_OpSerializer<CFF1TopDictVal>::serialize (c, opstr, mod.offsets));
+    }
+    return_trace (true);
+  }
+
+  inline unsigned int calculate_serialized_size (const CFF1TopDictVal &opstr) const
+  {
+    OpCode op = opstr.op;
+    switch (op)
+    {
+      case OpCode_charset:
+      case OpCode_Encoding:
+        return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (op);
+    
+      case OpCode_Private:
+        return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Private);
+    
+      case OpCode_version:
+      case OpCode_Notice:
+      case OpCode_Copyright:
+      case OpCode_FullName:
+      case OpCode_FamilyName:
+      case OpCode_Weight:
+      case OpCode_PostScript:
+      case OpCode_BaseFontName:
+      case OpCode_FontName:
+        return OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (op);
+
+      case OpCode_ROS:
+        return ((OpCode_Size (OpCode_shortint) + 2) * 2) + (opstr.str.len - opstr.last_arg_offset)/* supplement + op */;
+
+      default:
+        return CFFTopDict_OpSerializer<CFF1TopDictVal>::calculate_serialized_size (opstr);
+    }
+  }
+};
+
+struct FontDictValuesMod
+{
+  inline void init (const CFF1FontDictValues *base_,
+                    unsigned int fontName_,
+                    const TableInfo &privateDictInfo_)
+  {
+    base = base_;
+    fontName = fontName_;
+    privateDictInfo = privateDictInfo_;
+  }
+
+  inline unsigned getNumValues (void) const
+  {
+    return base->getNumValues ();
+  }
+
+  inline const OpStr &operator [] (unsigned int i) const { return (*base)[i]; }
+
+  const CFF1FontDictValues    *base;
+  TableInfo                   privateDictInfo;
+  unsigned int                fontName;
+};
+
+struct CFF1FontDict_OpSerializer : CFFFontDict_OpSerializer
+{
+  inline bool serialize (hb_serialize_context_t *c,
+                         const OpStr &opstr,
+                         const FontDictValuesMod &mod) const
+  {
+    TRACE_SERIALIZE (this);
+
+    if (opstr.op == OpCode_FontName)
+      return_trace (FontDict::serialize_uint2_op (c, opstr.op, mod.fontName));
+    else
+      return_trace (SUPER::serialize (c, opstr, mod.privateDictInfo));
+  }
+
+  inline unsigned int calculate_serialized_size (const OpStr &opstr) const
+  {
+    if (opstr.op == OpCode_FontName)
+      return OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_FontName);
+    else
+      return SUPER::calculate_serialized_size (opstr);
+  }
+
+  private:
+  typedef CFFFontDict_OpSerializer SUPER;
+};
+
+struct CFF1CSOpSet_Flatten : CFF1CSOpSet<CFF1CSOpSet_Flatten, FlattenParam>
+{
+  static inline void flush_args_and_op (OpCode op, CFF1CSInterpEnv &env, FlattenParam& param)
+  {
+    switch (op)
+    {
+      case OpCode_hstem:
+      case OpCode_hstemhm:
+      case OpCode_vstem:
+      case OpCode_vstemhm:
+      case OpCode_hintmask:
+      case OpCode_cntrmask:
+      case OpCode_hflex:
+      case OpCode_flex:
+      case OpCode_hflex1:
+      case OpCode_flex1:
+        if (param.drop_hints)
+        {
+          env.clear_args ();
+          return;
+        }
+        /* NO BREAK */
+
+      default:
+        SUPER::flush_args_and_op (op, env, param);
+        break;
+    }
+  }
+
+  static inline void flush_n_args (unsigned int n, CFF1CSInterpEnv &env, FlattenParam& param)
+  {
+    for (unsigned int i = env.argStack.count - n; i < env.argStack.count; i++)
+      param.flatStr.encode_num (env.argStack.elements[i]);
+    SUPER::flush_n_args (n, env, param);
+  }
+
+  static inline void flush_op (OpCode op, CFF1CSInterpEnv &env, FlattenParam& param)
+  {
+    param.flatStr.encode_op (op);
+  }
+
+  static inline void flush_hintmask (OpCode op, CFF1CSInterpEnv &env, FlattenParam& param)
+  {
+    SUPER::flush_hintmask (op, env, param);
+    if (!param.drop_hints)
+      for (unsigned int i = 0; i < env.hintmask_size; i++)
+        param.flatStr.encode_byte (env.substr[i]);
+  }
+
+  private:
+  typedef CFF1CSOpSet<CFF1CSOpSet_Flatten, FlattenParam> SUPER;
+};
+
+struct cff_subset_plan {
+  inline cff_subset_plan (void)
+    : final_size (0),
+      orig_fdcount (0),
+      subset_fdcount (1),
+      subset_fdselect_format (0),
+      offsets (),
+      drop_hints (false)
+  {
+    topdict_sizes.init ();
+    topdict_sizes.resize (1);
+    topdict_mod.init ();
+    subset_fdselect_first_glyphs.init ();
+    fdmap.init ();
+    subset_charstrings.init ();
+    flat_charstrings.init ();
+    fontdicts_mod.init ();
+    subset_enc_code_ranges.init ();
+    subset_enc_supp_codes.init ();
+    subset_charset_ranges.init ();
+    sidmap.init ();
+    for (unsigned int i = 0; i < NameDictValues::ValCount; i++)
+      topDictModSIDs[i] = CFF_UNDEF_SID;
+  }
+
+  inline ~cff_subset_plan (void)
+  {
+    topdict_sizes.fini ();
+    topdict_mod.fini ();
+    subset_fdselect_first_glyphs.fini ();
+    fdmap.fini ();
+    subset_charstrings.fini ();
+    flat_charstrings.fini ();
+    fontdicts_mod.fini ();
+    subset_enc_code_ranges.fini ();
+    subset_enc_supp_codes.init ();
+    subset_charset_ranges.fini ();
+    sidmap.fini ();
+    fontdicts_mod.fini ();
+  }
+
+  inline unsigned int plan_subset_encoding (const OT::cff1::accelerator_subset_t &acc, hb_subset_plan_t *plan)
+  {
+    const Encoding *encoding = acc.encoding;
+    unsigned int  size0, size1, supp_size;
+    hb_codepoint_t  code, last_code = CFF_UNDEF_CODE;
+    hb_vector_t<hb_codepoint_t> supp_codes;
+
+    subset_enc_code_ranges.resize (0);
+    supp_size = 0;
+    supp_codes.init ();
+
+    subset_enc_num_codes = plan->glyphs.len - 1;
+    unsigned int glyph;
+    for (glyph = 1; glyph < plan->glyphs.len; glyph++)
+    {
+      hb_codepoint_t  orig_glyph = plan->glyphs[glyph];
+      code = acc.glyph_to_code (orig_glyph);
+      if (code == CFF_UNDEF_CODE)
+      {
+        subset_enc_num_codes = glyph - 1;
+        break;
+      }
+
+      if (code != last_code + 1)
+      {
+        if (subset_enc_code_ranges.len > 0)
+        {
+          code_pair &pair = subset_enc_code_ranges[subset_enc_code_ranges.len - 1];
+          pair.glyph = glyph - pair.glyph - 1;
+        }
+        code_pair pair = { code, glyph };
+        subset_enc_code_ranges.push (pair);
+      }
+      last_code = code;
+
+      if (encoding != &Null(Encoding))
+      {
+        hb_codepoint_t  sid = acc.glyph_to_sid (orig_glyph);
+        encoding->get_supplement_codes (sid, supp_codes);
+        for (unsigned int i = 0; i < supp_codes.len; i++)
+        {
+          code_pair pair = { supp_codes[i], sid };
+          subset_enc_supp_codes.push (pair);
+        }
+        supp_size += SuppEncoding::static_size * supp_codes.len;
+      }
+    }
+    supp_codes.fini ();
+    if (subset_enc_code_ranges.len > 0)
+    {
+      code_pair &pair = subset_enc_code_ranges[subset_enc_code_ranges.len - 1];
+      pair.glyph = glyph - pair.glyph - 1;
+    }
+
+    assert (subset_enc_num_codes <= 0xFF);
+    size0 = Encoding0::min_size + HBUINT8::static_size * subset_enc_num_codes;
+    size1 = Encoding1::min_size + Encoding1_Range::static_size * subset_enc_code_ranges.len;
+
+    if (size0 < size1)
+      subset_enc_format = 0;
+    else
+      subset_enc_format = 1;
+
+    return Encoding::calculate_serialized_size (
+                        subset_enc_format,
+                        subset_enc_format? subset_enc_code_ranges.len: subset_enc_num_codes,
+                        subset_enc_supp_codes.len);
+  }
+
+  inline unsigned int plan_subset_charset (const OT::cff1::accelerator_subset_t &acc, hb_subset_plan_t *plan)
+  {
+    unsigned int  size0, size_ranges;
+    hb_codepoint_t  sid, last_sid = CFF_UNDEF_CODE;
+    bool  two_byte = false;
+
+    subset_charset_ranges.resize (0);
+    unsigned int glyph;
+    for (glyph = 1; glyph < plan->glyphs.len; glyph++)
+    {
+      hb_codepoint_t  orig_glyph = plan->glyphs[glyph];
+      sid = acc.glyph_to_sid (orig_glyph);
+
+      if (!acc.is_CID ())
+        sid = sidmap.add (sid);
+
+      if (sid != last_sid + 1)
+      {
+        if (subset_charset_ranges.len > 0)
+        {
+          code_pair &pair = subset_charset_ranges[subset_charset_ranges.len - 1];
+          pair.glyph = glyph - pair.glyph - 1;
+          if ((pair.glyph & ~0xFF) != 0) two_byte = true;
+        }
+        code_pair pair = { sid, glyph };
+        subset_charset_ranges.push (pair);
+      }
+      last_sid = sid;
+    }
+
+    if (subset_charset_ranges.len > 0)
+    {
+      code_pair &pair = subset_charset_ranges[subset_charset_ranges.len - 1];
+      pair.glyph = glyph - pair.glyph - 1;
+      if ((pair.glyph & ~0xFF) != 0) two_byte = true;
+    }
+
+    size0 = Charset0::min_size + HBUINT16::static_size * (plan->glyphs.len - 1);
+    if (!two_byte)
+      size_ranges = Charset1::min_size + Charset1_Range::static_size * subset_charset_ranges.len;
+    else
+      size_ranges = Charset2::min_size + Charset2_Range::static_size * subset_charset_ranges.len;
+
+    if (size0 < size_ranges)
+      subset_charset_format = 0;
+    else if (!two_byte)
+      subset_charset_format = 1;
+    else
+      subset_charset_format = 2;
+
+    return Charset::calculate_serialized_size (
+                        subset_charset_format,
+                        subset_charset_format? subset_charset_ranges.len: plan->glyphs.len);
+  }
+
+  inline bool collect_sids_in_dicts (const OT::cff1::accelerator_subset_t &acc)
+  {
+    if (unlikely (!sidmap.reset (acc.stringIndex->count)))
+      return false;
+    
+    for (unsigned int i = 0; i < NameDictValues::ValCount; i++)
+    {
+      unsigned int sid = acc.topDict.nameSIDs[i];
+      if (sid != CFF_UNDEF_SID)
+      {
+        (void)sidmap.add (sid);
+        topDictModSIDs[i] = sidmap[sid];
+      }
+    }
+
+    if (acc.fdArray != &Null(CFF1FDArray))
+      for (unsigned int fd = 0; fd < orig_fdcount; fd++)
+        if (!fdmap.excludes (fd))
+          (void)sidmap.add (acc.fontDicts[fd].fontName);
+  
+    return true;
+  }
+
+  inline bool create (const OT::cff1::accelerator_subset_t &acc,
+                      hb_subset_plan_t *plan)
+  {
+     /* make sure notdef is first */
+    if ((plan->glyphs.len == 0) || (plan->glyphs[0] != 0)) return false;
+
+    final_size = 0;
+    num_glyphs = plan->glyphs.len;
+    orig_fdcount = acc.fdCount;
+    drop_hints = plan->drop_hints;
+
+    /* check whether the subset renumbers any glyph IDs */
+    gid_renum = false;
+    for (unsigned int glyph = 0; glyph < plan->glyphs.len; glyph++)
+    {
+      if (plan->glyphs[glyph] != glyph) {
+        gid_renum = true;
+        break;
+      }
+    }
+
+    subset_charset = gid_renum || !acc.is_predef_charset ();
+    subset_encoding = !acc.is_CID() && (gid_renum || !acc.is_predef_encoding ());
+
+    /* CFF header */
+    final_size += OT::cff1::static_size;
+    
+    /* Name INDEX */
+    offsets.nameIndexOffset = final_size;
+    final_size += acc.nameIndex->get_size ();
+
+    /* top dict INDEX */
+    {
+      /* Add encoding/charset to a (copy of) top dict as necessary */
+      topdict_mod.init (&acc.topDict);
+      bool need_to_add_enc = (subset_encoding && !acc.topDict.hasOp (OpCode_Encoding));
+      bool need_to_add_set = (subset_charset && !acc.topDict.hasOp (OpCode_charset));
+      if (need_to_add_enc || need_to_add_set)
+      {
+        if (need_to_add_enc)
+          topdict_mod.addOp (OpCode_Encoding);
+        if (need_to_add_set)
+          topdict_mod.addOp (OpCode_charset);
+      }
+      offsets.topDictInfo.offset = final_size;
+      CFF1TopDict_OpSerializer topSzr;
+      unsigned int topDictSize = TopDict::calculate_serialized_size (topdict_mod, topSzr);
+      offsets.topDictInfo.offSize = calcOffSize(topDictSize);
+      final_size += CFF1IndexOf<TopDict>::calculate_serialized_size<CFF1TopDictValuesMod>
+                                                (offsets.topDictInfo.offSize,
+                                                 &topdict_mod, 1, topdict_sizes, topSzr);
+    }
+
+    /* Determine re-mapping of font index as fdmap among other info */
+    if (acc.fdSelect != &Null(CFF1FDSelect)
+        && unlikely (!hb_plan_subset_cff_fdselect (plan->glyphs,
+                                  orig_fdcount,
+                                  *acc.fdSelect,
+                                  subset_fdcount,
+                                  offsets.FDSelectInfo.size,
+                                  subset_fdselect_format,
+                                  subset_fdselect_first_glyphs,
+                                  fdmap)))
+        return false;
+
+    /* remove unused SIDs & reassign SIDs */
+    {
+      /* SIDs for name strings in dicts are added before glyph names so they fit in 16-bit int range */
+      if (unlikely (!collect_sids_in_dicts (acc)))
+        return false;
+      assert (sidmap.get_count () <= 0x8000);
+      if (subset_charset)
+        offsets.charsetInfo.size = plan_subset_charset (acc, plan);
+
+      topdict_mod.reassignSIDs (sidmap);
+    }
+
+    /* String INDEX */
+    {
+      offsets.stringIndexInfo.offset = final_size;
+      offsets.stringIndexInfo.size = acc.stringIndex->calculate_serialized_size (offsets.stringIndexInfo.offSize, sidmap);
+      final_size += offsets.stringIndexInfo.size;
+    }
+    
+    {
+      /* Flatten global & local subrs */
+      SubrFlattener<const OT::cff1::accelerator_subset_t, CFF1CSInterpEnv, CFF1CSOpSet_Flatten>
+                    flattener(acc, plan->glyphs, plan->drop_hints);
+      if (!flattener.flatten (flat_charstrings))
+        return false;
+      
+      /* no global/local subroutines */
+      offsets.globalSubrsInfo.size = HBUINT16::static_size; /* count 0 only */
+    }
+
+    /* global subrs */
+    offsets.globalSubrsInfo.offset = final_size;
+    final_size += offsets.globalSubrsInfo.size;
+
+    /* Encoding */
+    offsets.encodingOffset = final_size;
+    if (subset_encoding)
+      final_size += plan_subset_encoding (acc, plan);
+
+    /* Charset */
+    offsets.charsetInfo.offset = final_size;
+    final_size += offsets.charsetInfo.size;
+
+    /* FDSelect */
+    if (acc.fdSelect != &Null(CFF1FDSelect))
+    {
+      offsets.FDSelectInfo.offset = final_size;
+      if (!is_fds_subsetted ())
+        offsets.FDSelectInfo.size = acc.fdSelect->calculate_serialized_size (acc.num_glyphs);
+      final_size += offsets.FDSelectInfo.size;
+    }
+
+    /* FDArray (FDIndex) */
+    if (acc.fdArray != &Null(CFF1FDArray)) {
+      offsets.FDArrayInfo.offset = final_size;
+      CFF1FontDict_OpSerializer fontSzr;
+      unsigned int dictsSize = 0;
+      for (unsigned int i = 0; i < acc.fontDicts.len; i++)
+        if (!fdmap.excludes (i))
+          dictsSize += FontDict::calculate_serialized_size (acc.fontDicts[i], fontSzr);
+
+      offsets.FDArrayInfo.offSize = calcOffSize (dictsSize + 1);
+      final_size += CFF1Index::calculate_serialized_size (offsets.FDArrayInfo.offSize, subset_fdcount, dictsSize);
+    }
+
+    /* CharStrings */
+    {
+      offsets.charStringsInfo.offset = final_size;
+      unsigned int dataSize = 0;
+      for (unsigned int i = 0; i < plan->glyphs.len; i++)
+      {
+        ByteStrBuff &flatstr = flat_charstrings[i];
+        ByteStr str (&flatstr[0], flatstr.len);
+        subset_charstrings.push (str);
+        dataSize += flatstr.len;
+      }
+      offsets.charStringsInfo.offSize = calcOffSize (dataSize + 1);
+      final_size += CFF1CharStrings::calculate_serialized_size (offsets.charStringsInfo.offSize, plan->glyphs.len, dataSize);
+    }
+
+    /* private dicts & local subrs */
+    offsets.privateDictInfo.offset = final_size;
+    for (unsigned int i = 0; i < orig_fdcount; i++)
+    {
+      if (!fdmap.excludes (i))
+      {
+        CFFPrivateDict_OpSerializer privSzr (plan->drop_hints);
+        unsigned int  priv_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr);
+        TableInfo  privInfo = { final_size, priv_size, 0 };
+        FontDictValuesMod fontdict_mod;
+        fontdict_mod.init ( &acc.fontDicts[i], sidmap[acc.fontDicts[i].fontName], privInfo );
+        fontdicts_mod.push (fontdict_mod);
+        final_size += privInfo.size;
+      }
+    }
+
+    if (!acc.is_CID ())
+      offsets.privateDictInfo = fontdicts_mod[0].privateDictInfo;
+
+    return ((subset_charstrings.len == plan->glyphs.len) &&
+            (fontdicts_mod.len == subset_fdcount));
+  }
+
+  inline unsigned int get_final_size (void) const  { return final_size; }
+
+  unsigned int              final_size;
+  hb_vector_t<unsigned int> topdict_sizes;
+  CFF1TopDictValuesMod      topdict_mod;
+  CFF1SubTableOffsets       offsets;
+
+  unsigned int    num_glyphs;
+  unsigned int    orig_fdcount;
+  unsigned int    subset_fdcount;
+  inline bool     is_fds_subsetted (void) const { return subset_fdcount < orig_fdcount; }
+  unsigned int    subset_fdselect_format;
+  hb_vector_t<hb_codepoint_t>   subset_fdselect_first_glyphs;
+
+  /* font dict index remap table from fullset FDArray to subset FDArray.
+   * set to CFF_UNDEF_CODE if excluded from subset */
+  Remap   fdmap;
+
+  hb_vector_t<ByteStr>    subset_charstrings;
+  ByteStrBuffArray        flat_charstrings;
+  hb_vector_t<FontDictValuesMod>  fontdicts_mod;
+
+  bool                    flatten_subrs;
+  bool                    drop_hints;
+
+  bool                    gid_renum;
+  bool                    subset_encoding;
+  uint8_t                 subset_enc_format;
+  unsigned int            subset_enc_num_codes;
+  hb_vector_t<code_pair>  subset_enc_code_ranges;
+  hb_vector_t<code_pair>  subset_enc_supp_codes;
+
+  uint8_t                 subset_charset_format;
+  hb_vector_t<code_pair>  subset_charset_ranges;
+  bool                    subset_charset;
+
+  RemapSID                sidmap;
+  unsigned int            topDictModSIDs[NameDictValues::ValCount];
+};
+
+static inline bool _write_cff1 (const cff_subset_plan &plan,
+                                const OT::cff1::accelerator_subset_t  &acc,
+                                const hb_vector_t<hb_codepoint_t>& glyphs,
+                                unsigned int dest_sz,
+                                void *dest)
+{
+  hb_serialize_context_t c (dest, dest_sz);
+
+  char RETURN_OP[1] = { OpCode_return };
+  const ByteStr NULL_SUBR (RETURN_OP, 1);
+
+  OT::cff1 *cff = c.start_serialize<OT::cff1> ();
+  if (unlikely (!c.extend_min (*cff)))
+    return false;
+
+  /* header */
+  cff->version.major.set (0x01);
+  cff->version.minor.set (0x00);
+  cff->nameIndex.set (cff->min_size);
+  cff->offSize.set (4); /* unused? */
+
+  /* name INDEX */
+  {
+    assert (cff->nameIndex == c.head - c.start);
+    CFF1NameIndex *dest = c.start_embed<CFF1NameIndex> ();
+    if (unlikely (dest == nullptr)) return false;
+    if (unlikely (!dest->serialize (&c, *acc.nameIndex)))
+    {
+      DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF name INDEX");
+      return false;
+    }
+  }
+
+  /* top dict INDEX */
+  {
+    assert (plan.offsets.topDictInfo.offset == c.head - c.start);
+    CFF1IndexOf<TopDict> *dest = c.start_embed< CFF1IndexOf<TopDict> > ();
+    if (dest == nullptr) return false;
+    CFF1TopDict_OpSerializer topSzr;
+    TopDictModifiers  modifier (plan.offsets, plan.topDictModSIDs);
+    if (unlikely (!dest->serialize (&c, plan.offsets.topDictInfo.offSize,
+                                    &plan.topdict_mod, 1,
+                                    plan.topdict_sizes, topSzr, modifier)))
+    {
+      DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF top dict");
+      return false;
+    }
+  }
+
+  /* String INDEX */
+  {
+    assert (plan.offsets.stringIndexInfo.offset == c.head - c.start);
+    CFF1StringIndex *dest = c.start_embed<CFF1StringIndex> ();
+    if (unlikely (dest == nullptr)) return false;
+    if (unlikely (!dest->serialize (&c, *acc.stringIndex, plan.offsets.stringIndexInfo.offSize, plan.sidmap)))
+    {
+      DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF string INDEX");
+      return false;
+    }
+  }
+
+  /* global subrs */
+  {
+    assert (plan.offsets.globalSubrsInfo.offset != 0);
+    assert (plan.offsets.globalSubrsInfo.offset == c.head - c.start);
+    CFF1Subrs *dest = c.allocate_size <CFF1Subrs> (HBUINT16::static_size);
+    if (unlikely (dest == nullptr)) return false;
+    dest->count.set (0);
+  }
+
+  /* Encoding */
+  if (plan.subset_encoding)
+  {
+    assert (plan.offsets.encodingOffset == c.head - c.start);
+    Encoding *dest = c.start_embed<Encoding> ();
+    if (unlikely (dest == nullptr)) return false;
+    if (unlikely (!dest->serialize (&c,
+                                    plan.subset_enc_format,
+                                    plan.subset_enc_num_codes,
+                                    plan.subset_enc_code_ranges,
+                                    plan.subset_enc_supp_codes)))
+    {
+      DEBUG_MSG (SUBSET, nullptr, "failed to serialize Encoding");
+      return false;
+    }
+  }
+
+  /* Charset */
+  if (plan.subset_charset)
+  {
+    assert (plan.offsets.charsetInfo.offset == c.head - c.start);
+    Charset *dest = c.start_embed<Charset> ();
+    if (unlikely (dest == nullptr)) return false;
+    if (unlikely (!dest->serialize (&c,
+                                    plan.subset_charset_format,
+                                    plan.num_glyphs,
+                                    plan.subset_charset_ranges)))
+    {
+      DEBUG_MSG (SUBSET, nullptr, "failed to serialize Charset");
+      return false;
+    }
+  }
+
+  /* FDSelect */
+  if (acc.fdSelect != &Null(CFF1FDSelect))
+  {
+    assert (plan.offsets.FDSelectInfo.offset == c.head - c.start);
+    
+    if (plan.is_fds_subsetted ())
+    {
+      if (unlikely (!hb_serialize_cff_fdselect (&c, glyphs, *acc.fdSelect, acc.fdCount,
+                                                plan.subset_fdselect_format, plan.offsets.FDSelectInfo.size,
+                                                plan.subset_fdselect_first_glyphs,
+                                                plan.fdmap)))
+      {
+        DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF subset FDSelect");
+        return false;
+      }
+    }
+    else
+    {
+      CFF1FDSelect *dest = c.start_embed<CFF1FDSelect> ();
+      if (unlikely (!dest->serialize (&c, *acc.fdSelect, acc.num_glyphs)))
+      {
+        DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF FDSelect");
+        return false;
+      }
+    }
+  }
+
+  /* FDArray (FD Index) */
+  if (acc.fdArray != &Null(CFF1FDArray))
+  {
+    assert (plan.offsets.FDArrayInfo.offset == c.head - c.start);
+    CFF1FDArray  *fda = c.start_embed<CFF1FDArray> ();
+    if (unlikely (fda == nullptr)) return false;
+    CFF1FontDict_OpSerializer  fontSzr;
+    if (unlikely (!fda->serialize (&c, plan.offsets.FDArrayInfo.offSize,
+                                   plan.fontdicts_mod,
+                                   fontSzr)))
+    {
+      DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF FDArray");
+      return false;
+    }
+  }
+
+  /* CharStrings */
+  {
+    assert (plan.offsets.charStringsInfo.offset == c.head - c.start);
+    CFF1CharStrings  *cs = c.start_embed<CFF1CharStrings> ();
+    if (unlikely (cs == nullptr)) return false;
+    if (unlikely (!cs->serialize (&c, plan.offsets.charStringsInfo.offSize, plan.subset_charstrings)))
+    {
+      DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF CharStrings");
+      return false;
+    }
+  }
+
+  /* private dicts & local subrs */
+  assert (plan.offsets.privateDictInfo.offset == c.head - c.start);
+  for (unsigned int i = 0; i < acc.privateDicts.len; i++)
+  {
+    if (!plan.fdmap.excludes (i))
+    {
+      PrivateDict  *pd = c.start_embed<PrivateDict> ();
+      if (unlikely (pd == nullptr)) return false;
+      unsigned int priv_size = plan.fontdicts_mod[plan.fdmap[i]].privateDictInfo.size;
+      bool result;
+      CFFPrivateDict_OpSerializer privSzr (plan.drop_hints);
+      /* 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))
+      {
+        DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF Private Dict[%d]", i);
+        return false;
+      }
+    }
+  }
+
+  assert (c.head == c.end);
+  c.end_serialize ();
+
+  return true;
+}
+
+static bool
+_hb_subset_cff1 (const OT::cff1::accelerator_subset_t  &acc,
+                const char                      *data,
+                hb_subset_plan_t                *plan,
+                hb_blob_t                       **prime /* OUT */)
+{
+  cff_subset_plan cff_plan;
+
+  if (unlikely (!cff_plan.create (acc, plan)))
+  {
+    DEBUG_MSG(SUBSET, nullptr, "Failed to generate a cff subsetting plan.");
+    return false;
+  }
+
+  unsigned int  cff_prime_size = cff_plan.get_final_size ();
+  char *cff_prime_data = (char *) calloc (1, cff_prime_size);
+
+  if (unlikely (!_write_cff1 (cff_plan, acc, plan->glyphs,
+                              cff_prime_size, cff_prime_data))) {
+    DEBUG_MSG(SUBSET, nullptr, "Failed to write a subset cff.");
+    free (cff_prime_data);
+    return false;
+  }
+
+  *prime = hb_blob_create (cff_prime_data,
+                           cff_prime_size,
+                           HB_MEMORY_MODE_READONLY,
+                           cff_prime_data,
+                           free);
+  return true;
+}
+
+/**
+ * hb_subset_cff1:
+ * Subsets the CFF table according to a provided plan.
+ *
+ * Return value: subsetted cff table.
+ **/
+bool
+hb_subset_cff1 (hb_subset_plan_t *plan,
+                hb_blob_t       **prime /* OUT */)
+{
+  hb_blob_t *cff_blob = hb_sanitize_context_t().reference_table<CFF::cff1> (plan->source);
+  const char *data = hb_blob_get_data(cff_blob, nullptr);
+
+  OT::cff1::accelerator_subset_t acc;
+  acc.init(plan->source);
+  bool result = likely (acc.is_valid ()) &&
+                        _hb_subset_cff1 (acc, data, plan, prime);
+  hb_blob_destroy (cff_blob);
+  acc.fini ();
+
+  return result;
+}
diff --git a/src/hb-subset-cff1.hh b/src/hb-subset-cff1.hh
new file mode 100644
index 0000000..eb2bc53
--- /dev/null
+++ b/src/hb-subset-cff1.hh
@@ -0,0 +1,38 @@
+/*
+ * 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_SUBSET_CFF1_HH
+#define HB_SUBSET_CFF1_HH
+
+#include "hb.hh"
+
+#include "hb-subset-plan.hh"
+
+HB_INTERNAL bool
+hb_subset_cff1 (hb_subset_plan_t *plan,
+               hb_blob_t        **cff_prime /* OUT */);
+
+#endif /* HB_SUBSET_CFF1_HH */
diff --git a/src/hb-subset-cff2.cc b/src/hb-subset-cff2.cc
new file mode 100644
index 0000000..193b433
--- /dev/null
+++ b/src/hb-subset-cff2.cc
@@ -0,0 +1,498 @@
+/*
+ * 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
+ */
+
+#include "hb-open-type.hh"
+#include "hb-ot-cff2-table.hh"
+#include "hb-set.h"
+#include "hb-subset-cff2.hh"
+#include "hb-subset-plan.hh"
+#include "hb-subset-cff-common.hh"
+#include "hb-cff2-interp-cs.hh"
+
+using namespace CFF;
+
+struct CFF2SubTableOffsets : CFFSubTableOffsets
+{
+  inline CFF2SubTableOffsets (void)
+    : CFFSubTableOffsets (),
+      varStoreOffset (0)
+  {}
+
+  unsigned int  varStoreOffset;
+};
+
+struct CFF2TopDict_OpSerializer : CFFTopDict_OpSerializer<>
+{
+  inline bool serialize (hb_serialize_context_t *c,
+                         const OpStr &opstr,
+                         const CFF2SubTableOffsets &offsets) const
+  {
+    TRACE_SERIALIZE (this);
+
+    switch (opstr.op)
+    {
+      case OpCode_vstore:
+        return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.varStoreOffset));
+
+      default:
+        return_trace (CFFTopDict_OpSerializer<>::serialize (c, opstr, offsets));
+    }
+  }
+
+  inline unsigned int calculate_serialized_size (const OpStr &opstr) const
+  {
+    switch (opstr.op)
+    {
+      case OpCode_vstore:
+        return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (opstr.op);
+    
+      default:
+        return CFFTopDict_OpSerializer<>::calculate_serialized_size (opstr);
+    }
+  }
+};
+
+struct CFF2CSOpSet_Flatten : CFF2CSOpSet<CFF2CSOpSet_Flatten, FlattenParam>
+{
+  static inline void flush_args_and_op (OpCode op, CFF2CSInterpEnv &env, FlattenParam& param)
+  {
+    switch (op)
+    {
+      case OpCode_return:
+      case OpCode_endchar:
+        /* dummy opcodes in CFF2. ignore */
+        break;
+    
+      case OpCode_hstem:
+      case OpCode_hstemhm:
+      case OpCode_vstem:
+      case OpCode_vstemhm:
+      case OpCode_hintmask:
+      case OpCode_cntrmask:
+      case OpCode_hflex:
+      case OpCode_flex:
+      case OpCode_hflex1:
+      case OpCode_flex1:
+        if (param.drop_hints)
+        {
+          env.clear_args ();
+          return;
+        }
+        /* NO BREAK */
+
+      default:
+        SUPER::flush_args_and_op (op, env, param);
+        break;
+    }
+  }
+
+  static inline void flush_n_args (unsigned int n, CFF2CSInterpEnv &env, FlattenParam& param)
+  {
+    for (unsigned int i = env.argStack.count - n; i < env.argStack.count;)
+    {
+      const BlendArg &arg = env.argStack.elements[i];
+      if (arg.blended ())
+      {
+        assert ((arg.numValues > 0) && (n >= arg.numValues));
+        flatten_blends (arg, i, env, param);
+        i += arg.numValues;
+      }
+      else
+      {
+        param.flatStr.encode_num (arg);
+        i++;
+      }
+    }
+    SUPER::flush_n_args (n, env, param);
+  }
+
+  static inline void flatten_blends (const BlendArg &arg, unsigned int i, CFF2CSInterpEnv &env, FlattenParam& param)
+  {
+    /* flatten the default values */
+    for (unsigned int j = 0; j < arg.numValues; j++)
+    {
+      const BlendArg &arg1 = env.argStack.elements[i + j];
+      assert (arg1.blended () && (arg.numValues == arg1.numValues) && (arg1.valueIndex == j) &&
+              (arg1.deltas.len == env.get_region_count ()));
+      param.flatStr.encode_num (arg1);
+    }
+    /* flatten deltas for each value */
+    for (unsigned int j = 0; j < arg.numValues; j++)
+    {
+      const BlendArg &arg1 = env.argStack.elements[i + j];
+      for (unsigned int k = 0; k < arg1.deltas.len; k++)
+        param.flatStr.encode_num (arg1.deltas[k]);
+    }
+    /* flatten the number of values followed by blend operator */
+    param.flatStr.encode_int (arg.numValues);
+    param.flatStr.encode_op (OpCode_blendcs);
+  }
+
+  static inline void flush_op (OpCode op, CFF2CSInterpEnv &env, FlattenParam& param)
+  {
+    switch (op)
+    {
+      case OpCode_return:
+      case OpCode_endchar:
+        return;
+      default:
+        param.flatStr.encode_op (op);
+    }
+  }
+
+  private:
+  typedef CFF2CSOpSet<CFF2CSOpSet_Flatten, FlattenParam> SUPER;
+  typedef CSOpSet<BlendArg, CFF2CSOpSet_Flatten, CFF2CSInterpEnv, FlattenParam> CSOPSET;
+};
+
+struct cff2_subset_plan {
+  inline cff2_subset_plan (void)
+    : final_size (0),
+      orig_fdcount (0),
+      subset_fdcount(1),
+      subset_fdselect_format (0),
+      drop_hints (false)
+  {
+    subset_fdselect_first_glyphs.init ();
+    fdmap.init ();
+    subset_charstrings.init ();
+    flat_charstrings.init ();
+    privateDictInfos.init ();
+  }
+
+  inline ~cff2_subset_plan (void)
+  {
+    subset_fdselect_first_glyphs.fini ();
+    fdmap.fini ();
+    subset_charstrings.fini ();
+    flat_charstrings.fini ();
+    privateDictInfos.fini ();
+  }
+
+  inline bool create (const OT::cff2::accelerator_subset_t &acc,
+              hb_subset_plan_t *plan)
+  {
+    final_size = 0;
+    orig_fdcount = acc.fdArray->count;
+
+    drop_hints = plan->drop_hints;
+
+    /* CFF2 header */
+    final_size += OT::cff2::static_size;
+    
+    /* top dict */
+    {
+      CFF2TopDict_OpSerializer topSzr;
+      offsets.topDictInfo.size = TopDict::calculate_serialized_size (acc.topDict, topSzr);
+      final_size += offsets.topDictInfo.size;
+    }
+
+    {
+      /* Flatten global & local subrs */
+      SubrFlattener<const OT::cff2::accelerator_subset_t, CFF2CSInterpEnv, CFF2CSOpSet_Flatten>
+                    flattener(acc, plan->glyphs, plan->drop_hints);
+      if (!flattener.flatten (flat_charstrings))
+        return false;
+      
+      /* no global/local subroutines */
+      offsets.globalSubrsInfo.size = HBUINT32::static_size; /* count 0 only */
+    }
+    
+    /* global subrs */
+    offsets.globalSubrsInfo.offset = final_size;
+    final_size += offsets.globalSubrsInfo.size;
+
+    /* variation store */
+    if (acc.varStore != &Null(CFF2VariationStore))
+    {
+      offsets.varStoreOffset = final_size;
+      final_size += acc.varStore->get_size ();
+    }
+
+    /* FDSelect */
+    if (acc.fdSelect != &Null(CFF2FDSelect))
+    {
+      offsets.FDSelectInfo.offset = final_size;
+      if (unlikely (!hb_plan_subset_cff_fdselect (plan->glyphs,
+                                  orig_fdcount,
+                                  *(const FDSelect *)acc.fdSelect,
+                                  subset_fdcount,
+                                  offsets.FDSelectInfo.size,
+                                  subset_fdselect_format,
+                                  subset_fdselect_first_glyphs,
+                                  fdmap)))
+        return false;
+      
+      if (!is_fds_subsetted ())
+        offsets.FDSelectInfo.size = acc.fdSelect->calculate_serialized_size (acc.num_glyphs);
+      final_size += offsets.FDSelectInfo.size;
+    }
+
+    /* FDArray (FDIndex) */
+    {
+      offsets.FDArrayInfo.offset = final_size;
+      CFFFontDict_OpSerializer fontSzr;
+      final_size += CFF2FDArray::calculate_serialized_size(offsets.FDArrayInfo.offSize/*OUT*/, acc.fontDicts, subset_fdcount, fdmap, fontSzr);
+    }
+
+    /* CharStrings */
+    {
+      offsets.charStringsInfo.offset = final_size;
+      unsigned int dataSize = 0;
+      for (unsigned int i = 0; i < plan->glyphs.len; i++)
+      {
+        ByteStrBuff &flatstr = flat_charstrings[i];
+        ByteStr str (&flatstr[0], flatstr.len);
+        subset_charstrings.push (str);
+        dataSize += flatstr.len;
+      }
+      offsets.charStringsInfo.offSize = calcOffSize (dataSize + 1);
+      final_size += CFF2CharStrings::calculate_serialized_size (offsets.charStringsInfo.offSize, plan->glyphs.len, dataSize);
+    }
+
+    /* private dicts & local subrs */
+    offsets.privateDictsOffset = final_size;
+    for (unsigned int i = 0; i < orig_fdcount; i++)
+    {
+      if (!fdmap.excludes (i))
+      {
+        unsigned int priv_size;
+        CFFPrivateDict_OpSerializer privSzr (drop_hints);
+        priv_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr);
+        TableInfo  privInfo = { final_size, priv_size, 0 };
+        privateDictInfos.push (privInfo);
+        final_size += privInfo.size;
+      }
+    }
+
+    return true;
+  }
+
+  inline unsigned int get_final_size (void) const  { return final_size; }
+
+  unsigned int        final_size;
+  CFF2SubTableOffsets offsets;
+
+  unsigned int    orig_fdcount;
+  unsigned int    subset_fdcount;
+  inline bool     is_fds_subsetted (void) const { return subset_fdcount < orig_fdcount; }
+  unsigned int    subset_fdselect_format;
+  hb_vector_t<hb_codepoint_t>   subset_fdselect_first_glyphs;
+
+  Remap   fdmap;
+
+  hb_vector_t<ByteStr>    subset_charstrings;
+  ByteStrBuffArray        flat_charstrings;
+  hb_vector_t<TableInfo>  privateDictInfos;
+
+  bool            drop_hints;
+};
+
+static inline bool _write_cff2 (const cff2_subset_plan &plan,
+                                const OT::cff2::accelerator_subset_t  &acc,
+                                const hb_vector_t<hb_codepoint_t>& glyphs,
+                                unsigned int dest_sz,
+                                void *dest)
+{
+  hb_serialize_context_t c (dest, dest_sz);
+
+  OT::cff2 *cff2 = c.start_serialize<OT::cff2> ();
+  if (unlikely (!c.extend_min (*cff2)))
+    return 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.topDictInfo.size);
+    TopDict &dict = cff2 + cff2->topDict;
+    CFF2TopDict_OpSerializer topSzr;
+    if (unlikely (!dict.serialize (&c, acc.topDict, topSzr, plan.offsets)))
+    {
+      DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 top dict");
+      return false;
+    }
+  }
+
+  /* global subrs */
+  {
+    assert (cff2->topDict + plan.offsets.topDictInfo.size == c.head - c.start);
+    CFF2Subrs *dest = c.allocate_size <CFF2Subrs> (HBUINT32::static_size);
+    if (unlikely (dest == nullptr)) return false;
+    dest->count.set (0);
+  }
+  
+  /* 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 false;
+    }
+  }
+
+  /* FDSelect */
+  if (acc.fdSelect != &Null(CFF2FDSelect))
+  {
+    assert (plan.offsets.FDSelectInfo.offset == c.head - c.start);
+    
+    if (plan.is_fds_subsetted ())
+    {
+      if (unlikely (!hb_serialize_cff_fdselect (&c, glyphs, *(const FDSelect *)acc.fdSelect, acc.fdArray->count,
+                                                plan.subset_fdselect_format, plan.offsets.FDSelectInfo.size,
+                                                plan.subset_fdselect_first_glyphs,
+                                                plan.fdmap)))
+      {
+        DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 subset FDSelect");
+        return false;
+      }
+    }
+    else
+    {
+      CFF2FDSelect *dest = c.start_embed<CFF2FDSelect> ();
+      if (unlikely (!dest->serialize (&c, *acc.fdSelect, acc.num_glyphs)))
+      {
+        DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 FDSelect");
+        return false;
+      }
+    }
+  }
+
+  /* FDArray (FD Index) */
+  {
+    assert (plan.offsets.FDArrayInfo.offset == c.head - c.start);
+    CFF2FDArray  *fda = c.start_embed<CFF2FDArray> ();
+    if (unlikely (fda == nullptr)) return false;
+    CFFFontDict_OpSerializer  fontSzr;
+    if (unlikely (!fda->serialize (&c, plan.offsets.FDArrayInfo.offSize,
+                                   acc.fontDicts, plan.subset_fdcount, plan.fdmap,
+                                   fontSzr, plan.privateDictInfos)))
+    {
+      DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 FDArray");
+      return false;
+    }
+  }
+
+  /* CharStrings */
+  {
+    assert (plan.offsets.charStringsInfo.offset == c.head - c.start);
+    CFF2CharStrings  *cs = c.start_embed<CFF2CharStrings> ();
+    if (unlikely (cs == nullptr)) return false;
+    if (unlikely (!cs->serialize (&c, plan.offsets.charStringsInfo.offSize, plan.subset_charstrings)))
+    {
+      DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 CharStrings");
+      return false;
+    }
+  }
+
+  /* private dicts & local subrs */
+  assert (plan.offsets.privateDictsOffset == c.head - c.start);
+  for (unsigned int i = 0; i < acc.privateDicts.len; i++)
+  {
+    if (!plan.fdmap.excludes (i))
+    {
+      PrivateDict  *pd = c.start_embed<PrivateDict> ();
+      if (unlikely (pd == nullptr)) return false;
+      unsigned int priv_size = plan.privateDictInfos[plan.fdmap[i]].size;
+      bool result;
+      CFFPrivateDict_OpSerializer privSzr (plan.drop_hints);
+      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;
+      }
+    }
+  }
+
+  assert (c.head == c.end);
+  c.end_serialize ();
+
+  return true;
+}
+
+static bool
+_hb_subset_cff2 (const OT::cff2::accelerator_subset_t  &acc,
+                const char                      *data,
+                hb_subset_plan_t                *plan,
+                hb_blob_t                       **prime /* OUT */)
+{
+  cff2_subset_plan cff2_plan;
+
+  if (unlikely (!cff2_plan.create (acc, plan)))
+  {
+    DEBUG_MSG(SUBSET, nullptr, "Failed to generate a cff2 subsetting plan.");
+    return false;
+  }
+
+  unsigned int  cff2_prime_size = cff2_plan.get_final_size ();
+  char *cff2_prime_data = (char *) calloc (1, cff2_prime_size);
+
+  if (unlikely (!_write_cff2 (cff2_plan, acc, plan->glyphs,
+                              cff2_prime_size, cff2_prime_data))) {
+    DEBUG_MSG(SUBSET, nullptr, "Failed to write a subset cff2.");
+    free (cff2_prime_data);
+    return false;
+  }
+
+  *prime = hb_blob_create (cff2_prime_data,
+                                cff2_prime_size,
+                                HB_MEMORY_MODE_READONLY,
+                                cff2_prime_data,
+                                free);
+  return true;
+}
+
+/**
+ * hb_subset_cff2:
+ * Subsets the CFF2 table according to a provided plan.
+ *
+ * Return value: subsetted cff2 table.
+ **/
+bool
+hb_subset_cff2 (hb_subset_plan_t *plan,
+                hb_blob_t       **prime /* OUT */)
+{
+  hb_blob_t *cff2_blob = hb_sanitize_context_t().reference_table<CFF::cff2> (plan->source);
+  const char *data = hb_blob_get_data(cff2_blob, nullptr);
+
+  OT::cff2::accelerator_subset_t acc;
+  acc.init(plan->source);
+  bool result = likely (acc.is_valid ()) &&
+                _hb_subset_cff2 (acc, data, plan, prime);
+
+  hb_blob_destroy (cff2_blob);
+  acc.fini ();
+
+  return result;
+}
diff --git a/src/hb-subset-cff2.hh b/src/hb-subset-cff2.hh
new file mode 100644
index 0000000..0701240
--- /dev/null
+++ b/src/hb-subset-cff2.hh
@@ -0,0 +1,38 @@
+/*
+ * 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_SUBSET_CFF2_HH
+#define HB_SUBSET_CFF2_HH
+
+#include "hb.hh"
+
+#include "hb-subset-plan.hh"
+
+HB_INTERNAL bool
+hb_subset_cff2 (hb_subset_plan_t *plan,
+               hb_blob_t       **cff2_prime /* OUT */);
+
+#endif /* HB_SUBSET_CFF2_HH */
diff --git a/src/hb-subset.cc b/src/hb-subset.cc
index 2bed358..81f885a 100644
--- a/src/hb-subset.cc
+++ b/src/hb-subset.cc
@@ -40,6 +40,8 @@
 #include "hb-ot-maxp-table.hh"
 #include "hb-ot-os2-table.hh"
 #include "hb-ot-post-table.hh"
+#include "hb-ot-cff1-table.hh"
+#include "hb-ot-cff2-table.hh"
 #include "hb-ot-layout-gsub-table.hh"
 #include "hb-ot-layout-gpos-table.hh"
 
@@ -177,6 +179,12 @@
     case HB_OT_TAG_post:
       result = _subset<const OT::post> (plan);
       break;
+    case HB_OT_TAG_cff1:
+      result = _subset<const OT::cff1> (plan);
+      break;
+    case HB_OT_TAG_cff2:
+      result = _subset<const OT::cff2> (plan);
+      break;
 
     case HB_OT_TAG_GSUB:
       result = _subset2<const OT::GSUB> (plan);