CFF Subrs subsetting
authorMichiharu Ariza <ariza@adobe.com>
Wed, 15 Aug 2018 19:00:19 +0000 (12:00 -0700)
committerMichiharu Ariza <ariza@adobe.com>
Wed, 15 Aug 2018 19:00:19 +0000 (12:00 -0700)
Unreferenced subroutines are nullified (not removed) in a subset font

Separate Interpreter struct from hb-ot-cff-common-private.hh in a set of four header files, augmented for CharString (CS):
hb-cff-interp-common-private.hh
hb-cff-interp-dict-common-private.hh
hb-cff-interp-cs-common-private.hh
hb-cff-interp-cs.hh

Interpreter runtime is separated off as a new struct InterpEnv sub-classed differently for Dict and CharString (CS)

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

index 7e45f2c..c50f251 100644 (file)
@@ -26,8 +26,8 @@ HB_BASE_sources = \
        hb-ot-color-cbdt-table.hh \
        hb-ot-cmap-table.hh \
        hb-ot-glyf-table.hh \
-  hb-ot-cff-table.hh \
-  hb-ot-cff2-table.hh \
+       hb-ot-cff-table.hh \
+       hb-ot-cff2-table.hh \
        hb-ot-hdmx-table.hh \
        hb-ot-head-table.hh \
        hb-ot-hhea-table.hh \
@@ -147,6 +147,10 @@ HB_OT_sources = \
        hb-ot-shape-fallback.cc \
        hb-ot-shape-private.hh \
        hb-ot-cff-common-private.hh \
+       hb-ot-cff-interp-common-private.hh \
+       hb-ot-cff-interp-cs-common-private.hh \
+       hb-ot-cff-interp-cs.hh \
+       hb-ot-cff-interp-dict-common-private.hh \
        hb-ot-var.cc \
        hb-ot-var-avar-table.hh \
        hb-ot-var-fvar-table.hh \
diff --git a/src/hb-cff-interp-common-private.hh b/src/hb-cff-interp-common-private.hh
new file mode 100644 (file)
index 0000000..0f81b48
--- /dev/null
@@ -0,0 +1,581 @@
+/*
+ * 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_PRIVATE_HH
+#define HB_CFF_INTERP_COMMON_PRIVATE_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_vsindex,                /* 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_vsindex,                /* 15 CFF2 */
+    OpCode_blendcs = 16,           /* 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_longintcs = 255,        /* 32-bit integer */
+
+    /* 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 unsigned int OpCode_Size (OpCode op) { return (op >= OpCode_ESC_Base)? 2: 1; }
+
+struct Number
+{
+  inline Number (void) { set_int (0); }
+
+  inline void set_int (int v)       { is_real = false; u.int_val = v; };
+  inline int to_int (void) const    { return is_real? (int)u.real_val: u.int_val; }
+  inline void set_real (float v)    { is_real = true; u.real_val = v; };
+  inline float to_real (void) const { return is_real? u.real_val: (float)u.int_val; }
+
+protected:
+  bool is_real;
+  union {
+    int     int_val;
+    float   real_val;
+  } u;
+};
+
+/* 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)
+    : str (), offset (0) {}
+
+  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 [] (unsigned 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) { size = 0; }
+  inline void fini (void) { }
+
+  inline void push (const ELEM &v)
+  {
+    if (likely (size < kSizeLimit))
+      elements[size++] = v;
+  }
+
+  inline const ELEM& pop (void)
+  {
+    if (likely (size > 0))
+      return elements[--size];
+    else
+      return Null(ELEM);
+  }
+
+  inline void unpop (void)
+  {
+    if (likely (size < kSizeLimit))
+      size++;
+  }
+
+  inline void clear (void) { size = 0; }
+
+  inline bool check_overflow (unsigned int count) const { return (count <= kSizeLimit) && (count + size <= kSizeLimit); }
+  inline bool check_underflow (unsigned int count) const { return (count <= size); }
+
+  inline unsigned int get_size (void) const { return size; }
+  inline bool is_empty (void) const { return size == 0; }
+
+  static const unsigned int kSizeLimit = LIMIT;
+
+  unsigned int size;
+  ELEM elements[kSizeLimit];
+};
+
+/* argument stack */
+struct ArgStack : Stack<Number, 513>
+{
+  inline void push_int (int v)
+  {
+    Number n;
+    n.set_int (v);
+    push (n);
+  }
+
+  inline void push_real (float v)
+  {
+    Number n;
+    n.set_real (v);
+    push (n);
+  }
+
+  inline bool check_pop_num (Number& n)
+  {
+    if (unlikely (!this->check_underflow (1)))
+      return false;
+    n = this->pop ();
+    return true;
+  }
+
+  inline bool check_pop_num2 (Number& n1, Number& 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 (1)))
+      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<Number>& vec, bool even=false)
+  {
+    if (even && unlikely ((this->size & 1) != 0))
+      return false;
+
+    float val = 0.0f;
+    for (unsigned int i = 0; i < size; i++) {
+      val += elements[i].to_real ();
+      Number *n = vec.push ();
+      n->set_real (val);
+    }
+    return true;
+  }
+
+  inline bool push_longint_from_substr (SubByteStr& substr)
+  {
+    if (unlikely (!substr.avail (4) || !check_overflow (1)))
+      return false;
+    push_int ((int32_t)*(const HBUINT32*)&substr[0]);
+    substr.inc (4);
+    return true;
+  }
+
+  inline void reverse_range (int i, int j)
+  {
+    assert (i >= 0 && i < j);
+    Number  tmp;
+    while (i < j)
+    {
+      tmp = elements[i];
+      elements[i++] = elements[j];
+      elements[j++] = tmp;
+    }
+  }
+};
+
+/* an operator prefixed by its operands in a byte string */
+struct OpStr
+{
+  inline void init (void) {}
+
+  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);
+  }
+};
+
+struct InterpEnv
+{
+  inline void init (const ByteStr &str_)
+  {
+    substr.reset (str_);
+    argStack.init ();
+  }
+
+  inline void fini (void)
+  {
+    argStack.fini ();
+  }
+
+  SubByteStr    substr;
+  ArgStack      argStack;
+};
+
+struct OpSet
+{
+  static inline bool process_op (OpCode op, InterpEnv& 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.argStack.clear ();
+          return false;
+        }
+        break;
+    }
+
+    return true;
+  }
+};
+
+template <typename ENV>
+struct Interpreter {
+
+  inline ~Interpreter(void) { fini (); }
+
+  inline void fini (void) { env.fini (); }
+
+  inline bool fetch_op (OpCode &op)
+  {
+    if (unlikely (!env.substr.avail ()))
+      return false;
+    op = (OpCode)(unsigned char)env.substr[0];
+    if (op == OpCode_escape) {
+      if (unlikely (!env.substr.avail ()))
+        return false;
+      op = Make_OpCode_ESC (env.substr[1]);
+      env.substr.inc ();
+    }
+    env.substr.inc ();
+    return true;
+  }
+
+  ENV env;
+};
+
+} /* namespace CFF */
+
+#endif /* HB_CFF_INTERP_COMMON_PRIVATE_HH */
diff --git a/src/hb-cff-interp-cs-common-private.hh b/src/hb-cff-interp-cs-common-private.hh
new file mode 100644 (file)
index 0000000..8da9005
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * 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_CS_INTERP_COMMON_PRIVATE_HH
+#define HB_CFF_CS_INTERP_COMMON_PRIVATE_HH
+
+#include "hb-cff-interp-common-private.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 SUBRS>
+struct CSInterpEnv : InterpEnv
+{
+  inline void init (const ByteStr &str, const SUBRS &globalSubrs_, const SUBRS &localSubrs_)
+  {
+    InterpEnv::init (str);
+
+    callStack.init ();
+    globalSubrs.init (globalSubrs_);
+    localSubrs.init (localSubrs_);
+  }
+  inline void fini (void)
+  {
+    InterpEnv::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) ||
+                   !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 (substr);
+    substr = (*biasedSubrs.subrs)[subr_num];
+
+    return true;
+  }
+
+  inline bool returnFromSubr (void)
+  {
+    if (unlikely (!callStack.check_underflow (1)))
+      return false;
+
+    substr = callStack.pop ();
+    return true;
+  }
+
+  inline void set_endchar (bool endchar_flag_) { endchar_flag = endchar_flag_; }
+  inline bool is_endchar (void) const { return endchar_flag; }
+
+  protected:
+  bool              endchar_flag;
+
+  public:
+  CallStack            callStack;
+  BiasedSubrs<SUBRS>   globalSubrs;
+  BiasedSubrs<SUBRS>   localSubrs;
+};
+
+template <typename SUBRS, typename PARAM>
+struct CSOpSet : OpSet
+{
+  static inline bool process_op (OpCode op, CSInterpEnv<SUBRS> &env, PARAM& param)
+  {
+    switch (op) {
+
+      case OpCode_longintcs:
+        return env.argStack.push_longint_from_substr (env.substr);
+
+      case OpCode_callsubr:
+        return env.callSubr (env.localSubrs);
+      
+      case OpCode_callgsubr:
+        return env.callSubr (env.globalSubrs);
+
+      default:
+        return OpSet::process_op (op, env);
+    }
+  }
+};
+
+template <typename ENV, typename OPSET, typename PARAM>
+struct CSInterpreter : Interpreter<ENV>
+{
+  inline bool interpret (PARAM& param)
+  {
+    param.init ();
+    Interpreter<ENV> &super = *this;
+    super.env.set_endchar (false);
+
+    for (;;) {
+      OpCode op;
+      if (unlikely (!super.fetch_op (op) ||
+                    !OPSET::process_op (op, super.env, param)))
+        return false;
+      if (super.env.is_endchar ())
+        break;
+      if (!super.env.substr.avail ())
+        return false;
+    }
+    
+    return true;
+  }
+};
+
+} /* namespace CFF */
+
+#endif /* HB_CFF_CS_INTERP_COMMON_PRIVATE_HH */
diff --git a/src/hb-cff-interp-cs.hh b/src/hb-cff-interp-cs.hh
new file mode 100644 (file)
index 0000000..60ef5f7
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ * 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_CS_INTERP_HH
+#define HB_CFF_CS_INTERP_HH
+
+#include "hb-cff-interp-cs.hh"
+
+namespace CFF {
+
+using namespace OT;
+
+struct CFFCSInterpEnv : CSInterpEnv<CFFSubrs>
+{
+  inline void init (const ByteStr &str, const CFFSubrs &globalSubrs, const CFFSubrs &localSubrs)
+  {
+    CSInterpEnv<CFFSubrs>::init (str, globalSubrs, localSubrs);
+    seen_width = false;
+    seen_moveto = true;
+    seen_hintmask = false;
+    hstem_count = 0;
+    vstem_count = 0;
+    for (unsigned int i = 0; i < kTransientArraySize; i++)
+      transient_array[i].set_int (0);
+  }
+
+  bool check_transient_array_index (unsigned int i) const
+  { return i < kTransientArraySize; }
+
+  inline void determine_hintmask_size (void)
+  {
+    if (!seen_hintmask)
+    {
+      vstem_count += argStack.size / 2;
+      hintmask_size = (hstem_count + vstem_count + 7) >> 3;
+      seen_hintmask = true;
+    }
+    clear_stack ();
+  }
+
+  inline void process_moveto (void)
+  {
+    clear_stack ();
+
+    if (!seen_moveto)
+    {
+      determine_hintmask_size ();
+      seen_moveto = true;
+    }
+  }
+
+  inline void clear_stack (void)
+  {
+    seen_width = true;
+    argStack.clear ();
+  }
+
+  inline void process_width (void)
+  {
+    if (!seen_width && (argStack.size > 0))
+    {
+      assert (argStack.size == 1);
+      width = argStack.pop ();
+      seen_width = true;
+    }
+  }
+
+  bool          seen_width;
+  Number        width;
+  bool          seen_moveto;
+  bool          seen_hintmask;
+  unsigned int  hintmask_size;
+  unsigned int  hstem_count;
+  unsigned int  vstem_count;
+
+  static const unsigned int kTransientArraySize = 32;
+  Number  transient_array[kTransientArraySize];
+};
+
+template <typename PARAM>
+struct CFFCSOpSet : CSOpSet<CFFSubrs, PARAM>
+{
+  static inline bool process_op (OpCode op, CFFCSInterpEnv &env, PARAM& param)
+  {
+    Number  n1, n2;
+
+    switch (op) {
+
+      case OpCode_return:
+        return env.returnFromSubr ();
+      case OpCode_endchar:
+        env.set_endchar (true);
+        return true;
+      case OpCode_and:
+        if (unlikely (!env.argStack.check_pop_num2 (n1, n2))) return false;
+        env.argStack.push_int ((n1.to_real() != 0.0f) && (n1.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) || (n1.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() + n1.to_real());
+        break;
+      case OpCode_sub:
+        if (unlikely (!env.argStack.check_pop_num2 (n1, n2))) return false;
+        env.argStack.push_real (n1.to_real() - n1.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() == n1.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_real (((float)rand() + 1) / ((float)RAND_MAX + 1));
+      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.size || !env.argStack.check_overflow (1))) return false;
+          env.argStack.push (env.argStack.elements[env.argStack.size - 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.size)) return false;
+          if (likely (n > 0))
+          {
+            if (j < 0)
+              j = n - (-j % n);
+            j %= n;
+            unsigned int top = env.argStack.size - 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;
+      case OpCode_hstem:
+      case OpCode_vstem:
+        env.clear_stack ();
+        break;
+      case OpCode_hstemhm:
+        env.hstem_count += env.argStack.size / 2;
+        env.clear_stack ();
+        break;
+      case OpCode_vstemhm:
+        env.vstem_count += env.argStack.size / 2;
+        env.clear_stack ();
+        break;
+      case OpCode_hintmask:
+      case OpCode_cntrmask:
+        env.determine_hintmask_size ();
+        if (unlikely (!env.substr.avail (env.hintmask_size)))
+          return false;
+        env.substr.inc (env.hintmask_size);
+        break;
+      
+      case OpCode_vmoveto:
+      case OpCode_rlineto:
+      case OpCode_hlineto:
+      case OpCode_vlineto:
+      case OpCode_rmoveto:
+      case OpCode_hmoveto:
+        env.process_moveto ();
+        break;
+      case OpCode_rrcurveto:
+      case OpCode_rcurveline:
+      case OpCode_rlinecurve:
+      case OpCode_vvcurveto:
+      case OpCode_hhcurveto:
+      case OpCode_vhcurveto:
+      case OpCode_hvcurveto:
+      case OpCode_hflex:
+      case OpCode_flex:
+      case OpCode_hflex1:
+      case OpCode_flex1:
+        env.clear_stack ();
+        break;
+      default:
+        typedef CSOpSet<CFFSubrs, PARAM>  SUPER;
+        if (unlikely (!SUPER::process_op (op, env, param)))
+          return false;
+        env.process_width ();
+        break;
+    }
+    return true;
+  }
+};
+
+template <typename OPSET, typename PARAM>
+struct CFFCSInterpreter : CSInterpreter<CFFCSInterpEnv, OPSET, PARAM> {};
+
+} /* namespace CFF */
+
+#endif /* HB_CFF_CS_INTERP_HH */
diff --git a/src/hb-cff-interp-dict-common-private.hh b/src/hb-cff-interp-dict-common-private.hh
new file mode 100644 (file)
index 0000000..da7b590
--- /dev/null
@@ -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_CFF_DICT_INTERP_COMMON_PRIVATE_HH
+#define HB_CFF_DICT_INTERP_COMMON_PRIVATE_HH
+
+#include "hb-cff-interp-common-private.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;
+};
+
+template <typename VAL>
+struct DictValues
+{
+  inline void init (void)
+  {
+    opStart = 0;
+    values.init ();
+  }
+
+  inline void fini (void)
+  {
+    values.fini ();
+  }
+
+  inline void pushVal (OpCode op, const SubByteStr& substr)
+  {
+    VAL *val = values.push ();
+    val->op = op;
+    val->str = ByteStr (substr.str, opStart, substr.offset - opStart);
+    opStart = substr.offset;
+  }
+
+  inline void pushVal (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;
+  }
+
+  unsigned int       opStart;
+  hb_vector_t<VAL>   values;
+};
+
+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
+{
+  static inline bool process_op (OpCode op, InterpEnv& 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::process_op (op, env);
+    }
+
+    return true;
+  }
+};
+
+struct TopDictOpSet : DictOpSet
+{
+  static inline bool process_op (OpCode op, InterpEnv& env, TopDictValues& dictval)
+  {
+    switch (op) {
+      case OpCode_CharStrings:
+        if (unlikely (!env.argStack.check_pop_uint (dictval.charStringsOffset)))
+          return false;
+        env.argStack.clear ();
+        break;
+      case OpCode_FDArray:
+        if (unlikely (!env.argStack.check_pop_uint (dictval.FDArrayOffset)))
+          return false;
+        env.argStack.clear ();
+        break;
+      default:
+        return DictOpSet::process_op (op, env);
+    }
+
+    return true;
+  }
+};
+
+template <typename OPSET, typename PARAM>
+struct DictInterpreter : Interpreter<InterpEnv>
+{
+  inline bool interpret (PARAM& param)
+  {
+    param.init ();
+    Interpreter<InterpEnv>  &super = *this;
+    do
+    {
+      OpCode op;
+      if (unlikely (!super.fetch_op (op) || !OPSET::process_op (op, super.env, param)))
+        return false;
+    } while (super.env.substr.avail ());
+    
+    return true;
+  }
+};
+
+} /* namespace CFF */
+
+#endif /* HB_CFF_DICT_INTERP_COMMON_PRIVATE_HH */
index d5d1ec4..a526295 100644 (file)
@@ -28,6 +28,7 @@
 
 #include "hb-open-type-private.hh"
 #include "hb-ot-layout-common-private.hh"
