From fe01d87b94b5c25529cab877f8707c8d5b88e8e1 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 8 Aug 2017 17:20:37 -0700 Subject: [PATCH] [subset] Support GSUB/GPOS.FeatureVariations We should add a test using the font at: https://github.com/behdad/harfbuzz/issues/521 Test with: $ fonttools subset avar_rvrn.ttf --text=r --- Lib/fontTools/subset/__init__.py | 85 ++++++++++++++++++++++++++------ 1 file changed, 70 insertions(+), 15 deletions(-) diff --git a/Lib/fontTools/subset/__init__.py b/Lib/fontTools/subset/__init__.py index f88c730a1..0c6d12ffe 100644 --- a/Lib/fontTools/subset/__init__.py +++ b/Lib/fontTools/subset/__init__.py @@ -1183,8 +1183,7 @@ def subset_lookups(self, lookup_indices): @_add_method(otTables.Lookup) def collect_lookups(self): - return _uniq_sort(sum((st.collect_lookups() for st in self.SubTable - if st), [])) + return sum((st.collect_lookups() for st in self.SubTable if st), []) @_add_method(otTables.Lookup) def may_have_non_1to1(self): @@ -1220,6 +1219,7 @@ def neuter_lookups(self, lookup_indices): @_add_method(otTables.LookupList) def closure_lookups(self, lookup_indices): + """Returns sorted index of all lookups reachable from lookup_indices.""" lookup_indices = _uniq_sort(lookup_indices) recurse = lookup_indices while True: @@ -1235,6 +1235,7 @@ def closure_lookups(self, lookup_indices): @_add_method(otTables.Feature) def subset_lookups(self, lookup_indices): + """"Returns True if feature is non-empty afterwards.""" self.LookupListIndex = [l for l in self.LookupListIndex if l in lookup_indices] # Now map them. @@ -1243,10 +1244,6 @@ def subset_lookups(self, lookup_indices): self.LookupCount = len(self.LookupListIndex) return self.LookupCount or self.FeatureParams -@_add_method(otTables.Feature) -def collect_lookups(self): - return self.LookupListIndex[:] - @_add_method(otTables.FeatureList) def subset_lookups(self, lookup_indices): """Returns the indices of nonempty features.""" @@ -1254,17 +1251,15 @@ def subset_lookups(self, lookup_indices): # HarfBuzz chooses shaper for Khmer based on presence of this # feature. See thread at: # http://lists.freedesktop.org/archives/harfbuzz/2012-November/002660.html - feature_indices = [i for i,f in enumerate(self.FeatureRecord) - if (f.Feature.subset_lookups(lookup_indices) or - f.FeatureTag == 'pref')] - self.subset_features(feature_indices) - return feature_indices + return [i for i,f in enumerate(self.FeatureRecord) + if (f.Feature.subset_lookups(lookup_indices) or + f.FeatureTag == 'pref')] @_add_method(otTables.FeatureList) def collect_lookups(self, feature_indices): - return _uniq_sort(sum((self.FeatureRecord[i].Feature.collect_lookups() - for i in feature_indices - if i < self.FeatureCount), [])) + return sum((self.FeatureRecord[i].Feature.LookupListIndex + for i in feature_indices + if i < self.FeatureCount), []) @_add_method(otTables.FeatureList) def subset_features(self, feature_indices): @@ -1273,6 +1268,41 @@ def subset_features(self, feature_indices): self.FeatureCount = len(self.FeatureRecord) return bool(self.FeatureCount) +@_add_method(otTables.FeatureTableSubstitution) +def subset_lookups(self, lookup_indices): + """Returns the indices of nonempty features.""" + return [r.FeatureIndex for r in self.SubstitutionRecord + if r.Feature.subset_lookups(lookup_indices)] + +@_add_method(otTables.FeatureVariations) +def subset_lookups(self, lookup_indices): + """Returns the indices of nonempty features.""" + return sum((f.FeatureTableSubstitution.subset_lookups(lookup_indices) + for f in self.FeatureVariationRecord), []) + +@_add_method(otTables.FeatureVariations) +def collect_lookups(self, feature_indices): + return sum((r.Feature.LookupListIndex + for vr in self.FeatureVariationRecord + for r in vr.FeatureTableSubstitution.SubstitutionRecord + if r.FeatureIndex in feature_indices), []) + +@_add_method(otTables.FeatureTableSubstitution) +def subset_features(self, feature_indices): + self.ensureDecompiled() + self.SubstitutionRecord = [r for r in self.SubstitutionRecord + if r.FeatureIndex in feature_indices] + self.SubstitutionCount = len(self.SubstitutionRecord) + return bool(self.SubstitutionCount) + +@_add_method(otTables.FeatureVariations) +def subset_features(self, feature_indices): + self.ensureDecompiled() + self.FeaturVariationRecord = [r for r in self.FeatureVariationRecord + if r.FeatureTableSubstitution.subset_features(feature_indices)] + self.FeatureVariationCount = len(self.FeatureVariationRecord) + return bool(self.FeatureVariationCount) + @_add_method(otTables.DefaultLangSys, otTables.LangSys) def subset_features(self, feature_indices): @@ -1353,6 +1383,9 @@ def closure_glyphs(self, s): lookup_indices = self.table.FeatureList.collect_lookups(feature_indices) else: lookup_indices = [] + if getattr(self.table, 'FeatureVariations', None): + lookup_indices += self.table.FeatureVariations.collect_lookups(feature_indices) + lookup_indices = _uniq_sort(lookup_indices) if self.table.LookupList: while True: orig_glyphs = frozenset(s.glyphs) @@ -1396,6 +1429,13 @@ def subset_lookups(self, lookup_indices): feature_indices = self.table.FeatureList.subset_lookups(lookup_indices) else: feature_indices = [] + if getattr(self.table, 'FeatureVariations', None): + feature_indices += self.table.FeatureVariations.subset_lookups(lookup_indices) + feature_indices = _uniq_sort(feature_indices) + if self.table.FeatureList: + self.table.FeatureList.subset_features(feature_indices) + if getattr(self.table, 'FeatureVariations', None): + self.table.FeatureVariations.subset_features(feature_indices) if self.table.ScriptList: self.table.ScriptList.subset_features(feature_indices, self.retain_empty_scripts()) @@ -1418,6 +1458,9 @@ def prune_lookups(self, remap=True): lookup_indices = self.table.FeatureList.collect_lookups(feature_indices) else: lookup_indices = [] + if getattr(self.table, 'FeatureVariations', None): + lookup_indices += self.table.FeatureVariations.collect_lookups(feature_indices) + lookup_indices = _uniq_sort(lookup_indices) if self.table.LookupList: lookup_indices = self.table.LookupList.closure_lookups(lookup_indices) else: @@ -1428,13 +1471,15 @@ def prune_lookups(self, remap=True): self.neuter_lookups(lookup_indices) @_add_method(ttLib.getTableClass('GSUB'), - ttLib.getTableClass('GPOS')) + ttLib.getTableClass('GPOS')) def subset_feature_tags(self, feature_tags): if self.table.FeatureList: feature_indices = \ [i for i,f in enumerate(self.table.FeatureList.FeatureRecord) if f.FeatureTag in feature_tags] self.table.FeatureList.subset_features(feature_indices) + if getattr(self.table, 'FeatureVariations', None): + self.table.FeatureVariations.subset_features(feature_indices) else: feature_indices = [] if self.table.ScriptList: @@ -1450,6 +1495,8 @@ def prune_features(self): feature_indices = [] if self.table.FeatureList: self.table.FeatureList.subset_features(feature_indices) + if getattr(self.table, 'FeatureVariations', None): + self.table.FeatureVariations.subset_features(feature_indices) if self.table.ScriptList: self.table.ScriptList.subset_features(feature_indices, self.retain_empty_scripts()) @@ -1512,6 +1559,7 @@ def prune_post_subset(self, options): if not table.LookupList: table.FeatureList = None + if table.FeatureList: self.remove_redundant_langsys() # Remove unreferenced features @@ -1529,6 +1577,13 @@ def prune_post_subset(self, options): #if table.ScriptList and not table.ScriptList.ScriptRecord: # table.ScriptList = None + if not table.FeatureList and hasattr(table, 'FeatureVariations'): + table.FeatureVariations = None + + if hasattr(table, 'FeatureVariations') and not table.FeatureVariations: + if table.Version == 0x00010001: + table.Version = 0x00010000 + return True @_add_method(ttLib.getTableClass('GDEF'))