diff --git a/src/hb-cff-interp-common.hh b/src/hb-cff-interp-common.hh index fcf4d72e5..9567b6b66 100644 --- a/src/hb-cff-interp-common.hh +++ b/src/hb-cff-interp-common.hh @@ -448,6 +448,8 @@ struct SubByteStr bool error; }; +typedef hb_vector_t ByteStrArray; + /* stack */ template struct Stack @@ -652,6 +654,53 @@ struct OpSerializer } }; +template +struct ParsedValues +{ + inline void init (void) + { + opStart = 0; + values.init (); + } + + inline void fini (void) + { + values.fini_deep (); + } + + inline void add_op (OpCode op, const SubByteStr& substr = SubByteStr ()) + { + VAL *val = values.push (); + val->op = op; + assert (substr.offset >= opStart); + val->str = ByteStr (substr.str, opStart, substr.offset - opStart); + opStart = substr.offset; + } + + inline void add_op (OpCode op, const SubByteStr& substr, const VAL &v) + { + VAL *val = values.push (v); + val->op = op; + assert (substr.offset >= opStart); + val->str = ByteStr (substr.str, opStart, substr.offset - opStart); + opStart = substr.offset; + } + + inline bool has_op (OpCode op) const + { + for (unsigned int i = 0; i < get_count (); i++) + if (get_value (i).op == op) return true; + return false; + } + + inline unsigned get_count (void) const { return values.len; } + inline const VAL &get_value (unsigned int i) const { return values[i]; } + inline const VAL &operator [] (unsigned int i) const { return get_value (i); } + + unsigned int opStart; + hb_vector_t values; +}; + template struct InterpEnv { diff --git a/src/hb-cff-interp-cs-common.hh b/src/hb-cff-interp-cs-common.hh index f22d753eb..bddcb3215 100644 --- a/src/hb-cff-interp-cs-common.hh +++ b/src/hb-cff-interp-cs-common.hh @@ -33,8 +33,31 @@ namespace CFF { using namespace OT; +enum CSType { + CSType_CharString, + CSType_GlobalSubr, + CSType_LocalSubr +}; + +struct CallContext +{ + inline void init (const SubByteStr substr_=SubByteStr (), CSType type_=CSType_CharString, unsigned int subr_num_=0) + { + substr = substr_; + type = type_; + subr_num = subr_num_; + } + + inline void fini (void) {} + + SubByteStr substr; + CSType type; + unsigned int subr_num; +}; + /* call stack */ -struct CallStack : Stack {}; +const unsigned int kMaxCallLimit = 10; +struct CallStack : Stack {}; template struct BiasedSubrs @@ -80,6 +103,7 @@ struct CSInterpEnv : InterpEnv { InterpEnv::init (str); + context.init (str, CSType_CharString); seen_moveto = true; seen_hintmask = false; hstem_count = 0; @@ -114,23 +138,29 @@ struct CSInterpEnv : InterpEnv return true; } - inline bool callSubr (const BiasedSubrs& biasedSubrs) + inline void callSubr (const BiasedSubrs& biasedSubrs, CSType type) { unsigned int subr_num; - if (unlikely (!popSubrNum (biasedSubrs, subr_num))) - return false; - callStack.push (SUPER::substr); - SUPER::substr = (*biasedSubrs.subrs)[subr_num]; + if (unlikely (!popSubrNum (biasedSubrs, subr_num) + || callStack.get_count () >= kMaxCallLimit)) + { + SUPER::set_error (); + return; + } + context.substr = SUPER::substr; + callStack.push (context); - return true; + context.init ( (*biasedSubrs.subrs)[subr_num], type, subr_num); + SUPER::substr = context.substr; } inline void returnFromSubr (void) { if (unlikely (SUPER::substr.in_error ())) SUPER::set_error (); - SUPER::substr = callStack.pop (); + context = callStack.pop (); + SUPER::substr = context.substr; } inline void determine_hintmask_size (void) @@ -153,6 +183,7 @@ struct CSInterpEnv : InterpEnv inline void moveto (const Point &pt_ ) { pt = pt_; } public: + CallContext context; bool endchar_flag; bool seen_moveto; bool seen_hintmask; @@ -206,6 +237,7 @@ struct CSOpSet : OpSet env.returnFromSubr (); break; case OpCode_endchar: + OPSET::check_width (op, env, param); env.set_endchar (true); OPSET::flush_args_and_op (op, env, param); break; @@ -215,36 +247,42 @@ struct CSOpSet : OpSet break; case OpCode_callsubr: - env.callSubr (env.localSubrs); + env.callSubr (env.localSubrs, CSType_LocalSubr); break; case OpCode_callgsubr: - env.callSubr (env.globalSubrs); + env.callSubr (env.globalSubrs, CSType_GlobalSubr); break; case OpCode_hstem: case OpCode_hstemhm: + OPSET::check_width (op, env, param); OPSET::process_hstem (op, env, param); break; case OpCode_vstem: case OpCode_vstemhm: + OPSET::check_width (op, env, param); OPSET::process_vstem (op, env, param); break; case OpCode_hintmask: case OpCode_cntrmask: + OPSET::check_width (op, env, param); OPSET::process_hintmask (op, env, param); break; case OpCode_rmoveto: + OPSET::check_width (op, env, param); PATH::rmoveto (env, param); - process_post_move (op, env, param); + OPSET::process_post_move (op, env, param); break; case OpCode_hmoveto: + OPSET::check_width (op, env, param); PATH::hmoveto (env, param); - process_post_move (op, env, param); + OPSET::process_post_move (op, env, param); break; case OpCode_vmoveto: + OPSET::check_width (op, env, param); PATH::vmoveto (env, param); - process_post_move (op, env, param); + OPSET::process_post_move (op, env, param); break; case OpCode_rlineto: PATH::rlineto (env, param); @@ -340,6 +378,9 @@ struct CSOpSet : OpSet OPSET::flush_args_and_op (op, env, param); } + static inline void check_width (OpCode op, ENV &env, PARAM& param) + {} + static inline void process_post_move (OpCode op, ENV &env, PARAM& param) { if (!env.seen_moveto) @@ -355,15 +396,15 @@ struct CSOpSet : OpSet OPSET::flush_args_and_op (op, env, param); } - static inline void flush_args_and_op (OpCode op, ENV &env, PARAM& param, unsigned int start_arg = 0) + static inline void flush_args_and_op (OpCode op, ENV &env, PARAM& param) { - OPSET::flush_args (env, param, start_arg); + OPSET::flush_args (env, param); OPSET::flush_op (op, env, param); } - static inline void flush_args (ENV &env, PARAM& param, unsigned int start_arg = 0) + static inline void flush_args (ENV &env, PARAM& param) { - env.pop_n_args (env.argStack.get_count () - start_arg); + env.pop_n_args (env.argStack.get_count ()); } static inline void flush_op (OpCode op, ENV &env, PARAM& param) @@ -375,6 +416,24 @@ struct CSOpSet : OpSet OPSET::flush_args_and_op (op, env, param); } + static inline bool is_number_op (OpCode op) + { + switch (op) + { + case OpCode_shortint: + case OpCode_fixedcs: + case OpCode_TwoBytePosInt0: case OpCode_TwoBytePosInt1: + case OpCode_TwoBytePosInt2: case OpCode_TwoBytePosInt3: + case OpCode_TwoByteNegInt0: case OpCode_TwoByteNegInt1: + case OpCode_TwoByteNegInt2: case OpCode_TwoByteNegInt3: + return true; + + default: + /* 1-byte integer */ + return (OpCode_OneByteIntFirst <= op) && (op <= OpCode_OneByteIntLast); + } + } + protected: typedef OpSet SUPER; }; diff --git a/src/hb-cff-interp-dict-common.hh b/src/hb-cff-interp-dict-common.hh index 56988a33e..88fd34560 100644 --- a/src/hb-cff-interp-dict-common.hh +++ b/src/hb-cff-interp-dict-common.hh @@ -50,50 +50,7 @@ struct DictVal : OpStr typedef DictVal NumDictVal; -template -struct DictValues -{ - inline void init (void) - { - opStart = 0; - values.init (); - } - - inline void fini (void) - { - values.fini_deep (); - } - - inline void addOp (OpCode op, const SubByteStr& substr = SubByteStr ()) - { - VAL *val = values.push (); - val->op = op; - val->str = ByteStr (substr.str, opStart, substr.offset - opStart); - opStart = substr.offset; - } - - inline void addOp (OpCode op, const SubByteStr& substr, const VAL &v) - { - VAL *val = values.push (v); - val->op = op; - val->str = ByteStr (substr.str, opStart, substr.offset - opStart); - opStart = substr.offset; - } - - inline bool hasOp (OpCode op) const - { - for (unsigned int i = 0; i < getNumValues (); i++) - if (getValue (i).op == op) return true; - return false; - } - - inline unsigned getNumValues (void) const { return values.len; } - inline const VAL &getValue (unsigned int i) const { return values[i]; } - inline const VAL &operator [] (unsigned int i) const { return getValue (i); } - - unsigned int opStart; - hb_vector_t values; -}; +template struct DictValues : ParsedValues {}; template struct TopDictValues : DictValues diff --git a/src/hb-cff1-interp-cs.hh b/src/hb-cff1-interp-cs.hh index d0114613a..8a10abc26 100644 --- a/src/hb-cff1-interp-cs.hh +++ b/src/hb-cff1-interp-cs.hh @@ -33,6 +33,8 @@ namespace CFF { using namespace OT; +typedef BiasedSubrs CFF1BiasedSubrs; + struct CFF1CSInterpEnv : CSInterpEnv { template @@ -41,6 +43,7 @@ struct CFF1CSInterpEnv : CSInterpEnv SUPER::init (str, *acc.globalSubrs, *acc.privateDicts[fd].localSubrs); processed_width = false; has_width = false; + arg_start = 0; } inline void fini (void) @@ -48,24 +51,26 @@ struct CFF1CSInterpEnv : CSInterpEnv SUPER::fini (); } - inline unsigned int check_width (void) + inline void set_width (void) { - unsigned int arg_start = 0; - if (!processed_width) + if (likely (!processed_width && (SUPER::argStack.get_count () > 0))) { - if ((SUPER::argStack.get_count () & 1) != 0) - { - width = SUPER::argStack[0]; - has_width = true; - arg_start = 1; - } + width = SUPER::argStack[0]; + has_width = true; processed_width = true; + arg_start = 1; } - return arg_start; + } + + inline void clear_args (void) + { + arg_start = 0; + SUPER::clear_args (); } bool processed_width; bool has_width; + unsigned int arg_start; Number width; private: @@ -77,10 +82,45 @@ struct CFF1CSOpSet : CSOpSet { /* PostScript-originated legacy opcodes (OpCode_add etc) are unsupported */ - static inline void flush_args (CFF1CSInterpEnv &env, PARAM& param, unsigned int start_arg = 0) + static inline void check_width (OpCode op, CFF1CSInterpEnv &env, PARAM& param) { - start_arg = env.check_width (); - SUPER::flush_args (env, param, start_arg); + if (!env.processed_width) + { + bool has_width = false; + switch (op) + { + default: + case OpCode_endchar: + has_width = (env.argStack.get_count () > 0); + break; + case OpCode_hstem: + case OpCode_hstemhm: + case OpCode_hintmask: + case OpCode_cntrmask: + has_width = ((env.argStack.get_count () & 1) != 0); + break; + case OpCode_hmoveto: + case OpCode_vmoveto: + has_width = (env.argStack.get_count () > 1); + break; + case OpCode_rmoveto: + has_width = (env.argStack.get_count () > 2); + break; + } + if (has_width) + { + env.set_width (); + OPSET::process_width (env, param); + } + } + } + + static inline void process_width (CFF1CSInterpEnv &env, PARAM& param) + {} + + static inline void flush_args (CFF1CSInterpEnv &env, PARAM& param) + { + SUPER::flush_args (env, param); env.clear_args (); /* pop off width */ } diff --git a/src/hb-cff2-interp-cs.hh b/src/hb-cff2-interp-cs.hh index 71f2b4090..18877463c 100644 --- a/src/hb-cff2-interp-cs.hh +++ b/src/hb-cff2-interp-cs.hh @@ -74,6 +74,7 @@ struct BlendArg : Number }; typedef InterpEnv BlendInterpEnv; +typedef BiasedSubrs CFF2BiasedSubrs; struct CFF2CSInterpEnv : CSInterpEnv { diff --git a/src/hb-ot-cff-common.hh b/src/hb-ot-cff-common.hh index a7ff1972b..40ae9578c 100644 --- a/src/hb-ot-cff-common.hh +++ b/src/hb-ot-cff-common.hh @@ -61,6 +61,18 @@ struct code_pair hb_codepoint_t glyph; }; +typedef hb_vector_t StrBuff; +struct StrBuffArray : hb_vector_t +{ + inline unsigned int total_size (void) const + { + unsigned int size = 0; + for (unsigned int i = 0; i < len; i++) + size += (*this)[i].len; + return size; + } +}; + /* CFF INDEX */ template struct CFFIndex @@ -95,7 +107,7 @@ struct CFFIndex inline bool serialize (hb_serialize_context_t *c, unsigned int offSize_, - const hb_vector_t &byteArray) + const ByteStrArray &byteArray) { TRACE_SERIALIZE (this); /* serialize CFFIndex header */ @@ -126,6 +138,22 @@ struct CFFIndex return_trace (true); } + inline bool serialize (hb_serialize_context_t *c, + unsigned int offSize_, + const StrBuffArray &buffArray) + { + ByteStrArray byteArray; + byteArray.init (); + byteArray.resize (buffArray.len); + for (unsigned int i = 0; i < byteArray.len; i++) + { + byteArray[i] = ByteStr (buffArray[i].arrayZ (), buffArray[i].len); + } + bool result = this->serialize (c, offSize_, byteArray); + byteArray.fini (); + return result; + } + inline void set_offset_at (unsigned int index, unsigned int offset) { HBUINT8 *p = offsets + offSize * index + offSize; @@ -280,7 +308,7 @@ struct Dict : UnsizedByteStr PARAM& param) { TRACE_SERIALIZE (this); - for (unsigned int i = 0; i < dictval.getNumValues (); i++) + for (unsigned int i = 0; i < dictval.get_count (); i++) { if (unlikely (!opszr.serialize (c, dictval[i], param))) return_trace (false); @@ -294,7 +322,7 @@ struct Dict : UnsizedByteStr OP_SERIALIZER& opszr) { unsigned int size = 0; - for (unsigned int i = 0; i < dictval.getNumValues (); i++) + for (unsigned int i = 0; i < dictval.get_count (); i++) size += opszr.calculate_serialized_size (dictval[i]); return size; } @@ -383,6 +411,9 @@ struct Remap : hb_vector_t inline bool excludes (hb_codepoint_t id) const { return (id < len) && ((*this)[id] == CFF_UNDEF_CODE); } + inline bool includes (hb_codepoint_t id) const + { return !excludes (id); } + inline hb_codepoint_t operator[] (hb_codepoint_t i) const { if (fullset ()) @@ -649,52 +680,8 @@ struct FDSelect { template struct Subrs : CFFIndex { - inline bool serialize (hb_serialize_context_t *c, const Subrs &subrs, unsigned int offSize, const hb_set_t *set, const ByteStr& nullStr = ByteStr()) - { - TRACE_SERIALIZE (this); - if (&subrs == &Null(Subrs)) - return_trace (true); - if ((subrs.count == 0) || (set == nullptr) || (hb_set_is_empty (set))) - { - if (!unlikely (c->allocate_size (COUNT::static_size))) - return_trace (false); - CFFIndex::count.set (0); - return_trace (true); - } - - hb_vector_t bytesArray; - bytesArray.init (); - if (!bytesArray.resize (subrs.count)) - return_trace (false); - for (hb_codepoint_t i = 0; i < subrs.count; i++) - bytesArray[i] = (hb_set_has (set, i))? subrs[i]: nullStr; - - bool result = CFFIndex::serialize (c, offSize, bytesArray); - bytesArray.fini (); - return_trace (result); - } - - /* in parallel to above */ - inline unsigned int calculate_serialized_size (unsigned int &offSize /*OUT*/, const hb_set_t *set, unsigned int nullStrSize = 0) const - { - if (this == &Null(Subrs)) - return 0; - unsigned int count_ = CFFIndex::count; - offSize = 0; - if ((count_ == 0) || (hb_set_get_population (set) == 0)) - return COUNT::static_size; - - unsigned int dataSize = 0; - for (hb_codepoint_t i = 0; i < count_; i++) - { - if (hb_set_has (set, i)) - dataSize += (*this)[i].len; - else - dataSize += nullStrSize; - } - offSize = calcOffSize(dataSize); - return CFFIndex::calculate_serialized_size (offSize, count_, dataSize); - } + typedef COUNT count_type; + typedef CFFIndex SUPER; }; } /* namespace CFF */ diff --git a/src/hb-ot-cff1-table.hh b/src/hb-ot-cff1-table.hh index b99b8cfc6..bc67e49b5 100644 --- a/src/hb-ot-cff1-table.hh +++ b/src/hb-ot-cff1-table.hh @@ -537,7 +537,7 @@ struct CFF1StringIndex : CFF1Index return_trace (true); } - hb_vector_t bytesArray; + ByteStrArray bytesArray; bytesArray.init (); if (!bytesArray.resize (sidmap.get_count ())) return_trace (false); @@ -757,7 +757,7 @@ struct CFF1TopDictOpSet : TopDictOpSet if (unlikely (env.in_error ())) return; - dictval.addOp (op, env.substr, val); + dictval.add_op (op, env.substr, val); } }; @@ -806,7 +806,7 @@ struct CFF1FontDictOpSet : DictOpSet if (unlikely (env.in_error ())) return; - dictval.addOp (op, env.substr); + dictval.add_op (op, env.substr); } }; @@ -828,11 +828,11 @@ struct CFF1PrivateDictValues_Base : DictValues inline unsigned int calculate_serialized_size (void) const { unsigned int size = 0; - for (unsigned int i = 0; i < DictValues::getNumValues; i++) - if (DictValues::getValue (i).op == OpCode_Subrs) + for (unsigned int i = 0; i < DictValues::get_count; i++) + if (DictValues::get_value (i).op == OpCode_Subrs) size += OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs); else - size += DictValues::getValue (i).str.len; + size += DictValues::get_value (i).str.len; return size; } @@ -886,7 +886,7 @@ struct CFF1PrivateDictOpSet : DictOpSet if (unlikely (env.in_error ())) return; - dictval.addOp (op, env.substr, val); + dictval.add_op (op, env.substr, val); } }; @@ -928,7 +928,7 @@ struct CFF1PrivateDictOpSet_Subset : DictOpSet if (unlikely (env.in_error ())) return; - dictval.addOp (op, env.substr); + dictval.add_op (op, env.substr); } }; @@ -989,6 +989,7 @@ struct cff1 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; } } @@ -1041,12 +1042,14 @@ struct cff1 CFF1FontDict_Interpreter font_interp; font_interp.env.init (fontDictStr); font = fontDicts.push (); + font->init (); 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); + priv->init (); if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; } priv->localSubrs = &StructAtOffsetOrNull (privDictStr.str, priv->subrsOffset); @@ -1064,6 +1067,7 @@ struct cff1 if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; } DictInterpreter priv_interp; priv_interp.env.init (privDictStr); + priv->init (); if (unlikely (!priv_interp.interpret (*priv))) { fini (); return; } priv->localSubrs = &StructAtOffsetOrNull (privDictStr.str, priv->subrsOffset); diff --git a/src/hb-ot-cff2-table.hh b/src/hb-ot-cff2-table.hh index e27ace0d1..a12ef561e 100644 --- a/src/hb-ot-cff2-table.hh +++ b/src/hb-ot-cff2-table.hh @@ -153,9 +153,9 @@ struct CFF2TopDictValues : TopDictValues<> inline unsigned int calculate_serialized_size (void) const { unsigned int size = 0; - for (unsigned int i = 0; i < getNumValues (); i++) + for (unsigned int i = 0; i < get_count (); i++) { - OpCode op = getValue (i).op; + OpCode op = get_value (i).op; switch (op) { case OpCode_vstore: @@ -163,7 +163,7 @@ struct CFF2TopDictValues : TopDictValues<> size += OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (op); break; default: - size += TopDictValues<>::calculate_serialized_op_size (getValue (i)); + size += TopDictValues<>::calculate_serialized_op_size (get_value (i)); break; } } @@ -183,7 +183,7 @@ struct CFF2TopDictOpSet : TopDictOpSet<> { DictVal val; val.init (); - dictval.addOp (op, env.substr); + dictval.add_op (op, env.substr); env.clear_args (); } break; @@ -205,7 +205,7 @@ struct CFF2TopDictOpSet : TopDictOpSet<> if (unlikely (env.in_error ())) return; - dictval.addOp (op, env.substr); + dictval.add_op (op, env.substr); } typedef TopDictOpSet<> SUPER; @@ -246,7 +246,7 @@ struct CFF2FontDictOpSet : DictOpSet if (unlikely (env.in_error ())) return; - dictval.addOp (op, env.substr); + dictval.add_op (op, env.substr); } private: @@ -272,11 +272,11 @@ struct CFF2PrivateDictValues_Base : DictValues inline unsigned int calculate_serialized_size (void) const { unsigned int size = 0; - for (unsigned int i = 0; i < DictValues::getNumValues; i++) - if (DictValues::getValue (i).op == OpCode_Subrs) + for (unsigned int i = 0; i < DictValues::get_count; i++) + if (DictValues::get_value (i).op == OpCode_Subrs) size += OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs); else - size += DictValues::getValue (i).str.len; + size += DictValues::get_value (i).str.len; return size; } @@ -359,7 +359,7 @@ struct CFF2PrivateDictOpSet : DictOpSet if (unlikely (env.in_error ())) return; - dictval.addOp (op, env.substr, val); + dictval.add_op (op, env.substr, val); } }; @@ -401,7 +401,7 @@ struct CFF2PrivateDictOpSet_Subset : DictOpSet if (unlikely (env.in_error ())) return; - dictval.addOp (op, env.substr); + dictval.add_op (op, env.substr); } private: @@ -453,6 +453,7 @@ struct cff2 if (unlikely (!topDictStr.sanitize (&sc))) { fini (); return; } CFF2TopDict_Interpreter top_interp; top_interp.env.init (topDictStr); + topDict.init (); if (unlikely (!top_interp.interpret (topDict))) { fini (); return; } } @@ -484,12 +485,14 @@ struct cff2 CFF2FontDict_Interpreter font_interp; font_interp.env.init (fontDictStr); font = fontDicts.push (); + font->init (); if (unlikely (!font_interp.interpret (*font))) { fini (); return; } const ByteStr privDictStr (StructAtOffsetOrNull (cff2, font->privateDictInfo.offset), font->privateDictInfo.size); if (unlikely (!privDictStr.sanitize (&sc))) { fini (); return; } DictInterpreter priv_interp; priv_interp.env.init(privDictStr); + privateDicts[i].init (); if (unlikely (!priv_interp.interpret (privateDicts[i]))) { fini (); return; } privateDicts[i].localSubrs = &StructAtOffsetOrNull (privDictStr.str, privateDicts[i].subrsOffset); diff --git a/src/hb-subset-cff-common.hh b/src/hb-subset-cff-common.hh index 549436414..6debb85db 100644 --- a/src/hb-subset-cff-common.hh +++ b/src/hb-subset-cff-common.hh @@ -35,74 +35,101 @@ namespace CFF { /* Used for writing a temporary charstring */ -struct ByteStrBuff : hb_vector_t +struct StrEncoder { - inline bool encode_byte (unsigned char b) + inline StrEncoder (StrBuff &buff_) + : buff (buff_), error (false) + {} + + inline void reset (void) { - return (push ((const char)b) != &Crap(char)); + buff.resize (0); } - inline bool encode_int (int v) + inline void encode_byte (unsigned char b) + { + if (unlikely (buff.push ((const char)b) == &Crap(char))) + set_error (); + } + + inline void encode_int (int v) { if ((-1131 <= v) && (v <= 1131)) { if ((-107 <= v) && (v <= 107)) - return encode_byte (v + 139); + encode_byte (v + 139); else if (v > 0) { v -= 108; - return encode_byte ((v >> 8) + OpCode_TwoBytePosInt0) && encode_byte (v & 0xFF); + encode_byte ((v >> 8) + OpCode_TwoBytePosInt0); + encode_byte (v & 0xFF); } else { v = -v - 108; - return encode_byte ((v >> 8) + OpCode_TwoByteNegInt0) && encode_byte (v & 0xFF); + encode_byte ((v >> 8) + OpCode_TwoByteNegInt0); + encode_byte (v & 0xFF); } } - if (unlikely (v < -32768)) - v = -32768; - else if (unlikely (v > 32767)) - v = 32767; - return encode_byte (OpCode_shortint) && - encode_byte ((v >> 8) & 0xFF) && - encode_byte (v & 0xFF); + else + { + if (unlikely (v < -32768)) + v = -32768; + else if (unlikely (v > 32767)) + v = 32767; + encode_byte (OpCode_shortint); + encode_byte ((v >> 8) & 0xFF); + encode_byte (v & 0xFF); + } } - inline bool encode_num (const Number& n) + inline void encode_num (const Number& n) { if (n.in_int_range ()) { - return encode_int (n.to_int ()); + encode_int (n.to_int ()); } else { int32_t v = n.to_fixed (); - return encode_byte (OpCode_fixedcs) && - encode_byte ((v >> 24) & 0xFF) && - encode_byte ((v >> 16) & 0xFF) && - encode_byte ((v >> 8) & 0xFF) && - encode_byte (v & 0xFF); + encode_byte (OpCode_fixedcs); + encode_byte ((v >> 24) & 0xFF); + encode_byte ((v >> 16) & 0xFF); + encode_byte ((v >> 8) & 0xFF); + encode_byte (v & 0xFF); } } - inline bool encode_op (OpCode op) + inline void encode_op (OpCode op) { if (Is_OpCode_ESC (op)) - return encode_byte (OpCode_escape) && - encode_byte (Unmake_OpCode_ESC (op)); + { + encode_byte (OpCode_escape); + encode_byte (Unmake_OpCode_ESC (op)); + } else - return encode_byte (op); + encode_byte (op); } -}; -struct ByteStrBuffArray : hb_vector_t -{ - inline void fini (void) + inline void copy_str (const ByteStr &str) { - for (unsigned int i = 0; i < len; i++) - hb_vector_t::operator[] (i).fini (); - hb_vector_t::fini (); + unsigned int offset = buff.len; + buff.resize (offset + str.len); + if (unlikely (buff.len < offset + str.len)) + { + set_error (); + return; + } + memcpy (&buff[offset], &str.str[0], str.len); } + + inline bool is_error (void) const { return error; } + + protected: + inline void set_error (void) { error = true; } + + StrBuff &buff; + bool error; }; struct CFFSubTableOffsets { @@ -215,8 +242,8 @@ struct CFFFontDict_OpSerializer : OpSerializer struct CFFPrivateDict_OpSerializer : OpSerializer { - inline CFFPrivateDict_OpSerializer (bool drop_hints_=false) - : drop_hints (drop_hints_) {} + inline CFFPrivateDict_OpSerializer (bool desubroutinize_, bool drop_hints_) + : desubroutinize (desubroutinize_), drop_hints (drop_hints_) {} inline bool serialize (hb_serialize_context_t *c, const OpStr &opstr, @@ -227,7 +254,12 @@ struct CFFPrivateDict_OpSerializer : OpSerializer if (drop_hints && DictOpSet::is_hint_op (opstr.op)) return true; if (opstr.op == OpCode_Subrs) - return_trace (true); + { + if (desubroutinize) + return_trace (true); + else + return_trace (FontDict::serialize_offset4_op (c, opstr.op, subrsOffset)); + } else return_trace (copy_opstr (c, opstr)); } @@ -237,18 +269,24 @@ struct CFFPrivateDict_OpSerializer : OpSerializer if (drop_hints && DictOpSet::is_hint_op (opstr.op)) return 0; if (opstr.op == OpCode_Subrs) - return 0; + { + if (desubroutinize) + return 0; + else + return OpCode_Size (OpCode_longintdict) + 4 + OpCode_Size (opstr.op); + } else return opstr.str.len; } protected: + const bool desubroutinize; const bool drop_hints; }; struct FlattenParam { - ByteStrBuff &flatStr; + StrBuff &flatStr; bool drop_hints; }; @@ -263,7 +301,7 @@ struct SubrFlattener drop_hints (drop_hints_) {} - inline bool flatten (ByteStrBuffArray &flat_charstrings) + inline bool flatten (StrBuffArray &flat_charstrings) { if (!flat_charstrings.resize (glyphs.len)) return false; @@ -287,6 +325,594 @@ struct SubrFlattener const hb_vector_t &glyphs; bool drop_hints; }; + +struct SubrClosures +{ + inline SubrClosures (void) + : valid (false), + global_closure (nullptr) + { + local_closures.init (); + } + + inline void init (unsigned int fd_count) + { + valid = true; + global_closure = hb_set_create (); + if (global_closure == hb_set_get_empty ()) + valid = false; + if (!local_closures.resize (fd_count)) + valid = false; + + for (unsigned int i = 0; i < local_closures.len; i++) + { + local_closures[i] = hb_set_create (); + if (local_closures[i] == hb_set_get_empty ()) + valid = false; + } + } + + inline void fini (void) + { + hb_set_destroy (global_closure); + for (unsigned int i = 0; i < local_closures.len; i++) + hb_set_destroy (local_closures[i]); + local_closures.fini (); + } + + inline void reset (void) + { + hb_set_clear (global_closure); + for (unsigned int i = 0; i < local_closures.len; i++) + hb_set_clear (local_closures[i]); + } + + bool is_valid (void) const { return valid; } + bool valid; + hb_set_t *global_closure; + hb_vector_t local_closures; +}; + +struct ParsedCSOp : OpStr +{ + inline void init (unsigned int subr_num_ = 0) + { + OpStr::init (); + flags = kDropFlag_None; + subr_num = subr_num_; + } + + inline void fini (void) + { + OpStr::fini (); + } + + inline bool for_keep (void) const { return (flags & kDropFlag_Keep) != 0; } + inline bool for_drop (void) const { return (flags & kDropFlag_Drop) != 0; } + inline void set_drop (void) { if (!for_keep ()) flags |= kDropFlag_Drop; } + inline void set_keep (void) { flags |= kDropFlag_Keep; } + + enum DropFlag + { + kDropFlag_None = 0, + kDropFlag_Drop = 1, + kDropFlag_Keep = 2 + }; + + unsigned int flags; + unsigned int subr_num; +}; + +struct ParsedCStr : ParsedValues +{ + inline void init (void) + { + SUPER::init (); + parsed = false; + hint_removed = false; + has_prefix_ = false; + } + + inline void add_op (OpCode op, const SubByteStr& substr) + { + if (!is_parsed ()) + SUPER::add_op (op, substr); + } + + inline void addCallOp (OpCode op, const SubByteStr& substr, unsigned int subr_num) + { + if (!is_parsed ()) + { + unsigned int parsed_len = get_count (); + if (likely (parsed_len > 0)) + values[parsed_len-1].set_drop (); + + ParsedCSOp val; + val.init (subr_num); + SUPER::add_op (op, substr, val); + } + } + + inline void set_prefix (const Number &num, OpCode op = OpCode_Invalid) + { + has_prefix_ = true; + prefix_op_ = op; + prefix_num_ = num; + } + + inline bool is_parsed (void) const { return parsed; } + inline void set_parsed (void) { parsed = true; } + inline bool is_hint_removed (void) const { return hint_removed; } + inline void set_hint_removed (void) { hint_removed = true; } + inline bool has_prefix (void) const { return has_prefix_; } + inline OpCode prefix_op (void) const { return prefix_op_; } + inline const Number &prefix_num (void) const { return prefix_num_; } + + protected: + bool parsed; + bool hint_removed; + bool has_prefix_; + OpCode prefix_op_; + Number prefix_num_; + + private: + typedef ParsedValues SUPER; +}; + +struct ParsedCStrs : hb_vector_t +{ + inline void init (unsigned int len_ = 0) + { + hb_vector_t::init (); + resize (len_); + for (unsigned int i = 0; i < len; i++) + (*this)[i].init (); + } +}; + +struct SubrSubsetParam +{ + inline void init (ParsedCStr *parsed_charstring_, + ParsedCStrs *parsed_global_subrs_, ParsedCStrs *parsed_local_subrs_, + hb_set_t *global_closure_, hb_set_t *local_closure_, + bool drop_hints_) + { + parsed_charstring = parsed_charstring_; + current_parsed_str = parsed_charstring; + parsed_global_subrs = parsed_global_subrs_; + parsed_local_subrs = parsed_local_subrs_; + global_closure = global_closure_; + local_closure = local_closure_; + drop_hints = drop_hints_; + } + + template + inline void set_current_str (ENV &env) + { + const CallContext &context = env.context; + + switch (context.type) + { + case CSType_CharString: + current_parsed_str = parsed_charstring; + break; + + case CSType_LocalSubr: + if (likely (context.subr_num < parsed_local_subrs->len)) + current_parsed_str = &(*parsed_local_subrs)[context.subr_num]; + else + env.set_error (); + break; + + case CSType_GlobalSubr: + if (likely (context.subr_num < parsed_global_subrs->len)) + current_parsed_str = &(*parsed_global_subrs)[context.subr_num]; + else + env.set_error (); + break; + + default: + assert (0); + } + } + + ParsedCStr *current_parsed_str; + + ParsedCStr *parsed_charstring; + ParsedCStrs *parsed_global_subrs; + ParsedCStrs *parsed_local_subrs; + hb_set_t *global_closure; + hb_set_t *local_closure; + bool drop_hints; +}; + +struct SubrRemap : Remap +{ + inline void create (hb_set_t *closure) + { + /* create a remapping of subroutine numbers from old to new. + * no optimization based on usage counts. fonttools doesn't appear doing that either. + */ + reset (closure->get_max () + 1); + for (hb_codepoint_t old_num = 0; old_num < len; old_num++) + { + if (hb_set_has (closure, old_num)) + add (old_num); + } + + if (get_count () < 1240) + bias = 107; + else if (get_count () < 33900) + bias = 1131; + else + bias = 32768; + } + + inline hb_codepoint_t operator[] (unsigned int old_num) const + { + if (old_num >= len) + return CFF_UNDEF_CODE; + else + return Remap::operator[] (old_num); + } + + inline int biased_num (unsigned int old_num) const + { + return (int)(*this)[old_num] - bias; + } + + protected: + int bias; +}; + +struct SubrRemaps +{ + inline SubrRemaps (void) + { + global_remap.init (); + local_remaps.init (); + } + + inline ~SubrRemaps (void) + { + fini (); + } + + inline void init (unsigned int fdCount) + { + local_remaps.resize (fdCount); + for (unsigned int i = 0; i < fdCount; i++) + local_remaps[i].init (); + } + + inline void create (SubrClosures& closures) + { + global_remap.create (closures.global_closure); + for (unsigned int i = 0; i < local_remaps.len; i++) + local_remaps[i].create (closures.local_closures[i]); + } + + inline void fini (void) + { + global_remap.fini (); + local_remaps.fini_deep (); + } + + SubrRemap global_remap; + hb_vector_t local_remaps; +}; + +template +struct SubrSubsetter +{ + inline SubrSubsetter (void) + { + parsed_charstrings.init (); + parsed_global_subrs.init (); + parsed_local_subrs.init (); + } + + inline ~SubrSubsetter (void) + { + closures.fini (); + remaps.fini (); + parsed_charstrings.fini_deep (); + parsed_global_subrs.fini_deep (); + parsed_local_subrs.fini_deep (); + } + + /* Subroutine subsetting with --no-desubroutinize runs in phases: + * + * 1. execute charstrings/subroutines to determine subroutine closures + * 2. parse out all operators and numbers + * 3. mark hint operators and operands for removal if --no-hinting + * 4. re-encode all charstrings and subroutines with new subroutine numbers + * + * Phases #1 and #2 are done at the same time in collect_subrs (). + * Phase #3 requires walking charstrings/subroutines forward then backward (hence parsing), because + * we can't tell if a number belongs to a hint op until we see the first moveto. + * + * Assumption: a callsubr/callgsubr operator must immediately follow a (biased) subroutine number + * within the same charstring/subroutine, e.g., not split across a charstring and a subroutine. + */ + inline bool subset (ACC &acc, const hb_vector_t &glyphs, bool drop_hints) + { + closures.init (acc.fdCount); + remaps.init (acc.fdCount); + + parsed_charstrings.init (glyphs.len); + parsed_global_subrs.init (acc.globalSubrs->count); + parsed_local_subrs.resize (acc.fdCount); + for (unsigned int i = 0; i < acc.fdCount; i++) + { + parsed_local_subrs[i].init (acc.privateDicts[i].localSubrs->count); + } + if (unlikely (!closures.valid)) + return false; + + /* phase 1 & 2 */ + for (unsigned int i = 0; i < glyphs.len; i++) + { + hb_codepoint_t glyph = glyphs[i]; + const ByteStr str = (*acc.charStrings)[glyph]; + unsigned int fd = acc.fdSelect->get_fd (glyph); + + CSInterpreter interp; + interp.env.init (str, acc, fd); + + SubrSubsetParam param; + param.init (&parsed_charstrings[i], + &parsed_global_subrs, &parsed_local_subrs[fd], + closures.global_closure, closures.local_closures[fd], + drop_hints); + + if (unlikely (!interp.interpret (param))) + return false; + + /* copy CFF1 width or CFF2 vsindex to the parsed charstring for encoding */ + SUBSETTER::set_parsed_prefix (interp.env, parsed_charstrings[i]); + } + + if (drop_hints) + { + /* mark hint ops and arguments for drop */ + for (unsigned int i = 0; i < glyphs.len; i++) + { + unsigned int fd = acc.fdSelect->get_fd (glyphs[i]); + SubrSubsetParam param; + param.init (&parsed_charstrings[i], + &parsed_global_subrs, &parsed_local_subrs[fd], + closures.global_closure, closures.local_closures[fd], + drop_hints); + + bool seen_moveto = false; + if (drop_hints_in_str (parsed_charstrings[i], param, seen_moveto)) + parsed_charstrings[i].set_hint_removed (); + } + + /* after dropping hints recreate closures from subrs actually used */ + closures.reset (); + for (unsigned int i = 0; i < glyphs.len; i++) + { + unsigned int fd = acc.fdSelect->get_fd (glyphs[i]); + SubrSubsetParam param; + param.init (&parsed_charstrings[i], + &parsed_global_subrs, &parsed_local_subrs[fd], + closures.global_closure, closures.local_closures[fd], + drop_hints); + collect_subr_refs_in_str (parsed_charstrings[i], param); + } + } + + remaps.create (closures); + + return true; + } + + inline bool encode_charstrings (ACC &acc, const hb_vector_t &glyphs, StrBuffArray &buffArray) const + { + if (unlikely (!buffArray.resize (glyphs.len))) + return false; + for (unsigned int i = 0; i < glyphs.len; i++) + { + unsigned int fd = acc.fdSelect->get_fd (glyphs[i]); + if (unlikely (!encode_str (parsed_charstrings[i], fd, buffArray[i]))) + return false; + } + return true; + } + + inline bool encode_subrs (const ParsedCStrs &subrs, const SubrRemap& remap, StrBuffArray &buffArray) const + { + unsigned int count = remap.get_count (); + + if (unlikely (!buffArray.resize (count))) + return false; + for (unsigned int old_num = 0; old_num < subrs.len; old_num++) + { + hb_codepoint_t new_num = remap[old_num]; + if (new_num != CFF_UNDEF_CODE) + { + if (unlikely (!encode_str (subrs[old_num], 0, buffArray[new_num]))) + return false; + } + } + return true; + } + + inline bool encode_globalsubrs (StrBuffArray &buffArray) + { + return encode_subrs (parsed_global_subrs, remaps.global_remap, buffArray); + } + + inline bool encode_localsubrs (unsigned int fd, StrBuffArray &buffArray) const + { + return encode_subrs (parsed_local_subrs[fd], remaps.local_remaps[fd], buffArray); + } + + protected: + inline bool drop_hints_in_subr (ParsedCStr &str, unsigned int pos, + ParsedCStrs &subrs, unsigned int subr_num, + const SubrSubsetParam ¶m, bool &seen_moveto) + { + if (drop_hints_in_str (subrs[subr_num], param, seen_moveto)) + { + /* if the first op in the subr is a hint op, then all args/ops (especially including other subr calls) + * preceding this subr no and call op are hints */ + /* TODO CFF2 vsindex */ + for (unsigned int i = 0; i + 1 < pos; i++) + str.values[i].set_drop (); + return true; + } + else + return false; + } + + /* returns true if it sees a hint op before moveto */ + inline bool drop_hints_in_str (ParsedCStr &str, const SubrSubsetParam ¶m, bool &seen_moveto) + { + bool seen_hint = false; + unsigned int next_check_pos = 0; + + for (unsigned int pos = 0; pos < str.values.len; pos++) + { + switch (str.values[pos].op) + { + case OpCode_callsubr: + seen_hint |= drop_hints_in_subr (str, pos, + *param.parsed_local_subrs, str.values[pos].subr_num, + param, seen_moveto); + break; + + case OpCode_callgsubr: + seen_hint |= drop_hints_in_subr (str, pos, + *param.parsed_global_subrs, str.values[pos].subr_num, + param, seen_moveto); + break; + + case OpCode_rmoveto: + case OpCode_hmoveto: + case OpCode_vmoveto: + seen_moveto = true; + break; + + case OpCode_hintmask: + case OpCode_cntrmask: + if (seen_moveto) + { + str.values[pos].set_drop (); + break; + } + HB_FALLTHROUGH; + + case OpCode_hstemhm: + case OpCode_vstemhm: + case OpCode_hstem: + case OpCode_vstem: + seen_hint = true; + for (unsigned int i = next_check_pos; i <= pos; i++) + { + /* TODO: CFF2 vsindex */ + str.values[i].set_drop (); + } + next_check_pos = pos + 1; + break; + + default: + /* NONE */ + break; + } + } + + return seen_hint; + } + + inline void collect_subr_refs_in_subr (ParsedCStr &str, unsigned int pos, + unsigned int subr_num, ParsedCStrs &subrs, + hb_set_t *closure, + const SubrSubsetParam ¶m) + { + hb_set_add (closure, subr_num); + collect_subr_refs_in_str (subrs[subr_num], param); + } + + inline void collect_subr_refs_in_str (ParsedCStr &str, const SubrSubsetParam ¶m) + { + for (unsigned int pos = 0; pos < str.values.len; pos++) + { + if (!str.values[pos].for_drop ()) + { + switch (str.values[pos].op) + { + case OpCode_callsubr: + collect_subr_refs_in_subr (str, pos, + str.values[pos].subr_num, *param.parsed_local_subrs, + param.local_closure, param); + break; + + case OpCode_callgsubr: + collect_subr_refs_in_subr (str, pos, + str.values[pos].subr_num, *param.parsed_global_subrs, + param.global_closure, param); + break; + + default: break; + } + } + } + } + + inline bool encode_str (const ParsedCStr &str, const unsigned int fd, StrBuff &buff) const + { + buff.init (); + StrEncoder encoder (buff); + encoder.reset (); + /* if a prefix (CFF1 width or CFF2 vsindex) has been removed along with hints, + * re-insert it at the beginning of charstreing */ + if (str.has_prefix () && str.is_hint_removed ()) + { + encoder.encode_num (str.prefix_num ()); + if (str.prefix_op () != OpCode_Invalid) + encoder.encode_op (str.prefix_op ()); + } + for (unsigned int i = 0; i < str.get_count(); i++) + { + const ParsedCSOp &opstr = str.values[i]; + if (!opstr.for_drop ()) + { + switch (opstr.op) + { + case OpCode_callsubr: + encoder.encode_int (remaps.local_remaps[fd].biased_num (opstr.subr_num)); + encoder.encode_op (OpCode_callsubr); + break; + + case OpCode_callgsubr: + encoder.encode_int (remaps.global_remap.biased_num (opstr.subr_num)); + encoder.encode_op (OpCode_callgsubr); + break; + + default: + encoder.copy_str (opstr.str); + break; + } + } + } + return !encoder.is_error (); + } + + protected: + SubrClosures closures; + + ParsedCStrs parsed_charstrings; + ParsedCStrs parsed_global_subrs; + hb_vector_t parsed_local_subrs; + + SubrRemaps remaps; + + private: + typedef typename SUBRS::count_type subr_count_type; +}; }; /* namespace CFF */ HB_INTERNAL bool diff --git a/src/hb-subset-cff1.cc b/src/hb-subset-cff1.cc index 4216be45d..4654ac9a9 100644 --- a/src/hb-subset-cff1.cc +++ b/src/hb-subset-cff1.cc @@ -92,18 +92,18 @@ struct CFF1TopDictValuesMod : CFF1TopDictValues SUPER::fini (); } - inline unsigned getNumValues (void) const + inline unsigned get_count (void) const { - return base->getNumValues () + SUPER::getNumValues (); + return base->get_count () + SUPER::get_count (); } - inline const CFF1TopDictVal &getValue (unsigned int i) const + inline const CFF1TopDictVal &get_value (unsigned int i) const { - if (i < base->getNumValues ()) + if (i < base->get_count ()) return (*base)[i]; else - return SUPER::values[i - base->getNumValues ()]; + return SUPER::values[i - base->get_count ()]; } - inline const CFF1TopDictVal &operator [] (unsigned int i) const { return getValue (i); } + inline const CFF1TopDictVal &operator [] (unsigned int i) const { return get_value (i); } inline void reassignSIDs (const RemapSID& sidmap) { @@ -230,9 +230,9 @@ struct FontDictValuesMod privateDictInfo = privateDictInfo_; } - inline unsigned getNumValues (void) const + inline unsigned get_count (void) const { - return base->getNumValues (); + return base->get_count (); } inline const OpStr &operator [] (unsigned int i) const { return (*base)[i]; } @@ -270,10 +270,9 @@ struct CFF1FontDict_OpSerializer : CFFFontDict_OpSerializer struct CFF1CSOpSet_Flatten : CFF1CSOpSet { - static inline void flush_args_and_op (OpCode op, CFF1CSInterpEnv &env, FlattenParam& param, unsigned int start_arg = 0) + static inline void flush_args_and_op (OpCode op, CFF1CSInterpEnv &env, FlattenParam& param) { - start_arg = env.check_width (); - if ((start_arg > 0) && likely (param.flatStr.len == 0)) + if (env.arg_start > 0) flush_width (env, param); switch (op) @@ -292,35 +291,40 @@ struct CFF1CSOpSet_Flatten : CFF1CSOpSet HB_FALLTHROUGH; default: - SUPER::flush_args_and_op (op, env, param, start_arg); + SUPER::flush_args_and_op (op, env, param); break; } } - - static inline void flush_args (CFF1CSInterpEnv &env, FlattenParam& param, unsigned int start_arg = 0) + static inline void flush_args (CFF1CSInterpEnv &env, FlattenParam& param) { - for (unsigned int i = start_arg; i < env.argStack.get_count (); i++) - param.flatStr.encode_num (env.eval_arg (i)); - SUPER::flush_args (env, param, start_arg); + StrEncoder encoder (param.flatStr); + for (unsigned int i = env.arg_start; i < env.argStack.get_count (); i++) + encoder.encode_num (env.eval_arg (i)); + SUPER::flush_args (env, param); } static inline void flush_op (OpCode op, CFF1CSInterpEnv &env, FlattenParam& param) { - param.flatStr.encode_op (op); + StrEncoder encoder (param.flatStr); + encoder.encode_op (op); } static inline void flush_width (CFF1CSInterpEnv &env, FlattenParam& param) { assert (env.has_width); - param.flatStr.encode_num (env.width); + StrEncoder encoder (param.flatStr); + encoder.encode_num (env.width); } static inline void flush_hintmask (OpCode op, CFF1CSInterpEnv &env, FlattenParam& param) { SUPER::flush_hintmask (op, env, param); if (!param.drop_hints) + { + StrEncoder encoder (param.flatStr); for (unsigned int i = 0; i < env.hintmask_size; i++) - param.flatStr.encode_byte (env.substr[i]); + encoder.encode_byte (env.substr[i]); + } } private: @@ -342,6 +346,70 @@ struct RangeList : hb_vector_t } }; +struct CFF1CSOpSet_SubrSubset : CFF1CSOpSet +{ + static inline void process_op (OpCode op, CFF1CSInterpEnv &env, SubrSubsetParam& param) + { + switch (op) { + + case OpCode_return: + param.current_parsed_str->add_op (op, env.substr); + param.current_parsed_str->set_parsed (); + env.returnFromSubr (); + param.set_current_str (env); + break; + + case OpCode_endchar: + param.current_parsed_str->add_op (op, env.substr); + param.current_parsed_str->set_parsed (); + SUPER::process_op (op, env, param); + break; + + case OpCode_callsubr: + process_call_subr (op, CSType_LocalSubr, env, param, env.localSubrs, param.local_closure); + break; + + case OpCode_callgsubr: + process_call_subr (op, CSType_GlobalSubr, env, param, env.globalSubrs, param.global_closure); + break; + + default: + SUPER::process_op (op, env, param); + param.current_parsed_str->add_op (op, env.substr); + break; + } + } + + static inline void process_width (CFF1CSInterpEnv &env, SubrSubsetParam& param) + { + + } + + protected: + static inline void process_call_subr (OpCode op, CSType type, + CFF1CSInterpEnv &env, SubrSubsetParam& param, + CFF1BiasedSubrs& subrs, hb_set_t *closure) + { + SubByteStr substr = env.substr; + env.callSubr (subrs, type); + param.current_parsed_str->addCallOp (op, substr, env.context.subr_num); + hb_set_add (closure, env.context.subr_num); + param.set_current_str (env); + } + + private: + typedef CFF1CSOpSet SUPER; +}; + +struct CFF1SubrSubsetter : SubrSubsetter +{ + static inline void set_parsed_prefix (const CFF1CSInterpEnv &env, ParsedCStr &charstring) + { + if (env.processed_width) + charstring.set_prefix (env.width); + } +}; + struct cff_subset_plan { inline cff_subset_plan (void) : final_size (0), @@ -349,7 +417,8 @@ struct cff_subset_plan { orig_fdcount (0), subset_fdcount (1), subset_fdselect_format (0), - drop_hints (false) + drop_hints (false), + desubroutinize(false) { topdict_sizes.init (); topdict_sizes.resize (1); @@ -357,7 +426,8 @@ struct cff_subset_plan { subset_fdselect_ranges.init (); fdmap.init (); subset_charstrings.init (); - flat_charstrings.init (); + subset_globalsubrs.init (); + subset_localsubrs.init (); fontdicts_mod.init (); subset_enc_code_ranges.init (); subset_enc_supp_codes.init (); @@ -374,7 +444,8 @@ struct cff_subset_plan { subset_fdselect_ranges.fini (); fdmap.fini (); subset_charstrings.fini (); - flat_charstrings.fini (); + subset_globalsubrs.fini (); + subset_localsubrs.fini_deep (); fontdicts_mod.fini (); subset_enc_code_ranges.fini (); subset_enc_supp_codes.init (); @@ -504,9 +575,9 @@ struct cff_subset_plan { } if (acc.fdArray != &Null(CFF1FDArray)) - for (unsigned int fd = 0; fd < orig_fdcount; fd++) - if (!fdmap.excludes (fd)) - (void)sidmap.add (acc.fontDicts[fd].fontName); + for (unsigned int i = 0; i < orig_fdcount; i++) + if (fdmap.includes (i)) + (void)sidmap.add (acc.fontDicts[i].fontName); return true; } @@ -521,6 +592,7 @@ struct cff_subset_plan { num_glyphs = plan->glyphs.len; orig_fdcount = acc.fdCount; drop_hints = plan->drop_hints; + desubroutinize = true; // plan->desubroutinize; /* check whether the subset renumbers any glyph IDs */ gid_renum = false; @@ -546,14 +618,14 @@ struct cff_subset_plan { { /* Add encoding/charset to a (copy of) top dict as necessary */ topdict_mod.init (&acc.topDict); - bool need_to_add_enc = (subset_encoding && !acc.topDict.hasOp (OpCode_Encoding)); - bool need_to_add_set = (subset_charset && !acc.topDict.hasOp (OpCode_charset)); + bool need_to_add_enc = (subset_encoding && !acc.topDict.has_op (OpCode_Encoding)); + bool need_to_add_set = (subset_charset && !acc.topDict.has_op (OpCode_charset)); if (need_to_add_enc || need_to_add_set) { if (need_to_add_enc) - topdict_mod.addOp (OpCode_Encoding); + topdict_mod.add_op (OpCode_Encoding); if (need_to_add_set) - topdict_mod.addOp (OpCode_charset); + topdict_mod.add_op (OpCode_charset); } offsets.topDictInfo.offset = final_size; CFF1TopDict_OpSerializer topSzr; @@ -595,16 +667,54 @@ struct cff_subset_plan { final_size += offsets.stringIndexInfo.size; } + if (desubroutinize) { /* Flatten global & local subrs */ SubrFlattener flattener(acc, plan->glyphs, plan->drop_hints); - if (!flattener.flatten (flat_charstrings)) + if (!flattener.flatten (subset_charstrings)) return false; /* no global/local subroutines */ offsets.globalSubrsInfo.size = HBUINT16::static_size; /* count 0 only */ } + else + { + /* Subset subrs: collect used subroutines, leaving all unused ones behind */ + if (!subr_subsetter.subset (acc, plan->glyphs, plan->drop_hints)) + return false; + + /* encode charstrings, global subrs, local subrs with new subroutine numbers */ + if (!subr_subsetter.encode_charstrings (acc, plan->glyphs, subset_charstrings)) + return false; + + if (!subr_subsetter.encode_globalsubrs (subset_globalsubrs)) + return false; + + /* global subrs */ + unsigned int dataSize = subset_globalsubrs.total_size (); + offsets.globalSubrsInfo.offSize = calcOffSize (dataSize); + offsets.globalSubrsInfo.size = CFF1Subrs::calculate_serialized_size (offsets.globalSubrsInfo.offSize, subset_globalsubrs.len, dataSize); + + /* local subrs */ + if (!offsets.localSubrsInfos.resize (orig_fdcount)) + return false; + if (!subset_localsubrs.resize (orig_fdcount)) + return false; + for (unsigned int fd = 0; fd < orig_fdcount; fd++) + { + subset_localsubrs[fd].init (); + if (fdmap.includes (fd)) + { + if (!subr_subsetter.encode_localsubrs (fd, subset_localsubrs[fd])) + return false; + + unsigned int dataSize = subset_localsubrs[fd].total_size (); + offsets.localSubrsInfos[fd].offSize = calcOffSize (dataSize); + offsets.localSubrsInfos[fd].size = CFF1Subrs::calculate_serialized_size (offsets.localSubrsInfos[fd].offSize, subset_localsubrs[fd].len, dataSize); + } + } + } /* global subrs */ offsets.globalSubrsInfo.offset = final_size; @@ -640,7 +750,7 @@ struct cff_subset_plan { CFF1FontDict_OpSerializer fontSzr; unsigned int dictsSize = 0; for (unsigned int i = 0; i < acc.fontDicts.len; i++) - if (!fdmap.excludes (i)) + if (fdmap.includes (i)) dictsSize += FontDict::calculate_serialized_size (acc.fontDicts[i], fontSzr); offsets.FDArrayInfo.offSize = calcOffSize (dictsSize); @@ -650,14 +760,7 @@ struct cff_subset_plan { /* CharStrings */ { offsets.charStringsInfo.offset = final_size; - unsigned int dataSize = 0; - for (unsigned int i = 0; i < plan->glyphs.len; i++) - { - ByteStrBuff &flatstr = flat_charstrings[i]; - ByteStr str (&flatstr[0], flatstr.len); - subset_charstrings.push (str); - dataSize += flatstr.len; - } + unsigned int dataSize = subset_charstrings.total_size (); offsets.charStringsInfo.offSize = calcOffSize (dataSize); final_size += CFF1CharStrings::calculate_serialized_size (offsets.charStringsInfo.offSize, plan->glyphs.len, dataSize); } @@ -666,23 +769,26 @@ struct cff_subset_plan { offsets.privateDictInfo.offset = final_size; for (unsigned int i = 0; i < orig_fdcount; i++) { - if (!fdmap.excludes (i)) + if (fdmap.includes (i)) { - CFFPrivateDict_OpSerializer privSzr (plan->drop_hints); + CFFPrivateDict_OpSerializer privSzr (desubroutinize, plan->drop_hints); unsigned int priv_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr); TableInfo privInfo = { final_size, priv_size, 0 }; FontDictValuesMod fontdict_mod; fontdict_mod.init ( &acc.fontDicts[i], sidmap[acc.fontDicts[i].fontName], privInfo ); fontdicts_mod.push (fontdict_mod); final_size += privInfo.size; + + if (!plan->desubroutinize) + final_size += offsets.localSubrsInfos[i].size; } } if (!acc.is_CID ()) offsets.privateDictInfo = fontdicts_mod[0].privateDictInfo; - return ((subset_charstrings.len == plan->glyphs.len) && - (fontdicts_mod.len == subset_fdcount)); + return ((subset_charstrings.len == plan->glyphs.len) + && (fontdicts_mod.len == subset_fdcount)); } inline unsigned int get_final_size (void) const { return final_size; } @@ -703,11 +809,11 @@ struct cff_subset_plan { * set to CFF_UNDEF_CODE if excluded from subset */ Remap fdmap; - hb_vector_t subset_charstrings; - ByteStrBuffArray flat_charstrings; + StrBuffArray subset_charstrings; + StrBuffArray subset_globalsubrs; + hb_vector_t subset_localsubrs; hb_vector_t fontdicts_mod; - bool flatten_subrs; bool drop_hints; bool gid_renum; @@ -723,6 +829,9 @@ struct cff_subset_plan { RemapSID sidmap; unsigned int topDictModSIDs[NameDictValues::ValCount]; + + bool desubroutinize; + CFF1SubrSubsetter subr_subsetter; }; static inline bool _write_cff1 (const cff_subset_plan &plan, @@ -790,9 +899,23 @@ static inline bool _write_cff1 (const cff_subset_plan &plan, { assert (plan.offsets.globalSubrsInfo.offset != 0); assert (plan.offsets.globalSubrsInfo.offset == c.head - c.start); - CFF1Subrs *dest = c.allocate_size (HBUINT16::static_size); - if (unlikely (dest == nullptr)) return false; - dest->count.set (0); + + if (plan.desubroutinize) + { + CFF1Subrs *dest = c.allocate_size (HBUINT16::static_size); + if (unlikely (dest == nullptr)) return false; + dest->count.set (0); + } + else + { + CFF1Subrs *dest = c.start_embed (); + if (unlikely (dest == nullptr)) return false; + if (unlikely (!dest->serialize (&c, plan.offsets.globalSubrsInfo.offSize, plan.subset_globalsubrs))) + { + DEBUG_MSG (SUBSET, nullptr, "failed to serialize global subroutines"); + return false; + } + } } /* Encoding */ @@ -893,7 +1016,7 @@ static inline bool _write_cff1 (const cff_subset_plan &plan, if (unlikely (pd == nullptr)) return false; unsigned int priv_size = plan.fontdicts_mod[plan.fdmap[i]].privateDictInfo.size; bool result; - CFFPrivateDict_OpSerializer privSzr (plan.drop_hints); + CFFPrivateDict_OpSerializer privSzr (plan.desubroutinize, plan.drop_hints); /* N.B. local subrs immediately follows its corresponding private dict. i.e., subr offset == private dict size */ result = pd->serialize (&c, acc.privateDicts[i], privSzr, priv_size); if (unlikely (!result)) @@ -904,6 +1027,23 @@ static inline bool _write_cff1 (const cff_subset_plan &plan, } } + if (!plan.desubroutinize) + { + for (unsigned int i = 0; i < acc.privateDicts.len; i++) + { + if (!plan.fdmap.excludes (i)) + { + CFF1Subrs *dest = c.start_embed (); + if (unlikely (dest == nullptr)) return false; + if (unlikely (!dest->serialize (&c, plan.offsets.localSubrsInfos[i].offSize, plan.subset_localsubrs[i]))) + { + DEBUG_MSG (SUBSET, nullptr, "failed to serialize local subroutines"); + return false; + } + } + } + } + assert (c.head == c.end); c.end_serialize (); diff --git a/src/hb-subset-cff2.cc b/src/hb-subset-cff2.cc index b30145cd7..22a35c52f 100644 --- a/src/hb-subset-cff2.cc +++ b/src/hb-subset-cff2.cc @@ -77,7 +77,7 @@ struct CFF2TopDict_OpSerializer : CFFTopDict_OpSerializer<> struct CFF2CSOpSet_Flatten : CFF2CSOpSet { - static inline void flush_args_and_op (OpCode op, CFF2CSInterpEnv &env, FlattenParam& param, unsigned int start_arg = 0) + static inline void flush_args_and_op (OpCode op, CFF2CSInterpEnv &env, FlattenParam& param) { switch (op) { @@ -100,29 +100,30 @@ struct CFF2CSOpSet_Flatten : CFF2CSOpSet HB_FALLTHROUGH; default: - SUPER::flush_args_and_op (op, env, param, start_arg); + SUPER::flush_args_and_op (op, env, param); break; } } - static inline void flush_args (CFF2CSInterpEnv &env, FlattenParam& param, unsigned int start_arg = 0) + static inline void flush_args (CFF2CSInterpEnv &env, FlattenParam& param) { - for (unsigned int i = start_arg; i < env.argStack.get_count ();) + for (unsigned int i = 0; i < env.argStack.get_count ();) { const BlendArg &arg = env.argStack[i]; if (arg.blending ()) { - assert ((arg.numValues > 0) && (env.argStack.get_count () - start_arg >= arg.numValues)); + assert ((arg.numValues > 0) && (env.argStack.get_count () >= arg.numValues)); flatten_blends (arg, i, env, param); i += arg.numValues; } else { - param.flatStr.encode_num (arg); + StrEncoder encoder (param.flatStr); + encoder.encode_num (arg); i++; } } - SUPER::flush_args (env, param, start_arg); + SUPER::flush_args (env, param); } static inline void flatten_blends (const BlendArg &arg, unsigned int i, CFF2CSInterpEnv &env, FlattenParam& param) @@ -133,20 +134,22 @@ struct CFF2CSOpSet_Flatten : CFF2CSOpSet const BlendArg &arg1 = env.argStack[i + j]; assert (arg1.blending () && (arg.numValues == arg1.numValues) && (arg1.valueIndex == j) && (arg1.deltas.len == env.get_region_count ())); - param.flatStr.encode_num (arg1); + StrEncoder encoder (param.flatStr); + encoder.encode_num (arg1); } /* flatten deltas for each value */ + StrEncoder encoder (param.flatStr); for (unsigned int j = 0; j < arg.numValues; j++) { const BlendArg &arg1 = env.argStack[i + j]; for (unsigned int k = 0; k < arg1.deltas.len; k++) - param.flatStr.encode_num (arg1.deltas[k]); + encoder.encode_num (arg1.deltas[k]); } /* flatten the number of values followed by blend operator */ - param.flatStr.encode_int (arg.numValues); - param.flatStr.encode_op (OpCode_blendcs); + encoder.encode_int (arg.numValues); + encoder.encode_op (OpCode_blendcs); } - + static inline void flush_op (OpCode op, CFF2CSInterpEnv &env, FlattenParam& param) { switch (op) @@ -155,7 +158,8 @@ struct CFF2CSOpSet_Flatten : CFF2CSOpSet case OpCode_endchar: return; default: - param.flatStr.encode_op (op); + StrEncoder encoder (param.flatStr); + encoder.encode_op (op); } } @@ -170,7 +174,8 @@ struct cff2_subset_plan { orig_fdcount (0), subset_fdcount(1), subset_fdselect_format (0), - drop_hints (false) + drop_hints (false), + desubroutinize (false) { subset_fdselect_ranges.init (); fdmap.init (); @@ -195,6 +200,7 @@ struct cff2_subset_plan { orig_fdcount = acc.fdArray->count; drop_hints = plan->drop_hints; + desubroutinize = plan->desubroutinize; /* CFF2 header */ final_size += OT::cff2::static_size; @@ -260,7 +266,7 @@ struct cff2_subset_plan { unsigned int dataSize = 0; for (unsigned int i = 0; i < plan->glyphs.len; i++) { - ByteStrBuff &flatstr = flat_charstrings[i]; + StrBuff &flatstr = flat_charstrings[i]; ByteStr str (&flatstr[0], flatstr.len); subset_charstrings.push (str); dataSize += flatstr.len; @@ -276,7 +282,7 @@ struct cff2_subset_plan { if (!fdmap.excludes (i)) { unsigned int priv_size; - CFFPrivateDict_OpSerializer privSzr (drop_hints); + CFFPrivateDict_OpSerializer privSzr (desubroutinize, drop_hints); priv_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr); TableInfo privInfo = { final_size, priv_size, 0 }; privateDictInfos.push (privInfo); @@ -300,11 +306,12 @@ struct cff2_subset_plan { Remap fdmap; - hb_vector_t subset_charstrings; - ByteStrBuffArray flat_charstrings; + ByteStrArray subset_charstrings; + StrBuffArray flat_charstrings; hb_vector_t privateDictInfos; bool drop_hints; + bool desubroutinize; }; static inline bool _write_cff2 (const cff2_subset_plan &plan, @@ -421,7 +428,7 @@ static inline bool _write_cff2 (const cff2_subset_plan &plan, if (unlikely (pd == nullptr)) return false; unsigned int priv_size = plan.privateDictInfos[plan.fdmap[i]].size; bool result; - CFFPrivateDict_OpSerializer privSzr (plan.drop_hints); + CFFPrivateDict_OpSerializer privSzr (plan.desubroutinize, plan.drop_hints); result = pd->serialize (&c, acc.privateDicts[i], privSzr, priv_size); if (unlikely (!result)) { diff --git a/src/hb-subset-input.hh b/src/hb-subset-input.hh index 9fc86154e..75edd5d5e 100644 --- a/src/hb-subset-input.hh +++ b/src/hb-subset-input.hh @@ -44,6 +44,7 @@ struct hb_subset_input_t bool drop_hints : 1; bool drop_layout : 1; + bool desubroutinize : 1; /* TODO * * features diff --git a/src/hb-subset-plan.cc b/src/hb-subset-plan.cc index 057006039..71c4a4667 100644 --- a/src/hb-subset-plan.cc +++ b/src/hb-subset-plan.cc @@ -150,6 +150,7 @@ hb_subset_plan_create (hb_face_t *face, plan->drop_hints = input->drop_hints; plan->drop_layout = input->drop_layout; + plan->desubroutinize = input->desubroutinize; plan->unicodes = hb_set_create(); plan->glyphs.init(); plan->source = hb_face_reference (face); diff --git a/src/hb-subset-plan.hh b/src/hb-subset-plan.hh index c2c484a5b..c14f1bbb3 100644 --- a/src/hb-subset-plan.hh +++ b/src/hb-subset-plan.hh @@ -41,6 +41,7 @@ struct hb_subset_plan_t bool drop_hints : 1; bool drop_layout : 1; + bool desubroutinize : 1; // For each cp that we'd like to retain maps to the corresponding gid. hb_set_t *unicodes; diff --git a/util/options.cc b/util/options.cc index 5661cd059..e52d63be8 100644 --- a/util/options.cc +++ b/util/options.cc @@ -970,6 +970,8 @@ subset_options_t::add_options (option_parser_t *parser) GOptionEntry entries[] = { {"no-hinting", 0, 0, G_OPTION_ARG_NONE, &this->drop_hints, "Whether to drop hints", nullptr}, + {"desubroutinize", 0, 0, G_OPTION_ARG_NONE, &this->desubroutinize, "Remove CFF/CFF2 use of subroutinizes", nullptr}, + {nullptr} }; parser->add_group (entries, diff --git a/util/options.hh b/util/options.hh index dd628590e..f90b22f08 100644 --- a/util/options.hh +++ b/util/options.hh @@ -669,6 +669,7 @@ struct subset_options_t : option_group_t subset_options_t (option_parser_t *parser) { drop_hints = false; + desubroutinize = false; add_options (parser); } @@ -676,6 +677,7 @@ struct subset_options_t : option_group_t void add_options (option_parser_t *parser); hb_bool_t drop_hints; + hb_bool_t desubroutinize; }; /* fallback implementation for scalbn()/scalbnf() for pre-2013 MSVC */