+#include "hb-cff-interp-dict-common-private.hh"
 #include "hb-subset-plan.hh"
 
 namespace CFF {
@@ -39,194 +40,6 @@ 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); }
 
-const float UNSET_REAL_VALUE = -1.0f;
-
-enum OpCode {
-    /* 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_vsindex,                /* 22  CFF2 Private/CS */
-    OpCode_blend,                  /* 23  CFF2 Private/CS  */
-    OpCode_vstore,                 /* 24  CFF2 Top */
-    OpCode_reserved25,             /* 25 */
-    OpCode_reserved26,             /* 26 */
-    OpCode_reserved27,             /* 27 */
-
-    /* Numbers */
-    OpCode_shortint,               /* 28  All */
-    OpCode_longint,                /* 29  All */
-    OpCode_BCD,                    /* 30  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 = 32,
-    OpCode_Copyright = OpCode_ESC_Base, /* OpCode_ESC(0) CFF Top */
-    OpCode_isFixedPitch,           /* OpCode_ESC(1)  CFF Top (false) */
-    OpCode_ItalicAngle,            /* OpCode_ESC(2)  CFF Top (0) */
-    OpCode_UnderlinePosition,      /* OpCode_ESC(3)  CFF Top (-100) */
-    OpCode_UnderlineThickness,     /* OpCode_ESC(4)  CFF Top (50) */
-    OpCode_PaintType,              /* OpCode_ESC(5)  CFF Top (0) */
-    OpCode_CharstringType,         /* OpCode_ESC(6)  CFF Top (2) */
-    OpCode_FontMatrix,             /* OpCode_ESC(7)  CFF Top, CFF2 Top (.001 0 0 .001 0 0)*/
-    OpCode_StrokeWidth,            /* OpCode_ESC(8)  CFF Top (0) */
-    OpCode_BlueScale,              /* OpCode_ESC(9)  CFF Private, CFF2 Private (0.039625) */
-    OpCode_BlueShift,              /* OpCode_ESC(10) CFF Private, CFF2 Private (7) */
-    OpCode_BlueFuzz,               /* OpCode_ESC(11) CFF Private, CFF2 Private (1) */
-    OpCode_StemSnapH,              /* OpCode_ESC(12) CFF Private, CFF2 Private */
-    OpCode_StemSnapV,              /* OpCode_ESC(13) CFF Private, CFF2 Private */
-    OpCode_ForceBold,              /* OpCode_ESC(14) CFF Private (false) */
-    OpCode_reservedESC15,          /* OpCode_ESC(15) */
-    OpCode_reservedESC16,          /* OpCode_ESC(16) */
-    OpCode_LanguageGroup,          /* OpCode_ESC(17) CFF Private, CFF2 Private (0) */
-    OpCode_ExpansionFactor,        /* OpCode_ESC(18) CFF Private, CFF2 Private (0.06) */
-    OpCode_initialRandomSeed,      /* OpCode_ESC(19) CFF Private (0) */
-    OpCode_SyntheticBase,          /* OpCode_ESC(20) CFF Top */
-    OpCode_PostScript,             /* OpCode_ESC(21) CFF Top */
-    OpCode_BaseFontName,           /* OpCode_ESC(22) CFF Top */
-    OpCode_BaseFontBlend,          /* OpCode_ESC(23) CFF Top */
-    OpCode_reservedESC24,          /* OpCode_ESC(24) */
-    OpCode_reservedESC25,          /* OpCode_ESC(25) */
-    OpCode_reservedESC26,          /* OpCode_ESC(26) */
-    OpCode_reservedESC27,          /* OpCode_ESC(27) */
-    OpCode_reservedESC28,          /* OpCode_ESC(28) */
-    OpCode_reservedESC29,          /* OpCode_ESC(29) */
-    OpCode_ROS,                    /* OpCode_ESC(30) CFF Top_CID */
-    OpCode_CIDFontVersion,         /* OpCode_ESC(31) CFF Top_CID (0) */
-    OpCode_CIDFontRevision,        /* OpCode_ESC(32) CFF Top_CID (0) */
-    OpCode_CIDFontType,            /* OpCode_ESC(33) CFF Top_CID (0) */
-    OpCode_CIDCount,               /* OpCode_ESC(34) CFF Top_CID (8720) */
-    OpCode_UIDBase,                /* OpCode_ESC(35) CFF Top_CID */
-    OpCode_FDArray,                /* OpCode_ESC(36) CFF Top_CID, CFF2 Top */
-    OpCode_FDSelect,               /* OpCode_ESC(37) CFF Top_CID, CFF2 Top */
-    OpCode_FontName,               /* OpCode_ESC(38) CFF Top_CID */
-
-    OpCode_reserved255 = 255
-};
-
-inline OpCode Make_OpCode_ESC (unsigned char byte2)  { return (OpCode)(OpCode_ESC_Base + byte2); }
-inline unsigned int OpCode_Size (OpCode op) { return (op >= OpCode_ESC_Base)? 2: 1; }
-
-struct Number
-{
-  inline Number (void) { set_int (0); }
-
-  inline void set_int (int v)       { is_real = false; u.int_val = v; };
-  inline int to_int (void) const    { return is_real? (int)u.real_val: u.int_val; }
-  inline void set_real (float v)    { is_real = true; u.real_val = v; };
-  inline float to_real (void) const { return is_real? u.real_val: (float)u.int_val; }
-
-protected:
-  bool is_real;
-  union {
-    int     int_val;
-    float   real_val;
-  } u;
-};
-
-/* byte string */
-struct UnsizedByteStr : UnsizedArrayOf <HBUINT8>
-{
-  // encode 2-byte int (Dict/CharString) or 4-byte int (Dict)
-  template <typename INTTYPE, int minVal, int maxVal>
-  inline static bool serialize_int (hb_serialize_context_t *c, OpCode intOp, int value)
-  {
-    TRACE_SERIALIZE (this);
-
-    if (unlikely ((value < minVal || value > maxVal)))
-      return_trace (false);
-
-    HBUINT8 *p = c->allocate_size<HBUINT8> (1);
-    if (unlikely (p == nullptr)) return_trace (false);
-    p->set (intOp);
-
-    INTTYPE *ip = c->allocate_size<INTTYPE> (INTTYPE::static_size);
-    if (unlikely (ip == nullptr)) return_trace (false);
-    ip->set ((unsigned int)value);
-
-    return_trace (true);
-  }
-  
-  inline static bool serialize_int4 (hb_serialize_context_t *c, int value)
-  { return serialize_int<HBUINT32, 0, 0x7FFFFFFF> (c, OpCode_longint, value); }
-  
-  inline static bool serialize_int2 (hb_serialize_context_t *c, int value)
-  { return serialize_int<HBUINT16, 0, 0x7FFF> (c, OpCode_shortint, value); }
-};
-
-struct ByteStr
-{
-  ByteStr (const UnsizedByteStr& s, unsigned int l)
-    : str (&s), len (l) {}
-  ByteStr (const char *s=nullptr, unsigned int l=0)
-    : str ((const UnsizedByteStr *)s), len (l) {}
-  /* sub-string */
-  ByteStr (const ByteStr &bs, unsigned int offset, unsigned int l)
-  {  
-    str = (const UnsizedByteStr *)&bs.str[offset];
-    len = l;
-  }
-
-  inline bool sanitize (hb_sanitize_context_t *c) const { return str->sanitize (c, len); }
-
-  inline const HBUINT8& operator [] (unsigned int i) const {
-    assert (str && (i < len));
-    return (*str)[i];
-  }
-
-  inline bool 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;
-};
-
 inline unsigned int calcOffSize(unsigned int offset)
 {
   unsigned int size = 1;
@@ -445,190 +258,6 @@ struct IndexOf : Index<COUNT>
   }
 };
 
