fonttools 4.59.0__cp310-cp310-win_amd64.whl → 4.59.2__cp310-cp310-win_amd64.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.c +760 -653
- fontTools/cu2qu/cu2qu.cp310-win_amd64.pyd +0 -0
- fontTools/cu2qu/cu2qu.py +17 -2
- fontTools/feaLib/builder.py +15 -4
- fontTools/feaLib/lexer.c +30 -16
- fontTools/feaLib/lexer.cp310-win_amd64.pyd +0 -0
- fontTools/feaLib/parser.py +11 -1
- fontTools/feaLib/variableScalar.py +6 -1
- fontTools/misc/bezierTools.c +33 -19
- fontTools/misc/bezierTools.cp310-win_amd64.pyd +0 -0
- fontTools/misc/psCharStrings.py +17 -2
- fontTools/misc/textTools.py +4 -2
- fontTools/pens/momentsPen.c +20 -14
- fontTools/pens/momentsPen.cp310-win_amd64.pyd +0 -0
- fontTools/qu2cu/qu2cu.c +32 -18
- fontTools/qu2cu/qu2cu.cp310-win_amd64.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.c +32 -18
- fontTools/varLib/iup.cp310-win_amd64.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 +38 -38
- {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/qu2cu/qu2cu.c
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Generated by Cython 3.1.
|
|
1
|
+
/* Generated by Cython 3.1.3 */
|
|
2
2
|
|
|
3
3
|
/* BEGIN: Cython Metadata
|
|
4
4
|
{
|
|
@@ -32,8 +32,8 @@ END: Cython Metadata */
|
|
|
32
32
|
#elif PY_VERSION_HEX < 0x03080000
|
|
33
33
|
#error Cython requires Python 3.8+.
|
|
34
34
|
#else
|
|
35
|
-
#define __PYX_ABI_VERSION "
|
|
36
|
-
#define CYTHON_HEX_VERSION
|
|
35
|
+
#define __PYX_ABI_VERSION "3_1_3"
|
|
36
|
+
#define CYTHON_HEX_VERSION 0x030103F0
|
|
37
37
|
#define CYTHON_FUTURE_DIVISION 1
|
|
38
38
|
/* CModulePreamble */
|
|
39
39
|
#include <stddef.h>
|
|
@@ -396,6 +396,9 @@ END: Cython Metadata */
|
|
|
396
396
|
enum { __pyx_check_sizeof_voidp = 1 / (int)(SIZEOF_VOID_P == sizeof(void*)) };
|
|
397
397
|
#endif
|
|
398
398
|
#endif
|
|
399
|
+
#ifndef CYTHON_LOCK_AND_GIL_DEADLOCK_AVOIDANCE_TIME
|
|
400
|
+
#define CYTHON_LOCK_AND_GIL_DEADLOCK_AVOIDANCE_TIME 100
|
|
401
|
+
#endif
|
|
399
402
|
#ifndef __has_attribute
|
|
400
403
|
#define __has_attribute(x) 0
|
|
401
404
|
#endif
|
|
@@ -1963,7 +1966,7 @@ static void __Pyx_Raise(PyObject *type, PyObject *value, PyObject *tb, PyObject
|
|
|
1963
1966
|
static PyObject *__Pyx_PyLong_AbsNeg(PyObject *num);
|
|
1964
1967
|
#define __Pyx_PyNumber_Absolute(x)\
|
|
1965
1968
|
((likely(PyLong_CheckExact(x))) ?\
|
|
1966
|
-
(likely(__Pyx_PyLong_IsNonNeg(x)) ? (
|
|
1969
|
+
(likely(__Pyx_PyLong_IsNonNeg(x)) ? __Pyx_NewRef(x) : __Pyx_PyLong_AbsNeg(x)) :\
|
|
1967
1970
|
PyNumber_Absolute(x))
|
|
1968
1971
|
#else
|
|
1969
1972
|
#define __Pyx_PyNumber_Absolute(x) PyNumber_Absolute(x)
|
|
@@ -9028,16 +9031,15 @@ static int __Pyx_InitConstants(__pyx_mstatetype *__pyx_mstate) {
|
|
|
9028
9031
|
return -1;
|
|
9029
9032
|
}
|
|
9030
9033
|
/* #### Code section: init_codeobjects ### */
|
|
9031
|
-
|
|
9032
|
-
|
|
9033
|
-
|
|
9034
|
-
|
|
9035
|
-
|
|
9036
|
-
|
|
9037
|
-
|
|
9038
|
-
|
|
9039
|
-
|
|
9040
|
-
} __Pyx_PyCode_New_function_description;
|
|
9034
|
+
typedef struct {
|
|
9035
|
+
unsigned int argcount : 3;
|
|
9036
|
+
unsigned int num_posonly_args : 1;
|
|
9037
|
+
unsigned int num_kwonly_args : 1;
|
|
9038
|
+
unsigned int nlocals : 6;
|
|
9039
|
+
unsigned int flags : 10;
|
|
9040
|
+
unsigned int first_line : 9;
|
|
9041
|
+
unsigned int line_table_length : 15;
|
|
9042
|
+
} __Pyx_PyCode_New_function_description;
|
|
9041
9043
|
/* NewCodeObj.proto */
|
|
9042
9044
|
static PyObject* __Pyx_PyCode_New(
|
|
9043
9045
|
const __Pyx_PyCode_New_function_description descr,
|
|
@@ -10557,7 +10559,7 @@ static PyObject *__Pyx_PyLong_AbsNeg(PyObject *n) {
|
|
|
10557
10559
|
PyObject *copy = _PyLong_Copy((PyLongObject*)n);
|
|
10558
10560
|
if (likely(copy)) {
|
|
10559
10561
|
#if PY_VERSION_HEX >= 0x030C00A7
|
|
10560
|
-
((PyLongObject*)copy)->long_value.lv_tag
|
|
10562
|
+
((PyLongObject*)copy)->long_value.lv_tag ^= ((PyLongObject*)copy)->long_value.lv_tag & _PyLong_SIGN_MASK;
|
|
10561
10563
|
#else
|
|
10562
10564
|
__Pyx_SET_SIZE(copy, -Py_SIZE(copy));
|
|
10563
10565
|
#endif
|
|
@@ -11193,6 +11195,13 @@ try_unpack:
|
|
|
11193
11195
|
|
|
11194
11196
|
/* PyObjectCallMethod0 */
|
|
11195
11197
|
static PyObject* __Pyx_PyObject_CallMethod0(PyObject* obj, PyObject* method_name) {
|
|
11198
|
+
#if CYTHON_VECTORCALL && (__PYX_LIMITED_VERSION_HEX >= 0x030C0000 || (!CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX >= 0x03090000))
|
|
11199
|
+
PyObject *args[1] = {obj};
|
|
11200
|
+
(void) __Pyx_PyObject_GetMethod;
|
|
11201
|
+
(void) __Pyx_PyObject_CallOneArg;
|
|
11202
|
+
(void) __Pyx_PyObject_CallNoArg;
|
|
11203
|
+
return PyObject_VectorcallMethod(method_name, args, 1 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL);
|
|
11204
|
+
#else
|
|
11196
11205
|
PyObject *method = NULL, *result = NULL;
|
|
11197
11206
|
int is_method = __Pyx_PyObject_GetMethod(obj, method_name, &method);
|
|
11198
11207
|
if (likely(is_method)) {
|
|
@@ -11205,6 +11214,7 @@ static PyObject* __Pyx_PyObject_CallMethod0(PyObject* obj, PyObject* method_name
|
|
|
11205
11214
|
Py_DECREF(method);
|
|
11206
11215
|
bad:
|
|
11207
11216
|
return result;
|
|
11217
|
+
#endif
|
|
11208
11218
|
}
|
|
11209
11219
|
|
|
11210
11220
|
/* CallUnboundCMethod0 */
|
|
@@ -11742,6 +11752,7 @@ static int __Pyx_fix_up_extension_type_from_spec(PyType_Spec *spec, PyTypeObject
|
|
|
11742
11752
|
changed = 1;
|
|
11743
11753
|
}
|
|
11744
11754
|
#endif // CYTHON_METH_FASTCALL
|
|
11755
|
+
#if !CYTHON_COMPILING_IN_PYPY
|
|
11745
11756
|
else if (strcmp(memb->name, "__module__") == 0) {
|
|
11746
11757
|
PyObject *descr;
|
|
11747
11758
|
assert(memb->type == T_OBJECT);
|
|
@@ -11756,11 +11767,13 @@ static int __Pyx_fix_up_extension_type_from_spec(PyType_Spec *spec, PyTypeObject
|
|
|
11756
11767
|
}
|
|
11757
11768
|
changed = 1;
|
|
11758
11769
|
}
|
|
11770
|
+
#endif // !CYTHON_COMPILING_IN_PYPY
|
|
11759
11771
|
}
|
|
11760
11772
|
memb++;
|
|
11761
11773
|
}
|
|
11762
11774
|
}
|
|
11763
11775
|
#endif // !CYTHON_COMPILING_IN_LIMITED_API
|
|
11776
|
+
#if !CYTHON_COMPILING_IN_PYPY
|
|
11764
11777
|
slot = spec->slots;
|
|
11765
11778
|
while (slot && slot->slot && slot->slot != Py_tp_getset)
|
|
11766
11779
|
slot++;
|
|
@@ -11792,6 +11805,7 @@ static int __Pyx_fix_up_extension_type_from_spec(PyType_Spec *spec, PyTypeObject
|
|
|
11792
11805
|
++getset;
|
|
11793
11806
|
}
|
|
11794
11807
|
}
|
|
11808
|
+
#endif // !CYTHON_COMPILING_IN_PYPY
|
|
11795
11809
|
if (changed)
|
|
11796
11810
|
PyType_Modified(type);
|
|
11797
11811
|
#endif // PY_VERSION_HEX > 0x030900B1
|
|
@@ -12227,7 +12241,7 @@ bad:
|
|
|
12227
12241
|
}
|
|
12228
12242
|
|
|
12229
12243
|
/* CommonTypesMetaclass */
|
|
12230
|
-
PyObject* __pyx_CommonTypesMetaclass_get_module(CYTHON_UNUSED PyObject *self, CYTHON_UNUSED void* context) {
|
|
12244
|
+
static PyObject* __pyx_CommonTypesMetaclass_get_module(CYTHON_UNUSED PyObject *self, CYTHON_UNUSED void* context) {
|
|
12231
12245
|
return PyUnicode_FromString(__PYX_ABI_MODULE_NAME);
|
|
12232
12246
|
}
|
|
12233
12247
|
static PyGetSetDef __pyx_CommonTypesMetaclass_getset[] = {
|
|
@@ -14847,7 +14861,7 @@ static CYTHON_INLINE PyObject* __Pyx_PyObject_Call2Args(PyObject* function, PyOb
|
|
|
14847
14861
|
}
|
|
14848
14862
|
|
|
14849
14863
|
/* PyObjectCallMethod1 */
|
|
14850
|
-
#if !(CYTHON_VECTORCALL && __PYX_LIMITED_VERSION_HEX >= 0x030C0000)
|
|
14864
|
+
#if !(CYTHON_VECTORCALL && (__PYX_LIMITED_VERSION_HEX >= 0x030C0000 || (!CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX >= 0x03090000)))
|
|
14851
14865
|
static PyObject* __Pyx__PyObject_CallMethod1(PyObject* method, PyObject* arg) {
|
|
14852
14866
|
PyObject *result = __Pyx_PyObject_CallOneArg(method, arg);
|
|
14853
14867
|
Py_DECREF(method);
|
|
@@ -14855,7 +14869,7 @@ static PyObject* __Pyx__PyObject_CallMethod1(PyObject* method, PyObject* arg) {
|
|
|
14855
14869
|
}
|
|
14856
14870
|
#endif
|
|
14857
14871
|
static PyObject* __Pyx_PyObject_CallMethod1(PyObject* obj, PyObject* method_name, PyObject* arg) {
|
|
14858
|
-
#if CYTHON_VECTORCALL && __PYX_LIMITED_VERSION_HEX >= 0x030C0000
|
|
14872
|
+
#if CYTHON_VECTORCALL && (__PYX_LIMITED_VERSION_HEX >= 0x030C0000 || (!CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX >= 0x03090000))
|
|
14859
14873
|
PyObject *args[2] = {obj, arg};
|
|
14860
14874
|
(void) __Pyx_PyObject_GetMethod;
|
|
14861
14875
|
(void) __Pyx_PyObject_CallOneArg;
|
|
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()
|