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 a2d2150b0..000000000 Binary files a/test/api/fonts/AdobeVFPrototype.abc.nohints.otf and /dev/null differ diff --git a/test/api/fonts/AdobeVFPrototype.abc.otf b/test/api/fonts/AdobeVFPrototype.abc.otf index a2d2150b0..e21d87f7d 100644 Binary files a/test/api/fonts/AdobeVFPrototype.abc.otf and b/test/api/fonts/AdobeVFPrototype.abc.otf differ diff --git a/test/api/fonts/AdobeVFPrototype.ac.nohints.otf b/test/api/fonts/AdobeVFPrototype.ac.nohints.otf index 188a87051..c0d034ad0 100644 Binary files a/test/api/fonts/AdobeVFPrototype.ac.nohints.otf and b/test/api/fonts/AdobeVFPrototype.ac.nohints.otf differ diff --git a/test/api/fonts/AdobeVFPrototype.ac.nosubrs.nohints.otf b/test/api/fonts/AdobeVFPrototype.ac.nosubrs.nohints.otf new file mode 100644 index 000000000..85f6cf6a3 Binary files /dev/null and b/test/api/fonts/AdobeVFPrototype.ac.nosubrs.nohints.otf differ diff --git a/test/api/fonts/AdobeVFPrototype.ac.nosubrs.otf b/test/api/fonts/AdobeVFPrototype.ac.nosubrs.otf new file mode 100644 index 000000000..ad4d53b87 Binary files /dev/null and b/test/api/fonts/AdobeVFPrototype.ac.nosubrs.otf differ diff --git a/test/api/fonts/AdobeVFPrototype.ac.otf b/test/api/fonts/AdobeVFPrototype.ac.otf index b890310b8..c5080fcb3 100644 Binary files a/test/api/fonts/AdobeVFPrototype.ac.otf and b/test/api/fonts/AdobeVFPrototype.ac.otf differ 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 (); }