From 64c541226457907ec1801a5766050ee64feb67e8 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Fri, 10 Aug 2018 11:07:07 -0700 Subject: [PATCH] Added CFF support Added sources hb-ot-cff-table.hh, hb-subset-cff.cc & hh Templatized Index because CFF uses 16-bit count while CFF2 uses 32-bit Misc code cleanup & bug fixes --- src/Makefile.sources | 9 +- src/hb-ot-cff-common-private.hh | 477 +++++++++------ src/hb-ot-cff-table.hh | 919 ++++++++++++++++++++++++++++ src/hb-ot-cff2-table.hh | 235 +++---- src/hb-ot-font.cc | 1 + src/hb-subset-cff-common-private.cc | 6 +- src/hb-subset-cff-common-private.hh | 4 +- src/hb-subset-cff.cc | 560 +++++++++++++++++ src/hb-subset-cff.hh | 38 ++ src/hb-subset-cff2.cc | 104 ++-- src/hb-subset.cc | 4 + 11 files changed, 1991 insertions(+), 366 deletions(-) create mode 100644 src/hb-ot-cff-table.hh create mode 100644 src/hb-subset-cff.cc create mode 100644 src/hb-subset-cff.hh diff --git a/src/Makefile.sources b/src/Makefile.sources index 4346c836f..7e45f2c5f 100644 --- a/src/Makefile.sources +++ b/src/Makefile.sources @@ -26,7 +26,8 @@ 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-cff-table.hh \ + hb-ot-cff2-table.hh \ hb-ot-hdmx-table.hh \ hb-ot-head-table.hh \ hb-ot-hhea-table.hh \ @@ -210,7 +211,8 @@ HB_SUBSET_sources = \ hb-static.cc \ hb-subset.cc \ hb-subset-glyf.cc \ - hb-subset-cff2.cc \ + hb-subset-cff.cc \ + hb-subset-cff2.cc \ hb-subset-cff-common-private.cc \ hb-subset-input.cc \ hb-subset-plan.cc \ @@ -219,7 +221,8 @@ HB_SUBSET_sources = \ HB_SUBSET_headers = \ hb-subset.h \ hb-subset-glyf.hh \ - hb-subset-cff2.hh \ + hb-subset-cff.hh \ + hb-subset-cff2.hh \ hb-subset-cff-common-private.hh \ hb-subset-plan.hh \ hb-subset-private.hh \ diff --git a/src/hb-ot-cff-common-private.hh b/src/hb-ot-cff-common-private.hh index 3690a8588..3e3ac36fc 100644 --- a/src/hb-ot-cff-common-private.hh +++ b/src/hb-ot-cff-common-private.hh @@ -34,6 +34,11 @@ namespace CFF { using namespace OT; +/* utility macro */ +template +static inline const Type& StructAtOffsetOrNull(const void *P, unsigned int offset) +{ return offset? (* reinterpret_cast ((const char *) P + offset)): Null(Type); } + const float UNSET_REAL_VALUE = -1.0f; enum OpCode { @@ -153,12 +158,6 @@ protected: } u; }; -/* pair of table offset and length */ -struct offset_size_pair { - unsigned int offset; - unsigned int size; -}; - /* byte string */ struct UnsizedByteStr : UnsizedArrayOf { @@ -209,6 +208,18 @@ struct ByteStr return (*str)[i]; } + inline bool serialize (hb_serialize_context_t *c, const ByteStr &src) + { + TRACE_SANITIZE (this); + HBUINT8 *dest = c->allocate_size (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); } @@ -229,6 +240,7 @@ inline unsigned int calcOffSize(unsigned int offset) } /* CFF INDEX */ +template struct Index { inline bool sanitize (hb_sanitize_context_t *c) const @@ -259,35 +271,34 @@ struct Index } inline bool serialize (hb_serialize_context_t *c, - unsigned int offSize, - const hb_vector_t &bytesArray) + unsigned int offSize_, + const hb_vector_t &byteArray) { TRACE_SERIALIZE (this); - /* serialize Index header */ if (unlikely (!c->extend_min (*this))) return_trace (false); - this->count.set (bytesArray.len); - this->offSize.set (offSize); - if (!unlikely (c->allocate_size (offSize * (bytesArray.len + 1)))) + this->count.set (byteArray.len); + this->offSize.set (offSize_); + if (!unlikely (c->allocate_size (offSize_ * (byteArray.len + 1)))) return_trace (false); /* serialize indices */ unsigned int offset = 1; unsigned int i = 0; - for (; i < bytesArray.len; i++) + for (; i < byteArray.len; i++) { set_offset_at (i, offset); - offset += bytesArray[i].len; + offset += byteArray[i].get_size (); } set_offset_at (i, offset); /* serialize data */ - for (unsigned int i = 0; i < bytesArray.len; i++) + for (unsigned int i = 0; i < byteArray.len; i++) { - HBUINT8 *dest = c->allocate_size (bytesArray[i].len); - if (dest == nullptr) + ByteStr *dest = c->start_embed (); + if (unlikely (dest == nullptr || + !dest->serialize (c, byteArray[i]))) return_trace (false); - memcpy (dest, &bytesArray[i].str[0], bytesArray[i].len); } return_trace (true); } @@ -319,12 +330,12 @@ struct Index { return offset_at (index + 1) - offset_at (index); } inline const char *data_base (void) const - { return (const char *)this + 5 + offset_array_size (); } + { return (const char *)this + min_size + offset_array_size (); } inline unsigned int data_size (void) const { return HBINT8::static_size; }; - inline ByteStr operator [] (unsigned int index) const + 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)); @@ -358,23 +369,203 @@ struct Index } public: - HBUINT32 count; /* Number of object data. Note there are (count+1) offsets */ + 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 (5, offsets); + DEFINE_SIZE_ARRAY (COUNT::static_size + HBUINT8::static_size, offsets); }; -template -struct IndexOf : Index +template +struct IndexOf : Index { - inline ByteStr operator [] (unsigned int index) const + inline const ByteStr operator [] (unsigned int index) const { - if (likely (index < count)) - return ByteStr (data_base () + offset_at (index) - 1, length_at (index)); + if (likely (index < Index::count)) + return ByteStr (Index::data_base () + Index::offset_at (index) - 1, Index::length_at (index)); return Null(ByteStr); } + + template + inline bool serialize (hb_serialize_context_t *c, + unsigned int offSize_, + const hb_vector_t &dataArray, + const hb_vector_t &dataSizeArray, + const PARAM1 ¶m1, + const PARAM2 ¶m2) + { + TRACE_SERIALIZE (this); + /* serialize Index header */ + if (unlikely (!c->extend_min (*this))) return_trace (false); + this->count.set (dataArray.len); + this->offSize.set (offSize_); + if (!unlikely (c->allocate_size (offSize_ * (dataArray.len + 1)))) + return_trace (false); + + /* serialize indices */ + unsigned int offset = 1; + unsigned int i = 0; + for (; i < dataArray.len; i++) + { + Index::set_offset_at (i, offset); + offset += dataSizeArray[i]; + } + Index::set_offset_at (i, offset); + + /* serialize data */ + for (unsigned int i = 0; i < dataArray.len; i++) + { + TYPE *dest = c->start_embed (); + if (unlikely (dest == nullptr || + !dest->serialize (c, dataArray[i], param1, param2))) + return_trace (false); + } + return_trace (true); + } + + /* in parallel to above */ + template + inline static unsigned int calculate_serialized_size (unsigned int &offSize_ /* OUT */, + const hb_vector_t &dataArray, + hb_vector_t &dataSizeArray, /* OUT */ + const PARAM ¶m) + { + /* determine offset size */ + unsigned int totalDataSize = 0; + for (unsigned int i = 0; i < dataArray.len; i++) + { + unsigned int dataSize = TYPE::calculate_serialized_size (dataArray[i], param); + dataSizeArray[i] = dataSize; + totalDataSize += dataSize; + } + offSize_ = calcOffSize (totalDataSize); + + return Index::calculate_serialized_size (offSize_, dataArray.len, totalDataSize); + } +}; + +inline float parse_bcd (const ByteStr& str, unsigned int& offset, float& v) +{ + // XXX: TODO + v = 0; + for (;;) { + if (++offset >= str.len) + return false; + unsigned char byte = str[offset]; + if (((byte & 0xF0) == 0xF0) || ((byte & 0x0F) == 0x0F)) + break; + } + return true; +} + +/* operand stack */ +struct Stack +{ + inline void init (void) { size = 0; } + inline void fini (void) { } + + inline void push (const Number &v) + { + if (likely (size < kSizeLimit)) + numbers[size++] = v; + } + + inline void push_int (int v) + { + Number n; + n.set_int (v); + push (n); + } + + inline void push_real (float v) + { + Number n; + n.set_real (v); + push (n); + } + + inline const Number& pop (void) + { + if (likely (size > 0)) + return numbers[--size]; + else + return Null(Number); + } + + inline bool check_push (void) + { + if (likely (size < kSizeLimit)) { + size++; + return true; + } else + return false; + } + + inline bool check_pop (void) + { + if (likely (0 < size)) { + size--; + return true; + } else + return false; + } + + inline bool check_pop_num (Number& n) + { + if (unlikely (!this->check_underflow (1))) + return false; + n = this->pop (); + return true; + } + + inline bool check_pop_uint (unsigned int& v) + { + uint32_t i; + if (unlikely (!this->check_underflow (1))) + return false; + i = this->pop ().to_int (); + if (unlikely (i < 0)) + return false; + v = (uint32_t)i; + return true; + } + + inline bool check_pop_delta (hb_vector_t& vec, bool even=false) + { + if (even && unlikely ((this->size & 1) != 0)) + return false; + + float val = 0.0f; + for (unsigned int i = 0; i < size; i++) { + val += numbers[i].to_real (); + Number *n = vec.push (); + n->set_real (val); + } + return true; + } + + inline bool push_longint_from_str (const ByteStr& str, unsigned int& offset) + { + if (unlikely (!str.check_limit (offset, 5) || !check_overflow (1))) + return false; + push_int ((int32_t)*(const HBUINT32*)&str[offset + 1]); + offset += 4; + return true; + } + + inline void clear (void) { size = 0; } + + inline bool check_overflow (unsigned int count) const { return (count <= kSizeLimit) && (count + size <= kSizeLimit); } + inline bool check_underflow (unsigned int count) const { return (count <= size); } + + inline unsigned int get_size (void) const { return size; } + inline bool is_empty (void) const { return size == 0; } + + static const unsigned int kSizeLimit = 513; + + unsigned int size; + Number numbers[kSizeLimit]; }; /* an operator prefixed by its operands in a byte string */ @@ -438,21 +629,6 @@ struct DictValues hb_vector_t values; }; -/* 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 (opstr.str.len); - if (unlikely (d == nullptr)) return_trace (false); - memcpy (d, &opstr.str.str[0], opstr.str.len); - return_trace (true); - } -}; - /* Top Dict, Font Dict, Private Dict */ struct Dict : UnsizedByteStr { @@ -516,22 +692,31 @@ struct TopDict : Dict {}; struct FontDict : Dict {}; struct PrivateDict : Dict {}; -struct FDArray : IndexOf +struct TableInfo +{ + void init (void) { offset = size = 0; } + + unsigned int offset; + unsigned int size; +}; + +template +struct FDArray : IndexOf { template inline bool serialize (hb_serialize_context_t *c, - unsigned int offSize, + unsigned int offSize_, const hb_vector_t &fontDicts, unsigned int fdCount, const hb_vector_t &fdmap, OP_SERIALIZER& opszr, - const hb_vector_t &privatePairs) + const hb_vector_t &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 (offSize * (fdCount + 1)))) + this->offSize.set (offSize_); + if (!unlikely (c->allocate_size (offSize_ * (fdCount + 1)))) return_trace (false); /* serialize font dict offsets */ @@ -540,17 +725,17 @@ struct FDArray : IndexOf for (unsigned i = 0; i < fontDicts.len; i++) if (!fdmap.len || fdmap[i] != HB_SET_VALUE_INVALID) { - set_offset_at (fid++, offset); + IndexOf::set_offset_at (fid++, offset); offset += FontDict::calculate_serialized_size (fontDicts[i], opszr); } - set_offset_at (fid, offset); + IndexOf::set_offset_at (fid, offset); /* serialize font dicts */ for (unsigned int i = 0; i < fontDicts.len; i++) if (fdmap[i] != HB_SET_VALUE_INVALID) { FontDict *dict = c->start_embed (); - if (unlikely (!dict->serialize (c, fontDicts[i], opszr, privatePairs[i]))) + if (unlikely (!dict->serialize (c, fontDicts[i], opszr, privateInfos[i]))) return_trace (false); } return_trace (true); @@ -558,7 +743,7 @@ struct FDArray : IndexOf /* in parallel to above */ template - inline static unsigned int calculate_serialized_size (unsigned int &offSize /* OUT */, + inline static unsigned int calculate_serialized_size (unsigned int &offSize_ /* OUT */, const hb_vector_t &fontDicts, unsigned int fdCount, const hb_vector_t &fdmap, @@ -569,8 +754,8 @@ struct FDArray : IndexOf if (!fdmap.len || fdmap[i] != HB_SET_VALUE_INVALID) dictsSize += FontDict::calculate_serialized_size (fontDicts[i], opszr); - offSize = calcOffSize (dictsSize + 1); - return Index::calculate_serialized_size (offSize, fdCount, dictsSize); + offSize_ = calcOffSize (dictsSize + 1); + return Index::calculate_serialized_size (offSize_, fdCount, dictsSize); } }; @@ -641,9 +826,9 @@ struct FDSelect3_4 { inline hb_codepoint_t get_fd (hb_codepoint_t glyph) const { - for (unsigned int i = 0; i < nRanges; i++) - if (glyph < ranges[i + 1].first) - return (hb_codepoint_t)ranges[i].fd; + for (unsigned int i = 1; i < nRanges; i++) + if (glyph < ranges[i].first) + return (hb_codepoint_t)ranges[i - 1].fd; assert (false); } @@ -709,132 +894,90 @@ struct FDSelect { FDSelect3 format3; } u; - DEFINE_SIZE_MIN (2); + DEFINE_SIZE_MIN (1); }; -typedef Index CharStrings; -typedef Index Subrs; - -inline float parse_bcd (const ByteStr& str, unsigned int& offset, float& v) +struct TopDictValues : DictValues { - // 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 Stack -{ - inline void init (void) { size = 0; } - inline void fini (void) { } - - inline void push (const Number &v) + inline void init (void) { - if (likely (size < kSizeLimit)) - numbers[size++] = v; + DictValues::init (); + charStringsOffset = 0; + FDArrayOffset = 0; } - inline void push_int (int v) + inline void fini (void) { - Number n; - n.set_int (v); - push (n); + DictValues::fini (); } - inline void push_real (float v) + inline unsigned int calculate_serialized_op_size (const OpStr& opstr) const { - Number n; - n.set_real (v); - push (n); - } - - inline const Number& pop (void) - { - if (likely (size > 0)) - return numbers[--size]; - else - return Null(Number); - } + switch (opstr.op) + { + case OpCode_CharStrings: + case OpCode_FDArray: + return OpCode_Size (OpCode_longint) + 4 + OpCode_Size (opstr.op); - inline bool check_push (void) - { - if (likely (size < kSizeLimit)) { - size++; - return true; - } else - return false; + default: + return opstr.str.len; + } } - inline bool check_pop (void) - { - if (likely (0 < size)) { - size--; - return true; - } else - return false; - } + unsigned int charStringsOffset; + unsigned int FDArrayOffset; +}; - inline bool check_pop_num (Number& n) +struct TopDictOpSet +{ + static inline bool process_op (const ByteStr& str, unsigned int& offset, OpCode op, Stack& stack, TopDictValues& dictval) { - if (unlikely (!this->check_underflow (1))) - return false; - n = this->pop (); - return true; - } + switch (op) { + case OpCode_CharStrings: + if (unlikely (!stack.check_pop_uint (dictval.charStringsOffset))) + return false; + stack.clear (); + break; + case OpCode_FDArray: + if (unlikely (!stack.check_pop_uint (dictval.FDArrayOffset))) + return false; + stack.clear (); + break; + case OpCode_longint: /* 5-byte integer */ + return stack.push_longint_from_str (str, offset); + + case OpCode_BCD: /* real number */ + float v; + if (unlikely (stack.check_overflow (1) || !parse_bcd (str, offset, v))) + return false; + stack.push_real (v); + return true; + + default: + /* XXX: invalid */ + stack.clear (); + return false; + } - inline bool check_pop_uint (unsigned int& v) - { - uint32_t i; - if (unlikely (!this->check_underflow (1))) - return false; - i = this->pop ().to_int (); - if (unlikely (i <= 0)) - return false; - v = (uint32_t)i; return true; } +}; - inline bool check_pop_delta (hb_vector_t& vec, bool even=false) +/* base of OP_SERIALIZER */ +struct OpSerializer +{ + protected: + inline bool copy_opstr (hb_serialize_context_t *c, const OpStr& opstr) const { - if (even && unlikely ((this->size & 1) != 0)) - return false; + TRACE_SERIALIZE (this); - float val = 0.0f; - for (unsigned int i = 0; i < size; i++) { - val += numbers[i].to_real (); - Number *n = vec.push (); - n->set_real (val); - } - return true; + HBUINT8 *d = c->allocate_size (opstr.str.len); + if (unlikely (d == nullptr)) return_trace (false); + memcpy (d, &opstr.str.str[0], opstr.str.len); + return_trace (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 { @@ -919,24 +1062,6 @@ struct Interpreter { Stack stack; }; -/* used by subsettter */ -struct SubTableOffsets { - inline SubTableOffsets (void) - { - memset (this, 0, sizeof(*this)); - } - - unsigned int topDictSize; - unsigned int varStoreOffset; - unsigned int FDSelectOffset; - unsigned int FDSelectSize; - unsigned int FDArrayOffset; - unsigned int FDArrayOffSize; - unsigned int charStringsOffset; - unsigned int charStringsOffSize; - unsigned int privateDictsOffset; -}; - } /* namespace CFF */ #endif /* HB_OT_CFF_COMMON_PRIVATE_HH */ diff --git a/src/hb-ot-cff-table.hh b/src/hb-ot-cff-table.hh new file mode 100644 index 000000000..fb5f9113c --- /dev/null +++ b/src/hb-ot-cff-table.hh @@ -0,0 +1,919 @@ +/* + * 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_TABLE_HH +#define HB_OT_CFF_TABLE_HH + +#include "hb-ot-cff-common-private.hh" +#include "hb-subset-cff.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_cff HB_TAG('C','F','F',' ') + +typedef Index CFFIndex; +template struct CFFIndexOf : IndexOf {}; + +typedef Index CFFIndex; +typedef CFFIndex CFFCharStrings; +typedef FDArray CFFFDArray; +typedef CFFIndex CFFSubrs; + +struct CFFFDSelect : 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 bool get_code (hb_codepoint_t glyph, hb_codepoint_t &code) const + { + if (glyph < nCodes) + { + code = (hb_codepoint_t)codes[glyph]; + return true; + } + else + return false; + } + + 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 bool get_code (hb_codepoint_t glyph, hb_codepoint_t &code) const + { + for (unsigned int i = 0; i < nRanges; i++) + { + if (glyph <= ranges[i].nLeft) + { + code = (hb_codepoint_t)ranges[i].first + glyph; + return true; + } + glyph -= (ranges[i].nLeft + 1); + } + + return false; + } + + HBUINT8 nRanges; + Encoding1_Range ranges[VAR]; + + DEFINE_SIZE_ARRAY (1, ranges); +}; + +struct EncSupplement { + 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 CFFSuppEncData { + 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 bool get_code (hb_codepoint_t glyph, hb_codepoint_t &code) const + { + for (unsigned int i = 0; i < nSups; i++) + if (glyph == supps[i].glyph) + { + code = supps[i].code; + return true; + } + + return false; + } + + inline unsigned int get_size (void) const + { return HBUINT8::static_size + EncSupplement::static_size * nSups; } + + HBUINT8 nSups; + EncSupplement supps[VAR]; + + DEFINE_SIZE_ARRAY (1, supps); +}; + +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)); + } + + inline bool serialize (hb_serialize_context_t *c, const Encoding &src, unsigned int num_glyphs) + { + TRACE_SERIALIZE (this); + unsigned int size = src.get_size (); + Encoding *dest = c->allocate_size (size); + if (unlikely (dest == nullptr)) return_trace (false); + memcpy (dest, &src, size); + return_trace (true); + } + + inline unsigned int calculate_serialized_size (void) const + { return get_size (); } // XXX: TODO + + inline unsigned int get_size (void) const + { + unsigned int size = min_size; + if ((format & 0x7F) == 0) + size += u.format0.get_size (); + else + size += u.format1.get_size (); + if ((format & 0x80) != 0) + size += suppEncData ().get_size (); + return size; + } + + inline bool get_code (hb_codepoint_t glyph, hb_codepoint_t &code) const + { + // XXX: TODO + return false; + } + + inline const CFFSuppEncData &suppEncData (void) const + { + if ((format & 0x7F) == 0) + return StructAfter (u.format0.codes[u.format0.nCodes-1]); + else + return StructAfter (u.format1.ranges[u.format1.nRanges-1]); + } + + HBUINT8 format; + union { + Encoding0 format0; + Encoding1 format1; + } u; + /* CFFSuppEncData 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 unsigned int get_sid (hb_codepoint_t glyph) + { + 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 +struct Charset_Range { + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this)); + } + + HBUINT8 first; + TYPE nLeft; + + DEFINE_SIZE_STATIC (1 + TYPE::static_size); +}; + +template +struct Charset1_2 { + 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 bool get_sid (hb_codepoint_t glyph, unsigned int &sid) const + { + for (unsigned int i = 0; i < nRanges; i++) + { + if (glyph <= ranges[i].nLeft) + { + sid = (hb_codepoint_t)ranges[i].first + glyph; + return true; + } + glyph -= (ranges[i].nLeft + 1); + } + + return false; + } + + inline unsigned int get_size (void) const + { return HBUINT8::static_size + Charset_Range::static_size * nRanges; } + + HBUINT8 nRanges; + Charset_Range ranges[VAR]; + + DEFINE_SIZE_ARRAY (1, ranges); +}; + +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)); + else if (likely (format == 2)) + return_trace (u.format2.sanitize (c)); + else + return_trace (false); + } + + 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 (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); } // XXX: TODO + + 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 (); + else + size += u.format2.get_size (); + return size; + } + + inline bool get_sid (hb_codepoint_t glyph, unsigned int &sid) const + { + // XXX: TODO + return false; + } + + HBUINT8 format; + union { + Charset0 format0; + Charset1_2 format1; + Charset1_2 format2; + } u; + + DEFINE_SIZE_MIN (1); +}; + +struct CFFTopDictValues : TopDictValues +{ + inline void init (void) + { + TopDictValues::init (); + + ros[0] = ros[1] = ros[2] = 0; + cidCount = 8720; + EncodingOffset = 0; + CharsetOffset = 0; + FDSelectOffset = 0; + privateDictInfo.init (); + } + + inline void fini (void) + { + TopDictValues::fini (); + } + + inline bool is_CID (void) const + { return ros[0] != 0; } + + inline unsigned int calculate_serialized_size (void) const + { + unsigned int size = 0; + for (unsigned int i = 0; i < values.len; i++) + { + OpCode op = values[i].op; + switch (op) + { + case OpCode_FDSelect: + size += OpCode_Size (OpCode_longint) + 4 + OpCode_Size (op); + break; + default: + size += TopDictValues::calculate_serialized_op_size (values[i]); + break; + } + } + return size; + } + + unsigned int ros[3]; /* registry, ordering, supplement */ + unsigned int cidCount; + + enum EncodingID { StandardEncoding = 0, ExpertEncoding = 1 }; + unsigned int EncodingOffset; + unsigned int CharsetOffset; + unsigned int FDSelectOffset; + TableInfo privateDictInfo; +}; + +struct CFFTopDictOpSet : TopDictOpSet +{ + static inline bool process_op (const ByteStr& str, unsigned int& offset, + OpCode op, Stack& stack, CFFTopDictValues& dictval) + { + + switch (op) { + case OpCode_version: + case OpCode_Notice: + case OpCode_Copyright: + case OpCode_FullName: + case OpCode_FamilyName: + case OpCode_Weight: + 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_PostScript: + case OpCode_BaseFontName: + case OpCode_CIDFontVersion: + case OpCode_CIDFontRevision: + case OpCode_CIDFontType: + case OpCode_UIDBase: + case OpCode_FontBBox: + case OpCode_XUID: + case OpCode_BaseFontBlend: + stack.clear (); + break; + + case OpCode_CIDCount: + if (unlikely (!stack.check_pop_uint (dictval.cidCount))) + return false; + stack.clear (); + break; + + case OpCode_ROS: + if (unlikely (!stack.check_pop_uint (dictval.ros[2]) || + !stack.check_pop_uint (dictval.ros[1]) || + !stack.check_pop_uint (dictval.ros[0]))) + return false; + stack.clear (); + break; + + case OpCode_Encoding: + if (unlikely (!stack.check_pop_uint (dictval.EncodingOffset))) + return false; + stack.clear (); + break; + + case OpCode_charset: + if (unlikely (!stack.check_pop_uint (dictval.CharsetOffset))) + return false; + stack.clear (); + break; + + case OpCode_FDSelect: + if (unlikely (!stack.check_pop_uint (dictval.FDSelectOffset))) + return false; + stack.clear (); + break; + + case OpCode_Private: + if (unlikely (!stack.check_pop_uint (dictval.privateDictInfo.offset))) + return false; + if (unlikely (!stack.check_pop_uint (dictval.privateDictInfo.size))) + return false; + stack.clear (); + break; + + default: + if (unlikely (!TopDictOpSet::process_op (str, offset, op, stack, dictval))) + return false; + /* Record this operand below if stack is empty, otherwise done */ + if (!stack.is_empty ()) return true; + break; + } + + dictval.pushVal (op, str, offset + 1); + return true; + } +}; + +struct CFFFontDictValues : DictValues +{ + inline void init (void) + { + DictValues::init (); + privateDictInfo.init (); + } + + inline void fini (void) + { + DictValues::fini (); + } + + TableInfo privateDictInfo; +}; + +struct CFFFontDictOpSet +{ + static inline bool process_op (const ByteStr& str, unsigned int& offset, + OpCode op, Stack& stack, CFFFontDictValues& dictval) + { + switch (op) { + case OpCode_FontName: + case OpCode_FontMatrix: + case OpCode_PaintType: + stack.clear (); + break; + case OpCode_Private: + if (unlikely (!stack.check_pop_uint (dictval.privateDictInfo.offset))) + return false; + if (unlikely (!stack.check_pop_uint (dictval.privateDictInfo.size))) + return false; + stack.clear (); + break; + case OpCode_longint: /* 5-byte integer */ + return stack.push_longint_from_str (str, offset); + case OpCode_BCD: /* real number */ + float v; + if (unlikely (stack.check_overflow (1) || !parse_bcd (str, offset, v))) + return false; + stack.push_real (v); + return true; + + default: + /* XXX: invalid */ + stack.clear (); + return false; + } + + dictval.pushVal (op, str, offset + 1); + return true; + } +}; + +template +struct CFFPrivateDictValues_Base : DictValues +{ + inline void init (void) + { + DictValues::init (); + subrsOffset = 0; + localSubrs = &Null(CFFSubrs); + } + + inline void fini (void) + { + DictValues::fini (); + } + + inline unsigned int calculate_serialized_size (void) const + { + unsigned int size = 0; + for (unsigned int i = 0; i < DictValues::values.len; i++) + if (DictValues::values[i].op == OpCode_Subrs) + size += OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs); + else + size += DictValues::values[i].str.len; + return size; + } + + unsigned int subrsOffset; + const CFFSubrs *localSubrs; +}; + +typedef CFFPrivateDictValues_Base CFFPrivateDictValues_Subset; +typedef CFFPrivateDictValues_Base CFFPrivateDictValues; + +struct CFFPrivateDictOpSet +{ + static inline bool process_op (const ByteStr& str, unsigned int& offset, + OpCode op, Stack& stack, CFFPrivateDictValues& dictval) + { + DictVal 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 (!stack.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 (!stack.check_pop_num (val.single_val))) + return false; + stack.clear (); + break; + case OpCode_Subrs: + if (unlikely (!stack.check_pop_uint (dictval.subrsOffset))) + return false; + stack.clear (); + break; + case OpCode_longint: /* 5-byte integer */ + return stack.push_longint_from_str (str, offset); + case OpCode_BCD: /* real number */ + float v; + if (unlikely (!stack.check_overflow (1) || !parse_bcd (str, offset, v))) + return false; + stack.push_real (v); + return true; + + default: + return false; + } + + dictval.pushVal (op, str, offset + 1, val); + return true; + } +}; + +struct CFFPrivateDictOpSet_Subset +{ + static inline bool process_op (const ByteStr& str, unsigned int& offset, + OpCode op, Stack& stack, CFFPrivateDictValues_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: + stack.clear (); + break; + + case OpCode_BCD: + { + float v; + return parse_bcd (str, offset, v); + } + + case OpCode_Subrs: + if (unlikely (!stack.check_pop_uint (dictval.subrsOffset))) + return false; + stack.clear (); + break; + case OpCode_longint: /* 5-byte integer */ + return stack.push_longint_from_str (str, offset); + + default: + return false; + } + + dictval.pushVal (op, str, offset + 1); + return true; + } +}; + +typedef Interpreter CFFTopDict_Interpreter; +typedef Interpreter CFFFontDict_Interpreter; +typedef Interpreter CFFPrivateDict_Interpreter; + +typedef CFFIndex NameIndex; +typedef CFFIndexOf TopDictIndex; +typedef CFFIndex StringIndex; + +}; /* namespace CFF */ + +namespace OT { + +using namespace CFF; + +struct cff +{ + static const hb_tag_t tableTag = HB_OT_TAG_cff; + + inline bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + likely (version.major == 1)); + } + + template + struct accelerator_templ_t + { + inline void init (hb_face_t *face) + { + topDicts.init (); + topDicts.resize (1); + topDicts[0].init (); + fontDicts.init (); + privateDicts.init (); + + this->blob = sc.reference_table (face); + + /* setup for run-time santization */ + sc.init (this->blob); + sc.start_processing (); + + const OT::cff *cff = this->blob->template as (); + + if (cff == &Null(OT::cff)) + { fini (); return; } + + nameIndex = &cff->nameIndex (cff); + if ((nameIndex == &Null (NameIndex)) || !nameIndex->sanitize (&sc)) + { fini (); return; } + + topDictIndex = &StructAtOffset (nameIndex, nameIndex->get_size ()); + if ((topDictIndex == &Null (TopDictIndex)) || !topDictIndex->sanitize (&sc) || (topDictIndex->count == 0)) + { fini (); return; } + + { /* parse top dict */ + const ByteStr topDictStr = (*topDictIndex)[0]; + CFFTopDict_Interpreter top_interp; + if (unlikely (!topDictStr.sanitize (&sc) || + !top_interp.interpret (topDictStr, topDicts[0]))) + { fini (); return; } + } + + encoding = &Null(Encoding); + charset = &StructAtOffsetOrNull (cff, topDicts[0].CharsetOffset); + + fdCount = 1; + if (is_CID ()) + { + fdArray = &StructAtOffsetOrNull (cff, topDicts[0].FDArrayOffset); + fdSelect = &StructAtOffsetOrNull (cff, topDicts[0].FDSelectOffset); + if (unlikely ((fdArray == &Null(CFFFDArray)) || !fdArray->sanitize (&sc) || + (fdSelect == &Null(CFFFDSelect)) || !fdSelect->sanitize (&sc, fdArray->count))) + { fini (); return; } + + fdCount = fdArray->count; + } + else + { + fdArray = &Null(CFFFDArray); + fdSelect = &Null(CFFFDSelect); + if (topDicts[0].EncodingOffset != CFFTopDictValues::StandardEncoding && + topDicts[0].EncodingOffset != CFFTopDictValues::ExpertEncoding) + { + encoding = &StructAtOffsetOrNull (cff, topDicts[0].EncodingOffset); + if ((encoding == &Null (Encoding)) || !encoding->sanitize (&sc)) + { fini (); return; } + } + } + + stringIndex = &StructAtOffset (topDictIndex, topDictIndex->get_size ()); + if ((stringIndex == &Null (StringIndex)) || !stringIndex->sanitize (&sc)) + { fini (); return; } + + globalSubrs = &StructAtOffset (stringIndex, stringIndex->get_size ()); + if ((globalSubrs != &Null (CFFSubrs)) && !stringIndex->sanitize (&sc)) + { fini (); return; } + + charStrings = &StructAtOffsetOrNull (cff, topDicts[0].charStringsOffset); + + if ((charStrings == &Null(CFFCharStrings)) || 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]; + CFFFontDictValues *font; + CFFFontDict_Interpreter font_interp; + font = fontDicts.push (); + if (unlikely (!fontDictStr.sanitize (&sc) || + !font_interp.interpret (fontDictStr, *font))) + { fini (); return; } + PrivDictVal *priv = &privateDicts[i]; + const ByteStr privDictStr (StructAtOffset (cff, font->privateDictInfo.offset), font->privateDictInfo.size); + Interpreter priv_interp; + if (unlikely (!privDictStr.sanitize (&sc) || + !priv_interp.interpret (privDictStr, *priv))) + { fini (); return; } + + priv->localSubrs = &StructAtOffsetOrNull (privDictStr.str, priv->subrsOffset); + if (priv->localSubrs != &Null(CFFSubrs) && + unlikely (!priv->localSubrs->sanitize (&sc))) + { fini (); return; } + } + } + else /* non-CID */ + { + CFFTopDictValues *font = &topDicts[0]; + PrivDictVal *priv = &privateDicts[0]; + + const ByteStr privDictStr (StructAtOffset (cff, font->privateDictInfo.offset), font->privateDictInfo.size); + Interpreter priv_interp; + if (unlikely (!privDictStr.sanitize (&sc) || + !priv_interp.interpret (privDictStr, *priv))) + { fini (); return; } + + priv->localSubrs = &StructAtOffsetOrNull (privDictStr.str, priv->subrsOffset); + if (priv->localSubrs != &Null(CFFSubrs) && + unlikely (!priv->localSubrs->sanitize (&sc))) + { fini (); return; } + } + } + + inline void fini (void) + { + sc.end_processing (); + topDicts[0].fini (); + topDicts.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 topDicts[0].is_CID (); } + + 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 NameIndex *nameIndex; + const TopDictIndex *topDictIndex; + const StringIndex *stringIndex; + const Encoding *encoding; + const Charset *charset; + const CFFSubrs *globalSubrs; + const CFFCharStrings *charStrings; + const CFFFDArray *fdArray; + const CFFFDSelect *fdSelect; + unsigned int fdCount; + + hb_vector_t topDicts; + hb_vector_t fontDicts; + hb_vector_t privateDicts; + + unsigned int num_glyphs; + }; + + typedef accelerator_templ_t accelerator_t; + typedef accelerator_templ_t accelerator_subset_t; + + inline bool subset (hb_subset_plan_t *plan) const + { + hb_blob_t *cff_prime = nullptr; + + bool success = true; + if (hb_subset_cff (plan, &cff_prime)) { + success = success && plan->add_table (HB_OT_TAG_cff, cff_prime); + } else { + success = false; + } + hb_blob_destroy (cff_prime); + + return success; + } + + public: + FixedVersion version; /* Version of CFF table. set to 0x0100u */ + OffsetTo nameIndex; /* headerSize = Offset to Name INDEX. */ + HBUINT8 offSize; /* offset size (unused?) */ + + public: + DEFINE_SIZE_STATIC (4); +}; + +} /* namespace OT */ + +#endif /* HB_OT_CFF_TABLE_HH */ diff --git a/src/hb-ot-cff2-table.hh b/src/hb-ot-cff2-table.hh index a1611891b..4fa2ecbc4 100644 --- a/src/hb-ot-cff2-table.hh +++ b/src/hb-ot-cff2-table.hh @@ -38,6 +38,13 @@ namespace CFF { */ #define HB_OT_TAG_cff2 HB_TAG('C','F','F','2') +typedef Index CFF2Index; +template struct CFF2IndexOf : IndexOf {}; + +typedef CFF2Index CFF2CharStrings; +typedef FDArray CFF2FDArray; +typedef CFF2Index CFF2Subrs; + typedef FDSelect3_4 FDSelect4; typedef FDSelect3_4_Range FDSelect4_Range; @@ -126,22 +133,18 @@ struct CFF2VariationStore DEFINE_SIZE_MIN (2 + VariationStore::min_size); }; -struct CFF2TopDictValues : DictValues +struct CFF2TopDictValues : TopDictValues { inline void init (void) { - DictValues::init (); - charStringsOffset.set (0); - vstoreOffset.set (0); - FDArrayOffset.set (0); - FDSelectOffset.set (0); + TopDictValues::init (); + vstoreOffset = 0; + FDSelectOffset = 0; } inline void fini (void) { - DictValues::fini (); - FontMatrix[0] = FontMatrix[3] = 0.001f; - FontMatrix[1] = FontMatrix[2] = FontMatrix[4] = FontMatrix[5] = 0.0f; + TopDictValues::fini (); } inline unsigned int calculate_serialized_size (void) const @@ -150,66 +153,55 @@ struct CFF2TopDictValues : DictValues for (unsigned int i = 0; i < values.len; i++) { OpCode op = values[i].op; - if (op == OpCode_FontMatrix) - size += values[i].str.len; - else - size += OpCode_Size (OpCode_longint) + 4 + OpCode_Size (op); + switch (op) + { + case OpCode_vstore: + case OpCode_FDSelect: + size += OpCode_Size (OpCode_longint) + 4 + OpCode_Size (op); + break; + default: + size += TopDictValues::calculate_serialized_op_size (values[i]); + break; + } } return size; } - float FontMatrix[6]; - LOffsetTo charStringsOffset; - LOffsetTo vstoreOffset; - LOffsetTo FDArrayOffset; - LOffsetTo FDSelectOffset; + unsigned int vstoreOffset; + unsigned int FDSelectOffset; }; -struct CFF2TopDictOpSet +struct CFF2TopDictOpSet : TopDictOpSet { - static inline bool process_op (const ByteStr& str, unsigned int& offset, OpCode op, Stack& stack, CFF2TopDictValues& dictval) + static inline bool process_op (const ByteStr& str, unsigned int& offset, + OpCode op, Stack& stack, CFF2TopDictValues& dictval) { switch (op) { - case OpCode_CharStrings: - if (unlikely (!check_pop_offset (stack, dictval.charStringsOffset))) - return false; + case OpCode_FontMatrix: + { + DictVal val; + val.init (); + dictval.pushVal (op, str, offset + 1); + stack.clear (); + } break; + case OpCode_vstore: - if (unlikely (!check_pop_offset (stack, dictval.vstoreOffset))) - return false; - break; - case OpCode_FDArray: - if (unlikely (!check_pop_offset (stack, dictval.FDArrayOffset))) + if (unlikely (!stack.check_pop_uint (dictval.vstoreOffset))) return false; + stack.clear (); break; case OpCode_FDSelect: - if (unlikely (!check_pop_offset (stack, dictval.FDSelectOffset))) + if (unlikely (!stack.check_pop_uint (dictval.FDSelectOffset))) return false; + stack.clear (); break; - case OpCode_FontMatrix: - if (unlikely (!stack.check_underflow (6))) - return false; - for (int i = 0; i < 6; i++) - dictval.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_int ((int32_t)*(const HBUINT32*)&str[offset + 1]); - offset += 4; - return true; - - case OpCode_BCD: /* real number */ - float v; - if (unlikely (stack.check_overflow (1) || !parse_bcd (str, offset, v))) - return false; - stack.push_real (v); - return true; default: - /* XXX: invalid */ - stack.clear (); - return false; + if (unlikely (!TopDictOpSet::process_op (str, offset, op, stack, dictval))) + return false; + /* Record this operand below if stack is empty, otherwise done */ + if (!stack.is_empty ()) return true; } dictval.pushVal (op, str, offset + 1); @@ -222,8 +214,7 @@ struct CFF2FontDictValues : DictValues inline void init (void) { DictValues::init (); - privateDictSize = 0; - privateDictOffset.set (0); + privateDictInfo.init (); } inline void fini (void) @@ -231,27 +222,24 @@ struct CFF2FontDictValues : DictValues DictValues::fini (); } - unsigned int privateDictSize; - LOffsetTo privateDictOffset; + TableInfo privateDictInfo; }; struct CFF2FontDictOpSet { - static inline bool process_op (const ByteStr& str, unsigned int& offset, OpCode op, Stack& stack, CFF2FontDictValues& dictval) + static inline bool process_op (const ByteStr& str, unsigned int& offset, + OpCode op, Stack& stack, CFF2FontDictValues& dictval) { switch (op) { case OpCode_Private: - if (unlikely (!check_pop_offset (stack, dictval.privateDictOffset))) + if (unlikely (!stack.check_pop_uint (dictval.privateDictInfo.offset))) return false; - if (unlikely (!stack.check_pop_uint (dictval.privateDictSize))) + if (unlikely (!stack.check_pop_uint (dictval.privateDictInfo.size))) return false; + stack.clear (); break; case OpCode_longint: /* 5-byte integer */ - if (unlikely (!str.check_limit (offset, 5) || !stack.check_overflow (1))) - return false; - stack.push_int ((int32_t)((str[offset + 1] << 24) | ((uint32_t)str[offset + 2] << 16) | ((uint32_t)str[offset + 3] << 8) | str[offset + 4])); - offset += 4; - return true; + return stack.push_longint_from_str (str, offset); case OpCode_BCD: /* real number */ float v; if (unlikely (stack.check_overflow (1) || !parse_bcd (str, offset, v))) @@ -276,8 +264,8 @@ struct CFF2PrivateDictValues_Base : DictValues inline void init (void) { DictValues::init (); - subrsOffset.set (0); - localSubrs = &Null(Subrs); + subrsOffset = 0; + localSubrs = &Null(CFF2Subrs); } inline void fini (void) @@ -296,8 +284,8 @@ struct CFF2PrivateDictValues_Base : DictValues return size; } - LOffsetTo subrsOffset; - const Subrs *localSubrs; + unsigned int subrsOffset; + const CFF2Subrs *localSubrs; }; typedef CFF2PrivateDictValues_Base CFF2PrivateDictValues_Subset; @@ -305,47 +293,44 @@ typedef CFF2PrivateDictValues_Base CFF2PrivateDictValues; struct CFF2PrivateDictOpSet { - static inline bool process_op (const ByteStr& str, unsigned int& offset, OpCode op, Stack& stack, CFF2PrivateDictValues& dictval) + static inline bool process_op (const ByteStr& str, unsigned int& offset, + OpCode op, Stack& stack, CFF2PrivateDictValues& dictval) { DictVal 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 (!stack.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_ExpansionFactor: + case OpCode_LanguageGroup: if (unlikely (!stack.check_pop_num (val.single_val))) return false; + stack.clear (); break; - case OpCode_LanguageGroup: - if (unlikely (!stack.check_pop_num (val.single_val))) + case OpCode_BlueValues: + case OpCode_OtherBlues: + case OpCode_FamilyBlues: + case OpCode_FamilyOtherBlues: + case OpCode_StemSnapH: + case OpCode_StemSnapV: + if (unlikely (!stack.check_pop_delta (val.multi_val))) return false; + stack.clear (); break; case OpCode_Subrs: - if (unlikely (!check_pop_offset (stack, dictval.subrsOffset))) + if (unlikely (!stack.check_pop_uint (dictval.subrsOffset))) return false; + stack.clear (); break; case OpCode_blend: // XXX: TODO return true; case OpCode_longint: /* 5-byte integer */ - if (unlikely (!str.check_limit (offset, 5) || !stack.check_overflow (1))) - return false; - stack.push_int ((int32_t)((str[offset + 1] << 24) | (str[offset + 2] << 16) || (str[offset + 3] << 8) || str[offset + 4])); - offset += 4; - return true; + return stack.push_longint_from_str (str, offset); case OpCode_BCD: /* real number */ float v; if (unlikely (!stack.check_overflow (1) || !parse_bcd (str, offset, v))) @@ -364,7 +349,8 @@ struct CFF2PrivateDictOpSet struct CFF2PrivateDictOpSet_Subset { - static inline bool process_op (const ByteStr& str, unsigned int& offset, OpCode op, Stack& stack, CFF2PrivateDictValues_Subset& dictval) + static inline bool process_op (const ByteStr& str, unsigned int& offset, + OpCode op, Stack& stack, CFF2PrivateDictValues_Subset& dictval) { switch (op) { case OpCode_BlueValues: @@ -394,15 +380,12 @@ struct CFF2PrivateDictOpSet_Subset } case OpCode_Subrs: - if (unlikely (!check_pop_offset (stack, dictval.subrsOffset))) + if (unlikely (!stack.check_pop_uint (dictval.subrsOffset))) return false; + stack.clear (); break; case OpCode_longint: /* 5-byte integer */ - if (unlikely (!str.check_limit (offset, 5) || !stack.check_overflow (1))) - return false; - stack.push_int ((int32_t)((str[offset + 1] << 24) | (str[offset + 2] << 16) || (str[offset + 3] << 8) || str[offset + 4])); - offset += 4; - return true; + return stack.push_longint_from_str (str, offset); default: return false; @@ -439,6 +422,7 @@ struct cff2 { inline void init (hb_face_t *face) { + topDict.init (); fontDicts.init (); privateDicts.init (); @@ -451,43 +435,31 @@ struct cff2 const OT::cff2 *cff2 = this->blob->template as (); if (cff2 == &Null(OT::cff2)) - { - fini (); - return; - } + { fini (); return; } { /* parse top dict */ ByteStr topDictStr (cff2 + cff2->topDict, cff2->topDictSize); CFF2TopDict_Interpreter top_interp; if (unlikely (!topDictStr.sanitize (&sc) || - !top_interp.interpret (topDictStr, top))) - { - fini (); - return; - } + !top_interp.interpret (topDictStr, topDict))) + { fini (); return; } } - globalSubrs = &StructAtOffset (cff2, cff2->topDict + cff2->topDictSize); - varStore = &top.vstoreOffset (cff2); - charStrings = &top.charStringsOffset (cff2); - fdArray = &top.FDArrayOffset (cff2); - fdSelect = &top.FDSelectOffset (cff2); + globalSubrs = &StructAtOffset (cff2, cff2->topDict + cff2->topDictSize); + varStore = &StructAtOffsetOrNull (cff2, topDict.vstoreOffset); + charStrings = &StructAtOffsetOrNull (cff2, topDict.charStringsOffset); + fdArray = &StructAtOffsetOrNull (cff2, topDict.FDArrayOffset); + fdSelect = &StructAtOffsetOrNull (cff2, topDict.FDSelectOffset); if (((varStore != &Null(CFF2VariationStore)) && unlikely (!varStore->sanitize (&sc))) || - ((charStrings == &Null(CharStrings)) || unlikely (!charStrings->sanitize (&sc))) || - ((fdArray == &Null(FDArray)) || unlikely (!fdArray->sanitize (&sc))) || - ((fdSelect != &Null(CFF2FDSelect)) && unlikely (!fdSelect->sanitize (&sc, fdArray->count)))) - { - fini (); - return; - } + (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; - } + { fini (); return; } privateDicts.resize (fdArray->count); @@ -500,27 +472,18 @@ struct cff2 font = fontDicts.push (); if (unlikely (!fontDictStr.sanitize (&sc) || !font_interp.interpret (fontDictStr, *font))) - { - fini (); - return; - } + { fini (); return; } - const ByteStr privDictStr (font->privateDictOffset (cff2), font->privateDictSize); + const ByteStr privDictStr (StructAtOffsetOrNull (cff2, font->privateDictInfo.offset), font->privateDictInfo.size); Interpreter priv_interp; if (unlikely (!privDictStr.sanitize (&sc) || !priv_interp.interpret (privDictStr, privateDicts[i]))) - { - fini (); - return; - } + { fini (); return; } - privateDicts[i].localSubrs = &privateDicts[i].subrsOffset (privDictStr.str); - if (privateDicts[i].localSubrs != &Null(Subrs) && + privateDicts[i].localSubrs = &StructAtOffsetOrNull (privDictStr.str, privateDicts[i].subrsOffset); + if (privateDicts[i].localSubrs != &Null(CFF2Subrs) && unlikely (!privateDicts[i].localSubrs->sanitize (&sc))) - { - fini (); - return; - } + { fini (); return; } } } @@ -550,11 +513,11 @@ struct cff2 hb_sanitize_context_t sc; public: - CFF2TopDictValues top; - const Subrs *globalSubrs; + CFF2TopDictValues topDict; + const CFF2Subrs *globalSubrs; const CFF2VariationStore *varStore; - const CharStrings *charStrings; - const FDArray *fdArray; + const CFF2CharStrings *charStrings; + const CFF2FDArray *fdArray; const CFF2FDSelect *fdSelect; hb_vector_t fontDicts; diff --git a/src/hb-ot-font.cc b/src/hb-ot-font.cc index e25bb6916..7e3b78515 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-cff-table.hh" #include "hb-ot-cff2-table.hh" #include "hb-ot-hmtx-table.hh" #include "hb-ot-kern-table.hh" diff --git a/src/hb-subset-cff-common-private.cc b/src/hb-subset-cff-common-private.cc index c332a5a5f..b74a298ee 100644 --- a/src/hb-subset-cff-common-private.cc +++ b/src/hb-subset-cff-common-private.cc @@ -41,7 +41,7 @@ using namespace CFF; bool hb_plan_subset_cff_fdselect (const hb_vector_t &glyphs, unsigned int fdCount, - const CFF2FDSelect &src, /* IN */ + const FDSelect &src, /* IN */ unsigned int &subset_fd_count /* OUT */, unsigned int &subst_fdselect_size /* OUT */, unsigned int &subst_fdselect_format /* OUT */, @@ -128,7 +128,7 @@ template static inline bool serialize_fdselect_3_4 (hb_serialize_context_t *c, unsigned int num_glyphs, - const CFF2FDSelect &src, + const FDSelect &src, unsigned int size, const hb_vector_t &first_glyphs, const hb_vector_t &fdmap) @@ -154,7 +154,7 @@ serialize_fdselect_3_4 (hb_serialize_context_t *c, bool hb_serialize_cff_fdselect (hb_serialize_context_t *c, const hb_vector_t &glyphs, - const CFF2FDSelect &src, + const FDSelect &src, unsigned int fd_count, unsigned int fdselect_format, unsigned int size, diff --git a/src/hb-subset-cff-common-private.hh b/src/hb-subset-cff-common-private.hh index ff4b2d52d..97d5550fa 100644 --- a/src/hb-subset-cff-common-private.hh +++ b/src/hb-subset-cff-common-private.hh @@ -34,7 +34,7 @@ HB_INTERNAL bool hb_plan_subset_cff_fdselect (const hb_vector_t &glyphs, unsigned int fdCount, - const CFF::CFF2FDSelect &src, /* IN */ + const CFF::FDSelect &src, /* IN */ unsigned int &subset_fd_count /* OUT */, unsigned int &subst_fdselect_size /* OUT */, unsigned int &subst_fdselect_format /* OUT */, @@ -44,7 +44,7 @@ hb_plan_subset_cff_fdselect (const hb_vector_t &glyphs, HB_INTERNAL bool hb_serialize_cff_fdselect (hb_serialize_context_t *c, const hb_vector_t &glyphs, - const CFF::CFF2FDSelect &src, + const CFF::FDSelect &src, unsigned int fd_count, unsigned int fdselect_format, unsigned int size, diff --git a/src/hb-subset-cff.cc b/src/hb-subset-cff.cc new file mode 100644 index 000000000..ea5066787 --- /dev/null +++ b/src/hb-subset-cff.cc @@ -0,0 +1,560 @@ +/* + * 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-cff-table.hh" +#include "hb-set.h" +#include "hb-subset-cff.hh" +#include "hb-subset-plan.hh" +#include "hb-subset-cff-common-private.hh" + +using namespace CFF; + +struct CFFSubTableOffsets { + inline CFFSubTableOffsets (void) + { + memset (this, 0, sizeof(*this)); + } + + unsigned int nameIndexOffset; + unsigned int topDictOffset; + unsigned int topDictOffSize; + unsigned int stringIndexOffset; + unsigned int globalSubrsOffset; + unsigned int encodingOffset; + unsigned int charsetOffset; + TableInfo FDSelectInfo; + unsigned int FDArrayOffset; + unsigned int FDArrayOffSize; + unsigned int charStringsOffset; + unsigned int charStringsOffSize; + TableInfo privateDictInfo; +}; + +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_charset: + return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.charsetOffset)); + + case OpCode_Encoding: + return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.encodingOffset)); + + case OpCode_CharStrings: + return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.charStringsOffset)); + + case OpCode_FDArray: + return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.FDArrayOffset)); + + case OpCode_FDSelect: + return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.FDSelectInfo.offset)); + + case OpCode_Private: + { + if (unlikely (!UnsizedByteStr::serialize_int2 (c, offsets.privateDictInfo.size))) + return_trace (false); + if (unlikely (!UnsizedByteStr::serialize_int4 (c, offsets.privateDictInfo.offset))) + return_trace (false); + HBUINT8 *p = c->allocate_size (1); + if (unlikely (p == nullptr)) return_trace (false); + p->set (OpCode_Private); + } + break; + + 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_charset: + case OpCode_Encoding: + case OpCode_CharStrings: + case OpCode_FDArray: + case OpCode_FDSelect: + return OpCode_Size (OpCode_longint) + 4 + OpCode_Size (opstr.op); + + case OpCode_Private: + return OpCode_Size (OpCode_longint) + 4 + OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Private); + + 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 as a 2-byte integer */ + if (unlikely (!UnsizedByteStr::serialize_int2 (c, privateDictInfo.size))) + return_trace (false); + + /* serialize the private dict offset as a 4-byte integer */ + if (unlikely (!UnsizedByteStr::serialize_int4 (c, privateDictInfo.offset))) + return_trace (false); + + /* serialize the opcode */ + HBUINT8 *p = c->allocate_size (1); + if (unlikely (p == nullptr)) return_trace (false); + p->set (OpCode_Private); + + return_trace (true); + } + else + { + HBUINT8 *d = c->allocate_size (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_longint) + 4 + OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Private); + else + return opstr.str.len; + } +}; + +struct CFFPrivateDict_OpSerializer : OpSerializer +{ + inline bool serialize (hb_serialize_context_t *c, + const OpStr &opstr, + const unsigned int subrsOffset) const + { + TRACE_SERIALIZE (this); + + if (opstr.op == OpCode_Subrs) + return_trace (FontDict::serialize_offset2_op(c, OpCode_Subrs, subrsOffset)); + else + return_trace (copy_opstr (c, opstr)); + } + + inline unsigned int calculate_serialized_size (const OpStr &opstr) const + { + if (opstr.op == OpCode_Subrs) + return OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs); + else + return opstr.str.len; + } +}; + +struct cff_subset_plan { + inline cff_subset_plan (void) + : final_size (0), + orig_fdcount (0), + subst_fdcount(1), + subst_fdselect_format (0) + { + topdict_sizes.init (); + topdict_sizes.resize (1); + subst_fdselect_first_glyphs.init (); + fdmap.init (); + subset_charstrings.init (); + privateDictInfos.init (); + } + + inline ~cff_subset_plan (void) + { + topdict_sizes.fini (); + subst_fdselect_first_glyphs.fini (); + fdmap.fini (); + subset_charstrings.fini (); + privateDictInfos.fini (); + } + + inline bool create (const OT::cff::accelerator_subset_t &acc, + hb_subset_plan_t *plan) + { + final_size = 0; + orig_fdcount = acc.fdCount; + + /* CFF header */ + final_size += OT::cff::static_size; + + /* Name INDEX */ + offsets.nameIndexOffset = final_size; + final_size += acc.nameIndex->get_size (); + + /* top dict INDEX */ + { + offsets.topDictOffset = final_size; + CFFTopDict_OpSerializer topSzr; + unsigned int topDictSize = TopDict::calculate_serialized_size (acc.topDicts[0], topSzr); + offsets.topDictOffSize = calcOffSize(topDictSize); + final_size += CFFIndexOf::calculate_serialized_size (offsets.topDictOffSize, acc.topDicts, topdict_sizes, topSzr); + } + + /* String INDEX */ + offsets.stringIndexOffset = final_size; + final_size += acc.stringIndex->get_size (); + + /* global subrs */ + offsets.globalSubrsOffset = final_size; + final_size += acc.globalSubrs->get_size (); + + /* Encoding */ + offsets.encodingOffset = final_size; + if (acc.encoding != &Null(Encoding)) + final_size += acc.encoding->get_size (); + + /* Charset */ + offsets.charsetOffset = final_size; + if (acc.charset != &Null(Charset)) + final_size += acc.charset->get_size (acc.num_glyphs); + + /* FDSelect */ + if (acc.fdSelect != &Null(CFFFDSelect)) + { + offsets.FDSelectInfo.offset = final_size; + if (unlikely (!hb_plan_subset_cff_fdselect (plan->glyphs, + orig_fdcount, + *acc.fdSelect, + subst_fdcount, + offsets.FDSelectInfo.size, + subst_fdselect_format, + subst_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) */ + if (acc.fdArray != &Null(CFFFDArray)) { + offsets.FDArrayOffset = final_size; + CFFFontDict_OpSerializer fontSzr; + final_size += CFFFDArray::calculate_serialized_size(offsets.FDArrayOffSize/*OUT*/, acc.fontDicts, subst_fdcount, fdmap, fontSzr); + } + + /* CharStrings */ + { + offsets.charStringsOffset = final_size; + unsigned int dataSize = 0; + for (unsigned int i = 0; i < plan->glyphs.len; i++) + { + const ByteStr str = (*acc.charStrings)[plan->glyphs[i]]; + subset_charstrings.push (str); + dataSize += str.len; + } + offsets.charStringsOffSize = calcOffSize (dataSize + 1); + final_size += CFFCharStrings::calculate_serialized_size (offsets.charStringsOffSize, plan->glyphs.len, dataSize); + } + + /* private dicts & local subrs */ + offsets.privateDictInfo.offset = final_size; + for (unsigned int i = 0; i < orig_fdcount; i++) + { + CFFPrivateDict_OpSerializer privSzr; + TableInfo privInfo = { final_size, PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr) }; + privateDictInfos.push (privInfo); + final_size += privInfo.size + acc.privateDicts[i].localSubrs->get_size (); + } + + if (!acc.is_CID ()) + offsets.privateDictInfo = privateDictInfos[0]; + + return true; + } + + inline unsigned int get_final_size (void) const { return final_size; } + + unsigned int final_size; + hb_vector_t topdict_sizes; + CFFSubTableOffsets offsets; + + unsigned int orig_fdcount; + unsigned int subst_fdcount; + inline bool is_fds_subsetted (void) const { return subst_fdcount < orig_fdcount; } + unsigned int subst_fdselect_format; + hb_vector_t subst_fdselect_first_glyphs; + + /* font dict index remap table from fullset FDArray to subset FDArray. + * set to HB_SET_VALUE_INVALID if excluded from subset */ + hb_vector_t fdmap; + + hb_vector_t subset_charstrings; + hb_vector_t privateDictInfos; +}; + +static inline bool _write_cff (const cff_subset_plan &plan, + const OT::cff::accelerator_subset_t &acc, + const hb_vector_t& glyphs, + unsigned int dest_sz, + void *dest) +{ + hb_serialize_context_t c (dest, dest_sz); + + OT::cff *cff = c.start_serialize (); + 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); + NameIndex *dest = c.start_embed (); + 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.topDictOffset == c.head - c.start); + CFFIndexOf *dest = c.start_embed< CFFIndexOf > (); + if (dest == nullptr) return false; + CFFTopDict_OpSerializer topSzr; + if (unlikely (!dest->serialize (&c, plan.offsets.topDictOffSize, acc.topDicts, plan.topdict_sizes, topSzr, plan.offsets))) + { + DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF top dict"); + return false; + } + } + + /* String INDEX */ + { + assert (plan.offsets.stringIndexOffset == c.head - c.start); + StringIndex *dest = c.start_embed (); + if (unlikely (dest == nullptr)) return false; + if (unlikely (!dest->serialize (&c, *acc.stringIndex))) + { + DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF string INDEX"); + return false; + } + } + + /* global subrs */ + { + assert (plan.offsets.globalSubrsOffset == c.head - c.start); + CFFSubrs *dest = c.start_embed (); + if (unlikely (dest == nullptr)) return false; + if (unlikely (!dest->serialize (&c, *acc.globalSubrs))) + { + DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF global subrs"); + return false; + } + } + + /* Encoding */ + if (acc.encoding != &Null(Encoding)){ + assert (plan.offsets.encodingOffset == c.head - c.start); + Encoding *dest = c.start_embed (); + if (unlikely (dest == nullptr)) return false; + if (unlikely (!dest->serialize (&c, *acc.encoding, acc.num_glyphs))) // XXX: TODO + { + DEBUG_MSG (SUBSET, nullptr, "failed to serialize Encoding"); + return false; + } + } + + /* Charset */ + if (acc.charset != &Null(Charset)) + { + assert (plan.offsets.charsetOffset == c.head - c.start); + Charset *dest = c.start_embed (); + if (unlikely (dest == nullptr)) return false; + if (unlikely (!dest->serialize (&c, *acc.charset, acc.num_glyphs))) // XXX: TODO + { + DEBUG_MSG (SUBSET, nullptr, "failed to serialize Charset"); + return false; + } + } + + /* FDSelect */ + if (acc.fdSelect != &Null(CFFFDSelect)) + { + 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.subst_fdselect_format, plan.offsets.FDSelectInfo.size, + plan.subst_fdselect_first_glyphs, + plan.fdmap))) + { + DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF subset FDSelect"); + return false; + } + } + else + { + CFFFDSelect *dest = c.start_embed (); + 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(CFFFDArray)) + { + assert (plan.offsets.FDArrayOffset == c.head - c.start); + CFFFDArray *fda = c.start_embed (); + if (unlikely (fda == nullptr)) return false; + CFFFontDict_OpSerializer fontSzr; + if (unlikely (!fda->serialize (&c, plan.offsets.FDArrayOffSize, + acc.fontDicts, plan.subst_fdcount, plan.fdmap, + fontSzr, plan.privateDictInfos))) + { + DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF FDArray"); + return false; + } + } + + /* CharStrings */ + { + assert (plan.offsets.charStringsOffset == c.head - c.start); + CFFCharStrings *cs = c.start_embed (); + if (unlikely (cs == nullptr)) return false; + if (unlikely (!cs->serialize (&c, plan.offsets.charStringsOffSize, 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++) + { + PrivateDict *pd = c.start_embed (); + if (unlikely (pd == nullptr)) return false; + CFFPrivateDict_OpSerializer privSzr; + /* N.B. local subrs immediately follows its corresponding private dict. i.e., subr offset == private dict size */ + if (unlikely (!pd->serialize (&c, acc.privateDicts[i], privSzr, plan.privateDictInfos[i].size))) + { + DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF Private Dict[%d]", i); + return false; + } + if (acc.privateDicts[i].subrsOffset != 0) + { + CFFSubrs *subrs = c.start_embed (); + if (unlikely (subrs == nullptr) || acc.privateDicts[i].localSubrs == &Null(CFFSubrs)) + { + DEBUG_MSG (SUBSET, nullptr, "CFF subset: local subrs unexpectedly null [%d]", i); + return false; + } + if (unlikely (!subrs->serialize (&c, *acc.privateDicts[i].localSubrs))) + { + DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF local subrs [%d]", i); + return false; + } + } + } + + c.end_serialize (); + + return true; +} + +static bool +_hb_subset_cff (const OT::cff::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_cff (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_cff: + * Subsets the CFF table according to a provided plan. + * + * Return value: subsetted cff table. + **/ +bool +hb_subset_cff (hb_subset_plan_t *plan, + hb_blob_t **prime /* OUT */) +{ + hb_blob_t *cff_blob = hb_sanitize_context_t().reference_table (plan->source); + const char *data = hb_blob_get_data(cff_blob, nullptr); + + OT::cff::accelerator_subset_t acc; + acc.init(plan->source); + bool result = likely (acc.is_valid ()) && + _hb_subset_cff (acc, data, plan, prime); + hb_blob_destroy (cff_blob); + acc.fini (); + + return result; +} diff --git a/src/hb-subset-cff.hh b/src/hb-subset-cff.hh new file mode 100644 index 000000000..6c9660316 --- /dev/null +++ b/src/hb-subset-cff.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_CFF_HH +#define HB_SUBSET_CFF_HH + +#include "hb-private.hh" + +#include "hb-subset-plan.hh" + +HB_INTERNAL bool +hb_subset_cff (hb_subset_plan_t *plan, + hb_blob_t **cff_prime /* OUT */); + +#endif /* HB_SUBSET_CFF_HH */ diff --git a/src/hb-subset-cff2.cc b/src/hb-subset-cff2.cc index 49a79c3dd..cd762dd81 100644 --- a/src/hb-subset-cff2.cc +++ b/src/hb-subset-cff2.cc @@ -33,11 +33,27 @@ using namespace CFF; +struct CFF2SubTableOffsets { + inline CFF2SubTableOffsets (void) + { + memset (this, 0, sizeof(*this)); + } + + unsigned int topDictSize; + unsigned int varStoreOffset; + TableInfo FDSelectInfo; + unsigned int FDArrayOffset; + unsigned int FDArrayOffSize; + unsigned int charStringsOffset; + unsigned int charStringsOffSize; + unsigned int privateDictsOffset; +}; + struct CFF2TopDict_OpSerializer : OpSerializer { inline bool serialize (hb_serialize_context_t *c, const OpStr &opstr, - const SubTableOffsets &offsets) const + const CFF2SubTableOffsets &offsets) const { TRACE_SERIALIZE (this); @@ -53,7 +69,7 @@ struct CFF2TopDict_OpSerializer : OpSerializer return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.FDArrayOffset)); case OpCode_FDSelect: - return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.FDSelectOffset)); + return_trace (FontDict::serialize_offset4_op(c, opstr.op, offsets.FDSelectInfo.offset)); default: return_trace (copy_opstr (c, opstr)); @@ -81,18 +97,18 @@ struct CFF2FontDict_OpSerializer : OpSerializer { inline bool serialize (hb_serialize_context_t *c, const OpStr &opstr, - const offset_size_pair& privOffSize) const + const TableInfo& privDictInfo) const { TRACE_SERIALIZE (this); if (opstr.op == OpCode_Private) { /* serialize the private dict size as a 2-byte integer */ - if (unlikely (!UnsizedByteStr::serialize_int2 (c, privOffSize.size))) + if (unlikely (!UnsizedByteStr::serialize_int2 (c, privDictInfo.size))) return_trace (false); /* serialize the private dict offset as a 4-byte integer */ - if (unlikely (!UnsizedByteStr::serialize_int4 (c, privOffSize.offset))) + if (unlikely (!UnsizedByteStr::serialize_int4 (c, privDictInfo.offset))) return_trace (false); /* serialize the opcode */ @@ -143,8 +159,8 @@ struct CFF2PrivateDict_OpSerializer : OpSerializer } }; -struct subset_plan { - inline subset_plan (void) +struct cff2_subset_plan { + inline cff2_subset_plan (void) : final_size (0), orig_fdcount (0), subst_fdcount(1), @@ -153,15 +169,15 @@ struct subset_plan { subst_fdselect_first_glyphs.init (); fdmap.init (); subset_charstrings.init (); - private_off_and_size_pairs.init (); + privateDictInfos.init (); } - inline ~subset_plan (void) + inline ~cff2_subset_plan (void) { subst_fdselect_first_glyphs.fini (); fdmap.fini (); subset_charstrings.fini (); - private_off_and_size_pairs.fini (); + privateDictInfos.fini (); } inline bool create (const OT::cff2::accelerator_subset_t &acc, @@ -176,7 +192,7 @@ struct subset_plan { /* top dict */ { CFF2TopDict_OpSerializer topSzr; - offsets.topDictSize = TopDict::calculate_serialized_size (acc.top, topSzr); + offsets.topDictSize = TopDict::calculate_serialized_size (acc.topDict, topSzr); final_size += offsets.topDictSize; } @@ -193,27 +209,27 @@ struct subset_plan { /* FDSelect */ if (acc.fdSelect != &Null(CFF2FDSelect)) { - offsets.FDSelectOffset = final_size; + offsets.FDSelectInfo.offset = final_size; if (unlikely (!hb_plan_subset_cff_fdselect (plan->glyphs, orig_fdcount, - *acc.fdSelect, + *(const FDSelect *)acc.fdSelect, subst_fdcount, - offsets.FDSelectSize, + offsets.FDSelectInfo.size, subst_fdselect_format, subst_fdselect_first_glyphs, fdmap))) return false; if (!is_fds_subsetted ()) - offsets.FDSelectSize = acc.fdSelect->calculate_serialized_size (acc.num_glyphs); - final_size += offsets.FDSelectSize; + offsets.FDSelectInfo.size = acc.fdSelect->calculate_serialized_size (acc.num_glyphs); + final_size += offsets.FDSelectInfo.size; } /* FDArray (FDIndex) */ { offsets.FDArrayOffset = final_size; CFF2FontDict_OpSerializer fontSzr; - final_size += FDArray::calculate_serialized_size(offsets.FDArrayOffSize/*OUT*/, acc.fontDicts, subst_fdcount, fdmap, fontSzr); + final_size += CFF2FDArray::calculate_serialized_size(offsets.FDArrayOffSize/*OUT*/, acc.fontDicts, subst_fdcount, fdmap, fontSzr); } /* CharStrings */ @@ -227,7 +243,7 @@ struct subset_plan { dataSize += str.len; } offsets.charStringsOffSize = calcOffSize (dataSize + 1); - final_size += CharStrings::calculate_serialized_size (offsets.charStringsOffSize, plan->glyphs.len, dataSize); + final_size += CFF2CharStrings::calculate_serialized_size (offsets.charStringsOffSize, plan->glyphs.len, dataSize); } /* private dicts & local subrs */ @@ -235,10 +251,9 @@ struct subset_plan { for (unsigned int i = 0; i < orig_fdcount; i++) { CFF2PrivateDict_OpSerializer privSzr; - unsigned int private_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr); - offset_size_pair pair = { final_size, private_size }; - private_off_and_size_pairs.push (pair); - final_size += private_size + acc.privateDicts[i].localSubrs->get_size (); + TableInfo privInfo = { final_size, PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr) }; + privateDictInfos.push (privInfo); + final_size += privInfo.size + acc.privateDicts[i].localSubrs->get_size (); } return true; @@ -246,8 +261,8 @@ struct subset_plan { inline unsigned int get_final_size (void) const { return final_size; } - unsigned int final_size; - SubTableOffsets offsets; + unsigned int final_size; + CFF2SubTableOffsets offsets; unsigned int orig_fdcount; unsigned int subst_fdcount; @@ -260,10 +275,10 @@ struct subset_plan { hb_vector_t fdmap; hb_vector_t subset_charstrings; - hb_vector_t private_off_and_size_pairs; + hb_vector_t privateDictInfos; }; -static inline bool _write_cff2 (const subset_plan &plan, +static inline bool _write_cff2 (const cff2_subset_plan &plan, const OT::cff2::accelerator_subset_t &acc, const hb_vector_t& glyphs, unsigned int dest_sz, @@ -286,7 +301,7 @@ static inline bool _write_cff2 (const subset_plan &plan, cff2->topDictSize.set (plan.offsets.topDictSize); TopDict &dict = cff2 + cff2->topDict; CFF2TopDict_OpSerializer topSzr; - if (unlikely (!dict.serialize (&c, acc.top, topSzr, plan.offsets))) + if (unlikely (!dict.serialize (&c, acc.topDict, topSzr, plan.offsets))) { DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 top dict"); return false; @@ -296,7 +311,7 @@ static inline bool _write_cff2 (const subset_plan &plan, /* global subrs */ { assert (cff2->topDict + plan.offsets.topDictSize == c.head - c.start); - Subrs *dest = c.start_embed (); + CFF2Subrs *dest = c.start_embed (); if (unlikely (dest == nullptr)) return false; if (unlikely (!dest->serialize (&c, *acc.globalSubrs))) { @@ -320,12 +335,12 @@ static inline bool _write_cff2 (const subset_plan &plan, /* FDSelect */ if (acc.fdSelect != &Null(CFF2FDSelect)) { - assert (plan.offsets.FDSelectOffset == c.head - c.start); + 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.fdArray->count, - plan.subst_fdselect_format, plan.offsets.FDSelectSize, + if (unlikely (!hb_serialize_cff_fdselect (&c, glyphs, *(const FDSelect *)acc.fdSelect, acc.fdArray->count, + plan.subst_fdselect_format, plan.offsets.FDSelectInfo.size, plan.subst_fdselect_first_glyphs, plan.fdmap))) { @@ -347,12 +362,12 @@ static inline bool _write_cff2 (const subset_plan &plan, /* FDArray (FD Index) */ { assert (plan.offsets.FDArrayOffset == c.head - c.start); - FDArray *fda = c.start_embed (); + CFF2FDArray *fda = c.start_embed (); if (unlikely (fda == nullptr)) return false; CFF2FontDict_OpSerializer fontSzr; if (unlikely (!fda->serialize (&c, plan.offsets.FDArrayOffSize, acc.fontDicts, plan.subst_fdcount, plan.fdmap, - fontSzr, plan.private_off_and_size_pairs))) + fontSzr, plan.privateDictInfos))) { DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 FDArray"); return false; @@ -362,7 +377,7 @@ static inline bool _write_cff2 (const subset_plan &plan, /* CharStrings */ { assert (plan.offsets.charStringsOffset == c.head - c.start); - CharStrings *cs = c.start_embed (); + CFF2CharStrings *cs = c.start_embed (); if (unlikely (cs == nullptr)) return false; if (unlikely (!cs->serialize (&c, plan.offsets.charStringsOffSize, plan.subset_charstrings))) { @@ -379,15 +394,15 @@ static inline bool _write_cff2 (const subset_plan &plan, if (unlikely (pd == nullptr)) return false; CFF2PrivateDict_OpSerializer privSzr; /* N.B. local subrs immediately follows its corresponding private dict. i.e., subr offset == private dict size */ - if (unlikely (!pd->serialize (&c, acc.privateDicts[i], privSzr, plan.private_off_and_size_pairs[i].size))) + if (unlikely (!pd->serialize (&c, acc.privateDicts[i], privSzr, plan.privateDictInfos[i].size))) { DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 Private Dict[%d]", i); return false; } if (acc.privateDicts[i].subrsOffset != 0) { - Subrs *subrs = c.start_embed (); - if (unlikely (subrs == nullptr) || acc.privateDicts[i].localSubrs == &Null(Subrs)) + CFF2Subrs *subrs = c.start_embed (); + if (unlikely (subrs == nullptr) || acc.privateDicts[i].localSubrs == &Null(CFF2Subrs)) { DEBUG_MSG (SUBSET, nullptr, "CFF2 subset: local subrs unexpectedly null [%d]", i); return false; @@ -411,18 +426,18 @@ _hb_subset_cff2 (const OT::cff2::accelerator_subset_t &acc, hb_subset_plan_t *plan, hb_blob_t **prime /* OUT */) { - subset_plan cff2_subset_plan; + cff2_subset_plan cff2_plan; - if (unlikely (!cff2_subset_plan.create (acc, 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_subset_plan.get_final_size (); + 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_subset_plan, acc, plan->glyphs, + 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); @@ -453,13 +468,10 @@ hb_subset_cff2 (hb_subset_plan_t *plan, OT::cff2::accelerator_subset_t acc; acc.init(plan->source); bool result = likely (acc.is_valid ()) && - _hb_subset_cff2 (acc, - data, - plan, - prime); + _hb_subset_cff2 (acc, data, plan, prime); hb_blob_destroy (cff2_blob); - acc.fini(); + acc.fini (); return result; } diff --git a/src/hb-subset.cc b/src/hb-subset.cc index 3a421ff06..0512597fc 100644 --- a/src/hb-subset.cc +++ b/src/hb-subset.cc @@ -41,6 +41,7 @@ #include "hb-ot-maxp-table.hh" #include "hb-ot-os2-table.hh" #include "hb-ot-post-table.hh" +#include "hb-ot-cff-table.hh" #include "hb-ot-cff2-table.hh" @@ -271,6 +272,9 @@ _subset_table (hb_subset_plan_t *plan, case HB_OT_TAG_post: result = _subset (plan); break; + case HB_OT_TAG_cff: + result = _subset (plan); + break; case HB_OT_TAG_cff2: result = _subset (plan); break;