diff --git a/src/hb-ot-layout-common.hh b/src/hb-ot-layout-common.hh index b29d4036b..2777c659d 100644 --- a/src/hb-ot-layout-common.hh +++ b/src/hb-ot-layout-common.hh @@ -66,6 +66,23 @@ namespace OT { #define NOT_COVERED ((unsigned int) -1) + +template +static inline void Coverage_serialize (hb_serialize_context_t *c, + Iterator it); + +template +static inline void ClassDef_serialize (hb_serialize_context_t *c, + Iterator it); + +static void ClassDef_remap_and_serialize (hb_serialize_context_t *c, + const hb_set_t &glyphset, + const hb_map_t &gid_klass_map, + hb_sorted_vector_t glyphs, + hb_sorted_vector_t klasses, + hb_map_t *klass_map /*INOUT*/); + + template struct subset_offset_array_t { @@ -120,7 +137,6 @@ struct } HB_FUNCOBJ (subset_offset_array); - /* * * OpenType Layout Common Table Formats @@ -1179,6 +1195,23 @@ struct Coverage } } + bool subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto it = + + iter () + | hb_filter (glyphset) + | hb_map_retains_sorting (glyph_map) + ; + + bool ret = bool (it); + Coverage_serialize (c->serializer, it); + return_trace (ret); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -1297,15 +1330,51 @@ struct Coverage DEFINE_SIZE_UNION (2, format); }; +template +static inline void +Coverage_serialize (hb_serialize_context_t *c, + Iterator it) +{ c->start_embed ()->serialize (c, it); } + +static void ClassDef_remap_and_serialize (hb_serialize_context_t *c, + const hb_set_t &glyphset, + const hb_map_t &gid_klass_map, + hb_sorted_vector_t glyphs, + hb_sorted_vector_t klasses, + hb_map_t *klass_map /*INOUT*/) +{ + bool has_no_match = glyphset.get_population () > gid_klass_map.get_population (); + + hb_map_t m; + if (!klass_map) klass_map = &m; + + if (has_no_match) klass_map->set (0, 0); + unsigned idx = klass_map->has (0) ? 1 : 0; + for (const unsigned k: klasses.iter ()) + { + if (klass_map->has (k)) continue; + klass_map->set (k, idx); + idx++; + } + + auto it = + + glyphs.iter () + | hb_map_retains_sorting ([&] (const HBGlyphID& gid) -> hb_pair_t + { + HBUINT16 new_klass; + new_klass = klass_map->get (gid_klass_map[gid]); + return hb_pair ((hb_codepoint_t)gid, new_klass); + }) + ; + + c->propagate_error (glyphs, klasses); + ClassDef_serialize (c, it); +} /* * Class Definition Table */ -static inline void ClassDef_serialize (hb_serialize_context_t *c, - hb_array_t glyphs, - hb_array_t klasses); - struct ClassDefFormat1 { friend struct ClassDef; @@ -1316,53 +1385,53 @@ struct ClassDefFormat1 return classValue[(unsigned int) (glyph_id - startGlyph)]; } + template bool serialize (hb_serialize_context_t *c, - hb_array_t glyphs, - hb_array_t klasses) + Iterator it) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (*this))) return_trace (false); - if (unlikely (!glyphs)) + if (unlikely (!it)) { startGlyph = 0; classValue.len = 0; return_trace (true); } - hb_codepoint_t glyph_min = +glyphs | hb_reduce (hb_min, 0xFFFFu); - hb_codepoint_t glyph_max = +glyphs | hb_reduce (hb_max, 0u); - - startGlyph = glyph_min; - c->check_assign (classValue.len, glyph_max - glyph_min + 1); - if (unlikely (!c->extend (classValue))) return_trace (false); - - for (unsigned int i = 0; i < glyphs.length; i++) - classValue[glyphs[i] - glyph_min] = klasses[i]; - + startGlyph = (*it).first; + classValue.serialize (c, + it + | hb_map (hb_second)); return_trace (true); } - bool subset (hb_subset_context_t *c) const + bool subset (hb_subset_context_t *c, + hb_map_t *klass_map = nullptr /*OUT*/) const { TRACE_SUBSET (this); const hb_set_t &glyphset = *c->plan->glyphset (); const hb_map_t &glyph_map = *c->plan->glyph_map; + hb_sorted_vector_t glyphs; - hb_vector_t klasses; + hb_sorted_vector_t orig_klasses; + hb_map_t gid_org_klass_map; hb_codepoint_t start = startGlyph; hb_codepoint_t end = start + classValue.len; - for (hb_codepoint_t g = start; g < end; g++) + for (const hb_codepoint_t gid : + hb_range (start, end) + | hb_filter (glyphset)) { - if (!glyphset.has (g)) continue; - unsigned int value = classValue[g - start]; - if (!value) continue; - glyphs.push(glyph_map[g]); - klasses.push(value); + unsigned klass = classValue[gid - start]; + if (!klass) continue; + + glyphs.push (glyph_map[gid]); + gid_org_klass_map.set (glyph_map[gid], klass); + orig_klasses.push (klass); } - c->serializer->propagate_error (glyphs, klasses); - ClassDef_serialize (c->serializer, glyphs, klasses); + + ClassDef_remap_and_serialize (c->serializer, glyphset, gid_org_klass_map, + glyphs, orig_klasses, klass_map); return_trace ((bool) glyphs); } @@ -1452,70 +1521,87 @@ struct ClassDefFormat2 return rangeRecord.bsearch (glyph_id).value; } + template bool serialize (hb_serialize_context_t *c, - hb_array_t glyphs, - hb_array_t klasses) + Iterator it) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (*this))) return_trace (false); - if (unlikely (!glyphs)) + if (unlikely (!it)) { rangeRecord.len = 0; return_trace (true); } - unsigned int count = glyphs.len (); - unsigned int num_ranges = 1; - for (unsigned int i = 1; i < count; i++) - if (glyphs[i - 1] + 1 != glyphs[i] || - klasses[i - 1] != klasses[i]) - num_ranges++; - rangeRecord.len = num_ranges; - if (unlikely (!c->extend (rangeRecord))) return_trace (false); + unsigned num_ranges = 1; + hb_codepoint_t prev_gid = (*it).first; + unsigned prev_klass = (*it).second; - unsigned int range = 0; - rangeRecord[range].start = glyphs[0]; - rangeRecord[range].value = klasses[0]; - for (unsigned int i = 1; i < count; i++) + RangeRecord range_rec; + range_rec.start = prev_gid; + range_rec.end = prev_gid; + range_rec.value = prev_klass; + + RangeRecord *record = c->copy (range_rec); + + for (const auto gid_klass_pair : + (++it)) { - if (glyphs[i - 1] + 1 != glyphs[i] || - klasses[i - 1] != klasses[i]) + hb_codepoint_t cur_gid = gid_klass_pair.first; + unsigned cur_klass = gid_klass_pair.second; + + if (cur_gid != prev_gid + 1 || + cur_klass != prev_klass) { - rangeRecord[range].end = glyphs[i - 1]; - range++; - rangeRecord[range].start = glyphs[i]; - rangeRecord[range].value = klasses[i]; + record->end = prev_gid; + num_ranges++; + + range_rec.start = cur_gid; + range_rec.end = cur_gid; + range_rec.value = cur_klass; + + record = c->copy (range_rec); } + + prev_klass = cur_klass; + prev_gid = cur_gid; } - rangeRecord[range].end = glyphs[count - 1]; + + record->end = prev_gid; + rangeRecord.len = num_ranges; return_trace (true); } - bool subset (hb_subset_context_t *c) const + bool subset (hb_subset_context_t *c, + hb_map_t *klass_map = nullptr /*OUT*/) const { TRACE_SUBSET (this); const hb_set_t &glyphset = *c->plan->glyphset (); const hb_map_t &glyph_map = *c->plan->glyph_map; - hb_vector_t glyphs; - hb_vector_t klasses; - unsigned int count = rangeRecord.len; - for (unsigned int i = 0; i < count; i++) + hb_sorted_vector_t glyphs; + hb_sorted_vector_t orig_klasses; + hb_map_t gid_org_klass_map; + + unsigned count = rangeRecord.len; + for (unsigned i = 0; i < count; i++) { - unsigned int value = rangeRecord[i].value; - if (!value) continue; + unsigned klass = rangeRecord[i].value; + if (!klass) continue; hb_codepoint_t start = rangeRecord[i].start; hb_codepoint_t end = rangeRecord[i].end + 1; for (hb_codepoint_t g = start; g < end; g++) { if (!glyphset.has (g)) continue; glyphs.push (glyph_map[g]); - klasses.push (value); + gid_org_klass_map.set (glyph_map[g], klass); + orig_klasses.push (klass); } } - c->serializer->propagate_error (glyphs, klasses); - ClassDef_serialize (c->serializer, glyphs, klasses); + + ClassDef_remap_and_serialize (c->serializer, glyphset, gid_org_klass_map, + glyphs, orig_klasses, klass_map); return_trace ((bool) glyphs); } @@ -1612,25 +1698,36 @@ struct ClassDef } } - bool serialize (hb_serialize_context_t *c, - hb_array_t glyphs, - hb_array_t klasses) + template + bool serialize (hb_serialize_context_t *c, Iterator it) { TRACE_SERIALIZE (this); if (unlikely (!c->extend_min (*this))) return_trace (false); - unsigned int format = 2; - if (likely (glyphs)) + unsigned format = 2; + if (likely (it)) { - hb_codepoint_t glyph_min = +glyphs | hb_reduce (hb_min, 0xFFFFu); - hb_codepoint_t glyph_max = +glyphs | hb_reduce (hb_max, 0u); + hb_codepoint_t glyph_min = (*it).first; + hb_codepoint_t glyph_max = + it + | hb_map (hb_first) + | hb_reduce (hb_max, 0u); - unsigned int count = glyphs.len (); - unsigned int num_ranges = 1; - for (unsigned int i = 1; i < count; i++) - if (glyphs[i - 1] + 1 != glyphs[i] || - klasses[i - 1] != klasses[i]) - num_ranges++; + unsigned num_ranges = 1; + hb_codepoint_t prev_gid = glyph_min; + unsigned prev_klass = (*it).second; + + for (const auto gid_klass_pair : it) + { + hb_codepoint_t cur_gid = gid_klass_pair.first; + unsigned cur_klass = gid_klass_pair.second; + if (cur_gid != prev_gid + 1 || + cur_klass != prev_klass) + num_ranges++; + + prev_gid = cur_gid; + prev_klass = cur_klass; + } if (1 + (glyph_max - glyph_min + 1) < num_ranges * 3) format = 1; @@ -1639,18 +1736,19 @@ struct ClassDef switch (u.format) { - case 1: return_trace (u.format1.serialize (c, glyphs, klasses)); - case 2: return_trace (u.format2.serialize (c, glyphs, klasses)); + case 1: return_trace (u.format1.serialize (c, it)); + case 2: return_trace (u.format2.serialize (c, it)); default:return_trace (false); } } - bool subset (hb_subset_context_t *c) const + bool subset (hb_subset_context_t *c, + hb_map_t *klass_map = nullptr /*OUT*/) const { TRACE_SUBSET (this); switch (u.format) { - case 1: return_trace (u.format1.subset (c)); - case 2: return_trace (u.format2.subset (c)); + case 1: return_trace (u.format1.subset (c, klass_map)); + case 2: return_trace (u.format2.subset (c, klass_map)); default:return_trace (false); } } @@ -1717,10 +1815,10 @@ struct ClassDef DEFINE_SIZE_UNION (2, format); }; +template static inline void ClassDef_serialize (hb_serialize_context_t *c, - hb_array_t glyphs, - hb_array_t klasses) -{ c->start_embed ()->serialize (c, glyphs, klasses); } + Iterator it) +{ c->start_embed ()->serialize (c, it); } /* diff --git a/src/hb-ot-layout-gsubgpos.hh b/src/hb-ot-layout-gsubgpos.hh index c6e6ac9cb..1380e5531 100644 --- a/src/hb-ot-layout-gsubgpos.hh +++ b/src/hb-ot-layout-gsubgpos.hh @@ -2002,6 +2002,80 @@ struct ChainRule lookup.arrayZ, lookup_context)); } + template + void serialize_array (hb_serialize_context_t *c, + HBUINT16 len, + Iterator it) const + { + c->copy (len); + for (const auto g : it) + { + HBUINT16 gid; + gid = g; + c->copy (gid); + } + } + + ChainRule* copy (hb_serialize_context_t *c, + const hb_map_t *backtrack_map, + const hb_map_t *input_map = nullptr, + const hb_map_t *lookahead_map = nullptr) const + { + TRACE_SERIALIZE (this); + auto *out = c->start_embed (this); + if (unlikely (!out)) return_trace (nullptr); + + const hb_map_t *mapping = backtrack_map; + serialize_array (c, backtrack.len, + backtrack.iter () + | hb_map (mapping)); + + const HeadlessArrayOf &input = StructAfter> (backtrack); + if (input_map) mapping = input_map; + serialize_array (c, input.lenP1, + input.iter () + | hb_map (mapping)); + + const ArrayOf &lookahead = StructAfter> (input); + if (lookahead_map) mapping = lookahead_map; + serialize_array (c, lookahead.len, + lookahead.iter () + | hb_map (mapping)); + + const ArrayOf &lookup = StructAfter> (lookahead); + c->copy (lookup); + + return_trace (out); + } + + bool subset (hb_subset_context_t *c, + const hb_map_t *backtrack_map = nullptr, + const hb_map_t *input_map = nullptr, + const hb_map_t *lookahead_map = nullptr) const + { + TRACE_SUBSET (this); + + if (!backtrack_map) + { + const hb_set_t &glyphset = *c->plan->glyphset (); + if (!hb_all (backtrack, glyphset) || + !hb_all (inputX, glyphset) || + !hb_all (lookaheadX, glyphset)) + return_trace (false); + + copy (c->serializer, c->plan->glyph_map); + } + else + { + if (!hb_all (backtrack, backtrack_map) || + !hb_all (inputX, input_map) || + !hb_all (lookaheadX, lookahead_map)) + return_trace (false); + + copy (c->serializer, backtrack_map, input_map, lookahead_map); + } + + return_trace (true); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -2083,6 +2157,40 @@ struct ChainRuleSet ; } + bool subset (hb_subset_context_t *c, + const hb_map_t *backtrack_klass_map = nullptr, + const hb_map_t *input_klass_map = nullptr, + const hb_map_t *lookahead_klass_map = nullptr) const + { + TRACE_SUBSET (this); + + auto snap = c->serializer->snapshot (); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + + for (const OffsetTo& _ : rule) + { + if (!_) continue; + auto *o = out->rule.serialize_append (c->serializer); + if (unlikely (!o)) continue; + + auto o_snap = c->serializer->snapshot (); + if (!o->serialize_subset (c, _, this, out, + backtrack_klass_map, + input_klass_map, + lookahead_klass_map)) + { + out->rule.pop (); + c->serializer->revert (o_snap); + } + } + + bool ret = bool (out->rule); + if (!ret) c->serializer->revert (snap); + + return_trace (ret); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -2175,8 +2283,25 @@ struct ChainContextFormat1 bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - // TODO(subset) - return_trace (false); + const hb_set_t &glyphset = *c->plan->glyphset (); + const hb_map_t &glyph_map = *c->plan->glyph_map; + + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + + hb_sorted_vector_t new_coverage; + + hb_zip (this+coverage, ruleSet) + | hb_filter (glyphset, hb_first) + | hb_filter (subset_offset_array (c, out->ruleSet, this, out), hb_second) + | hb_map (hb_first) + | hb_map (glyph_map) + | hb_sink (new_coverage) + ; + + out->coverage.serialize (c->serializer, out) + .serialize (c->serializer, new_coverage.iter ()); + return_trace (bool (new_coverage)); } bool sanitize (hb_sanitize_context_t *c) const @@ -2314,8 +2439,54 @@ struct ChainContextFormat2 bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - // TODO(subset) - return_trace (false); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!c->serializer->extend_min (out))) return_trace (false); + out->format = format; + out->coverage.serialize_subset (c, coverage, this, out); + + hb_map_t backtrack_klass_map; + out->backtrackClassDef.serialize_subset (c, backtrackClassDef, this, out, &backtrack_klass_map); + + // subset inputClassDef based on glyphs survived in Coverage subsetting + hb_map_t input_klass_map; + out->inputClassDef.serialize_subset (c, inputClassDef, this, out, &input_klass_map); + + hb_map_t lookahead_klass_map; + out->lookaheadClassDef.serialize_subset (c, lookaheadClassDef, this, out, &lookahead_klass_map); + + hb_vector_t rulesets; + bool ret = true; + for (const OffsetTo& _ : + hb_enumerate (ruleSet) + | hb_filter (input_klass_map, hb_first) + | hb_map (hb_second)) + { + auto *o = out->ruleSet.serialize_append (c->serializer); + if (unlikely (!o)) + { + ret = false; + break; + } + if (!o->serialize_subset (c, _, this, out, + &backtrack_klass_map, + &input_klass_map, + &lookahead_klass_map)) + { + rulesets.push (0); + } + else rulesets.push (1); + } + + if (!ret) return_trace (ret); + + //prune empty trailing ruleSets + unsigned count = rulesets.length; + while (count > 0 && rulesets[count-1] == 0) + { + out->ruleSet.pop (); + count--; + } + + return_trace (bool (out->ruleSet)); } bool sanitize (hb_sanitize_context_t *c) const @@ -2457,11 +2628,46 @@ struct ChainContextFormat3 lookup.len, lookup.arrayZ, lookup_context)); } + template + bool serialize_coverage_offsets (hb_subset_context_t *c, + Iterator it, + const void* src_base, + const void* dst_base) const + { + TRACE_SERIALIZE (this); + auto *out = c->serializer->start_embed> (); + + if (unlikely (!c->serializer->allocate_size (HBUINT16::static_size))) return_trace (false); + + + it + | hb_apply (subset_offset_array (c, *out, src_base, dst_base)) + ; + + return_trace (out->len); + } + bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - // TODO(subset) - return_trace (false); + + auto *out = c->serializer->start_embed (this); + if (unlikely (!out)) return_trace (false); + if (unlikely (!c->serializer->embed (this->format))) return_trace (false); + + if (!serialize_coverage_offsets (c, backtrack.iter (), this, out)) + return_trace (false); + + const OffsetArrayOf &input = StructAfter> (backtrack); + if (!serialize_coverage_offsets (c, input.iter (), this, out)) + return_trace (false); + + const OffsetArrayOf &lookahead = StructAfter> (input); + if (!serialize_coverage_offsets (c, lookahead.iter (), this, out)) + return_trace (false); + + const ArrayOf &lookup = StructAfter> (lookahead); + return_trace (c->serializer->copy (lookup)); } bool sanitize (hb_sanitize_context_t *c) const diff --git a/test/subset/data/Makefile.am b/test/subset/data/Makefile.am index 0b2745206..b50029f5e 100644 --- a/test/subset/data/Makefile.am +++ b/test/subset/data/Makefile.am @@ -15,6 +15,7 @@ EXTRA_DIST += \ expected/layout \ expected/layout.gpos \ expected/layout.gpos3 \ + expected/layout.gsub6 \ expected/cmap14 \ fonts \ profiles \ diff --git a/test/subset/data/Makefile.sources b/test/subset/data/Makefile.sources index c6a3042c9..ccc0cdce5 100644 --- a/test/subset/data/Makefile.sources +++ b/test/subset/data/Makefile.sources @@ -7,6 +7,7 @@ TESTS = \ tests/layout.tests \ tests/layout.gpos.tests \ tests/layout.gpos3.tests \ + tests/layout.gsub6.tests \ tests/cmap14.tests \ $(NULL) diff --git a/test/subset/data/expected/layout.gsub6/gsub_chaining1_multiple_subrules_f1.keep-layout-retain-gids.30,31,32,33.otf b/test/subset/data/expected/layout.gsub6/gsub_chaining1_multiple_subrules_f1.keep-layout-retain-gids.30,31,32,33.otf new file mode 100644 index 000000000..e10d863c0 Binary files /dev/null and b/test/subset/data/expected/layout.gsub6/gsub_chaining1_multiple_subrules_f1.keep-layout-retain-gids.30,31,32,33.otf differ diff --git a/test/subset/data/expected/layout.gsub6/gsub_chaining1_multiple_subrules_f1.keep-layout-retain-gids.retain-all-codepoint.otf b/test/subset/data/expected/layout.gsub6/gsub_chaining1_multiple_subrules_f1.keep-layout-retain-gids.retain-all-codepoint.otf new file mode 100644 index 000000000..1f9075403 Binary files /dev/null and b/test/subset/data/expected/layout.gsub6/gsub_chaining1_multiple_subrules_f1.keep-layout-retain-gids.retain-all-codepoint.otf differ diff --git a/test/subset/data/expected/layout.gsub6/gsub_chaining1_multiple_subrules_f1.keep-layout.30,31,32,33.otf b/test/subset/data/expected/layout.gsub6/gsub_chaining1_multiple_subrules_f1.keep-layout.30,31,32,33.otf new file mode 100644 index 000000000..bdaa8059f Binary files /dev/null and b/test/subset/data/expected/layout.gsub6/gsub_chaining1_multiple_subrules_f1.keep-layout.30,31,32,33.otf differ diff --git a/test/subset/data/expected/layout.gsub6/gsub_chaining1_multiple_subrules_f1.keep-layout.retain-all-codepoint.otf b/test/subset/data/expected/layout.gsub6/gsub_chaining1_multiple_subrules_f1.keep-layout.retain-all-codepoint.otf new file mode 100644 index 000000000..1f9075403 Binary files /dev/null and b/test/subset/data/expected/layout.gsub6/gsub_chaining1_multiple_subrules_f1.keep-layout.retain-all-codepoint.otf differ diff --git a/test/subset/data/expected/layout.gsub6/gsub_chaining2_multiple_subrules_f1.keep-layout-retain-gids.30,31,32,33.otf b/test/subset/data/expected/layout.gsub6/gsub_chaining2_multiple_subrules_f1.keep-layout-retain-gids.30,31,32,33.otf new file mode 100644 index 000000000..856249e72 Binary files /dev/null and b/test/subset/data/expected/layout.gsub6/gsub_chaining2_multiple_subrules_f1.keep-layout-retain-gids.30,31,32,33.otf differ diff --git a/test/subset/data/expected/layout.gsub6/gsub_chaining2_multiple_subrules_f1.keep-layout-retain-gids.retain-all-codepoint.otf b/test/subset/data/expected/layout.gsub6/gsub_chaining2_multiple_subrules_f1.keep-layout-retain-gids.retain-all-codepoint.otf new file mode 100644 index 000000000..e76439392 Binary files /dev/null and b/test/subset/data/expected/layout.gsub6/gsub_chaining2_multiple_subrules_f1.keep-layout-retain-gids.retain-all-codepoint.otf differ diff --git a/test/subset/data/expected/layout.gsub6/gsub_chaining2_multiple_subrules_f1.keep-layout.30,31,32,33.otf b/test/subset/data/expected/layout.gsub6/gsub_chaining2_multiple_subrules_f1.keep-layout.30,31,32,33.otf new file mode 100644 index 000000000..a53b114d6 Binary files /dev/null and b/test/subset/data/expected/layout.gsub6/gsub_chaining2_multiple_subrules_f1.keep-layout.30,31,32,33.otf differ diff --git a/test/subset/data/expected/layout.gsub6/gsub_chaining2_multiple_subrules_f1.keep-layout.retain-all-codepoint.otf b/test/subset/data/expected/layout.gsub6/gsub_chaining2_multiple_subrules_f1.keep-layout.retain-all-codepoint.otf new file mode 100644 index 000000000..e76439392 Binary files /dev/null and b/test/subset/data/expected/layout.gsub6/gsub_chaining2_multiple_subrules_f1.keep-layout.retain-all-codepoint.otf differ diff --git a/test/subset/data/expected/layout.gsub6/gsub_chaining3_simple_f2.keep-layout-retain-gids.30,31,32,33.otf b/test/subset/data/expected/layout.gsub6/gsub_chaining3_simple_f2.keep-layout-retain-gids.30,31,32,33.otf new file mode 100644 index 000000000..2d08eb0b5 Binary files /dev/null and b/test/subset/data/expected/layout.gsub6/gsub_chaining3_simple_f2.keep-layout-retain-gids.30,31,32,33.otf differ diff --git a/test/subset/data/expected/layout.gsub6/gsub_chaining3_simple_f2.keep-layout-retain-gids.retain-all-codepoint.otf b/test/subset/data/expected/layout.gsub6/gsub_chaining3_simple_f2.keep-layout-retain-gids.retain-all-codepoint.otf new file mode 100644 index 000000000..737f85a46 Binary files /dev/null and b/test/subset/data/expected/layout.gsub6/gsub_chaining3_simple_f2.keep-layout-retain-gids.retain-all-codepoint.otf differ diff --git a/test/subset/data/expected/layout.gsub6/gsub_chaining3_simple_f2.keep-layout.30,31,32,33.otf b/test/subset/data/expected/layout.gsub6/gsub_chaining3_simple_f2.keep-layout.30,31,32,33.otf new file mode 100644 index 000000000..fbd9a4461 Binary files /dev/null and b/test/subset/data/expected/layout.gsub6/gsub_chaining3_simple_f2.keep-layout.30,31,32,33.otf differ diff --git a/test/subset/data/expected/layout.gsub6/gsub_chaining3_simple_f2.keep-layout.retain-all-codepoint.otf b/test/subset/data/expected/layout.gsub6/gsub_chaining3_simple_f2.keep-layout.retain-all-codepoint.otf new file mode 100644 index 000000000..737f85a46 Binary files /dev/null and b/test/subset/data/expected/layout.gsub6/gsub_chaining3_simple_f2.keep-layout.retain-all-codepoint.otf differ diff --git a/test/subset/data/fonts/gsub_chaining1_multiple_subrules_f1.otf b/test/subset/data/fonts/gsub_chaining1_multiple_subrules_f1.otf new file mode 100644 index 000000000..74b994558 Binary files /dev/null and b/test/subset/data/fonts/gsub_chaining1_multiple_subrules_f1.otf differ diff --git a/test/subset/data/fonts/gsub_chaining2_multiple_subrules_f1.otf b/test/subset/data/fonts/gsub_chaining2_multiple_subrules_f1.otf new file mode 100644 index 000000000..a3a1846e6 Binary files /dev/null and b/test/subset/data/fonts/gsub_chaining2_multiple_subrules_f1.otf differ diff --git a/test/subset/data/fonts/gsub_chaining3_simple_f2.otf b/test/subset/data/fonts/gsub_chaining3_simple_f2.otf new file mode 100644 index 000000000..bc9ed9a79 Binary files /dev/null and b/test/subset/data/fonts/gsub_chaining3_simple_f2.otf differ diff --git a/test/subset/data/tests/layout.gsub6.tests b/test/subset/data/tests/layout.gsub6.tests new file mode 100644 index 000000000..47399b804 --- /dev/null +++ b/test/subset/data/tests/layout.gsub6.tests @@ -0,0 +1,12 @@ +FONTS: +gsub_chaining1_multiple_subrules_f1.otf +gsub_chaining2_multiple_subrules_f1.otf +gsub_chaining3_simple_f2.otf + +PROFILES: +keep-layout.txt +keep-layout-retain-gids.txt + +SUBSETS: +0123 +*