-inline float parse_bcd (const ByteStr& str, unsigned int& offset, float& v)
-{
-  // XXX: TODO
-  v = 0;
-  for (;;) {
-    if (++offset >= str.len)
-      return false;
-    unsigned char byte = str[offset];
-    if (((byte & 0xF0) == 0xF0) || ((byte & 0x0F) == 0x0F))
-      break;
-  }
-  return true;
-}
-
-/* operand stack */
-struct Stack
-{
-  inline void init (void) { size = 0; }
-  inline void fini (void) { }
-
-  inline void push (const Number &v)
-  {
-    if (likely (size < kSizeLimit))
-      numbers[size++] = v;
-  }
-
-  inline void push_int (int v)
-  {
-    Number n;
-    n.set_int (v);
-    push (n);
-  }
-
-  inline void push_real (float v)
-  {
-    Number n;
-    n.set_real (v);
-    push (n);
-  }
-
-  inline const Number& pop (void)
-  {
-    if (likely (size > 0))
-      return numbers[--size];
-    else
-      return Null(Number);
-  }
-
-  inline bool check_push (void)
-  {
-    if (likely (size < kSizeLimit)) {
-      size++;
-      return true;
-    } else
-      return false;
-  }
-
-  inline bool check_pop (void)
-  {
-    if (likely (0 < size)) {
-      size--;
-      return true;
-    } else
-      return false;
-  }
-
-  inline bool check_pop_num (Number& n)
-  {
-    if (unlikely (!this->check_underflow (1)))
-      return false;
-    n = this->pop ();
-    return true;
-  }
-
-  inline bool check_pop_uint (unsigned int& v)
-  {
-    uint32_t  i;
-    if (unlikely (!this->check_underflow (1)))
-      return false;
-    i = this->pop ().to_int ();
-    if (unlikely (i < 0))
-      return false;
-    v = (uint32_t)i;
-    return true;
-  }
-
-  inline bool check_pop_delta (hb_vector_t<Number>& vec, bool even=false)
-  {
-    if (even && unlikely ((this->size & 1) != 0))
-      return false;
-
-    float val = 0.0f;
-    for (unsigned int i = 0; i < size; i++) {
-      val += numbers[i].to_real ();
-      Number *n = vec.push ();
-      n->set_real (val);
-    }
-    return true;
-  }
-
-  inline bool push_longint_from_str (const ByteStr& str, unsigned int& offset)
-  {
-    if (unlikely (!str.check_limit (offset, 5) || !check_overflow (1)))
-      return false;
-    push_int ((int32_t)*(const HBUINT32*)&str[offset + 1]);
-    offset += 4;
-    return true;
-  }
-
-  inline void clear (void) { size = 0; }
-
-  inline bool check_overflow (unsigned int count) const { return (count <= kSizeLimit) && (count + size <= kSizeLimit); }
-  inline bool check_underflow (unsigned int count) const { return (count <= size); }
-
-  inline unsigned int get_size (void) const { return size; }
-  inline bool is_empty (void) const { return size == 0; }
-
-  static const unsigned int kSizeLimit = 513;
-
-  unsigned int size;
-  Number numbers[kSizeLimit];
-};
-
-/* an operator prefixed by its operands in a byte string */
-struct OpStr
-{
-  inline void init (void) {}
-
-  OpCode  op;
-  ByteStr str;
-};
-
-/* 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;
-};
-
-template <typename VAL>
-struct DictValues
-{
-  inline void init (void)
-  {
-    opStart = 0;
-    values.init ();
-  }
-
-  inline void fini (void)
-  {
-    values.fini ();
-  }
-
-  inline void pushVal (OpCode op, const ByteStr& str, unsigned int offset)
-  {
-    VAL *val = values.push ();
-    val->op = op;
-    val->str = ByteStr (str, opStart, offset - opStart);
-    opStart = offset;
-  }
-
-  inline void pushVal (OpCode op, const ByteStr& str, unsigned int offset, const VAL &v)
-  {
-    VAL *val = values.push (v);
-    val->op = op;
-    val->str = ByteStr (str, opStart, offset - opStart);
-    opStart = offset;
-  }
-
-  unsigned int       opStart;
-  hb_vector_t<VAL>   values;
-};
-
 /* Top Dict, Font Dict, Private Dict */
 struct Dict : UnsizedByteStr
 {
@@ -682,7 +311,7 @@ struct Dict : UnsizedByteStr
   }
 
   inline static bool serialize_offset4_op (hb_serialize_context_t *c, OpCode op, int value)
-  { return serialize_offset_op<HBUINT32, 0, 0x7FFFFFFF> (c, op, value, OpCode_longint); }
+  { return serialize_offset_op<HBUINT32, 0, 0x7FFFFFFF> (c, op, value, OpCode_longintdict); }
 
   inline static bool serialize_offset2_op (hb_serialize_context_t *c, OpCode op, int value)
   { return serialize_offset_op<HBUINT16, 0, 0x7FFF> (c, op, value, OpCode_shortint); }
