summaryrefslogtreecommitdiffstats
path: root/contrib/python
diff options
context:
space:
mode:
authorrobot-piglet <[email protected]>2025-07-18 09:09:17 +0300
committerrobot-piglet <[email protected]>2025-07-18 09:21:36 +0300
commitb27290b9f146a42ce239e15c67cf203f4a151aa7 (patch)
treef1aea70e5c4f342eb93a862e4ed3b5714b353cce /contrib/python
parent9976b916cb8114c23b8b981651f6c4b6256a502a (diff)
Intermediate changes
commit_hash:134fe147a2593cd4e39895d6b77ac876aa724f8b
Diffstat (limited to 'contrib/python')
-rw-r--r--contrib/python/fonttools/.dist-info/METADATA9
-rw-r--r--contrib/python/fonttools/fontTools/__init__.py2
-rw-r--r--contrib/python/fonttools/fontTools/feaLib/ast.py3
-rw-r--r--contrib/python/fonttools/fontTools/feaLib/builder.py5
-rw-r--r--contrib/python/fonttools/fontTools/misc/visitor.py8
-rw-r--r--contrib/python/fonttools/fontTools/otlLib/builder.py30
-rw-r--r--contrib/python/fonttools/fontTools/varLib/instancer/__init__.py58
-rw-r--r--contrib/python/fonttools/ya.make2
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)