From a11420b48c405e1a43e2ba62893e9a0872e5f496 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Wed, 29 Aug 2018 12:14:30 -0700 Subject: [PATCH] Subroutine flattener for CFF1 Subr-flattened charstrings are temporarily re-encoded in ByteStrBuff during "plan" phase, then copied to hb_serialize_context_t during "write" phase CSOpSet may callback opcode processing "virtual" functions via CRTP Numer struct may store a value as fixed optionally in addition to int and float --- src/hb-cff-interp-common-private.hh | 53 +++++++-- src/hb-cff-interp-cs-common-private.hh | 109 ++++++++++++++---- src/hb-cff1-interp-cs.hh | 35 ++++-- src/hb-cff2-interp-cs.hh | 14 +-- src/hb-ot-cff-common-private.hh | 10 +- src/hb-subset-cff-common-private.hh | 102 ++++++++++++++++- src/hb-subset-cff1.cc | 152 ++++++++++++++++--------- src/hb-subset-cff2.cc | 7 +- 8 files changed, 372 insertions(+), 110 deletions(-) diff --git a/src/hb-cff-interp-common-private.hh b/src/hb-cff-interp-common-private.hh index 2d20163ff..79add1210 100644 --- a/src/hb-cff-interp-common-private.hh +++ b/src/hb-cff-interp-common-private.hh @@ -204,23 +204,62 @@ enum OpCode { }; inline OpCode Make_OpCode_ESC (unsigned char byte2) { return (OpCode)(OpCode_ESC_Base + byte2); } -inline unsigned int OpCode_Size (OpCode op) { return (op >= OpCode_ESC_Base)? 2: 1; } +inline OpCode Unmake_OpCode_ESC (OpCode op) { return (OpCode)(op - OpCode_ESC_Base); } +inline bool Is_OpCode_ESC (OpCode op) { return op >= OpCode_ESC_Base; } +inline unsigned int OpCode_Size (OpCode op) { return Is_OpCode_ESC (op)? 2: 1; } struct Number { inline Number (void) { set_int (0); } - inline void set_int (int v) { is_real = false; u.int_val = v; }; - inline int to_int (void) const { return is_real? (int)u.real_val: u.int_val; } - inline void set_real (float v) { is_real = true; u.real_val = v; }; - inline float to_real (void) const { return is_real? u.real_val: (float)u.int_val; } + inline void set_int (int v) { format = NumInt; u.int_val = v; }; + inline int to_int (void) const { return is_int ()? u.int_val: (int)to_real (); } + inline void set_fixed (int32_t v) { format = NumFixed; u.fixed_val = v; }; + inline int32_t to_fixed (void) const + { + if (is_fixed ()) + return u.fixed_val; + else if (is_real ()) + return (int32_t)(u.real_val * 65536.0); + else + return (int32_t)(u.int_val << 16); + } + inline void set_real (float v) { format = NumReal; u.real_val = v; }; + inline float to_real (void) const + { + if (is_real ()) + return u.real_val; + if (is_fixed ()) + return u.fixed_val / 65536.0; + else + return (float)u.int_val; + } + inline bool in_int_range (void) const + { + if (is_int ()) + return true; + if (is_fixed () && ((u.fixed_val & 0xFFFF) == 0)) + return true; + else + return ((float)(int16_t)to_int () == u.real_val); + } protected: - bool is_real; + enum NumFormat { + NumInt, + NumFixed, + NumReal + }; + NumFormat format; union { int int_val; + int32_t fixed_val; float real_val; } u; + + inline bool is_int (void) const { return format == NumInt; } + inline bool is_fixed (void) const { return format == NumFixed; } + inline bool is_real (void) const { return format == NumReal; } }; /* byte string */ @@ -308,7 +347,7 @@ struct SubByteStr offset = offset_; } - inline const HBUINT8& operator [] (unsigned int i) const { + inline const HBUINT8& operator [] (int i) const { return str[offset + i]; } diff --git a/src/hb-cff-interp-cs-common-private.hh b/src/hb-cff-interp-cs-common-private.hh index 4cf63c266..a8fd8ba6a 100644 --- a/src/hb-cff-interp-cs-common-private.hh +++ b/src/hb-cff-interp-cs-common-private.hh @@ -125,18 +125,6 @@ struct CSInterpEnv : InterpEnv hintmask_size = (hstem_count + vstem_count + 7) >> 3; seen_hintmask = true; } - clear_stack (); - } - - inline void process_moveto (void) - { - clear_stack (); - - if (!seen_moveto) - { - determine_hintmask_size (); - seen_moveto = true; - } } inline void clear_stack (void) @@ -149,13 +137,12 @@ struct CSInterpEnv : InterpEnv inline bool is_endchar (void) const { return endchar_flag; } inline bool is_stack_cleared (void) const { return stack_cleared; } - protected: + public: bool endchar_flag; bool stack_cleared; bool seen_moveto; bool seen_hintmask; - public: unsigned int hstem_count; unsigned int vstem_count; unsigned int hintmask_size; @@ -164,11 +151,11 @@ struct CSInterpEnv : InterpEnv BiasedSubrs localSubrs; }; -template +template struct CSOpSet : OpSet { - static inline bool process_op (OpCode op, CSInterpEnv &env, PARAM& param) - { + static inline bool process_op (OpCode op, ENV &env, PARAM& param) + { switch (op) { case OpCode_return: @@ -188,17 +175,16 @@ struct CSOpSet : OpSet case OpCode_hstem: case OpCode_hstemhm: - env.hstem_count += env.argStack.size / 2; - env.clear_stack (); + OPSET::process_hstem (env, param); break; case OpCode_vstem: case OpCode_vstemhm: - env.vstem_count += env.argStack.size / 2; - env.clear_stack (); + OPSET::process_vstem (env, param); break; case OpCode_hintmask: case OpCode_cntrmask: env.determine_hintmask_size (); + OPSET::flush_stack (env, param); if (unlikely (!env.substr.avail (env.hintmask_size))) return false; env.substr.inc (env.hintmask_size); @@ -210,7 +196,7 @@ struct CSOpSet : OpSet case OpCode_vlineto: case OpCode_rmoveto: case OpCode_hmoveto: - env.process_moveto (); + OPSET::process_moveto (env, param); break; case OpCode_rrcurveto: case OpCode_rcurveline: @@ -223,7 +209,7 @@ struct CSOpSet : OpSet case OpCode_flex: case OpCode_hflex1: case OpCode_flex1: - env.clear_stack (); + OPSET::flush_stack (env, param); break; default: @@ -231,6 +217,83 @@ struct CSOpSet : OpSet } return true; } + + static inline void process_hstem (ENV &env, PARAM& param) + { + env.hstem_count += env.argStack.size / 2; + OPSET::flush_stack (env, param); + } + + static inline void process_vstem (ENV &env, PARAM& param) + { + env.vstem_count += env.argStack.size / 2; + OPSET::flush_stack (env, param); + } + + static inline void process_moveto (ENV &env, PARAM& param) + { + if (!env.seen_moveto) + { + env.determine_hintmask_size (); + env.seen_moveto = true; + } + OPSET::flush_stack (env, param); + } + + static inline void flush_stack (ENV &env, PARAM& param) + { + env.clear_stack (); + } + + /* numeric / logical / arithmetic operators */ + static inline bool is_arg_op (OpCode op) + { + switch (op) + { + case OpCode_shortint: + case OpCode_TwoBytePosInt0: case OpCode_TwoBytePosInt1: + case OpCode_TwoBytePosInt2: case OpCode_TwoBytePosInt3: + case OpCode_TwoByteNegInt0: case OpCode_TwoByteNegInt1: + case OpCode_TwoByteNegInt2: case OpCode_TwoByteNegInt3: + case OpCode_fixedcs: + case OpCode_and: + case OpCode_or: + case OpCode_not: + case OpCode_abs: + case OpCode_add: + case OpCode_sub: + case OpCode_div: + case OpCode_neg: + case OpCode_eq: + case OpCode_drop: + case OpCode_put: + case OpCode_get: + case OpCode_ifelse: + case OpCode_random: + case OpCode_mul: + case OpCode_sqrt: + case OpCode_dup: + case OpCode_exch: + case OpCode_index: + case OpCode_roll: + return true; + default: + return (OpCode_OneByteIntFirst <= op) && (op <= OpCode_OneByteIntLast); + } + } + + static inline bool is_subr_op (OpCode op) + { + switch (op) + { + case OpCode_callsubr: + case OpCode_callgsubr: + case OpCode_return: + return true; + default: + return false; + } + } }; template diff --git a/src/hb-cff1-interp-cs.hh b/src/hb-cff1-interp-cs.hh index c76a7881b..ba93454c4 100644 --- a/src/hb-cff1-interp-cs.hh +++ b/src/hb-cff1-interp-cs.hh @@ -38,6 +38,8 @@ struct CFF1CSInterpEnv : CSInterpEnv inline void init (const ByteStr &str, const CFF1Subrs &globalSubrs, const CFF1Subrs &localSubrs) { CSInterpEnv::init (str, globalSubrs, localSubrs); + processed_width = false; + has_width = false; for (unsigned int i = 0; i < kTransientArraySize; i++) transient_array[i].set_int (0); } @@ -45,25 +47,29 @@ struct CFF1CSInterpEnv : CSInterpEnv bool check_transient_array_index (unsigned int i) const { return i < kTransientArraySize; } - inline void process_width (void) + inline void check_width (void) { - if (!seen_width && (argStack.size > 0)) + if (!processed_width) { - assert (argStack.size == 1); - width = argStack.pop (); - seen_width = true; + if ((this->argStack.size & 1) != 0) + { + width = this->argStack.elements[0]; + has_width = true; + } + processed_width = true; } } - bool seen_width; + bool processed_width; + bool has_width; Number width; static const unsigned int kTransientArraySize = 32; Number transient_array[kTransientArraySize]; }; -template -struct CFF1CSOpSet : CSOpSet +template +struct CFF1CSOpSet : CSOpSet { static inline bool process_op (OpCode op, CFF1CSInterpEnv &env, PARAM& param) { @@ -133,7 +139,7 @@ struct CFF1CSOpSet : CSOpSet break; case OpCode_random: if (unlikely (!env.argStack.check_overflow (1))) return false; - env.argStack.push_real (((float)rand() + 1) / ((float)RAND_MAX + 1)); + env.argStack.push_int (1); /* we can't deal with random behavior; make it constant */ case OpCode_mul: if (unlikely (!env.argStack.check_pop_num2 (n1, n2))) return false; env.argStack.push_real (n1.to_real() * n2.to_real()); @@ -181,14 +187,21 @@ struct CFF1CSOpSet : CSOpSet } break; default: - typedef CSOpSet SUPER; if (unlikely (!SUPER::process_op (op, env, param))) return false; - env.process_width (); break; } return true; } + + static inline void flush_stack (CFF1CSInterpEnv &env, PARAM& param) + { + env.check_width (); + SUPER::flush_stack (env, param); + } + + private: + typedef CSOpSet SUPER; }; template diff --git a/src/hb-cff2-interp-cs.hh b/src/hb-cff2-interp-cs.hh index 519b5fc5d..96e209b8f 100644 --- a/src/hb-cff2-interp-cs.hh +++ b/src/hb-cff2-interp-cs.hh @@ -43,11 +43,11 @@ struct CFF2CSInterpEnv : CSInterpEnv inline bool fetch_op (OpCode &op) { - if (unlikely (substr.avail ())) + if (unlikely (this->substr.avail ())) return CSInterpEnv::fetch_op (op); /* make up return or endchar op */ - if (callStack.check_underflow ()) + if (this->callStack.check_underflow ()) op = OpCode_return; else op = OpCode_endchar; @@ -61,26 +61,26 @@ struct CFF2CSInterpEnv : CSInterpEnv unsigned int ivs; }; -template -struct CFF2CSOpSet : CSOpSet +template +struct CFF2CSOpSet : CSOpSet { static inline bool process_op (OpCode op, CFF2CSInterpEnv &env, PARAM& param) { switch (op) { case OpCode_blendcs: - env.clear_stack (); // XXX: TODO + //env.flush_stack (); // XXX: TODO break; case OpCode_vsindexcs: { unsigned int ivs; if (unlikely (!env.argStack.check_pop_uint (ivs))) return false; env.set_ivs (ivs); - env.clear_stack (); + //env.flush_stack (); } break; default: - typedef CSOpSet SUPER; + typedef CSOpSet SUPER; if (unlikely (!SUPER::process_op (op, env, param))) return false; break; diff --git a/src/hb-ot-cff-common-private.hh b/src/hb-ot-cff-common-private.hh index 83f6a1af7..09bcc226d 100644 --- a/src/hb-ot-cff-common-private.hh +++ b/src/hb-ot-cff-common-private.hh @@ -299,12 +299,12 @@ struct Dict : UnsizedByteStr TRACE_SERIALIZE (this); /* serialize the opcode */ - HBUINT8 *p = c->allocate_size ((op >= OpCode_ESC_Base)? 2: 1); + HBUINT8 *p = c->allocate_size (OpCode_Size (op)); if (unlikely (p == nullptr)) return_trace (false); - if (op >= OpCode_ESC_Base) + if (Is_OpCode_ESC (op)) { p->set (OpCode_escape); - op = (OpCode)(op - OpCode_ESC_Base); + op = Unmake_OpCode_ESC (op); p++; } p->set (op); @@ -344,7 +344,7 @@ struct FDMap : hb_vector_t inline bool fullset (void) const { for (unsigned int i = 0; i < len; i++) - if ((*this)[i] == HB_SET_VALUE_INVALID) + if (hb_vector_t::operator[] (i) == HB_SET_VALUE_INVALID) return false; return true; } @@ -574,7 +574,7 @@ struct Subrs : CFFIndex TRACE_SERIALIZE (this); if (&subrs == &Null(Subrs)) return_trace (true); - if ((subrs.count == 0) || (hb_set_get_population (set) == 0)) + if ((subrs.count == 0) || (set == nullptr) || (hb_set_is_empty (set))) { if (!unlikely (c->allocate_size (COUNT::static_size))) return_trace (false); diff --git a/src/hb-subset-cff-common-private.hh b/src/hb-subset-cff-common-private.hh index 124bb6594..4013c3213 100644 --- a/src/hb-subset-cff-common-private.hh +++ b/src/hb-subset-cff-common-private.hh @@ -34,12 +34,108 @@ namespace CFF { +/* Used for writing a temporary charstring */ +struct ByteStrBuff : hb_vector_t +{ + inline bool encode_byte (unsigned char b) + { + return (push ((const char)b) != &Crap(char)); + } + + inline bool encode_num (const Number& n) + { + if (n.in_int_range ()) + { + int v = n.to_int (); + if ((-1131 <= v) && (v <= 1131)) + { + if ((-107 <= v) && (v <= 107)) + return encode_byte (v + 139); + else if (v > 0) + { + v -= 108; + return 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); + } + } + assert ((v & ~0xFFFF) == 0); + return encode_byte (OpCode_shortint) && + encode_byte ((v >> 8) & 0xFF) && + encode_byte (v & 0xFF); + } + 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); + } + } + + inline bool encode_op (OpCode op) + { + if (Is_OpCode_ESC (op)) + return encode_byte (OpCode_escape) && + encode_byte (Unmake_OpCode_ESC (op)); + else + return encode_byte (op); + } +}; + +struct ByteStrBuffArray : hb_vector_t +{ + inline void fini (void) + { + for (unsigned int i = 0; i < len; i++) + hb_vector_t::operator[] (i).fini (); + hb_vector_t::fini (); + } +}; + + +template +struct SubrFlattener +{ + inline SubrFlattener (const ACCESSOR &acc_, const hb_vector_t &glyphs_) + : acc (acc_), + glyphs (glyphs_) + {} + + inline bool flatten (ByteStrBuffArray &flat_charstrings) + { + if (!flat_charstrings.resize (glyphs.len)) + return false; + for (unsigned int i = 0; i < glyphs.len; i++) + flat_charstrings[i].init (); + 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.globalSubrs, *acc.privateDicts[fd].localSubrs); + if (unlikely (!interp.interpret (flat_charstrings[i]))) + return false; + } + return true; + } + + const ACCESSOR &acc; + const hb_vector_t &glyphs; +}; + struct SubrRefMaps { - inline SubrRefMaps (void) - : valid (false), - global_map (nullptr) + inline void init (void) { + valid = false; + global_map = nullptr; local_maps.init (); } diff --git a/src/hb-subset-cff1.cc b/src/hb-subset-cff1.cc index 0622c255c..8e647eac2 100644 --- a/src/hb-subset-cff1.cc +++ b/src/hb-subset-cff1.cc @@ -167,49 +167,84 @@ struct CFF1FontDict_OpSerializer : OpSerializer struct CFF1PrivateDict_OpSerializer : OpSerializer { + inline CFF1PrivateDict_OpSerializer (bool drop_hints_=false, bool flatten_subrs_=false) + : drop_hints (drop_hints_), flatten_subrs (flatten_subrs_) {} + inline bool serialize (hb_serialize_context_t *c, const OpStr &opstr, const unsigned int subrsOffset) const { TRACE_SERIALIZE (this); + if (drop_hints && DictOpSet::is_hint_op (opstr.op)) + return true; if (opstr.op == OpCode_Subrs) - return_trace (FontDict::serialize_offset2_op(c, OpCode_Subrs, subrsOffset)); + { + if (flatten_subrs) + return_trace (true); + else + return_trace (FontDict::serialize_offset2_op(c, OpCode_Subrs, subrsOffset)); + } else return_trace (copy_opstr (c, opstr)); } inline unsigned int calculate_serialized_size (const OpStr &opstr) const { + if (drop_hints && DictOpSet::is_hint_op (opstr.op)) + return 0; if (opstr.op == OpCode_Subrs) - return OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs); + { + if (flatten_subrs) + return 0; + else + return OpCode_Size (OpCode_shortint) + 2 + OpCode_Size (OpCode_Subrs); + } else return opstr.str.len; } + + protected: + const bool drop_hints; + const bool flatten_subrs; }; -struct CFF1PrivateDict_OpSerializer_DropHints : CFF1PrivateDict_OpSerializer +struct CFF1CSOpSet_Flatten : CFF1CSOpSet { - inline bool serialize (hb_serialize_context_t *c, - const OpStr &opstr, - const unsigned int subrsOffset) const + static inline bool process_op (OpCode op, CFF1CSInterpEnv &env, ByteStrBuff& flatStr) { - if (DictOpSet::is_hint_op (opstr.op)) - return true; - else - return CFF1PrivateDict_OpSerializer::serialize (c, opstr, subrsOffset); + if (unlikely (!SUPER::process_op (op, env, flatStr))) + return false; + switch (op) + { + case OpCode_hintmask: + case OpCode_cntrmask: + if (unlikely (!flatStr.encode_op (op))) + return false; + for (int i = -env.hintmask_size; i < 0; i++) + if (unlikely (!flatStr.encode_byte (env.substr[i]))) + return false; + break; + default: + if (!CSOpSet::is_subr_op (op) && + !CSOpSet::is_arg_op (op)) + return flatStr.encode_op (op); + } + return true; } - inline unsigned int calculate_serialized_size (const OpStr &opstr) const + static inline void flush_stack (CFF1CSInterpEnv &env, ByteStrBuff& flatStr) { - if (DictOpSet::is_hint_op (opstr.op)) - return 0; - else - return CFF1PrivateDict_OpSerializer::calculate_serialized_size (opstr); + for (unsigned int i = 0; i < env.argStack.size; i++) + flatStr.encode_num (env.argStack.elements[i]); + SUPER::flush_stack (env, flatStr); } + + private: + typedef CFF1CSOpSet SUPER; }; -struct CFF1CSOpSet_SubrSubset : CFF1CSOpSet +struct CFF1CSOpSet_SubsetSubrs : CFF1CSOpSet { static inline bool process_op (OpCode op, CFF1CSInterpEnv &env, SubrRefMapPair& refMapPair) { @@ -230,7 +265,7 @@ struct CFF1CSOpSet_SubrSubset : CFF1CSOpSet default: break; } - return CFF1CSOpSet::process_op (op, env, refMapPair); + return CFF1CSOpSet::process_op (op, env, refMapPair); } }; @@ -238,16 +273,20 @@ struct cff_subset_plan { inline cff_subset_plan (void) : final_size (0), orig_fdcount (0), - subst_fdcount(1), + subst_fdcount (1), subst_fdselect_format (0), - offsets() + offsets (), + flatten_subrs (true), + drop_hints (false) { topdict_sizes.init (); topdict_sizes.resize (1); subst_fdselect_first_glyphs.init (); fdmap.init (); subset_charstrings.init (); + flat_charstrings.init (); privateDictInfos.init (); + subrRefMaps.init (); } inline ~cff_subset_plan (void) @@ -256,6 +295,7 @@ struct cff_subset_plan { subst_fdselect_first_glyphs.fini (); fdmap.fini (); subset_charstrings.fini (); + flat_charstrings.fini (); privateDictInfos.fini (); subrRefMaps.fini (); } @@ -287,9 +327,20 @@ struct cff_subset_plan { offsets.stringIndexOffset = final_size; final_size += acc.stringIndex->get_size (); - /* Subset global & local subrs */ + if (flatten_subrs) { - SubrSubsetter subsetter(acc, plan->glyphs); + /* Flatten global & local subrs */ + SubrFlattener flattener(acc, plan->glyphs); + if (!flattener.flatten (flat_charstrings)) + return false; + + /* no global/local subroutines */ + offsets.globalSubrsInfo.size = HBUINT16::static_size; /* count 0 only */ + } + else + { + /* Subset global & local subrs */ + SubrSubsetter subsetter(acc, plan->glyphs); if (!subsetter.collect_refs (subrRefMaps)) return false; @@ -299,7 +350,6 @@ struct cff_subset_plan { for (unsigned int i = 0; i < orig_fdcount; i++) offsets.localSubrsInfos[i].size = acc.privateDicts[i].localSubrs->calculate_serialized_size (offsets.localSubrsInfos[i].offSize, subrRefMaps.local_maps[i], 1); } - /* global subrs */ offsets.globalSubrsInfo.offset = final_size; final_size += offsets.globalSubrsInfo.size; @@ -346,9 +396,19 @@ struct cff_subset_plan { unsigned int dataSize = 0; for (unsigned int i = 0; i < plan->glyphs.len; i++) { - const ByteStr str = (*acc.charStrings)[plan->glyphs[i]]; - subset_charstrings.push (str); - dataSize += str.len; + if (flatten_subrs) + { + ByteStrBuff &flatstr = flat_charstrings[i]; + ByteStr str (&flatstr[0], flatstr.len); + subset_charstrings.push (str); + dataSize += flatstr.len; + } + else + { + const ByteStr str = (*acc.charStrings)[plan->glyphs[i]]; + subset_charstrings.push (str); + dataSize += str.len; + } } offsets.charStringsInfo.offSize = calcOffSize (dataSize + 1); final_size += CFF1CharStrings::calculate_serialized_size (offsets.charStringsInfo.offSize, plan->glyphs.len, dataSize); @@ -361,26 +421,21 @@ struct cff_subset_plan { if (!fdmap.excludes (i)) { unsigned int priv_size; - if (plan->drop_hints) - { - CFF1PrivateDict_OpSerializer_DropHints privSzr_drop; - priv_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr_drop); - } - else - { - CFF1PrivateDict_OpSerializer privSzr; - priv_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr); - } + CFF1PrivateDict_OpSerializer privSzr (plan->drop_hints, flatten_subrs); + priv_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr); TableInfo privInfo = { final_size, priv_size, 0 }; privateDictInfos.push (privInfo); - final_size += privInfo.size + offsets.localSubrsInfos[i].size; + final_size += privInfo.size; + if (!flatten_subrs) + final_size += offsets.localSubrsInfos[i].size; } } if (!acc.is_CID ()) offsets.privateDictInfo = privateDictInfos[0]; - return true; + return ((subset_charstrings.len == plan->glyphs.len) && + (privateDictInfos.len == subst_fdcount)); } inline unsigned int get_final_size (void) const { return final_size; } @@ -399,11 +454,13 @@ struct cff_subset_plan { * set to HB_SET_VALUE_INVALID if excluded from subset */ FDMap fdmap; - hb_vector_t subset_charstrings; - hb_vector_t privateDictInfos; + hb_vector_t subset_charstrings; + ByteStrBuffArray flat_charstrings; + hb_vector_t privateDictInfos; SubrRefMaps subrRefMaps; + bool flatten_subrs; bool drop_hints; }; @@ -467,6 +524,7 @@ static inline bool _write_cff1 (const cff_subset_plan &plan, /* global subrs */ { + assert (plan.offsets.globalSubrsInfo.offset != 0); assert (plan.offsets.globalSubrsInfo.offset == c.head - c.start); CFF1Subrs *dest = c.start_embed (); if (unlikely (dest == nullptr)) return false; @@ -565,25 +623,17 @@ static inline bool _write_cff1 (const cff_subset_plan &plan, { PrivateDict *pd = c.start_embed (); if (unlikely (pd == nullptr)) return false; - unsigned int priv_size = plan.privateDictInfos[plan.fdmap[i]].size; + unsigned int priv_size = plan.flatten_subrs? 0: plan.privateDictInfos[plan.fdmap[i]].size; bool result; + CFF1PrivateDict_OpSerializer privSzr (plan.drop_hints, plan.flatten_subrs); /* N.B. local subrs immediately follows its corresponding private dict. i.e., subr offset == private dict size */ - if (plan.drop_hints) - { - CFF1PrivateDict_OpSerializer_DropHints privSzr_drop; - result = pd->serialize (&c, acc.privateDicts[i], privSzr_drop, priv_size); - } - else - { - CFF1PrivateDict_OpSerializer privSzr; - result = pd->serialize (&c, acc.privateDicts[i], privSzr, priv_size); - } + result = pd->serialize (&c, acc.privateDicts[i], privSzr, priv_size); if (unlikely (!result)) { DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF Private Dict[%d]", i); return false; } - if (acc.privateDicts[i].subrsOffset != 0) + if (!plan.flatten_subrs && (acc.privateDicts[i].subrsOffset != 0)) { CFF1Subrs *subrs = c.start_embed (); if (unlikely (subrs == nullptr) || acc.privateDicts[i].localSubrs == &Null(CFF1Subrs)) diff --git a/src/hb-subset-cff2.cc b/src/hb-subset-cff2.cc index e76a438be..8d45b00ad 100644 --- a/src/hb-subset-cff2.cc +++ b/src/hb-subset-cff2.cc @@ -187,7 +187,7 @@ struct CFF2PrivateDict_OpSerializer_DropHints : CFF2PrivateDict_OpSerializer } }; -struct CFF2CSOpSet_SubrSubset : CFF2CSOpSet +struct CFF2CSOpSet_SubsetSubrs : CFF2CSOpSet { static inline bool process_op (OpCode op, CFF2CSInterpEnv &env, SubrRefMapPair& refMapPair) { @@ -208,7 +208,7 @@ struct CFF2CSOpSet_SubrSubset : CFF2CSOpSet default: break; } - return CFF2CSOpSet::process_op (op, env, refMapPair); + return CFF2CSOpSet::process_op (op, env, refMapPair); } }; @@ -223,6 +223,7 @@ struct cff2_subset_plan { fdmap.init (); subset_charstrings.init (); privateDictInfos.init (); + subrRefMaps.init (); } inline ~cff2_subset_plan (void) @@ -254,7 +255,7 @@ struct cff2_subset_plan { /* Subset global & local subrs */ { - SubrSubsetter subsetter(acc, plan->glyphs); + SubrSubsetter subsetter(acc, plan->glyphs); if (!subsetter.collect_refs (subrRefMaps)) return false;