diff --git a/src/Makefile.sources b/src/Makefile.sources index 0bc9e589e..4c0384127 100644 --- a/src/Makefile.sources +++ b/src/Makefile.sources @@ -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 index 000000000..fde100ad4 --- /dev/null +++ b/src/hb-ot-cff-common-private.hh @@ -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 +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(data_base (), offset_at (index) - 1); + return Null(Type); + } +}; + +struct UnsizedByteStr : UnsizedArrayOf {}; + +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 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& 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 +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 +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 index 000000000..2f495915b --- /dev/null +++ b/src/hb-ot-cff2-table.hh @@ -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 charStringsOffset; + LOffsetTo vstoreOffset; + LOffsetTo FDArrayOffset; + LOffsetTo 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 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 subrsOffset; + float blueScale; + float blueShift; + float blueFuzz; + float stdHW; + float stdVW; + hb_vector_t blueValues; + hb_vector_t otherBlues; + hb_vector_t familyBlues; + hb_vector_t familyOtherBlues; + hb_vector_t stemSnapH; + hb_vector_t 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 CFF2TopDict_Interpreter; +typedef Interpreter CFF2FontDict_Interpreter; +typedef Interpreter 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().sanitize (face->reference_table (HB_OT_TAG_cff2)); + const OT::cff2 *cff2 = this->blob->as (); + + 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(cff2, top.vstoreOffset); + charStrings = &OT::StructAtOffset(cff2, top.charStringsOffset); + fdArray = &OT::StructAtOffset(cff2, top.FDArrayOffset); + fdSelect = &OT::StructAtOffset(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 version; /* Version of CFF2 table. set to 0x0200u */ + OffsetTo 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 831023032..8605d111c 100644 --- a/src/hb-ot-font.cc +++ b/src/hb-ot-font.cc @@ -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 glyf; + OT::hb_lazy_loader_t cff2; OT::hb_lazy_loader_t cbdt; OT::hb_lazy_loader_t post; OT::hb_lazy_loader_t kern; diff --git a/src/hb-subset-cff2.cc b/src/hb-subset-cff2.cc new file mode 100644 index 000000000..7ec15466e --- /dev/null +++ b/src/hb-subset-cff2.cc @@ -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 &glyph_ids, + hb_bool_t drop_hints, + unsigned int *cff2_size /* OUT */, + hb_vector_t *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 &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 &glyphs_to_retain = plan->glyphs; + + unsigned int cff2_prime_size; + hb_vector_t 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().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 index 000000000..f927070da --- /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-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 */ diff --git a/src/hb-subset.cc b/src/hb-subset.cc index b97c7633e..050f15915 100644 --- a/src/hb-subset.cc +++ b/src/hb-subset.cc @@ -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 (plan); break; + case HB_OT_TAG_cff2: + result = _subset (plan); + break; default: hb_blob_t *source_table = hb_face_reference_table(plan->source, tag); if (likely (source_table))