@@ -694,10 +323,11 @@ struct PrivateDict : Dict {};
 
 struct TableInfo
 {
-  void init (void) { offset = size = 0; }
+  void init (void) { offSize = offset = size = 0; }
 
   unsigned int    offset;
   unsigned int    size;
+  unsigned int    offSize;
 };
 
 /* font dict index remap table from fullset FDArray to subset FDArray.
@@ -904,6 +534,8 @@ struct FDSelect {
 
   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
@@ -919,169 +551,51 @@ struct FDSelect {
   DEFINE_SIZE_MIN (1);
 };
 
-struct TopDictValues : DictValues<OpStr>
+template <typename COUNT>
+struct Subrs : Index<COUNT>
 {
-  inline void init (void)
-  {
-    DictValues<OpStr>::init ();
-    charStringsOffset = 0;
-    FDArrayOffset = 0;
-  }
-
-  inline void fini (void)
+  inline bool serialize (hb_serialize_context_t *c, const Subrs<COUNT> &subrs, unsigned int offSize, const hb_set_t *set, const ByteStr& nullStr = ByteStr())
   {
-    DictValues<OpStr>::fini ();
-  }
-
-  inline unsigned int calculate_serialized_op_size (const OpStr& opstr) const
-  {
-    switch (opstr.op)
+    TRACE_SERIALIZE (this);
+    if ((subrs.count == 0) || (hb_set_get_population (set) == 0))
     {
-      case OpCode_CharStrings:
-      case OpCode_FDArray:
-        return OpCode_Size (OpCode_longint) + 4 + OpCode_Size (opstr.op);
-
-      default:
-        return opstr.str.len;
+      if (!unlikely (c->allocate_size<COUNT> (COUNT::static_size)))
+        return_trace (false);
+      Index<COUNT>::count.set (0);
+      return_trace (true);
     }
-  }
-
-  unsigned int  charStringsOffset;
-  unsigned int  FDArrayOffset;
-};
-
-struct TopDictOpSet
-{
-  static inline bool process_op (const ByteStr& str, unsigned int& offset, OpCode op, Stack& stack, TopDictValues& dictval)
-  {
-    switch (op) {
-      case OpCode_CharStrings:
-        if (unlikely (!stack.check_pop_uint (dictval.charStringsOffset)))
-          return false;
-        stack.clear ();
-        break;
-      case OpCode_FDArray:
-        if (unlikely (!stack.check_pop_uint (dictval.FDArrayOffset)))
-          return false;
-        stack.clear ();
-        break;
-      case OpCode_longint:  /* 5-byte integer */
-        return stack.push_longint_from_str (str, offset);
-      
-      case OpCode_BCD:  /* real number */
-        float v;
-        if (unlikely (stack.check_overflow (1) || !parse_bcd (str, offset, v)))
-          return false;
-        stack.push_real (v);
-        return true;
     
-      default:
-        /* XXX: invalid */
-        stack.clear ();
-        return false;
-    }
-
-    return true;
-  }
-};
-
-/* 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 OpSet, typename Param>
-struct Interpreter {
+    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;
 
-  inline Interpreter (void)
-  {
-    stack.init ();
+    bool result = Index<COUNT>::serialize (c, offSize, bytesArray);
+    bytesArray.fini ();
+    return_trace (result);
   }
   
-  inline ~Interpreter (void)
-  {
-    stack.fini ();
-  }
-
-  inline bool interpret (const ByteStr& str, Param& param)
+  /* in parallel to above */
+  inline unsigned int calculate_serialized_size (unsigned int &offSize /*OUT*/, const hb_set_t *set, unsigned int nullStrSize = 0) const
   {
-    param.init ();
+    unsigned int  count_ = Index<COUNT>::count;
+    offSize = 0;
+    if ((count_ == 0) || (hb_set_get_population (set) == 0))
+      return COUNT::static_size;
 
-    for (unsigned int i = 0; i < str.len; i++)
+    unsigned int dataSize = 0;
+    for (hb_codepoint_t i = 0; i < count_; i++)
     {
-      OpCode op = (OpCode)(unsigned char)str[i];
-      if ((OpCode_shortint == op) ||
-          (OpCode_OneByteIntFirst <= op && OpCode_TwoByteNegInt3 >= op))
-      {
-        if (unlikely (!process_intop (str, i, op)))
-          return false;
-      } else {
-        if (op == OpCode_escape) {
-          if (unlikely (!str.check_limit (i, 1)))
-            return false;
-          op = Make_OpCode_ESC(str[++i]);
-        }
-
-        if (unlikely (!OpSet::process_op (str, i, op, stack, param)))
-          return false;
-      }
-    }
-    
-    return true;
-  }
-
-  inline bool process_intop (const ByteStr& str, unsigned int& offset, OpCode op)
-  {
-    switch (op) {
-      case OpCode_TwoBytePosInt0: case OpCode_TwoBytePosInt1:
-      case OpCode_TwoBytePosInt2: case OpCode_TwoBytePosInt3:
-        if (unlikely (!str.check_limit (offset, 2) || !stack.check_overflow (1)))
-          return false;
-        stack.push_int ((int16_t)((op - OpCode_TwoBytePosInt0) * 256 + str[offset + 1] + 108));
-        offset++;
-        break;
-      
-      case OpCode_TwoByteNegInt0: case OpCode_TwoByteNegInt1:
-      case OpCode_TwoByteNegInt2: case OpCode_TwoByteNegInt3:
-        if (unlikely (!str.check_limit (offset, 2) || !stack.check_overflow (1)))
-          return false;
-        stack.push_int ((int16_t)(-(op - OpCode_TwoByteNegInt0) * 256 - str[offset + 1] - 108));
-        offset++;
-        break;
-      
-      case OpCode_shortint: /* 3-byte integer */
-        if (unlikely (!str.check_limit (offset, 3) || !stack.check_overflow (1)))
-          return false;
-        stack.push_int ((int16_t)*(const HBUINT16*)&str[offset + 1]);
-        offset += 2;
-        break;
-      
-      default:
-        /* 1-byte integer */
-        if (likely ((OpCode_OneByteIntFirst <= op) && (op <= OpCode_OneByteIntLast)) &&
-            likely (stack.check_overflow (1)))
-        {
-          stack.push_int ((int)op - 139);
-        } else {
-          return false;
-        }
-        break;
+      if (hb_set_has (set, i))
+        dataSize += (*this)[i].len;
+      else
+        dataSize += nullStrSize;
     }
-    return true;
+    offSize = calcOffSize(dataSize);
+    return Index<COUNT>::calculate_serialized_size (offSize, count_, dataSize);
   }
-
-  protected:
-  Stack stack;
 };
 
 } /* namespace CFF */
index fb5f911..7b17ef1 100644 (file)
@@ -44,7 +44,7 @@ template <typename Type> struct CFFIndexOf : IndexOf<HBUINT16, Type> {};
 typedef Index<HBUINT16>   CFFIndex;
 typedef CFFIndex          CFFCharStrings;
 typedef FDArray<HBUINT16> CFFFDArray;
-typedef CFFIndex          CFFSubrs;
+typedef Subrs<HBUINT16>   CFFSubrs;
 
 struct CFFFDSelect : FDSelect {};
 
@@ -387,7 +387,7 @@ struct CFFTopDictValues : TopDictValues
       switch (op)
       {
         case OpCode_FDSelect:
-          size += OpCode_Size (OpCode_longint) + 4 + OpCode_Size (op);
+          size += OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (op);
           break;
         default:
           size += TopDictValues::calculate_serialized_op_size (values[i]);
@@ -409,8 +409,7 @@ struct CFFTopDictValues : TopDictValues
 
 struct CFFTopDictOpSet : TopDictOpSet
 {
-  static inline bool process_op (const ByteStr& str, unsigned int& offset,
-                                 OpCode op, Stack& stack, CFFTopDictValues& dictval)
+  static inline bool process_op (OpCode op, InterpEnv& env, CFFTopDictValues& dictval)
   {
   
     switch (op) {
@@ -438,58 +437,58 @@ struct CFFTopDictOpSet : TopDictOpSet
       case OpCode_FontBBox:
       case OpCode_XUID:
       case OpCode_BaseFontBlend:
-        stack.clear ();
+        env.argStack.clear ();
         break;
         
       case OpCode_CIDCount:
-        if (unlikely (!stack.check_pop_uint (dictval.cidCount)))
+        if (unlikely (!env.argStack.check_pop_uint (dictval.cidCount)))
           return false;
-        stack.clear ();
+        env.argStack.clear ();
         break;
 
       case OpCode_ROS:
-        if (unlikely (!stack.check_pop_uint (dictval.ros[2]) ||
-                      !stack.check_pop_uint (dictval.ros[1]) ||
-                      !stack.check_pop_uint (dictval.ros[0])))
+        if (unlikely (!env.argStack.check_pop_uint (dictval.ros[2]) ||
+                      !env.argStack.check_pop_uint (dictval.ros[1]) ||
+                      !env.argStack.check_pop_uint (dictval.ros[0])))
           return false;
-        stack.clear ();
+        env.argStack.clear ();
         break;
 
       case OpCode_Encoding:
-        if (unlikely (!stack.check_pop_uint (dictval.EncodingOffset)))
+        if (unlikely (!env.argStack.check_pop_uint (dictval.EncodingOffset)))
           return false;
-        stack.clear ();
+        env.argStack.clear ();
         break;
 
       case OpCode_charset:
-        if (unlikely (!stack.check_pop_uint (dictval.CharsetOffset)))
+        if (unlikely (!env.argStack.check_pop_uint (dictval.CharsetOffset)))
           return false;
-        stack.clear ();
+        env.argStack.clear ();
         break;
 
       case OpCode_FDSelect:
-        if (unlikely (!stack.check_pop_uint (dictval.FDSelectOffset)))
+        if (unlikely (!env.argStack.check_pop_uint (dictval.FDSelectOffset)))
           return false;
-        stack.clear ();
+        env.argStack.clear ();
         break;
     
       case OpCode_Private:
-        if (unlikely (!stack.check_pop_uint (dictval.privateDictInfo.offset)))
+        if (unlikely (!env.argStack.check_pop_uint (dictval.privateDictInfo.offset)))
           return false;
-        if (unlikely (!stack.check_pop_uint (dictval.privateDictInfo.size)))
+        if (unlikely (!env.argStack.check_pop_uint (dictval.privateDictInfo.size)))
           return false;
-        stack.clear ();
+        env.argStack.clear ();
         break;
     
       default:
-        if (unlikely (!TopDictOpSet::process_op (str, offset, op, stack, dictval)))
+        if (unlikely (!TopDictOpSet::process_op (op, env, dictval)))
           return false;
         /* Record this operand below if stack is empty, otherwise done */
-        if (!stack.is_empty ()) return true;
+        if (!env.argStack.is_empty ()) return true;
         break;
     }
 
-    dictval.pushVal (op, str, offset + 1);
+    dictval.pushVal (op, env.substr);
     return true;
   }
 };
@@ -510,40 +509,32 @@ struct CFFFontDictValues : DictValues<OpStr>
   TableInfo   privateDictInfo;
 };
 
