first push of CFF/CFF2 work
authorMichiharu Ariza <ariza@adobe.com>
Wed, 18 Jul 2018 21:17:52 +0000 (14:17 -0700)
committerMichiharu Ariza <ariza@adobe.com>
Wed, 18 Jul 2018 21:17:52 +0000 (14:17 -0700)
Index, Dict structs
hooked up to hb-subset (takes CFF2, outputs empty CFF2)

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

index 0bc9e58..4c03841 100644 (file)
@@ -23,6 +23,7 @@ HB_BASE_sources = \
        hb-ot-color-cbdt-table.hh \
        hb-ot-cmap-table.hh \
        hb-ot-glyf-table.hh \
+       hb-ot-cff2-table.hh \
        hb-ot-hdmx-table.hh \
        hb-ot-head-table.hh \
        hb-ot-hhea-table.hh \
@@ -142,6 +143,7 @@ HB_OT_sources = \
        hb-ot-shape-fallback-private.hh \
        hb-ot-shape-fallback.cc \
        hb-ot-shape-private.hh \
+       hb-ot-cff-common-private.hh \
        hb-ot-var.cc \
        hb-ot-var-avar-table.hh \
        hb-ot-var-fvar-table.hh \
@@ -206,6 +208,7 @@ HB_SUBSET_sources = \
        hb-static.cc \
        hb-subset.cc \
        hb-subset-glyf.cc \
+       hb-subset-cff2.cc \
        hb-subset-input.cc \
        hb-subset-plan.cc \
        $(NULL)
@@ -213,6 +216,7 @@ HB_SUBSET_sources = \
 HB_SUBSET_headers = \
        hb-subset.h \
        hb-subset-glyf.hh \
+       hb-subset-cff2.hh \
        hb-subset-plan.hh \
        hb-subset-private.hh \
        $(NULL)
