diff options
Diffstat (limited to 'Lib/fontTools/varLib/merger.py')
-rw-r--r-- | Lib/fontTools/varLib/merger.py | 78 |
1 files changed, 50 insertions, 28 deletions
diff --git a/Lib/fontTools/varLib/merger.py b/Lib/fontTools/varLib/merger.py index c9d14381..5a3a4f34 100644 --- a/Lib/fontTools/varLib/merger.py +++ b/Lib/fontTools/varLib/merger.py @@ -1,8 +1,10 @@ """ Merge OpenType Layout tables (GDEF / GPOS / GSUB). """ +import os import copy from operator import ior +import logging from fontTools.misc import classifyTools from fontTools.misc.roundTools import otRound from fontTools.ttLib.tables import otTables as ot @@ -13,6 +15,13 @@ from fontTools.varLib.models import nonNone, allNone, allEqual, allEqualTo from fontTools.varLib.varStore import VarStoreInstancer from functools import reduce from fontTools.otlLib.builder import buildSinglePos +from fontTools.otlLib.optimize.gpos import ( + compact_pair_pos, + GPOS_COMPACT_MODE_DEFAULT, + GPOS_COMPACT_MODE_ENV_KEY, +) + +log = logging.getLogger("fontTools.varLib.merger") from .errors import ( ShouldBeConstant, @@ -143,7 +152,7 @@ class AligningMerger(Merger): def merge(merger, self, lst): if self is None: if not allNone(lst): - raise NotANone(self, expected=None, got=lst) + raise NotANone(merger, expected=None, got=lst) return lst = [l.classDefs for l in lst] @@ -156,7 +165,7 @@ def merge(merger, self, lst): for k in allKeys: allValues = nonNone(l.get(k) for l in lst) if not allEqual(allValues): - raise ShouldBeConstant(self, expected=allValues[0], got=lst, stack="."+k) + raise ShouldBeConstant(merger, expected=allValues[0], got=lst, stack=["." + k]) if not allValues: self[k] = None else: @@ -193,7 +202,7 @@ def _merge_GlyphOrders(font, lst, values_lst=None, default=None): order = sorted(combined, key=sortKey) # Make sure all input glyphsets were in proper order if not all(sorted(vs, key=sortKey) == vs for vs in lst): - raise InconsistentGlyphOrder(self) + raise InconsistentGlyphOrder() del combined paddedValues = None @@ -208,7 +217,7 @@ def _merge_GlyphOrders(font, lst, values_lst=None, default=None): for dict_set in dict_sets] return order, padded -def _Lookup_SinglePos_get_effective_value(subtables, glyph): +def _Lookup_SinglePos_get_effective_value(merger, subtables, glyph): for self in subtables: if self is None or \ type(self) != ot.SinglePos or \ @@ -220,10 +229,10 @@ def _Lookup_SinglePos_get_effective_value(subtables, glyph): elif self.Format == 2: return self.Value[self.Coverage.glyphs.index(glyph)] else: - raise UnsupportedFormat(self, subtable="single positioning lookup") + raise UnsupportedFormat(merger, subtable="single positioning lookup") return None -def _Lookup_PairPos_get_effective_value_pair(subtables, firstGlyph, secondGlyph): +def _Lookup_PairPos_get_effective_value_pair(merger, subtables, firstGlyph, secondGlyph): for self in subtables: if self is None or \ type(self) != ot.PairPos or \ @@ -242,20 +251,21 @@ def _Lookup_PairPos_get_effective_value_pair(subtables, firstGlyph, secondGlyph) klass2 = self.ClassDef2.classDefs.get(secondGlyph, 0) return self.Class1Record[klass1].Class2Record[klass2] else: - raise UnsupportedFormat(self, subtable="pair positioning lookup") + raise UnsupportedFormat(merger, subtable="pair positioning lookup") return None @AligningMerger.merger(ot.SinglePos) def merge(merger, self, lst): self.ValueFormat = valueFormat = reduce(int.__or__, [l.ValueFormat for l in lst], 0) if not (len(lst) == 1 or (valueFormat & ~0xF == 0)): - raise UnsupportedFormat(self, subtable="single positioning lookup") + raise UnsupportedFormat(merger, subtable="single positioning lookup") # If all have same coverage table and all are format 1, coverageGlyphs = self.Coverage.glyphs if all(v.Format == 1 for v in lst) and all(coverageGlyphs == v.Coverage.glyphs for v in lst): - self.Value = otBase.ValueRecord(valueFormat) - merger.mergeThings(self.Value, [v.Value for v in lst]) + self.Value = otBase.ValueRecord(valueFormat, self.Value) + if valueFormat != 0: + merger.mergeThings(self.Value, [v.Value for v in lst]) self.ValueFormat = self.Value.getFormat() return @@ -279,7 +289,7 @@ def merge(merger, self, lst): # Note!!! This *might* result in behavior change if ValueFormat2-zeroedness # is different between used subtable and current subtable! # TODO(behdad) Check and warn if that happens? - v = _Lookup_SinglePos_get_effective_value(merger.lookup_subtables[i], glyph) + v = _Lookup_SinglePos_get_effective_value(merger, merger.lookup_subtables[i], glyph) if v is None: v = otBase.ValueRecord(valueFormat) values[j] = v @@ -288,8 +298,8 @@ def merge(merger, self, lst): # Merge everything else; though, there shouldn't be anything else. :) merger.mergeObjects(self, lst, - exclude=('Format', 'Coverage', 'Value', 'ValueCount')) - self.ValueFormat = reduce(int.__or__, [v.getFormat() for v in self.Value], 0) + exclude=('Format', 'Coverage', 'Value', 'ValueCount', 'ValueFormat')) + self.ValueFormat = reduce(int.__or__, [v.getEffectiveFormat() for v in self.Value], 0) @AligningMerger.merger(ot.PairSet) def merge(merger, self, lst): @@ -315,7 +325,9 @@ def merge(merger, self, lst): if values[j] is not None: vpair = values[j] else: - vpair = _Lookup_PairPos_get_effective_value_pair(merger.lookup_subtables[i], self._firstGlyph, glyph) + vpair = _Lookup_PairPos_get_effective_value_pair( + merger, merger.lookup_subtables[i], self._firstGlyph, glyph + ) if vpair is None: v1, v2 = None, None else: @@ -518,7 +530,7 @@ def merge(merger, self, lst): elif self.Format == 2: _PairPosFormat2_merge(self, lst, merger) else: - raise UnsupportedFormat(self, subtable="pair positioning lookup") + raise UnsupportedFormat(merger, subtable="pair positioning lookup") del merger.valueFormat1, merger.valueFormat2 @@ -584,8 +596,7 @@ def _MarkBasePosFormat1_merge(self, lst, merger, Mark='Mark', Base='Base'): # input masters. if not allEqual(allClasses): - raise allClasses(self, allClasses) - rec = None + raise ShouldBeConstant(merger, expected=allClasses[0], got=allClasses) else: rec = ot.MarkRecord() rec.Class = allClasses[0] @@ -633,7 +644,8 @@ def _MarkBasePosFormat1_merge(self, lst, merger, Mark='Mark', Base='Base'): @AligningMerger.merger(ot.MarkBasePos) def merge(merger, self, lst): if not allEqualTo(self.Format, (l.Format for l in lst)): - raise InconsistentFormats(self, + raise InconsistentFormats( + merger, subtable="mark-to-base positioning lookup", expected=self.Format, got=[l.Format for l in lst] @@ -641,12 +653,13 @@ def merge(merger, self, lst): if self.Format == 1: _MarkBasePosFormat1_merge(self, lst, merger) else: - raise UnsupportedFormat(self, subtable="mark-to-base positioning lookup") + raise UnsupportedFormat(merger, subtable="mark-to-base positioning lookup") @AligningMerger.merger(ot.MarkMarkPos) def merge(merger, self, lst): if not allEqualTo(self.Format, (l.Format for l in lst)): - raise InconsistentFormats(self, + raise InconsistentFormats( + merger, subtable="mark-to-mark positioning lookup", expected=self.Format, got=[l.Format for l in lst] @@ -654,7 +667,7 @@ def merge(merger, self, lst): if self.Format == 1: _MarkBasePosFormat1_merge(self, lst, merger, 'Mark1', 'Mark2') else: - raise UnsupportedFormat(self, subtable="mark-to-mark positioning lookup") + raise UnsupportedFormat(merger, subtable="mark-to-mark positioning lookup") def _PairSet_flatten(lst, font): self = ot.PairSet() @@ -780,12 +793,13 @@ def merge(merger, self, lst): continue if sts[0].__class__.__name__.startswith('Extension'): if not allEqual([st.__class__ for st in sts]): - raise InconsistentExtensions(self, + raise InconsistentExtensions( + merger, expected="Extension", got=[st.__class__.__name__ for st in sts] ) if not allEqual([st.ExtensionLookupType for st in sts]): - raise InconsistentExtensions(self) + raise InconsistentExtensions(merger) l.LookupType = sts[0].ExtensionLookupType new_sts = [st.ExtSubTable for st in sts] del sts[:] @@ -833,6 +847,15 @@ def merge(merger, self, lst): self.SubTable.pop(-1) self.SubTableCount -= 1 + # Compact the merged subtables + # This is a good moment to do it because the compaction should create + # smaller subtables, which may prevent overflows from happening. + mode = os.environ.get(GPOS_COMPACT_MODE_ENV_KEY, GPOS_COMPACT_MODE_DEFAULT) + if mode and mode != "0": + log.info("Compacting GPOS...") + self.SubTable = compact_pair_pos(merger.font, mode, self.SubTable) + self.SubTableCount = len(self.SubTable) + elif isSinglePos and flattened: singlePosTable = self.SubTable[0] glyphs = singlePosTable.Coverage.glyphs @@ -847,7 +870,6 @@ def merge(merger, self, lst): del merger.lookup_subtables - # # InstancerMerger # @@ -989,7 +1011,7 @@ def merge(merger, self, lst): varidx = (dev.StartSize << 16) + dev.EndSize delta = otRound(instancer[varidx]) - setattr(self, name, getattr(self, name) + delta) + setattr(self, name, getattr(self, name, 0) + delta) # @@ -1035,7 +1057,7 @@ def buildVarDevTable(store_builder, master_values): @VariationMerger.merger(ot.BaseCoord) def merge(merger, self, lst): if self.Format != 1: - raise UnsupportedFormat(self, subtable="a baseline coordinate") + raise UnsupportedFormat(merger, subtable="a baseline coordinate") self.Coordinate, DeviceTable = buildVarDevTable(merger.store_builder, [a.Coordinate for a in lst]) if DeviceTable: self.Format = 3 @@ -1044,7 +1066,7 @@ def merge(merger, self, lst): @VariationMerger.merger(ot.CaretValue) def merge(merger, self, lst): if self.Format != 1: - raise UnsupportedFormat(self, subtable="a caret") + raise UnsupportedFormat(merger, subtable="a caret") self.Coordinate, DeviceTable = buildVarDevTable(merger.store_builder, [a.Coordinate for a in lst]) if DeviceTable: self.Format = 3 @@ -1053,7 +1075,7 @@ def merge(merger, self, lst): @VariationMerger.merger(ot.Anchor) def merge(merger, self, lst): if self.Format != 1: - raise UnsupportedFormat(self, subtable="an anchor") + raise UnsupportedFormat(merger, subtable="an anchor") self.XCoordinate, XDeviceTable = buildVarDevTable(merger.store_builder, [a.XCoordinate for a in lst]) self.YCoordinate, YDeviceTable = buildVarDevTable(merger.store_builder, [a.YCoordinate for a in lst]) if XDeviceTable or YDeviceTable: |