aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrobot-contrib <robot-contrib@yandex-team.com>2023-11-18 09:39:00 +0300
committerrobot-contrib <robot-contrib@yandex-team.com>2023-11-18 09:58:55 +0300
commit02af688a175993fc8169edf79bef767977b91764 (patch)
treea08abcf2e33fea2290da638dab525819d9eedd40
parent45d2a1a9ad08d7bb7ecb28ea9fae9f59c67768b7 (diff)
downloadydb-02af688a175993fc8169edf79bef767977b91764.tar.gz
Update contrib/python/fonttools to 4.44.0
-rw-r--r--contrib/python/fonttools/.dist-info/METADATA27
-rw-r--r--contrib/python/fonttools/fontTools/__init__.py2
-rw-r--r--contrib/python/fonttools/fontTools/afmLib.py1
-rw-r--r--contrib/python/fonttools/fontTools/cffLib/__init__.py3
-rw-r--r--contrib/python/fonttools/fontTools/cffLib/specializer.py3
-rw-r--r--contrib/python/fonttools/fontTools/cffLib/width.py2
-rw-r--r--contrib/python/fonttools/fontTools/feaLib/parser.py4
-rw-r--r--contrib/python/fonttools/fontTools/merge/__init__.py1
-rw-r--r--contrib/python/fonttools/fontTools/merge/options.py1
-rw-r--r--contrib/python/fonttools/fontTools/merge/tables.py1
-rw-r--r--contrib/python/fonttools/fontTools/misc/arrayTools.py4
-rw-r--r--contrib/python/fonttools/fontTools/misc/classifyTools.py1
-rw-r--r--contrib/python/fonttools/fontTools/misc/dictTools.py1
-rw-r--r--contrib/python/fonttools/fontTools/misc/psCharStrings.py3
-rw-r--r--contrib/python/fonttools/fontTools/misc/psLib.py1
-rw-r--r--contrib/python/fonttools/fontTools/misc/psOperators.py2
-rw-r--r--contrib/python/fonttools/fontTools/misc/roundTools.py1
-rw-r--r--contrib/python/fonttools/fontTools/misc/symfont.py3
-rw-r--r--contrib/python/fonttools/fontTools/misc/visitor.py2
-rw-r--r--contrib/python/fonttools/fontTools/otlLib/builder.py2
-rw-r--r--contrib/python/fonttools/fontTools/otlLib/optimize/gpos.py3
-rw-r--r--contrib/python/fonttools/fontTools/pens/cu2quPen.py4
-rw-r--r--contrib/python/fonttools/fontTools/pens/statisticsPen.py10
-rw-r--r--contrib/python/fonttools/fontTools/subset/__init__.py10
-rw-r--r--contrib/python/fonttools/fontTools/subset/svg.py1
-rw-r--r--contrib/python/fonttools/fontTools/svgLib/path/parser.py1
-rw-r--r--contrib/python/fonttools/fontTools/ttLib/__main__.py8
-rw-r--r--contrib/python/fonttools/fontTools/ttLib/scaleUpem.py1
-rw-r--r--contrib/python/fonttools/fontTools/ttLib/sfnt.py3
-rw-r--r--contrib/python/fonttools/fontTools/ttLib/tables/C_B_D_T_.py2
-rw-r--r--contrib/python/fonttools/fontTools/ttLib/tables/C_B_L_C_.py1
-rw-r--r--contrib/python/fonttools/fontTools/ttLib/tables/C_O_L_R_.py2
-rw-r--r--contrib/python/fonttools/fontTools/ttLib/tables/C_P_A_L_.py1
-rw-r--r--contrib/python/fonttools/fontTools/ttLib/tables/D__e_b_g.py2
-rw-r--r--contrib/python/fonttools/fontTools/ttLib/tables/DefaultTable.py1
-rw-r--r--contrib/python/fonttools/fontTools/ttLib/tables/E_B_D_T_.py4
-rw-r--r--contrib/python/fonttools/fontTools/ttLib/tables/E_B_L_C_.py7
-rw-r--r--contrib/python/fonttools/fontTools/ttLib/tables/G_M_A_P_.py1
-rw-r--r--contrib/python/fonttools/fontTools/ttLib/tables/M_E_T_A_.py1
-rw-r--r--contrib/python/fonttools/fontTools/ttLib/tables/S_I_N_G_.py1
-rw-r--r--contrib/python/fonttools/fontTools/ttLib/tables/T_S_I__0.py1
-rw-r--r--contrib/python/fonttools/fontTools/ttLib/tables/T_S_I__1.py1
-rw-r--r--contrib/python/fonttools/fontTools/ttLib/tables/T_S_I__2.py1
-rw-r--r--contrib/python/fonttools/fontTools/ttLib/tables/T_S_I__3.py1
-rw-r--r--contrib/python/fonttools/fontTools/ttLib/tables/TupleVariation.py20
-rw-r--r--contrib/python/fonttools/fontTools/ttLib/tables/_c_m_a_p.py2
-rw-r--r--contrib/python/fonttools/fontTools/ttLib/tables/_g_l_y_f.py100
-rw-r--r--contrib/python/fonttools/fontTools/ttLib/tables/_h_e_a_d.py1
-rw-r--r--contrib/python/fonttools/fontTools/ttLib/tables/_h_h_e_a.py1
-rw-r--r--contrib/python/fonttools/fontTools/ttLib/tables/_h_m_t_x.py1
-rw-r--r--contrib/python/fonttools/fontTools/ttLib/tables/_k_e_r_n.py1
-rw-r--r--contrib/python/fonttools/fontTools/ttLib/tables/_l_o_c_a.py1
-rw-r--r--contrib/python/fonttools/fontTools/ttLib/tables/_m_a_x_p.py1
-rw-r--r--contrib/python/fonttools/fontTools/ttLib/tables/_v_h_e_a.py1
-rw-r--r--contrib/python/fonttools/fontTools/ttLib/tables/_v_m_t_x.py1
-rw-r--r--contrib/python/fonttools/fontTools/ttLib/tables/otTables.py1
-rw-r--r--contrib/python/fonttools/fontTools/ttLib/ttCollection.py1
-rw-r--r--contrib/python/fonttools/fontTools/ttLib/ttFont.py1
-rw-r--r--contrib/python/fonttools/fontTools/ttLib/ttGlyphSet.py4
-rw-r--r--contrib/python/fonttools/fontTools/ttLib/woff2.py3
-rw-r--r--contrib/python/fonttools/fontTools/ttx.py1
-rw-r--r--contrib/python/fonttools/fontTools/varLib/instancer/__init__.py87
-rw-r--r--contrib/python/fonttools/fontTools/varLib/instancer/names.py10
-rw-r--r--contrib/python/fonttools/fontTools/varLib/instancer/solver.py8
-rw-r--r--contrib/python/fonttools/fontTools/varLib/interpolatable.py442
-rw-r--r--contrib/python/fonttools/fontTools/varLib/models.py1
-rw-r--r--contrib/python/fonttools/fontTools/varLib/varStore.py46
-rw-r--r--contrib/python/fonttools/ya.make2
68 files changed, 506 insertions, 364 deletions
diff --git a/contrib/python/fonttools/.dist-info/METADATA b/contrib/python/fonttools/.dist-info/METADATA
index b6ed83e9b0..996d69838a 100644
--- a/contrib/python/fonttools/.dist-info/METADATA
+++ b/contrib/python/fonttools/.dist-info/METADATA
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: fonttools
-Version: 4.43.1
+Version: 4.44.0
Summary: Tools to manipulate font files
Home-page: http://github.com/fonttools/fonttools
Author: Just van Rossum
@@ -42,7 +42,7 @@ Requires-Dist: brotlicffi >=0.8.0 ; (platform_python_implementation != "CPython"
Requires-Dist: scipy ; (platform_python_implementation != "PyPy") and extra == 'all'
Requires-Dist: brotli >=1.0.1 ; (platform_python_implementation == "CPython") and extra == 'all'
Requires-Dist: munkres ; (platform_python_implementation == "PyPy") and extra == 'all'
-Requires-Dist: unicodedata2 >=15.0.0 ; (python_version <= "3.11") and extra == 'all'
+Requires-Dist: unicodedata2 >=15.1.0 ; (python_version <= "3.12") and extra == 'all'
Requires-Dist: xattr ; (sys_platform == "darwin") and extra == 'all'
Provides-Extra: graphite
Requires-Dist: lz4 >=1.7.4.2 ; extra == 'graphite'
@@ -64,7 +64,7 @@ Requires-Dist: xattr ; (sys_platform == "darwin") and extra == 'type1'
Provides-Extra: ufo
Requires-Dist: fs <3,>=2.2.0 ; extra == 'ufo'
Provides-Extra: unicode
-Requires-Dist: unicodedata2 >=15.0.0 ; (python_version <= "3.11") and extra == 'unicode'
+Requires-Dist: unicodedata2 >=15.1.0 ; (python_version <= "3.12") and extra == 'unicode'
Provides-Extra: woff
Requires-Dist: zopfli >=0.1.4 ; extra == 'woff'
Requires-Dist: brotlicffi >=0.8.0 ; (platform_python_implementation != "CPython") and extra == 'woff'
@@ -366,6 +366,27 @@ Have fun!
Changelog
~~~~~~~~~
+4.44.0 (released 2023-11-03)
+----------------------------
+
+- [instancer] Recalc OS/2 AvgCharWidth after instancing if default changes (#3317).
+- [otlLib] Make ClassDefBuilder class order match varLib.merger's, i.e. large
+ classes first, then glyph lexicographic order (#3321, #3324).
+- [instancer] Allow not specifying any of min:default:max values and let be filled
+ up with fvar's values (#3322, #3323).
+- [instancer] When running --update-name-table ignore axes that have no STAT axis
+ values (#3318, #3319).
+- [Debg] When dumping to ttx, write the embedded JSON as multi-line string with
+ indentation (92cbfee0d).
+- [varStore] Handle > 65535 items per encoding by splitting VarData subtable (#3310).
+- [subset] Handle null-offsets in MarkLigPos subtables.
+- [subset] Keep East Asian spacing fatures vhal, halt, chws, vchw by default (#3305).
+- [instancer.solver] Fixed case where axisDef < lower and upper < axisMax (#3304).
+- [glyf] Speed up compilation, mostly around ``recalcBounds`` (#3301).
+- [varLib.interpolatable] Speed it up when working on variable fonts, plus various
+ micro-optimizations (#3300).
+- Require unicodedata2 >= 15.1.0 when installed with 'unicode' extra, contains UCD 15.1.
+
4.43.1 (released 2023-10-06)
----------------------------
diff --git a/contrib/python/fonttools/fontTools/__init__.py b/contrib/python/fonttools/fontTools/__init__.py
index 765000c5dc..9a59504e44 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.43.1"
+version = __version__ = "4.44.0"
__all__ = ["version", "log", "configLogger"]
diff --git a/contrib/python/fonttools/fontTools/afmLib.py b/contrib/python/fonttools/fontTools/afmLib.py
index 394b901ff5..935a1e8e0d 100644
--- a/contrib/python/fonttools/fontTools/afmLib.py
+++ b/contrib/python/fonttools/fontTools/afmLib.py
@@ -119,7 +119,6 @@ class error(Exception):
class AFM(object):
-
_attrs = None
_keywords = [
diff --git a/contrib/python/fonttools/fontTools/cffLib/__init__.py b/contrib/python/fonttools/fontTools/cffLib/__init__.py
index b5b859fc50..644508c155 100644
--- a/contrib/python/fonttools/fontTools/cffLib/__init__.py
+++ b/contrib/python/fonttools/fontTools/cffLib/__init__.py
@@ -1000,7 +1000,6 @@ class TopDictIndex(Index):
class FDArrayIndex(Index):
-
compilerClass = FDArrayIndexCompiler
def toXML(self, xmlWriter):
@@ -2483,7 +2482,6 @@ def encodeNumber(num):
class TopDictCompiler(DictCompiler):
-
opcodes = buildOpcodeDict(topDictOperators)
def getChildren(self, strings):
@@ -2590,7 +2588,6 @@ class FontDictCompiler(DictCompiler):
class PrivateDictCompiler(DictCompiler):
-
maxBlendStack = maxStackLimit
opcodes = buildOpcodeDict(privateDictOperators)
diff --git a/contrib/python/fonttools/fontTools/cffLib/specializer.py b/contrib/python/fonttools/fontTools/cffLib/specializer.py
index 3d28c82dc7..efc15af704 100644
--- a/contrib/python/fonttools/fontTools/cffLib/specializer.py
+++ b/contrib/python/fonttools/fontTools/cffLib/specializer.py
@@ -41,7 +41,7 @@ def programToCommands(program, getNumRegions=None):
Each command is a two-tuple of commandname,arg-list. The commandname might
be empty string if no commandname shall be emitted (used for glyph width,
hintmask/cntrmask argument, as well as stray arguments at the end of the
- program (¯\_(ツ)_/¯).
+ program (🤷).
'getNumRegions' may be None, or a callable object. It must return the
number of regions. 'getNumRegions' takes a single argument, vsindex. If
the vsindex argument is None, getNumRegions returns the default number
@@ -513,7 +513,6 @@ def specializeCommands(
preserveTopology=False,
maxstack=48,
):
-
# We perform several rounds of optimizations. They are carefully ordered and are:
#
# 0. Generalize commands.
diff --git a/contrib/python/fonttools/fontTools/cffLib/width.py b/contrib/python/fonttools/fontTools/cffLib/width.py
index c0a746b692..0ba3ed39bf 100644
--- a/contrib/python/fonttools/fontTools/cffLib/width.py
+++ b/contrib/python/fonttools/fontTools/cffLib/width.py
@@ -22,7 +22,6 @@ class missingdict(dict):
def cumSum(f, op=add, start=0, decreasing=False):
-
keys = sorted(f.keys())
minx, maxx = keys[0], keys[-1]
@@ -46,7 +45,6 @@ def cumSum(f, op=add, start=0, decreasing=False):
def byteCost(widths, default, nominal):
-
if not hasattr(widths, "items"):
d = defaultdict(int)
for w in widths:
diff --git a/contrib/python/fonttools/fontTools/feaLib/parser.py b/contrib/python/fonttools/fontTools/feaLib/parser.py
index 49667f4503..8ffdf644c3 100644
--- a/contrib/python/fonttools/fontTools/feaLib/parser.py
+++ b/contrib/python/fonttools/fontTools/feaLib/parser.py
@@ -45,7 +45,6 @@ class Parser(object):
def __init__(
self, featurefile, glyphNames=(), followIncludes=True, includeDir=None, **kwargs
):
-
if "glyphMap" in kwargs:
from fontTools.misc.loggingTools import deprecateArgument
@@ -1754,7 +1753,8 @@ class Parser(object):
def parse_featureNames_(self, tag):
"""Parses a ``featureNames`` statement found in stylistic set features.
- See section `8.c <https://adobe-type-tools.github.io/afdko/OpenTypeFeatureFileSpecification.html#8.c>`_."""
+ See section `8.c <https://adobe-type-tools.github.io/afdko/OpenTypeFeatureFileSpecification.html#8.c>`_.
+ """
assert self.cur_token_ == "featureNames", self.cur_token_
block = self.ast.NestedBlock(
tag, self.cur_token_, location=self.cur_token_location_
diff --git a/contrib/python/fonttools/fontTools/merge/__init__.py b/contrib/python/fonttools/fontTools/merge/__init__.py
index 10eff133fa..8d8a5213e8 100644
--- a/contrib/python/fonttools/fontTools/merge/__init__.py
+++ b/contrib/python/fonttools/fontTools/merge/__init__.py
@@ -51,7 +51,6 @@ class Merger(object):
"""
def __init__(self, options=None):
-
if not options:
options = Options()
diff --git a/contrib/python/fonttools/fontTools/merge/options.py b/contrib/python/fonttools/fontTools/merge/options.py
index 0c4cfb9988..f134009368 100644
--- a/contrib/python/fonttools/fontTools/merge/options.py
+++ b/contrib/python/fonttools/fontTools/merge/options.py
@@ -8,7 +8,6 @@ class Options(object):
pass
def __init__(self, **kwargs):
-
self.verbose = False
self.timing = False
self.drop_tables = []
diff --git a/contrib/python/fonttools/fontTools/merge/tables.py b/contrib/python/fonttools/fontTools/merge/tables.py
index 394541b8a4..57ed64d337 100644
--- a/contrib/python/fonttools/fontTools/merge/tables.py
+++ b/contrib/python/fonttools/fontTools/merge/tables.py
@@ -210,6 +210,7 @@ ttLib.getTableClass("glyf").mergeMap = {
"tableTag": equal,
"glyphs": sumDicts,
"glyphOrder": sumLists,
+ "_reverseGlyphOrder": recalculate,
"axisTags": equal,
}
diff --git a/contrib/python/fonttools/fontTools/misc/arrayTools.py b/contrib/python/fonttools/fontTools/misc/arrayTools.py
index 5fb01a838a..ced8d87a61 100644
--- a/contrib/python/fonttools/fontTools/misc/arrayTools.py
+++ b/contrib/python/fonttools/fontTools/misc/arrayTools.py
@@ -47,7 +47,7 @@ def updateBounds(bounds, p, min=min, max=max):
Args:
bounds: A bounding rectangle expressed as a tuple
- ``(xMin, yMin, xMax, yMax)``.
+ ``(xMin, yMin, xMax, yMax), or None``.
p: A 2D tuple representing a point.
min,max: functions to compute the minimum and maximum.
@@ -55,6 +55,8 @@ def updateBounds(bounds, p, min=min, max=max):
The updated bounding rectangle ``(xMin, yMin, xMax, yMax)``.
"""
(x, y) = p
+ if bounds is None:
+ return x, y, x, y
xMin, yMin, xMax, yMax = bounds
return min(xMin, x), min(yMin, y), max(xMax, x), max(yMax, y)
diff --git a/contrib/python/fonttools/fontTools/misc/classifyTools.py b/contrib/python/fonttools/fontTools/misc/classifyTools.py
index e46386230e..2235bbd7f8 100644
--- a/contrib/python/fonttools/fontTools/misc/classifyTools.py
+++ b/contrib/python/fonttools/fontTools/misc/classifyTools.py
@@ -9,7 +9,6 @@ class Classifier(object):
"""
def __init__(self, sort=True):
-
self._things = set() # set of all things known so far
self._sets = [] # list of class sets produced so far
self._mapping = {} # map from things to their class set
diff --git a/contrib/python/fonttools/fontTools/misc/dictTools.py b/contrib/python/fonttools/fontTools/misc/dictTools.py
index 259613b270..e3c0df7355 100644
--- a/contrib/python/fonttools/fontTools/misc/dictTools.py
+++ b/contrib/python/fonttools/fontTools/misc/dictTools.py
@@ -3,6 +3,7 @@
__all__ = ["hashdict"]
+
# https://stackoverflow.com/questions/1151658/python-hashable-dicts
class hashdict(dict):
"""
diff --git a/contrib/python/fonttools/fontTools/misc/psCharStrings.py b/contrib/python/fonttools/fontTools/misc/psCharStrings.py
index 0092a98ece..cc9ca01c7f 100644
--- a/contrib/python/fonttools/fontTools/misc/psCharStrings.py
+++ b/contrib/python/fonttools/fontTools/misc/psCharStrings.py
@@ -1092,7 +1092,6 @@ class T1OutlineExtractor(T2OutlineExtractor):
class T2CharString(object):
-
operandEncoding = t2OperandEncoding
operators, opcodes = buildOperatorDict(t2Operators)
decompilerClass = SimpleT2Decompiler
@@ -1313,7 +1312,6 @@ class T2CharString(object):
class T1CharString(T2CharString):
-
operandEncoding = t1OperandEncoding
operators, opcodes = buildOperatorDict(t1Operators)
@@ -1347,7 +1345,6 @@ class T1CharString(T2CharString):
class DictDecompiler(object):
-
operandEncoding = cffDictOperandEncoding
def __init__(self, strings, parent=None):
diff --git a/contrib/python/fonttools/fontTools/misc/psLib.py b/contrib/python/fonttools/fontTools/misc/psLib.py
index 1e0408ce9c..3bfdb4ae9f 100644
--- a/contrib/python/fonttools/fontTools/misc/psLib.py
+++ b/contrib/python/fonttools/fontTools/misc/psLib.py
@@ -100,7 +100,6 @@ class PSTokenizer(object):
commentmatch=commentRE.match,
endmatch=endofthingRE.match,
):
-
self.skipwhite()
if self.pos >= self.len:
return None, None
diff --git a/contrib/python/fonttools/fontTools/misc/psOperators.py b/contrib/python/fonttools/fontTools/misc/psOperators.py
index d0ef432f52..d0b975eab1 100644
--- a/contrib/python/fonttools/fontTools/misc/psOperators.py
+++ b/contrib/python/fonttools/fontTools/misc/psOperators.py
@@ -2,7 +2,6 @@ _accessstrings = {0: "", 1: "readonly", 2: "executeonly", 3: "noaccess"}
class ps_object(object):
-
literal = 1
access = 0
value = None
@@ -16,7 +15,6 @@ class ps_object(object):
class ps_operator(ps_object):
-
literal = 0
def __init__(self, name, function):
diff --git a/contrib/python/fonttools/fontTools/misc/roundTools.py b/contrib/python/fonttools/fontTools/misc/roundTools.py
index 48a47c07c8..a4d45c31b2 100644
--- a/contrib/python/fonttools/fontTools/misc/roundTools.py
+++ b/contrib/python/fonttools/fontTools/misc/roundTools.py
@@ -13,6 +13,7 @@ __all__ = [
"otRound",
"maybeRound",
"roundFunc",
+ "nearestMultipleShortestRepr",
]
diff --git a/contrib/python/fonttools/fontTools/misc/symfont.py b/contrib/python/fonttools/fontTools/misc/symfont.py
index 0bd69a386e..fb9e20a46e 100644
--- a/contrib/python/fonttools/fontTools/misc/symfont.py
+++ b/contrib/python/fonttools/fontTools/misc/symfont.py
@@ -61,7 +61,6 @@ class _BezierFuncsLazy(dict):
class GreenPen(BasePen):
-
_BezierFuncs = {}
@classmethod
@@ -115,7 +114,6 @@ MomentXYPen = partial(GreenPen, func=x * y)
def printGreenPen(penName, funcs, file=sys.stdout, docstring=None):
-
if docstring is not None:
print('"""%s"""' % docstring)
@@ -167,7 +165,6 @@ class %s(BasePen):
)
for n in (1, 2, 3):
-
subs = {P[i][j]: [X, Y][j][i] for i in range(n + 1) for j in range(2)}
greens = [green(f, BezierCurve[n]) for name, f in funcs]
greens = [sp.gcd_terms(f.collect(sum(P, ()))) for f in greens] # Optimize
diff --git a/contrib/python/fonttools/fontTools/misc/visitor.py b/contrib/python/fonttools/fontTools/misc/visitor.py
index 3d28135fad..d289895467 100644
--- a/contrib/python/fonttools/fontTools/misc/visitor.py
+++ b/contrib/python/fonttools/fontTools/misc/visitor.py
@@ -4,7 +4,6 @@ import enum
class Visitor(object):
-
defaultStop = False
@classmethod
@@ -58,7 +57,6 @@ class Visitor(object):
typ = type(thing)
for celf in celf.mro():
-
_visitors = getattr(celf, "_visitors", None)
if _visitors is None:
break
diff --git a/contrib/python/fonttools/fontTools/otlLib/builder.py b/contrib/python/fonttools/fontTools/otlLib/builder.py
index 94628bff11..3508a7e28d 100644
--- a/contrib/python/fonttools/fontTools/otlLib/builder.py
+++ b/contrib/python/fonttools/fontTools/otlLib/builder.py
@@ -2674,7 +2674,7 @@ class ClassDefBuilder(object):
# class form a contiguous range, the encoding is actually quite
# compact, whereas a non-contiguous set might need a lot of bytes
# in the output file. We don't get this right with the key below.
- result = sorted(self.classes_, key=lambda s: (len(s), s), reverse=True)
+ result = sorted(self.classes_, key=lambda s: (-len(s), s))
if not self.useClass0_:
result.insert(0, frozenset())
return result
diff --git a/contrib/python/fonttools/fontTools/otlLib/optimize/gpos.py b/contrib/python/fonttools/fontTools/otlLib/optimize/gpos.py
index 0acd9ed04c..01c2257cd7 100644
--- a/contrib/python/fonttools/fontTools/otlLib/optimize/gpos.py
+++ b/contrib/python/fonttools/fontTools/otlLib/optimize/gpos.py
@@ -135,6 +135,7 @@ Pairs = Dict[
Tuple[otBase.ValueRecord, otBase.ValueRecord],
]
+
# Adapted from https://github.com/fonttools/fonttools/blob/f64f0b42f2d1163b2d85194e0979def539f5dca3/Lib/fontTools/ttLib/tables/otTables.py#L935-L958
def _getClassRanges(glyphIDs: Iterable[int]):
glyphIDs = sorted(glyphIDs)
@@ -274,7 +275,7 @@ class Cluster:
)
merged_range_count = 0
last = None
- for (start, end) in ranges:
+ for start, end in ranges:
if last is not None and start != last + 1:
merged_range_count += 1
last = end
diff --git a/contrib/python/fonttools/fontTools/pens/cu2quPen.py b/contrib/python/fonttools/fontTools/pens/cu2quPen.py
index f182aed44a..5730b325cf 100644
--- a/contrib/python/fonttools/fontTools/pens/cu2quPen.py
+++ b/contrib/python/fonttools/fontTools/pens/cu2quPen.py
@@ -199,7 +199,7 @@ class Cu2QuPointPen(BasePointToSegmentPen):
# will be appended at the end of the contour
last_offcurves = offcurves
else:
- for (pt, smooth, name, kwargs) in offcurves:
+ for pt, smooth, name, kwargs in offcurves:
pen.addPoint(pt, None, smooth, name, **kwargs)
pt, smooth, name, kwargs = points[-1]
if pt is None:
@@ -212,7 +212,7 @@ class Cu2QuPointPen(BasePointToSegmentPen):
pen.addPoint(pt, segment_type, smooth, name, **kwargs)
else:
raise AssertionError("unexpected segment type: %r" % segment_type)
- for (pt, smooth, name, kwargs) in last_offcurves:
+ for pt, smooth, name, kwargs in last_offcurves:
pen.addPoint(pt, None, smooth, name, **kwargs)
pen.endPath()
diff --git a/contrib/python/fonttools/fontTools/pens/statisticsPen.py b/contrib/python/fonttools/fontTools/pens/statisticsPen.py
index 8e00aa44da..39f319e02d 100644
--- a/contrib/python/fonttools/fontTools/pens/statisticsPen.py
+++ b/contrib/python/fonttools/fontTools/pens/statisticsPen.py
@@ -85,11 +85,13 @@ def _test(glyphset, upem, glyphs, quiet=False):
transformer = TransformPen(pen, Scale(1.0 / upem))
glyph.draw(transformer)
- wght_sum += abs(pen.area)
- wght_sum_perceptual += abs(pen.area) * glyph.width
- wdth_sum += glyph.width
+ area = abs(pen.area)
+ width = glyph.width
+ wght_sum += area
+ wght_sum_perceptual += pen.area * width
+ wdth_sum += width
slnt_sum += pen.slant
- slnt_sum_perceptual += pen.slant * glyph.width
+ slnt_sum_perceptual += pen.slant * width
if quiet:
continue
diff --git a/contrib/python/fonttools/fontTools/subset/__init__.py b/contrib/python/fonttools/fontTools/subset/__init__.py
index 4b9cb00f60..bd826ed224 100644
--- a/contrib/python/fonttools/fontTools/subset/__init__.py
+++ b/contrib/python/fonttools/fontTools/subset/__init__.py
@@ -870,6 +870,8 @@ def subset_glyphs(self, s):
for m in self.MarkArray.MarkRecord:
m.Class = class_indices.index(m.Class)
for l in self.LigatureArray.LigatureAttach:
+ if l is None:
+ continue
for c in l.ComponentRecord:
c.LigatureAnchor = _list_subset(c.LigatureAnchor, class_indices)
return bool(
@@ -888,6 +890,8 @@ def prune_post_subset(self, font, options):
if m.MarkAnchor:
m.MarkAnchor.prune_hints()
for l in self.LigatureArray.LigatureAttach:
+ if l is None:
+ continue
for c in l.ComponentRecord:
for a in c.LigatureAnchor:
if a:
@@ -3004,6 +3008,7 @@ class Options(object):
"rand": ["rand"],
"justify": ["jalt"],
"private": ["Harf", "HARF", "Buzz", "BUZZ"],
+ "east_asian_spacing": ["chws", "vchw", "halt", "vhal"],
# Complex shapers
"arabic": [
"init",
@@ -3666,7 +3671,10 @@ def main(args=None):
)
if outfile is None:
- outfile = makeOutputFileName(fontfile, overWrite=True, suffix=".subset")
+ ext = "." + options.flavor.lower() if options.flavor is not None else None
+ outfile = makeOutputFileName(
+ fontfile, extension=ext, overWrite=True, suffix=".subset"
+ )
with timer("compile glyph list"):
if wildcard_glyphs:
diff --git a/contrib/python/fonttools/fontTools/subset/svg.py b/contrib/python/fonttools/fontTools/subset/svg.py
index 9daac6470c..329c68fb0a 100644
--- a/contrib/python/fonttools/fontTools/subset/svg.py
+++ b/contrib/python/fonttools/fontTools/subset/svg.py
@@ -204,7 +204,6 @@ def subset_glyphs(self, s) -> bool:
new_docs: List[SVGDocument] = []
for doc in self.docList:
-
glyphs = {
glyph_order[i] for i in range(doc.startGlyphID, doc.endGlyphID + 1)
}.intersection(s.glyphs)
diff --git a/contrib/python/fonttools/fontTools/svgLib/path/parser.py b/contrib/python/fonttools/fontTools/svgLib/path/parser.py
index 70ae4c17ea..fa53474558 100644
--- a/contrib/python/fonttools/fontTools/svgLib/path/parser.py
+++ b/contrib/python/fonttools/fontTools/svgLib/path/parser.py
@@ -125,7 +125,6 @@ def parse_path(pathdef, pen, current_pos=(0, 0), arc_class=EllipticalArc):
have_arcTo = hasattr(pen, "arcTo")
while elements:
-
if elements[-1] in COMMANDS:
# New command.
last_command = command # Used by S and T
diff --git a/contrib/python/fonttools/fontTools/ttLib/__main__.py b/contrib/python/fonttools/fontTools/ttLib/__main__.py
index d9b2a465d7..2733444d8b 100644
--- a/contrib/python/fonttools/fontTools/ttLib/__main__.py
+++ b/contrib/python/fonttools/fontTools/ttLib/__main__.py
@@ -51,6 +51,9 @@ def main(args=None):
)
parser.add_argument("font", metavar="font", nargs="*", help="Font file.")
parser.add_argument(
+ "-t", "--table", metavar="table", nargs="*", help="Tables to decompile."
+ )
+ parser.add_argument(
"-o", "--output", metavar="FILE", default=None, help="Output file."
)
parser.add_argument(
@@ -74,6 +77,7 @@ def main(args=None):
outFile = options.output
lazy = options.lazy
flavor = options.flavor
+ tables = options.table if options.table is not None else []
fonts = []
for f in options.font:
@@ -84,6 +88,10 @@ def main(args=None):
collection = TTCollection(f, lazy=lazy)
fonts.extend(collection.fonts)
+ for font in fonts:
+ for table in tables if "*" not in tables else font.keys():
+ font[table] # Decompiles
+
if outFile is not None:
if len(fonts) == 1:
fonts[0].flavor = flavor
diff --git a/contrib/python/fonttools/fontTools/ttLib/scaleUpem.py b/contrib/python/fonttools/fontTools/ttLib/scaleUpem.py
index 7018f27a7c..3f9b22af8f 100644
--- a/contrib/python/fonttools/fontTools/ttLib/scaleUpem.py
+++ b/contrib/python/fonttools/fontTools/ttLib/scaleUpem.py
@@ -139,7 +139,6 @@ def visit(visitor, obj, attr, glyphs):
@ScalerVisitor.register_attr(ttLib.getTableClass("gvar"), "variations")
def visit(visitor, obj, attr, variations):
-
# VarComposites are a pain to handle :-(
glyfTable = visitor.font["glyf"]
diff --git a/contrib/python/fonttools/fontTools/ttLib/sfnt.py b/contrib/python/fonttools/fontTools/ttLib/sfnt.py
index 354fb85ea2..b1569423c4 100644
--- a/contrib/python/fonttools/fontTools/ttLib/sfnt.py
+++ b/contrib/python/fonttools/fontTools/ttLib/sfnt.py
@@ -524,13 +524,11 @@ class DirectoryEntry(object):
class SFNTDirectoryEntry(DirectoryEntry):
-
format = sfntDirectoryEntryFormat
formatSize = sfntDirectoryEntrySize
class WOFFDirectoryEntry(DirectoryEntry):
-
format = woffDirectoryEntryFormat
formatSize = woffDirectoryEntrySize
@@ -571,7 +569,6 @@ class WOFFDirectoryEntry(DirectoryEntry):
class WOFFFlavorData:
-
Flavor = "woff"
def __init__(self, reader=None):
diff --git a/contrib/python/fonttools/fontTools/ttLib/tables/C_B_D_T_.py b/contrib/python/fonttools/fontTools/ttLib/tables/C_B_D_T_.py
index e9e2d5fde9..2b87ac8628 100644
--- a/contrib/python/fonttools/fontTools/ttLib/tables/C_B_D_T_.py
+++ b/contrib/python/fonttools/fontTools/ttLib/tables/C_B_D_T_.py
@@ -21,7 +21,6 @@ import struct
class table_C_B_D_T_(E_B_D_T_.table_E_B_D_T_):
-
# Change the data locator table being referenced.
locatorName = "CBLC"
@@ -42,7 +41,6 @@ def _removeUnsupportedForColor(dataFunctions):
class ColorBitmapGlyph(BitmapGlyph):
-
fileExtension = ".png"
xmlDataFunctions = _removeUnsupportedForColor(BitmapGlyph.xmlDataFunctions)
diff --git a/contrib/python/fonttools/fontTools/ttLib/tables/C_B_L_C_.py b/contrib/python/fonttools/fontTools/ttLib/tables/C_B_L_C_.py
index e9ed58e582..fc3974ece0 100644
--- a/contrib/python/fonttools/fontTools/ttLib/tables/C_B_L_C_.py
+++ b/contrib/python/fonttools/fontTools/ttLib/tables/C_B_L_C_.py
@@ -6,5 +6,4 @@ from . import E_B_L_C_
class table_C_B_L_C_(E_B_L_C_.table_E_B_L_C_):
-
dependencies = ["CBDT"]
diff --git a/contrib/python/fonttools/fontTools/ttLib/tables/C_O_L_R_.py b/contrib/python/fonttools/fontTools/ttLib/tables/C_O_L_R_.py
index b4bc5d0c20..2f03ec054f 100644
--- a/contrib/python/fonttools/fontTools/ttLib/tables/C_O_L_R_.py
+++ b/contrib/python/fonttools/fontTools/ttLib/tables/C_O_L_R_.py
@@ -151,7 +151,7 @@ class LayerRecord(object):
writer.newline()
def fromXML(self, eltname, attrs, content, ttFont):
- for (name, value) in attrs.items():
+ for name, value in attrs.items():
if name == "name":
setattr(self, name, value)
else:
diff --git a/contrib/python/fonttools/fontTools/ttLib/tables/C_P_A_L_.py b/contrib/python/fonttools/fontTools/ttLib/tables/C_P_A_L_.py
index 03eb851e8c..9fb2074afc 100644
--- a/contrib/python/fonttools/fontTools/ttLib/tables/C_P_A_L_.py
+++ b/contrib/python/fonttools/fontTools/ttLib/tables/C_P_A_L_.py
@@ -11,7 +11,6 @@ import sys
class table_C_P_A_L_(DefaultTable.DefaultTable):
-
NO_NAME_ID = 0xFFFF
DEFAULT_PALETTE_TYPE = 0
diff --git a/contrib/python/fonttools/fontTools/ttLib/tables/D__e_b_g.py b/contrib/python/fonttools/fontTools/ttLib/tables/D__e_b_g.py
index ff64a9b519..54449a5fd6 100644
--- a/contrib/python/fonttools/fontTools/ttLib/tables/D__e_b_g.py
+++ b/contrib/python/fonttools/fontTools/ttLib/tables/D__e_b_g.py
@@ -11,7 +11,7 @@ class table_D__e_b_g(DefaultTable.DefaultTable):
return json.dumps(self.data).encode("utf-8")
def toXML(self, writer, ttFont):
- writer.writecdata(json.dumps(self.data))
+ writer.writecdata(json.dumps(self.data, indent=2))
def fromXML(self, name, attrs, content, ttFont):
self.data = json.loads(content)
diff --git a/contrib/python/fonttools/fontTools/ttLib/tables/DefaultTable.py b/contrib/python/fonttools/fontTools/ttLib/tables/DefaultTable.py
index 32a4b1f258..92f2aa6523 100644
--- a/contrib/python/fonttools/fontTools/ttLib/tables/DefaultTable.py
+++ b/contrib/python/fonttools/fontTools/ttLib/tables/DefaultTable.py
@@ -3,7 +3,6 @@ from fontTools.ttLib import getClassTag
class DefaultTable(object):
-
dependencies = []
def __init__(self, tag=None):
diff --git a/contrib/python/fonttools/fontTools/ttLib/tables/E_B_D_T_.py b/contrib/python/fonttools/fontTools/ttLib/tables/E_B_D_T_.py
index 535f98fd80..9f7f82efd5 100644
--- a/contrib/python/fonttools/fontTools/ttLib/tables/E_B_D_T_.py
+++ b/contrib/python/fonttools/fontTools/ttLib/tables/E_B_D_T_.py
@@ -38,7 +38,6 @@ ebdtComponentFormat = """
class table_E_B_D_T_(DefaultTable.DefaultTable):
-
# Keep a reference to the name of the data locator table.
locatorName = "EBLC"
@@ -84,7 +83,6 @@ class table_E_B_D_T_(DefaultTable.DefaultTable):
bitmapGlyphDict[curName] = curGlyph
def compile(self, ttFont):
-
dataList = []
dataList.append(sstruct.pack(ebdtTableVersionFormat, self))
dataSize = len(dataList[0])
@@ -428,7 +426,6 @@ _bitmapGlyphSubclassPrefix = "ebdt_bitmap_format_"
class BitmapGlyph(object):
-
# For the external file format. This can be changed in subclasses. This way
# when the extfile option is turned on files have the form: glyphName.ext
# The default is just a flat binary file with no meaning.
@@ -555,6 +552,7 @@ def _createBitmapPlusMetricsMixin(metricsClass):
BitmapPlusBigMetricsMixin = _createBitmapPlusMetricsMixin(BigGlyphMetrics)
BitmapPlusSmallMetricsMixin = _createBitmapPlusMetricsMixin(SmallGlyphMetrics)
+
# Data that is bit aligned can be tricky to deal with. These classes implement
# helper functionality for dealing with the data and getting a particular row
# of bitwise data. Also helps implement fancy data export/import in XML.
diff --git a/contrib/python/fonttools/fontTools/ttLib/tables/E_B_L_C_.py b/contrib/python/fonttools/fontTools/ttLib/tables/E_B_L_C_.py
index 9cc60ff82d..6046d9100b 100644
--- a/contrib/python/fonttools/fontTools/ttLib/tables/E_B_L_C_.py
+++ b/contrib/python/fonttools/fontTools/ttLib/tables/E_B_L_C_.py
@@ -66,7 +66,6 @@ codeOffsetPairSize = struct.calcsize(codeOffsetPairFormat)
class table_E_B_L_C_(DefaultTable.DefaultTable):
-
dependencies = ["EBDT"]
# This method can be overridden in subclasses to support new formats
@@ -76,7 +75,6 @@ class table_E_B_L_C_(DefaultTable.DefaultTable):
return eblc_sub_table_classes[indexFormat]
def decompile(self, data, ttFont):
-
# Save the original data because offsets are from the start of the table.
origData = data
i = 0
@@ -138,7 +136,6 @@ class table_E_B_L_C_(DefaultTable.DefaultTable):
curStrike.indexSubTables.append(indexSubTable)
def compile(self, ttFont):
-
dataList = []
self.numSizes = len(self.strikes)
dataList.append(sstruct.pack(eblcHeaderFormat, self))
@@ -297,7 +294,6 @@ class Strike(object):
class BitmapSizeTable(object):
-
# Returns all the simple metric names that bitmap size table
# cares about in terms of XML creation.
def _getXMLMetricNames(self):
@@ -476,14 +472,12 @@ class EblcIndexSubTable(object):
# are very similar. The only difference between them is the size per offset
# value. Code put in here should handle both cases generally.
def _createOffsetArrayIndexSubTableMixin(formatStringForDataType):
-
# Prep the data size for the offset array data format.
dataFormat = ">" + formatStringForDataType
offsetDataSize = struct.calcsize(dataFormat)
class OffsetArrayIndexSubTableMixin(object):
def decompile(self):
-
numGlyphs = self.lastGlyphIndex - self.firstGlyphIndex + 1
indexingOffsets = [
glyphIndex * offsetDataSize for glyphIndex in range(numGlyphs + 2)
@@ -625,7 +619,6 @@ class eblc_index_sub_table_3(
class eblc_index_sub_table_4(EblcIndexSubTable):
def decompile(self):
-
(numGlyphs,) = struct.unpack(">L", self.data[:4])
data = self.data[4:]
indexingOffsets = [
diff --git a/contrib/python/fonttools/fontTools/ttLib/tables/G_M_A_P_.py b/contrib/python/fonttools/fontTools/ttLib/tables/G_M_A_P_.py
index 39b0050c5f..949ef842ef 100644
--- a/contrib/python/fonttools/fontTools/ttLib/tables/G_M_A_P_.py
+++ b/contrib/python/fonttools/fontTools/ttLib/tables/G_M_A_P_.py
@@ -81,7 +81,6 @@ class GMAPRecord(object):
class table_G_M_A_P_(DefaultTable.DefaultTable):
-
dependencies = []
def decompile(self, data, ttFont):
diff --git a/contrib/python/fonttools/fontTools/ttLib/tables/M_E_T_A_.py b/contrib/python/fonttools/fontTools/ttLib/tables/M_E_T_A_.py
index 6631e2f30c..445aeb4dea 100644
--- a/contrib/python/fonttools/fontTools/ttLib/tables/M_E_T_A_.py
+++ b/contrib/python/fonttools/fontTools/ttLib/tables/M_E_T_A_.py
@@ -68,7 +68,6 @@ def getLabelString(labelID):
class table_M_E_T_A_(DefaultTable.DefaultTable):
-
dependencies = []
def decompile(self, data, ttFont):
diff --git a/contrib/python/fonttools/fontTools/ttLib/tables/S_I_N_G_.py b/contrib/python/fonttools/fontTools/ttLib/tables/S_I_N_G_.py
index 7420da7e5d..4522c06c6b 100644
--- a/contrib/python/fonttools/fontTools/ttLib/tables/S_I_N_G_.py
+++ b/contrib/python/fonttools/fontTools/ttLib/tables/S_I_N_G_.py
@@ -20,7 +20,6 @@ SINGFormat = """
class table_S_I_N_G_(DefaultTable.DefaultTable):
-
dependencies = []
def decompile(self, data, ttFont):
diff --git a/contrib/python/fonttools/fontTools/ttLib/tables/T_S_I__0.py b/contrib/python/fonttools/fontTools/ttLib/tables/T_S_I__0.py
index 4112937d45..f15fc67bce 100644
--- a/contrib/python/fonttools/fontTools/ttLib/tables/T_S_I__0.py
+++ b/contrib/python/fonttools/fontTools/ttLib/tables/T_S_I__0.py
@@ -16,7 +16,6 @@ def fixlongs(glyphID, textLength, textOffset):
class table_T_S_I__0(DefaultTable.DefaultTable):
-
dependencies = ["TSI1"]
def decompile(self, data, ttFont):
diff --git a/contrib/python/fonttools/fontTools/ttLib/tables/T_S_I__1.py b/contrib/python/fonttools/fontTools/ttLib/tables/T_S_I__1.py
index 57163d726c..55aca33991 100644
--- a/contrib/python/fonttools/fontTools/ttLib/tables/T_S_I__1.py
+++ b/contrib/python/fonttools/fontTools/ttLib/tables/T_S_I__1.py
@@ -10,7 +10,6 @@ from fontTools.misc.textTools import strjoin, tobytes, tostr
class table_T_S_I__1(LogMixin, DefaultTable.DefaultTable):
-
extras = {0xFFFA: "ppgm", 0xFFFB: "cvt", 0xFFFC: "reserved", 0xFFFD: "fpgm"}
indextable = "TSI0"
diff --git a/contrib/python/fonttools/fontTools/ttLib/tables/T_S_I__2.py b/contrib/python/fonttools/fontTools/ttLib/tables/T_S_I__2.py
index 43a17f6f1f..4278be1556 100644
--- a/contrib/python/fonttools/fontTools/ttLib/tables/T_S_I__2.py
+++ b/contrib/python/fonttools/fontTools/ttLib/tables/T_S_I__2.py
@@ -11,5 +11,4 @@ superclass = ttLib.getTableClass("TSI0")
class table_T_S_I__2(superclass):
-
dependencies = ["TSI3"]
diff --git a/contrib/python/fonttools/fontTools/ttLib/tables/T_S_I__3.py b/contrib/python/fonttools/fontTools/ttLib/tables/T_S_I__3.py
index 8ef3c5ade2..785ca23152 100644
--- a/contrib/python/fonttools/fontTools/ttLib/tables/T_S_I__3.py
+++ b/contrib/python/fonttools/fontTools/ttLib/tables/T_S_I__3.py
@@ -9,7 +9,6 @@ superclass = ttLib.getTableClass("TSI1")
class table_T_S_I__3(superclass):
-
extras = {
0xFFFA: "reserved0",
0xFFFB: "reserved1",
diff --git a/contrib/python/fonttools/fontTools/ttLib/tables/TupleVariation.py b/contrib/python/fonttools/fontTools/ttLib/tables/TupleVariation.py
index 13ff867874..30d009906d 100644
--- a/contrib/python/fonttools/fontTools/ttLib/tables/TupleVariation.py
+++ b/contrib/python/fonttools/fontTools/ttLib/tables/TupleVariation.py
@@ -166,15 +166,15 @@ class TupleVariation(object):
return b"".join(tupleData), auxData
def compileCoord(self, axisTags):
- result = bytearray()
+ result = []
axes = self.axes
for axis in axisTags:
triple = axes.get(axis)
if triple is None:
- result.extend(b"\0\0")
+ result.append(b"\0\0")
else:
- result.extend(struct.pack(">h", fl2fi(triple[1], 14)))
- return bytes(result)
+ result.append(struct.pack(">h", fl2fi(triple[1], 14)))
+ return b"".join(result)
def compileIntermediateCoord(self, axisTags):
needed = False
@@ -187,13 +187,13 @@ class TupleVariation(object):
break
if not needed:
return None
- minCoords = bytearray()
- maxCoords = bytearray()
+ minCoords = []
+ maxCoords = []
for axis in axisTags:
minValue, value, maxValue = self.axes.get(axis, (0.0, 0.0, 0.0))
- minCoords.extend(struct.pack(">h", fl2fi(minValue, 14)))
- maxCoords.extend(struct.pack(">h", fl2fi(maxValue, 14)))
- return minCoords + maxCoords
+ minCoords.append(struct.pack(">h", fl2fi(minValue, 14)))
+ maxCoords.append(struct.pack(">h", fl2fi(maxValue, 14)))
+ return b"".join(minCoords + maxCoords)
@staticmethod
def decompileCoord_(axisTags, data, offset):
@@ -802,7 +802,7 @@ def inferRegion_(peak):
intermediateEndTuple fields.
"""
start, end = {}, {}
- for (axis, value) in peak.items():
+ for axis, value in peak.items():
start[axis] = min(value, 0.0) # -0.3 --> -0.3; 0.7 --> 0.0
end[axis] = max(value, 0.0) # -0.3 --> 0.0; 0.7 --> 0.7
return (start, end)
diff --git a/contrib/python/fonttools/fontTools/ttLib/tables/_c_m_a_p.py b/contrib/python/fonttools/fontTools/ttLib/tables/_c_m_a_p.py
index 6c00aaf63d..484c331cb7 100644
--- a/contrib/python/fonttools/fontTools/ttLib/tables/_c_m_a_p.py
+++ b/contrib/python/fonttools/fontTools/ttLib/tables/_c_m_a_p.py
@@ -1291,7 +1291,6 @@ class cmap_format_12_or_13(CmapSubtable):
class cmap_format_12(cmap_format_12_or_13):
-
_format_step = 1
def __init__(self, format=12):
@@ -1305,7 +1304,6 @@ class cmap_format_12(cmap_format_12_or_13):
class cmap_format_13(cmap_format_12_or_13):
-
_format_step = 0
def __init__(self, format=13):
diff --git a/contrib/python/fonttools/fontTools/ttLib/tables/_g_l_y_f.py b/contrib/python/fonttools/fontTools/ttLib/tables/_g_l_y_f.py
index 341c4ed646..bff0d92c32 100644
--- a/contrib/python/fonttools/fontTools/ttLib/tables/_g_l_y_f.py
+++ b/contrib/python/fonttools/fontTools/ttLib/tables/_g_l_y_f.py
@@ -6,7 +6,7 @@ from fontTools import ttLib
from fontTools import version
from fontTools.misc.transform import DecomposedTransform
from fontTools.misc.textTools import tostr, safeEval, pad
-from fontTools.misc.arrayTools import calcIntBounds, pointInRect
+from fontTools.misc.arrayTools import updateBounds, pointInRect
from fontTools.misc.bezierTools import calcQuadraticBounds
from fontTools.misc.fixedTools import (
fixedToFloat as fi2fl,
@@ -102,6 +102,7 @@ class table__g_l_y_f(DefaultTable.DefaultTable):
noname = 0
self.glyphs = {}
self.glyphOrder = glyphOrder = ttFont.getGlyphOrder()
+ self._reverseGlyphOrder = {}
for i in range(0, len(loca) - 1):
try:
glyphName = glyphOrder[i]
@@ -144,9 +145,10 @@ class table__g_l_y_f(DefaultTable.DefaultTable):
currentLocation = 0
dataList = []
recalcBBoxes = ttFont.recalcBBoxes
+ boundsDone = set()
for glyphName in self.glyphOrder:
glyph = self.glyphs[glyphName]
- glyphData = glyph.compile(self, recalcBBoxes)
+ glyphData = glyph.compile(self, recalcBBoxes, boundsDone=boundsDone)
if padding > 1:
glyphData = pad(glyphData, size=padding)
locations.append(currentLocation)
@@ -281,6 +283,7 @@ class table__g_l_y_f(DefaultTable.DefaultTable):
glyphOrder ([str]): List of glyph names in order.
"""
self.glyphOrder = glyphOrder
+ self._reverseGlyphOrder = {}
def getGlyphName(self, glyphID):
"""Returns the name for the glyph with the given ID.
@@ -289,13 +292,24 @@ class table__g_l_y_f(DefaultTable.DefaultTable):
"""
return self.glyphOrder[glyphID]
+ def _buildReverseGlyphOrderDict(self):
+ self._reverseGlyphOrder = d = {}
+ for glyphID, glyphName in enumerate(self.glyphOrder):
+ d[glyphName] = glyphID
+
def getGlyphID(self, glyphName):
"""Returns the ID of the glyph with the given name.
Raises a ``ValueError`` if the glyph is not found in the font.
"""
- # XXX optimize with reverse dict!!!
- return self.glyphOrder.index(glyphName)
+ glyphOrder = self.glyphOrder
+ id = getattr(self, "_reverseGlyphOrder", {}).get(glyphName)
+ if id is None or id >= len(glyphOrder) or glyphOrder[id] != glyphName:
+ self._buildReverseGlyphOrderDict()
+ id = self._reverseGlyphOrder.get(glyphName)
+ if id is None:
+ raise ValueError(glyphName)
+ return id
def removeHinting(self):
"""Removes TrueType hints from all glyphs in the glyphset.
@@ -488,7 +502,7 @@ class table__g_l_y_f(DefaultTable.DefaultTable):
assert len(coord) == len(glyph.coordinates)
glyph.coordinates = GlyphCoordinates(coord)
- glyph.recalcBounds(self)
+ glyph.recalcBounds(self, boundsDone=set())
horizontalAdvanceWidth = otRound(rightSideX - leftSideX)
if horizontalAdvanceWidth < 0:
@@ -728,7 +742,7 @@ class Glyph(object):
else:
self.decompileCoordinates(data)
- def compile(self, glyfTable, recalcBBoxes=True):
+ def compile(self, glyfTable, recalcBBoxes=True, *, boundsDone=None):
if hasattr(self, "data"):
if recalcBBoxes:
# must unpack glyph in order to recalculate bounding box
@@ -737,8 +751,10 @@ class Glyph(object):
return self.data
if self.numberOfContours == 0:
return b""
+
if recalcBBoxes:
- self.recalcBounds(glyfTable)
+ self.recalcBounds(glyfTable, boundsDone=boundsDone)
+
data = sstruct.pack(glyphHeaderFormat, self)
if self.isComposite():
data = data + self.compileComponents(glyfTable)
@@ -1148,7 +1164,7 @@ class Glyph(object):
return (compressedFlags, compressedXs, compressedYs)
- def recalcBounds(self, glyfTable):
+ def recalcBounds(self, glyfTable, *, boundsDone=None):
"""Recalculates the bounds of the glyph.
Each glyph object stores its bounding box in the
@@ -1156,12 +1172,55 @@ class Glyph(object):
recomputed when the ``coordinates`` change. The ``table__g_l_y_f`` bounds
must be provided to resolve component bounds.
"""
+ if self.isComposite() and self.tryRecalcBoundsComposite(
+ glyfTable, boundsDone=boundsDone
+ ):
+ return
try:
coords, endPts, flags = self.getCoordinates(glyfTable)
- self.xMin, self.yMin, self.xMax, self.yMax = calcIntBounds(coords)
+ self.xMin, self.yMin, self.xMax, self.yMax = coords.calcIntBounds()
except NotImplementedError:
pass
+ def tryRecalcBoundsComposite(self, glyfTable, *, boundsDone=None):
+ """Try recalculating the bounds of a composite glyph that has
+ certain constrained properties. Namely, none of the components
+ have a transform other than an integer translate, and none
+ uses the anchor points.
+
+ Each glyph object stores its bounding box in the
+ ``xMin``/``yMin``/``xMax``/``yMax`` attributes. These bounds must be
+ recomputed when the ``coordinates`` change. The ``table__g_l_y_f`` bounds
+ must be provided to resolve component bounds.
+
+ Return True if bounds were calculated, False otherwise.
+ """
+ for compo in self.components:
+ if hasattr(compo, "firstPt") or hasattr(compo, "transform"):
+ return False
+ if not float(compo.x).is_integer() or not float(compo.y).is_integer():
+ return False
+
+ # All components are untransformed and have an integer x/y translate
+ bounds = None
+ for compo in self.components:
+ glyphName = compo.glyphName
+ g = glyfTable[glyphName]
+
+ if boundsDone is None or glyphName not in boundsDone:
+ g.recalcBounds(glyfTable, boundsDone=boundsDone)
+ if boundsDone is not None:
+ boundsDone.add(glyphName)
+
+ x, y = compo.x, compo.y
+ bounds = updateBounds(bounds, (g.xMin + x, g.yMin + y))
+ bounds = updateBounds(bounds, (g.xMax + x, g.yMax + y))
+
+ if bounds is None:
+ bounds = (0, 0, 0, 0)
+ self.xMin, self.yMin, self.xMax, self.yMax = bounds
+ return True
+
def isComposite(self):
"""Test whether a glyph has components"""
if hasattr(self, "data"):
@@ -2300,10 +2359,18 @@ class GlyphCoordinates(object):
def __getitem__(self, k):
"""Returns a two element tuple (x,y)"""
+ a = self._a
if isinstance(k, slice):
indices = range(*k.indices(len(self)))
- return [self[i] for i in indices]
- a = self._a
+ # Instead of calling ourselves recursively, duplicate code; faster
+ ret = []
+ for k in indices:
+ x = a[2 * k]
+ y = a[2 * k + 1]
+ ret.append(
+ (int(x) if x.is_integer() else x, int(y) if y.is_integer() else y)
+ )
+ return ret
x = a[2 * k]
y = a[2 * k + 1]
return (int(x) if x.is_integer() else x, int(y) if y.is_integer() else y)
@@ -2341,6 +2408,17 @@ class GlyphCoordinates(object):
for i in range(len(a)):
a[i] = round(a[i])
+ def calcBounds(self):
+ a = self._a
+ if not a:
+ return 0, 0, 0, 0
+ xs = a[0::2]
+ ys = a[1::2]
+ return min(xs), min(ys), max(xs), max(ys)
+
+ def calcIntBounds(self, round=otRound):
+ return tuple(round(v) for v in self.calcBounds())
+
def relativeToAbsolute(self):
a = self._a
x, y = 0, 0
diff --git a/contrib/python/fonttools/fontTools/ttLib/tables/_h_e_a_d.py b/contrib/python/fonttools/fontTools/ttLib/tables/_h_e_a_d.py
index 04505e8250..fe29c8fc4f 100644
--- a/contrib/python/fonttools/fontTools/ttLib/tables/_h_e_a_d.py
+++ b/contrib/python/fonttools/fontTools/ttLib/tables/_h_e_a_d.py
@@ -37,7 +37,6 @@ headFormat = """
class table__h_e_a_d(DefaultTable.DefaultTable):
-
dependencies = ["maxp", "loca", "CFF ", "CFF2"]
def decompile(self, data, ttFont):
diff --git a/contrib/python/fonttools/fontTools/ttLib/tables/_h_h_e_a.py b/contrib/python/fonttools/fontTools/ttLib/tables/_h_h_e_a.py
index 5500cf6a93..43e464f74f 100644
--- a/contrib/python/fonttools/fontTools/ttLib/tables/_h_h_e_a.py
+++ b/contrib/python/fonttools/fontTools/ttLib/tables/_h_h_e_a.py
@@ -31,7 +31,6 @@ hheaFormat = """
class table__h_h_e_a(DefaultTable.DefaultTable):
-
# Note: Keep in sync with table__v_h_e_a
dependencies = ["hmtx", "glyf", "CFF ", "CFF2"]
diff --git a/contrib/python/fonttools/fontTools/ttLib/tables/_h_m_t_x.py b/contrib/python/fonttools/fontTools/ttLib/tables/_h_m_t_x.py
index 2dafe617a0..2dbdd7f985 100644
--- a/contrib/python/fonttools/fontTools/ttLib/tables/_h_m_t_x.py
+++ b/contrib/python/fonttools/fontTools/ttLib/tables/_h_m_t_x.py
@@ -12,7 +12,6 @@ log = logging.getLogger(__name__)
class table__h_m_t_x(DefaultTable.DefaultTable):
-
headerTag = "hhea"
advanceName = "width"
sideBearingName = "lsb"
diff --git a/contrib/python/fonttools/fontTools/ttLib/tables/_k_e_r_n.py b/contrib/python/fonttools/fontTools/ttLib/tables/_k_e_r_n.py
index 94183c8a0a..8f55a311cd 100644
--- a/contrib/python/fonttools/fontTools/ttLib/tables/_k_e_r_n.py
+++ b/contrib/python/fonttools/fontTools/ttLib/tables/_k_e_r_n.py
@@ -101,7 +101,6 @@ class table__k_e_r_n(DefaultTable.DefaultTable):
class KernTable_format_0(object):
-
# 'version' is kept for backward compatibility
version = format = 0
diff --git a/contrib/python/fonttools/fontTools/ttLib/tables/_l_o_c_a.py b/contrib/python/fonttools/fontTools/ttLib/tables/_l_o_c_a.py
index ad1b715133..5884cef45f 100644
--- a/contrib/python/fonttools/fontTools/ttLib/tables/_l_o_c_a.py
+++ b/contrib/python/fonttools/fontTools/ttLib/tables/_l_o_c_a.py
@@ -8,7 +8,6 @@ log = logging.getLogger(__name__)
class table__l_o_c_a(DefaultTable.DefaultTable):
-
dependencies = ["glyf"]
def decompile(self, data, ttFont):
diff --git a/contrib/python/fonttools/fontTools/ttLib/tables/_m_a_x_p.py b/contrib/python/fonttools/fontTools/ttLib/tables/_m_a_x_p.py
index 2934149773..f0e6c33ae3 100644
--- a/contrib/python/fonttools/fontTools/ttLib/tables/_m_a_x_p.py
+++ b/contrib/python/fonttools/fontTools/ttLib/tables/_m_a_x_p.py
@@ -27,7 +27,6 @@ maxpFormat_1_0_add = """
class table__m_a_x_p(DefaultTable.DefaultTable):
-
dependencies = ["glyf"]
def decompile(self, data, ttFont):
diff --git a/contrib/python/fonttools/fontTools/ttLib/tables/_v_h_e_a.py b/contrib/python/fonttools/fontTools/ttLib/tables/_v_h_e_a.py
index 934f2f12d4..de7ce245ad 100644
--- a/contrib/python/fonttools/fontTools/ttLib/tables/_v_h_e_a.py
+++ b/contrib/python/fonttools/fontTools/ttLib/tables/_v_h_e_a.py
@@ -31,7 +31,6 @@ vheaFormat = """
class table__v_h_e_a(DefaultTable.DefaultTable):
-
# Note: Keep in sync with table__h_h_e_a
dependencies = ["vmtx", "glyf", "CFF ", "CFF2"]
diff --git a/contrib/python/fonttools/fontTools/ttLib/tables/_v_m_t_x.py b/contrib/python/fonttools/fontTools/ttLib/tables/_v_m_t_x.py
index c965c94ee5..a13304c321 100644
--- a/contrib/python/fonttools/fontTools/ttLib/tables/_v_m_t_x.py
+++ b/contrib/python/fonttools/fontTools/ttLib/tables/_v_m_t_x.py
@@ -4,7 +4,6 @@ superclass = ttLib.getTableClass("hmtx")
class table__v_m_t_x(superclass):
-
headerTag = "vhea"
advanceName = "height"
sideBearingName = "tsb"
diff --git a/contrib/python/fonttools/fontTools/ttLib/tables/otTables.py b/contrib/python/fonttools/fontTools/ttLib/tables/otTables.py
index 9470873440..262f8d4187 100644
--- a/contrib/python/fonttools/fontTools/ttLib/tables/otTables.py
+++ b/contrib/python/fonttools/fontTools/ttLib/tables/otTables.py
@@ -538,7 +538,6 @@ class FeatureParamsCharacterVariants(FeatureParams):
class Coverage(FormatSwitchingBaseTable):
-
# manual implementation to get rid of glyphID dependencies
def populateDefaults(self, propagator=None):
diff --git a/contrib/python/fonttools/fontTools/ttLib/ttCollection.py b/contrib/python/fonttools/fontTools/ttLib/ttCollection.py
index 3ab579ee00..70ed4b7a0d 100644
--- a/contrib/python/fonttools/fontTools/ttLib/ttCollection.py
+++ b/contrib/python/fonttools/fontTools/ttLib/ttCollection.py
@@ -87,7 +87,6 @@ class TTCollection(object):
file.close()
def saveXML(self, fileOrPath, newlinestr="\n", writeVersion=True, **kwargs):
-
from fontTools.misc import xmlWriter
writer = xmlWriter.XMLWriter(fileOrPath, newlinestr=newlinestr)
diff --git a/contrib/python/fonttools/fontTools/ttLib/ttFont.py b/contrib/python/fonttools/fontTools/ttLib/ttFont.py
index 1bece8e5e4..6a9ca09820 100644
--- a/contrib/python/fonttools/fontTools/ttLib/ttFont.py
+++ b/contrib/python/fonttools/fontTools/ttLib/ttFont.py
@@ -287,7 +287,6 @@ class TTFont(object):
disassembleInstructions=True,
bitmapGlyphDataFormat="raw",
):
-
if quiet is not None:
deprecateArgument("quiet", "configure logging instead")
diff --git a/contrib/python/fonttools/fontTools/ttLib/ttGlyphSet.py b/contrib/python/fonttools/fontTools/ttLib/ttGlyphSet.py
index fa7fbd4f23..d4384c89f6 100644
--- a/contrib/python/fonttools/fontTools/ttLib/ttGlyphSet.py
+++ b/contrib/python/fonttools/fontTools/ttLib/ttGlyphSet.py
@@ -170,7 +170,6 @@ class _TTGlyphGlyf(_TTGlyph):
glyph, offset = self._getGlyphAndOffset()
with self.glyphSet.pushDepth() as depth:
-
if depth:
offset = 0 # Offset should only apply at top-level
@@ -187,7 +186,6 @@ class _TTGlyphGlyf(_TTGlyph):
glyph, offset = self._getGlyphAndOffset()
with self.glyphSet.pushDepth() as depth:
-
if depth:
offset = 0 # Offset should only apply at top-level
@@ -198,14 +196,12 @@ class _TTGlyphGlyf(_TTGlyph):
glyph.drawPoints(pen, self.glyphSet.glyfTable, offset)
def _drawVarComposite(self, glyph, pen, isPointPen):
-
from fontTools.ttLib.tables._g_l_y_f import (
VarComponentFlags,
VAR_COMPONENT_TRANSFORM_MAPPING,
)
for comp in glyph.components:
-
with self.glyphSet.pushLocation(
comp.location, comp.flags & VarComponentFlags.RESET_UNSPECIFIED_AXES
):
diff --git a/contrib/python/fonttools/fontTools/ttLib/woff2.py b/contrib/python/fonttools/fontTools/ttLib/woff2.py
index 3e247b02e9..9da2f7e6d6 100644
--- a/contrib/python/fonttools/fontTools/ttLib/woff2.py
+++ b/contrib/python/fonttools/fontTools/ttLib/woff2.py
@@ -42,7 +42,6 @@ except ImportError:
class WOFF2Reader(SFNTReader):
-
flavor = "woff2"
def __init__(self, file, checkChecksums=0, fontNumber=-1):
@@ -177,7 +176,6 @@ class WOFF2Reader(SFNTReader):
class WOFF2Writer(SFNTWriter):
-
flavor = "woff2"
def __init__(
@@ -1291,7 +1289,6 @@ class WOFF2HmtxTable(getTableClass("hmtx")):
class WOFF2FlavorData(WOFFFlavorData):
-
Flavor = "woff2"
def __init__(self, reader=None, data=None, transformedTables=None):
diff --git a/contrib/python/fonttools/fontTools/ttx.py b/contrib/python/fonttools/fontTools/ttx.py
index 65a3c7a808..d8c2a3a758 100644
--- a/contrib/python/fonttools/fontTools/ttx.py
+++ b/contrib/python/fonttools/fontTools/ttx.py
@@ -124,7 +124,6 @@ opentypeheaderRE = re.compile("""sfntVersion=['"]OTTO["']""")
class Options(object):
-
listTables = False
outputDir = None
outputFile = None
diff --git a/contrib/python/fonttools/fontTools/varLib/instancer/__init__.py b/contrib/python/fonttools/fontTools/varLib/instancer/__init__.py
index a8663ec422..cde1d39f6f 100644
--- a/contrib/python/fonttools/fontTools/varLib/instancer/__init__.py
+++ b/contrib/python/fonttools/fontTools/varLib/instancer/__init__.py
@@ -87,7 +87,7 @@ from fontTools.misc.fixedTools import (
strToFixedToFloat,
otRound,
)
-from fontTools.varLib.models import supportScalar, normalizeValue, piecewiseLinearMap
+from fontTools.varLib.models import normalizeValue, piecewiseLinearMap
from fontTools.ttLib import TTFont
from fontTools.ttLib.tables.TupleVariation import TupleVariation
from fontTools.ttLib.tables import _g_l_y_f
@@ -139,26 +139,36 @@ def NormalizedAxisRange(minimum, maximum):
class AxisTriple(Sequence):
"""A triple of (min, default, max) axis values.
- The default value can be None, in which case the limitRangeAndPopulateDefault()
- method can be used to fill in the missing default value based on the fvar axis
- default.
+ Any of the values can be None, in which case the limitRangeAndPopulateDefaults()
+ method can be used to fill in the missing values based on the fvar axis values.
"""
- minimum: float
- default: Optional[float] # if None, filled with by limitRangeAndPopulateDefault
- maximum: float
+ minimum: Optional[float]
+ default: Optional[float]
+ maximum: Optional[float]
def __post_init__(self):
if self.default is None and self.minimum == self.maximum:
object.__setattr__(self, "default", self.minimum)
- if not (
- (self.minimum <= self.default <= self.maximum)
- if self.default is not None
- else (self.minimum <= self.maximum)
+ if (
+ (
+ self.minimum is not None
+ and self.default is not None
+ and self.minimum > self.default
+ )
+ or (
+ self.default is not None
+ and self.maximum is not None
+ and self.default > self.maximum
+ )
+ or (
+ self.minimum is not None
+ and self.maximum is not None
+ and self.minimum > self.maximum
+ )
):
raise ValueError(
- f"{type(self).__name__} minimum ({self.minimum}) must be <= default "
- f"({self.default}) which must be <= maximum ({self.maximum})"
+ f"{type(self).__name__} minimum ({self.minimum}), default ({self.default}), maximum ({self.maximum}) must be in sorted order"
)
def __getitem__(self, i):
@@ -210,7 +220,7 @@ class AxisTriple(Sequence):
raise ValueError(f"expected sequence of 2 or 3; got {n}: {v!r}")
return cls(minimum, default, maximum)
- def limitRangeAndPopulateDefault(self, fvarTriple) -> "AxisTriple":
+ def limitRangeAndPopulateDefaults(self, fvarTriple) -> "AxisTriple":
"""Return a new AxisTriple with the default value filled in.
Set default to fvar axis default if the latter is within the min/max range,
@@ -219,13 +229,17 @@ class AxisTriple(Sequence):
If the default value is already set, return self.
"""
minimum = self.minimum
- maximum = self.maximum
+ if minimum is None:
+ minimum = fvarTriple[0]
default = self.default
if default is None:
default = fvarTriple[1]
+ maximum = self.maximum
+ if maximum is None:
+ maximum = fvarTriple[2]
- minimum = max(self.minimum, fvarTriple[0])
- maximum = max(self.maximum, fvarTriple[0])
+ minimum = max(minimum, fvarTriple[0])
+ maximum = max(maximum, fvarTriple[0])
minimum = min(minimum, fvarTriple[2])
maximum = min(maximum, fvarTriple[2])
default = max(minimum, min(maximum, default))
@@ -372,7 +386,7 @@ class AxisLimits(_BaseAxisLimits):
if triple is None:
newLimits[axisTag] = AxisTriple(default, default, default)
else:
- newLimits[axisTag] = triple.limitRangeAndPopulateDefault(fvarTriple)
+ newLimits[axisTag] = triple.limitRangeAndPopulateDefaults(fvarTriple)
return type(self)(newLimits)
def normalize(self, varfont, usingAvar=True) -> "NormalizedAxisLimits":
@@ -1273,6 +1287,9 @@ def instantiateVariableFont(
ignoreErrors=(overlap == OverlapMode.REMOVE_AND_IGNORE_ERRORS),
)
+ if "OS/2" in varfont:
+ varfont["OS/2"].recalcAvgCharWidth(varfont)
+
varLib.set_default_weight_width_slant(
varfont, location=axisLimits.defaultLocation()
)
@@ -1326,29 +1343,27 @@ def parseLimits(limits: Iterable[str]) -> Dict[str, Optional[AxisTriple]]:
result = {}
for limitString in limits:
match = re.match(
- r"^(\w{1,4})=(?:(drop)|(?:([^:]+)(?:[:]([^:]+))?(?:[:]([^:]+))?))$",
+ r"^(\w{1,4})=(?:(drop)|(?:([^:]*)(?:[:]([^:]*))?(?:[:]([^:]*))?))$",
limitString,
)
if not match:
raise ValueError("invalid location format: %r" % limitString)
tag = match.group(1).ljust(4)
+
if match.group(2): # 'drop'
- lbound = None
- else:
- lbound = strToFixedToFloat(match.group(3), precisionBits=16)
- ubound = default = lbound
- if match.group(4):
- ubound = default = strToFixedToFloat(match.group(4), precisionBits=16)
- default = None
- if match.group(5):
- default = ubound
- ubound = strToFixedToFloat(match.group(5), precisionBits=16)
-
- if all(v is None for v in (lbound, default, ubound)):
result[tag] = None
continue
- result[tag] = AxisTriple(lbound, default, ubound)
+ triple = match.group(3, 4, 5)
+
+ if triple[1] is None: # "value" syntax
+ triple = (triple[0], triple[0], triple[0])
+ elif triple[2] is None: # "min:max" syntax
+ triple = (triple[0], None, triple[1])
+
+ triple = tuple(float(v) if v else None for v in triple)
+
+ result[tag] = AxisTriple(*triple)
return result
@@ -1377,9 +1392,11 @@ def parseArgs(args):
metavar="AXIS=LOC",
nargs="*",
help="List of space separated locations. A location consists of "
- "the tag of a variation axis, followed by '=' and one of number, "
- "number:number or the literal string 'drop'. "
- "E.g.: wdth=100 or wght=75.0:125.0 or wght=drop",
+ "the tag of a variation axis, followed by '=' and the literal, "
+ "string 'drop', or comma-separate list of one to three values, "
+ "each of which is the empty string, or a number. "
+ "E.g.: wdth=100 or wght=75.0:125.0 or wght=100:400:700 or wght=:500: "
+ "or wght=drop",
)
parser.add_argument(
"-o",
diff --git a/contrib/python/fonttools/fontTools/varLib/instancer/names.py b/contrib/python/fonttools/fontTools/varLib/instancer/names.py
index dad3fd7e57..f9454688e0 100644
--- a/contrib/python/fonttools/fontTools/varLib/instancer/names.py
+++ b/contrib/python/fonttools/fontTools/varLib/instancer/names.py
@@ -134,6 +134,14 @@ def updateNameTable(varfont, axisLimits):
def checkAxisValuesExist(stat, axisValues, axisCoords):
seen = set()
designAxes = stat.DesignAxisRecord.Axis
+ hasValues = set()
+ for value in stat.AxisValueArray.AxisValue:
+ if value.Format in (1, 2, 3):
+ hasValues.add(designAxes[value.AxisIndex].AxisTag)
+ elif value.Format == 4:
+ for rec in value.AxisValueRecord:
+ hasValues.add(designAxes[rec.AxisIndex].AxisTag)
+
for axisValueTable in axisValues:
axisValueFormat = axisValueTable.Format
if axisValueTable.Format in (1, 2, 3):
@@ -150,7 +158,7 @@ def checkAxisValuesExist(stat, axisValues, axisCoords):
if axisTag in axisCoords and rec.Value == axisCoords[axisTag]:
seen.add(axisTag)
- missingAxes = set(axisCoords) - seen
+ missingAxes = (set(axisCoords) - seen) & hasValues
if missingAxes:
missing = ", ".join(f"'{i}': {axisCoords[i]}" for i in missingAxes)
raise ValueError(f"Cannot find Axis Values {{{missing}}}")
diff --git a/contrib/python/fonttools/fontTools/varLib/instancer/solver.py b/contrib/python/fonttools/fontTools/varLib/instancer/solver.py
index c991fcdcfb..9c568fe9a5 100644
--- a/contrib/python/fonttools/fontTools/varLib/instancer/solver.py
+++ b/contrib/python/fonttools/fontTools/varLib/instancer/solver.py
@@ -99,11 +99,13 @@ def _solve(tent, axisLimit, negative=False):
# axisDef | axisMax
# |
# crossing
- if gain > outGain:
+ if gain >= outGain:
+ # Note that this is the branch taken if both gain and outGain are 0.
+
# Crossing point on the axis.
crossing = peak + (1 - gain) * (upper - peak)
- loc = (axisDef, peak, crossing)
+ loc = (max(lower, axisDef), peak, crossing)
scalar = 1
# The part before the crossing point.
@@ -175,7 +177,7 @@ def _solve(tent, axisLimit, negative=False):
# axisDef axisMax
#
newUpper = peak + (1 - gain) * (upper - peak)
- assert axisMax <= newUpper # Because outGain >= gain
+ assert axisMax <= newUpper # Because outGain > gain
if newUpper <= axisDef + (axisMax - axisDef) * 2:
upper = newUpper
if not negative and axisDef + (axisMax - axisDef) * MAX_F2DOT14 < upper:
diff --git a/contrib/python/fonttools/fontTools/varLib/interpolatable.py b/contrib/python/fonttools/fontTools/varLib/interpolatable.py
index d5428c2002..c3f01f46e0 100644
--- a/contrib/python/fonttools/fontTools/varLib/interpolatable.py
+++ b/contrib/python/fonttools/fontTools/varLib/interpolatable.py
@@ -7,11 +7,11 @@ $ fonttools varLib.interpolatable font1 font2 ...
"""
from fontTools.pens.basePen import AbstractPen, BasePen
-from fontTools.pens.pointPen import SegmentToPointPen
+from fontTools.pens.pointPen import AbstractPointPen, SegmentToPointPen
from fontTools.pens.recordingPen import RecordingPen
from fontTools.pens.statisticsPen import StatisticsPen
from fontTools.pens.momentsPen import OpenContourError
-from collections import OrderedDict
+from collections import defaultdict
import math
import itertools
import sys
@@ -20,11 +20,7 @@ import sys
def _rot_list(l, k):
"""Rotate list by k items forward. Ie. item at position 0 will be
at position k in returned list. Negative k is allowed."""
- n = len(l)
- k %= n
- if not k:
- return l
- return l[n - k :] + l[: n - k]
+ return l[-k:] + l[:-k]
class PerContourPen(BasePen):
@@ -67,7 +63,7 @@ class PerContourOrComponentPen(PerContourPen):
self.value[-1].addComponent(glyphName, transformation)
-class RecordingPointPen(BasePen):
+class RecordingPointPen(AbstractPointPen):
def __init__(self):
self.value = []
@@ -81,48 +77,43 @@ class RecordingPointPen(BasePen):
self.value.append((pt, False if segmentType is None else True))
-def _vdiff(v0, v1):
- return tuple(b - a for a, b in zip(v0, v1))
+def _vdiff_hypot2(v0, v1):
+ s = 0
+ for x0, x1 in zip(v0, v1):
+ d = x1 - x0
+ s += d * d
+ return s
-def _vlen(vec):
- v = 0
- for x in vec:
- v += x * x
- return v
-
-
-def _complex_vlen(vec):
- v = 0
- for x in vec:
- v += abs(x) * abs(x)
- return v
+def _vdiff_hypot2_complex(v0, v1):
+ s = 0
+ for x0, x1 in zip(v0, v1):
+ d = x1 - x0
+ s += d.real * d.real + d.imag * d.imag
+ return s
def _matching_cost(G, matching):
return sum(G[i][j] for i, j in enumerate(matching))
-def min_cost_perfect_bipartite_matching(G):
+def min_cost_perfect_bipartite_matching_scipy(G):
n = len(G)
- try:
- from scipy.optimize import linear_sum_assignment
+ rows, cols = linear_sum_assignment(G)
+ assert (rows == list(range(n))).all()
+ return list(cols), _matching_cost(G, cols)
- rows, cols = linear_sum_assignment(G)
- assert (rows == list(range(n))).all()
- return list(cols), _matching_cost(G, cols)
- except ImportError:
- pass
- try:
- from munkres import Munkres
+def min_cost_perfect_bipartite_matching_munkres(G):
+ n = len(G)
+ cols = [None] * n
+ for row, col in Munkres().compute(G):
+ cols[row] = col
+ return cols, _matching_cost(G, cols)
- cols = [None] * n
- for row, col in Munkres().compute(G):
- cols[row] = col
- return cols, _matching_cost(G, cols)
- except ImportError:
- pass
+
+def min_cost_perfect_bipartite_matching_bruteforce(G):
+ n = len(G)
if n > 6:
raise Exception("Install Python module 'munkres' or 'scipy >= 0.17.0'")
@@ -138,7 +129,24 @@ def min_cost_perfect_bipartite_matching(G):
return best, best_cost
-def test(glyphsets, glyphs=None, names=None, ignore_missing=False):
+try:
+ from scipy.optimize import linear_sum_assignment
+
+ min_cost_perfect_bipartite_matching = min_cost_perfect_bipartite_matching_scipy
+except ImportError:
+ try:
+ from munkres import Munkres
+
+ min_cost_perfect_bipartite_matching = (
+ min_cost_perfect_bipartite_matching_munkres
+ )
+ except ImportError:
+ min_cost_perfect_bipartite_matching = (
+ min_cost_perfect_bipartite_matching_bruteforce
+ )
+
+
+def test_gen(glyphsets, glyphs=None, names=None, ignore_missing=False):
if names is None:
names = glyphsets
if glyphs is None:
@@ -147,10 +155,6 @@ def test(glyphsets, glyphs=None, names=None, ignore_missing=False):
glyphs = {g for glyphset in glyphsets for g in glyphset.keys()}
hist = []
- problems = OrderedDict()
-
- def add_problem(glyphname, problem):
- problems.setdefault(glyphname, []).append(problem)
for glyph_name in glyphs:
try:
@@ -158,12 +162,13 @@ def test(glyphsets, glyphs=None, names=None, ignore_missing=False):
allVectors = []
allNodeTypes = []
allContourIsomorphisms = []
- for glyphset, name in zip(glyphsets, names):
- glyph = glyphset[glyph_name]
-
+ allGlyphs = [glyphset[glyph_name] for glyphset in glyphsets]
+ if len([1 for glyph in allGlyphs if glyph is not None]) <= 1:
+ continue
+ for glyph, glyphset, name in zip(allGlyphs, glyphsets, names):
if glyph is None:
if not ignore_missing:
- add_problem(glyph_name, {"type": "missing", "master": name})
+ yield (glyph_name, {"type": "missing", "master": name})
allNodeTypes.append(None)
allVectors.append(None)
allContourIsomorphisms.append(None)
@@ -193,7 +198,7 @@ def test(glyphsets, glyphs=None, names=None, ignore_missing=False):
try:
contour.replay(stats)
except OpenContourError as e:
- add_problem(
+ yield (
glyph_name,
{"master": name, "contour": ix, "type": "open_path"},
)
@@ -228,36 +233,35 @@ def test(glyphsets, glyphs=None, names=None, ignore_missing=False):
mask = (1 << n) - 1
isomorphisms = []
contourIsomorphisms.append(isomorphisms)
+ complexPoints = [complex(*pt) for pt, bl in points.value]
for i in range(n):
b = ((bits << i) & mask) | ((bits >> (n - i)))
if b == bits:
- isomorphisms.append(
- _rot_list([complex(*pt) for pt, bl in points.value], i)
- )
+ isomorphisms.append(_rot_list(complexPoints, i))
# Add mirrored rotations
mirrored = list(reversed(points.value))
reversed_bits = 0
for pt, b in mirrored:
reversed_bits = (reversed_bits << 1) | b
+ complexPoints = list(reversed(complexPoints))
for i in range(n):
b = ((reversed_bits << i) & mask) | ((reversed_bits >> (n - i)))
if b == bits:
- isomorphisms.append(
- _rot_list([complex(*pt) for pt, bl in mirrored], i)
- )
+ isomorphisms.append(_rot_list(complexPoints, i))
# m0idx should be the index of the first non-None item in allNodeTypes,
- # else give it the first index of None, which is likely 0
- m0idx = allNodeTypes.index(
- next((x for x in allNodeTypes if x is not None), None)
+ # else give it the last item.
+ m0idx = next(
+ (i for i, x in enumerate(allNodeTypes) if x is not None),
+ len(allNodeTypes) - 1,
)
- # m0 is the first non-None item in allNodeTypes, or the first item if all are None
+ # m0 is the first non-None item in allNodeTypes, or last one if all None
m0 = allNodeTypes[m0idx]
for i, m1 in enumerate(allNodeTypes[m0idx + 1 :]):
if m1 is None:
continue
if len(m0) != len(m1):
- add_problem(
+ yield (
glyph_name,
{
"type": "path_count",
@@ -273,7 +277,7 @@ def test(glyphsets, glyphs=None, names=None, ignore_missing=False):
if nodes1 == nodes2:
continue
if len(nodes1) != len(nodes2):
- add_problem(
+ yield (
glyph_name,
{
"type": "node_count",
@@ -287,7 +291,7 @@ def test(glyphsets, glyphs=None, names=None, ignore_missing=False):
continue
for nodeIx, (n1, n2) in enumerate(zip(nodes1, nodes2)):
if n1 != n2:
- add_problem(
+ yield (
glyph_name,
{
"type": "node_incompatibility",
@@ -302,81 +306,94 @@ def test(glyphsets, glyphs=None, names=None, ignore_missing=False):
continue
# m0idx should be the index of the first non-None item in allVectors,
- # else give it the first index of None, which is likely 0
- m0idx = allVectors.index(
- next((x for x in allVectors if x is not None), None)
+ # else give it the last item.
+ m0idx = next(
+ (i for i, x in enumerate(allVectors) if x is not None),
+ len(allVectors) - 1,
)
- # m0 is the first non-None item in allVectors, or the first item if all are None
+ # m0 is the first non-None item in allVectors, or last one if all None
m0 = allVectors[m0idx]
- for i, m1 in enumerate(allVectors[m0idx + 1 :]):
- if m1 is None:
- continue
- if len(m0) != len(m1):
- # We already reported this
- continue
- if not m0:
- continue
- costs = [[_vlen(_vdiff(v0, v1)) for v1 in m1] for v0 in m0]
- matching, matching_cost = min_cost_perfect_bipartite_matching(costs)
- identity_matching = list(range(len(m0)))
- identity_cost = sum(costs[i][i] for i in range(len(m0)))
- if (
- matching != identity_matching
- and matching_cost < identity_cost * 0.95
- ):
- add_problem(
- glyph_name,
- {
- "type": "contour_order",
- "master_1": names[m0idx],
- "master_2": names[m0idx + i + 1],
- "value_1": list(range(len(m0))),
- "value_2": matching,
- },
- )
- break
-
- # m0idx should be the index of the first non-None item in allContourIsomorphisms,
- # else give it the first index of None, which is likely 0
- m0idx = allContourIsomorphisms.index(
- next((x for x in allContourIsomorphisms if x is not None), None)
- )
- # m0 is the first non-None item in allContourIsomorphisms, or the first item if all are None
- m0 = allContourIsomorphisms[m0idx]
- for i, m1 in enumerate(allContourIsomorphisms[m0idx + 1 :]):
- if m1 is None:
- continue
- if len(m0) != len(m1):
- # We already reported this
- continue
- if not m0:
- continue
- for ix, (contour0, contour1) in enumerate(zip(m0, m1)):
- c0 = contour0[0]
- costs = [
- v for v in (_complex_vlen(_vdiff(c0, c1)) for c1 in contour1)
- ]
- min_cost = min(costs)
- first_cost = costs[0]
- if min_cost < first_cost * 0.95:
- add_problem(
+ if m0 is not None and len(m0) > 1:
+ for i, m1 in enumerate(allVectors[m0idx + 1 :]):
+ if m1 is None:
+ continue
+ if len(m0) != len(m1):
+ # We already reported this
+ continue
+ costs = [[_vdiff_hypot2(v0, v1) for v1 in m1] for v0 in m0]
+ matching, matching_cost = min_cost_perfect_bipartite_matching(costs)
+ identity_matching = list(range(len(m0)))
+ identity_cost = sum(costs[i][i] for i in range(len(m0)))
+ if (
+ matching != identity_matching
+ and matching_cost < identity_cost * 0.95
+ ):
+ yield (
glyph_name,
{
- "type": "wrong_start_point",
- "contour": ix,
+ "type": "contour_order",
"master_1": names[m0idx],
"master_2": names[m0idx + i + 1],
+ "value_1": list(range(len(m0))),
+ "value_2": matching,
},
)
+ break
+
+ # m0idx should be the index of the first non-None item in allContourIsomorphisms,
+ # else give it the last item.
+ m0idx = next(
+ (i for i, x in enumerate(allContourIsomorphisms) if x is not None),
+ len(allVectors) - 1,
+ )
+ # m0 is the first non-None item in allContourIsomorphisms, or last one if all None
+ m0 = allContourIsomorphisms[m0idx]
+ if m0:
+ for i, m1 in enumerate(allContourIsomorphisms[m0idx + 1 :]):
+ if m1 is None:
+ continue
+ if len(m0) != len(m1):
+ # We already reported this
+ continue
+ for ix, (contour0, contour1) in enumerate(zip(m0, m1)):
+ c0 = contour0[0]
+ costs = [_vdiff_hypot2_complex(c0, c1) for c1 in contour1]
+ min_cost = min(costs)
+ first_cost = costs[0]
+ if min_cost < first_cost * 0.95:
+ yield (
+ glyph_name,
+ {
+ "type": "wrong_start_point",
+ "contour": ix,
+ "master_1": names[m0idx],
+ "master_2": names[m0idx + i + 1],
+ },
+ )
except ValueError as e:
- add_problem(
+ yield (
glyph_name,
{"type": "math_error", "master": name, "error": e},
)
+
+
+def test(glyphsets, glyphs=None, names=None, ignore_missing=False):
+ problems = defaultdict(list)
+ for glyphname, problem in test_gen(glyphsets, glyphs, names, ignore_missing):
+ problems[glyphname].append(problem)
return problems
+def recursivelyAddGlyph(glyphname, glyphset, ttGlyphSet, glyf):
+ if glyphname in glyphset:
+ return
+ glyphset[glyphname] = ttGlyphSet[glyphname]
+
+ for component in getattr(glyf[glyphname], "components", []):
+ recursivelyAddGlyph(component.glyphName, glyphset, ttGlyphSet, glyf)
+
+
def main(args=None):
"""Test for interpolatability issues between fonts"""
import argparse
@@ -410,12 +427,12 @@ def main(args=None):
metavar="FILE",
type=str,
nargs="+",
- help="Input a single DesignSpace/Glyphs file, or multiple TTF/UFO files",
+ help="Input a single variable font / DesignSpace / Glyphs file, or multiple TTF/UFO files",
)
args = parser.parse_args(args)
- glyphs = set(args.glyphs.split()) if args.glyphs else None
+ glyphs = args.glyphs.split() if args.glyphs else None
from os.path import basename
@@ -444,30 +461,37 @@ def main(args=None):
if "gvar" in font:
# Is variable font
gvar = font["gvar"]
- # Gather all "master" locations
- locs = set()
- for variations in gvar.variations.values():
- for var in variations:
+ glyf = font["glyf"]
+ # Gather all glyphs at their "master" locations
+ ttGlyphSets = {}
+ glyphsets = defaultdict(dict)
+
+ if glyphs is None:
+ glyphs = sorted(gvar.variations.keys())
+ for glyphname in glyphs:
+ for var in gvar.variations[glyphname]:
+ locDict = {}
loc = []
for tag, val in sorted(var.axes.items()):
+ locDict[tag] = val[1]
loc.append((tag, val[1]))
- locs.add(tuple(loc))
- # Rebuild locs as dictionaries
- new_locs = [{}]
- names.append("()")
- for loc in sorted(locs, key=lambda v: (len(v), v)):
- names.append(str(loc))
- l = {}
- for tag, val in loc:
- l[tag] = val
- new_locs.append(l)
- locs = new_locs
- del new_locs
- # locs is all master locations now
-
- for loc in locs:
- fonts.append(font.getGlyphSet(location=loc, normalized=True))
+ locTuple = tuple(loc)
+ if locTuple not in ttGlyphSets:
+ ttGlyphSets[locTuple] = font.getGlyphSet(
+ location=locDict, normalized=True
+ )
+
+ recursivelyAddGlyph(
+ glyphname, glyphsets[locTuple], ttGlyphSets[locTuple], glyf
+ )
+
+ names = ["()"]
+ fonts = [font.getGlyphSet()]
+ for locTuple in sorted(glyphsets.keys(), key=lambda v: (len(v), v)):
+ names.append(str(locTuple))
+ fonts.append(glyphsets[locTuple])
+ args.ignore_missing = True
args.inputs = []
for filename in args.inputs:
@@ -491,87 +515,101 @@ def main(args=None):
glyphsets.append({k: glyphset[k] for k in glyphset.keys()})
if not glyphs:
- glyphs = set([gn for glyphset in glyphsets for gn in glyphset.keys()])
+ glyphs = sorted(set([gn for glyphset in glyphsets for gn in glyphset.keys()]))
+ glyphsSet = set(glyphs)
for glyphset in glyphsets:
glyphSetGlyphNames = set(glyphset.keys())
- diff = glyphs - glyphSetGlyphNames
+ diff = glyphsSet - glyphSetGlyphNames
if diff:
for gn in diff:
glyphset[gn] = None
- problems = test(
+ problems_gen = test_gen(
glyphsets, glyphs=glyphs, names=names, ignore_missing=args.ignore_missing
)
+ problems = defaultdict(list)
if not args.quiet:
if args.json:
import json
+ for glyphname, problem in problems_gen:
+ problems[glyphname].append(problem)
+
print(json.dumps(problems))
else:
- for glyph, glyph_problems in problems.items():
- print(f"Glyph {glyph} was not compatible: ")
- for p in glyph_problems:
- if p["type"] == "missing":
- print(" Glyph was missing in master %s" % p["master"])
- if p["type"] == "open_path":
- print(" Glyph has an open path in master %s" % p["master"])
- if p["type"] == "path_count":
- print(
- " Path count differs: %i in %s, %i in %s"
- % (p["value_1"], p["master_1"], p["value_2"], p["master_2"])
- )
- if p["type"] == "node_count":
- print(
- " Node count differs in path %i: %i in %s, %i in %s"
- % (
- p["path"],
- p["value_1"],
- p["master_1"],
- p["value_2"],
- p["master_2"],
- )
+ last_glyphname = None
+ for glyphname, p in problems_gen:
+ problems[glyphname].append(p)
+
+ if glyphname != last_glyphname:
+ print(f"Glyph {glyphname} was not compatible: ")
+ last_glyphname = glyphname
+
+ if p["type"] == "missing":
+ print(" Glyph was missing in master %s" % p["master"])
+ if p["type"] == "open_path":
+ print(" Glyph has an open path in master %s" % p["master"])
+ if p["type"] == "path_count":
+ print(
+ " Path count differs: %i in %s, %i in %s"
+ % (p["value_1"], p["master_1"], p["value_2"], p["master_2"])
+ )
+ if p["type"] == "node_count":
+ print(
+ " Node count differs in path %i: %i in %s, %i in %s"
+ % (
+ p["path"],
+ p["value_1"],
+ p["master_1"],
+ p["value_2"],
+ p["master_2"],
)
- if p["type"] == "node_incompatibility":
- print(
- " Node %o incompatible in path %i: %s in %s, %s in %s"
- % (
- p["node"],
- p["path"],
- p["value_1"],
- p["master_1"],
- p["value_2"],
- p["master_2"],
- )
+ )
+ if p["type"] == "node_incompatibility":
+ print(
+ " Node %o incompatible in path %i: %s in %s, %s in %s"
+ % (
+ p["node"],
+ p["path"],
+ p["value_1"],
+ p["master_1"],
+ p["value_2"],
+ p["master_2"],
)
- if p["type"] == "contour_order":
- print(
- " Contour order differs: %s in %s, %s in %s"
- % (
- p["value_1"],
- p["master_1"],
- p["value_2"],
- p["master_2"],
- )
+ )
+ if p["type"] == "contour_order":
+ print(
+ " Contour order differs: %s in %s, %s in %s"
+ % (
+ p["value_1"],
+ p["master_1"],
+ p["value_2"],
+ p["master_2"],
)
- if p["type"] == "wrong_start_point":
- print(
- " Contour %d start point differs: %s, %s"
- % (
- p["contour"],
- p["master_1"],
- p["master_2"],
- )
+ )
+ if p["type"] == "wrong_start_point":
+ print(
+ " Contour %d start point differs: %s, %s"
+ % (
+ p["contour"],
+ p["master_1"],
+ p["master_2"],
)
- if p["type"] == "math_error":
- print(
- " Miscellaneous error in %s: %s"
- % (
- p["master"],
- p["error"],
- )
+ )
+ if p["type"] == "math_error":
+ print(
+ " Miscellaneous error in %s: %s"
+ % (
+ p["master"],
+ p["error"],
)
+ )
+ else:
+ for glyphname, problem in problems_gen:
+ problems[glyphname].append(problem)
+
if problems:
return problems
diff --git a/contrib/python/fonttools/fontTools/varLib/models.py b/contrib/python/fonttools/fontTools/varLib/models.py
index 954cf87bfa..5bd66dba15 100644
--- a/contrib/python/fonttools/fontTools/varLib/models.py
+++ b/contrib/python/fonttools/fontTools/varLib/models.py
@@ -248,7 +248,6 @@ class VariationModel(object):
"""
def __init__(self, locations, axisOrder=None, extrapolate=False):
-
if len(set(tuple(sorted(l.items())) for l in locations)) != len(locations):
raise VariationModelError("Locations must be unique.")
diff --git a/contrib/python/fonttools/fontTools/varLib/varStore.py b/contrib/python/fonttools/fontTools/varLib/varStore.py
index 74828e407e..7805769074 100644
--- a/contrib/python/fonttools/fontTools/varLib/varStore.py
+++ b/contrib/python/fonttools/fontTools/varLib/varStore.py
@@ -126,11 +126,11 @@ def VarData_addItem(self, deltas, *, round=round):
countUs = self.VarRegionCount
countThem = len(deltas)
if countUs + 1 == countThem:
- deltas = tuple(deltas[1:])
+ deltas = list(deltas[1:])
else:
assert countUs == countThem, (countUs, countThem)
- deltas = tuple(deltas)
- self.Item.append(list(deltas))
+ deltas = list(deltas)
+ self.Item.append(deltas)
self.ItemCount = len(self.Item)
@@ -532,6 +532,23 @@ def VarStore_optimize(self, use_NO_VARIATION_INDEX=True, quantization=1):
# - Insert the new encoding into the todo list,
#
# - Encode all remaining items in the todo list.
+ #
+ # The output is then sorted for stability, in the following way:
+ # - The VarRegionList of the input is kept intact.
+ # - All encodings are sorted before the main algorithm, by
+ # gain_key_sort(), which is a tuple of the following items:
+ # * The gain of the encoding.
+ # * The characteristic bitmap of the encoding, with higher-numbered
+ # columns compared first.
+ # - The VarData is sorted by width_sort_key(), which is a tuple
+ # of the following items:
+ # * The "width" of the encoding.
+ # * The characteristic bitmap of the encoding, with higher-numbered
+ # columns compared first.
+ # - Within each VarData, the items are sorted as vectors of numbers.
+ #
+ # Finally, each VarData is optimized to remove the empty columns and
+ # reorder columns as needed.
# TODO
# Check that no two VarRegions are the same; if they are, fold them.
@@ -619,14 +636,21 @@ def VarStore_optimize(self, use_NO_VARIATION_INDEX=True, quantization=1):
back_mapping = {} # Mapping from full rows to new VarIdxes
encodings.sort(key=_Encoding.width_sort_key)
self.VarData = []
- for major, encoding in enumerate(encodings):
- data = ot.VarData()
- self.VarData.append(data)
- data.VarRegionIndex = range(n)
- data.VarRegionCount = len(data.VarRegionIndex)
- data.Item = sorted(encoding.items)
- for minor, item in enumerate(data.Item):
- back_mapping[item] = (major << 16) + minor
+ for encoding in encodings:
+ items = sorted(encoding.items)
+
+ while items:
+ major = len(self.VarData)
+ data = ot.VarData()
+ self.VarData.append(data)
+ data.VarRegionIndex = range(n)
+ data.VarRegionCount = len(data.VarRegionIndex)
+
+ # Each major can only encode up to 0xFFFF entries.
+ data.Item, items = items[:0xFFFF], items[0xFFFF:]
+
+ for minor, item in enumerate(data.Item):
+ back_mapping[item] = (major << 16) + minor
# Compile final mapping.
varidx_map = {NO_VARIATION_INDEX: NO_VARIATION_INDEX}
diff --git a/contrib/python/fonttools/ya.make b/contrib/python/fonttools/ya.make
index 4aeb2b26f6..adcfec1693 100644
--- a/contrib/python/fonttools/ya.make
+++ b/contrib/python/fonttools/ya.make
@@ -2,7 +2,7 @@
PY3_LIBRARY()
-VERSION(4.43.1)
+VERSION(4.44.0)
LICENSE(MIT)