[subset] GSUB5/GPOS7 Contextual Subst/Pos Subtbale Subsetting support

This commit is contained in:
Qunxin Liu 2020-04-17 11:58:31 -07:00 committed by Garret Rieger
parent 00aba82a6d
commit 8b5d3ebd96
39 changed files with 176 additions and 10 deletions

View File

@ -1144,7 +1144,7 @@ static inline bool match_lookahead (hb_ot_apply_context_t *c,
struct LookupRecord
{
LookupRecord* copy (hb_serialize_context_t *c,
const hb_map_t *lookup_map)
const hb_map_t *lookup_map) const
{
TRACE_SERIALIZE (this);
auto *out = c->embed (*this);
@ -1450,6 +1450,47 @@ struct Rule
return_trace (context_apply_lookup (c, inputCount, inputZ.arrayZ, lookupCount, lookupRecord.arrayZ, lookup_context));
}
bool serialize (hb_serialize_context_t *c,
const hb_map_t *input_mapping, /* old->new glyphid or class mapping */
const hb_map_t *lookup_map) const
{
TRACE_SERIALIZE (this);
auto *out = c->start_embed (this);
if (unlikely (!c->extend_min (out))) return_trace (false);
out->inputCount = inputCount;
out->lookupCount = lookupCount;
const hb_array_t<const HBUINT16> input = inputZ.as_array (inputCount - 1);
for (const auto org : input)
{
HBUINT16 d;
d = input_mapping->get (org);
c->copy (d);
}
const UnsizedArrayOf<LookupRecord> &lookupRecord = StructAfter<UnsizedArrayOf<LookupRecord>>
(inputZ.as_array ((inputCount ? inputCount - 1 : 0)));
for (unsigned i = 0; i < (unsigned) lookupCount; i++)
c->copy (lookupRecord[i], lookup_map);
return_trace (true);
}
bool subset (hb_subset_context_t *c,
const hb_map_t *lookup_map,
const hb_map_t *klass_map = nullptr) const
{
TRACE_SUBSET (this);
const hb_array_t<const HBUINT16> input = inputZ.as_array ((inputCount ? inputCount - 1 : 0));
if (!input.length) return_trace (false);
const hb_map_t *mapping = klass_map == nullptr ? c->plan->glyph_map : klass_map;
if (!hb_all (input, mapping)) return_trace (false);
return_trace (serialize (c->serializer, mapping, lookup_map));
}
public:
bool sanitize (hb_sanitize_context_t *c) const
{
@ -1546,6 +1587,36 @@ struct RuleSet
;
}
bool subset (hb_subset_context_t *c,
const hb_map_t *lookup_map,
const hb_map_t *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>& _ : 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, lookup_map, 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);
@ -1648,8 +1719,26 @@ struct ContextFormat1
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;
const hb_map_t *lookup_map = c->table_tag == HB_OT_TAG_GSUB ? c->plan->gsub_lookups : c->plan->gpos_lookups;
hb_sorted_vector_t<hb_codepoint_t> new_coverage;
+ hb_zip (this+coverage, ruleSet)
| hb_filter (glyphset, hb_first)
| hb_filter (subset_offset_array (c, out->ruleSet, this, lookup_map), 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
@ -1774,8 +1863,45 @@ struct ContextFormat2
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;
if (unlikely (!out->coverage.serialize_subset (c, coverage, this)))
return_trace (false);
hb_map_t klass_map;
out->classDef.serialize_subset (c, classDef, this, &klass_map);
const hb_map_t *lookup_map = c->table_tag == HB_OT_TAG_GSUB ? c->plan->gsub_lookups : c->plan->gpos_lookups;
bool ret = true;
unsigned non_zero_index = 0, index = 0;
for (const hb_pair_t<unsigned, const OffsetTo<RuleSet>&>& _ : + hb_enumerate (ruleSet)
| hb_filter (klass_map, hb_first))
{
auto *o = out->ruleSet.serialize_append (c->serializer);
if (unlikely (!o))
{
ret = false;
break;
}
if (o->serialize_subset (c, _.second, this, lookup_map, &klass_map))
non_zero_index = index;
index++;
}
if (!ret) return_trace (ret);
//prune empty trailing ruleSets
--index;
while (index > non_zero_index)
{
out->ruleSet.pop ();
index--;
}
return_trace (bool (out->ruleSet));
}
bool sanitize (hb_sanitize_context_t *c) const
@ -1886,8 +2012,28 @@ struct ContextFormat3
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->glyphCount = glyphCount;
out->lookupCount = lookupCount;
const hb_array_t<const OffsetTo<Coverage>> coverages = coverageZ.as_array (glyphCount);
for (const OffsetTo<Coverage>& offset : coverages)
{
auto *o = c->serializer->allocate_size<OffsetTo<Coverage>> (OffsetTo<Coverage>::static_size);
if (unlikely (!o)) return_trace (false);
if (!o->serialize_subset (c, offset, this)) return_trace (false);
}
const LookupRecord *lookupRecord = &StructAfter<LookupRecord> (coverageZ.as_array (glyphCount));
const hb_map_t *lookup_map = c->table_tag == HB_OT_TAG_GSUB ? c->plan->gsub_lookups : c->plan->gpos_lookups;
for (unsigned i = 0; i < (unsigned) lookupCount; i++)
c->serializer->copy (lookupRecord[i], lookup_map);
return_trace (true);
}
bool sanitize (hb_sanitize_context_t *c) const

View File

@ -89,7 +89,7 @@ _subset (hb_subset_plan_t *plan)
retry:
hb_serialize_context_t serializer ((void *) buf, buf_size);
serializer.start_serialize<TableType> ();
hb_subset_context_t c (source_blob, plan, &serializer);
hb_subset_context_t c (source_blob, plan, &serializer, tag);
bool needed = table->subset (&c);
if (serializer.ran_out_of_room)
{

View File

@ -57,14 +57,17 @@ struct hb_subset_context_t :
hb_blob_t *source_blob;
hb_subset_plan_t *plan;
hb_serialize_context_t *serializer;
hb_tag_t table_tag;
unsigned int debug_depth;
hb_subset_context_t (hb_blob_t *source_blob_,
hb_subset_plan_t *plan_,
hb_serialize_context_t *serializer_) :
source_blob (source_blob_),
hb_serialize_context_t *serializer_,
hb_tag_t table_tag_) :
source_blob (source_blob_),
plan (plan_),
serializer (serializer_),
table_tag (table_tag_),
debug_depth (0) {}
};

View File

@ -21,6 +21,7 @@ EXTRA_DIST += \
expected/layout.gsub3 \
expected/layout.gsub6 \
expected/layout.gdef \
expected/layout.context \
expected/cmap \
expected/cmap14 \
expected/sbix \

View File

@ -21,6 +21,7 @@ DISABLED_TESTS = \
tests/layout.gsub3.tests \
tests/layout.gsub6.tests \
tests/layout.gdef.tests \
tests/layout.context.tests \
$(NULL)
XFAIL_TESTS = \

Binary file not shown.

View File

@ -0,0 +1,15 @@
FONTS:
gpos_context1_multiple_subrules_f1.otf
gpos_context2_multiple_subrules_f1.otf
gpos_context3_simple_f1.otf
PROFILES:
keep-layout.txt
keep-layout-retain-gids.txt
SUBSETS:
A
AB
AC
ABC
*