diff --git a/src/hb-ot-cff-common-private.hh b/src/hb-ot-cff-common-private.hh
new file mode 100644 (file)
index 0000000..fde100a
--- /dev/null
@@ -0,0 +1,475 @@
+/*
+ * 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-private.hh"
+#include "hb-ot-layout-common-private.hh"
+
+namespace CFF {
+
+using namespace OT;
+
+const float UNSET_REAL_VALUE = -1.0f;
+
+enum OpCode {
+    /* One byte operators (0-31) */
+    OpCode_version,                /* 0   CFF Top */
+    OpCode_Notice,                 /* 1   CFF Top */
+    OpCode_FullName,               /* 2   CFF Top */
+    OpCode_FamilyName,             /* 3   CFF Top */
+    OpCode_Weight,                 /* 4   CFF Top */
+    OpCode_FontBBox,               /* 5   CFF Top */
+    OpCode_BlueValues,             /* 6   CFF Private, CFF2 Private */
+    OpCode_OtherBlues,             /* 7   CFF Private, CFF2 Private */
+    OpCode_FamilyBlues,            /* 8   CFF Private, CFF2 Private */
+    OpCode_FamilyOtherBlues,       /* 9   CFF Private, CFF2 Private */
+    OpCode_StdHW,                  /* 10  CFF Private, CFF2 Private */
+    OpCode_StdVW,                  /* 11  CFF Private, CFF2 Private */
+    OpCode_escape,                 /* 12  All. Shared with CS */
+    OpCode_UniqueID,               /* 13  CFF Top */
+    OpCode_XUID,                   /* 14  CFF Top */
+    OpCode_charset,                /* 15  CFF Top (0) */
+    OpCode_Encoding,               /* 16  CFF Top (0) */
+    OpCode_CharStrings,            /* 17  CFF Top, CFF2 Top */
+    OpCode_Private,                /* 18  CFF Top, CFF2 FD */
+    OpCode_Subrs,                  /* 19  CFF Private, CFF2 Private */
+    OpCode_defaultWidthX,          /* 20  CFF Private (0) */
+    OpCode_nominalWidthX,          /* 21  CFF Private (0) */
+    OpCode_vsindex,                /* 22  CFF2 Private/CS */
+    OpCode_blend,                  /* 23  CFF2 Private/CS  */
+    OpCode_vstore,                 /* 24  CFF2 Top */
+    OpCode_reserved25,             /* 25 */
+    OpCode_reserved26,             /* 26 */
+    OpCode_reserved27,             /* 27 */
+
+    /* Numbers */
+    OpCode_shortint,               /* 28  All */
+    OpCode_longint,                /* 29  All */
+    OpCode_BCD,                    /* 30  CFF2 Top/FD */
+    OpCode_reserved31,             /* 31 */
+
+    /* 1-byte integers */
+    OpCode_OneByteIntFirst = 32,   /* All. beginning of the range of first byte ints */
+    OpCode_OneByteIntLast = 246,   /* All. ending of the range of first byte int */
+
+    /* 2-byte integers */
+    OpCode_TwoBytePosInt0,         /* 247  All. first byte of two byte positive int (+108 to +1131) */
+    OpCode_TwoBytePosInt1,
+    OpCode_TwoBytePosInt2,
+    OpCode_TwoBytePosInt3,
+
+    OpCode_TwoByteNegInt0,         /* 251  All. first byte of two byte negative int (-1131 to -108) */
+    OpCode_TwoByteNegInt1,
+    OpCode_TwoByteNegInt2,
+    OpCode_TwoByteNegInt3,
+
+    /* Two byte escape operators 12, (0-41) */
+    OpCode_ESC_Base = 32,
+    OpCode_Copyright = OpCode_ESC_Base, /* OpCode_ESC(0) CFF Top */
+    OpCode_isFixedPitch,           /* OpCode_ESC(1)  CFF Top (false) */
+    OpCode_ItalicAngle,            /* OpCode_ESC(2)  CFF Top (0) */
+    OpCode_UnderlinePosition,      /* OpCode_ESC(3)  CFF Top (-100) */
+    OpCode_UnderlineThickness,     /* OpCode_ESC(4)  CFF Top (50) */
+    OpCode_PaintType,              /* OpCode_ESC(5)  CFF Top (0) */
+    OpCode_CharstringType,         /* OpCode_ESC(6)  CFF Top (2) */
+    OpCode_FontMatrix,             /* OpCode_ESC(7)  CFF Top, CFF2 Top (.001 0 0 .001 0 0)*/
+    OpCode_StrokeWidth,            /* OpCode_ESC(8)  CFF Top (0) */
+    OpCode_BlueScale,              /* OpCode_ESC(9)  CFF Private, CFF2 Private (0.039625) */
+    OpCode_BlueShift,              /* OpCode_ESC(10) CFF Private, CFF2 Private (7) */
+    OpCode_BlueFuzz,               /* OpCode_ESC(11) CFF Private, CFF2 Private (1) */
+    OpCode_StemSnapH,              /* OpCode_ESC(12) CFF Private, CFF2 Private */
+    OpCode_StemSnapV,              /* OpCode_ESC(13) CFF Private, CFF2 Private */
+    OpCode_ForceBold,              /* OpCode_ESC(14) CFF Private (false) */
+    OpCode_reservedESC15,          /* OpCode_ESC(15) */
+    OpCode_reservedESC16,          /* OpCode_ESC(16) */
+    OpCode_LanguageGroup,          /* OpCode_ESC(17) CFF Private, CFF2 Private (0) */
+    OpCode_ExpansionFactor,        /* OpCode_ESC(18) CFF Private, CFF2 Private (0.06) */
+    OpCode_initialRandomSeed,      /* OpCode_ESC(19) CFF Private (0) */
+    OpCode_SyntheticBase,          /* OpCode_ESC(20) CFF Top */
+    OpCode_PostScript,             /* OpCode_ESC(21) CFF Top */
+    OpCode_BaseFontName,           /* OpCode_ESC(22) CFF Top */
+    OpCode_BaseFontBlend,          /* OpCode_ESC(23) CFF Top */
+    OpCode_reservedESC24,          /* OpCode_ESC(24) */
+    OpCode_reservedESC25,          /* OpCode_ESC(25) */
+    OpCode_reservedESC26,          /* OpCode_ESC(26) */
+    OpCode_reservedESC27,          /* OpCode_ESC(27) */
+    OpCode_reservedESC28,          /* OpCode_ESC(28) */
+    OpCode_reservedESC29,          /* OpCode_ESC(29) */
+    OpCode_ROS,                    /* OpCode_ESC(30) CFF Top_CID */
+    OpCode_CIDFontVersion,         /* OpCode_ESC(31) CFF Top_CID (0) */
+    OpCode_CIDFontRevision,        /* OpCode_ESC(32) CFF Top_CID (0) */
+    OpCode_CIDFontType,            /* OpCode_ESC(33) CFF Top_CID (0) */
+    OpCode_CIDCount,               /* OpCode_ESC(34) CFF Top_CID (8720) */
+    OpCode_UIDBase,                /* OpCode_ESC(35) CFF Top_CID */
+    OpCode_FDArray,                /* OpCode_ESC(36) CFF Top_CID, CFF2 Top */
+    OpCode_FDSelect,               /* OpCode_ESC(37) CFF Top_CID, CFF2 Top */
+    OpCode_FontName,               /* OpCode_ESC(38) CFF Top_CID */
+
+    OpCode_reserved255 = 255
+};
+
+inline OpCode Make_OpCode_ESC(unsigned char byte2)  { return (OpCode)(OpCode_ESC_Base + byte2); }
+
+/* CFF INDEX */
+struct Index
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) && offSize >= 1 && offSize <= 4 &&
+                  c->check_array (offsets, offSize, count + 1) &&
+                  c->check_array (data_base (), 1, offset_at (count)));
+  }
+
+  inline unsigned int offset_array_size (void) const
+  { return offSize * (count + 1); }
+
+  inline const unsigned int offset_at (unsigned int index) const
+  {
+    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 + 5 + offset_array_size (); }
+
+  inline unsigned int data_size (void) const
+  { return HBINT8::static_size; };
+
+  inline hb_bytes_t operator [] (unsigned int index) const
+  {
+    if (likely (index < count))
+    {
+      hb_bytes_t  b;
+      b.bytes = data_base () + offset_at (index) - 1;
+      b.len = offset_at (index + 1) - offset_at (index);
+      return b;
+    }
+    return Null(hb_bytes_t);
+  }
+
+  inline unsigned int get_size (void) const
+  { return count.static_size + offSize.static_size + offset_array_size () + (offset_at (count) - 1); }
+
+  HBUINT32  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_MIN (6);
+};
+
+template <typename Type>
+struct IndexOf : Index
+{
+  inline bool sanitize (hb_sanitize_context_t *c) const
+  {
+    TRACE_SANITIZE (this);
+    return_trace (c->check_struct (this) && offSize >= 1 && offSize <= 4 &&
+                  c->check_array (offsets, offSize, count + 1) &&
+                  c->check_array (data_base (), sizeof (Type), offset_at (count)));
+  }
+
+  inline const Type& operator [] (unsigned int index) const
+  {
+    if (likely (index < count))
+      return StructAtOffset<Type>(data_base (), offset_at (index) - 1);
+    return Null(Type);
+  }
+};
+
+struct UnsizedByteStr : UnsizedArrayOf <HBUINT8> {};
+
+struct ByteStr {
+  ByteStr (const UnsizedByteStr& s, unsigned int l)
+    : str (s), len (l) {}
+
+  inline bool sanitize (hb_sanitize_context_t *c) const { return str.sanitize (c, len); }
+
+  inline const HBUINT8& operator [] (unsigned int i) const { return str[i]; }
+
+  inline bool check_limit (unsigned int offset, unsigned int count) const
+  { return (offset + count <= len); }
+
+  const UnsizedByteStr& str;
+  const unsigned int len;
+};
+
+/* Top Dict, Font Dict, Private Dict */
+typedef UnsizedByteStr Dict;
+
+typedef Index CharStrings;
+typedef Index Subrs;
+typedef IndexOf<Dict> FDArray;
+
+/* FDSelect */
+struct FDSelect0 {
+  HBUINT8     fds[VAR];
+};
+
+struct FDSelect3_Range {
+  HBUINT16    first;
+  HBUINT8     fd;
+};
+
+struct FDSelect3 {
+  HBUINT16         nRanges;
+  FDSelect3_Range  ranges[VAR];
+  /* HBUINT16 sentinel */
+};
+
+struct FDSelect {
+  // XXX: need to sanitize at run time
+  HBUINT8       format;
+  union {
+    FDSelect0   format0;
+    FDSelect3   format3;
+  } u;
+};
+
+inline float parse_bcd (const ByteStr& str, unsigned int& offset, float& v)
+{
+  // XXX: TODO
+  v = 0;
+  for (;;) {
+    if (++offset >= str.len)
+      return false;
+    unsigned char byte = str[offset];
+    if (((byte & 0xF0) == 0xF0) || ((byte & 0x0F) == 0x0F))
+      break;
+  }
+  return true;
+}
+
+struct Number
+{
+  Number (int v = 0) { set_int (v); }
+  Number (float v) { set_real (v); }
+
+  inline void set_int (int v)       { is_real = false; u.int_val = v; };
+  inline int to_int (void) const    { return is_real? (int)u.real_val: u.int_val; }
+  inline void set_real (float v)    { is_real = true; u.real_val = v; };
+  inline float to_real (void) const { return is_real? u.real_val: (float)u.int_val; }
+
+protected:
+  bool is_real;
+  union {
+    int     int_val;
+    float   real_val;
+  } u;
+};
+
+struct Stack
+{
+  inline void init (void) { size = 0; }
+  inline void fini (void) { }
+
+  inline void push (const Number &v)
+  {
+    if (likely (size < kSizeLimit))
+      numbers[size++] = v;
+  }
+
+  inline const Number& pop (void)
+  {
+    if (likely (size > 0))
+      return numbers[--size];
+    else
+      return Null(Number);
+  }
+
+  inline bool check_push (void)
+  {
+    if (likely (size < kSizeLimit)) {
+      size++;
+      return true;
+    } else
+      return false;
+  }
+
+  inline bool check_pop (void)
+  {
+    if (likely (0 < size)) {
+      size--;
+      return true;
+    } else
+      return false;
+  }
+
+  inline bool check_pop_int (int& v)
+  {
+    if (unlikely (!this->check_underflow (1)))
+      return false;
+    v = this->pop ().to_int ();
+    return true;
+  }
+
+  inline bool check_pop_uint (unsigned int& v)
+  {
+    uint32_t  i;
+    if (unlikely (!this->check_underflow (1)))
+      return false;
+    i = this->pop ().to_int ();
+    if (unlikely (i <= 0))
+      return false;
+    v = (uint32_t)i;
+    return true;
+  }
+
+  inline bool check_pop_real (float& v)
+  {
+    if (unlikely (!this->check_underflow (1)))
+      return false;
+    v = this->pop ().to_real ();
+    return true;
+  }
+
+  inline bool check_pop_delta (hb_vector_t<float>& vec, bool even=false)
+  {
+    if (even && unlikely ((this->size & 1) != 0))
+      return false;
+
+    float val = 0.0f;
+    for (unsigned int i = 0; i < size; i++) {
+      val += numbers[i].to_real ();
+      vec.push (val);
+    }
+    return true;
+  }
+
+  inline void clear (void) { size = 0; }
+
+  inline bool check_overflow (unsigned int count) { return (count <= kSizeLimit) && (count + size <= kSizeLimit); }
+  inline bool check_underflow (unsigned int count) { return (count <= size); }
+
+  static const unsigned int kSizeLimit = 513;
+
+  unsigned int size;
+  Number numbers[kSizeLimit];
+};
+
+template <typename Offset>
+inline bool check_pop_offset (Stack& stack, Offset& offset)
+{
+  unsigned int  v;
+  if (unlikely (!stack.check_pop_uint (v)))
+    return false;
+  offset.set (v);
+  return true;
+}
+
+template <typename OpSet, typename Param>
+struct Interpreter {
+
+  inline void init ()
+  {
+    stack.init ();
+  }
+  
+  inline void fini ()
+  {
+    stack.fini ();
+  }
+
+  inline bool interpret (const ByteStr& str, Param& param)
+  {
+    param.init();
+
+    for (unsigned int i = 0; i < str.len; i++)
+    {
+      unsigned char byte = str[i];
+      if ((OpCode_shortint == byte) ||
+          (OpCode_OneByteIntFirst <= byte && OpCode_TwoByteNegInt3 >= byte))
+      {
+        if (unlikely (!process_intop (str, i, byte)))
+          return false;
+      } else {
+        if (byte == OpCode_escape) {
+          if (unlikely (!str.check_limit (i, 1)))
+            return false;
+          byte = Make_OpCode_ESC(str[++i]);
+        }
+
+        if (unlikely (!OpSet::process_op (str, i, byte, stack, param)))
+          return false;
+      }
+    }
+    return true;
+  }
+
+  inline bool process_intop (const ByteStr& str, unsigned int& offset, unsigned char byte)
+  {
+    switch (byte) {
+      case OpCode_TwoBytePosInt0: case OpCode_TwoBytePosInt1:
+      case OpCode_TwoBytePosInt2: case OpCode_TwoBytePosInt3:
+        if (unlikely (str.check_limit (offset, 2) || !stack.check_overflow (1)))
+          return false;
+        stack.push ((int16_t)((byte - OpCode_TwoBytePosInt0) * 256 + str[offset + 1] + 108));
+        break;
+      
+      case OpCode_TwoByteNegInt0: case OpCode_TwoByteNegInt1:
+      case OpCode_TwoByteNegInt2: case OpCode_TwoByteNegInt3:
+        if (unlikely (!str.check_limit (offset, 2) || !stack.check_overflow (1)))
+          return false;
+        stack.push ((int16_t)(-(byte - OpCode_TwoByteNegInt0) * 256 - str[offset + 1] - 108));
+        break;
+      
+      case OpCode_shortint: /* 3-byte integer */
+        if (unlikely (!str.check_limit (offset, 3) || !stack.check_overflow (1)))
+          return false;
+        stack.push ((int16_t)((str[offset + 1] << 8) | str[offset + 2]));
+        break;
+      
+      default:
+        /* 1-byte integer */
+        if (likely ((OpCode_OneByteIntFirst <= byte) && (byte <= OpCode_OneByteIntLast)) &&
+            likely (stack.check_overflow (1)))
+        {
+          stack.push ((int)byte - 139);
+        } else {
+          return false;
+        }
+        break;
+    }
+    return true;
+  }
+
+  protected:
+  Stack stack;
+};
+
+} /* namespace CFF */
+
+#endif /* HB_OT_CFF_COMMON_HH */
+
diff --git a/src/hb-ot-cff2-table.hh b/src/hb-ot-cff2-table.hh
new file mode 100644 (file)
index 0000000..2f49591
--- /dev/null
@@ -0,0 +1,386 @@
+/*
+ * 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-private.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')
+
+struct CFF2TopDictValues
+{
+  inline void init ()
+  {
+    charStringsOffset.set (0);
+    vstoreOffset.set (0);
+    FDArrayOffset.set (0);
+    FDSelectOffset.set (0);
+    FontMatrix[0] = FontMatrix[3] = 0.001f;
+    FontMatrix[1] = FontMatrix[2] = FontMatrix[4] = FontMatrix[5] = 0.0f;
+  }
+
+  LOffsetTo<CharStrings>    charStringsOffset;
+  LOffsetTo<VariationStore> vstoreOffset;
+  LOffsetTo<FDArray>        FDArrayOffset;
+  LOffsetTo<FDSelect>       FDSelectOffset;
+  float     FontMatrix[6];
+};
+
+struct CFF2TopDictOpSet
+{
+  static inline bool process_op (const ByteStr& str, unsigned int& offset, unsigned char byte, Stack& stack, CFF2TopDictValues& val)
+  {
+    switch (byte) {
+      case OpCode_CharStrings:
+        if (unlikely (!check_pop_offset (stack, val.charStringsOffset)))
+          return false;
+        break;
+      case OpCode_vstore:
+        if (unlikely (!check_pop_offset (stack, val.vstoreOffset)))
+          return false;
+        break;
+      case OpCode_FDArray:
+        if (unlikely (!check_pop_offset (stack, val.FDArrayOffset)))
+          return false;
+        break;
+      case OpCode_FDSelect:
+        if (unlikely (!check_pop_offset (stack, val.FDSelectOffset)))
+          return false;
+        break;
+      case OpCode_FontMatrix:
+        if (unlikely (!stack.check_underflow (6)))
+          return false;
+        for (int i = 0; i < 6; i++)
+          val.FontMatrix[i] = stack.pop ().to_real ();
+        break;
+      case OpCode_longint:  /* 5-byte integer */
+        if (unlikely (!str.check_limit (offset, 5) || !stack.check_overflow (1)))
+          return false;
+        stack.push ((int32_t)((str[offset + 1] << 24) | (str[offset + 2] << 16) | (str[offset + 3] << 8) | str[offset + 4]));
+        offset += 4;
+        break;
+      
+      case OpCode_BCD:  /* real number */
+        float v;
+        if (unlikely (stack.check_overflow (1) || !parse_bcd (str, offset, v)))
+          return false;
+        stack.push (v);
+        break;
+    
+      default:
+        /* XXX: invalid */
+        stack.clear ();
+        return false;
+    }
+    return true;
+  }
+};
+
+struct CFF2FontDictValues
+{
+  inline void init ()
+  {
+    privateDictSize = 0;
+    privateDictOffset.set (0);
+  }
+
+  unsigned int    privateDictSize;
+  OffsetTo<Dict>  privateDictOffset;
+};
+
+struct CFF2FontDictOpSet
+{
+  static inline bool process_op (const ByteStr& str, unsigned int& offset, unsigned char byte, Stack& stack, CFF2FontDictValues& val)
+  {
+    switch (byte) {
+      case OpCode_Private:
+        if (unlikely (!stack.check_pop_uint (val.privateDictSize)))
+          return false;
+        if (unlikely (!check_pop_offset (stack, val.privateDictOffset)))
+          return false;
+        break;
+      case OpCode_longint:  /* 5-byte integer */
+        if (unlikely (!str.check_limit (offset, 5) || !stack.check_overflow (1)))
+          return false;
+        stack.push ((int32_t)((str[offset + 1] << 24) | (str[offset + 2] << 16) || (str[offset + 3] << 8) || str[offset + 4]));
+        break;
+      case OpCode_BCD:  /* real number */
+        float v;
+        if (unlikely (stack.check_overflow (1) || !parse_bcd (str, offset, v)))
+          return false;
+        stack.push (v);
+        break;
+    
+      default:
+        /* XXX: invalid */
+        stack.clear ();
+        return false;
+    }
+    return true;
+  }
+};
+
+struct CFF2PrivateDictValues
+{
+  inline void init ()
+  {
+    languageGroup = 0;
+    expansionFactor = 0.06f;
+    vsIndex = 0;
+    subrsOffset.set (0);
+    blueScale = 0.039625f;
+    blueShift = 7.0f;
+    blueFuzz = 1.0f;
+    stdHW = UNSET_REAL_VALUE;
+    stdVW = UNSET_REAL_VALUE;
+    blueValues.init ();
+    otherBlues.init ();
+    familyBlues.init ();
+    familyOtherBlues.init ();
+    stemSnapH.init ();
+    stemSnapV.init ();
+  }
+
+  inline void fini ()
+  {
+    blueValues.fini ();
+    otherBlues.fini ();
+    familyBlues.fini ();
+    familyOtherBlues.fini ();
+    stemSnapH.fini ();
+    stemSnapV.fini ();
+  }
+
+  int       languageGroup;
+  float     expansionFactor;
+  int       vsIndex;
+  OffsetTo<Subrs>  subrsOffset;
+  float     blueScale;
+  float     blueShift;
+  float     blueFuzz;
+  float     stdHW;
+  float     stdVW;
+  hb_vector_t <float> blueValues;
+  hb_vector_t <float> otherBlues;
+  hb_vector_t <float> familyBlues;
+  hb_vector_t <float> familyOtherBlues;
+  hb_vector_t <float> stemSnapH;
+  hb_vector_t <float> stemSnapV;
+};
+
+struct CFF2PrivateDictOpSet
+{
+  static inline bool process_op (const ByteStr& str, unsigned int& offset, unsigned char byte, Stack& stack, CFF2PrivateDictValues& val)
+  {
+    switch (byte) {
+      case OpCode_BlueValues:
+        if (unlikely (!stack.check_pop_delta (val.blueValues, true)))
+          return false;
+        break;
+      case OpCode_OtherBlues:
+        if (unlikely (!stack.check_pop_delta (val.otherBlues, true)))
+          return false;
+        break;
+      case OpCode_FamilyBlues:
+        if (unlikely (!stack.check_pop_delta (val.familyBlues, true)))
+          return false;
+        break;
+      case OpCode_FamilyOtherBlues:
+        if (unlikely (!stack.check_pop_delta (val.familyOtherBlues, true)))
+          return false;
+        break;
+      case OpCode_StdHW:
+        if (unlikely (!stack.check_pop_real (val.stdHW)))
+          return false;
+        break;
+      case OpCode_StdVW:
+        if (unlikely (!stack.check_pop_real (val.stdVW)))
+          return false;
+        break;
+      case OpCode_BlueScale:
+        if (unlikely (!stack.check_pop_real (val.blueScale)))
+          return false;
+        break;
+      case OpCode_BlueShift:
+        if (unlikely (!stack.check_pop_real (val.blueShift)))
+          return false;
+        break;
+      case OpCode_BlueFuzz:
+        if (unlikely (!stack.check_pop_real (val.blueFuzz)))
+          return false;
+        break;
+      case OpCode_StemSnapH:
+        if (unlikely (!stack.check_pop_delta (val.stemSnapH)))
+          return false;
+        break;
+      case OpCode_StemSnapV:
+        if (unlikely (!stack.check_pop_delta (val.stemSnapV)))
+          return false;
+        break;
+      case OpCode_LanguageGroup:
+        if (unlikely (!stack.check_pop_int (val.languageGroup)))
+          return false;
+        break;
+      case OpCode_ExpansionFactor:
+        if (unlikely (!stack.check_pop_real (val.expansionFactor)))
+          return false;
+        break;
+      case OpCode_Subrs:
+        if (unlikely (!check_pop_offset (stack, val.subrsOffset)))
+          return false;
+        break;
+      case OpCode_blend:
+        // XXX: TODO
+        break;
+
+      default:
+        return false;
+    }
+    return true;
+  }
+};
+
+typedef Interpreter<CFF2TopDictOpSet, CFF2TopDictValues> CFF2TopDict_Interpreter;
+typedef Interpreter<CFF2FontDictOpSet, CFF2FontDictValues> CFF2FontDict_Interpreter;
+typedef Interpreter<CFF2PrivateDictOpSet, CFF2PrivateDictValues> CFF2PrivateDict_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) &&
+                  version.sanitize (c) &&
+                  likely (version.major == 2));
+  }
+
+  struct accelerator_t
+  {
+    inline void init (hb_face_t *face)
+    {
+      this->blob = OT::Sanitizer<OT::cff2>().sanitize (face->reference_table (HB_OT_TAG_cff2));
+      const OT::cff2 *cff2 = this->blob->as<OT::cff2> ();
+
+      if (cff2 == &Null(OT::cff2))
+      {
+        hb_blob_destroy (blob);
+        return;
+      }
+
+      { /* parse top dict */
+        ByteStr topDictStr (cff2 + cff2->topDict, cff2->topDictSize);
+        CFF2TopDict_Interpreter top_interp;
+        top_interp.init ();
+        top_interp.interpret (topDictStr, top);
+        top_interp.fini ();
+      }
+      
+      varStore = &OT::StructAtOffset<VariationStore>(cff2, top.vstoreOffset);
+      charStrings = &OT::StructAtOffset<CharStrings>(cff2, top.charStringsOffset);
+      fdArray = &OT::StructAtOffset<FDArray>(cff2, top.FDArrayOffset);
+      fdSelect = &OT::StructAtOffset<FDSelect>(cff2, top.FDSelectOffset);
+      
+      // XXX: sanitize above?
+      if ((charStrings == &Null(CharStrings)) ||
+          (fdArray == &Null(FDArray)))
+      {
+        hb_blob_destroy (blob);
+        return;
+      }
+
+      num_glyphs = charStrings->count;
+    }
+
+    inline void fini (void)
+    {
+      hb_blob_destroy (blob);
+    }
+
+    inline bool get_extents (hb_codepoint_t glyph,
+           hb_glyph_extents_t *extents) const
+    {
+      // XXX: TODO
+      if (glyph >= num_glyphs)
+        return false;
+      
+      return true;
+    }
+
+    private:
+    hb_blob_t               *blob;
+
+    CFF2TopDictValues       top;
+    const VariationStore    *varStore;
+    const CharStrings       *charStrings;
+    const FDArray           *fdArray;
+    const FDSelect          *fdSelect;
+
+    unsigned int            num_glyphs;
+  };
+
+  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;
+  }
+
+  protected:
+  FixedVersion<HBUINT8> version;    /* Version of CFF2 table. set to 0x0200u */
+  OffsetTo<Dict, HBUINT8> topDict;  /* headerSize = Offset to Top DICT. */
+  HBUINT16       topDictSize;       /* Top DICT size */
+
+  public:
+  DEFINE_SIZE_STATIC (5);
+};
+
+} /* namespace OT */
+
+#endif /* HB_OT_CFF2_TABLE_HH */
index 8310230..8605d11 100644 (file)
@@ -32,6 +32,7 @@
 
 #include "hb-ot-cmap-table.hh"
 #include "hb-ot-glyf-table.hh"
