/* * Copyright © 2018 Adobe Inc. * * 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_CFF1_TABLE_HH #define HB_OT_CFF1_TABLE_HH #include "hb-ot-head-table.hh" #include "hb-ot-cff-common.hh" #include "hb-subset-cff1.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_cff1 HB_TAG('C','F','F',' ') #define CFF_UNDEF_SID CFF_UNDEF_CODE enum EncodingID { StandardEncoding = 0, ExpertEncoding = 1 }; enum CharsetID { ISOAdobeCharset = 0, ExpertCharset = 1, ExpertSubsetCharset = 2 }; typedef CFFIndex<HBUINT16> CFF1Index; template <typename Type> struct CFF1IndexOf : CFFIndexOf<HBUINT16, Type> {}; typedef CFFIndex<HBUINT16> CFF1Index; typedef CFF1Index CFF1CharStrings; typedef FDArray<HBUINT16> CFF1FDArray; typedef Subrs<HBUINT16> CFF1Subrs; struct CFF1FDSelect : FDSelect {}; /* Encoding */ struct Encoding0 { bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && codes[nCodes - 1].sanitize (c)); } hb_codepoint_t get_code (hb_codepoint_t glyph) const { assert (glyph > 0); glyph--; if (glyph < nCodes) { return (hb_codepoint_t)codes[glyph]; } else return CFF_UNDEF_CODE; } unsigned int get_size () const { return HBUINT8::static_size * (nCodes + 1); } HBUINT8 nCodes; HBUINT8 codes[VAR]; DEFINE_SIZE_ARRAY(1, codes); }; struct Encoding1_Range { 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 { unsigned int get_size () const { return HBUINT8::static_size + Encoding1_Range::static_size * nRanges; } bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && ((nRanges == 0) || (ranges[nRanges - 1]).sanitize (c))); } hb_codepoint_t get_code (hb_codepoint_t glyph) const { assert (glyph > 0); glyph--; for (unsigned int i = 0; i < nRanges; i++) { if (glyph <= ranges[i].nLeft) { return (hb_codepoint_t)ranges[i].first + glyph; } glyph -= (ranges[i].nLeft + 1); } return CFF_UNDEF_CODE; } HBUINT8 nRanges; Encoding1_Range ranges[VAR]; DEFINE_SIZE_ARRAY (1, ranges); }; struct SuppEncoding { 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 CFF1SuppEncData { bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && ((nSups == 0) || (supps[nSups - 1]).sanitize (c))); } void get_codes (hb_codepoint_t sid, hb_vector_t<hb_codepoint_t> &codes) const { for (unsigned int i = 0; i < nSups; i++) if (sid == supps[i].glyph) codes.push (supps[i].code); } unsigned int get_size () const { return HBUINT8::static_size + SuppEncoding::static_size * nSups; } HBUINT8 nSups; SuppEncoding supps[VAR]; DEFINE_SIZE_ARRAY (1, supps); }; struct Encoding { 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)); } /* serialize a fullset Encoding */ bool serialize (hb_serialize_context_t *c, const Encoding &src) { TRACE_SERIALIZE (this); unsigned int size = src.get_size (); Encoding *dest = c->allocate_size<Encoding> (size); if (unlikely (dest == nullptr)) return_trace (false); memcpy (dest, &src, size); return_trace (true); } /* serialize a subset Encoding */ bool serialize (hb_serialize_context_t *c, uint8_t format, unsigned int enc_count, const hb_vector_t<code_pair>& code_ranges, const hb_vector_t<code_pair>& supp_codes) { TRACE_SERIALIZE (this); Encoding *dest = c->extend_min (*this); if (unlikely (dest == nullptr)) return_trace (false); dest->format.set (format | ((supp_codes.len > 0)? 0x80: 0)); if (format == 0) { Encoding0 *fmt0 = c->allocate_size<Encoding0> (Encoding0::min_size + HBUINT8::static_size * enc_count); if (unlikely (fmt0 == nullptr)) return_trace (false); fmt0->nCodes.set (enc_count); unsigned int glyph = 0; for (unsigned int i = 0; i < code_ranges.len; i++) { hb_codepoint_t code = code_ranges[i].code; for (int left = (int)code_ranges[i].glyph; left >= 0; left--) fmt0->codes[glyph++].set (code++); if (unlikely (!((glyph <= 0x100) && (code <= 0x100)))) return_trace (false); } } else { Encoding1 *fmt1 = c->allocate_size<Encoding1> (Encoding1::min_size + Encoding1_Range::static_size * code_ranges.len); if (unlikely (fmt1 == nullptr)) return_trace (false); fmt1->nRanges.set (code_ranges.len); for (unsigned int i = 0; i < code_ranges.len; i++) { if (unlikely (!((code_ranges[i].code <= 0xFF) && (code_ranges[i].glyph <= 0xFF)))) return_trace (false); fmt1->ranges[i].first.set (code_ranges[i].code); fmt1->ranges[i].nLeft.set (code_ranges[i].glyph); } } if (supp_codes.len > 0) { CFF1SuppEncData *suppData = c->allocate_size<CFF1SuppEncData> (CFF1SuppEncData::min_size + SuppEncoding::static_size * supp_codes.len); if (unlikely (suppData == nullptr)) return_trace (false); suppData->nSups.set (supp_codes.len); for (unsigned int i = 0; i < supp_codes.len; i++) { suppData->supps[i].code.set (supp_codes[i].code); suppData->supps[i].glyph.set (supp_codes[i].glyph); /* actually SID */ } } return_trace (true); } /* parallel to above: calculate the size of a subset Encoding */ static unsigned int calculate_serialized_size (uint8_t format, unsigned int enc_count, unsigned int supp_count) { unsigned int size = min_size; if (format == 0) size += Encoding0::min_size + HBUINT8::static_size * enc_count; else size += Encoding1::min_size + Encoding1_Range::static_size * enc_count; if (supp_count > 0) size += CFF1SuppEncData::min_size + SuppEncoding::static_size * supp_count; return size; } unsigned int get_size () const { unsigned int size = min_size; if (table_format () == 0) size += u.format0.get_size (); else size += u.format1.get_size (); if (has_supplement ()) size += suppEncData ().get_size (); return size; } hb_codepoint_t get_code (hb_codepoint_t glyph) const { if (table_format () == 0) return u.format0.get_code (glyph); else return u.format1.get_code (glyph); } uint8_t table_format () const { return (format & 0x7F); } bool has_supplement () const { return (format & 0x80) != 0; } void get_supplement_codes (hb_codepoint_t sid, hb_vector_t<hb_codepoint_t> &codes) const { codes.resize (0); if (has_supplement ()) suppEncData().get_codes (sid, codes); } protected: const CFF1SuppEncData &suppEncData () const { if ((format & 0x7F) == 0) return StructAfter<CFF1SuppEncData> (u.format0.codes[u.format0.nCodes-1]); else return StructAfter<CFF1SuppEncData> (u.format1.ranges[u.format1.nRanges-1]); } public: HBUINT8 format; union { Encoding0 format0; Encoding1 format1; } u; /* CFF1SuppEncData suppEncData; */ DEFINE_SIZE_MIN (1); }; /* Charset */ struct Charset0 { 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)); } hb_codepoint_t get_sid (hb_codepoint_t glyph) const { if (glyph == 0) return 0; else return sids[glyph - 1]; } hb_codepoint_t get_glyph (hb_codepoint_t sid, unsigned int num_glyphs) const { if (sid == 0) return 0; for (unsigned int glyph = 1; glyph < num_glyphs; glyph++) { if (sids[glyph-1] == sid) return glyph; } return 0; } 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 <typename TYPE> struct Charset_Range { bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this)); } HBUINT16 first; TYPE nLeft; DEFINE_SIZE_STATIC (HBUINT16::static_size + TYPE::static_size); }; template <typename TYPE> struct Charset1_2 { bool sanitize (hb_sanitize_context_t *c, unsigned int num_glyphs) const { TRACE_SANITIZE (this); if (unlikely (!c->check_struct (this))) return_trace (false); num_glyphs--; for (unsigned int i = 0; num_glyphs > 0; i++) { if (unlikely (!ranges[i].sanitize (c) || (num_glyphs < ranges[i].nLeft + 1))) return_trace (false); num_glyphs -= (ranges[i].nLeft + 1); } return_trace (true); } hb_codepoint_t get_sid (hb_codepoint_t glyph) const { if (glyph == 0) return 0; glyph--; for (unsigned int i = 0;; i++) { if (glyph <= ranges[i].nLeft) return (hb_codepoint_t)ranges[i].first + glyph; glyph -= (ranges[i].nLeft + 1); } return 0; } hb_codepoint_t get_glyph (hb_codepoint_t sid, unsigned int num_glyphs) const { if (sid == 0) return 0; hb_codepoint_t glyph = 1; for (unsigned int i = 0;; i++) { if (glyph >= num_glyphs) return 0; if ((ranges[i].first <= sid) && (sid <= ranges[i].first + ranges[i].nLeft)) return glyph + (sid - ranges[i].first); glyph += (ranges[i].nLeft + 1); } return 0; } unsigned int get_size (unsigned int num_glyphs) const { unsigned int size = HBUINT8::static_size; int glyph = (int)num_glyphs; assert (glyph > 0); glyph--; for (unsigned int i = 0; glyph > 0; i++) { glyph -= (ranges[i].nLeft + 1); size += Charset_Range<TYPE>::static_size; } return size; } Charset_Range<TYPE> ranges[VAR]; DEFINE_SIZE_ARRAY (0, ranges); }; typedef Charset1_2<HBUINT8> Charset1; typedef Charset1_2<HBUINT16> Charset2; typedef Charset_Range<HBUINT8> Charset1_Range; typedef Charset_Range<HBUINT16> Charset2_Range; struct Charset { 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, c->get_num_glyphs ())); else if (likely (format == 2)) return_trace (u.format2.sanitize (c, c->get_num_glyphs ())); else return_trace (false); } /* serialize a fullset Charset */ 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<Charset> (size); if (unlikely (dest == nullptr)) return_trace (false); memcpy (dest, &src, size); return_trace (true); } /* serialize a subset Charset */ bool serialize (hb_serialize_context_t *c, uint8_t format, unsigned int num_glyphs, const hb_vector_t<code_pair>& sid_ranges) { TRACE_SERIALIZE (this); Charset *dest = c->extend_min (*this); if (unlikely (dest == nullptr)) return_trace (false); dest->format.set (format); if (format == 0) { Charset0 *fmt0 = c->allocate_size<Charset0> (Charset0::min_size + HBUINT16::static_size * (num_glyphs - 1)); if (unlikely (fmt0 == nullptr)) return_trace (false); unsigned int glyph = 0; for (unsigned int i = 0; i < sid_ranges.len; i++) { hb_codepoint_t sid = sid_ranges[i].code; for (int left = (int)sid_ranges[i].glyph; left >= 0; left--) fmt0->sids[glyph++].set (sid++); } } else if (format == 1) { Charset1 *fmt1 = c->allocate_size<Charset1> (Charset1::min_size + Charset1_Range::static_size * sid_ranges.len); if (unlikely (fmt1 == nullptr)) return_trace (false); for (unsigned int i = 0; i < sid_ranges.len; i++) { if (unlikely (!(sid_ranges[i].glyph <= 0xFF))) return_trace (false); fmt1->ranges[i].first.set (sid_ranges[i].code); fmt1->ranges[i].nLeft.set (sid_ranges[i].glyph); } } else /* format 2 */ { Charset2 *fmt2 = c->allocate_size<Charset2> (Charset2::min_size + Charset2_Range::static_size * sid_ranges.len); if (unlikely (fmt2 == nullptr)) return_trace (false); for (unsigned int i = 0; i < sid_ranges.len; i++) { if (unlikely (!(sid_ranges[i].glyph <= 0xFFFF))) return_trace (false); fmt2->ranges[i].first.set (sid_ranges[i].code); fmt2->ranges[i].nLeft.set (sid_ranges[i].glyph); } } return_trace (true); } /* parallel to above: calculate the size of a subset Charset */ static unsigned int calculate_serialized_size ( uint8_t format, unsigned int count) { unsigned int size = min_size; if (format == 0) size += Charset0::min_size + HBUINT16::static_size * (count - 1); else if (format == 1) size += Charset1::min_size + Charset1_Range::static_size * count; else size += Charset2::min_size + Charset2_Range::static_size * count; return size; } 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 (num_glyphs); else size += u.format2.get_size (num_glyphs); return size; } hb_codepoint_t get_sid (hb_codepoint_t glyph) const { if (format == 0) return u.format0.get_sid (glyph); else if (format == 1) return u.format1.get_sid (glyph); else return u.format2.get_sid (glyph); } hb_codepoint_t get_glyph (hb_codepoint_t sid, unsigned int num_glyphs) const { if (format == 0) return u.format0.get_glyph (sid, num_glyphs); else if (format == 1) return u.format1.get_glyph (sid, num_glyphs); else return u.format2.get_glyph (sid, num_glyphs); } HBUINT8 format; union { Charset0 format0; Charset1 format1; Charset2 format2; } u; DEFINE_SIZE_MIN (1); }; struct CFF1StringIndex : CFF1Index { bool serialize (hb_serialize_context_t *c, const CFF1StringIndex &strings, unsigned int offSize_, const Remap &sidmap) { TRACE_SERIALIZE (this); if (unlikely ((strings.count == 0) || (sidmap.get_count () == 0))) { if (!unlikely (c->extend_min (this->count))) return_trace (false); count.set (0); return_trace (true); } ByteStrArray bytesArray; bytesArray.init (); if (!bytesArray.resize (sidmap.get_count ())) return_trace (false); for (unsigned int i = 0; i < strings.count; i++) { hb_codepoint_t j = sidmap[i]; if (j != CFF_UNDEF_CODE) bytesArray[j] = strings[i]; } bool result = CFF1Index::serialize (c, offSize_, bytesArray); bytesArray.fini (); return_trace (result); } /* in parallel to above */ unsigned int calculate_serialized_size (unsigned int &offSize /*OUT*/, const Remap &sidmap) const { offSize = 0; if ((count == 0) || (sidmap.get_count () == 0)) return count.static_size; unsigned int dataSize = 0; for (unsigned int i = 0; i < count; i++) if (sidmap[i] != CFF_UNDEF_CODE) dataSize += length_at (i); offSize = calcOffSize(dataSize); return CFF1Index::calculate_serialized_size (offSize, sidmap.get_count (), dataSize); } }; struct CFF1TopDictInterpEnv : NumInterpEnv { CFF1TopDictInterpEnv () : NumInterpEnv(), prev_offset(0), last_offset(0) {} unsigned int prev_offset; unsigned int last_offset; }; struct NameDictValues { enum NameDictValIndex { version, notice, copyright, fullName, familyName, weight, postscript, fontName, baseFontName, registry, ordering, ValCount }; void init () { for (unsigned int i = 0; i < ValCount; i++) values[i] = CFF_UNDEF_SID; } unsigned int& operator[] (unsigned int i) { assert (i < ValCount); return values[i]; } unsigned int operator[] (unsigned int i) const { assert (i < ValCount); return values[i]; } static enum NameDictValIndex name_op_to_index (OpCode op) { switch (op) { default: // can't happen - just make some compiler happy case OpCode_version: return version; case OpCode_Notice: return notice; case OpCode_Copyright: return copyright; case OpCode_FullName: return fullName; case OpCode_FamilyName: return familyName; case OpCode_Weight: return weight; case OpCode_PostScript: return postscript; case OpCode_FontName: return fontName; case OpCode_BaseFontName: return baseFontName; } } unsigned int values[ValCount]; }; struct CFF1TopDictVal : OpStr { unsigned int last_arg_offset; }; struct CFF1TopDictValues : TopDictValues<CFF1TopDictVal> { void init () { TopDictValues<CFF1TopDictVal>::init (); nameSIDs.init (); ros_supplement = 0; cidCount = 8720; EncodingOffset = 0; CharsetOffset = 0; FDSelectOffset = 0; privateDictInfo.init (); } void fini () { TopDictValues<CFF1TopDictVal>::fini (); } bool is_CID () const { return nameSIDs[NameDictValues::registry] != CFF_UNDEF_SID; } NameDictValues nameSIDs; unsigned int ros_supplement_offset; unsigned int ros_supplement; unsigned int cidCount; unsigned int EncodingOffset; unsigned int CharsetOffset; unsigned int FDSelectOffset; TableInfo privateDictInfo; }; struct CFF1TopDictOpSet : TopDictOpSet<CFF1TopDictVal> { static void process_op (OpCode op, CFF1TopDictInterpEnv& env, CFF1TopDictValues& dictval) { CFF1TopDictVal val; val.last_arg_offset = (env.last_offset-1) - dictval.opStart; /* offset to the last argument */ switch (op) { case OpCode_version: case OpCode_Notice: case OpCode_Copyright: case OpCode_FullName: case OpCode_FamilyName: case OpCode_Weight: case OpCode_PostScript: case OpCode_BaseFontName: dictval.nameSIDs[NameDictValues::name_op_to_index (op)] = env.argStack.pop_uint (); env.clear_args (); break; 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_CIDFontVersion: case OpCode_CIDFontRevision: case OpCode_CIDFontType: case OpCode_UIDBase: case OpCode_FontBBox: case OpCode_XUID: case OpCode_BaseFontBlend: env.clear_args (); break; case OpCode_CIDCount: dictval.cidCount = env.argStack.pop_uint (); env.clear_args (); break; case OpCode_ROS: dictval.ros_supplement = env.argStack.pop_uint (); dictval.nameSIDs[NameDictValues::ordering] = env.argStack.pop_uint (); dictval.nameSIDs[NameDictValues::registry] = env.argStack.pop_uint (); env.clear_args (); break; case OpCode_Encoding: dictval.EncodingOffset = env.argStack.pop_uint (); env.clear_args (); if (unlikely (dictval.EncodingOffset == 0)) return; break; case OpCode_charset: dictval.CharsetOffset = env.argStack.pop_uint (); env.clear_args (); if (unlikely (dictval.CharsetOffset == 0)) return; break; case OpCode_FDSelect: dictval.FDSelectOffset = env.argStack.pop_uint (); env.clear_args (); break; case OpCode_Private: dictval.privateDictInfo.offset = env.argStack.pop_uint (); dictval.privateDictInfo.size = env.argStack.pop_uint (); env.clear_args (); break; default: env.last_offset = env.substr.offset; TopDictOpSet<CFF1TopDictVal>::process_op (op, env, dictval); /* Record this operand below if stack is empty, otherwise done */ if (!env.argStack.is_empty ()) return; break; } if (unlikely (env.in_error ())) return; dictval.add_op (op, env.substr, val); } }; struct CFF1FontDictValues : DictValues<OpStr> { void init () { DictValues<OpStr>::init (); privateDictInfo.init (); fontName = CFF_UNDEF_SID; } void fini () { DictValues<OpStr>::fini (); } TableInfo privateDictInfo; unsigned int fontName; }; struct CFF1FontDictOpSet : DictOpSet { static void process_op (OpCode op, NumInterpEnv& env, CFF1FontDictValues& dictval) { switch (op) { case OpCode_FontName: dictval.fontName = env.argStack.pop_uint (); env.clear_args (); break; case OpCode_FontMatrix: case OpCode_PaintType: env.clear_args (); break; case OpCode_Private: dictval.privateDictInfo.offset = env.argStack.pop_uint (); dictval.privateDictInfo.size = env.argStack.pop_uint (); env.clear_args (); break; default: DictOpSet::process_op (op, env); if (!env.argStack.is_empty ()) return; break; } if (unlikely (env.in_error ())) return; dictval.add_op (op, env.substr); } }; template <typename VAL> struct CFF1PrivateDictValues_Base : DictValues<VAL> { void init () { DictValues<VAL>::init (); subrsOffset = 0; localSubrs = &Null(CFF1Subrs); } void fini () { DictValues<VAL>::fini (); } unsigned int calculate_serialized_size () const { unsigned int size = 0; for (unsigned int i = 0; i < DictValues<VAL>::get_count; i++) if (DictValues<VAL>::get_value (i).op == OpCode_Subrs) size += OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs); else size += DictValues<VAL>::get_value (i).str.len; return size; } unsigned int subrsOffset; const CFF1Subrs *localSubrs; }; typedef CFF1PrivateDictValues_Base<OpStr> CFF1PrivateDictValues_Subset; typedef CFF1PrivateDictValues_Base<NumDictVal> CFF1PrivateDictValues; struct CFF1PrivateDictOpSet : DictOpSet { static void process_op (OpCode op, NumInterpEnv& env, CFF1PrivateDictValues& dictval) { NumDictVal val; val.init (); switch (op) { case OpCode_BlueValues: case OpCode_OtherBlues: case OpCode_FamilyBlues: case OpCode_FamilyOtherBlues: case OpCode_StemSnapH: case OpCode_StemSnapV: env.clear_args (); 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: val.single_val = env.argStack.pop_num (); env.clear_args (); break; case OpCode_Subrs: dictval.subrsOffset = env.argStack.pop_uint (); env.clear_args (); break; default: DictOpSet::process_op (op, env); if (!env.argStack.is_empty ()) return; break; } if (unlikely (env.in_error ())) return; dictval.add_op (op, env.substr, val); } }; struct CFF1PrivateDictOpSet_Subset : DictOpSet { static void process_op (OpCode op, NumInterpEnv& env, CFF1PrivateDictValues_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: env.clear_args (); break; case OpCode_Subrs: dictval.subrsOffset = env.argStack.pop_uint (); env.clear_args (); break; default: DictOpSet::process_op (op, env); if (!env.argStack.is_empty ()) return; break; } if (unlikely (env.in_error ())) return; dictval.add_op (op, env.substr); } }; typedef DictInterpreter<CFF1TopDictOpSet, CFF1TopDictValues, CFF1TopDictInterpEnv> CFF1TopDict_Interpreter; typedef DictInterpreter<CFF1FontDictOpSet, CFF1FontDictValues> CFF1FontDict_Interpreter; typedef DictInterpreter<CFF1PrivateDictOpSet, CFF1PrivateDictValues> CFF1PrivateDict_Interpreter; typedef CFF1Index CFF1NameIndex; typedef CFF1IndexOf<TopDict> CFF1TopDictIndex; }; /* namespace CFF */ namespace OT { using namespace CFF; struct cff1 { static const hb_tag_t tableTag = HB_OT_TAG_cff1; bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && likely (version.major == 1)); } template <typename PRIVOPSET, typename PRIVDICTVAL> struct accelerator_templ_t { void init (hb_face_t *face) { topDict.init (); fontDicts.init (); privateDicts.init (); this->blob = sc.reference_table<cff1> (face); /* setup for run-time santization */ sc.init (this->blob); sc.start_processing (); const OT::cff1 *cff = this->blob->template as<OT::cff1> (); if (cff == &Null(OT::cff1)) { fini (); return; } nameIndex = &cff->nameIndex (cff); if ((nameIndex == &Null (CFF1NameIndex)) || !nameIndex->sanitize (&sc)) { fini (); return; } topDictIndex = &StructAtOffset<CFF1TopDictIndex> (nameIndex, nameIndex->get_size ()); if ((topDictIndex == &Null (CFF1TopDictIndex)) || !topDictIndex->sanitize (&sc) || (topDictIndex->count == 0)) { fini (); return; } { /* parse top dict */ const ByteStr topDictStr = (*topDictIndex)[0]; if (unlikely (!topDictStr.sanitize (&sc))) { fini (); return; } CFF1TopDict_Interpreter top_interp; top_interp.env.init (topDictStr); topDict.init (); if (unlikely (!top_interp.interpret (topDict))) { fini (); return; } } if (is_predef_charset ()) charset = &Null(Charset); else { charset = &StructAtOffsetOrNull<Charset> (cff, topDict.CharsetOffset); if (unlikely ((charset == &Null (Charset)) || !charset->sanitize (&sc))) { fini (); return; } } fdCount = 1; if (is_CID ()) { fdArray = &StructAtOffsetOrNull<CFF1FDArray> (cff, topDict.FDArrayOffset); fdSelect = &StructAtOffsetOrNull<CFF1FDSelect> (cff, topDict.FDSelectOffset); if (unlikely ((fdArray == &Null(CFF1FDArray)) || !fdArray->sanitize (&sc) || (fdSelect == &Null(CFF1FDSelect)) || !fdSelect->sanitize (&sc, fdArray->count))) { fini (); return; } fdCount = fdArray->count; } else { fdArray = &Null(CFF1FDArray); fdSelect = &Null(CFF1FDSelect); } stringIndex = &StructAtOffset<CFF1StringIndex> (topDictIndex, topDictIndex->get_size ()); if ((stringIndex == &Null (CFF1StringIndex)) || !stringIndex->sanitize (&sc)) { fini (); return; } globalSubrs = &StructAtOffset<CFF1Subrs> (stringIndex, stringIndex->get_size ()); if ((globalSubrs != &Null (CFF1Subrs)) && !globalSubrs->sanitize (&sc)) { fini (); return; } charStrings = &StructAtOffsetOrNull<CFF1CharStrings> (cff, topDict.charStringsOffset); if ((charStrings == &Null(CFF1CharStrings)) || 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]; if (unlikely (!fontDictStr.sanitize (&sc))) { fini (); return; } CFF1FontDictValues *font; CFF1FontDict_Interpreter font_interp; font_interp.env.init (fontDictStr); font = fontDicts.push (); if (unlikely (font == &Crap(CFF1FontDictValues))) { fini (); return; } font->init (); if (unlikely (!font_interp.interpret (*font))) { fini (); return; } PRIVDICTVAL *priv = &privateDicts[i]; const ByteStr privDictStr (StructAtOffset<UnsizedByteStr> (cff, font->privateDictInfo.offset), font->privateDictInfo.size); if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; } DictInterpreter<PRIVOPSET, PRIVDICTVAL> priv_interp; priv_interp.env.init (privDictStr); priv->init (); if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; } priv->localSubrs = &StructAtOffsetOrNull<CFF1Subrs> (privDictStr.str, priv->subrsOffset); if (priv->localSubrs != &Null(CFF1Subrs) && unlikely (!priv->localSubrs->sanitize (&sc))) { fini (); return; } } } else /* non-CID */ { CFF1TopDictValues *font = &topDict; PRIVDICTVAL *priv = &privateDicts[0]; const ByteStr privDictStr (StructAtOffset<UnsizedByteStr> (cff, font->privateDictInfo.offset), font->privateDictInfo.size); if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; } DictInterpreter<PRIVOPSET, PRIVDICTVAL> priv_interp; priv_interp.env.init (privDictStr); priv->init (); if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; } priv->localSubrs = &StructAtOffsetOrNull<CFF1Subrs> (privDictStr.str, priv->subrsOffset); if (priv->localSubrs != &Null(CFF1Subrs) && unlikely (!priv->localSubrs->sanitize (&sc))) { fini (); return; } } } void fini () { sc.end_processing (); topDict.fini (); fontDicts.fini_deep (); privateDicts.fini_deep (); hb_blob_destroy (blob); blob = nullptr; } bool is_valid () const { return blob != nullptr; } bool is_CID () const { return topDict.is_CID (); } bool is_predef_charset () const { return topDict.CharsetOffset <= ExpertSubsetCharset; } unsigned int std_code_to_glyph (hb_codepoint_t code) const { hb_codepoint_t sid = lookup_standard_encoding_for_sid (code); if (unlikely (sid == CFF_UNDEF_SID)) return 0; if (charset != &Null(Charset)) return charset->get_glyph (sid, num_glyphs); else if ((topDict.CharsetOffset == ISOAdobeCharset) && (code <= 228 /*zcaron*/)) return sid; return 0; } protected: hb_blob_t *blob; hb_sanitize_context_t sc; public: const Charset *charset; const CFF1NameIndex *nameIndex; const CFF1TopDictIndex *topDictIndex; const CFF1StringIndex *stringIndex; const CFF1Subrs *globalSubrs; const CFF1CharStrings *charStrings; const CFF1FDArray *fdArray; const CFF1FDSelect *fdSelect; unsigned int fdCount; CFF1TopDictValues topDict; hb_vector_t<CFF1FontDictValues> fontDicts; hb_vector_t<PRIVDICTVAL> privateDicts; unsigned int num_glyphs; }; struct accelerator_t : accelerator_templ_t<CFF1PrivateDictOpSet, CFF1PrivateDictValues> { HB_INTERNAL bool get_extents (hb_codepoint_t glyph, hb_glyph_extents_t *extents) const; HB_INTERNAL bool get_seac_components (hb_codepoint_t glyph, hb_codepoint_t *base, hb_codepoint_t *accent) const; }; struct accelerator_subset_t : accelerator_templ_t<CFF1PrivateDictOpSet_Subset, CFF1PrivateDictValues_Subset> { void init (hb_face_t *face) { SUPER::init (face); if (blob == nullptr) return; const OT::cff1 *cff = this->blob->as<OT::cff1> (); encoding = &Null(Encoding); if (is_CID ()) { if (unlikely (charset == &Null(Charset))) { fini (); return; } } else { if (!is_predef_encoding ()) { encoding = &StructAtOffsetOrNull<Encoding> (cff, topDict.EncodingOffset); if (unlikely ((encoding == &Null (Encoding)) || !encoding->sanitize (&sc))) { fini (); return; } } } } bool is_predef_encoding () const { return topDict.EncodingOffset <= ExpertEncoding; } hb_codepoint_t glyph_to_code (hb_codepoint_t glyph) const { if (encoding != &Null(Encoding)) return encoding->get_code (glyph); else { hb_codepoint_t sid = glyph_to_sid (glyph); if (sid == 0) return 0; hb_codepoint_t code = 0; switch (topDict.EncodingOffset) { case StandardEncoding: code = lookup_standard_encoding_for_code (sid); break; case ExpertEncoding: code = lookup_expert_encoding_for_code (sid); break; default: break; } return code; } } hb_codepoint_t glyph_to_sid (hb_codepoint_t glyph) const { if (charset != &Null(Charset)) return charset->get_sid (glyph); else { hb_codepoint_t sid = 0; switch (topDict.CharsetOffset) { case ISOAdobeCharset: if (glyph <= 228 /*zcaron*/) sid = glyph; break; case ExpertCharset: sid = lookup_expert_charset_for_sid (glyph); break; case ExpertSubsetCharset: sid = lookup_expert_subset_charset_for_sid (glyph); break; default: break; } return sid; } } const Encoding *encoding; private: typedef accelerator_templ_t<CFF1PrivateDictOpSet_Subset, CFF1PrivateDictValues_Subset> SUPER; }; bool subset (hb_subset_plan_t *plan) const { hb_blob_t *cff_prime = nullptr; bool success = true; if (hb_subset_cff1 (plan, &cff_prime)) { success = success && plan->add_table (HB_OT_TAG_cff1, cff_prime); hb_blob_t *head_blob = hb_sanitize_context_t().reference_table<head> (plan->source); success = success && head_blob && plan->add_table (HB_OT_TAG_head, head_blob); hb_blob_destroy (head_blob); } else { success = false; } hb_blob_destroy (cff_prime); return success; } protected: HB_INTERNAL static hb_codepoint_t lookup_standard_encoding_for_code (hb_codepoint_t sid); HB_INTERNAL static hb_codepoint_t lookup_expert_encoding_for_code (hb_codepoint_t sid); HB_INTERNAL static hb_codepoint_t lookup_expert_charset_for_sid (hb_codepoint_t glyph); HB_INTERNAL static hb_codepoint_t lookup_expert_subset_charset_for_sid (hb_codepoint_t glyph); HB_INTERNAL static hb_codepoint_t lookup_standard_encoding_for_sid (hb_codepoint_t code); public: FixedVersion<HBUINT8> version; /* Version of CFF table. set to 0x0100u */ OffsetTo<CFF1NameIndex, HBUINT8> nameIndex; /* headerSize = Offset to Name INDEX. */ HBUINT8 offSize; /* offset size (unused?) */ public: DEFINE_SIZE_STATIC (4); }; struct cff1_accelerator_t : cff1::accelerator_t {}; } /* namespace OT */ #endif /* HB_OT_CFF1_TABLE_HH */