Handle non-exclusive features when mapping OpenType tags to AAT feature type/selector pairs.

Fixes #2290.
This commit is contained in:
Jonathan Kew 2020-03-30 23:20:30 +01:00 committed by Ebrahim Byagowi
parent 0d5695983e
commit 573b6bf826
4 changed files with 27 additions and 9 deletions

View File

@ -129,6 +129,8 @@ struct FeatureName
hb_ot_name_id_t get_feature_name_id () const { return nameIndex; } hb_ot_name_id_t get_feature_name_id () const { return nameIndex; }
bool is_exclusive () const { return featureFlags & Exclusive; }
/* A FeatureName with no settings is meaningless */ /* A FeatureName with no settings is meaningless */
bool has_data () const { return nSettings; } bool has_data () const { return nSettings; }

View File

@ -948,8 +948,10 @@ struct Chain
hb_aat_layout_feature_type_t type = (hb_aat_layout_feature_type_t) (unsigned int) feature.featureType; hb_aat_layout_feature_type_t type = (hb_aat_layout_feature_type_t) (unsigned int) feature.featureType;
hb_aat_layout_feature_selector_t setting = (hb_aat_layout_feature_selector_t) (unsigned int) feature.featureSetting; hb_aat_layout_feature_selector_t setting = (hb_aat_layout_feature_selector_t) (unsigned int) feature.featureSetting;
retry: retry:
const hb_aat_map_builder_t::feature_info_t *info = map->features.bsearch (type); // Check whether this type/setting pair was requested in the map, and if so, apply its flags.
if (info && info->setting == setting) // (The search here only looks at the type and setting fields of feature_info_t.)
hb_aat_map_builder_t::feature_info_t info = { type, setting, false, 0 };
if (map->features.bsearch (info))
{ {
flags &= feature.disableFlags; flags &= feature.disableFlags;
flags |= feature.enableFlags; flags |= feature.enableFlags;

View File

@ -48,12 +48,15 @@ void hb_aat_map_builder_t::add_feature (hb_tag_t tag, unsigned value)
info->type = HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES; info->type = HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES;
info->setting = (hb_aat_layout_feature_selector_t) value; info->setting = (hb_aat_layout_feature_selector_t) value;
info->seq = features.length; info->seq = features.length;
info->is_exclusive = true;
return; return;
} }
const hb_aat_feature_mapping_t *mapping = hb_aat_layout_find_feature_mapping (tag); const hb_aat_feature_mapping_t *mapping = hb_aat_layout_find_feature_mapping (tag);
if (!mapping) return; if (!mapping) return;
if (!face->table.feat->exposes_feature (mapping->aatFeatureType))
const AAT::FeatureName* feature = &face->table.feat->get_feature (mapping->aatFeatureType);
if (!feature->has_data ())
{ {
/* Special case: Chain::compile_flags will fall back to the deprecated version of /* Special case: Chain::compile_flags will fall back to the deprecated version of
* small-caps if necessary, so we need to check for that possibility. * small-caps if necessary, so we need to check for that possibility.
@ -61,7 +64,8 @@ void hb_aat_map_builder_t::add_feature (hb_tag_t tag, unsigned value)
if (mapping->aatFeatureType == HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE && if (mapping->aatFeatureType == HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE &&
mapping->selectorToEnable == HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS) mapping->selectorToEnable == HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS)
{ {
if (!face->table.feat->exposes_feature (HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE)) return; feature = &face->table.feat->get_feature (HB_AAT_LAYOUT_FEATURE_TYPE_LETTER_CASE);
if (!feature->has_data ()) return;
} }
else return; else return;
} }
@ -70,6 +74,7 @@ void hb_aat_map_builder_t::add_feature (hb_tag_t tag, unsigned value)
info->type = mapping->aatFeatureType; info->type = mapping->aatFeatureType;
info->setting = value ? mapping->selectorToEnable : mapping->selectorToDisable; info->setting = value ? mapping->selectorToEnable : mapping->selectorToDisable;
info->seq = features.length; info->seq = features.length;
info->is_exclusive = feature->is_exclusive ();
} }
void void
@ -81,7 +86,11 @@ hb_aat_map_builder_t::compile (hb_aat_map_t &m)
features.qsort (); features.qsort ();
unsigned int j = 0; unsigned int j = 0;
for (unsigned int i = 1; i < features.length; i++) for (unsigned int i = 1; i < features.length; i++)
if (features[i].type != features[j].type) if (features[i].type != features[j].type ||
/* Nonexclusive feature selectors come in even/odd pairs to turn a setting on/off
* respectively, so we mask out the low-order bit when checking for "duplicates"
* (selectors referring to the same feature setting) here. */
(!features[i].is_exclusive && ((features[i].setting & ~1) != (features[j].setting & ~1))))
features[++j] = features[i]; features[++j] = features[i];
features.shrink (j + 1); features.shrink (j + 1);
} }

View File

@ -64,19 +64,24 @@ struct hb_aat_map_builder_t
{ {
hb_aat_layout_feature_type_t type; hb_aat_layout_feature_type_t type;
hb_aat_layout_feature_selector_t setting; hb_aat_layout_feature_selector_t setting;
bool is_exclusive;
unsigned seq; /* For stable sorting only. */ unsigned seq; /* For stable sorting only. */
HB_INTERNAL static int cmp (const void *pa, const void *pb) HB_INTERNAL static int cmp (const void *pa, const void *pb)
{ {
const feature_info_t *a = (const feature_info_t *) pa; const feature_info_t *a = (const feature_info_t *) pa;
const feature_info_t *b = (const feature_info_t *) pb; const feature_info_t *b = (const feature_info_t *) pb;
return (a->type != b->type) ? (a->type < b->type ? -1 : 1) : if (a->type != b->type) return (a->type < b->type ? -1 : 1);
(a->seq < b->seq ? -1 : a->seq > b->seq ? 1 : 0); if (!a->is_exclusive &&
(a->setting & ~1) != (b->setting & ~1)) return (a->setting < b->setting ? -1 : 1);
return (a->seq < b->seq ? -1 : a->seq > b->seq ? 1 : 0);
} }
int cmp (hb_aat_layout_feature_type_t ty) const /* compares type & setting only, not is_exclusive flag or seq number */
int cmp (const feature_info_t& f) const
{ {
return (ty != type) ? (ty < type ? -1 : 1) : 0; return (f.type != type) ? (f.type < type ? -1 : 1) :
(f.setting != setting) ? (f.setting < setting ? -1 : 1) : 0;
} }
}; };