-struct CFFFontDictOpSet
+struct CFFFontDictOpSet : DictOpSet
 {
-  static inline bool process_op (const ByteStr& str, unsigned int& offset,
-                                 OpCode op, Stack& stack, CFFFontDictValues& dictval)
+  static inline bool process_op (OpCode op, InterpEnv& env, CFFFontDictValues& dictval)
   {
     switch (op) {
       case OpCode_FontName:
       case OpCode_FontMatrix:
       case OpCode_PaintType:
-        stack.clear ();
+        env.argStack.clear ();
         break;
       case OpCode_Private:
-        if (unlikely (!stack.check_pop_uint (dictval.privateDictInfo.offset)))
+        if (unlikely (!env.argStack.check_pop_uint (dictval.privateDictInfo.offset)))
           return false;
-        if (unlikely (!stack.check_pop_uint (dictval.privateDictInfo.size)))
+        if (unlikely (!env.argStack.check_pop_uint (dictval.privateDictInfo.size)))
           return false;
-        stack.clear ();
+        env.argStack.clear ();
         break;
-      case OpCode_longint:  /* 5-byte integer */
-        return stack.push_longint_from_str (str, offset);
-      case OpCode_BCD:  /* real number */
-        float v;
-        if (unlikely (stack.check_overflow (1) || !parse_bcd (str, offset, v)))
-          return false;
-        stack.push_real (v);
-        return true;
     
       default:
-        /* XXX: invalid */
-        stack.clear ();
-        return false;
+        if (unlikely (!DictOpSet::process_op (op, env)))
+          return false;
+        if (!env.argStack.is_empty ()) return true;
+        break;
     }
 
-    dictval.pushVal (op, str, offset + 1);
+    dictval.pushVal (op, env.substr);
     return true;
   }
 };
@@ -581,10 +572,9 @@ struct CFFPrivateDictValues_Base : DictValues<VAL>
 typedef CFFPrivateDictValues_Base<OpStr> CFFPrivateDictValues_Subset;
 typedef CFFPrivateDictValues_Base<DictVal> CFFPrivateDictValues;
 
-struct CFFPrivateDictOpSet
+struct CFFPrivateDictOpSet : DictOpSet
 {
-  static inline bool process_op (const ByteStr& str, unsigned int& offset,
-                                 OpCode op, Stack& stack, CFFPrivateDictValues& dictval)
+  static inline bool process_op (OpCode op, InterpEnv& env, CFFPrivateDictValues& dictval)
   {
     DictVal val;
     val.init ();
@@ -596,7 +586,7 @@ struct CFFPrivateDictOpSet
       case OpCode_FamilyOtherBlues:
       case OpCode_StemSnapH:
       case OpCode_StemSnapV:
-        if (unlikely (!stack.check_pop_delta (val.multi_val)))
+        if (unlikely (!env.argStack.check_pop_delta (val.multi_val)))
           return false;
         break;
       case OpCode_StdHW:
@@ -610,37 +600,31 @@ struct CFFPrivateDictOpSet
       case OpCode_initialRandomSeed:
       case OpCode_defaultWidthX:
       case OpCode_nominalWidthX:
-        if (unlikely (!stack.check_pop_num (val.single_val)))
+        if (unlikely (!env.argStack.check_pop_num (val.single_val)))
           return false;
-        stack.clear ();
+        env.argStack.clear ();
         break;
       case OpCode_Subrs:
-        if (unlikely (!stack.check_pop_uint (dictval.subrsOffset)))
+        if (unlikely (!env.argStack.check_pop_uint (dictval.subrsOffset)))
           return false;
-        stack.clear ();
+        env.argStack.clear ();
         break;
-      case OpCode_longint:  /* 5-byte integer */
-        return stack.push_longint_from_str (str, offset);
-      case OpCode_BCD:  /* real number */
-        float v;
-        if (unlikely (!stack.check_overflow (1) || !parse_bcd (str, offset, v)))
-          return false;
-        stack.push_real (v);
-        return true;
 
       default:
-        return false;
+        if (unlikely (!DictOpSet::process_op (op, env)))
+          return false;
+        if (!env.argStack.is_empty ()) return true;
+        break;
     }
 
-    dictval.pushVal (op, str, offset + 1, val);
+    dictval.pushVal (op, env.substr, val);
     return true;
   }
 };
 
-struct CFFPrivateDictOpSet_Subset
+struct CFFPrivateDictOpSet_Subset : DictOpSet
 {
-  static inline bool process_op (const ByteStr& str, unsigned int& offset,
-                                 OpCode op, Stack& stack, CFFPrivateDictValues_Subset& dictval)
+  static inline bool process_op (OpCode op, InterpEnv& env, CFFPrivateDictValues_Subset& dictval)
   {
     switch (op) {
       case OpCode_BlueValues:
@@ -660,35 +644,30 @@ struct CFFPrivateDictOpSet_Subset
       case OpCode_initialRandomSeed:
       case OpCode_defaultWidthX:
       case OpCode_nominalWidthX:
-        stack.clear ();
+        env.argStack.clear ();
         break;
 
-      case OpCode_BCD:
-        {
-          float v;
-          return parse_bcd (str, offset, v);
-        }
-
       case OpCode_Subrs:
-        if (unlikely (!stack.check_pop_uint (dictval.subrsOffset)))
+        if (unlikely (!env.argStack.check_pop_uint (dictval.subrsOffset)))
           return false;
-        stack.clear ();
+        env.argStack.clear ();
         break;
-      case OpCode_longint:  /* 5-byte integer */
-        return stack.push_longint_from_str (str, offset);
 
       default:
-        return false;
+        if (unlikely (!DictOpSet::process_op (op, env)))
+          return false;
+        if (!env.argStack.is_empty ()) return true;
+        break;
     }
 
-    dictval.pushVal (op, str, offset + 1);
+    dictval.pushVal (op, env.substr);
     return true;
   }
 };
 
-typedef Interpreter<CFFTopDictOpSet, CFFTopDictValues> CFFTopDict_Interpreter;
-typedef Interpreter<CFFFontDictOpSet, CFFFontDictValues> CFFFontDict_Interpreter;
-typedef Interpreter<CFFPrivateDictOpSet, CFFPrivateDictValues> CFFPrivateDict_Interpreter;
+typedef DictInterpreter<CFFTopDictOpSet, CFFTopDictValues> CFFTopDict_Interpreter;
+typedef DictInterpreter<CFFFontDictOpSet, CFFFontDictValues> CFFFontDict_Interpreter;
+typedef DictInterpreter<CFFPrivateDictOpSet, CFFPrivateDictValues> CFFPrivateDict_Interpreter;
 
 typedef CFFIndex NameIndex;
 typedef CFFIndexOf<TopDict> TopDictIndex;
@@ -743,10 +722,10 @@ struct cff
 
       { /* parse top dict */
         const ByteStr topDictStr = (*topDictIndex)[0];
+        if (unlikely (!topDictStr.sanitize (&sc))) { fini (); return; }
         CFFTopDict_Interpreter top_interp;
-        if (unlikely (!topDictStr.sanitize (&sc) ||
-                      !top_interp.interpret (topDictStr, topDicts[0])))
-        { fini (); return; }
+        top_interp.env.init (topDictStr);
+        if (unlikely (!top_interp.interpret (topDicts[0]))) { fini (); return; }
       }
       
       encoding = &Null(Encoding);
