diff options
author | robot-piglet <[email protected]> | 2025-07-18 09:09:17 +0300 |
---|---|---|
committer | robot-piglet <[email protected]> | 2025-07-18 09:21:36 +0300 |
commit | b27290b9f146a42ce239e15c67cf203f4a151aa7 (patch) | |
tree | f1aea70e5c4f342eb93a862e4ed3b5714b353cce /contrib/python | |
parent | 9976b916cb8114c23b8b981651f6c4b6256a502a (diff) |
Intermediate changes
commit_hash:134fe147a2593cd4e39895d6b77ac876aa724f8b
Diffstat (limited to 'contrib/python')
-rw-r--r-- | contrib/python/fonttools/.dist-info/METADATA | 9 | ||||
-rw-r--r-- | contrib/python/fonttools/fontTools/__init__.py | 2 | ||||
-rw-r--r-- | contrib/python/fonttools/fontTools/feaLib/ast.py | 3 | ||||
-rw-r--r-- | contrib/python/fonttools/fontTools/feaLib/builder.py | 5 | ||||
-rw-r--r-- | contrib/python/fonttools/fontTools/misc/visitor.py | 8 | ||||
-rw-r--r-- | contrib/python/fonttools/fontTools/otlLib/builder.py | 30 | ||||
-rw-r--r-- | contrib/python/fonttools/fontTools/varLib/instancer/__init__.py | 58 | ||||
-rw-r--r-- | contrib/python/fonttools/ya.make | 2 |
8 files changed, 86 insertions, 31 deletions
diff --git a/contrib/python/fonttools/.dist-info/METADATA b/contrib/python/fonttools/.dist-info/METADATA index ddc8be2d7de..164ca311dc3 100644 --- a/contrib/python/fonttools/.dist-info/METADATA +++ b/contrib/python/fonttools/.dist-info/METADATA @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: fonttools -Version: 4.58.4 +Version: 4.58.5 Summary: Tools to manipulate font files Home-page: http://github.com/fonttools/fonttools Author: Just van Rossum @@ -388,6 +388,13 @@ Have fun! Changelog ~~~~~~~~~ +4.58.5 (released 2025-07-03) +---------------------------- + +- [feaLib] Don't try to combine ligature & multisub rules (#3874). +- [feaLib/ast] Use weakref proxies to avoid cycles in visitor (#3873). +- [varLib.instancer] Fixed instancing CFF2 fonts where VarData contains more than 64k items (#3858). + 4.58.4 (released 2025-06-13) ---------------------------- diff --git a/contrib/python/fonttools/fontTools/__init__.py b/contrib/python/fonttools/fontTools/__init__.py index 155d9250220..306cd68a6be 100644 --- a/contrib/python/fonttools/fontTools/__init__.py +++ b/contrib/python/fonttools/fontTools/__init__.py @@ -3,6 +3,6 @@ from fontTools.misc.loggingTools import configLogger log = logging.getLogger(__name__) -version = __version__ = "4.58.4" +version = __version__ = "4.58.5" __all__ = ["version", "log", "configLogger"] diff --git a/contrib/python/fonttools/fontTools/feaLib/ast.py b/contrib/python/fonttools/fontTools/feaLib/ast.py index 9663f73b4c8..2624e6b9d6d 100644 --- a/contrib/python/fonttools/fontTools/feaLib/ast.py +++ b/contrib/python/fonttools/fontTools/feaLib/ast.py @@ -1,3 +1,4 @@ +import weakref from fontTools.feaLib.error import FeatureLibError from fontTools.feaLib.location import FeatureLibLocation from fontTools.misc.encodingTools import getEncoding @@ -529,7 +530,7 @@ class MarkClass(object): def addDefinition(self, definition): """Add a :class:`MarkClassDefinition` statement to this mark class.""" assert isinstance(definition, MarkClassDefinition) - self.definitions.append(definition) + self.definitions.append(weakref.proxy(definition)) for glyph in definition.glyphSet(): if glyph in self.glyphs: otherLoc = self.glyphs[glyph].location diff --git a/contrib/python/fonttools/fontTools/feaLib/builder.py b/contrib/python/fonttools/fontTools/feaLib/builder.py index ce9515bd68d..25b319c0b01 100644 --- a/contrib/python/fonttools/fontTools/feaLib/builder.py +++ b/contrib/python/fonttools/fontTools/feaLib/builder.py @@ -259,12 +259,13 @@ class Builder(object): key = (script, lang, feature_name) self.features_.setdefault(key, []).append(lookup) - def get_lookup_(self, location, builder_class): + def get_lookup_(self, location, builder_class, mapping=None): if ( self.cur_lookup_ and type(self.cur_lookup_) == builder_class and self.cur_lookup_.lookupflag == self.lookupflag_ and self.cur_lookup_.markFilterSet == self.lookupflag_markFilterSet_ + and self.cur_lookup_.can_add_mapping(mapping) ): return self.cur_lookup_ if self.cur_lookup_name_ and self.cur_lookup_: @@ -1305,7 +1306,7 @@ class Builder(object): # GSUB rules def add_any_subst_(self, location, mapping): - lookup = self.get_lookup_(location, AnySubstBuilder) + lookup = self.get_lookup_(location, AnySubstBuilder, mapping=mapping) for key, value in mapping.items(): if key in lookup.mapping: if value == lookup.mapping[key]: diff --git a/contrib/python/fonttools/fontTools/misc/visitor.py b/contrib/python/fonttools/fontTools/misc/visitor.py index 6de432ef934..65f305632de 100644 --- a/contrib/python/fonttools/fontTools/misc/visitor.py +++ b/contrib/python/fonttools/fontTools/misc/visitor.py @@ -1,11 +1,19 @@ """Generic visitor pattern implementation for Python objects.""" import enum +import weakref class Visitor(object): defaultStop = False + _visitors = { + # By default we skip visiting weak references to avoid recursion + # issues. Users can override this by registering a visit + # function for weakref.ProxyType. + weakref.ProxyType: {None: lambda self, obj, *args, **kwargs: False} + } + @classmethod def _register(celf, clazzes_attrs): assert celf != Visitor, "Subclass Visitor instead." diff --git a/contrib/python/fonttools/fontTools/otlLib/builder.py b/contrib/python/fonttools/fontTools/otlLib/builder.py index da00e9c5ebd..c4b482f02b4 100644 --- a/contrib/python/fonttools/fontTools/otlLib/builder.py +++ b/contrib/python/fonttools/fontTools/otlLib/builder.py @@ -256,6 +256,10 @@ class LookupBuilder(object): ) ) + def can_add_mapping(self, _mapping) -> bool: + # used by AnySubstBuilder, below + return True + class AlternateSubstBuilder(LookupBuilder): """Builds an Alternate Substitution (GSUB3) lookup. @@ -1376,6 +1380,32 @@ class AnySubstBuilder(LookupBuilder): def _add_to_ligature_subst(self, builder, key, value): builder.ligatures[key] = value[0] + def can_add_mapping(self, mapping) -> bool: + if mapping is None: + return True + # single sub rules can be treated as (degenerate) liga-or-multi sub + # rules, but multi and liga sub rules themselves have incompatible + # representations. It is uncommon that these are in the same set of + # rules, but it happens. + is_multi = any(len(v) > 1 for v in mapping.values()) + is_liga = any(len(k) > 1 for k in mapping.keys()) + + has_existing_multi = False + has_existing_liga = False + + for k, v in self.mapping.items(): + if k[0] == self.SUBTABLE_BREAK_: + continue + if len(k) > 1: + has_existing_liga = True + if len(v) > 1: + has_existing_multi = True + + can_reuse = not (has_existing_multi and is_liga) and not ( + has_existing_liga and is_multi + ) + return can_reuse + def promote_lookup_type(self, is_named_lookup): # https://github.com/fonttools/fonttools/issues/612 # A multiple substitution may have a single destination, in which case diff --git a/contrib/python/fonttools/fontTools/varLib/instancer/__init__.py b/contrib/python/fonttools/fontTools/varLib/instancer/__init__.py index a0b832a3c18..76901880553 100644 --- a/contrib/python/fonttools/fontTools/varLib/instancer/__init__.py +++ b/contrib/python/fonttools/fontTools/varLib/instancer/__init__.py @@ -723,9 +723,7 @@ def instantiateCFF2( minor = varDataCursor[major] varDataCursor[major] += 1 - varIdx = (major << 16) + minor - - defaultValue += round(defaultDeltas[varIdx]) + defaultValue += round(defaultDeltas[major][minor]) newDefaults.append(defaultValue) varData = varStore.VarData[major] @@ -781,7 +779,9 @@ def instantiateCFF2( storeBlendsToVarStore(value + [count]) # Instantiate VarStore - defaultDeltas = instantiateItemVariationStore(varStore, fvarAxes, axisLimits) + defaultDeltas = instantiateItemVariationStore( + varStore, fvarAxes, axisLimits, hierarchical=True + ) # Read back new charstring blends from the instantiated VarStore varDataCursor = [0] * len(varStore.VarData) @@ -839,18 +839,13 @@ def instantiateCFF2( varData.Item = [] varData.ItemCount = 0 - # Remove vsindex commands that are no longer needed, collect those that are. - usedVsindex = set() - for commands in allCommands: - if any(isinstance(arg, list) for command in commands for arg in command[1]): - vsindex = 0 - for command in commands: - if command[0] == "vsindex": - vsindex = command[1][0] - continue - if any(isinstance(arg, list) for arg in command[1]): - usedVsindex.add(vsindex) - else: + # Collect surviving vsindexes + usedVsindex = set( + i for i in range(len(varStore.VarData)) if varStore.VarData[i].VarRegionCount + ) + # Remove vsindex commands that are no longer needed + for commands, private in zip(allCommands, allCommandPrivates): + if not any(isinstance(arg, list) for command in commands for arg in command[1]): commands[:] = [command for command in commands if command[0] != "vsindex"] # Remove unused VarData and update vsindex values @@ -863,10 +858,14 @@ def instantiateCFF2( for command in commands: if command[0] == "vsindex": command[1][0] = vsindexMapping[command[1][0]] + for private in privateDicts: + if hasattr(private, "vsindex"): + private.vsindex = vsindexMapping[private.vsindex] # Remove initial vsindex commands that are implied - for commands in allCommands: - if commands and commands[0] == ("vsindex", [0]): + for commands, private in zip(allCommands, allCommandPrivates): + vsindex = getattr(private, "vsindex", 0) + if commands and commands[0] == ("vsindex", [vsindex]): commands.pop(0) # Ship the charstrings! @@ -1247,7 +1246,9 @@ class _TupleVarStoreAdapter(object): return itemVarStore -def instantiateItemVariationStore(itemVarStore, fvarAxes, axisLimits): +def instantiateItemVariationStore( + itemVarStore, fvarAxes, axisLimits, hierarchical=False +): """Compute deltas at partial location, and update varStore in-place. Remove regions in which all axes were instanced, or fall outside the new axis @@ -1279,12 +1280,19 @@ def instantiateItemVariationStore(itemVarStore, fvarAxes, axisLimits): assert itemVarStore.VarDataCount == newItemVarStore.VarDataCount itemVarStore.VarData = newItemVarStore.VarData - defaultDeltas = { - ((major << 16) + minor): delta - for major, deltas in enumerate(defaultDeltaArray) - for minor, delta in enumerate(deltas) - } - defaultDeltas[itemVarStore.NO_VARIATION_INDEX] = 0 + if not hierarchical: + defaultDeltas = { + ((major << 16) + minor): delta + for major, deltas in enumerate(defaultDeltaArray) + for minor, delta in enumerate(deltas) + } + defaultDeltas[itemVarStore.NO_VARIATION_INDEX] = 0 + else: + defaultDeltas = {0xFFFF: {0xFFFF: 0}} # NO_VARIATION_INDEX + for major, deltas in enumerate(defaultDeltaArray): + defaultDeltasForMajor = defaultDeltas.setdefault(major, {}) + for minor, delta in enumerate(deltas): + defaultDeltasForMajor[minor] = delta return defaultDeltas diff --git a/contrib/python/fonttools/ya.make b/contrib/python/fonttools/ya.make index f2a7b5ccf30..e114871a154 100644 --- a/contrib/python/fonttools/ya.make +++ b/contrib/python/fonttools/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(4.58.4) +VERSION(4.58.5) LICENSE(MIT) |