[subset] subset lookup/feature/script lists for GSUB/GPOS

This commit is contained in:
Qunxin Liu 2019-11-01 10:21:36 -07:00 committed by Garret Rieger
parent 93376a6436
commit e565d1f9bc
22 changed files with 381 additions and 79 deletions

View File

@ -82,6 +82,44 @@ static void ClassDef_remap_and_serialize (hb_serialize_context_t *c,
hb_sorted_vector_t<unsigned> klasses,
hb_map_t *klass_map /*INOUT*/);
struct hb_subset_layout_context_t :
hb_dispatch_context_t<hb_subset_layout_context_t, hb_empty_t, HB_DEBUG_SUBSET>
{
const char *get_name () { return "SUBSET_LAYOUT"; }
static return_t default_return_value () { return hb_empty_t (); }
bool visitScript ()
{
return script_count++ < HB_MAX_SCRIPTS;
}
bool visitLangSys ()
{
return langsys_count++ < HB_MAX_LANGSYS;
}
hb_subset_context_t *subset_context;
const hb_tag_t table_tag;
const hb_map_t *lookup_index_map;
const hb_map_t *feature_index_map;
unsigned debug_depth;
hb_subset_layout_context_t (hb_subset_context_t *c_,
hb_tag_t tag_,
hb_map_t *lookup_map_,
hb_map_t *feature_map_) :
subset_context (c_),
table_tag (tag_),
lookup_index_map (lookup_map_),
feature_index_map (feature_map_),
debug_depth (0),
script_count (0),
langsys_count (0) {}
private:
unsigned script_count;
unsigned langsys_count;
};
template<typename OutputArray>
struct subset_offset_array_t
@ -137,6 +175,53 @@ struct
}
HB_FUNCOBJ (subset_offset_array);
template<typename OutputArray>
struct subset_record_array_t
{
subset_record_array_t
(hb_subset_layout_context_t *c,
OutputArray* out,
const void *src_base,
const void *dest_base)
: _subset_layout_context(c), _out (out), _src_base (src_base), _dest_base (dest_base) {}
template <typename T>
void
operator ()
(T&& record)
{
auto snap = _subset_layout_context->subset_context->serializer->snapshot ();
bool ret = record.subset (_subset_layout_context, _src_base, _dest_base);
if (!ret) _subset_layout_context->subset_context->serializer->revert (snap);
else _out->len++;
}
private:
hb_subset_layout_context_t *_subset_layout_context;
OutputArray *_out;
const void *_src_base;
const void *_dest_base;
};
/*
* Helper to subset a RecordList/record array. Subsets each Record in the array and
* discards the record if the subset operation returns false.
*/
struct
{
template<typename OutputArray>
subset_record_array_t<OutputArray>
operator ()
(hb_subset_layout_context_t *c,
OutputArray* out,
const void *src_base,
const void *dest_base) const
{
return subset_record_array_t<OutputArray> (c, out, src_base, dest_base);
}
}
HB_FUNCOBJ (subset_record_array);
/*
*
* OpenType Layout Common Table Formats
@ -153,31 +238,22 @@ struct Record_sanitize_closure_t {
const void *list_base;
};
struct RecordList_subset_context_t {
RecordList_subset_context_t() : script_count (0), langsys_count (0)
{}
bool visitScript ()
{
return script_count++ < HB_MAX_SCRIPTS;
}
bool visitLangSys ()
{
return langsys_count++ < HB_MAX_LANGSYS;
}
private:
unsigned int script_count;
unsigned int langsys_count;
};
template <typename Type>
struct Record
{
int cmp (hb_tag_t a) const { return tag.cmp (a); }
bool subset (hb_subset_layout_context_t *c,
const void *src_base,
const void *dst_base) const
{
TRACE_SUBSET (this);
auto *out = c->subset_context->serializer->embed (this);
if (unlikely (!out)) return_trace (false);
bool ret = out->offset.serialize_subset (c->subset_context, offset, src_base, dst_base, c, &tag);
return_trace (ret);
}
bool sanitize (hb_sanitize_context_t *c, const void *base) const
{
TRACE_SANITIZE (this);
@ -226,29 +302,16 @@ struct RecordListOf : RecordArrayOf<Type>
const Type& operator [] (unsigned int i) const
{ return this+this->get_offset (i); }
bool subset (hb_subset_context_t *c) const
bool subset (hb_subset_context_t *c,
hb_subset_layout_context_t *l) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (*this);
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
RecordList_subset_context_t record_list_context;
unsigned int count = this->len;
for (unsigned int i = 0; i < count; i++)
{
auto *record = out->serialize_append (c->serializer);
if (unlikely (!record)) return false;
auto snap = c->serializer->snapshot ();
if (record->offset.serialize_subset (c, this->get_offset (i), this, out, &record_list_context))
{
record->tag = this->get_tag(i);
continue;
}
out->pop ();
c->serializer->revert (snap);
}
+ this->iter ()
| hb_apply (subset_record_array (l, out, this, out))
;
return_trace (true);
}
@ -259,6 +322,26 @@ struct RecordListOf : RecordArrayOf<Type>
}
};
struct Feature;
struct RecordListOfFeature : RecordListOf<Feature>
{
bool subset (hb_subset_context_t *c,
hb_subset_layout_context_t *l) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (*this);
if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
unsigned count = this->len;
+ hb_zip (*this, hb_range (count))
| hb_filter (l->feature_index_map, hb_second)
| hb_map (hb_first)
| hb_apply (subset_record_array (l, out, this, out))
;
return_trace (true);
}
};
struct RangeRecord
{
@ -292,6 +375,23 @@ struct IndexArray : ArrayOf<Index>
bool intersects (const hb_map_t *indexes) const
{ return hb_any (*this, indexes); }
template <typename Iterator,
hb_requires (hb_is_iterator (Iterator))>
void serialize (hb_serialize_context_t *c,
Iterator it)
{
if (!it.len ()) return;
if (unlikely (!c->extend_min ((*this)))) return;
this->len = it.len ();
for (const auto _ : it)
{
Index i;
i = _;
c->copy (i);
}
}
unsigned int get_indexes (unsigned int start_offset,
unsigned int *_count /* IN/OUT */,
unsigned int *_indexes /* OUT */) const
@ -312,10 +412,6 @@ struct IndexArray : ArrayOf<Index>
};
struct Script;
struct LangSys;
struct Feature;
struct LangSys
{
unsigned int get_feature_count () const
@ -343,6 +439,39 @@ struct LangSys
return_trace (c->embed (*this));
}
bool operator == (const LangSys& o) const
{
if (featureIndex.len != o.featureIndex.len ||
reqFeatureIndex != o.reqFeatureIndex)
return false;
for (const auto _ : + hb_zip (featureIndex, o.featureIndex))
if (_.first != _.second) return false;
return true;
}
bool subset (hb_subset_context_t *c,
hb_subset_layout_context_t *l,
const Tag *tag = nullptr) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (*this);
if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
out->reqFeatureIndex = l->feature_index_map->has (reqFeatureIndex) ? l->feature_index_map->get (reqFeatureIndex) : 0xFFFFu;
auto it =
+ hb_iter (featureIndex)
| hb_filter (l->feature_index_map)
| hb_map (l->feature_index_map)
;
bool ret = bool (it);
out->featureIndex.serialize (c->serializer, it);
return_trace (ret);
}
bool sanitize (hb_sanitize_context_t *c,
const Record_sanitize_closure_t * = nullptr) const
{
@ -382,34 +511,46 @@ struct Script
bool has_default_lang_sys () const { return defaultLangSys != 0; }
const LangSys& get_default_lang_sys () const { return this+defaultLangSys; }
bool subset (hb_subset_context_t *c, RecordList_subset_context_t *record_list_context) const
bool subset (hb_subset_context_t *c,
hb_subset_layout_context_t *l,
const Tag *tag) const
{
TRACE_SUBSET (this);
if (!record_list_context->visitScript ()) return_trace (false);
if (!l->visitScript ()) return_trace (false);
auto *out = c->serializer->start_embed (*this);
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
out->defaultLangSys.serialize_copy (c->serializer, defaultLangSys, this, out);
for (const auto &src: langSys)
bool defaultLang = false;
if (has_default_lang_sys ())
{
if (!record_list_context->visitLangSys ()) {
continue;
}
auto snap = c->serializer->snapshot ();
auto *lang_sys = c->serializer->embed (src);
if (likely(lang_sys)
&& lang_sys->offset.serialize_copy (c->serializer, src.offset, this, out))
c->serializer->push ();
const LangSys& ls = this+defaultLangSys;
bool ret = ls.subset (c, l);
if (!ret && tag && *tag != HB_TAG ('D', 'F', 'L', 'T'))
{
out->langSys.len++;
continue;
c->serializer->pop_discard ();
out->defaultLangSys = 0;
}
else
{
c->serializer->add_link (out->defaultLangSys, c->serializer->pop_pack (), out);
defaultLang = true;
}
c->serializer->revert (snap);
}
return_trace (true);
+ langSys.iter ()
| hb_filter ([=] (const Record<LangSys>& record) {return l->visitLangSys (); })
| hb_filter ([&] (const Record<LangSys>& record)
{
const LangSys& d = this+defaultLangSys;
const LangSys& l = this+record.offset;
return !(l == d);
})
| hb_apply (subset_record_array (l, &(out->langSys), this, out))
;
return_trace (bool (out->langSys.len) || defaultLang || l->table_tag == HB_OT_TAG_GSUB);
}
bool sanitize (hb_sanitize_context_t *c,
@ -688,13 +829,24 @@ struct Feature
bool intersects_lookup_indexes (const hb_map_t *lookup_indexes) const
{ return lookupIndex.intersects (lookup_indexes); }
bool subset (hb_subset_context_t *c, RecordList_subset_context_t *r) const
bool subset (hb_subset_context_t *c,
hb_subset_layout_context_t *l,
const Tag *tag = nullptr) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->embed (*this);
if (unlikely (!out)) return_trace (false);
auto *out = c->serializer->start_embed (*this);
if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
out->featureParams = 0; /* TODO(subset) FeatureParams. */
return_trace (true);
auto it =
+ hb_iter (lookupIndex)
| hb_filter (l->lookup_index_map)
| hb_map (l->lookup_index_map)
;
out->lookupIndex.serialize (c->serializer, it);
return_trace (bool (it) || (tag && *tag == HB_TAG ('p', 'r', 'e', 'f')));
}
bool sanitize (hb_sanitize_context_t *c,
@ -918,6 +1070,32 @@ struct Lookup
typedef OffsetListOf<Lookup> LookupList;
template <typename TLookup>
struct LookupOffsetList : OffsetListOf<TLookup>
{
bool subset (hb_subset_context_t *c,
hb_subset_layout_context_t *l) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (this);
if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
unsigned count = this->len;
+ hb_zip (*this, hb_range (count))
| hb_filter (l->lookup_index_map, hb_second)
| hb_map (hb_first)
| hb_apply (subset_offset_array (c, *out, this, out))
;
return_trace (true);
}
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (OffsetListOf<TLookup>::sanitize (c, this));
}
};
/*
* Coverage Table
@ -2227,6 +2405,14 @@ struct ConditionFormat1
{
friend struct Condition;
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->embed (this);
if (unlikely (!out)) return_trace (false);
return_trace (true);
}
private:
bool evaluate (const int *coords, unsigned int coord_len) const
{
@ -2259,6 +2445,17 @@ struct Condition
}
}
template <typename context_t, typename ...Ts>
typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
{
TRACE_DISPATCH (this, u.format);
if (unlikely (!c->may_dispatch (this, &u.format))) return_trace (c->no_dispatch_return_value ());
switch (u.format) {
case 1: return_trace (c->dispatch (u.format1, hb_forward<Ts> (ds)...));
default:return_trace (c->default_return_value ());
}
}
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
@ -2289,6 +2486,18 @@ struct ConditionSet
return true;
}
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (this);
if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
+ conditions.iter ()
| hb_apply (subset_offset_array (c, out->conditions, this, out))
;
return_trace (true);
}
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
@ -2318,6 +2527,19 @@ struct FeatureTableSubstitutionRecord
feature_indexes->add (featureIndex);
}
bool subset (hb_subset_layout_context_t *c,
const void *src_base,
const void *dst_base) const
{
TRACE_SUBSET (this);
auto *out = c->subset_context->serializer->embed (this);
if (unlikely (!out)) return_trace (false);
out->featureIndex = c->feature_index_map->get (featureIndex);
bool ret = out->feature.serialize_subset (c->subset_context, feature, src_base, dst_base, c);
return_trace (ret);
}
bool sanitize (hb_sanitize_context_t *c, const void *base) const
{
TRACE_SANITIZE (this);
@ -2364,6 +2586,22 @@ struct FeatureTableSubstitution
record.closure_features (this, lookup_indexes, feature_indexes);
}
bool subset (hb_subset_context_t *c,
hb_subset_layout_context_t *l) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (*this);
if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
out->version.major = version.major;
out->version.minor = version.minor;
+ substitutions.iter ()
| hb_apply (subset_record_array (l, &(out->substitutions), this, out))
;
return_trace (bool (out->substitutions));
}
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
@ -2398,6 +2636,20 @@ struct FeatureVariationRecord
(base+substitutions).closure_features (lookup_indexes, feature_indexes);
}
bool subset (hb_subset_layout_context_t *c,
const void *src_base,
const void *dst_base) const
{
TRACE_SUBSET (this);
auto *out = c->subset_context->serializer->embed (this);
if (unlikely (!out)) return_trace (false);
out->conditions.serialize_subset (c->subset_context, conditions, src_base, dst_base);
bool ret = out->substitutions.serialize_subset (c->subset_context, substitutions, src_base, dst_base, c);
return_trace (ret);
}
bool sanitize (hb_sanitize_context_t *c, const void *base) const
{
TRACE_SANITIZE (this);
@ -2462,6 +2714,22 @@ struct FeatureVariations
record.closure_features (this, lookup_indexes, feature_indexes);
}
bool subset (hb_subset_context_t *c,
hb_subset_layout_context_t *l) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (*this);
if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
out->version.major = version.major;
out->version.minor = version.minor;
+ varRecords.iter ()
| hb_apply (subset_record_array (l, &(out->varRecords), this, out))
;
return_trace (bool (out->varRecords));
}
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);

View File

@ -1960,7 +1960,10 @@ struct GPOS : GSUBGPOS
static inline void position_finish_offsets (hb_font_t *font, hb_buffer_t *buffer);
bool subset (hb_subset_context_t *c) const
{ return GSUBGPOS::subset<PosLookup> (c); }
{
hb_subset_layout_context_t l (c, tableTag, c->plan->gpos_lookups, c->plan->gpos_features);
return GSUBGPOS::subset<PosLookup> (&l);
}
bool sanitize (hb_sanitize_context_t *c) const
{ return GSUBGPOS::sanitize<PosLookup> (c); }

View File

@ -1533,7 +1533,10 @@ struct GSUB : GSUBGPOS
{ return static_cast<const SubstLookup &> (GSUBGPOS::get_lookup (i)); }
bool subset (hb_subset_context_t *c) const
{ return GSUBGPOS::subset<SubstLookup> (c); }
{
hb_subset_layout_context_t l (c, tableTag, c->plan->gsub_lookups, c->plan->gsub_features);
return GSUBGPOS::subset<SubstLookup> (&l);
}
bool sanitize (hb_sanitize_context_t *c) const
{ return GSUBGPOS::sanitize<SubstLookup> (c); }

View File

@ -3058,26 +3058,43 @@ struct GSUBGPOS
}
template <typename TLookup>
bool subset (hb_subset_context_t *c) const
bool subset (hb_subset_layout_context_t *c) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->embed (*this);
auto *out = c->subset_context->serializer->embed (*this);
if (unlikely (!out)) return_trace (false);
out->scriptList.serialize_subset (c, scriptList, this, out);
out->featureList.serialize_subset (c, featureList, this, out);
typedef OffsetListOf<TLookup> TLookupList;
/* TODO Use intersects() to count how many subtables survive? */
typedef LookupOffsetList<TLookup> TLookupList;
reinterpret_cast<OffsetTo<TLookupList> &> (out->lookupList)
.serialize_subset (c,
reinterpret_cast<const OffsetTo<TLookupList> &> (lookupList),
this,
out);
.serialize_subset (c->subset_context,
reinterpret_cast<const OffsetTo<TLookupList> &> (lookupList),
this,
out,
c);
reinterpret_cast<OffsetTo<RecordListOfFeature> &> (out->featureList)
.serialize_subset (c->subset_context,
reinterpret_cast<const OffsetTo<RecordListOfFeature> &> (featureList),
this,
out,
c);
out->scriptList.serialize_subset (c->subset_context,
scriptList,
this,
out,
c);
#ifndef HB_NO_VAR
if (version.to_int () >= 0x00010001u)
out->featureVars.serialize_copy (c->serializer, featureVars, this, out);
{
bool ret = out->featureVars.serialize_subset (c->subset_context, featureVars, this, out, c);
if (!ret)
{
out->version.major = 1;
out->version.major = 0;
}
}
#endif
return_trace (true);