+#include "hb-ot-cff2-table.hh"
 #include "hb-ot-hmtx-table.hh"
 #include "hb-ot-kern-table.hh"
 #include "hb-ot-post-table.hh"
@@ -45,6 +46,7 @@ struct hb_ot_font_t
   OT::hmtx::accelerator_t h_metrics;
   OT::vmtx::accelerator_t v_metrics;
   OT::hb_lazy_loader_t<OT::glyf::accelerator_t> glyf;
+  OT::hb_lazy_loader_t<OT::cff2::accelerator_t> cff2;
   OT::hb_lazy_loader_t<OT::CBDT::accelerator_t> cbdt;
   OT::hb_lazy_loader_t<OT::post::accelerator_t> post;
   OT::hb_lazy_loader_t<OT::kern::accelerator_t> kern;
diff --git a/src/hb-subset-cff2.cc b/src/hb-subset-cff2.cc
new file mode 100644 (file)
index 0000000..7ec1546
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * 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-private.hh"
+#include "hb-ot-cff2-table.hh"
+#include "hb-set.h"
+#include "hb-subset-cff2.hh"
+#include "hb-subset-plan.hh"
+
+static bool
+_calculate_cff2_prime_size (const OT::cff2::accelerator_t &cff2,
+                                     hb_vector_t<hb_codepoint_t> &glyph_ids,
+                                     hb_bool_t drop_hints,
+                                     unsigned int *cff2_size /* OUT */,
+                                     hb_vector_t<unsigned int> *instruction_ranges /* OUT */)
+{
+  // XXX: TODO
+  *cff2_size = 0;
+  return true;
+}
+
+static bool
+_write_cff2_prime (hb_subset_plan_t              *plan,
+          const OT::cff2::accelerator_t &cff2,
+                            const char                    *cff2_data,
+                            hb_vector_t<unsigned int> &instruction_ranges,
+                            unsigned int                   cff2_prime_size,
+                            char                          *cff2_prime_data /* OUT */)
+{
+  // XXX: TODO
+  return true;
+}
+
+static bool
+_hb_subset_cff2 (const OT::cff2::accelerator_t  &cff2,
+                          const char                     *cff2_data,
+                          hb_subset_plan_t               *plan,
+                          hb_blob_t                     **cff2_prime /* OUT */)
+{
+  hb_vector_t<hb_codepoint_t> &glyphs_to_retain = plan->glyphs;
+
+  unsigned int cff2_prime_size;
+  hb_vector_t<unsigned int> instruction_ranges;
+  instruction_ranges.init();
+
+  if (unlikely (!_calculate_cff2_prime_size (cff2,
+                                                      glyphs_to_retain,
+                                                      plan->drop_hints,
+                                                      &cff2_prime_size,
+                                                      &instruction_ranges))) {
+    instruction_ranges.fini();
+    return false;
+  }
+
+  char *cff2_prime_data = (char *) calloc (1, cff2_prime_size);
+  if (unlikely (!_write_cff2_prime (plan, cff2, cff2_data,
+                                             instruction_ranges,
+                                             cff2_prime_size, cff2_prime_data))) {
+    free (cff2_prime_data);
+    instruction_ranges.fini();
+    return false;
+  }
+  instruction_ranges.fini();
+
+  *cff2_prime = hb_blob_create (cff2_prime_data,
+                                cff2_prime_size,
+                                HB_MEMORY_MODE_READONLY,
+                                cff2_prime_data,
+                                free);
+  // XXX: TODO
+  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       **cff2_prime /* OUT */)
+{
+  hb_blob_t *cff2_blob = OT::Sanitizer<OT::cff2>().sanitize (plan->source->reference_table (HB_OT_TAG_cff2));
+  const char *cff2_data = hb_blob_get_data(cff2_blob, nullptr);
+
+  OT::cff2::accelerator_t cff2;
+  cff2.init(plan->source);
+  bool result = _hb_subset_cff2 (cff2,
+                                 cff2_data,
+                                 plan,
+                                 cff2_prime);
+
+  hb_blob_destroy (cff2_blob);
+  cff2.fini();
+
+  return result;
+}
diff --git a/src/hb-subset-cff2.hh b/src/hb-subset-cff2.hh
new file mode 100644 (file)
index 0000000..f927070
--- /dev/null
@@ -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-private.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 */
index b97c763..050f159 100644 (file)
@@ -42,6 +42,7 @@
 #include "hb-ot-maxp-table.hh"
 #include "hb-ot-os2-table.hh"
 #include "hb-ot-post-table.hh"
+#include "hb-ot-cff2-table.hh"
 
 
 struct hb_subset_profile_t {
@@ -273,6 +274,9 @@ _subset_table (hb_subset_plan_t *plan,
     case HB_OT_TAG_post:
       result = _subset<const OT::post> (plan);
       break;
+    case HB_OT_TAG_cff2:
+      result = _subset<const OT::cff2> (plan);
+      break;
     default:
       hb_blob_t *source_table = hb_face_reference_table(plan->source, tag);
       if (likely (source_table))