| /* |
| * Copyright © 2018 Adobe Systems Incorporated. |
| * |
| * This is part of HarfBuzz, a text shaping library. |
| * |
| * Permission is hereby granted, without written agreement and without |
| * license or royalty fees, to use, copy, modify, and distribute this |
| * software and its documentation for any purpose, provided that the |
| * above copyright notice and the following two paragraphs appear in |
| * all copies of this software. |
| * |
| * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR |
| * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES |
| * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN |
| * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH |
| * DAMAGE. |
| * |
| * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, |
| * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
| * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS |
| * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO |
| * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
| * |
| * Adobe Author(s): Michiharu Ariza |
| */ |
| |
| #ifndef HB_OT_CFF2_TABLE_HH |
| #define HB_OT_CFF2_TABLE_HH |
| |
| #include "hb-ot-cff-common-private.hh" |
| #include "hb-subset-cff2.hh" |
| |
| namespace CFF { |
| |
| /* |
| * CFF2 -- Compact Font Format (CFF) Version 2 |
| * https://docs.microsoft.com/en-us/typography/opentype/spec/cff2 |
| */ |
| #define HB_OT_TAG_cff2 HB_TAG('C','F','F','2') |
| |
| struct CFF2TopDictValues |
| { |
| inline void init () |
| { |
| charStringsOffset.set (0); |
| vstoreOffset.set (0); |
| FDArrayOffset.set (0); |
| FDSelectOffset.set (0); |
| FontMatrix[0] = FontMatrix[3] = 0.001f; |
| FontMatrix[1] = FontMatrix[2] = FontMatrix[4] = FontMatrix[5] = 0.0f; |
| } |
| |
| LOffsetTo<CharStrings> charStringsOffset; |
| LOffsetTo<VariationStore> vstoreOffset; |
| LOffsetTo<FDArray> FDArrayOffset; |
| LOffsetTo<FDSelect> FDSelectOffset; |
| float FontMatrix[6]; |
| }; |
| |
| struct CFF2TopDictOpSet |
| { |
| static inline bool process_op (const ByteStr& str, unsigned int& offset, unsigned char byte, Stack& stack, CFF2TopDictValues& val) |
| { |
| switch (byte) { |
| case OpCode_CharStrings: |
| if (unlikely (!check_pop_offset (stack, val.charStringsOffset))) |
| return false; |
| break; |
| case OpCode_vstore: |
| if (unlikely (!check_pop_offset (stack, val.vstoreOffset))) |
| return false; |
| break; |
| case OpCode_FDArray: |
| if (unlikely (!check_pop_offset (stack, val.FDArrayOffset))) |
| return false; |
| break; |
| case OpCode_FDSelect: |
| if (unlikely (!check_pop_offset (stack, val.FDSelectOffset))) |
| return false; |
| break; |
| case OpCode_FontMatrix: |
| if (unlikely (!stack.check_underflow (6))) |
| return false; |
| for (int i = 0; i < 6; i++) |
| val.FontMatrix[i] = stack.pop ().to_real (); |
| break; |
| case OpCode_longint: /* 5-byte integer */ |
| if (unlikely (!str.check_limit (offset, 5) || !stack.check_overflow (1))) |
| return false; |
| stack.push ((int32_t)((str[offset + 1] << 24) | (str[offset + 2] << 16) | (str[offset + 3] << 8) | str[offset + 4])); |
| offset += 4; |
| break; |
| |
| case OpCode_BCD: /* real number */ |
| float v; |
| if (unlikely (stack.check_overflow (1) || !parse_bcd (str, offset, v))) |
| return false; |
| stack.push (v); |
| break; |
| |
| default: |
| /* XXX: invalid */ |
| stack.clear (); |
| return false; |
| } |
| return true; |
| } |
| }; |
| |
| struct CFF2FontDictValues |
| { |
| inline void init () |
| { |
| privateDictSize = 0; |
| privateDictOffset.set (0); |
| } |
| |
| unsigned int privateDictSize; |
| OffsetTo<Dict> privateDictOffset; |
| }; |
| |
| struct CFF2FontDictOpSet |
| { |
| static inline bool process_op (const ByteStr& str, unsigned int& offset, unsigned char byte, Stack& stack, CFF2FontDictValues& val) |
| { |
| switch (byte) { |
| case OpCode_Private: |
| if (unlikely (!stack.check_pop_uint (val.privateDictSize))) |
| return false; |
| if (unlikely (!check_pop_offset (stack, val.privateDictOffset))) |
| return false; |
| break; |
| case OpCode_longint: /* 5-byte integer */ |
| if (unlikely (!str.check_limit (offset, 5) || !stack.check_overflow (1))) |
| return false; |
| stack.push ((int32_t)((str[offset + 1] << 24) | (str[offset + 2] << 16) || (str[offset + 3] << 8) || str[offset + 4])); |
| break; |
| case OpCode_BCD: /* real number */ |
| float v; |
| if (unlikely (stack.check_overflow (1) || !parse_bcd (str, offset, v))) |
| return false; |
| stack.push (v); |
| break; |
| |
| default: |
| /* XXX: invalid */ |
| stack.clear (); |
| return false; |
| } |
| return true; |
| } |
| }; |
| |
| struct CFF2PrivateDictValues |
| { |
| inline void init () |
| { |
| languageGroup = 0; |
| expansionFactor = 0.06f; |
| vsIndex = 0; |
| subrsOffset.set (0); |
| blueScale = 0.039625f; |
| blueShift = 7.0f; |
| blueFuzz = 1.0f; |
| stdHW = UNSET_REAL_VALUE; |
| stdVW = UNSET_REAL_VALUE; |
| blueValues.init (); |
| otherBlues.init (); |
| familyBlues.init (); |
| familyOtherBlues.init (); |
| stemSnapH.init (); |
| stemSnapV.init (); |
| } |
| |
| inline void fini () |
| { |
| blueValues.fini (); |
| otherBlues.fini (); |
| familyBlues.fini (); |
| familyOtherBlues.fini (); |
| stemSnapH.fini (); |
| stemSnapV.fini (); |
| } |
| |
| int languageGroup; |
| float expansionFactor; |
| int vsIndex; |
| OffsetTo<Subrs> subrsOffset; |
| float blueScale; |
| float blueShift; |
| float blueFuzz; |
| float stdHW; |
| float stdVW; |
| hb_vector_t <float> blueValues; |
| hb_vector_t <float> otherBlues; |
| hb_vector_t <float> familyBlues; |
| hb_vector_t <float> familyOtherBlues; |
| hb_vector_t <float> stemSnapH; |
| hb_vector_t <float> stemSnapV; |
| }; |
| |
| struct CFF2PrivateDictOpSet |
| { |
| static inline bool process_op (const ByteStr& str, unsigned int& offset, unsigned char byte, Stack& stack, CFF2PrivateDictValues& val) |
| { |
| switch (byte) { |
| case OpCode_BlueValues: |
| if (unlikely (!stack.check_pop_delta (val.blueValues, true))) |
| return false; |
| break; |
| case OpCode_OtherBlues: |
| if (unlikely (!stack.check_pop_delta (val.otherBlues, true))) |
| return false; |
| break; |
| case OpCode_FamilyBlues: |
| if (unlikely (!stack.check_pop_delta (val.familyBlues, true))) |
| return false; |
| break; |
| case OpCode_FamilyOtherBlues: |
| if (unlikely (!stack.check_pop_delta (val.familyOtherBlues, true))) |
| return false; |
| break; |
| case OpCode_StdHW: |
| if (unlikely (!stack.check_pop_real (val.stdHW))) |
| return false; |
| break; |
| case OpCode_StdVW: |
| if (unlikely (!stack.check_pop_real (val.stdVW))) |
| return false; |
| break; |
| case OpCode_BlueScale: |
| if (unlikely (!stack.check_pop_real (val.blueScale))) |
| return false; |
| break; |
| case OpCode_BlueShift: |
| if (unlikely (!stack.check_pop_real (val.blueShift))) |
| return false; |
| break; |
| case OpCode_BlueFuzz: |
| if (unlikely (!stack.check_pop_real (val.blueFuzz))) |
| return false; |
| break; |
| case OpCode_StemSnapH: |
| if (unlikely (!stack.check_pop_delta (val.stemSnapH))) |
| return false; |
| break; |
| case OpCode_StemSnapV: |
| if (unlikely (!stack.check_pop_delta (val.stemSnapV))) |
| return false; |
| break; |
| case OpCode_LanguageGroup: |
| if (unlikely (!stack.check_pop_int (val.languageGroup))) |
| return false; |
| break; |
| case OpCode_ExpansionFactor: |
| if (unlikely (!stack.check_pop_real (val.expansionFactor))) |
| return false; |
| break; |
| case OpCode_Subrs: |
| if (unlikely (!check_pop_offset (stack, val.subrsOffset))) |
| return false; |
| break; |
| case OpCode_blend: |
| // XXX: TODO |
| break; |
| |
| default: |
| return false; |
| } |
| return true; |
| } |
| }; |
| |
| typedef Interpreter<CFF2TopDictOpSet, CFF2TopDictValues> CFF2TopDict_Interpreter; |
| typedef Interpreter<CFF2FontDictOpSet, CFF2FontDictValues> CFF2FontDict_Interpreter; |
| typedef Interpreter<CFF2PrivateDictOpSet, CFF2PrivateDictValues> CFF2PrivateDict_Interpreter; |
| |
| }; /* namespace CFF */ |
| |
| namespace OT { |
| |
| using namespace CFF; |
| |
| struct cff2 |
| { |
| static const hb_tag_t tableTag = HB_OT_TAG_cff2; |
| |
| inline bool sanitize (hb_sanitize_context_t *c) const |
| { |
| TRACE_SANITIZE (this); |
| return_trace (c->check_struct (this) && |
| version.sanitize (c) && |
| likely (version.major == 2)); |
| } |
| |
| struct accelerator_t |
| { |
| inline void init (hb_face_t *face) |
| { |
| this->blob = OT::Sanitizer<OT::cff2>().sanitize (face->reference_table (HB_OT_TAG_cff2)); |
| const OT::cff2 *cff2 = this->blob->as<OT::cff2> (); |
| |
| if (cff2 == &Null(OT::cff2)) |
| { |
| hb_blob_destroy (blob); |
| return; |
| } |
| |
| { /* parse top dict */ |
| ByteStr topDictStr (cff2 + cff2->topDict, cff2->topDictSize); |
| CFF2TopDict_Interpreter top_interp; |
| top_interp.init (); |
| top_interp.interpret (topDictStr, top); |
| top_interp.fini (); |
| } |
| |
| varStore = &OT::StructAtOffset<VariationStore>(cff2, top.vstoreOffset); |
| charStrings = &OT::StructAtOffset<CharStrings>(cff2, top.charStringsOffset); |
| fdArray = &OT::StructAtOffset<FDArray>(cff2, top.FDArrayOffset); |
| fdSelect = &OT::StructAtOffset<FDSelect>(cff2, top.FDSelectOffset); |
| |
| // XXX: sanitize above? |
| if ((charStrings == &Null(CharStrings)) || |
| (fdArray == &Null(FDArray))) |
| { |
| hb_blob_destroy (blob); |
| return; |
| } |
| |
| num_glyphs = charStrings->count; |
| } |
| |
| inline void fini (void) |
| { |
| hb_blob_destroy (blob); |
| } |
| |
| inline bool get_extents (hb_codepoint_t glyph, |
| hb_glyph_extents_t *extents) const |
| { |
| // XXX: TODO |
| if (glyph >= num_glyphs) |
| return false; |
| |
| return true; |
| } |
| |
| private: |
| hb_blob_t *blob; |
| |
| CFF2TopDictValues top; |
| const VariationStore *varStore; |
| const CharStrings *charStrings; |
| const FDArray *fdArray; |
| const FDSelect *fdSelect; |
| |
| unsigned int num_glyphs; |
| }; |
| |
| inline bool subset (hb_subset_plan_t *plan) const |
| { |
| hb_blob_t *cff2_prime = nullptr; |
| |
| bool success = true; |
| if (hb_subset_cff2 (plan, &cff2_prime)) { |
| success = success && plan->add_table (HB_OT_TAG_cff2, cff2_prime); |
| } else { |
| success = false; |
| } |
| hb_blob_destroy (cff2_prime); |
| |
| return success; |
| } |
| |
| protected: |
| FixedVersion<HBUINT8> version; /* Version of CFF2 table. set to 0x0200u */ |
| OffsetTo<Dict, HBUINT8> topDict; /* headerSize = Offset to Top DICT. */ |
| HBUINT16 topDictSize; /* Top DICT size */ |
| |
| public: |
| DEFINE_SIZE_STATIC (5); |
| }; |
| |
| } /* namespace OT */ |
| |
| #endif /* HB_OT_CFF2_TABLE_HH */ |