From 5c278464e0b39443846b10a8a68c2dc4ccbe4673 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Sun, 17 Dec 2023 10:52:43 -0700 Subject: [PATCH] [subset/VARC] Subset MultiVarStore --- Lib/fontTools/subset/__init__.py | 32 +++++++++++++++++++++++++-- Lib/fontTools/varLib/multiVarStore.py | 27 ++++++++++++++++++++++ Lib/fontTools/varLib/varStore.py | 32 +++++++++++++++------------ 3 files changed, 75 insertions(+), 16 deletions(-) diff --git a/Lib/fontTools/subset/__init__.py b/Lib/fontTools/subset/__init__.py index 909ba7d80..4016c3534 100644 --- a/Lib/fontTools/subset/__init__.py +++ b/Lib/fontTools/subset/__init__.py @@ -14,7 +14,7 @@ from fontTools.misc.cliTools import makeOutputFileName from fontTools.subset.util import _add_method, _uniq_sort from fontTools.subset.cff import * from fontTools.subset.svg import * -from fontTools.varLib import varStore # for subset_varidxes +from fontTools.varLib import varStore, multiVarStore # For monkey-patching from fontTools.ttLib.tables._n_a_m_e import NameRecordVisitor import sys import struct @@ -2629,13 +2629,17 @@ def closure_glyphs(self, s): s.glyphs.update(variants) + @_add_method(ttLib.getTableClass("VARC")) def subset_glyphs(self, s): indices = self.table.Coverage.subset(s.glyphs) # TODO Subset MultiVarStore - self.table.VarCompositeGlyphs.glyphs = _list_subset(self.table.VarCompositeGlyphs.glyphs, indices) + self.table.VarCompositeGlyphs.glyphs = _list_subset( + self.table.VarCompositeGlyphs.glyphs, indices + ) return bool(self.table.VarCompositeGlyphs.glyphs) + @_add_method(ttLib.getTableClass("VARC")) def closure_glyphs(self, s): if self.table.VarCompositeGlyphs: @@ -2650,6 +2654,30 @@ def closure_glyphs(self, s): glyphs.add(name) new.add(name) + +@_add_method(ttLib.getTableClass("VARC")) +def prune_post_subset(self, font, options): + table = self.table + + if not hasattr(table, "MultiVarStore"): + return True + + store = table.MultiVarStore + + usedVarIdxes = set() + + # Collect. + table.collect_varidxes(usedVarIdxes) + + # Subset. + varidx_map = store.subset_varidxes(usedVarIdxes) + + # Map. + table.remap_varidxes(varidx_map) + + return True + + @_add_method(ttLib.getTableClass("MATH")) def closure_glyphs(self, s): if self.table.MathVariants: diff --git a/Lib/fontTools/varLib/multiVarStore.py b/Lib/fontTools/varLib/multiVarStore.py index fbee351a4..365e7aad1 100644 --- a/Lib/fontTools/varLib/multiVarStore.py +++ b/Lib/fontTools/varLib/multiVarStore.py @@ -3,6 +3,7 @@ from fontTools.misc.intTools import bit_count from fontTools.misc.vector import Vector from fontTools.ttLib.tables import otTables as ot from fontTools.varLib.models import supportScalar +import fontTools.varLib.varStore # For monkey-patching from fontTools.varLib.builder import ( buildVarRegionList, buildVarRegion, @@ -191,3 +192,29 @@ class MultiVarStoreInstancer(object): varData = self._varData scalars = [self._getScalar(ri) for ri in varData[varDataIndex].VarRegionIndex] return self.interpolateFromDeltasAndScalars(deltas, scalars) + + +def MultiVarStore_subset_varidxes(self, varIdxes): + return ot.VarStore.subset_varidxes(self, varIdxes, VarData="MultiVarData") + + +ot.MultiVarStore.prune_regions = ot.VarStore.prune_regions +ot.MultiVarStore.subset_varidxes = MultiVarStore_subset_varidxes + + +def VARC_collect_varidxes(self, varidxes): + for glyph in self.VarCompositeGlyphs.glyphs: + for component in glyph.components: + varidxes.add(component.locationVarIndex) + varidxes.add(component.transformVarIndex) + + +def VARC_remap_varidxes(self, varidxes_map): + for glyph in self.VarCompositeGlyphs.glyphs: + for component in glyph.components: + component.locationVarIndex = varidxes_map[component.locationVarIndex] + component.transformVarIndex = varidxes_map[component.transformVarIndex] + + +ot.VARC.collect_varidxes = VARC_collect_varidxes +ot.VARC.remap_varidxes = VARC_remap_varidxes diff --git a/Lib/fontTools/varLib/varStore.py b/Lib/fontTools/varLib/varStore.py index d4e199a4d..ecfc02fc7 100644 --- a/Lib/fontTools/varLib/varStore.py +++ b/Lib/fontTools/varLib/varStore.py @@ -246,26 +246,29 @@ class VarStoreInstancer(object): def VarStore_subset_varidxes( - self, varIdxes, optimize=True, retainFirstMap=False, advIdxes=set() + self, + varIdxes, + optimize=True, + retainFirstMap=False, + advIdxes=set(), + *, + VarData="VarData", ): # Sort out used varIdxes by major/minor. - used = {} + used = defaultdict(set) for varIdx in varIdxes: if varIdx == NO_VARIATION_INDEX: continue major = varIdx >> 16 minor = varIdx & 0xFFFF - d = used.get(major) - if d is None: - d = used[major] = set() - d.add(minor) + used[major].add(minor) del varIdxes # # Subset VarData # - varData = self.VarData + varData = getattr(self, VarData) newVarData = [] varDataMap = {NO_VARIATION_INDEX: NO_VARIATION_INDEX} for major, data in enumerate(varData): @@ -296,12 +299,13 @@ def VarStore_subset_varidxes( data.Item = newItems data.ItemCount = len(data.Item) - data.calculateNumShorts(optimize=optimize) + if VarData == "VarData": + data.calculateNumShorts(optimize=optimize) - self.VarData = newVarData - self.VarDataCount = len(self.VarData) + setattr(self, VarData, newVarData) + setattr(self, VarData + "Count", len(newVarData)) - self.prune_regions() + self.prune_regions(VarData=VarData) return varDataMap @@ -309,7 +313,7 @@ def VarStore_subset_varidxes( ot.VarStore.subset_varidxes = VarStore_subset_varidxes -def VarStore_prune_regions(self): +def VarStore_prune_regions(self, *, VarData="VarData"): """Remove unused VarRegions.""" # # Subset VarRegionList @@ -317,7 +321,7 @@ def VarStore_prune_regions(self): # Collect. usedRegions = set() - for data in self.VarData: + for data in getattr(self, VarData): usedRegions.update(data.VarRegionIndex) # Subset. regionList = self.VarRegionList @@ -330,7 +334,7 @@ def VarStore_prune_regions(self): regionList.Region = newRegions regionList.RegionCount = len(regionList.Region) # Map. - for data in self.VarData: + for data in getattr(self, VarData): data.VarRegionIndex = [regionMap[i] for i in data.VarRegionIndex]