From 0996c0ff6279f377e2b14f08663df2ce82de2b14 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Wed, 7 Nov 2018 14:48:37 -0800 Subject: [PATCH] implented no-desubroutinize with CFF2 along with API test replaced AdobeVFPrototype.abc.otf with a hinted (maually) & subroutinized copy replaced expected results as well --- src/hb-cff-interp-dict-common.hh | 4 +- src/hb-cff2-interp-cs.hh | 16 +- src/hb-ot-cff-common.hh | 22 ++- src/hb-subset-cff-common.hh | 64 ++++--- src/hb-subset-cff2.cc | 180 +++++++++++++++--- .../fonts/AdobeVFPrototype.abc.nohints.otf | Bin 7800 -> 0 bytes test/api/fonts/AdobeVFPrototype.abc.otf | Bin 7800 -> 7460 bytes .../api/fonts/AdobeVFPrototype.ac.nohints.otf | Bin 7152 -> 6780 bytes .../AdobeVFPrototype.ac.nosubrs.nohints.otf | Bin 0 -> 6844 bytes .../api/fonts/AdobeVFPrototype.ac.nosubrs.otf | Bin 0 -> 7060 bytes test/api/fonts/AdobeVFPrototype.ac.otf | Bin 7336 -> 6996 bytes test/api/test-subset-cff2.c | 51 ++++- 12 files changed, 271 insertions(+), 66 deletions(-) delete mode 100644 test/api/fonts/AdobeVFPrototype.abc.nohints.otf create mode 100644 test/api/fonts/AdobeVFPrototype.ac.nosubrs.nohints.otf create mode 100644 test/api/fonts/AdobeVFPrototype.ac.nosubrs.otf diff --git a/src/hb-cff-interp-dict-common.hh b/src/hb-cff-interp-dict-common.hh index 88fd34560..35b9c3a93 100644 --- a/src/hb-cff-interp-dict-common.hh +++ b/src/hb-cff-interp-dict-common.hh @@ -262,12 +262,12 @@ struct DictInterpreter : Interpreter inline bool interpret (PARAM& param) { param.init (); - do + while (SUPER::env.substr.avail ()) { OPSET::process_op (SUPER::env.fetch_op (), SUPER::env, param); if (unlikely (SUPER::env.in_error ())) return false; - } while (SUPER::env.substr.avail ()); + } return true; } diff --git a/src/hb-cff2-interp-cs.hh b/src/hb-cff2-interp-cs.hh index 592a1b23e..672e67c94 100644 --- a/src/hb-cff2-interp-cs.hh +++ b/src/hb-cff2-interp-cs.hh @@ -88,7 +88,7 @@ struct CFF2CSInterpEnv : CSInterpEnv num_coords = num_coords_; varStore = acc.varStore; seen_blend = false; - seen_vsindex = false; + seen_vsindex_ = false; scalars.init (); do_blend = (coords != nullptr) && num_coords && (varStore != &Null(CFF2VariationStore)); set_ivs (acc.privateDicts[fd].ivs); @@ -145,18 +145,22 @@ struct CFF2CSInterpEnv : CSInterpEnv inline void process_vsindex (void) { unsigned int index = argStack.pop_uint (); - if (do_blend) + if (unlikely (seen_vsindex () || seen_blend)) { - if (likely (!seen_vsindex && !seen_blend)) - set_ivs (index); + set_error (); } - seen_vsindex = true; + else + { + set_ivs (index); + } + seen_vsindex_ = true; } inline unsigned int get_region_count (void) const { return region_count; } inline void set_region_count (unsigned int region_count_) { region_count = region_count_; } inline unsigned int get_ivs (void) const { return ivs; } inline void set_ivs (unsigned int ivs_) { ivs = ivs_; } + inline bool seen_vsindex (void) const { return seen_vsindex_; } protected: inline void blend_arg (BlendArg &arg) @@ -184,7 +188,7 @@ struct CFF2CSInterpEnv : CSInterpEnv unsigned int ivs; hb_vector_t scalars; bool do_blend; - bool seen_vsindex; + bool seen_vsindex_; bool seen_blend; typedef CSInterpEnv SUPER; diff --git a/src/hb-ot-cff-common.hh b/src/hb-ot-cff-common.hh index b98119f54..4607efb24 100644 --- a/src/hb-ot-cff-common.hh +++ b/src/hb-ot-cff-common.hh @@ -418,14 +418,14 @@ struct TableInfo struct Remap : hb_vector_t { inline void init (void) - { hb_vector_t::init (); } + { SUPER::init (); } inline void fini (void) - { hb_vector_t::fini (); } + { SUPER::fini (); } inline bool reset (unsigned int size) { - if (unlikely (!hb_vector_t::resize (size))) + if (unlikely (!SUPER::resize (size))) return false; for (unsigned int i = 0; i < len; i++) (*this)[i] = CFF_UNDEF_CODE; @@ -436,7 +436,7 @@ struct Remap : hb_vector_t inline bool fullset (void) const { for (unsigned int i = 0; i < len; i++) - if (hb_vector_t::operator[] (i) == CFF_UNDEF_CODE) + if (SUPER::operator[] (i) == CFF_UNDEF_CODE) return false; return true; } @@ -452,13 +452,13 @@ struct Remap : hb_vector_t if (fullset ()) return i; else - return hb_vector_t::operator[] (i); + return SUPER::operator[] (i); } inline hb_codepoint_t &operator[] (hb_codepoint_t i) { assert (i < len); - return hb_vector_t::operator[] (i); + return SUPER::operator[] (i); } inline unsigned int add (unsigned int i) @@ -473,6 +473,9 @@ struct Remap : hb_vector_t protected: hb_codepoint_t count; + + private: + typedef hb_vector_t SUPER; }; template @@ -533,7 +536,7 @@ struct FDArray : CFFIndexOf unsigned int offset = 1; unsigned int fid = 0; for (unsigned i = 0; i < fontDicts.len; i++) - if (!fdmap.excludes (i)) + if (fdmap.includes (i)) { CFFIndexOf::set_offset_at (fid++, offset); offset += FontDict::calculate_serialized_size (fontDicts[i], opszr); @@ -542,7 +545,7 @@ struct FDArray : CFFIndexOf /* serialize font dicts */ for (unsigned int i = 0; i < fontDicts.len; i++) - if (!fdmap.excludes (i)) + if (fdmap.includes (i)) { FontDict *dict = c->start_embed (); if (unlikely (!dict->serialize (c, fontDicts[i], opszr, privateInfos[fdmap[i]]))) @@ -561,7 +564,7 @@ struct FDArray : CFFIndexOf { unsigned int dictsSize = 0; for (unsigned int i = 0; i < fontDicts.len; i++) - if (!fdmap.excludes (i)) + if (fdmap.includes (i)) dictsSize += FontDict::calculate_serialized_size (fontDicts[i], opszr); offSize_ = calcOffSize (dictsSize); @@ -720,4 +723,3 @@ struct Subrs : CFFIndex } /* namespace CFF */ #endif /* HB_OT_CFF_COMMON_HH */ - diff --git a/src/hb-subset-cff-common.hh b/src/hb-subset-cff-common.hh index 8631d2b8d..3cb407070 100644 --- a/src/hb-subset-cff-common.hh +++ b/src/hb-subset-cff-common.hh @@ -411,7 +411,7 @@ struct ParsedCStr : ParsedValues { SUPER::init (); parsed = false; - hint_removed = false; + hint_dropped = false; has_prefix_ = false; } @@ -444,15 +444,18 @@ struct ParsedCStr : ParsedValues 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 is_hint_dropped (void) const { return hint_dropped; } + inline void set_hint_dropped (void) { hint_dropped = true; } + inline bool is_vsindex_dropped (void) const { return vsindex_dropped; } + inline void set_vsindex_dropped (void) { vsindex_dropped = 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 hint_dropped; + bool vsindex_dropped; bool has_prefix_; OpCode prefix_op_; Number prefix_num_; @@ -697,10 +700,13 @@ struct SubrSubsetter closures.global_closure, closures.local_closures[fd], drop_hints); - bool seen_moveto = false; - bool ends_in_hint = false; - if (drop_hints_in_str (parsed_charstrings[i], param, seen_moveto, ends_in_hint)) - parsed_charstrings[i].set_hint_removed (); + DropHintsParam drop; + if (drop_hints_in_str (parsed_charstrings[i], param, drop)) + { + parsed_charstrings[i].set_hint_dropped (); + if (drop.vsindex_dropped) + parsed_charstrings[i].set_vsindex_dropped (); + } } /* after dropping hints recreate closures of actually used subrs */ @@ -764,24 +770,35 @@ struct SubrSubsetter } protected: + struct DropHintsParam + { + inline DropHintsParam (void) + : seen_moveto (false), + ends_in_hint (false), + vsindex_dropped (false) {} + + bool seen_moveto; + bool ends_in_hint; + bool vsindex_dropped; + }; + inline bool drop_hints_in_subr (ParsedCStr &str, unsigned int pos, ParsedCStrs &subrs, unsigned int subr_num, - const SubrSubsetParam ¶m, bool &seen_moveto) + const SubrSubsetParam ¶m, DropHintsParam &drop) { - bool ends_in_hint = false; - bool has_hint = drop_hints_in_str (subrs[subr_num], param, seen_moveto, ends_in_hint); + drop.ends_in_hint = false; + bool has_hint = drop_hints_in_str (subrs[subr_num], param, drop); /* if this subr ends with a stem hint (i.e., not a number a potential argument for moveto), * then this entire subroutine must be a hint. drop its call. */ - if (ends_in_hint) + if (drop.ends_in_hint) str.values[pos].set_drop (); return has_hint; } /* returns true if it sees a hint op before the first moveto */ - inline bool drop_hints_in_str (ParsedCStr &str, const SubrSubsetParam ¶m, - bool &seen_moveto, bool &ends_in_hint) + inline bool drop_hints_in_str (ParsedCStr &str, const SubrSubsetParam ¶m, DropHintsParam &drop) { bool seen_hint = false; @@ -793,25 +810,25 @@ struct SubrSubsetter case OpCode_callsubr: has_hint = drop_hints_in_subr (str, pos, *param.parsed_local_subrs, str.values[pos].subr_num, - param, seen_moveto); + param, drop); break; case OpCode_callgsubr: has_hint = drop_hints_in_subr (str, pos, *param.parsed_global_subrs, str.values[pos].subr_num, - param, seen_moveto); + param, drop); break; case OpCode_rmoveto: case OpCode_hmoveto: case OpCode_vmoveto: - seen_moveto = true; + drop.seen_moveto = true; break; case OpCode_hintmask: case OpCode_cntrmask: - if (seen_moveto) + if (drop.seen_moveto) { str.values[pos].set_drop (); break; @@ -826,7 +843,7 @@ struct SubrSubsetter str.values[pos].set_drop (); if ((pos + 1 >= str.values.len) /* CFF2 */ || (str.values[pos + 1].op == OpCode_return)) - ends_in_hint = true; + drop.ends_in_hint = true; break; default: @@ -837,9 +854,12 @@ struct SubrSubsetter { for (int i = pos - 1; i >= 0; i--) { - if (str.values[i].for_drop ()) + ParsedCSOp &csop = str.values[i]; + if (csop.for_drop ()) break; - str.values[i].set_drop (); + csop.set_drop (); + if (csop.op == OpCode_vsindexcs) + drop.vsindex_dropped = true; } seen_hint |= has_hint; } @@ -890,7 +910,7 @@ struct SubrSubsetter 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 ()) + if (str.has_prefix () && str.is_hint_dropped ()) { encoder.encode_num (str.prefix_num ()); if (str.prefix_op () != OpCode_Invalid) diff --git a/src/hb-subset-cff2.cc b/src/hb-subset-cff2.cc index f28af2ba2..990bced27 100644 --- a/src/hb-subset-cff2.cc +++ b/src/hb-subset-cff2.cc @@ -167,6 +167,68 @@ struct CFF2CSOpSet_Flatten : CFF2CSOpSet typedef CSOpSet CSOPSET; }; +struct CFF2CSOpSet_SubrSubset : CFF2CSOpSet +{ + static inline void process_op (OpCode op, CFF2CSInterpEnv &env, SubrSubsetParam& param) + { + switch (op) { + + case OpCode_return: + param.current_parsed_str->set_parsed (); + env.returnFromSubr (); + param.set_current_str (env); + break; + + case OpCode_endchar: + 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; + } + } + + protected: + static inline void process_call_subr (OpCode op, CSType type, + CFF2CSInterpEnv &env, SubrSubsetParam& param, + CFF2BiasedSubrs& subrs, hb_set_t *closure) + { + SubByteStr substr = env.substr; + env.callSubr (subrs, type); + param.current_parsed_str->add_call_op (op, substr, env.context.subr_num); + hb_set_add (closure, env.context.subr_num); + param.set_current_str (env); + } + + private: + typedef CFF2CSOpSet SUPER; +}; + +struct CFF2SubrSubsetter : SubrSubsetter +{ + static inline void finalize_parsed_str (CFF2CSInterpEnv &env, SubrSubsetParam& param, ParsedCStr &charstring) + { + /* vsindex is inserted at the beginning of the charstring as necessary */ + if (env.seen_vsindex ()) + { + Number ivs; + ivs.set_int ((int)env.get_ivs ()); + charstring.set_prefix (ivs, OpCode_vsindexcs); + } + } +}; + struct cff2_subset_plan { inline cff2_subset_plan (void) : final_size (0), @@ -179,7 +241,8 @@ struct cff2_subset_plan { subset_fdselect_ranges.init (); fdmap.init (); subset_charstrings.init (); - flat_charstrings.init (); + subset_globalsubrs.init (); + subset_localsubrs.init (); privateDictInfos.init (); } @@ -187,8 +250,9 @@ struct cff2_subset_plan { { subset_fdselect_ranges.fini (); fdmap.fini (); - subset_charstrings.fini (); - flat_charstrings.fini_deep (); + subset_charstrings.fini_deep (); + subset_globalsubrs.fini_deep (); + subset_localsubrs.fini_deep (); privateDictInfos.fini (); } @@ -211,17 +275,60 @@ struct cff2_subset_plan { final_size += offsets.topDictInfo.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 = HBUINT32::static_size; /* count 0 only */ + offsets.globalSubrsInfo.size = CFF2Subrs::calculate_serialized_size (1, 0, 0); } - + 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 = CFF2Subrs::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 (); + offsets.localSubrsInfos[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 (); + if (dataSize > 0) + { + offsets.localSubrsInfos[fd].offset = final_size; + offsets.localSubrsInfos[fd].offSize = calcOffSize (dataSize); + offsets.localSubrsInfos[fd].size = CFF2Subrs::calculate_serialized_size (offsets.localSubrsInfos[fd].offSize, subset_localsubrs[fd].len, dataSize); + } + } + } + } + /* global subrs */ offsets.globalSubrsInfo.offset = final_size; final_size += offsets.globalSubrsInfo.size; @@ -256,20 +363,19 @@ struct cff2_subset_plan { { offsets.FDArrayInfo.offset = final_size; CFFFontDict_OpSerializer fontSzr; - final_size += CFF2FDArray::calculate_serialized_size(offsets.FDArrayInfo.offSize/*OUT*/, acc.fontDicts, subset_fdcount, fdmap, fontSzr); + unsigned int dictsSize = 0; + for (unsigned int i = 0; i < acc.fontDicts.len; i++) + if (fdmap.includes (i)) + dictsSize += FontDict::calculate_serialized_size (acc.fontDicts[i], fontSzr); + + offsets.FDArrayInfo.offSize = calcOffSize (dictsSize); + final_size += CFF2Index::calculate_serialized_size (offsets.FDArrayInfo.offSize, subset_fdcount, dictsSize); } /* CharStrings */ { offsets.charStringsInfo.offset = final_size; - unsigned int dataSize = 0; - for (unsigned int i = 0; i < plan->glyphs.len; i++) - { - StrBuff &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 += CFF2CharStrings::calculate_serialized_size (offsets.charStringsInfo.offSize, plan->glyphs.len, dataSize); } @@ -278,14 +384,20 @@ struct cff2_subset_plan { offsets.privateDictsOffset = final_size; for (unsigned int i = 0; i < orig_fdcount; i++) { - if (!fdmap.excludes (i)) + if (fdmap.includes (i)) { - unsigned int priv_size; + bool has_localsubrs = offsets.localSubrsInfos[i].size > 0; CFFPrivateDict_OpSerializer privSzr (desubroutinize, drop_hints); - priv_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr); + unsigned int priv_size = PrivateDict::calculate_serialized_size (acc.privateDicts[i], privSzr, has_localsubrs); TableInfo privInfo = { final_size, priv_size, 0 }; privateDictInfos.push (privInfo); final_size += privInfo.size; + + if (!plan->desubroutinize && has_localsubrs) + { + offsets.localSubrsInfos[i].offset = final_size; + final_size += offsets.localSubrsInfos[i].size; + } } } @@ -305,12 +417,14 @@ struct cff2_subset_plan { Remap fdmap; - ByteStrArray subset_charstrings; - StrBuffArray flat_charstrings; + StrBuffArray subset_charstrings; + StrBuffArray subset_globalsubrs; + hb_vector_t subset_localsubrs; hb_vector_t privateDictInfos; bool drop_hints; bool desubroutinize; + CFF2SubrSubsetter subr_subsetter; }; static inline bool _write_cff2 (const cff2_subset_plan &plan, @@ -346,9 +460,13 @@ static inline bool _write_cff2 (const cff2_subset_plan &plan, /* global subrs */ { assert (cff2->topDict + plan.offsets.topDictInfo.size == c.head - c.start); - CFF2Subrs *dest = c.allocate_size (HBUINT32::static_size); + CFF2Subrs *dest = c.start_embed (); if (unlikely (dest == nullptr)) return false; - dest->count.set (0); + if (unlikely (!dest->serialize (&c, plan.offsets.globalSubrsInfo.offSize, plan.subset_globalsubrs))) + { + DEBUG_MSG (SUBSET, nullptr, "failed to serialize global subroutines"); + return false; + } } /* variation store */ @@ -420,19 +538,31 @@ static inline bool _write_cff2 (const cff2_subset_plan &plan, assert (plan.offsets.privateDictsOffset == c.head - c.start); for (unsigned int i = 0; i < acc.privateDicts.len; i++) { - if (!plan.fdmap.excludes (i)) + if (plan.fdmap.includes (i)) { PrivateDict *pd = c.start_embed (); if (unlikely (pd == nullptr)) return false; unsigned int priv_size = plan.privateDictInfos[plan.fdmap[i]].size; bool result; CFFPrivateDict_OpSerializer privSzr (plan.desubroutinize, plan.drop_hints); - result = pd->serialize (&c, acc.privateDicts[i], privSzr, priv_size); + /* N.B. local subrs immediately follows its corresponding private dict. i.e., subr offset == private dict size */ + unsigned int subroffset = (plan.offsets.localSubrsInfos[i].size > 0)? priv_size: 0; + result = pd->serialize (&c, acc.privateDicts[i], privSzr, subroffset); if (unlikely (!result)) { - DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF2 Private Dict[%d]", i); + DEBUG_MSG (SUBSET, nullptr, "failed to serialize CFF Private Dict[%d]", i); return false; } + if (plan.offsets.localSubrsInfos[i].size > 0) + { + CFF2Subrs *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; + } + } } } diff --git a/test/api/fonts/AdobeVFPrototype.abc.nohints.otf b/test/api/fonts/AdobeVFPrototype.abc.nohints.otf deleted file mode 100644 index a2d2150b066190d507e132091322fa13f2407827..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7800 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 VLd&%)@fRst>`nQt8}D3!{{vtI#LoZ# diff --git a/test/api/fonts/AdobeVFPrototype.abc.otf b/test/api/fonts/AdobeVFPrototype.abc.otf index a2d2150b066190d507e132091322fa13f2407827..e21d87f7d469a62f170e4bc6b51e26aadfc4bcdd 100644 GIT binary patch delta 1268 zcmW+#eN0aqR7Zq5DiA$E~CQ%Y&%ruK8#>FLb>SDrQrggC3DSzB^-|wEU-}&8l z)0OitxNB-_YuHZK%T(;_D@Tr$<=?NfGRFR4EGzG|x~ex{w6DL*Sat)-8KY5h2 zF_zOtF`k+e&z8Nl*m;p~n()~ZwN9Ajn+ z@7k5wN(#sbf}p}&)}S%#IU5RsmH}W4$48XsF)XCTT2UR3yyY>FXr+6gw9FCKsYMY$c`PoX9jwb9LiD)?f= z-Ns?!IEOJ0RqUY|`iwAzNMksH6Rrh}Q#V=R;Gtp)b$5;FVIeV*jG~LC_s((XOO!&- zm(cq3wDxYrvGOPa*;mC!6s4bpH?4l(xthf(A(7epME_I&^G z1q+T-^|*^z4N<*#=4y&JIz6GLXtxD;VTxTx?+l^|&>xgkR}d%t97;hBN{^?^eX)Dk zw8~**kaVD^!QP=^54BBwqXfU`k6U0Ujv*uXFi6RoRIM(MsFnQ|(okb4p)^TjizNEI z6nLi(m1bfXGQ(!I_x1VA1t_0u%pz^SYsy8s&)wQnBxP)Xq-@s?h`dmFs4Y5_`7KDE zVI=W<1~{`kUykGFX&k!ua66)Rd^XZUlpK+`^HUtFHUiB#xIpz9bWgra*%aqa7(p?> z?VhTeW#}W-Kcp3>vd#pDsWv@qJhmb3-*|3g|J{A5!oR<}c4q-)EBTeLb@d{LmEtTx zaA%&^jeIg72d%+Q*Wbyl{FkXz^v)T(ef|Cqnf;fn-*j8Yx3uaOl}u2@M6xRF7(VT#v`mES7uF%gX{S|CCu(I5b%;gr0@sylMeDQ!T<0hv7E85Kz)ERP z%SUe+r3_T;7)FN}vt-M@j4{(>*%rO>(CXu9(7Q&H3Ma9K=Km2Gm5JG+AAsaKtG(v$pIUwfpT;3A^+%68{<%x?2+^PcvPe!8WKI@VfmsX}s~k5@M0MQ_CcaOr7x=c9pK#(30dtM(%P^#fjvHQb3kzrePPQAzmTI#+>eWkWZe@ zf0F05oeMWp?_gqQ(s71T$rI!=rt|-Ly7m8{Nd$$*oZjfOAnBe=yO2_P*9j=S1~8nX zw2N1Ym2|8XR7#U;BtgD7AM17+p#B~v4gjJ)8l*j4&>|kJLZ@gT!fQrHPoHypCgLRr~X>ax0N z05a|90EAl@1OSr23G0#D6+%4i0k9b$=SL&aYZqzDIDFds0RHy?jl%%U1`gTMm)r&w zmOhD9p5n9~5SG%{%et{)HBb)dYXeBltS>eN{}82Ut|2tw9-&coj-Ui+W9cP!HaDuF z{zz0d))$5~BVG536eFnPU)K4nThu>W8~qywNUR5gWhMIZTJz zp?5|Ay=MWe0=*bYWdHWVt%#lwDW$R8w1LZR8Zjc8StKKMM-bV2GbL zp=<4ZL~CzB=wbKgJ&&`rbac0&;KbKbDEpH$QABk_X(wF#J{t1lE1NI{Vy2)U89^6 z&>0DYF!Xq=FWwzE9Si!Ym)%M`%ixJIe^3nleskf?Ja*-jo|I@V#)lPkGLIqL0#Z!P zn3AuYS~IosVa`O>FW-Fn05z=4N!q8ZEjIi`{wc29Q L=&w6c$N7H&u=81H diff --git a/test/api/fonts/AdobeVFPrototype.ac.nohints.otf b/test/api/fonts/AdobeVFPrototype.ac.nohints.otf index 188a87051929decd23b849d383320c0961e5ed20..c0d034ad0a1aef30c71b0ce378720651a9214e00 100644 GIT binary patch delta 1105 zcmW+#Z%kWN6hG~2A9!H$wmF?s*rk0Ga4xdW2*#O7jAY5!YJ|)XSzL!(`hgDYx|K?$ zrBn(%Erq_amNNbb$hzn%WPULr*=j?A4dM*OsD~wOHv^KWhsJN46Jj?h`=Xs~c;#PYBO5%)bI^51~7&Lyz z_%p_xr_Vcsp6{Q1<_tioU_;(@%K46xD=y`NhZw)$Vnfj}V}boQ7^^O~w?{q+ml-ob zD0Mq~x$Nd&p6$u{`)<24`2(!E$!W1XT6&@!49yioejbWX4qcNyI`rRtK`@I|tfxZbc~kGN)1ebu>z zlvIiwZB*zNc&VQ#q3l@oKCn~DPZU?yy&HClOzB&h%Cr`vrB2`=#b|g;pf)Km8@1tB zvI^5d8zy=wmR2hA`S)ji0rE`=?3uHncExD53GCtn{*=ITA6oFZYxa76eEDu6)_XBN z9FJyV3CfLBp|)Xsxklg!_d2E&e;V;?sbDR(g`54(0L795hlT{ELcC&J-6b8av7tY~ zY-p?sBU2WN@qFmc$u6R+udZsW+OfYK2<4X(sY2q{=;k!+qBZ^GSrTVy< zCzbVmLYZb2IoIGKzd^wK7`-`N>}Jof{!VcwIaP!Dn3>~B5l=VIKYaPag6e*|?7)4HB>qAGw^n}pR@Xlk k|6H_rbW=8UN%}vQz^Gf{>NUAulEHM?ZGx+HCD)bz0fe+{MgRZ+ delta 1433 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 Yf7TLO&a|ZczoPgt`QMlSNu7562eua}`v3p{ literal 0 HcmV?d00001 diff --git a/test/api/fonts/AdobeVFPrototype.ac.nosubrs.otf b/test/api/fonts/AdobeVFPrototype.ac.nosubrs.otf new file mode 100644 index 0000000000000000000000000000000000000000..ad4d53b874827c32af3b4a1a109d8f88b68b148b GIT binary patch literal 7060 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 literal 0 HcmV?d00001 diff --git a/test/api/fonts/AdobeVFPrototype.ac.otf b/test/api/fonts/AdobeVFPrototype.ac.otf index b890310b8ee495c34f992c953c30464efb029fa8..c5080fcb36548a659558454dec0389cec72e8631 100644 GIT binary patch delta 1135 zcmW+#ZA_b06h3d?evkum@3yGZ3A?nVfO9&!Wgl^7A?mVZY%GFPB8%(TW`EFutfNIL zg;FW>yrs~$Kq=!xn9xO6A^cimvdt`Ui7`5L$r7W{#xt=g6HMO{dmrE&pFSz z_uR~ZANK~UYiq0F3FrhFPQQBiuygL=i#Gt^ZvgY2BPT20c-iyM834=M0P?|Oj6Yq? zxB)Q9XF}CAFFD`I`Y!|IZ)JSCrna*7PSJ17<@*``_Dc)ryYZ2A6$=P$4!$A*mioa?NT&MV-Ag7IEA8!RUcS%3{wjO}iZ*Ka-pR~g@C zT;Or}n?XLo+YKBP%okjqbN~I(xy1NBkNW1Oi{7=n-~`yVhk0QD6KpBuU9iBeFY!&A z^rcu=FG-S28=+oPEyuLCq^A$jm4N_e*I`EYL6uQn*#6p1Xe8we0`EA zu2`z;DBC4)u8Sz9Jel)8c2c5^NL5z6t4<1!>szxZynZJq<;rQem7Rt{ksrv%Of;TU z`{IhE)flY11olvr`UVARV**nV2M)$dFd1}Utev7sr6`m6aLN~8M}hun2Wr<0Hiy6_ zc4$ioy!5dZPr0XVW`-7i&qmuXt9@!@BpRc1e+g=9I_b0$hD~MqpRE1UqhU#*hs^dC<^E-S2ayUV)xi%SX| zs`yCYFbSNA5e+EYzL~pNPNN(>6<}4p951E6+4g0BsJ}7UUXG$rd)8ERK)-sHFhDFY zV#8DOS9a6rd3NEujb9=Pk4`5#I9p31<@=0As_e+IdS>|z0;XpUu{WO!rtPTN@Losd zmrjasWgqZJaadnk;DpzT%lXTOoy!%=J0I-$xA4!eum3Vjs*8E;S5uuq;Nl^(Bn9=( zzO{uXlqBQkr=Jrx$BtdQSr<1|8CIvWzQ7gkr}gUeX*)B0`a`L#a=Z-nCN!yvg?pcW z2i#sfA-(tR#)FSQmidDL-239;xB7ZN?`O-}@inutN!EY03`9wRI+ I<=<5P2U)3o>;M1& delta 1440 zcmZ8hYfM{Z82;KrDYwy%>F6An$?9MOhU>PDDZ_Co8#}FvQr>+}8g03egY}H;DK)DUp#)@vvMQ8ON#OqwqQx z#fk|rii;7&qEVuvg zQ*Rfg9;HkOELd%Gh}_M*rx`F$dwb}>tLu@~_I^r5JF4%Z=zi$`MXM;f-%kFZO1vRa z$5BdGte~PLkZzjN5v5$Hqze}y#VBclSSU@(gzca%IyA zlnKG$`O8)twc=2;a|V8KO1eEAqL~$Tc|jeoQ97#JT4la`H8{TX!-A{(qI<~goN#$) zU>NPh+tNe3kvEL%7*mG5JJ2P1v6sBEuc*spqb|7V;2_A`%g6;13psv2hIM&RPsc!P zKMZ+i-oG?Udl!-8m;epE0_vCn;kJ;FC|=|u+tYZHs zfBjZ%Bkex}M^`&&vR3R5iOGgF>;sLz3}QX$tY^L7^*7pf(n&VJZv#08Svx=bVu@>= zeZ!5uZd{ajJtTpx;lM*}qadGieOT4v++I4_0Ed%(jgbzVYI4TgGoqBWdi~b7=|mCS zL#(M|9;;4byxZz?d9cY`U}A36bw1!8q62JI2kT%DuzW~ta|ML0!-(iM{C5bj!C5QL&H!_Jtjh_R-^U0^zN;9ez zS$`0YWB&!@=9M46Tz`L9n7f=WKgQW5nY<;0Y|{#95eh|z!0UNV`w%=Y2!b?WhrCgj zQ&f2(Gwx_uR<3TRuF$mSbY#sFWz{N9CFuG5+T$;6DSpaack)>M_JZW6pG@5MVvD+5 z8EY`67zIm+R=Z1^vX_4@CRNav@Xtp%rc;WaKpbmJZAeli(ZUJK3W9!1|qnb?n%Bl@o{$G^p^?7-D5{V=ww z?QY{Wj~qxBcz(m7Q0yf<`g~G+mR8@o|H!F>8D}y!>mTl|eY`pQf%89Kh_s?2HB}EN Q6e^X`n4n$uMO;<>1GwBDw*UYD diff --git a/test/api/test-subset-cff2.c b/test/api/test-subset-cff2.c index dafb32b62..da948557e 100644 --- a/test/api/test-subset-cff2.c +++ b/test/api/test-subset-cff2.c @@ -84,7 +84,54 @@ test_subset_cff2_strip_hints (void) face_abc_subset = hb_subset_test_create_subset (face_abc, input); hb_set_destroy (codepoints); - hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('C', 'F', 'F', ' ')); + hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('C', 'F', 'F', '2')); + + hb_face_destroy (face_abc_subset); + hb_face_destroy (face_abc); + hb_face_destroy (face_ac); +} + +static void +test_subset_cff2_desubr (void) +{ + hb_face_t *face_abc = hb_test_open_font_file ("fonts/AdobeVFPrototype.abc.otf"); + hb_face_t *face_ac = hb_test_open_font_file ("fonts/AdobeVFPrototype.ac.nosubrs.otf"); + + hb_set_t *codepoints = hb_set_create (); + hb_subset_input_t *input; + hb_face_t *face_abc_subset; + hb_set_add (codepoints, 'a'); + hb_set_add (codepoints, 'c'); + input = hb_subset_test_create_input (codepoints); + hb_subset_input_set_desubroutinize (input, true); + face_abc_subset = hb_subset_test_create_subset (face_abc, input); + hb_set_destroy (codepoints); + + hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('C', 'F', 'F', '2')); + + hb_face_destroy (face_abc_subset); + hb_face_destroy (face_abc); + hb_face_destroy (face_ac); +} + +static void +test_subset_cff2_desubr_strip_hints (void) +{ + hb_face_t *face_abc = hb_test_open_font_file ("fonts/AdobeVFPrototype.abc.otf"); + hb_face_t *face_ac = hb_test_open_font_file ("fonts/AdobeVFPrototype.ac.nosubrs.nohints.otf"); + + hb_set_t *codepoints = hb_set_create (); + hb_subset_input_t *input; + hb_face_t *face_abc_subset; + hb_set_add (codepoints, 'a'); + hb_set_add (codepoints, 'c'); + input = hb_subset_test_create_input (codepoints); + hb_subset_input_set_desubroutinize (input, true); + hb_subset_input_set_drop_hints (input, true); + face_abc_subset = hb_subset_test_create_subset (face_abc, input); + hb_set_destroy (codepoints); + + hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('C', 'F', 'F', '2')); hb_face_destroy (face_abc_subset); hb_face_destroy (face_abc); @@ -99,6 +146,8 @@ main (int argc, char **argv) hb_test_add (test_subset_cff2_noop); hb_test_add (test_subset_cff2); hb_test_add (test_subset_cff2_strip_hints); + hb_test_add (test_subset_cff2_desubr); + hb_test_add (test_subset_cff2_desubr_strip_hints); return hb_test_run (); }