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