fonttools 4.59.0__cp39-cp39-win32.whl → 4.59.2__cp39-cp39-win32.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of fonttools might be problematic. Click here for more details.
- fontTools/__init__.py +1 -1
- fontTools/cffLib/CFF2ToCFF.py +40 -10
- fontTools/cffLib/transforms.py +11 -6
- fontTools/cu2qu/cu2qu.cp39-win32.pyd +0 -0
- fontTools/cu2qu/cu2qu.py +17 -2
- fontTools/feaLib/builder.py +15 -4
- fontTools/feaLib/lexer.cp39-win32.pyd +0 -0
- fontTools/feaLib/parser.py +11 -1
- fontTools/feaLib/variableScalar.py +6 -1
- fontTools/misc/bezierTools.cp39-win32.pyd +0 -0
- fontTools/misc/psCharStrings.py +17 -2
- fontTools/misc/textTools.py +4 -2
- fontTools/pens/momentsPen.cp39-win32.pyd +0 -0
- fontTools/qu2cu/qu2cu.cp39-win32.pyd +0 -0
- fontTools/subset/__init__.py +1 -0
- fontTools/ttLib/tables/_a_v_a_r.py +4 -2
- fontTools/ttLib/tables/_g_v_a_r.py +6 -3
- fontTools/ttLib/tables/_h_m_t_x.py +7 -3
- fontTools/ttLib/tables/_n_a_m_e.py +11 -6
- fontTools/varLib/__init__.py +80 -1
- fontTools/varLib/featureVars.py +8 -0
- fontTools/varLib/instancer/__init__.py +120 -22
- fontTools/varLib/iup.cp39-win32.pyd +0 -0
- fontTools/varLib/mutator.py +11 -0
- {fonttools-4.59.0.dist-info → fonttools-4.59.2.dist-info}/METADATA +38 -10
- {fonttools-4.59.0.dist-info → fonttools-4.59.2.dist-info}/RECORD +32 -32
- {fonttools-4.59.0.data → fonttools-4.59.2.data}/data/share/man/man1/ttx.1 +0 -0
- {fonttools-4.59.0.dist-info → fonttools-4.59.2.dist-info}/WHEEL +0 -0
- {fonttools-4.59.0.dist-info → fonttools-4.59.2.dist-info}/entry_points.txt +0 -0
- {fonttools-4.59.0.dist-info → fonttools-4.59.2.dist-info}/licenses/LICENSE +0 -0
- {fonttools-4.59.0.dist-info → fonttools-4.59.2.dist-info}/licenses/LICENSE.external +0 -0
- {fonttools-4.59.0.dist-info → fonttools-4.59.2.dist-info}/top_level.txt +0 -0
fontTools/__init__.py
CHANGED
fontTools/cffLib/CFF2ToCFF.py
CHANGED
|
@@ -2,13 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
from fontTools.ttLib import TTFont, newTable
|
|
4
4
|
from fontTools.misc.cliTools import makeOutputFileName
|
|
5
|
+
from fontTools.misc.psCharStrings import T2StackUseExtractor
|
|
5
6
|
from fontTools.cffLib import (
|
|
6
7
|
TopDictIndex,
|
|
7
8
|
buildOrder,
|
|
8
9
|
buildDefaults,
|
|
9
10
|
topDictOperators,
|
|
10
11
|
privateDictOperators,
|
|
12
|
+
FDSelect,
|
|
11
13
|
)
|
|
14
|
+
from .transforms import desubroutinizeCharString
|
|
15
|
+
from .specializer import specializeProgram
|
|
12
16
|
from .width import optimizeWidths
|
|
13
17
|
from collections import defaultdict
|
|
14
18
|
import logging
|
|
@@ -27,7 +31,7 @@ def _convertCFF2ToCFF(cff, otFont):
|
|
|
27
31
|
The CFF2 font cannot be variable. (TODO Accept those and convert to the
|
|
28
32
|
default instance?)
|
|
29
33
|
|
|
30
|
-
This assumes a decompiled
|
|
34
|
+
This assumes a decompiled CFF2 table. (i.e. that the object has been
|
|
31
35
|
filled via :meth:`decompile` and e.g. not loaded from XML.)"""
|
|
32
36
|
|
|
33
37
|
cff.major = 1
|
|
@@ -51,9 +55,14 @@ def _convertCFF2ToCFF(cff, otFont):
|
|
|
51
55
|
if hasattr(topDict, key):
|
|
52
56
|
delattr(topDict, key)
|
|
53
57
|
|
|
54
|
-
fdArray = topDict.FDArray
|
|
55
58
|
charStrings = topDict.CharStrings
|
|
56
59
|
|
|
60
|
+
fdArray = topDict.FDArray
|
|
61
|
+
if not hasattr(topDict, "FDSelect"):
|
|
62
|
+
# FDSelect is optional in CFF2, but required in CFF.
|
|
63
|
+
fdSelect = topDict.FDSelect = FDSelect()
|
|
64
|
+
fdSelect.gidArray = [0] * len(charStrings.charStrings)
|
|
65
|
+
|
|
57
66
|
defaults = buildDefaults(privateDictOperators)
|
|
58
67
|
order = buildOrder(privateDictOperators)
|
|
59
68
|
for fd in fdArray:
|
|
@@ -69,6 +78,7 @@ def _convertCFF2ToCFF(cff, otFont):
|
|
|
69
78
|
if hasattr(privateDict, key):
|
|
70
79
|
delattr(privateDict, key)
|
|
71
80
|
|
|
81
|
+
# Add ending operators
|
|
72
82
|
for cs in charStrings.values():
|
|
73
83
|
cs.decompile()
|
|
74
84
|
cs.program.append("endchar")
|
|
@@ -100,23 +110,43 @@ def _convertCFF2ToCFF(cff, otFont):
|
|
|
100
110
|
if width != private.defaultWidthX:
|
|
101
111
|
cs.program.insert(0, width - private.nominalWidthX)
|
|
102
112
|
|
|
113
|
+
# Handle stack use since stack-depth is lower in CFF than in CFF2.
|
|
114
|
+
for glyphName in charStrings.keys():
|
|
115
|
+
cs, fdIndex = charStrings.getItemAndSelector(glyphName)
|
|
116
|
+
if fdIndex is None:
|
|
117
|
+
fdIndex = 0
|
|
118
|
+
private = fdArray[fdIndex].Private
|
|
119
|
+
extractor = T2StackUseExtractor(
|
|
120
|
+
getattr(private, "Subrs", []), cff.GlobalSubrs, private=private
|
|
121
|
+
)
|
|
122
|
+
stackUse = extractor.execute(cs)
|
|
123
|
+
if stackUse > 48: # CFF stack depth is 48
|
|
124
|
+
desubroutinizeCharString(cs)
|
|
125
|
+
cs.program = specializeProgram(cs.program)
|
|
126
|
+
|
|
127
|
+
# Unused subroutines are still in CFF2 (ie. lacking 'return' operator)
|
|
128
|
+
# because they were not decompiled when we added the 'return'.
|
|
129
|
+
# Moreover, some used subroutines may have become unused after the
|
|
130
|
+
# stack-use fixup. So we remove all unused subroutines now.
|
|
131
|
+
cff.remove_unused_subroutines()
|
|
132
|
+
|
|
103
133
|
mapping = {
|
|
104
|
-
name: ("cid" + str(n) if n else ".notdef")
|
|
134
|
+
name: ("cid" + str(n).zfill(5) if n else ".notdef")
|
|
105
135
|
for n, name in enumerate(topDict.charset)
|
|
106
136
|
}
|
|
107
137
|
topDict.charset = [
|
|
108
|
-
"cid" + str(n) if n else ".notdef" for n in range(len(topDict.charset))
|
|
138
|
+
"cid" + str(n).zfill(5) if n else ".notdef" for n in range(len(topDict.charset))
|
|
109
139
|
]
|
|
110
140
|
charStrings.charStrings = {
|
|
111
141
|
mapping[name]: v for name, v in charStrings.charStrings.items()
|
|
112
142
|
}
|
|
113
143
|
|
|
114
|
-
|
|
115
|
-
# the output if I add it.
|
|
116
|
-
# topDict.ROS = ("Adobe", "Identity", 0)
|
|
144
|
+
topDict.ROS = ("Adobe", "Identity", 0)
|
|
117
145
|
|
|
118
146
|
|
|
119
147
|
def convertCFF2ToCFF(font, *, updatePostTable=True):
|
|
148
|
+
if "CFF2" not in font:
|
|
149
|
+
raise ValueError("Input font does not contain a CFF2 table.")
|
|
120
150
|
cff = font["CFF2"].cff
|
|
121
151
|
_convertCFF2ToCFF(cff, font)
|
|
122
152
|
del font["CFF2"]
|
|
@@ -131,7 +161,7 @@ def convertCFF2ToCFF(font, *, updatePostTable=True):
|
|
|
131
161
|
|
|
132
162
|
|
|
133
163
|
def main(args=None):
|
|
134
|
-
"""Convert
|
|
164
|
+
"""Convert CFF2 OTF font to CFF OTF font"""
|
|
135
165
|
if args is None:
|
|
136
166
|
import sys
|
|
137
167
|
|
|
@@ -140,8 +170,8 @@ def main(args=None):
|
|
|
140
170
|
import argparse
|
|
141
171
|
|
|
142
172
|
parser = argparse.ArgumentParser(
|
|
143
|
-
"fonttools cffLib.
|
|
144
|
-
description="
|
|
173
|
+
"fonttools cffLib.CFF2ToCFF",
|
|
174
|
+
description="Convert a non-variable CFF2 font to CFF.",
|
|
145
175
|
)
|
|
146
176
|
parser.add_argument(
|
|
147
177
|
"input", metavar="INPUT.ttf", help="Input OTF file with CFF table."
|
fontTools/cffLib/transforms.py
CHANGED
|
@@ -94,17 +94,22 @@ class _DesubroutinizingT2Decompiler(SimpleT2Decompiler):
|
|
|
94
94
|
cs._patches.append((index, subr._desubroutinized))
|
|
95
95
|
|
|
96
96
|
|
|
97
|
+
def desubroutinizeCharString(cs):
|
|
98
|
+
"""Desubroutinize a charstring in-place."""
|
|
99
|
+
cs.decompile()
|
|
100
|
+
subrs = getattr(cs.private, "Subrs", [])
|
|
101
|
+
decompiler = _DesubroutinizingT2Decompiler(subrs, cs.globalSubrs, cs.private)
|
|
102
|
+
decompiler.execute(cs)
|
|
103
|
+
cs.program = cs._desubroutinized
|
|
104
|
+
del cs._desubroutinized
|
|
105
|
+
|
|
106
|
+
|
|
97
107
|
def desubroutinize(cff):
|
|
98
108
|
for fontName in cff.fontNames:
|
|
99
109
|
font = cff[fontName]
|
|
100
110
|
cs = font.CharStrings
|
|
101
111
|
for c in cs.values():
|
|
102
|
-
c
|
|
103
|
-
subrs = getattr(c.private, "Subrs", [])
|
|
104
|
-
decompiler = _DesubroutinizingT2Decompiler(subrs, c.globalSubrs, c.private)
|
|
105
|
-
decompiler.execute(c)
|
|
106
|
-
c.program = c._desubroutinized
|
|
107
|
-
del c._desubroutinized
|
|
112
|
+
desubroutinizeCharString(c)
|
|
108
113
|
# Delete all the local subrs
|
|
109
114
|
if hasattr(font, "FDArray"):
|
|
110
115
|
for fd in font.FDArray:
|
|
Binary file
|
fontTools/cu2qu/cu2qu.py
CHANGED
|
@@ -37,7 +37,7 @@ NAN = float("NaN")
|
|
|
37
37
|
@cython.cfunc
|
|
38
38
|
@cython.inline
|
|
39
39
|
@cython.returns(cython.double)
|
|
40
|
-
@cython.locals(v1=cython.complex, v2=cython.complex)
|
|
40
|
+
@cython.locals(v1=cython.complex, v2=cython.complex, result=cython.double)
|
|
41
41
|
def dot(v1, v2):
|
|
42
42
|
"""Return the dot product of two vectors.
|
|
43
43
|
|
|
@@ -48,7 +48,16 @@ def dot(v1, v2):
|
|
|
48
48
|
Returns:
|
|
49
49
|
double: Dot product.
|
|
50
50
|
"""
|
|
51
|
-
|
|
51
|
+
result = (v1 * v2.conjugate()).real
|
|
52
|
+
# When vectors are perpendicular (i.e. dot product is 0), the above expression may
|
|
53
|
+
# yield slightly different results when running in pure Python vs C/Cython,
|
|
54
|
+
# both of which are correct within IEEE-754 floating-point precision.
|
|
55
|
+
# It's probably due to the different order of operations and roundings in each
|
|
56
|
+
# implementation. Because we are using the result in a denominator and catching
|
|
57
|
+
# ZeroDivisionError (see `calc_intersect`), it's best to normalize the result here.
|
|
58
|
+
if abs(result) < 1e-15:
|
|
59
|
+
result = 0.0
|
|
60
|
+
return result
|
|
52
61
|
|
|
53
62
|
|
|
54
63
|
@cython.cfunc
|
|
@@ -273,6 +282,12 @@ def calc_intersect(a, b, c, d):
|
|
|
273
282
|
try:
|
|
274
283
|
h = dot(p, a - c) / dot(p, cd)
|
|
275
284
|
except ZeroDivisionError:
|
|
285
|
+
# if 3 or 4 points are equal, we do have an intersection despite the zero-div:
|
|
286
|
+
# return one of the off-curves so that the algorithm can attempt a one-curve
|
|
287
|
+
# solution if it's within tolerance:
|
|
288
|
+
# https://github.com/linebender/kurbo/pull/484
|
|
289
|
+
if b == c and (a == b or c == d):
|
|
290
|
+
return b
|
|
276
291
|
return complex(NAN, NAN)
|
|
277
292
|
return c + cd * h
|
|
278
293
|
|
fontTools/feaLib/builder.py
CHANGED
|
@@ -32,6 +32,7 @@ from fontTools.otlLib.builder import (
|
|
|
32
32
|
AnySubstBuilder,
|
|
33
33
|
)
|
|
34
34
|
from fontTools.otlLib.error import OpenTypeLibError
|
|
35
|
+
from fontTools.varLib.errors import VarLibError
|
|
35
36
|
from fontTools.varLib.varStore import OnlineVarStoreBuilder
|
|
36
37
|
from fontTools.varLib.builder import buildVarDevTable
|
|
37
38
|
from fontTools.varLib.featureVars import addFeatureVariationsRaw
|
|
@@ -926,6 +927,11 @@ class Builder(object):
|
|
|
926
927
|
l.lookup_index for l in lookups if l.lookup_index is not None
|
|
927
928
|
)
|
|
928
929
|
)
|
|
930
|
+
# order doesn't matter, but lookup_indices preserves it.
|
|
931
|
+
# We want to combine identical sets of lookups (order doesn't matter)
|
|
932
|
+
# but also respect the order provided by the user (although there's
|
|
933
|
+
# a reasonable argument to just sort and dedupe, which fontc does)
|
|
934
|
+
lookup_key = frozenset(lookup_indices)
|
|
929
935
|
|
|
930
936
|
size_feature = tag == "GPOS" and feature_tag == "size"
|
|
931
937
|
force_feature = self.any_feature_variations(feature_tag, tag)
|
|
@@ -943,7 +949,7 @@ class Builder(object):
|
|
|
943
949
|
"stash debug information. See fonttools#2065."
|
|
944
950
|
)
|
|
945
951
|
|
|
946
|
-
feature_key = (feature_tag,
|
|
952
|
+
feature_key = (feature_tag, lookup_key)
|
|
947
953
|
feature_index = feature_indices.get(feature_key)
|
|
948
954
|
if feature_index is None:
|
|
949
955
|
feature_index = len(table.FeatureList.FeatureRecord)
|
|
@@ -1723,9 +1729,14 @@ class Builder(object):
|
|
|
1723
1729
|
if not varscalar.does_vary:
|
|
1724
1730
|
return varscalar.default, None
|
|
1725
1731
|
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1732
|
+
try:
|
|
1733
|
+
default, index = varscalar.add_to_variation_store(
|
|
1734
|
+
self.varstorebuilder, self.model_cache, self.font.get("avar")
|
|
1735
|
+
)
|
|
1736
|
+
except VarLibError as e:
|
|
1737
|
+
raise FeatureLibError(
|
|
1738
|
+
"Failed to compute deltas for variable scalar", location
|
|
1739
|
+
) from e
|
|
1729
1740
|
|
|
1730
1741
|
device = None
|
|
1731
1742
|
if index is not None and index != 0xFFFFFFFF:
|
|
Binary file
|
fontTools/feaLib/parser.py
CHANGED
|
@@ -2198,7 +2198,7 @@ class Parser(object):
|
|
|
2198
2198
|
raise FeatureLibError(
|
|
2199
2199
|
"Expected an equals sign", self.cur_token_location_
|
|
2200
2200
|
)
|
|
2201
|
-
value = self.
|
|
2201
|
+
value = self.expect_integer_or_float_()
|
|
2202
2202
|
location[axis] = value
|
|
2203
2203
|
if self.next_token_type_ is Lexer.NAME and self.next_token_[0] == ":":
|
|
2204
2204
|
# Lexer has just read the value as a glyph name. We'll correct it later
|
|
@@ -2230,6 +2230,16 @@ class Parser(object):
|
|
|
2230
2230
|
"Expected a floating-point number", self.cur_token_location_
|
|
2231
2231
|
)
|
|
2232
2232
|
|
|
2233
|
+
def expect_integer_or_float_(self):
|
|
2234
|
+
if self.next_token_type_ == Lexer.FLOAT:
|
|
2235
|
+
return self.expect_float_()
|
|
2236
|
+
elif self.next_token_type_ is Lexer.NUMBER:
|
|
2237
|
+
return self.expect_number_()
|
|
2238
|
+
else:
|
|
2239
|
+
raise FeatureLibError(
|
|
2240
|
+
"Expected an integer or floating-point number", self.cur_token_location_
|
|
2241
|
+
)
|
|
2242
|
+
|
|
2233
2243
|
def expect_decipoint_(self):
|
|
2234
2244
|
if self.next_token_type_ == Lexer.FLOAT:
|
|
2235
2245
|
return self.expect_float_()
|
|
@@ -17,7 +17,12 @@ class VariableScalar:
|
|
|
17
17
|
def __repr__(self):
|
|
18
18
|
items = []
|
|
19
19
|
for location, value in self.values.items():
|
|
20
|
-
loc = ",".join(
|
|
20
|
+
loc = ",".join(
|
|
21
|
+
[
|
|
22
|
+
f"{ax}={int(coord) if float(coord).is_integer() else coord}"
|
|
23
|
+
for ax, coord in location
|
|
24
|
+
]
|
|
25
|
+
)
|
|
21
26
|
items.append("%s:%i" % (loc, value))
|
|
22
27
|
return "(" + (" ".join(items)) + ")"
|
|
23
28
|
|
|
Binary file
|
fontTools/misc/psCharStrings.py
CHANGED
|
@@ -338,7 +338,7 @@ class SimpleT2Decompiler(object):
|
|
|
338
338
|
self.numRegions = 0
|
|
339
339
|
self.vsIndex = 0
|
|
340
340
|
|
|
341
|
-
def execute(self, charString):
|
|
341
|
+
def execute(self, charString, *, pushToStack=None):
|
|
342
342
|
self.callingStack.append(charString)
|
|
343
343
|
needsDecompilation = charString.needsDecompilation()
|
|
344
344
|
if needsDecompilation:
|
|
@@ -346,7 +346,8 @@ class SimpleT2Decompiler(object):
|
|
|
346
346
|
pushToProgram = program.append
|
|
347
347
|
else:
|
|
348
348
|
pushToProgram = lambda x: None
|
|
349
|
-
pushToStack
|
|
349
|
+
if pushToStack is None:
|
|
350
|
+
pushToStack = self.operandStack.append
|
|
350
351
|
index = 0
|
|
351
352
|
while True:
|
|
352
353
|
token, isOperator, index = charString.getToken(index)
|
|
@@ -551,6 +552,20 @@ t1Operators = [
|
|
|
551
552
|
]
|
|
552
553
|
|
|
553
554
|
|
|
555
|
+
class T2StackUseExtractor(SimpleT2Decompiler):
|
|
556
|
+
|
|
557
|
+
def execute(self, charString):
|
|
558
|
+
maxStackUse = 0
|
|
559
|
+
|
|
560
|
+
def pushToStack(value):
|
|
561
|
+
nonlocal maxStackUse
|
|
562
|
+
self.operandStack.append(value)
|
|
563
|
+
maxStackUse = max(maxStackUse, len(self.operandStack))
|
|
564
|
+
|
|
565
|
+
super().execute(charString, pushToStack=pushToStack)
|
|
566
|
+
return maxStackUse
|
|
567
|
+
|
|
568
|
+
|
|
554
569
|
class T2WidthExtractor(SimpleT2Decompiler):
|
|
555
570
|
def __init__(
|
|
556
571
|
self,
|
fontTools/misc/textTools.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""fontTools.misc.textTools.py -- miscellaneous routines."""
|
|
2
2
|
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
3
5
|
import ast
|
|
4
6
|
import string
|
|
5
7
|
|
|
@@ -118,14 +120,14 @@ def pad(data, size):
|
|
|
118
120
|
return data
|
|
119
121
|
|
|
120
122
|
|
|
121
|
-
def tostr(s, encoding="ascii", errors="strict"):
|
|
123
|
+
def tostr(s: str | bytes, encoding: str = "ascii", errors: str = "strict") -> str:
|
|
122
124
|
if not isinstance(s, str):
|
|
123
125
|
return s.decode(encoding, errors)
|
|
124
126
|
else:
|
|
125
127
|
return s
|
|
126
128
|
|
|
127
129
|
|
|
128
|
-
def tobytes(s, encoding="ascii", errors="strict"):
|
|
130
|
+
def tobytes(s: str | bytes, encoding: str = "ascii", errors: str = "strict") -> bytes:
|
|
129
131
|
if isinstance(s, str):
|
|
130
132
|
return s.encode(encoding, errors)
|
|
131
133
|
else:
|
|
Binary file
|
|
Binary file
|
fontTools/subset/__init__.py
CHANGED
|
@@ -1530,6 +1530,7 @@ def subset_glyphs(self, s):
|
|
|
1530
1530
|
if self.MarkFilteringSet not in s.used_mark_sets:
|
|
1531
1531
|
self.MarkFilteringSet = None
|
|
1532
1532
|
self.LookupFlag &= ~0x10
|
|
1533
|
+
self.LookupFlag |= 0x8
|
|
1533
1534
|
else:
|
|
1534
1535
|
self.MarkFilteringSet = s.used_mark_sets.index(self.MarkFilteringSet)
|
|
1535
1536
|
return bool(self.SubTableCount)
|
|
@@ -143,7 +143,7 @@ class table__a_v_a_r(BaseTTXConverter):
|
|
|
143
143
|
else:
|
|
144
144
|
super().fromXML(name, attrs, content, ttFont)
|
|
145
145
|
|
|
146
|
-
def renormalizeLocation(self, location, font):
|
|
146
|
+
def renormalizeLocation(self, location, font, dropZeroes=True):
|
|
147
147
|
|
|
148
148
|
majorVersion = getattr(self, "majorVersion", 1)
|
|
149
149
|
|
|
@@ -185,7 +185,9 @@ class table__a_v_a_r(BaseTTXConverter):
|
|
|
185
185
|
out.append(v)
|
|
186
186
|
|
|
187
187
|
mappedLocation = {
|
|
188
|
-
axis.axisTag: fi2fl(v, 14)
|
|
188
|
+
axis.axisTag: fi2fl(v, 14)
|
|
189
|
+
for v, axis in zip(out, axes)
|
|
190
|
+
if v != 0 or not dropZeroes
|
|
189
191
|
}
|
|
190
192
|
|
|
191
193
|
return mappedLocation
|
|
@@ -64,7 +64,6 @@ class table__g_v_a_r(DefaultTable.DefaultTable):
|
|
|
64
64
|
self.variations = {}
|
|
65
65
|
|
|
66
66
|
def compile(self, ttFont):
|
|
67
|
-
|
|
68
67
|
axisTags = [axis.axisTag for axis in ttFont["fvar"].axes]
|
|
69
68
|
sharedTuples = tv.compileSharedTuples(
|
|
70
69
|
axisTags, itertools.chain(*self.variations.values())
|
|
@@ -141,8 +140,12 @@ class table__g_v_a_r(DefaultTable.DefaultTable):
|
|
|
141
140
|
self,
|
|
142
141
|
)
|
|
143
142
|
|
|
144
|
-
assert len(glyphs) == self.glyphCount
|
|
145
|
-
assert len(axisTags) == self.axisCount
|
|
143
|
+
assert len(glyphs) == self.glyphCount, (len(glyphs), self.glyphCount)
|
|
144
|
+
assert len(axisTags) == self.axisCount, (
|
|
145
|
+
len(axisTags),
|
|
146
|
+
self.axisCount,
|
|
147
|
+
axisTags,
|
|
148
|
+
)
|
|
146
149
|
sharedCoords = tv.decompileSharedTuples(
|
|
147
150
|
axisTags, self.sharedTupleCount, data, self.offsetToSharedTuples
|
|
148
151
|
)
|
|
@@ -40,15 +40,19 @@ class table__h_m_t_x(DefaultTable.DefaultTable):
|
|
|
40
40
|
% (self.headerTag, self.numberOfMetricsName)
|
|
41
41
|
)
|
|
42
42
|
numberOfMetrics = numGlyphs
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
numberOfSideBearings = numGlyphs - numberOfMetrics
|
|
44
|
+
tableSize = 4 * numberOfMetrics + 2 * numberOfSideBearings
|
|
45
|
+
if len(data) < tableSize:
|
|
46
|
+
raise ttLib.TTLibError(
|
|
47
|
+
f"not enough '{self.tableTag}' table data: "
|
|
48
|
+
f"expected {tableSize} bytes, got {len(data)}"
|
|
49
|
+
)
|
|
45
50
|
# Note: advanceWidth is unsigned, but some font editors might
|
|
46
51
|
# read/write as signed. We can't be sure whether it was a mistake
|
|
47
52
|
# or not, so we read as unsigned but also issue a warning...
|
|
48
53
|
metricsFmt = ">" + self.longMetricFormat * numberOfMetrics
|
|
49
54
|
metrics = struct.unpack(metricsFmt, data[: 4 * numberOfMetrics])
|
|
50
55
|
data = data[4 * numberOfMetrics :]
|
|
51
|
-
numberOfSideBearings = numGlyphs - numberOfMetrics
|
|
52
56
|
sideBearings = array.array("h", data[: 2 * numberOfSideBearings])
|
|
53
57
|
data = data[2 * numberOfSideBearings :]
|
|
54
58
|
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
2
4
|
from fontTools.misc import sstruct
|
|
3
5
|
from fontTools.misc.textTools import (
|
|
4
6
|
bytechr,
|
|
@@ -63,7 +65,7 @@ class table__n_a_m_e(DefaultTable.DefaultTable):
|
|
|
63
65
|
)
|
|
64
66
|
stringData = data[stringOffset:]
|
|
65
67
|
data = data[6:]
|
|
66
|
-
self.names = []
|
|
68
|
+
self.names: list[NameRecord] = []
|
|
67
69
|
for i in range(n):
|
|
68
70
|
if len(data) < 12:
|
|
69
71
|
log.error("skipping malformed name record #%d", i)
|
|
@@ -112,7 +114,9 @@ class table__n_a_m_e(DefaultTable.DefaultTable):
|
|
|
112
114
|
self.names.append(name)
|
|
113
115
|
name.fromXML(name, attrs, content, ttFont)
|
|
114
116
|
|
|
115
|
-
def getName(
|
|
117
|
+
def getName(
|
|
118
|
+
self, nameID: int, platformID: int, platEncID: int, langID: int | None = None
|
|
119
|
+
) -> "NameRecord | None":
|
|
116
120
|
for namerecord in self.names:
|
|
117
121
|
if (
|
|
118
122
|
namerecord.nameID == nameID
|
|
@@ -123,8 +127,9 @@ class table__n_a_m_e(DefaultTable.DefaultTable):
|
|
|
123
127
|
return namerecord
|
|
124
128
|
return None # not found
|
|
125
129
|
|
|
126
|
-
def getDebugName(self, nameID):
|
|
127
|
-
englishName
|
|
130
|
+
def getDebugName(self, nameID: int) -> str | None:
|
|
131
|
+
englishName: str | None = None
|
|
132
|
+
someName: str | None = None
|
|
128
133
|
for name in self.names:
|
|
129
134
|
if name.nameID != nameID:
|
|
130
135
|
continue
|
|
@@ -513,7 +518,7 @@ class NameRecord(object):
|
|
|
513
518
|
self.platformID == 3 and self.platEncID in [0, 1, 10]
|
|
514
519
|
)
|
|
515
520
|
|
|
516
|
-
def toUnicode(self, errors="strict"):
|
|
521
|
+
def toUnicode(self, errors: str = "strict") -> str:
|
|
517
522
|
"""
|
|
518
523
|
If self.string is a Unicode string, return it; otherwise try decoding the
|
|
519
524
|
bytes in self.string to a Unicode string using the encoding of this
|
|
@@ -533,7 +538,7 @@ class NameRecord(object):
|
|
|
533
538
|
and saving it back will not change them.
|
|
534
539
|
"""
|
|
535
540
|
|
|
536
|
-
def isascii(b):
|
|
541
|
+
def isascii(b: int) -> bool:
|
|
537
542
|
return (b >= 0x20 and b <= 0x7E) or b in [0x09, 0x0A, 0x0D]
|
|
538
543
|
|
|
539
544
|
encoding = self.getEncoding()
|
fontTools/varLib/__init__.py
CHANGED
|
@@ -30,7 +30,11 @@ from fontTools.misc.fixedTools import floatToFixed as fl2fi
|
|
|
30
30
|
from fontTools.misc.textTools import Tag, tostr
|
|
31
31
|
from fontTools.ttLib import TTFont, newTable
|
|
32
32
|
from fontTools.ttLib.tables._f_v_a_r import Axis, NamedInstance
|
|
33
|
-
from fontTools.ttLib.tables._g_l_y_f import
|
|
33
|
+
from fontTools.ttLib.tables._g_l_y_f import (
|
|
34
|
+
GlyphCoordinates,
|
|
35
|
+
dropImpliedOnCurvePoints,
|
|
36
|
+
USE_MY_METRICS,
|
|
37
|
+
)
|
|
34
38
|
from fontTools.ttLib.tables.ttProgram import Program
|
|
35
39
|
from fontTools.ttLib.tables.TupleVariation import TupleVariation
|
|
36
40
|
from fontTools.ttLib.tables import otTables as ot
|
|
@@ -489,6 +493,77 @@ def _merge_TTHinting(font, masterModel, master_ttfs):
|
|
|
489
493
|
cvar.variations = variations
|
|
490
494
|
|
|
491
495
|
|
|
496
|
+
def _has_inconsistent_use_my_metrics_flag(
|
|
497
|
+
master_glyf, glyph_name, flagged_components, expected_num_components
|
|
498
|
+
) -> bool:
|
|
499
|
+
master_glyph = master_glyf.get(glyph_name)
|
|
500
|
+
# 'sparse' glyph master doesn't contribute. Besides when components don't match
|
|
501
|
+
# the VF build is going to fail anyway, so be lenient here.
|
|
502
|
+
if (
|
|
503
|
+
master_glyph is not None
|
|
504
|
+
and master_glyph.isComposite()
|
|
505
|
+
and len(master_glyph.components) == expected_num_components
|
|
506
|
+
):
|
|
507
|
+
for i, base_glyph in flagged_components:
|
|
508
|
+
comp = master_glyph.components[i]
|
|
509
|
+
if comp.glyphName != base_glyph:
|
|
510
|
+
break
|
|
511
|
+
if not (comp.flags & USE_MY_METRICS):
|
|
512
|
+
return True
|
|
513
|
+
return False
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
def _unset_inconsistent_use_my_metrics_flags(vf, master_fonts):
|
|
517
|
+
"""Clear USE_MY_METRICS on composite components if inconsistent across masters.
|
|
518
|
+
|
|
519
|
+
If a composite glyph's component has USE_MY_METRICS set differently among
|
|
520
|
+
the masters, the flag is removed from the variable font's glyf table so that
|
|
521
|
+
advance widths are not determined by that single component's phantom points.
|
|
522
|
+
"""
|
|
523
|
+
glyf = vf["glyf"]
|
|
524
|
+
master_glyfs = [m["glyf"] for m in master_fonts if "glyf" in m]
|
|
525
|
+
if not master_glyfs:
|
|
526
|
+
# Should not happen: at least the base master (as copied into vf) has glyf
|
|
527
|
+
return
|
|
528
|
+
|
|
529
|
+
for glyph_name in glyf.keys():
|
|
530
|
+
glyph = glyf[glyph_name]
|
|
531
|
+
if not glyph.isComposite():
|
|
532
|
+
continue
|
|
533
|
+
|
|
534
|
+
# collect indices of component(s) that carry the USE_MY_METRICS flag.
|
|
535
|
+
# This is supposed to be 1 component per composite, but you never know.
|
|
536
|
+
flagged_components = [
|
|
537
|
+
(i, comp.glyphName)
|
|
538
|
+
for i, comp in enumerate(glyph.components)
|
|
539
|
+
if (comp.flags & USE_MY_METRICS)
|
|
540
|
+
]
|
|
541
|
+
if not flagged_components:
|
|
542
|
+
# Nothing to fix
|
|
543
|
+
continue
|
|
544
|
+
|
|
545
|
+
# Verify that for all master glyf tables that contribute this glyph, the
|
|
546
|
+
# corresponding component (same glyphName and index) also carries USE_MY_METRICS
|
|
547
|
+
# and unset the flag if not.
|
|
548
|
+
expected_num_components = len(glyph.components)
|
|
549
|
+
if any(
|
|
550
|
+
_has_inconsistent_use_my_metrics_flag(
|
|
551
|
+
master_glyf, glyph_name, flagged_components, expected_num_components
|
|
552
|
+
)
|
|
553
|
+
for master_glyf in master_glyfs
|
|
554
|
+
):
|
|
555
|
+
comp_names = [name for _, name in flagged_components]
|
|
556
|
+
log.info(
|
|
557
|
+
"Composite glyph '%s' has inconsistent USE_MY_METRICS flags across "
|
|
558
|
+
"masters; clearing the flag on component%s %s",
|
|
559
|
+
glyph_name,
|
|
560
|
+
"s" if len(comp_names) > 1 else "",
|
|
561
|
+
comp_names if len(comp_names) > 1 else comp_names[0],
|
|
562
|
+
)
|
|
563
|
+
for i, _ in flagged_components:
|
|
564
|
+
glyph.components[i].flags &= ~USE_MY_METRICS
|
|
565
|
+
|
|
566
|
+
|
|
492
567
|
_MetricsFields = namedtuple(
|
|
493
568
|
"_MetricsFields",
|
|
494
569
|
[
|
|
@@ -1205,6 +1280,10 @@ def build(
|
|
|
1205
1280
|
if "DSIG" in vf:
|
|
1206
1281
|
del vf["DSIG"]
|
|
1207
1282
|
|
|
1283
|
+
# Clear USE_MY_METRICS composite flags if set inconsistently across masters.
|
|
1284
|
+
if "glyf" in vf:
|
|
1285
|
+
_unset_inconsistent_use_my_metrics_flags(vf, master_fonts)
|
|
1286
|
+
|
|
1208
1287
|
# TODO append masters as named-instances as well; needs .designspace change.
|
|
1209
1288
|
fvar = _add_fvar(vf, ds.axes, ds.instances)
|
|
1210
1289
|
if "STAT" not in exclude:
|
fontTools/varLib/featureVars.py
CHANGED
|
@@ -95,6 +95,14 @@ def addFeatureVariations(font, conditionalSubstitutions, featureTag="rvrn"):
|
|
|
95
95
|
|
|
96
96
|
addFeatureVariationsRaw(font, font["GSUB"].table, conditionsAndLookups, featureTags)
|
|
97
97
|
|
|
98
|
+
# Update OS/2.usMaxContext in case the font didn't have features before, but
|
|
99
|
+
# does now, if the OS/2 table exists. The table may be required, but
|
|
100
|
+
# fontTools needs to be able to deal with non-standard fonts. Since feature
|
|
101
|
+
# variations are always 1:1 mappings, we can set the value to at least 1
|
|
102
|
+
# instead of recomputing it with `otlLib.maxContextCalc.maxCtxFont()`.
|
|
103
|
+
if (os2 := font.get("OS/2")) is not None:
|
|
104
|
+
os2.usMaxContext = max(1, os2.usMaxContext)
|
|
105
|
+
|
|
98
106
|
|
|
99
107
|
def _existingVariableFeatures(table):
|
|
100
108
|
existingFeatureVarsTags = set()
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Partially instantiate a variable font.
|
|
2
2
|
|
|
3
3
|
The module exports an `instantiateVariableFont` function and CLI that allow to
|
|
4
4
|
create full instances (i.e. static fonts) from variable fonts, as well as "partial"
|
|
@@ -36,7 +36,7 @@ If the input location specifies all the axes, the resulting instance is no longe
|
|
|
36
36
|
'variable' (same as using fontools varLib.mutator):
|
|
37
37
|
.. code-block:: pycon
|
|
38
38
|
|
|
39
|
-
>>>
|
|
39
|
+
>>>
|
|
40
40
|
>> instance = instancer.instantiateVariableFont(
|
|
41
41
|
... varfont, {"wght": 700, "wdth": 67.5}
|
|
42
42
|
... )
|
|
@@ -56,8 +56,10 @@ From the console script, this is equivalent to passing `wght=drop` as input.
|
|
|
56
56
|
|
|
57
57
|
This module is similar to fontTools.varLib.mutator, which it's intended to supersede.
|
|
58
58
|
Note that, unlike varLib.mutator, when an axis is not mentioned in the input
|
|
59
|
-
location, the varLib.instancer will keep the axis and the corresponding
|
|
60
|
-
whereas mutator implicitly drops the axis at its default coordinate.
|
|
59
|
+
location, by default the varLib.instancer will keep the axis and the corresponding
|
|
60
|
+
deltas, whereas mutator implicitly drops the axis at its default coordinate.
|
|
61
|
+
To obtain the same behavior as mutator, pass the `static=True` parameter or
|
|
62
|
+
the `--static` CLI option.
|
|
61
63
|
|
|
62
64
|
The module supports all the following "levels" of instancing, which can of
|
|
63
65
|
course be combined:
|
|
@@ -72,7 +74,7 @@ L1
|
|
|
72
74
|
L2
|
|
73
75
|
dropping one or more axes while pinning them at non-default locations;
|
|
74
76
|
.. code-block:: pycon
|
|
75
|
-
|
|
77
|
+
|
|
76
78
|
>>>
|
|
77
79
|
>> font = instancer.instantiateVariableFont(varfont, {"wght": 700})
|
|
78
80
|
|
|
@@ -81,22 +83,18 @@ L3
|
|
|
81
83
|
a new minimum or maximum, potentially -- though not necessarily -- dropping
|
|
82
84
|
entire regions of variations that fall completely outside this new range.
|
|
83
85
|
.. code-block:: pycon
|
|
84
|
-
|
|
86
|
+
|
|
85
87
|
>>>
|
|
86
88
|
>> font = instancer.instantiateVariableFont(varfont, {"wght": (100, 300)})
|
|
87
89
|
|
|
88
90
|
L4
|
|
89
91
|
moving the default location of an axis, by specifying (min,defalt,max) values:
|
|
90
92
|
.. code-block:: pycon
|
|
91
|
-
|
|
93
|
+
|
|
92
94
|
>>>
|
|
93
95
|
>> font = instancer.instantiateVariableFont(varfont, {"wght": (100, 300, 700)})
|
|
94
96
|
|
|
95
|
-
|
|
96
|
-
are supported, but support for CFF2 variable fonts will be added soon.
|
|
97
|
-
|
|
98
|
-
The discussion and implementation of these features are tracked at
|
|
99
|
-
https://github.com/fonttools/fonttools/issues/1537
|
|
97
|
+
Both TrueType-flavored (glyf+gvar) variable and CFF2 variable fonts are supported.
|
|
100
98
|
"""
|
|
101
99
|
|
|
102
100
|
from fontTools.misc.fixedTools import (
|
|
@@ -120,6 +118,7 @@ from fontTools.cffLib.specializer import (
|
|
|
120
118
|
specializeCommands,
|
|
121
119
|
generalizeCommands,
|
|
122
120
|
)
|
|
121
|
+
from fontTools.cffLib.CFF2ToCFF import convertCFF2ToCFF
|
|
123
122
|
from fontTools.varLib import builder
|
|
124
123
|
from fontTools.varLib.mvar import MVAR_ENTRIES
|
|
125
124
|
from fontTools.varLib.merger import MutatorMerger
|
|
@@ -136,6 +135,7 @@ from enum import IntEnum
|
|
|
136
135
|
import logging
|
|
137
136
|
import os
|
|
138
137
|
import re
|
|
138
|
+
import io
|
|
139
139
|
from typing import Dict, Iterable, Mapping, Optional, Sequence, Tuple, Union
|
|
140
140
|
import warnings
|
|
141
141
|
|
|
@@ -433,7 +433,27 @@ class AxisLimits(_BaseAxisLimits):
|
|
|
433
433
|
|
|
434
434
|
avarSegments = {}
|
|
435
435
|
if usingAvar and "avar" in varfont:
|
|
436
|
-
|
|
436
|
+
avar = varfont["avar"]
|
|
437
|
+
avarSegments = avar.segments
|
|
438
|
+
|
|
439
|
+
if getattr(avar, "majorVersion", 1) >= 2 and avar.table.VarStore:
|
|
440
|
+
pinnedAxes = set(self.pinnedLocation())
|
|
441
|
+
if not pinnedAxes.issuperset(avarSegments):
|
|
442
|
+
raise NotImplementedError(
|
|
443
|
+
"Partial-instancing avar2 table is not supported"
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
# TODO: Merge this with the main codepath.
|
|
447
|
+
|
|
448
|
+
# Full instancing of avar2 font. Use avar table to normalize location and return.
|
|
449
|
+
location = self.pinnedLocation()
|
|
450
|
+
location = {
|
|
451
|
+
tag: normalize(value, axes[tag], avarSegments.get(tag, None))
|
|
452
|
+
for tag, value in location.items()
|
|
453
|
+
}
|
|
454
|
+
return NormalizedAxisLimits(
|
|
455
|
+
**avar.renormalizeLocation(location, varfont, dropZeroes=False)
|
|
456
|
+
)
|
|
437
457
|
|
|
438
458
|
normalizedLimits = {}
|
|
439
459
|
|
|
@@ -643,7 +663,11 @@ def instantiateCFF2(
|
|
|
643
663
|
# the Private dicts.
|
|
644
664
|
#
|
|
645
665
|
# Then prune unused things and possibly drop the VarStore if it's empty.
|
|
646
|
-
#
|
|
666
|
+
#
|
|
667
|
+
# If the downgrade parameter is True, no actual downgrading is done, but
|
|
668
|
+
# the function returns True if the VarStore was empty after instantiation,
|
|
669
|
+
# and hence a downgrade to CFF is possible. In all other cases it returns
|
|
670
|
+
# False.
|
|
647
671
|
|
|
648
672
|
log.info("Instantiating CFF2 table")
|
|
649
673
|
|
|
@@ -882,9 +906,9 @@ def instantiateCFF2(
|
|
|
882
906
|
del private.vstore
|
|
883
907
|
|
|
884
908
|
if downgrade:
|
|
885
|
-
|
|
909
|
+
return True
|
|
886
910
|
|
|
887
|
-
|
|
911
|
+
return False
|
|
888
912
|
|
|
889
913
|
|
|
890
914
|
def _instantiateGvarGlyph(
|
|
@@ -1116,7 +1140,8 @@ def _instantiateVHVAR(varfont, axisLimits, tableFields, *, round=round):
|
|
|
1116
1140
|
varIdx = advMapping.mapping[glyphName]
|
|
1117
1141
|
else:
|
|
1118
1142
|
varIdx = varfont.getGlyphID(glyphName)
|
|
1119
|
-
|
|
1143
|
+
delta = round(defaultDeltas[varIdx])
|
|
1144
|
+
metrics[glyphName] = (max(0, advanceWidth + delta), sb)
|
|
1120
1145
|
|
|
1121
1146
|
if (
|
|
1122
1147
|
tableTag == "VVAR"
|
|
@@ -1377,12 +1402,55 @@ def _isValidAvarSegmentMap(axisTag, segmentMap):
|
|
|
1377
1402
|
return True
|
|
1378
1403
|
|
|
1379
1404
|
|
|
1405
|
+
def downgradeCFF2ToCFF(varfont):
|
|
1406
|
+
# Save these properties
|
|
1407
|
+
recalcTimestamp = varfont.recalcTimestamp
|
|
1408
|
+
recalcBBoxes = varfont.recalcBBoxes
|
|
1409
|
+
|
|
1410
|
+
# Disable them
|
|
1411
|
+
varfont.recalcTimestamp = False
|
|
1412
|
+
varfont.recalcBBoxes = False
|
|
1413
|
+
|
|
1414
|
+
# Save to memory, reload, downgrade and save again, reload.
|
|
1415
|
+
# We do this dance because the convertCFF2ToCFF changes glyph
|
|
1416
|
+
# names, so following save would fail if any other table was
|
|
1417
|
+
# loaded and referencing glyph names.
|
|
1418
|
+
#
|
|
1419
|
+
# The second save+load is unfortunate but also necessary.
|
|
1420
|
+
|
|
1421
|
+
stream = io.BytesIO()
|
|
1422
|
+
log.info("Saving CFF2 font to memory for downgrade")
|
|
1423
|
+
varfont.save(stream)
|
|
1424
|
+
stream.seek(0)
|
|
1425
|
+
varfont = TTFont(stream, recalcTimestamp=False, recalcBBoxes=False)
|
|
1426
|
+
|
|
1427
|
+
convertCFF2ToCFF(varfont)
|
|
1428
|
+
|
|
1429
|
+
stream = io.BytesIO()
|
|
1430
|
+
log.info("Saving downgraded CFF font to memory")
|
|
1431
|
+
varfont.save(stream)
|
|
1432
|
+
stream.seek(0)
|
|
1433
|
+
varfont = TTFont(stream, recalcTimestamp=False, recalcBBoxes=False)
|
|
1434
|
+
|
|
1435
|
+
# Uncomment, to see test all tables can be loaded. This fails without
|
|
1436
|
+
# the extra save+load above.
|
|
1437
|
+
"""
|
|
1438
|
+
for tag in varfont.keys():
|
|
1439
|
+
print("Loading", tag)
|
|
1440
|
+
varfont[tag]
|
|
1441
|
+
"""
|
|
1442
|
+
|
|
1443
|
+
# Restore them
|
|
1444
|
+
varfont.recalcTimestamp = recalcTimestamp
|
|
1445
|
+
varfont.recalcBBoxes = recalcBBoxes
|
|
1446
|
+
|
|
1447
|
+
return varfont
|
|
1448
|
+
|
|
1449
|
+
|
|
1380
1450
|
def instantiateAvar(varfont, axisLimits):
|
|
1381
1451
|
# 'axisLimits' dict must contain user-space (non-normalized) coordinates.
|
|
1382
1452
|
|
|
1383
1453
|
avar = varfont["avar"]
|
|
1384
|
-
if getattr(avar, "majorVersion", 1) >= 2 and avar.table.VarStore:
|
|
1385
|
-
raise NotImplementedError("avar table with VarStore is not supported")
|
|
1386
1454
|
|
|
1387
1455
|
segments = avar.segments
|
|
1388
1456
|
|
|
@@ -1393,6 +1461,9 @@ def instantiateAvar(varfont, axisLimits):
|
|
|
1393
1461
|
del varfont["avar"]
|
|
1394
1462
|
return
|
|
1395
1463
|
|
|
1464
|
+
if getattr(avar, "majorVersion", 1) >= 2 and avar.table.VarStore:
|
|
1465
|
+
raise NotImplementedError("avar table with VarStore is not supported")
|
|
1466
|
+
|
|
1396
1467
|
log.info("Instantiating avar table")
|
|
1397
1468
|
for axis in pinnedAxes:
|
|
1398
1469
|
if axis in segments:
|
|
@@ -1594,6 +1665,7 @@ def instantiateVariableFont(
|
|
|
1594
1665
|
updateFontNames=False,
|
|
1595
1666
|
*,
|
|
1596
1667
|
downgradeCFF2=False,
|
|
1668
|
+
static=False,
|
|
1597
1669
|
):
|
|
1598
1670
|
"""Instantiate variable font, either fully or partially.
|
|
1599
1671
|
|
|
@@ -1637,12 +1709,23 @@ def instantiateVariableFont(
|
|
|
1637
1709
|
software that does not support CFF2. Defaults to False. Note that this
|
|
1638
1710
|
operation also removes overlaps within glyph shapes, as CFF does not support
|
|
1639
1711
|
overlaps but CFF2 does.
|
|
1712
|
+
static (bool): if True, generate a full instance (static font) instead of a partial
|
|
1713
|
+
instance (variable font).
|
|
1640
1714
|
"""
|
|
1641
1715
|
# 'overlap' used to be bool and is now enum; for backward compat keep accepting bool
|
|
1642
1716
|
overlap = OverlapMode(int(overlap))
|
|
1643
1717
|
|
|
1644
1718
|
sanityCheckVariableTables(varfont)
|
|
1645
1719
|
|
|
1720
|
+
if static:
|
|
1721
|
+
unspecified = []
|
|
1722
|
+
for axis in varfont["fvar"].axes:
|
|
1723
|
+
if axis.axisTag not in axisLimits:
|
|
1724
|
+
axisLimits[axis.axisTag] = None
|
|
1725
|
+
unspecified.append(axis.axisTag)
|
|
1726
|
+
if unspecified:
|
|
1727
|
+
log.info("Pinning unspecified axes to default: %s", unspecified)
|
|
1728
|
+
|
|
1646
1729
|
axisLimits = AxisLimits(axisLimits).limitAxesAndPopulateDefaults(varfont)
|
|
1647
1730
|
|
|
1648
1731
|
log.info("Restricted limits: %s", axisLimits)
|
|
@@ -1665,7 +1748,9 @@ def instantiateVariableFont(
|
|
|
1665
1748
|
instantiateVARC(varfont, normalizedLimits)
|
|
1666
1749
|
|
|
1667
1750
|
if "CFF2" in varfont:
|
|
1668
|
-
instantiateCFF2(
|
|
1751
|
+
downgradeCFF2 = instantiateCFF2(
|
|
1752
|
+
varfont, normalizedLimits, downgrade=downgradeCFF2
|
|
1753
|
+
)
|
|
1669
1754
|
|
|
1670
1755
|
if "gvar" in varfont:
|
|
1671
1756
|
instantiateGvar(varfont, normalizedLimits, optimize=optimize)
|
|
@@ -1720,6 +1805,12 @@ def instantiateVariableFont(
|
|
|
1720
1805
|
# name table has been updated.
|
|
1721
1806
|
setRibbiBits(varfont)
|
|
1722
1807
|
|
|
1808
|
+
if downgradeCFF2:
|
|
1809
|
+
origVarfont = varfont
|
|
1810
|
+
varfont = downgradeCFF2ToCFF(varfont)
|
|
1811
|
+
if inplace:
|
|
1812
|
+
origVarfont.__dict__ = varfont.__dict__.copy()
|
|
1813
|
+
|
|
1723
1814
|
return varfont
|
|
1724
1815
|
|
|
1725
1816
|
|
|
@@ -1826,6 +1917,12 @@ def parseArgs(args):
|
|
|
1826
1917
|
default=None,
|
|
1827
1918
|
help="Output instance TTF file (default: INPUT-instance.ttf).",
|
|
1828
1919
|
)
|
|
1920
|
+
parser.add_argument(
|
|
1921
|
+
"--static",
|
|
1922
|
+
dest="static",
|
|
1923
|
+
action="store_true",
|
|
1924
|
+
help="Make a static font: pin unspecified axes to their default location.",
|
|
1925
|
+
)
|
|
1829
1926
|
parser.add_argument(
|
|
1830
1927
|
"--no-optimize",
|
|
1831
1928
|
dest="optimize",
|
|
@@ -1923,13 +2020,13 @@ def main(args=None):
|
|
|
1923
2020
|
recalcBBoxes=options.recalc_bounds,
|
|
1924
2021
|
)
|
|
1925
2022
|
|
|
1926
|
-
isFullInstance = {
|
|
2023
|
+
isFullInstance = options.static or {
|
|
1927
2024
|
axisTag
|
|
1928
2025
|
for axisTag, limit in axisLimits.items()
|
|
1929
2026
|
if limit is None or limit[0] == limit[2]
|
|
1930
2027
|
}.issuperset(axis.axisTag for axis in varfont["fvar"].axes)
|
|
1931
2028
|
|
|
1932
|
-
instantiateVariableFont(
|
|
2029
|
+
varfont = instantiateVariableFont(
|
|
1933
2030
|
varfont,
|
|
1934
2031
|
axisLimits,
|
|
1935
2032
|
inplace=True,
|
|
@@ -1937,6 +2034,7 @@ def main(args=None):
|
|
|
1937
2034
|
overlap=options.overlap,
|
|
1938
2035
|
updateFontNames=options.update_name_table,
|
|
1939
2036
|
downgradeCFF2=options.downgrade_cff2,
|
|
2037
|
+
static=options.static,
|
|
1940
2038
|
)
|
|
1941
2039
|
|
|
1942
2040
|
suffix = "-instance" if isFullInstance else "-partial"
|
|
Binary file
|
fontTools/varLib/mutator.py
CHANGED
|
@@ -4,9 +4,16 @@ Instantiate a variation font. Run, eg:
|
|
|
4
4
|
.. code-block:: sh
|
|
5
5
|
|
|
6
6
|
$ fonttools varLib.mutator ./NotoSansArabic-VF.ttf wght=140 wdth=85
|
|
7
|
+
|
|
8
|
+
.. warning::
|
|
9
|
+
``fontTools.varLib.mutator`` is deprecated in favor of :mod:`fontTools.varLib.instancer`
|
|
10
|
+
which provides equivalent full instancing and also supports partial instancing.
|
|
11
|
+
Please migrate CLI usage to ``fonttools varLib.instancer`` and API usage to
|
|
12
|
+
:func:`fontTools.varLib.instancer.instantiateVariableFont`.
|
|
7
13
|
"""
|
|
8
14
|
|
|
9
15
|
from fontTools.misc.fixedTools import floatToFixedToFloat, floatToFixed
|
|
16
|
+
from fontTools.misc.loggingTools import deprecateFunction
|
|
10
17
|
from fontTools.misc.roundTools import otRound
|
|
11
18
|
from fontTools.pens.boundsPen import BoundsPen
|
|
12
19
|
from fontTools.ttLib import TTFont, newTable
|
|
@@ -159,6 +166,10 @@ def interpolate_cff2_metrics(varfont, topDict, glyphOrder, loc):
|
|
|
159
166
|
hmtx[gname] = tuple(entry)
|
|
160
167
|
|
|
161
168
|
|
|
169
|
+
@deprecateFunction(
|
|
170
|
+
"use fontTools.varLib.instancer.instantiateVariableFont instead "
|
|
171
|
+
"for either full or partial instancing",
|
|
172
|
+
)
|
|
162
173
|
def instantiateVariableFont(varfont, location, inplace=False, overlap=True):
|
|
163
174
|
"""Generate a static instance from a variable TTFont and a dictionary
|
|
164
175
|
defining the desired location along the variable font's axes.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fonttools
|
|
3
|
-
Version: 4.59.
|
|
3
|
+
Version: 4.59.2
|
|
4
4
|
Summary: Tools to manipulate font files
|
|
5
5
|
Home-page: http://github.com/fonttools/fonttools
|
|
6
6
|
Author: Just van Rossum
|
|
@@ -168,15 +168,6 @@ are required to unlock the extra features named "ufo", etc.
|
|
|
168
168
|
|
|
169
169
|
*Extra:* ``lxml``
|
|
170
170
|
|
|
171
|
-
- ``Lib/fontTools/ufoLib``
|
|
172
|
-
|
|
173
|
-
Package for reading and writing UFO source files; it requires:
|
|
174
|
-
|
|
175
|
-
* `fs <https://pypi.org/pypi/fs>`__: (aka ``pyfilesystem2``) filesystem
|
|
176
|
-
abstraction layer.
|
|
177
|
-
|
|
178
|
-
*Extra:* ``ufo``
|
|
179
|
-
|
|
180
171
|
- ``Lib/fontTools/ttLib/woff2.py``
|
|
181
172
|
|
|
182
173
|
Module to compress/decompress WOFF 2.0 web fonts; it requires:
|
|
@@ -269,6 +260,17 @@ are required to unlock the extra features named "ufo", etc.
|
|
|
269
260
|
|
|
270
261
|
*Extra:* ``pathops``
|
|
271
262
|
|
|
263
|
+
- ``Lib/fontTools/ufoLib``
|
|
264
|
+
|
|
265
|
+
Package for reading and writing UFO source files; if available, it will use:
|
|
266
|
+
|
|
267
|
+
* `fs <https://pypi.org/pypi/fs>`__: (aka ``pyfilesystem2``) filesystem abstraction layer
|
|
268
|
+
|
|
269
|
+
for reading and writing UFOs to the local filesystem or zip files (.ufoz), instead of
|
|
270
|
+
the built-in ``fontTools.misc.filesystem`` package.
|
|
271
|
+
The reader and writer classes can in theory also accept any object compatible the
|
|
272
|
+
``fs.base.FS`` interface, although not all have been tested.
|
|
273
|
+
|
|
272
274
|
- ``Lib/fontTools/pens/cocoaPen.py`` and ``Lib/fontTools/pens/quartzPen.py``
|
|
273
275
|
|
|
274
276
|
Pens for drawing glyphs with Cocoa ``NSBezierPath`` or ``CGPath`` require:
|
|
@@ -386,6 +388,32 @@ Have fun!
|
|
|
386
388
|
Changelog
|
|
387
389
|
~~~~~~~~~
|
|
388
390
|
|
|
391
|
+
4.59.2 (released 2025-08-27)
|
|
392
|
+
----------------------------
|
|
393
|
+
|
|
394
|
+
- [varLib] Clear ``USE_MY_METRICS`` component flags when inconsistent across masters (#3912).
|
|
395
|
+
- [varLib.instancer] Avoid negative advance width/height values when instatiating HVAR/VVAR,
|
|
396
|
+
(unlikely in well-behaved fonts) (#3918).
|
|
397
|
+
- [subset] Fix shaping behaviour when pruning empty mark sets (#3915, harfbuzz/harfbuzz#5499).
|
|
398
|
+
- [cu2qu] Fixed ``dot()`` product of perpendicular vectors not always returning exactly 0.0
|
|
399
|
+
in all Python implementations (#3911)
|
|
400
|
+
- [varLib.instancer] Implemented fully-instantiating ``avar2`` fonts (#3909).
|
|
401
|
+
- [feaLib] Allow float values in ``VariableScalar``'s axis locations (#3906, #3907).
|
|
402
|
+
- [cu2qu] Handle special case in ``calc_intersect`` for degenerate cubic curves where 3 to 4
|
|
403
|
+
control points are equal (#3904).
|
|
404
|
+
|
|
405
|
+
4.59.1 (released 2025-08-14)
|
|
406
|
+
----------------------------
|
|
407
|
+
|
|
408
|
+
- [featureVars] Update OS/2.usMaxContext if possible after addFeatureVariationsRaw (#3894).
|
|
409
|
+
- [vhmtx] raise TTLibError('not enough data...') when hmtx/vmtx are truncated (#3843, #3901).
|
|
410
|
+
- [feaLib] Combine duplicate features that have the same set of lookups regardless of the order in which those lookups are added to the feature (#3895).
|
|
411
|
+
- [varLib] Deprecate ``varLib.mutator`` in favor of ``varLib.instancer``. The latter
|
|
412
|
+
provides equivalent full (static font) instancing in addition to partial VF instancing.
|
|
413
|
+
CLI users should replace ``fonttools varLib.mutator`` with ``fonttools varLib.instancer``.
|
|
414
|
+
API users should migrate to ``fontTools.varLib.instancer.instantiateVariableFont`` (#2680).
|
|
415
|
+
|
|
416
|
+
|
|
389
417
|
4.59.0 (released 2025-07-16)
|
|
390
418
|
----------------------------
|
|
391
419
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
fontTools/__init__.py,sha256=
|
|
1
|
+
fontTools/__init__.py,sha256=tDtr9dKOrmxz5xXK-4JYaRrVOBx6WHpvfrXg_aWIzYU,191
|
|
2
2
|
fontTools/__main__.py,sha256=T8Tg8xPKHOCVoYVG82p_zpQXfW7_ERRAphBkZVvhWN8,960
|
|
3
3
|
fontTools/afmLib.py,sha256=YbmmjT8Du6qFUhFHwnAhOdvsyfXszODVjSJtd18CCjY,13603
|
|
4
4
|
fontTools/agl.py,sha256=4aKwnbvSVUa39eV5Ka8e5ULwV-IEp4pcfwlMwEH_z3k,118208
|
|
@@ -7,11 +7,11 @@ fontTools/help.py,sha256=8yn5iAonGPsijFSHmU6aLuuZtaLMhR5CIkSp9hVYL2c,1161
|
|
|
7
7
|
fontTools/tfmLib.py,sha256=-bv4iv2VhUSse5pA0oXdudf7o7ZuFWdWNsiHElO06dk,14730
|
|
8
8
|
fontTools/ttx.py,sha256=CpfOtEVTXAv79XM2jiWKrOFXHFtWSyAniIgC7b9tWf8,17756
|
|
9
9
|
fontTools/unicode.py,sha256=a7460sU25TnVYGzrVl0uv0lI_pDbANZp8Jfmqx9tAag,1287
|
|
10
|
-
fontTools/cffLib/CFF2ToCFF.py,sha256=
|
|
10
|
+
fontTools/cffLib/CFF2ToCFF.py,sha256=y-RJL8bbg6F1DPklI5xLUqfrjvjmByyx_l1XG_L1WRA,7657
|
|
11
11
|
fontTools/cffLib/CFFToCFF2.py,sha256=0dCYSSozptUC9BVUre49e6LgjSxJRtVyMl8vDB6i3r4,10424
|
|
12
12
|
fontTools/cffLib/__init__.py,sha256=E4wzLsJ1LxWO7CIR7fjZMHaYQJSVdqCO08fOVFowwpM,111580
|
|
13
13
|
fontTools/cffLib/specializer.py,sha256=dznFa-7VrKZkx6D8klaixTaqEAnrnT6YLX9jzA6S0Cc,33536
|
|
14
|
-
fontTools/cffLib/transforms.py,sha256=
|
|
14
|
+
fontTools/cffLib/transforms.py,sha256=YvLBqogfYx_UsPKsU_WsOcS_ASeN3rwFzlow-lL2C6I,17950
|
|
15
15
|
fontTools/cffLib/width.py,sha256=3L9NWI0uQrJHvHF_IvC_tbW1cq94zgDEPSjubdug8qM,6284
|
|
16
16
|
fontTools/colorLib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
17
|
fontTools/colorLib/builder.py,sha256=S8z4Qzw2FAE-d1Zm1eHyqDBYh6FW4W_hQJWjVeVicOk,23672
|
|
@@ -24,8 +24,8 @@ fontTools/cu2qu/__init__.py,sha256=OoM_nBJAleZal6kxeNJn1ESy1pNm5c3DG417yVIE0-Q,6
|
|
|
24
24
|
fontTools/cu2qu/__main__.py,sha256=6Vb8Ler3yqJ5w84UwlMJV6cS01uhV4PN10OlXQ6jlqo,98
|
|
25
25
|
fontTools/cu2qu/benchmark.py,sha256=FwdvNjKfWHo18_CX0CO8AY5c68XSBE4M4TJo_EkB4q8,1350
|
|
26
26
|
fontTools/cu2qu/cli.py,sha256=CvWzC5a6XF_v5o0yrS4vGI1JXiVVLzSJahTIqpJmiPk,6274
|
|
27
|
-
fontTools/cu2qu/cu2qu.cp39-win32.pyd,sha256=
|
|
28
|
-
fontTools/cu2qu/cu2qu.py,sha256=
|
|
27
|
+
fontTools/cu2qu/cu2qu.cp39-win32.pyd,sha256=rMMtbICqS_jx7jotomTTGA262urw4Lje1P7OPVDJGjw,88064
|
|
28
|
+
fontTools/cu2qu/cu2qu.py,sha256=ZciNHndtwWFx5leQO7Gxuk5ybyYTd8ZMsVawBOprNMc,17907
|
|
29
29
|
fontTools/cu2qu/errors.py,sha256=uYyPSs_x-EMJKO2S3cLGWyk_KlHoOoh_XEtdB_oKBp0,2518
|
|
30
30
|
fontTools/cu2qu/ufo.py,sha256=Mpd_7Be9jxNcOKFqkyRp8Oem3CS3R-ZYMMSD03LJL6o,12143
|
|
31
31
|
fontTools/designspaceLib/__init__.py,sha256=80fzbsWaoTMaXsPGMnevXAxR4eqvZeYCwV_GYpBvlkM,132601
|
|
@@ -40,14 +40,14 @@ fontTools/encodings/codecs.py,sha256=bSpO6kuPbEIDsXSVHhzftqsm_FFUiXpLVfPSk410SqE
|
|
|
40
40
|
fontTools/feaLib/__init__.py,sha256=RprjP6BKswq4pt0J-9L1XGuZfjIFAGD6HDly_haMAN4,217
|
|
41
41
|
fontTools/feaLib/__main__.py,sha256=niUAPkiYxeRAJMlJuvVJZism2VFufZrNaQtieA7sNLk,2318
|
|
42
42
|
fontTools/feaLib/ast.py,sha256=q-UvEPZ97AAHpggVOzVHdgfTcE072kuOK08rdAYpCXU,76301
|
|
43
|
-
fontTools/feaLib/builder.py,sha256=
|
|
43
|
+
fontTools/feaLib/builder.py,sha256=_ofuSJ9Jtz1Ybzk4ReCWSa431PXfIq-UcVkYZ2B0Ruo,75565
|
|
44
44
|
fontTools/feaLib/error.py,sha256=pqi8F2tnH2h7pXVffxwzuBuWaSHMzZsXs5VckdQKQAI,670
|
|
45
|
-
fontTools/feaLib/lexer.cp39-win32.pyd,sha256=
|
|
45
|
+
fontTools/feaLib/lexer.cp39-win32.pyd,sha256=XABp4lzyM2N_Fgh9wyYpyDknBydiNQtutjxxOcl9_rM,109568
|
|
46
46
|
fontTools/feaLib/lexer.py,sha256=7VZ3NPFH7V1mvRbym111BNKvbB4hLfGLTMS0VV_3Ipw,11408
|
|
47
47
|
fontTools/feaLib/location.py,sha256=teHrhjT8zzImcGBEJS1J43oaX9onCPu_pynxS8d-tUg,246
|
|
48
48
|
fontTools/feaLib/lookupDebugInfo.py,sha256=h4Ig8kmEk5WlGf1C9JJAbbOKQK5OwkFLdj8CT7fOkmU,316
|
|
49
|
-
fontTools/feaLib/parser.py,sha256=
|
|
50
|
-
fontTools/feaLib/variableScalar.py,sha256=
|
|
49
|
+
fontTools/feaLib/parser.py,sha256=T8IVOqzU92A3_V2-R0-W-HzNBjziJJHSg1ZGcmde-Rc,102105
|
|
50
|
+
fontTools/feaLib/variableScalar.py,sha256=KWFE5YBW7WWgLC4eqLfcGmiA7SbYO3VSdsF_5ZwJa0k,4318
|
|
51
51
|
fontTools/merge/__init__.py,sha256=A6aQxwtbzcLUrE9UOdk3yCrMwqPOboTEr5ZyEu9RxRg,8504
|
|
52
52
|
fontTools/merge/__main__.py,sha256=3_u3dnyEOyh0O-SrLMLlkXxOfCFT-0SlwJpimosVJ-c,100
|
|
53
53
|
fontTools/merge/base.py,sha256=LPJKOwMiDwayLGzA1xH325CtYHPvahAA17lihvKjiPw,2470
|
|
@@ -59,7 +59,7 @@ fontTools/merge/unicode.py,sha256=mgqRFhRugda62Xt0r28SduaN7YBzRfHxrpNprjLqoX8,43
|
|
|
59
59
|
fontTools/merge/util.py,sha256=3alo4b7mhFNC6h8PjeqNU99dS7EuO8sdZkZpvRsEE6E,3521
|
|
60
60
|
fontTools/misc/__init__.py,sha256=QoK6HlOoqtVqX5gOyv0bJiTXsVBbBRreUifdccWNp2k,76
|
|
61
61
|
fontTools/misc/arrayTools.py,sha256=baENNALPvYRUhS4rdx_F3ltOmVIf1PV9G2EaMt7gAHM,11907
|
|
62
|
-
fontTools/misc/bezierTools.cp39-win32.pyd,sha256=
|
|
62
|
+
fontTools/misc/bezierTools.cp39-win32.pyd,sha256=1Tl8f4LRU346cyRPjjt88-0veA3EmR3iPnXMaEi5xbM,317440
|
|
63
63
|
fontTools/misc/bezierTools.py,sha256=m4j14ckKYtrKy8NhFFFY_Uv3kuL8g-SWNdEKUzqGjRQ,46535
|
|
64
64
|
fontTools/misc/classifyTools.py,sha256=wLTjOhLiZaLiwwUTj2Ad5eZ5T_38W0Eo_uzRGWHWYvE,5783
|
|
65
65
|
fontTools/misc/cliTools.py,sha256=7zKOXczaCKRMW6Yv5jdCZYHco8y0-lfimhIWzQ2IL8A,1915
|
|
@@ -77,7 +77,7 @@ fontTools/misc/lazyTools.py,sha256=LJ7QvDG65xOBw2AI43qGCLxVmfdbsf-PUECfrenbkAU,1
|
|
|
77
77
|
fontTools/misc/loggingTools.py,sha256=27VatVrX8Yu-w5rFYSUjOnPLJIJ9Hx2R6hJ5YpP_djA,20476
|
|
78
78
|
fontTools/misc/macCreatorType.py,sha256=5JZKTsnkI_VBhC52lwMSrdmzqgUOhwC42jPvbGahsPo,1649
|
|
79
79
|
fontTools/misc/macRes.py,sha256=ewiYDKioxxBKW6JQcRmxpNYw5JgtJZIJyqWBG_KplUo,8840
|
|
80
|
-
fontTools/misc/psCharStrings.py,sha256=
|
|
80
|
+
fontTools/misc/psCharStrings.py,sha256=T91YzpraGjZ3ChrM56t-KCoEuE79OvPq1tJA3qYywDU,44979
|
|
81
81
|
fontTools/misc/psLib.py,sha256=cqxG8yMZ7_5VTxgTUl2ARNhIhNu_iTzxLTEd1Egwugo,12497
|
|
82
82
|
fontTools/misc/psOperators.py,sha256=9nZ4ymbiiCApY9V8OARpYqvO73OEcJgGyTtCuGzD-rw,16272
|
|
83
83
|
fontTools/misc/py23.py,sha256=BhByQabxZis6fDvK3ZVeI-YRj_1rMQeBZCFzGWIac0U,2334
|
|
@@ -85,7 +85,7 @@ fontTools/misc/roundTools.py,sha256=2rmbuk73NYGPmJqP58FQCFioSLilvNffd0WbL5znKUg,
|
|
|
85
85
|
fontTools/misc/sstruct.py,sha256=7Lc9x2RV_e2JvSI4A_Rs0tM1mZpjYg_oAJKhP_F9zts,7236
|
|
86
86
|
fontTools/misc/symfont.py,sha256=KYAtw-ZnG5YReS8XkSDIvxc1bl0xzZl-Wx4J7k7u7LA,7219
|
|
87
87
|
fontTools/misc/testTools.py,sha256=SG48M4TJIQ4_cPpitUzGEITPnwL-o0yNZKXzWSQdwVE,7285
|
|
88
|
-
fontTools/misc/textTools.py,sha256=
|
|
88
|
+
fontTools/misc/textTools.py,sha256=ws89gG0KB67ldR8kdGOdYWwx3ht644nHhNrmUt-WYzc,3639
|
|
89
89
|
fontTools/misc/timeTools.py,sha256=lmncKUKvxQKO4Kqx2k7UNFkYYpj2n5CwR1lPiLZv3tA,2322
|
|
90
90
|
fontTools/misc/transform.py,sha256=pCR0tbKzmhH6crB_rDT5hnAWySztW_XqL0efmKOVsCU,16314
|
|
91
91
|
fontTools/misc/treeTools.py,sha256=IMopMUcuhelvz8gNra50Zc1w8DSlWywnL6DFaz1ijQs,1314
|
|
@@ -127,7 +127,7 @@ fontTools/pens/explicitClosingLinePen.py,sha256=knCXcjSl2iPy6mLCDnsdDYx6J5rV7FH4
|
|
|
127
127
|
fontTools/pens/filterPen.py,sha256=tWhgklyaCTUt7oQRTBbFUcOlc702V0NfadCH3X93CYg,8031
|
|
128
128
|
fontTools/pens/freetypePen.py,sha256=NqNzXrOTDckoH4N6WLnj-KuxGcg6z7DlqSCfmpq8qAE,20370
|
|
129
129
|
fontTools/pens/hashPointPen.py,sha256=ZAU87uw5ge3Kb4i9kRV28a5VFeZ_TWSsJabyAzwAHrU,3662
|
|
130
|
-
fontTools/pens/momentsPen.cp39-win32.pyd,sha256=
|
|
130
|
+
fontTools/pens/momentsPen.cp39-win32.pyd,sha256=ZYHhKHGqPb2kuPQ_0E2PstMuK1KyPUqLIbZSUUyCQe0,81920
|
|
131
131
|
fontTools/pens/momentsPen.py,sha256=Z-V5CjQBSj3qPxg3C_DBFKExqno89nOe3jWwHT9_xsM,26537
|
|
132
132
|
fontTools/pens/perimeterPen.py,sha256=Zy5F8QzaNJAkkQQSb2QJCp-wZTvDAjBn-B099t2ABds,2222
|
|
133
133
|
fontTools/pens/pointInsidePen.py,sha256=Hy48iR5NWV3x_wWoos-UC7GMtwvvUhd_q_ykiwaWdzQ,6547
|
|
@@ -150,9 +150,9 @@ fontTools/qu2cu/__init__.py,sha256=MpdE0XsHSDo9M3hyHLkPPLxB3FKr3aiT0dPW5qHCuSo,6
|
|
|
150
150
|
fontTools/qu2cu/__main__.py,sha256=leKpToUNNyHf0nobr1I19vus2ziA1pO7rRKkreat-Xw,100
|
|
151
151
|
fontTools/qu2cu/benchmark.py,sha256=PFxx2Bfu7-KuNrzdOIBXHPZvyNphqqcTVy4CneaCo3M,1456
|
|
152
152
|
fontTools/qu2cu/cli.py,sha256=1QLBTSZW7e_VATJN9vjszRxIk_-Xjxu1KP53yX4T7q8,3839
|
|
153
|
-
fontTools/qu2cu/qu2cu.cp39-win32.pyd,sha256=
|
|
153
|
+
fontTools/qu2cu/qu2cu.cp39-win32.pyd,sha256=xoTTEKNkYjJ_yXcFoMayXLhKHxeQxl5ubqDNEwo71t0,96768
|
|
154
154
|
fontTools/qu2cu/qu2cu.py,sha256=dtp5Zqhcs_NePwA2U5fgG2LtWleRwmBilTurau8sLL0,12693
|
|
155
|
-
fontTools/subset/__init__.py,sha256=
|
|
155
|
+
fontTools/subset/__init__.py,sha256=BIx4NBKTps8oFRnB_3uN6MDsQh10IPkrId5VyR9AI9M,141672
|
|
156
156
|
fontTools/subset/__main__.py,sha256=cEIC52EtGOJvFDfHXzi0M2EAYmyHAcI-ZZ0lb2y4r7s,101
|
|
157
157
|
fontTools/subset/cff.py,sha256=GSmxdsokxuFKvJJQVcAIOhd5hYQq8KkzxnXE_dgm8yo,6329
|
|
158
158
|
fontTools/subset/svg.py,sha256=y_yTZuAm3bjcoEOFu5likXoHuG5u1oNiv0mOni2Z9fQ,9637
|
|
@@ -231,7 +231,7 @@ fontTools/ttLib/tables/V_O_R_G_.py,sha256=s9g03_qeTV3qoJAWpXxpRCmao0l1wj4WagR_Ys
|
|
|
231
231
|
fontTools/ttLib/tables/V_V_A_R_.py,sha256=PiwzLv95tfXH25hYQFAxL11mwgbLjeg4R1LvVH5m7lU,332
|
|
232
232
|
fontTools/ttLib/tables/__init__.py,sha256=pYmftKvp7RCNIaEJuUNjcZFaiIfOC0zzcApViNKUxkU,2749
|
|
233
233
|
fontTools/ttLib/tables/_a_n_k_r.py,sha256=eiy6DKxPGw-H9QCLWIQBFveuTFQSKgcPItwgyBOghk8,498
|
|
234
|
-
fontTools/ttLib/tables/_a_v_a_r.py,sha256=
|
|
234
|
+
fontTools/ttLib/tables/_a_v_a_r.py,sha256=521RCooxpE8T9sOdm0BMY2E--iWnrhO-cTr7O4dLhC4,7368
|
|
235
235
|
fontTools/ttLib/tables/_b_s_l_n.py,sha256=iHLFy3sjFFoGa-pDGdcLCCudv4QFMt2VBL06gigGA_k,480
|
|
236
236
|
fontTools/ttLib/tables/_c_i_d_g.py,sha256=BPa6b0yrmT8OXPW3USRpn_H8DOLKFlDe9subtRDJBrc,937
|
|
237
237
|
fontTools/ttLib/tables/_c_m_a_p.py,sha256=dvtnrDf1LjXVf_DXW0tz67BqOlf7dINyG0_HF3aoDmw,63793
|
|
@@ -243,11 +243,11 @@ fontTools/ttLib/tables/_f_v_a_r.py,sha256=hpxU0-u7_pZYDmQdKpmd_7c6BjVpXHdoQ97jKk
|
|
|
243
243
|
fontTools/ttLib/tables/_g_a_s_p.py,sha256=Pr4X2CEg3a_nYAZrKSWT0auQ5HU2WutP1Shxxg7ALPw,2266
|
|
244
244
|
fontTools/ttLib/tables/_g_c_i_d.py,sha256=diZlew4U8nFK5vimh-GMOjwHw8ccZtIEl9cPq0PrNdA,375
|
|
245
245
|
fontTools/ttLib/tables/_g_l_y_f.py,sha256=gnZ1femTeEaFeWn4inLiWL0h8U9_iJ0CzU_6PDtFryk,87895
|
|
246
|
-
fontTools/ttLib/tables/_g_v_a_r.py,sha256=
|
|
246
|
+
fontTools/ttLib/tables/_g_v_a_r.py,sha256=3_uN_OGGLpEjLJKfEOBIMyt92NOXpKBoLymRkiMwzCQ,12536
|
|
247
247
|
fontTools/ttLib/tables/_h_d_m_x.py,sha256=1dRbNSzHQlznrXqG0GtFYbGp_SzzgDcYfJqhSoS1FL8,4379
|
|
248
248
|
fontTools/ttLib/tables/_h_e_a_d.py,sha256=ft9ghTA1NZsGBvB0yElFFCqVHecuCKGjT2m2GfYB3Yc,5056
|
|
249
249
|
fontTools/ttLib/tables/_h_h_e_a.py,sha256=pY92ZLt3o0jZ3KQVd_qtxYtk_tbP2DLzSWm_wVP8FNM,4914
|
|
250
|
-
fontTools/ttLib/tables/_h_m_t_x.py,sha256=
|
|
250
|
+
fontTools/ttLib/tables/_h_m_t_x.py,sha256=39O10q6zJNPGgoDbd5Ivli4-BpESP81ajtDOZy-xCHg,6356
|
|
251
251
|
fontTools/ttLib/tables/_k_e_r_n.py,sha256=AjG5Fd6XaPAdXi5puDtLuMrfCsHUi9X7uFh76QGCMrc,11083
|
|
252
252
|
fontTools/ttLib/tables/_l_c_a_r.py,sha256=N-1I6OJHvnF_YfGktyfFTRAG5lrExV7q6HX-0ffSRyQ,403
|
|
253
253
|
fontTools/ttLib/tables/_l_o_c_a.py,sha256=MHRhxCcHbyK71lD3L91qNfYXD_rflSXsRa-YHDptCik,2250
|
|
@@ -256,7 +256,7 @@ fontTools/ttLib/tables/_m_a_x_p.py,sha256=9B6lvWo4y42dyLPIvG6CsVOlWCk7bs4DoVJDB8
|
|
|
256
256
|
fontTools/ttLib/tables/_m_e_t_a.py,sha256=I8HaZgcIPQZcCxBiSX0rGrfrs-zXRGUfEbJ8eGvZ07A,4025
|
|
257
257
|
fontTools/ttLib/tables/_m_o_r_t.py,sha256=LU3D9PmV_nFs6hoccGmr1pfUzjJaeB_WRW2OIS0RwPc,501
|
|
258
258
|
fontTools/ttLib/tables/_m_o_r_x.py,sha256=vLyrtx_O__BwnPi7Qo3oT8WHaANRARtHcqHSdZ5ct0E,563
|
|
259
|
-
fontTools/ttLib/tables/_n_a_m_e.py,sha256=
|
|
259
|
+
fontTools/ttLib/tables/_n_a_m_e.py,sha256=Z3BZtWPZfL-tpRjypnxjsvJzdwZWMbJ50BuFngtnkCs,42508
|
|
260
260
|
fontTools/ttLib/tables/_o_p_b_d.py,sha256=lfJi6kblt_nGmGmRSupwEaud3Ri_y6ftWNuyrCPpzQ0,462
|
|
261
261
|
fontTools/ttLib/tables/_p_o_s_t.py,sha256=HIsvathEMOAm8QFG7vcRfPQvBqXh-QmsVyOoDWsaRNc,12020
|
|
262
262
|
fontTools/ttLib/tables/_p_r_e_p.py,sha256=qWDjHiHvHaJCx2hYFmjJeMwpgwvD-cG5zkibMh9TWuk,443
|
|
@@ -293,14 +293,14 @@ fontTools/unicodedata/OTTags.py,sha256=IAt8NXaZOhu5cuuks46DDX3E7Ovoqp-PMUQC-WJUP
|
|
|
293
293
|
fontTools/unicodedata/ScriptExtensions.py,sha256=eIAXBnM9BbI5V_MWeA9I9Iv2rvgWi8mt8dCWN3cN1gY,29033
|
|
294
294
|
fontTools/unicodedata/Scripts.py,sha256=jCKY8wlKrSFmsFndzLegVS6vrhVGZ-S3T0dw2vO9Drg,133888
|
|
295
295
|
fontTools/unicodedata/__init__.py,sha256=dUWWA4Ga1Wud8XkCIWZ02NCHBLtX2XYYUJo4ZLHTd0U,9337
|
|
296
|
-
fontTools/varLib/__init__.py,sha256=
|
|
296
|
+
fontTools/varLib/__init__.py,sha256=HTGmRXieDmkNGeav4uSmoHuhV6IasgiLbeT5TAfu-tQ,58918
|
|
297
297
|
fontTools/varLib/__main__.py,sha256=ykyZY5GG9IPDsPrUWiHgXEnsgKrQudZkneCTes6GUpU,101
|
|
298
298
|
fontTools/varLib/avar.py,sha256=tRgKAUn_K5MTCSkB2MgPYYZ2U6Qo_Cg3jFQV0TDKFgc,9907
|
|
299
299
|
fontTools/varLib/avarPlanner.py,sha256=orjyFvg3YkC-slt7fgSEU1AGjLCkGgMEJ7hTRV6CqUA,28362
|
|
300
300
|
fontTools/varLib/builder.py,sha256=1k-N-rTwnZqQpzhNLBx2tqu2oYGG44sJSXKTCjAvIVM,6824
|
|
301
301
|
fontTools/varLib/cff.py,sha256=bl8rrPHHpwzUdZBY80_5JJLWYcXQOolhKKvTJiiU-Bs,23532
|
|
302
302
|
fontTools/varLib/errors.py,sha256=mXl-quT2Z75_t7Uwb6ug3VMhmbQjO841YNLeghwuY_s,7153
|
|
303
|
-
fontTools/varLib/featureVars.py,sha256=
|
|
303
|
+
fontTools/varLib/featureVars.py,sha256=cPiijbzRMZ0AiudBNPhF8XE1LoBr5VGkjiwpw5N0oKQ,26883
|
|
304
304
|
fontTools/varLib/hvar.py,sha256=Tm0ibxOtSVrBQeHiA5-idUQaJEjzOdUWxAQyKTGsOdQ,3808
|
|
305
305
|
fontTools/varLib/interpolatable.py,sha256=8AXrhsnYY1z0hR6gskqYRYx8qcFsvUKmIIHZRpIOlAU,46430
|
|
306
306
|
fontTools/varLib/interpolatableHelpers.py,sha256=JnabttZY7sY9-QzdiqkgzQ_S5nG8k_O1TzLEmfNUvNo,11892
|
|
@@ -308,17 +308,17 @@ fontTools/varLib/interpolatablePlot.py,sha256=tUKFd8H9B2eD_GE6jV13J-dZkkIeLmk3oj
|
|
|
308
308
|
fontTools/varLib/interpolatableTestContourOrder.py,sha256=Pbt0jW0LoVggIwrtADZ7HWK6Ftdoo1bjuWz0ost0HD0,3103
|
|
309
309
|
fontTools/varLib/interpolatableTestStartingPoint.py,sha256=f5MJ3mj8MctJCvDJwqmW1fIVOgovUMYAOela9HweaRU,4403
|
|
310
310
|
fontTools/varLib/interpolate_layout.py,sha256=tTPUes_K7MwooUO_wac9AeFEVgL1uGSz4ITYiOizaME,3813
|
|
311
|
-
fontTools/varLib/iup.cp39-win32.pyd,sha256=
|
|
311
|
+
fontTools/varLib/iup.cp39-win32.pyd,sha256=cYJaZcH83dXKrw2M-zenN0raltc7wWx2VvSJaPu2pVw,116736
|
|
312
312
|
fontTools/varLib/iup.py,sha256=O_xPJOBECrNDbQqCC3e5xf9KsWXUd1i3BAp9Fl6Hv2Y,15474
|
|
313
313
|
fontTools/varLib/merger.py,sha256=V-B17poOYbbrRsfUYJbdqt46GtRfG833MKwtv9NOB3Q,62519
|
|
314
314
|
fontTools/varLib/models.py,sha256=ZqQb1Lapj5dCO8dwa3UTx1LsIpF0-GiDte32t_TMJJQ,23040
|
|
315
315
|
fontTools/varLib/multiVarStore.py,sha256=OvrrTaKrCZCXP40Rrv-2w416P-dNz3xE6gPOEyS3PrY,8558
|
|
316
|
-
fontTools/varLib/mutator.py,sha256=
|
|
316
|
+
fontTools/varLib/mutator.py,sha256=0Mxy2ZSfFPEQrZM1fIHxZ17f0U_axLXArQ7apkjm0UI,20333
|
|
317
317
|
fontTools/varLib/mvar.py,sha256=Gf3q54ICH-E9oAwKYeIKUPLZabfjY0bUT4t220zLzYI,2489
|
|
318
318
|
fontTools/varLib/plot.py,sha256=BtozrcnKoEyCs0rGy7PZmrUvUNTmZT-5_sylW5PuJ28,7732
|
|
319
319
|
fontTools/varLib/stat.py,sha256=ScaVFIVpXTqA-F07umv_66GoxtcjaZ54MPLFvFK4s68,4960
|
|
320
320
|
fontTools/varLib/varStore.py,sha256=GWz-B1YcR-JnIh2aDmeQg621GDEBj9M4pKYcbZraA3w,24808
|
|
321
|
-
fontTools/varLib/instancer/__init__.py,sha256=
|
|
321
|
+
fontTools/varLib/instancer/__init__.py,sha256=Enbv3I0j25PhYymbZNTboqnx5dnQXmUSv3ORT9-qib8,77606
|
|
322
322
|
fontTools/varLib/instancer/__main__.py,sha256=YN_tyJDdmLlH3umiLDS2ue0Zc3fSFexa9wCuk3Wuod0,109
|
|
323
323
|
fontTools/varLib/instancer/featureVars.py,sha256=b3qtGCYVZ9fqkqcgFQUikYQBX_3_x0YgdrvvxIALbuU,7300
|
|
324
324
|
fontTools/varLib/instancer/names.py,sha256=vmHi7JZlh-N4amxKdaTJ-5DN9mDJ8Wnh_s9W1gJAQ4Y,15338
|
|
@@ -330,11 +330,11 @@ fontTools/voltLib/error.py,sha256=3TsaZBA82acFd2j5Beq3WUQTURTKM0zxOnUFGZovSNA,40
|
|
|
330
330
|
fontTools/voltLib/lexer.py,sha256=v9V4zdBO2VqVJG__IWrL8fv_CRURmh2eD_1UpbIJn9g,3467
|
|
331
331
|
fontTools/voltLib/parser.py,sha256=HS72gxtFzvcPSwEbUYj3E41CPK7ZqK9mSe0nLRxn-IY,26060
|
|
332
332
|
fontTools/voltLib/voltToFea.py,sha256=nS-OSlx_a-TngGICFNKyFxMhjqkV3OQLcvyzw4sQFyk,37460
|
|
333
|
-
fonttools-4.59.
|
|
334
|
-
fonttools-4.59.
|
|
335
|
-
fonttools-4.59.
|
|
336
|
-
fonttools-4.59.
|
|
337
|
-
fonttools-4.59.
|
|
338
|
-
fonttools-4.59.
|
|
339
|
-
fonttools-4.59.
|
|
340
|
-
fonttools-4.59.
|
|
333
|
+
fonttools-4.59.2.data/data/share/man/man1/ttx.1,sha256=E71F9mRNWlttVpzlnP7w_fqkQygPkph5s-AtVa0Js50,5601
|
|
334
|
+
fonttools-4.59.2.dist-info/licenses/LICENSE,sha256=Ir74Bpfs-qF_l-YrmibfoSggvgVYPo3RKtFpskEnTJk,1093
|
|
335
|
+
fonttools-4.59.2.dist-info/licenses/LICENSE.external,sha256=sIKl-Gd1smQfAbzLi5yCkISB3l9QK7JUseE7_CqfMD0,20410
|
|
336
|
+
fonttools-4.59.2.dist-info/METADATA,sha256=44wZQ6IJ78akWmrHMwFY8-yQxxnNyoD8fXsBLqOulnw,111931
|
|
337
|
+
fonttools-4.59.2.dist-info/WHEEL,sha256=Q3uEVTFw-CqGed7ywmQZcdvBC5FiRV941NvAhTjjkOQ,95
|
|
338
|
+
fonttools-4.59.2.dist-info/entry_points.txt,sha256=8kVHddxfFWA44FSD4mBpmC-4uCynQnkoz_9aNJb227Y,147
|
|
339
|
+
fonttools-4.59.2.dist-info/top_level.txt,sha256=rRgRylrXzekqWOsrhygzib12pQ7WILf7UGjqEwkIFDM,10
|
|
340
|
+
fonttools-4.59.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|