@@ -803,18 +782,18 @@ struct cff
         for (unsigned int i = 0; i < fdCount; i++)
         {
           ByteStr fontDictStr = (*fdArray)[i];
+          if (unlikely (!fontDictStr.sanitize (&sc))) { fini (); return; }
           CFFFontDictValues  *font;
           CFFFontDict_Interpreter font_interp;
+          font_interp.env.init (fontDictStr);
           font = fontDicts.push ();
-          if (unlikely (!fontDictStr.sanitize (&sc) ||
-                        !font_interp.interpret (fontDictStr, *font)))
-          { fini (); return; }
+          if (unlikely (!font_interp.interpret (*font))) { fini (); return; }
           PrivDictVal  *priv = &privateDicts[i];
           const ByteStr privDictStr (StructAtOffset<UnsizedByteStr> (cff, font->privateDictInfo.offset), font->privateDictInfo.size);
-          Interpreter<PrivOpSet, PrivDictVal> priv_interp;
-          if (unlikely (!privDictStr.sanitize (&sc) ||
-                        !priv_interp.interpret (privDictStr, *priv)))
-          { fini (); return; }
+          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<CFFSubrs> (privDictStr.str, priv->subrsOffset);
           if (priv->localSubrs != &Null(CFFSubrs) &&
@@ -828,10 +807,10 @@ struct cff
         PrivDictVal  *priv = &privateDicts[0];
         
         const ByteStr privDictStr (StructAtOffset<UnsizedByteStr> (cff, font->privateDictInfo.offset), font->privateDictInfo.size);
-        Interpreter<PrivOpSet, PrivDictVal> priv_interp;
-        if (unlikely (!privDictStr.sanitize (&sc) ||
-                      !priv_interp.interpret (privDictStr, *priv)))
-        { fini (); return; }
+        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<CFFSubrs> (privDictStr.str, priv->subrsOffset);
         if (priv->localSubrs != &Null(CFFSubrs) &&
index 4fa2ecb..71e00c4 100644 (file)
@@ -43,7 +43,7 @@ template <typename Type> struct CFF2IndexOf : IndexOf<HBUINT32, Type> {};
 
 typedef CFF2Index         CFF2CharStrings;
 typedef FDArray<HBUINT32> CFF2FDArray;
-typedef CFF2Index         CFF2Subrs;
+typedef Subrs<HBUINT32>   CFF2Subrs;
 
 typedef FDSelect3_4<HBUINT32, HBUINT16> FDSelect4;
 typedef FDSelect3_4_Range<HBUINT32, HBUINT16> FDSelect4_Range;
@@ -89,6 +89,8 @@ struct CFF2FDSelect
 
   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)
@@ -157,7 +159,7 @@ struct CFF2TopDictValues : TopDictValues
       {
         case OpCode_vstore:
         case OpCode_FDSelect:
-          size += OpCode_Size (OpCode_longint) + 4 + OpCode_Size (op);
+          size += OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (op);
           break;
         default:
           size += TopDictValues::calculate_serialized_op_size (values[i]);
@@ -173,38 +175,37 @@ struct CFF2TopDictValues : TopDictValues
 
 struct CFF2TopDictOpSet : TopDictOpSet
 {
-  static inline bool process_op (const ByteStr& str, unsigned int& offset,
-                                 OpCode op, Stack& stack, CFF2TopDictValues& dictval)
+  static inline bool process_op (OpCode op, InterpEnv& env, CFF2TopDictValues& dictval)
   {
     switch (op) {
       case OpCode_FontMatrix:
         {
           DictVal val;
           val.init ();
-          dictval.pushVal (op, str, offset + 1);
-          stack.clear ();
+          dictval.pushVal (op, env.substr);
+          env.argStack.clear ();
         }
         break;
 
       case OpCode_vstore:
-        if (unlikely (!stack.check_pop_uint (dictval.vstoreOffset)))
+        if (unlikely (!env.argStack.check_pop_uint (dictval.vstoreOffset)))
           return false;
-        stack.clear ();
+        env.argStack.clear ();
         break;
       case OpCode_FDSelect:
-        if (unlikely (!stack.check_pop_uint (dictval.FDSelectOffset)))
+        if (unlikely (!env.argStack.check_pop_uint (dictval.FDSelectOffset)))
           return false;
-        stack.clear ();
+        env.argStack.clear ();
         break;
     
       default:
-        if (unlikely (!TopDictOpSet::process_op (str, offset, op, stack, dictval)))
+        if (unlikely (!TopDictOpSet::process_op (op, env, dictval)))
           return false;
         /* Record this operand below if stack is empty, otherwise done */
-        if (!stack.is_empty ()) return true;
+        if (!env.argStack.is_empty ()) return true;
     }
 
-    dictval.pushVal (op, str, offset + 1);
+    dictval.pushVal (op, env.substr);
     return true;
   }
 };
@@ -225,35 +226,27 @@ struct CFF2FontDictValues : DictValues<OpStr>
   TableInfo    privateDictInfo;
 };
 
-struct CFF2FontDictOpSet
+struct CFF2FontDictOpSet : DictOpSet
 {
-  static inline bool process_op (const ByteStr& str, unsigned int& offset,
-                                 OpCode op, Stack& stack, CFF2FontDictValues& dictval)
+  static inline bool process_op (OpCode op, InterpEnv& env, CFF2FontDictValues& dictval)
   {
     switch (op) {
       case OpCode_Private:
-        if (unlikely (!stack.check_pop_uint (dictval.privateDictInfo.offset)))
+        if (unlikely (!env.argStack.check_pop_uint (dictval.privateDictInfo.offset)))
           return false;
-        if (unlikely (!stack.check_pop_uint (dictval.privateDictInfo.size)))
+        if (unlikely (!env.argStack.check_pop_uint (dictval.privateDictInfo.size)))
           return false;
-        stack.clear ();
+        env.argStack.clear ();
         break;
-      case OpCode_longint:  /* 5-byte integer */
-        return stack.push_longint_from_str (str, offset);
-      case OpCode_BCD:  /* real number */
-        float v;
-        if (unlikely (stack.check_overflow (1) || !parse_bcd (str, offset, v)))
-          return false;
-        stack.push_real (v);
-        return true;
     
       default:
-        /* XXX: invalid */
-        stack.clear ();
-        return false;
+        if (unlikely (!DictOpSet::process_op (op, env)))
+          return false;
+        if (!env.argStack.is_empty ())
+          return true;
     }
 
-    dictval.pushVal (op, str, offset + 1);
+    dictval.pushVal (op, env.substr);
     return true;
   }
 };
@@ -291,10 +284,9 @@ struct CFF2PrivateDictValues_Base : DictValues<VAL>
 typedef CFF2PrivateDictValues_Base<OpStr> CFF2PrivateDictValues_Subset;
 typedef CFF2PrivateDictValues_Base<DictVal> CFF2PrivateDictValues;
 
-struct CFF2PrivateDictOpSet
+struct CFF2PrivateDictOpSet : DictOpSet
 {
-  static inline bool process_op (const ByteStr& str, unsigned int& offset,
-                                 OpCode op, Stack& stack, CFF2PrivateDictValues& dictval)
+  static inline bool process_op (OpCode op, InterpEnv& env, CFF2PrivateDictValues& dictval)
   {
     DictVal val;
     val.init ();
@@ -307,9 +299,9 @@ struct CFF2PrivateDictOpSet
       case OpCode_BlueFuzz:
       case OpCode_ExpansionFactor:
       case OpCode_LanguageGroup:
-        if (unlikely (!stack.check_pop_num (val.single_val)))
+        if (unlikely (!env.argStack.check_pop_num (val.single_val)))
           return false;
-        stack.clear ();
+        env.argStack.clear ();
         break;
       case OpCode_BlueValues:
       case OpCode_OtherBlues:
@@ -317,40 +309,34 @@ struct CFF2PrivateDictOpSet
       case OpCode_FamilyOtherBlues:
       case OpCode_StemSnapH:
       case OpCode_StemSnapV:
-        if (unlikely (!stack.check_pop_delta (val.multi_val)))
+        if (unlikely (!env.argStack.check_pop_delta (val.multi_val)))
           return false;
-        stack.clear ();
+        env.argStack.clear ();
         break;
       case OpCode_Subrs:
-        if (unlikely (!stack.check_pop_uint (dictval.subrsOffset)))
+        if (unlikely (!env.argStack.check_pop_uint (dictval.subrsOffset)))
           return false;
-        stack.clear ();
+        env.argStack.clear ();
         break;
-      case OpCode_blend:
+      case OpCode_blenddict:
         // XXX: TODO
         return true;
-      case OpCode_longint:  /* 5-byte integer */
-        return stack.push_longint_from_str (str, offset);
-      case OpCode_BCD:  /* real number */
-        float v;
-        if (unlikely (!stack.check_overflow (1) || !parse_bcd (str, offset, v)))
-          return false;
-        stack.push_real (v);
-        return true;
 
       default:
-        return false;
+        if (unlikely (!DictOpSet::process_op (op, env)))
+          return false;
+        if (!env.argStack.is_empty ()) return true;
+        break;
     }
 
-    dictval.pushVal (op, str, offset + 1, val);
+    dictval.pushVal (op, env.substr, val);
     return true;
   }
 };
 
-struct CFF2PrivateDictOpSet_Subset
+struct CFF2PrivateDictOpSet_Subset : DictOpSet
 {
-  static inline bool process_op (const ByteStr& str, unsigned int& offset,
-                                 OpCode op, Stack& stack, CFF2PrivateDictValues_Subset& dictval)
+  static inline bool process_op (OpCode op, InterpEnv& env, CFF2PrivateDictValues_Subset& dictval)
   {
     switch (op) {
       case OpCode_BlueValues:
@@ -366,39 +352,34 @@ struct CFF2PrivateDictOpSet_Subset
       case OpCode_StemSnapV:
       case OpCode_LanguageGroup:
       case OpCode_ExpansionFactor:
-        stack.clear ();
+        env.argStack.clear ();
         break;
 
-      case OpCode_blend:
-        stack.clear ();
+      case OpCode_blenddict:
+        env.argStack.clear ();
         return true;
 
-      case OpCode_BCD:
-        {
-          float v;
-          return parse_bcd (str, offset, v);
-        }
-
       case OpCode_Subrs:
-        if (unlikely (!stack.check_pop_uint (dictval.subrsOffset)))
+        if (unlikely (!env.argStack.check_pop_uint (dictval.subrsOffset)))
           return false;
-        stack.clear ();
+        env.argStack.clear ();
         break;
-      case OpCode_longint:  /* 5-byte integer */
-        return stack.push_longint_from_str (str, offset);
 
       default:
-        return false;
+        if (unlikely (!DictOpSet::process_op (op, env)))
+          return false;
+        if (!env.argStack.is_empty ()) return true;
+        break;
     }
 
-    dictval.pushVal (op, str, offset + 1);
+    dictval.pushVal (op, env.substr);
     return true;
   }
 };
 
-typedef Interpreter<CFF2TopDictOpSet, CFF2TopDictValues> CFF2TopDict_Interpreter;
-typedef Interpreter<CFF2FontDictOpSet, CFF2FontDictValues> CFF2FontDict_Interpreter;
-typedef Interpreter<CFF2PrivateDictOpSet, CFF2PrivateDictValues> CFF2PrivateDict_Interpreter;
+typedef DictInterpreter<CFF2TopDictOpSet, CFF2TopDictValues> CFF2TopDict_Interpreter;
+typedef DictInterpreter<CFF2FontDictOpSet, CFF2FontDictValues> CFF2FontDict_Interpreter;
+typedef DictInterpreter<CFF2PrivateDictOpSet, CFF2PrivateDictValues> CFF2PrivateDict_Interpreter;
 
 }; /* namespace CFF */
 
@@ -439,10 +420,10 @@ struct cff2
 
       { /* parse top dict */
         ByteStr topDictStr (cff2 + cff2->topDict, cff2->topDictSize);
+        if (unlikely (!topDictStr.sanitize (&sc))) { fini (); return; }
         CFF2TopDict_Interpreter top_interp;
-        if (unlikely (!topDictStr.sanitize (&sc) ||
-                      !top_interp.interpret (topDictStr, topDict)))
-        { fini (); return; }
+        top_interp.env.init (topDictStr);
+        if (unlikely (!top_interp.interpret (topDict))) { fini (); return; }
       }
       
       globalSubrs = &StructAtOffset<CFF2Subrs> (cff2, cff2->topDict + cff2->topDictSize);
@@ -463,22 +444,22 @@ struct cff2
 
       privateDicts.resize (fdArray->count);
 
-      // parse font dicts and gather private dicts
+      /* parse font dicts and gather private dicts */
       for (unsigned int i = 0; i < fdArray->count; 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 (!fontDictStr.sanitize (&sc) ||
-                      !font_interp.interpret (fontDictStr, *font)))
-        { fini (); return; }
+        if (unlikely (!font_interp.interpret (*font))) { fini (); return; }
 
         const ByteStr privDictStr (StructAtOffsetOrNull<UnsizedByteStr> (cff2, font->privateDictInfo.offset), font->privateDictInfo.size);
-        Interpreter<PrivOpSet, PrivDictVal> priv_interp;
-        if (unlikely (!privDictStr.sanitize (&sc) ||
-                      !priv_interp.interpret (privDictStr, privateDicts[i])))
-        { fini (); return; }
+        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) &&
index b1a743c..124bb65 100644 (file)
 #include "hb-private.hh"
 
 #include "hb-subset-plan.hh"
