| /* |
| * 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 CFF2VariationStore |
| { |
| inline bool sanitize (hb_sanitize_context_t *c) const |
| { |
| TRACE_SANITIZE (this); |
| return_trace (likely (c->check_struct (this)) && varStore.sanitize (c)); |
| } |
| |
| inline bool serialize (hb_serialize_context_t *c, const CFF2VariationStore *varStore) |
| { |
| TRACE_SERIALIZE (this); |
| unsigned int size_ = varStore->get_size (); |
| CFF2VariationStore *dest = c->allocate_size<CFF2VariationStore> (size_); |
| if (unlikely (dest == nullptr)) return_trace (false); |
| memcpy (dest, varStore, size_); |
| return_trace (true); |
| } |
| |
| inline unsigned int get_size (void) const { return HBUINT16::static_size + size; } |
| |
| HBUINT16 size; |
| VariationStore varStore; |
| |
| DEFINE_SIZE_MIN (2 + VariationStore::min_size); |
| }; |
| |
| struct CFF2TopDictValues : DictValues<OpStr> |
| { |
| inline void init (void) |
| { |
| DictValues<OpStr>::init (); |
| charStringsOffset.set (0); |
| vstoreOffset.set (0); |
| FDArrayOffset.set (0); |
| FDSelectOffset.set (0); |
| } |
| |
| inline void fini (void) |
| { |
| DictValues<OpStr>::fini (); |
| FontMatrix[0] = FontMatrix[3] = 0.001f; |
| FontMatrix[1] = FontMatrix[2] = FontMatrix[4] = FontMatrix[5] = 0.0f; |
| } |
| |
| 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; |
| if (op == OpCode_FontMatrix) |
| size += values[i].str.len; |
| else |
| size += OpCode_Size (OpCode_longint) + 4 + OpCode_Size (op); |
| } |
| return size; |
| } |
| |
| float FontMatrix[6]; |
| LOffsetTo<CharStrings> charStringsOffset; |
| LOffsetTo<CFF2VariationStore> vstoreOffset; |
| LOffsetTo<FDArray> FDArrayOffset; |
| LOffsetTo<FDSelect> FDSelectOffset; |
| }; |
| |
| struct CFF2TopDictOpSet |
| { |
| 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; |
| 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))) |
| return false; |
| break; |
| case OpCode_FDSelect: |
| if (unlikely (!check_pop_offset (stack, dictval.FDSelectOffset))) |
| return false; |
| 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; |
| } |
| |
| dictval.pushVal (op, str, offset + 1); |
| return true; |
| } |
| }; |
| |
| struct CFF2FontDictValues : DictValues<OpStr> |
| { |
| inline void init (void) |
| { |
| DictValues<OpStr>::init (); |
| privateDictSize = 0; |
| privateDictOffset.set (0); |
| } |
| |
| inline void fini (void) |
| { |
| DictValues<OpStr>::fini (); |
| } |
| |
| unsigned int privateDictSize; |
| LOffsetTo<PrivateDict> privateDictOffset; |
| }; |
| |
| struct CFF2FontDictOpSet |
| { |
| 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))) |
| return false; |
| if (unlikely (!stack.check_pop_uint (dictval.privateDictSize))) |
| return false; |
| 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; |
| 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 <typename VAL> |
| struct CFF2PrivateDictValues_Base : DictValues<VAL> |
| { |
| inline void init (void) |
| { |
| DictValues<VAL>::init (); |
| subrsOffset.set (0); |
| localSubrs = &Null(Subrs); |
| } |
| |
| inline void fini (void) |
| { |
| DictValues<VAL>::fini (); |
| } |
| |
| inline unsigned int calculate_serialized_size (void) const |
| { |
| unsigned int size = 0; |
| for (unsigned int i = 0; i < DictValues<VAL>::values.len; i++) |
| if (DictValues<VAL>::values[i].op == OpCode_Subrs) |
| size += OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs); |
| else |
| size += DictValues<VAL>::values[i].str.len; |
| return size; |
| } |
| |
| LOffsetTo<Subrs> subrsOffset; |
| const Subrs *localSubrs; |
| }; |
| |
| typedef CFF2PrivateDictValues_Base<OpStr> CFF2PrivateDictValues_Subset; |
| typedef CFF2PrivateDictValues_Base<DictVal> CFF2PrivateDictValues; |
| |
| struct CFF2PrivateDictOpSet |
| { |
| 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: |
| if (unlikely (!stack.check_pop_num (val.single_val))) |
| return false; |
| break; |
| case OpCode_LanguageGroup: |
| if (unlikely (!stack.check_pop_num (val.single_val))) |
| return false; |
| break; |
| case OpCode_Subrs: |
| if (unlikely (!check_pop_offset (stack, dictval.subrsOffset))) |
| return false; |
| 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; |
| 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 CFF2PrivateDictOpSet_Subset |
| { |
| static inline bool process_op (const ByteStr& str, unsigned int& offset, OpCode op, Stack& stack, CFF2PrivateDictValues_Subset& dictval) |
| { |
| switch (op) { |
| case OpCode_BlueValues: |
| case OpCode_OtherBlues: |
| case OpCode_FamilyBlues: |
| case OpCode_FamilyOtherBlues: |
| case OpCode_StdHW: |
| case OpCode_StdVW: |
| case OpCode_BlueScale: |
| case OpCode_BlueShift: |
| case OpCode_BlueFuzz: |
| case OpCode_StemSnapH: |
| case OpCode_StemSnapV: |
| case OpCode_LanguageGroup: |
| case OpCode_ExpansionFactor: |
| stack.clear (); |
| break; |
| |
| case OpCode_blend: |
| stack.clear (); |
| return true; |
| |
| case OpCode_BCD: |
| { |
| float v; |
| return parse_bcd (str, offset, v); |
| } |
| |
| case OpCode_Subrs: |
| if (unlikely (!check_pop_offset (stack, dictval.subrsOffset))) |
| return false; |
| 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; |
| |
| default: |
| return false; |
| } |
| |
| dictval.pushVal (op, str, offset + 1); |
| return true; |
| } |
| }; |
| |
| typedef Interpreter<CFF2TopDictOpSet, CFF2TopDictValues> CFF2TopDict_Interpreter; |
| typedef Interpreter<CFF2FontDictOpSet, CFF2FontDictValues> CFF2FontDict_Interpreter; |
| typedef Interpreter<CFF2PrivateDictOpSet, CFF2PrivateDictValues> CFF2PrivateDict_Interpreter; |
| |
| }; /* namespace CFF */ |
| |
| namespace OT { |
| |
| using namespace CFF; |
| |
| struct cff2 |
| { |
| static const hb_tag_t tableTag = HB_OT_TAG_cff2; |
| |
| inline bool sanitize (hb_sanitize_context_t *c) const |
| { |
| TRACE_SANITIZE (this); |
| return_trace (c->check_struct (this) && |
| likely (version.major == 2)); |
| } |
| |
| template <typename PrivOpSet, typename PrivDictVal> |
| struct accelerator_templ_t |
| { |
| inline void init (hb_face_t *face) |
| { |
| fontDicts.init (); |
| privateDicts.init (); |
| |
| this->blob = sc.reference_table<cff2> (face); |
| |
| /* setup for run-time santization */ |
| sc.init (this->blob); |
| sc.start_processing (); |
| |
| const OT::cff2 *cff2 = this->blob->template as<OT::cff2> (); |
| |
| if (cff2 == &Null(OT::cff2)) |
| { |
| fini (); |
| return; |
| } |
| |
| { /* parse top dict */ |
| ByteStr topDictStr (cff2 + cff2->topDict, cff2->topDictSize); |
| CFF2TopDict_Interpreter top_interp; |
| if (unlikely (!topDictStr.sanitize (&sc) || |
| !top_interp.interpret (topDictStr, top))) |
| { |
| fini (); |
| return; |
| } |
| } |
| |
| globalSubrs = &StructAtOffset<Subrs> (cff2, cff2->topDict + cff2->topDictSize); |
| varStore = &top.vstoreOffset (cff2); |
| charStrings = &top.charStringsOffset (cff2); |
| fdArray = &top.FDArrayOffset (cff2); |
| fdSelect = &top.FDSelectOffset (cff2); |
| |
| if (((varStore != &Null(CFF2VariationStore)) && unlikely (!varStore->sanitize (&sc))) || |
| ((charStrings == &Null(CharStrings)) || unlikely (!charStrings->sanitize (&sc))) || |
| ((fdArray == &Null(FDArray)) || unlikely (!fdArray->sanitize (&sc))) || |
| ((fdSelect != &Null(FDSelect)) && unlikely (!fdSelect->sanitize (&sc)))) |
| { |
| fini (); |
| return; |
| } |
| |
| num_glyphs = charStrings->count; |
| if (num_glyphs != sc.get_num_glyphs ()) |
| { |
| fini (); |
| return; |
| } |
| |
| privateDicts.resize (fdArray->count); |
| |
| // parse font dicts and gather private dicts |
| for (unsigned int i = 0; i < fdArray->count; i++) |
| { |
| const ByteStr fontDictStr = (*fdArray)[i]; |
| CFF2FontDictValues *font; |
| CFF2FontDict_Interpreter font_interp; |
| font = fontDicts.push (); |
| if (unlikely (!fontDictStr.sanitize (&sc) || |
| !font_interp.interpret (fontDictStr, *font))) |
| { |
| fini (); |
| return; |
| } |
| |
| const ByteStr privDictStr (font->privateDictOffset (cff2), font->privateDictSize); |
| Interpreter<PrivOpSet, PrivDictVal> priv_interp; |
| if (unlikely (!privDictStr.sanitize (&sc) || |
| !priv_interp.interpret (privDictStr, privateDicts[i]))) |
| { |
| fini (); |
| return; |
| } |
| |
| privateDicts[i].localSubrs = &privateDicts[i].subrsOffset (privDictStr.str); |
| if (unlikely (!privateDicts[i].localSubrs->sanitize (&sc))) |
| { |
| fini (); |
| return; |
| } |
| } |
| } |
| |
| inline void fini (void) |
| { |
| sc.end_processing (); |
| fontDicts.fini (); |
| privateDicts.fini (); |
| hb_blob_destroy (blob); |
| blob = nullptr; |
| } |
| |
| inline bool is_valid (void) const { return blob != nullptr; } |
| |
| inline bool get_extents (hb_codepoint_t glyph, |
| hb_glyph_extents_t *extents) const |
| { |
| // XXX: TODO |
| if (glyph >= num_glyphs) |
| return false; |
| |
| return true; |
| } |
| |
| protected: |
| hb_blob_t *blob; |
| hb_sanitize_context_t sc; |
| |
| public: |
| CFF2TopDictValues top; |
| const Subrs *globalSubrs; |
| const CFF2VariationStore *varStore; |
| const CharStrings *charStrings; |
| const FDArray *fdArray; |
| const FDSelect *fdSelect; |
| |
| hb_vector_t<CFF2FontDictValues> fontDicts; |
| hb_vector_t<PrivDictVal> privateDicts; |
| |
| unsigned int num_glyphs; |
| }; |
| |
| typedef accelerator_templ_t<CFF2PrivateDictOpSet, CFF2PrivateDictValues> accelerator_t; |
| typedef accelerator_templ_t<CFF2PrivateDictOpSet_Subset, CFF2PrivateDictValues_Subset> accelerator_subset_t; |
| |
| inline bool subset (hb_subset_plan_t *plan) const |
| { |
| hb_blob_t *cff2_prime = nullptr; |
| |
| bool success = true; |
| if (hb_subset_cff2 (plan, &cff2_prime)) { |
| success = success && plan->add_table (HB_OT_TAG_cff2, cff2_prime); |
| } else { |
| success = false; |
| } |
| hb_blob_destroy (cff2_prime); |
| |
| return success; |
| } |
| |
| public: |
| FixedVersion<HBUINT8> version; /* Version of CFF2 table. set to 0x0200u */ |
| OffsetTo<TopDict, HBUINT8> topDict; /* headerSize = Offset to Top DICT. */ |
| HBUINT16 topDictSize; /* Top DICT size */ |
| |
| public: |
| DEFINE_SIZE_STATIC (5); |
| }; |
| |
| } /* namespace OT */ |
| |
| #endif /* HB_OT_CFF2_TABLE_HH */ |