View File

@ -77,7 +77,14 @@ test_face (hb_face_t *face,
hb_set_t *lookup_indexes = hb_set_create ();
hb_set_add (lookup_indexes, 0);
hb_ot_layout_closure_lookups (face, HB_OT_TAG_GSUB, set, lookup_indexes);
hb_map_t *lookup_mapping = hb_map_create ();
hb_map_set (lookup_mapping, 0, 0);
hb_set_t *feature_indices = hb_set_create ();
hb_ot_layout_closure_features (face, HB_OT_TAG_GSUB, lookup_mapping, feature_indices);
hb_set_destroy (lookup_indexes);
hb_set_destroy (feature_indices);
hb_map_destroy (lookup_mapping);
hb_ot_layout_get_baseline (font, HB_OT_LAYOUT_BASELINE_TAG_HANGING, HB_DIRECTION_RTL, HB_SCRIPT_HANGUL, HB_TAG_NONE, NULL);

View File

@ -8,4 +8,5 @@ SUBSETS:
AC
CF
AF
BD
*

View File

@ -9,4 +9,5 @@ keep-layout-retain-gids.txt
SUBSETS:
!#
!#%
ABC
*

View File

@ -9,4 +9,5 @@ SUBSETS:
()
(+
)+
ABC
*

View File

@ -9,4 +9,5 @@ keep-layout-retain-gids.txt
SUBSETS:
0123
ABC
*