+#include "hb-cff-interp-cs-common-private.hh"
+
+namespace CFF {
+
+struct SubrRefMaps
+{
+  inline SubrRefMaps (void)
+    : valid (false),
+      global_map (nullptr)
+  {
+    local_maps.init ();
+  }
+
+  inline void init (unsigned int fd_count)
+  {
+    valid = true;
+    global_map = hb_set_create ();
+    if (global_map == hb_set_get_empty ())
+      valid = false;
+    if (!local_maps.resize (fd_count))
+      valid = false;
+
+    for (unsigned int i = 0; i < local_maps.len; i++)
+    {
+      local_maps[i] = hb_set_create ();
+      if (local_maps[i] == hb_set_get_empty ())
+        valid = false;
+    }
+  }
+
+  inline void fini (void)
+  {
+    hb_set_destroy (global_map);
+    for (unsigned int i = 0; i < local_maps.len; i++)
+      hb_set_destroy (local_maps[i]);
+    local_maps.fini ();
+  }
+
+  bool is_valid (void) const { return valid; }
+  bool  valid;
+  hb_set_t  *global_map;
+  hb_vector_t<hb_set_t *> local_maps;
+};
+
+struct SubrRefMapPair
+{
+  inline void init (void) {}
+
+  hb_set_t  *global_map;
+  hb_set_t  *local_map;
+};
+
+template <typename ACCESSOR, typename ENV, typename OPSET>
+struct SubrSubsetter
+{
+  inline SubrSubsetter (const ACCESSOR &acc_, const hb_vector_t<hb_codepoint_t> &glyphs_)
+    : acc (acc_),
+      glyphs (glyphs_)
+  {}
+  
+  bool collect_refs (SubrRefMaps& refmaps /*OUT*/)
+  {
+    refmaps.init (acc.fdCount);
+    if (unlikely (!refmaps.valid)) return false;
+    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);
+      SubrRefMapPair  pair = { refmaps.global_map, refmaps.local_maps[fd] };
+      CSInterpreter<ENV, OPSET, SubrRefMapPair> interp;
+      interp.env.init (str, *acc.globalSubrs, *acc.privateDicts[fd].localSubrs);
+      if (unlikely (!interp.interpret (pair)))
+        return false;
+    }
+    return true;
+  }
+
+  const ACCESSOR &acc;
+  const hb_vector_t<hb_codepoint_t> &glyphs;
+};
+
+};  /* namespace CFF */
 
 HB_INTERNAL bool
 hb_plan_subset_cff_fdselect (const hb_vector_t<hb_codepoint_t> &glyphs,
index afc32f2..c778e1e 100644 (file)
 #include "hb-subset-cff.hh"
 #include "hb-subset-plan.hh"
 #include "hb-subset-cff-common-private.hh"
+#include "hb-cff-interp-cs.hh"
 
 using namespace CFF;
 
+char RETURN_OP[1] = { OpCode_return };
+static const ByteStr NULL_SUBR = { RETURN_OP/* str */, 1/* len */ };
+
 struct CFFSubTableOffsets {
   inline CFFSubTableOffsets (void)
   {
     memset (this, 0, sizeof(*this));
+    localSubrsInfos.init ();
+  }
+
+  inline ~CFFSubTableOffsets (void)
+  {
+    localSubrsInfos.fini ();
   }
 
   unsigned int  nameIndexOffset;
-  unsigned int  topDictOffset;
-  unsigned int  topDictOffSize;
+  TableInfo     topDictInfo;
   unsigned int  stringIndexOffset;
-  unsigned int  globalSubrsOffset;
+  TableInfo     globalSubrsInfo;
   unsigned int  encodingOffset;
   unsigned int  charsetOffset;
   TableInfo     FDSelectInfo;
-  unsigned int  FDArrayOffset;
-  unsigned int  FDArrayOffSize;
-  unsigned int  charStringsOffset;
-  unsigned int  charStringsOffSize;
+  TableInfo     FDArrayInfo;
+  TableInfo     charStringsInfo;
   TableInfo     privateDictInfo;
+  hb_vector_t<TableInfo>  localSubrsInfos;
 };
 
 struct CFFTopDict_OpSerializer : OpSerializer
@@ -71,10 +79,10 @@ struct CFFTopDict_OpSerializer : OpSerializer
         return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.encodingOffset));
 
       case OpCode_CharStrings:
-        return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.charStringsOffset));
+        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.FDArrayOffset));
+        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));
@@ -106,10 +114,10 @@ struct CFFTopDict_OpSerializer : OpSerializer
       case OpCode_CharStrings:
       case OpCode_FDArray:
       case OpCode_FDSelect:
-        return OpCode_Size (OpCode_longint) + 4 + OpCode_Size (opstr.op);
+        return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (opstr.op);
     
       case OpCode_Private:
-        return OpCode_Size (OpCode_longint) + 4 + OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Private);
+        return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Private);
     
       default:
         return opstr.str.len;
@@ -154,7 +162,7 @@ struct CFFFontDict_OpSerializer : OpSerializer
   inline unsigned int calculate_serialized_size (const OpStr &opstr) const
   {
     if (opstr.op == OpCode_Private)
-      return OpCode_Size (OpCode_longint) + 4 + OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Private);
+      return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Private);
     else
       return opstr.str.len;
   }
@@ -183,12 +191,38 @@ struct CFFPrivateDict_OpSerializer : OpSerializer
   }
 };
 
