/* * 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_CFF1_TABLE_HH #define HB_OT_CFF1_TABLE_HH #include "hb-ot-cff-common-private.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',' ') typedef CFFIndex CFF1Index; template struct CFF1IndexOf : IndexOf {}; typedef CFFIndex CFF1Index; typedef CFF1Index CFF1CharStrings; typedef FDArray CFF1FDArray; typedef Subrs CFF1Subrs; struct CFF1FDSelect : 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 CFF1SuppEncData { 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 CFF1SuppEncData &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; /* CFF1SuppEncData 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 CFF1TopDictValues : 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_longintdict) + 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 CFF1TopDictOpSet : TopDictOpSet { static inline bool process_op (OpCode op, InterpEnv& env, CFF1TopDictValues& 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: env.argStack.clear (); break; case OpCode_CIDCount: if (unlikely (!env.argStack.check_pop_uint (dictval.cidCount))) return false; env.argStack.clear (); break; case OpCode_ROS: if (unlikely (!env.argStack.check_pop_uint (dictval.ros[2]) || !env.argStack.check_pop_uint (dictval.ros[1]) || !env.argStack.check_pop_uint (dictval.ros[0]))) return false; env.argStack.clear (); break; case OpCode_Encoding: if (unlikely (!env.argStack.check_pop_uint (dictval.EncodingOffset))) return false; env.argStack.clear (); break; case OpCode_charset: if (unlikely (!env.argStack.check_pop_uint (dictval.CharsetOffset))) return false; env.argStack.clear (); break; case OpCode_FDSelect: if (unlikely (!env.argStack.check_pop_uint (dictval.FDSelectOffset))) return false; env.argStack.clear (); break; case OpCode_Private: if (unlikely (!env.argStack.check_pop_uint (dictval.privateDictInfo.offset))) return false; if (unlikely (!env.argStack.check_pop_uint (dictval.privateDictInfo.size))) return false; env.argStack.clear (); break; default: if (unlikely (!TopDictOpSet::process_op (op, env, dictval))) return false; /* Record this operand below if stack is empty, otherwise done */ if (!env.argStack.is_empty ()) return true; break; } dictval.pushVal (op, env.substr); return true; } }; struct CFF1FontDictValues : DictValues { inline void init (void) { DictValues::init (); privateDictInfo.init (); } inline void fini (void) { DictValues::fini (); } TableInfo privateDictInfo; }; struct CFF1FontDictOpSet : DictOpSet { static inline bool process_op (OpCode op, InterpEnv& env, CFF1FontDictValues& dictval) { switch (op) { case OpCode_FontName: case OpCode_FontMatrix: case OpCode_PaintType: env.argStack.clear (); break; case OpCode_Private: if (unlikely (!env.argStack.check_pop_uint (dictval.privateDictInfo.offset))) return false; if (unlikely (!env.argStack.check_pop_uint (dictval.privateDictInfo.size))) return false; env.argStack.clear (); break; default: if (unlikely (!DictOpSet::process_op (op, env))) return false; if (!env.argStack.is_empty ()) return true; break; } dictval.pushVal (op, env.substr); return true; } }; template struct CFF1PrivateDictValues_Base : DictValues { inline void init (void) { DictValues::init (); subrsOffset = 0; localSubrs = &Null(CFF1Subrs); } 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 CFF1Subrs *localSubrs; }; typedef CFF1PrivateDictValues_Base CFF1PrivateDictValues_Subset; typedef CFF1PrivateDictValues_Base CFF1PrivateDictValues; struct CFF1PrivateDictOpSet : DictOpSet { static inline bool process_op (OpCode op, InterpEnv& env, CFF1PrivateDictValues& 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 (!env.argStack.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 (!env.argStack.check_pop_num (val.single_val))) return false; env.argStack.clear (); break; case OpCode_Subrs: if (unlikely (!env.argStack.check_pop_uint (dictval.subrsOffset))) return false; env.argStack.clear (); break; default: if (unlikely (!DictOpSet::process_op (op, env))) return false; if (!env.argStack.is_empty ()) return true; break; } dictval.pushVal (op, env.substr, val); return true; } }; struct CFF1PrivateDictOpSet_Subset : DictOpSet { static inline bool process_op (OpCode op, InterpEnv& 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.argStack.clear (); break; case OpCode_Subrs: if (unlikely (!env.argStack.check_pop_uint (dictval.subrsOffset))) return false; env.argStack.clear (); break; default: if (unlikely (!DictOpSet::process_op (op, env))) return false; if (!env.argStack.is_empty ()) return true; break; } dictval.pushVal (op, env.substr); return true; } }; typedef DictInterpreter CFF1TopDict_Interpreter; typedef DictInterpreter CFF1FontDict_Interpreter; typedef DictInterpreter CFF1PrivateDict_Interpreter; typedef CFF1Index CFF1NameIndex; typedef CFF1IndexOf CFF1TopDictIndex; typedef CFF1Index CFF1StringIndex; }; /* namespace CFF */ namespace OT { using namespace CFF; struct cff1 { static const hb_tag_t tableTag = HB_OT_TAG_cff1; 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::cff1 *cff = this->blob->template as (); if (cff == &Null(OT::cff1)) { fini (); return; } nameIndex = &cff->nameIndex (cff); if ((nameIndex == &Null (CFF1NameIndex)) || !nameIndex->sanitize (&sc)) { fini (); return; } topDictIndex = &StructAtOffset (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); if (unlikely (!top_interp.interpret (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(CFF1FDArray)) || !fdArray->sanitize (&sc) || (fdSelect == &Null(CFF1FDSelect)) || !fdSelect->sanitize (&sc, fdArray->count))) { fini (); return; } fdCount = fdArray->count; } else { fdArray = &Null(CFF1FDArray); fdSelect = &Null(CFF1FDSelect); if (topDicts[0].EncodingOffset != CFF1TopDictValues::StandardEncoding && topDicts[0].EncodingOffset != CFF1TopDictValues::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 (CFF1StringIndex)) || !stringIndex->sanitize (&sc)) { fini (); return; } globalSubrs = &StructAtOffset (stringIndex, stringIndex->get_size ()); if ((globalSubrs != &Null (CFF1Subrs)) && !stringIndex->sanitize (&sc)) { fini (); return; } charStrings = &StructAtOffsetOrNull (cff, topDicts[0].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_interp.interpret (*font))) { fini (); return; } PrivDictVal *priv = &privateDicts[i]; const ByteStr privDictStr (StructAtOffset (cff, font->privateDictInfo.offset), font->privateDictInfo.size); if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; } DictInterpreter priv_interp; priv_interp.env.init (privDictStr); if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; } priv->localSubrs = &StructAtOffsetOrNull (privDictStr.str, priv->subrsOffset); if (priv->localSubrs != &Null(CFF1Subrs) && unlikely (!priv->localSubrs->sanitize (&sc))) { fini (); return; } } } else /* non-CID */ { CFF1TopDictValues *font = &topDicts[0]; PrivDictVal *priv = &privateDicts[0]; const ByteStr privDictStr (StructAtOffset (cff, font->privateDictInfo.offset), font->privateDictInfo.size); if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; } DictInterpreter priv_interp; priv_interp.env.init (privDictStr); if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; } priv->localSubrs = &StructAtOffsetOrNull (privDictStr.str, priv->subrsOffset); if (priv->localSubrs != &Null(CFF1Subrs) && 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 CFF1NameIndex *nameIndex; const CFF1TopDictIndex *topDictIndex; const CFF1StringIndex *stringIndex; const Encoding *encoding; const Charset *charset; const CFF1Subrs *globalSubrs; const CFF1CharStrings *charStrings; const CFF1FDArray *fdArray; const CFF1FDSelect *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_cff1 (plan, &cff_prime)) { success = success && plan->add_table (HB_OT_TAG_cff1, 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_CFF1_TABLE_HH */