+struct CFFCSOpSet_SubrSubset : CFFCSOpSet<SubrRefMapPair>
+{
+  static inline bool process_op (OpCode op, CFFCSInterpEnv &env, SubrRefMapPair& refMapPair)
+  {
+    unsigned int  subr_num;
+    switch (op) {
+      case OpCode_callsubr:
+        if (!unlikely (env.popSubrNum(env.localSubrs, subr_num)))
+          return false;
+        env.argStack.unpop ();
+        refMapPair.local_map->add (subr_num);
+        break;
+      case OpCode_callgsubr:
+        if (!unlikely (env.popSubrNum(env.globalSubrs, subr_num)))
+          return false;
+        env.argStack.unpop ();
+        refMapPair.global_map->add (subr_num);
+        break;
+      default:
+        break;
+    }
+    return CFFCSOpSet::process_op (op, env, refMapPair);
+  }
+};
+
 struct cff_subset_plan {
   inline cff_subset_plan (void)
     : final_size (0),
       orig_fdcount (0),
       subst_fdcount(1),
-      subst_fdselect_format (0)
+      subst_fdselect_format (0),
+      offsets()
   {
     topdict_sizes.init ();
     topdict_sizes.resize (1);
@@ -205,6 +239,7 @@ struct cff_subset_plan {
     fdmap.fini ();
     subset_charstrings.fini ();
     privateDictInfos.fini ();
+    subrRefMaps.fini ();
   }
 
   inline bool create (const OT::cff::accelerator_subset_t &acc,
@@ -222,20 +257,33 @@ struct cff_subset_plan {
     
     /* top dict INDEX */
     {
-      offsets.topDictOffset = final_size;
+      offsets.topDictInfo.offset = final_size;
       CFFTopDict_OpSerializer topSzr;
       unsigned int topDictSize = TopDict::calculate_serialized_size (acc.topDicts[0], topSzr);
-      offsets.topDictOffSize = calcOffSize(topDictSize);
-      final_size += CFFIndexOf<TopDict>::calculate_serialized_size<CFFTopDictValues> (offsets.topDictOffSize, acc.topDicts, topdict_sizes, topSzr);
+      offsets.topDictInfo.offSize = calcOffSize(topDictSize);
+      final_size += CFFIndexOf<TopDict>::calculate_serialized_size<CFFTopDictValues> (offsets.topDictInfo.offSize, acc.topDicts, topdict_sizes, topSzr);
     }
 
     /* String INDEX */
     offsets.stringIndexOffset = final_size;
     final_size += acc.stringIndex->get_size ();
     
+    /* Subset global & local subrs */
+    {
+      SubrSubsetter<const OT::cff::accelerator_subset_t, CFFCSInterpEnv, CFFCSOpSet_SubrSubset> subsetter(acc, plan->glyphs);
+      if (!subsetter.collect_refs (subrRefMaps))
+        return false;
+      
+      offsets.globalSubrsInfo.size = acc.globalSubrs->calculate_serialized_size (offsets.globalSubrsInfo.offSize, subrRefMaps.global_map, 1);
+      if (!offsets.localSubrsInfos.resize (orig_fdcount))
+        return false;
+      for (unsigned int i = 0; i < orig_fdcount; i++)
+        offsets.localSubrsInfos[i].size = acc.privateDicts[i].localSubrs->calculate_serialized_size (offsets.localSubrsInfos[i].offSize, subrRefMaps.local_maps[i], 1);
+    }
+    
     /* global subrs */
-    offsets.globalSubrsOffset = final_size;
-    final_size += acc.globalSubrs->get_size ();
+    offsets.globalSubrsInfo.offset = final_size;
+    final_size += offsets.globalSubrsInfo.size;
 
     /* Encoding */
     offsets.encodingOffset = final_size;
@@ -268,14 +316,14 @@ struct cff_subset_plan {
 
     /* FDArray (FDIndex) */
     if (acc.fdArray != &Null(CFFFDArray)) {
-      offsets.FDArrayOffset = final_size;
+      offsets.FDArrayInfo.offset = final_size;
       CFFFontDict_OpSerializer fontSzr;
-      final_size += CFFFDArray::calculate_serialized_size(offsets.FDArrayOffSize/*OUT*/, acc.fontDicts, subst_fdcount, fdmap, fontSzr);
+      final_size += CFFFDArray::calculate_serialized_size(offsets.FDArrayInfo.offSize/*OUT*/, acc.fontDicts, subst_fdcount, fdmap, fontSzr);
     }
 
     /* CharStrings */
     {
-      offsets.charStringsOffset = final_size;
+      offsets.charStringsInfo.offset = final_size;
       unsigned int dataSize = 0;
       for (unsigned int i = 0; i < plan->glyphs.len; i++)
       {
@@ -283,8 +331,8 @@ struct cff_subset_plan {
         subset_charstrings.push (str);
         dataSize += str.len;
       }
-      offsets.charStringsOffSize = calcOffSize (dataSize + 1);
-      final_size += CFFCharStrings::calculate_serialized_size (offsets.charStringsOffSize, plan->glyphs.len, dataSize);
+      offsets.charStringsInfo.offSize = calcOffSize (dataSize + 1);
+      final_size += CFFCharStrings::calculate_serialized_size (offsets.charStringsInfo.offSize, plan->glyphs.len, dataSize);
     }
 
     /* private dicts & local subrs */
@@ -294,9 +342,9 @@ struct cff_subset_plan {
       if (!fdmap.excludes (i))
       {
         CFFPrivateDict_OpSerializer privSzr;
-        TableInfo  privInfo = { final_size, PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr) };
+        TableInfo  privInfo = { final_size, PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr), 0 };
         privateDictInfos.push (privInfo);
-        final_size += privInfo.size + acc.privateDicts[i].localSubrs->get_size ();
+        final_size += privInfo.size + offsets.localSubrsInfos[i].size;
       }
     }
 
@@ -324,6 +372,8 @@ struct cff_subset_plan {
 
   hb_vector_t<ByteStr> subset_charstrings;
   hb_vector_t<TableInfo> privateDictInfos;
+
+  SubrRefMaps             subrRefMaps;
 };
 
 static inline bool _write_cff (const cff_subset_plan &plan,
@@ -358,11 +408,11 @@ static inline bool _write_cff (const cff_subset_plan &plan,
 
   /* top dict INDEX */
   {
-    assert (plan.offsets.topDictOffset == c.head - c.start);
+    assert (plan.offsets.topDictInfo.offset == c.head - c.start);
     CFFIndexOf<TopDict> *dest = c.start_embed< CFFIndexOf<TopDict> > ();
     if (dest == nullptr) return false;
     CFFTopDict_OpSerializer topSzr;
-    if (unlikely (!dest->serialize (&c, plan.offsets.topDictOffSize, acc.topDicts, plan.topdict_sizes, topSzr, plan.offsets)))
+    if (unlikely (!dest->serialize (&c, plan.offsets.topDictInfo.offSize, acc.topDicts, plan.topdict_sizes, topSzr, plan.offsets)))
     {
       DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF top dict");
       return false;
@@ -383,10 +433,10 @@ static inline bool _write_cff (const cff_subset_plan &plan,
 
   /* global subrs */
   {
-    assert (plan.offsets.globalSubrsOffset == c.head - c.start);
+    assert (plan.offsets.globalSubrsInfo.offset == c.head - c.start);
     CFFSubrs *dest = c.start_embed<CFFSubrs> ();
     if (unlikely (dest == nullptr)) return false;
-    if (unlikely (!dest->serialize (&c, *acc.globalSubrs)))
+    if (unlikely (!dest->serialize (&c, *acc.globalSubrs, plan.offsets.globalSubrsInfo.offSize, plan.subrRefMaps.global_map, NULL_SUBR)))
     {
       DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF global subrs");
       return false;
@@ -448,11 +498,11 @@ static inline bool _write_cff (const cff_subset_plan &plan,
   /* FDArray (FD Index) */
   if (acc.fdArray != &Null(CFFFDArray))
   {
-    assert (plan.offsets.FDArrayOffset == c.head - c.start);
+    assert (plan.offsets.FDArrayInfo.offset == c.head - c.start);
     CFFFDArray  *fda = c.start_embed<CFFFDArray> ();
     if (unlikely (fda == nullptr)) return false;
     CFFFontDict_OpSerializer  fontSzr;
-    if (unlikely (!fda->serialize (&c, plan.offsets.FDArrayOffSize,
+    if (unlikely (!fda->serialize (&c, plan.offsets.FDArrayInfo.offSize,
                                    acc.fontDicts, plan.subst_fdcount, plan.fdmap,
                                    fontSzr, plan.privateDictInfos)))
     {
@@ -463,10 +513,10 @@ static inline bool _write_cff (const cff_subset_plan &plan,
 
   /* CharStrings */
   {
-    assert (plan.offsets.charStringsOffset == c.head - c.start);
+    assert (plan.offsets.charStringsInfo.offset == c.head - c.start);
     CFFCharStrings  *cs = c.start_embed<CFFCharStrings> ();
     if (unlikely (cs == nullptr)) return false;
-    if (unlikely (!cs->serialize (&c, plan.offsets.charStringsOffSize, plan.subset_charstrings)))
+    if (unlikely (!cs->serialize (&c, plan.offsets.charStringsInfo.offSize, plan.subset_charstrings)))
     {
       DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF CharStrings");
       return false;
@@ -496,7 +546,7 @@ static inline bool _write_cff (const cff_subset_plan &plan,
           DEBUG_MSG (SUBSET, nullptr, "CFF subset: local subrs unexpectedly null [%d]", i);
           return false;
         }
-        if (unlikely (!subrs->serialize (&c, *acc.privateDicts[i].localSubrs)))
+        if (unlikely (!subrs->serialize (&c, *acc.privateDicts[i].localSubrs, plan.offsets.localSubrsInfos[i].offSize, plan.subrRefMaps.local_maps[i], NULL_SUBR)))
         {
           DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF local subrs [%d]", i);
           return false;
index 418cdfb..dfae90f 100644 (file)
@@ -42,10 +42,8 @@ struct CFF2SubTableOffsets {
   unsigned int  topDictSize;
   unsigned int  varStoreOffset;
   TableInfo     FDSelectInfo;
-  unsigned int  FDArrayOffset;
-  unsigned int  FDArrayOffSize;
-  unsigned int  charStringsOffset;
-  unsigned int  charStringsOffSize;
+  TableInfo     FDArrayInfo;
+  TableInfo     charStringsInfo;
   unsigned int  privateDictsOffset;
 };
 
@@ -63,10 +61,10 @@ struct CFF2TopDict_OpSerializer : OpSerializer
         return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.varStoreOffset));
 
       case OpCode_CharStrings:
-        return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.charStringsOffset));
+        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.FDArrayOffset));
+        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));
@@ -85,7 +83,7 @@ struct CFF2TopDict_OpSerializer : OpSerializer
       case OpCode_CharStrings:
       case OpCode_FDArray:
       case OpCode_FDSelect:
-        return OpCode_Size (OpCode_longint) + 4 + OpCode_Size (opstr.op);
+        return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (opstr.op);
     
       default:
         return opstr.str.len;
@@ -130,7 +128,7 @@ struct CFF2FontDict_OpSerializer : OpSerializer
   inline unsigned int calculate_serialized_size (const OpStr &opstr) const
   {
     if (opstr.op == OpCode_Private)
-      return OpCode_Size (OpCode_longint) + 4 + OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Private);
+      return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Private);
     else
       return opstr.str.len;
   }
@@ -227,14 +225,14 @@ struct cff2_subset_plan {
 
     /* FDArray (FDIndex) */
     {
-      offsets.FDArrayOffset = final_size;
+      offsets.FDArrayInfo.offset = final_size;
       CFF2FontDict_OpSerializer fontSzr;
-      final_size += CFF2FDArray::calculate_serialized_size(offsets.FDArrayOffSize/*OUT*/, acc.fontDicts, subst_fdcount, fdmap, fontSzr);
+      final_size += CFF2FDArray::calculate_serialized_size(offsets.FDArrayInfo.offSize/*OUT*/, acc.fontDicts, subst_fdcount, fdmap, fontSzr);
     }
 
     /* CharStrings */
     {
-      offsets.charStringsOffset = final_size;
+      offsets.charStringsInfo.offset = final_size;
       unsigned int dataSize = 0;
       for (unsigned int i = 0; i < plan->glyphs.len; i++)
       {
@@ -242,8 +240,8 @@ struct cff2_subset_plan {
         subset_charstrings.push (str);
         dataSize += str.len;
       }
-      offsets.charStringsOffSize = calcOffSize (dataSize + 1);
-      final_size += CFF2CharStrings::calculate_serialized_size (offsets.charStringsOffSize, plan->glyphs.len, dataSize);
+      offsets.charStringsInfo.offSize = calcOffSize (dataSize + 1);
+      final_size += CFF2CharStrings::calculate_serialized_size (offsets.charStringsInfo.offSize, plan->glyphs.len, dataSize);
     }
 
     /* private dicts & local subrs */
@@ -253,7 +251,7 @@ struct cff2_subset_plan {
       if (!fdmap.excludes (i))
       {
         CFF2PrivateDict_OpSerializer privSzr;
-        TableInfo  privInfo = { final_size, PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr) };
+        TableInfo  privInfo = { final_size, PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr), 0 };
         privateDictInfos.push (privInfo);
         final_size += privInfo.size + acc.privateDicts[i].localSubrs->get_size ();
       }
@@ -314,7 +312,7 @@ static inline bool _write_cff2 (const cff2_subset_plan &plan,
     assert (cff2->topDict + plan.offsets.topDictSize == c.head - c.start);
     CFF2Subrs *dest = c.start_embed<CFF2Subrs> ();
     if (unlikely (dest == nullptr)) return false;
-    if (unlikely (!dest->serialize (&c, *acc.globalSubrs)))
+    if (unlikely (!dest->Index<HBUINT32>::serialize (&c, *acc.globalSubrs)))
     {
       DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 global subrs");
       return false;
@@ -362,11 +360,11 @@ static inline bool _write_cff2 (const cff2_subset_plan &plan,
 
   /* FDArray (FD Index) */
   {
-    assert (plan.offsets.FDArrayOffset == c.head - c.start);
+    assert (plan.offsets.FDArrayInfo.offset == c.head - c.start);
     CFF2FDArray  *fda = c.start_embed<CFF2FDArray> ();
     if (unlikely (fda == nullptr)) return false;
     CFF2FontDict_OpSerializer  fontSzr;
-    if (unlikely (!fda->serialize (&c, plan.offsets.FDArrayOffSize,
+    if (unlikely (!fda->serialize (&c, plan.offsets.FDArrayInfo.offSize,
                                    acc.fontDicts, plan.subst_fdcount, plan.fdmap,
                                    fontSzr, plan.privateDictInfos)))
     {
@@ -377,10 +375,10 @@ static inline bool _write_cff2 (const cff2_subset_plan &plan,
 
   /* CharStrings */
   {
-    assert (plan.offsets.charStringsOffset == c.head - c.start);
+    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.charStringsOffSize, plan.subset_charstrings)))
+    if (unlikely (!cs->serialize (&c, plan.offsets.charStringsInfo.offSize, plan.subset_charstrings)))
     {
       DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 CharStrings");
       return false;
@@ -410,7 +408,7 @@ static inline bool _write_cff2 (const cff2_subset_plan &plan,
           DEBUG_MSG (SUBSET, nullptr, "CFF2 subset: local subrs unexpectedly null [%d]", i);
           return false;
         }
-        if (unlikely (!subrs->serialize (&c, *acc.privateDicts[i].localSubrs)))
+        if (unlikely (!subrs->Index<HBUINT32>::serialize (&c, *acc.privateDicts[i].localSubrs)))
         {
           DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 local subrs [%d]", i);
           return false;