xref: /aosp_15_r20/external/fonttools/Lib/fontTools/fontBuilder.py (revision e1fe3e4ad2793916b15cccdc4a7da52a7e1dd0e9)
1*e1fe3e4aSElliott Hughes__all__ = ["FontBuilder"]
2*e1fe3e4aSElliott Hughes
3*e1fe3e4aSElliott Hughes"""
4*e1fe3e4aSElliott HughesThis module is *experimental*, meaning it still may evolve and change.
5*e1fe3e4aSElliott Hughes
6*e1fe3e4aSElliott HughesThe `FontBuilder` class is a convenient helper to construct working TTF or
7*e1fe3e4aSElliott HughesOTF fonts from scratch.
8*e1fe3e4aSElliott Hughes
9*e1fe3e4aSElliott HughesNote that the various setup methods cannot be called in arbitrary order,
10*e1fe3e4aSElliott Hughesdue to various interdependencies between OpenType tables. Here is an order
11*e1fe3e4aSElliott Hughesthat works:
12*e1fe3e4aSElliott Hughes
13*e1fe3e4aSElliott Hughes    fb = FontBuilder(...)
14*e1fe3e4aSElliott Hughes    fb.setupGlyphOrder(...)
15*e1fe3e4aSElliott Hughes    fb.setupCharacterMap(...)
16*e1fe3e4aSElliott Hughes    fb.setupGlyf(...) --or-- fb.setupCFF(...)
17*e1fe3e4aSElliott Hughes    fb.setupHorizontalMetrics(...)
18*e1fe3e4aSElliott Hughes    fb.setupHorizontalHeader()
19*e1fe3e4aSElliott Hughes    fb.setupNameTable(...)
20*e1fe3e4aSElliott Hughes    fb.setupOS2()
21*e1fe3e4aSElliott Hughes    fb.addOpenTypeFeatures(...)
22*e1fe3e4aSElliott Hughes    fb.setupPost()
23*e1fe3e4aSElliott Hughes    fb.save(...)
24*e1fe3e4aSElliott Hughes
25*e1fe3e4aSElliott HughesHere is how to build a minimal TTF:
26*e1fe3e4aSElliott Hughes
27*e1fe3e4aSElliott Hughes```python
28*e1fe3e4aSElliott Hughesfrom fontTools.fontBuilder import FontBuilder
29*e1fe3e4aSElliott Hughesfrom fontTools.pens.ttGlyphPen import TTGlyphPen
30*e1fe3e4aSElliott Hughes
31*e1fe3e4aSElliott Hughes
32*e1fe3e4aSElliott Hughesdef drawTestGlyph(pen):
33*e1fe3e4aSElliott Hughes    pen.moveTo((100, 100))
34*e1fe3e4aSElliott Hughes    pen.lineTo((100, 1000))
35*e1fe3e4aSElliott Hughes    pen.qCurveTo((200, 900), (400, 900), (500, 1000))
36*e1fe3e4aSElliott Hughes    pen.lineTo((500, 100))
37*e1fe3e4aSElliott Hughes    pen.closePath()
38*e1fe3e4aSElliott Hughes
39*e1fe3e4aSElliott Hughes
40*e1fe3e4aSElliott Hughesfb = FontBuilder(1024, isTTF=True)
41*e1fe3e4aSElliott Hughesfb.setupGlyphOrder([".notdef", ".null", "space", "A", "a"])
42*e1fe3e4aSElliott Hughesfb.setupCharacterMap({32: "space", 65: "A", 97: "a"})
43*e1fe3e4aSElliott HughesadvanceWidths = {".notdef": 600, "space": 500, "A": 600, "a": 600, ".null": 0}
44*e1fe3e4aSElliott Hughes
45*e1fe3e4aSElliott HughesfamilyName = "HelloTestFont"
46*e1fe3e4aSElliott HughesstyleName = "TotallyNormal"
47*e1fe3e4aSElliott Hughesversion = "0.1"
48*e1fe3e4aSElliott Hughes
49*e1fe3e4aSElliott HughesnameStrings = dict(
50*e1fe3e4aSElliott Hughes    familyName=dict(en=familyName, nl="HalloTestFont"),
51*e1fe3e4aSElliott Hughes    styleName=dict(en=styleName, nl="TotaalNormaal"),
52*e1fe3e4aSElliott Hughes    uniqueFontIdentifier="fontBuilder: " + familyName + "." + styleName,
53*e1fe3e4aSElliott Hughes    fullName=familyName + "-" + styleName,
54*e1fe3e4aSElliott Hughes    psName=familyName + "-" + styleName,
55*e1fe3e4aSElliott Hughes    version="Version " + version,
56*e1fe3e4aSElliott Hughes)
57*e1fe3e4aSElliott Hughes
58*e1fe3e4aSElliott Hughespen = TTGlyphPen(None)
59*e1fe3e4aSElliott HughesdrawTestGlyph(pen)
60*e1fe3e4aSElliott Hughesglyph = pen.glyph()
61*e1fe3e4aSElliott Hughesglyphs = {".notdef": glyph, "space": glyph, "A": glyph, "a": glyph, ".null": glyph}
62*e1fe3e4aSElliott Hughesfb.setupGlyf(glyphs)
63*e1fe3e4aSElliott Hughesmetrics = {}
64*e1fe3e4aSElliott HughesglyphTable = fb.font["glyf"]
65*e1fe3e4aSElliott Hughesfor gn, advanceWidth in advanceWidths.items():
66*e1fe3e4aSElliott Hughes    metrics[gn] = (advanceWidth, glyphTable[gn].xMin)
67*e1fe3e4aSElliott Hughesfb.setupHorizontalMetrics(metrics)
68*e1fe3e4aSElliott Hughesfb.setupHorizontalHeader(ascent=824, descent=-200)
69*e1fe3e4aSElliott Hughesfb.setupNameTable(nameStrings)
70*e1fe3e4aSElliott Hughesfb.setupOS2(sTypoAscender=824, usWinAscent=824, usWinDescent=200)
71*e1fe3e4aSElliott Hughesfb.setupPost()
72*e1fe3e4aSElliott Hughesfb.save("test.ttf")
73*e1fe3e4aSElliott Hughes```
74*e1fe3e4aSElliott Hughes
75*e1fe3e4aSElliott HughesAnd here's how to build a minimal OTF:
76*e1fe3e4aSElliott Hughes
77*e1fe3e4aSElliott Hughes```python
78*e1fe3e4aSElliott Hughesfrom fontTools.fontBuilder import FontBuilder
79*e1fe3e4aSElliott Hughesfrom fontTools.pens.t2CharStringPen import T2CharStringPen
80*e1fe3e4aSElliott Hughes
81*e1fe3e4aSElliott Hughes
82*e1fe3e4aSElliott Hughesdef drawTestGlyph(pen):
83*e1fe3e4aSElliott Hughes    pen.moveTo((100, 100))
84*e1fe3e4aSElliott Hughes    pen.lineTo((100, 1000))
85*e1fe3e4aSElliott Hughes    pen.curveTo((200, 900), (400, 900), (500, 1000))
86*e1fe3e4aSElliott Hughes    pen.lineTo((500, 100))
87*e1fe3e4aSElliott Hughes    pen.closePath()
88*e1fe3e4aSElliott Hughes
89*e1fe3e4aSElliott Hughes
90*e1fe3e4aSElliott Hughesfb = FontBuilder(1024, isTTF=False)
91*e1fe3e4aSElliott Hughesfb.setupGlyphOrder([".notdef", ".null", "space", "A", "a"])
92*e1fe3e4aSElliott Hughesfb.setupCharacterMap({32: "space", 65: "A", 97: "a"})
93*e1fe3e4aSElliott HughesadvanceWidths = {".notdef": 600, "space": 500, "A": 600, "a": 600, ".null": 0}
94*e1fe3e4aSElliott Hughes
95*e1fe3e4aSElliott HughesfamilyName = "HelloTestFont"
96*e1fe3e4aSElliott HughesstyleName = "TotallyNormal"
97*e1fe3e4aSElliott Hughesversion = "0.1"
98*e1fe3e4aSElliott Hughes
99*e1fe3e4aSElliott HughesnameStrings = dict(
100*e1fe3e4aSElliott Hughes    familyName=dict(en=familyName, nl="HalloTestFont"),
101*e1fe3e4aSElliott Hughes    styleName=dict(en=styleName, nl="TotaalNormaal"),
102*e1fe3e4aSElliott Hughes    uniqueFontIdentifier="fontBuilder: " + familyName + "." + styleName,
103*e1fe3e4aSElliott Hughes    fullName=familyName + "-" + styleName,
104*e1fe3e4aSElliott Hughes    psName=familyName + "-" + styleName,
105*e1fe3e4aSElliott Hughes    version="Version " + version,
106*e1fe3e4aSElliott Hughes)
107*e1fe3e4aSElliott Hughes
108*e1fe3e4aSElliott Hughespen = T2CharStringPen(600, None)
109*e1fe3e4aSElliott HughesdrawTestGlyph(pen)
110*e1fe3e4aSElliott HughescharString = pen.getCharString()
111*e1fe3e4aSElliott HughescharStrings = {
112*e1fe3e4aSElliott Hughes    ".notdef": charString,
113*e1fe3e4aSElliott Hughes    "space": charString,
114*e1fe3e4aSElliott Hughes    "A": charString,
115*e1fe3e4aSElliott Hughes    "a": charString,
116*e1fe3e4aSElliott Hughes    ".null": charString,
117*e1fe3e4aSElliott Hughes}
118*e1fe3e4aSElliott Hughesfb.setupCFF(nameStrings["psName"], {"FullName": nameStrings["psName"]}, charStrings, {})
119*e1fe3e4aSElliott Hugheslsb = {gn: cs.calcBounds(None)[0] for gn, cs in charStrings.items()}
120*e1fe3e4aSElliott Hughesmetrics = {}
121*e1fe3e4aSElliott Hughesfor gn, advanceWidth in advanceWidths.items():
122*e1fe3e4aSElliott Hughes    metrics[gn] = (advanceWidth, lsb[gn])
123*e1fe3e4aSElliott Hughesfb.setupHorizontalMetrics(metrics)
124*e1fe3e4aSElliott Hughesfb.setupHorizontalHeader(ascent=824, descent=200)
125*e1fe3e4aSElliott Hughesfb.setupNameTable(nameStrings)
126*e1fe3e4aSElliott Hughesfb.setupOS2(sTypoAscender=824, usWinAscent=824, usWinDescent=200)
127*e1fe3e4aSElliott Hughesfb.setupPost()
128*e1fe3e4aSElliott Hughesfb.save("test.otf")
129*e1fe3e4aSElliott Hughes```
130*e1fe3e4aSElliott Hughes"""
131*e1fe3e4aSElliott Hughes
132*e1fe3e4aSElliott Hughesfrom .ttLib import TTFont, newTable
133*e1fe3e4aSElliott Hughesfrom .ttLib.tables._c_m_a_p import cmap_classes
134*e1fe3e4aSElliott Hughesfrom .ttLib.tables._g_l_y_f import flagCubic
135*e1fe3e4aSElliott Hughesfrom .ttLib.tables.O_S_2f_2 import Panose
136*e1fe3e4aSElliott Hughesfrom .misc.timeTools import timestampNow
137*e1fe3e4aSElliott Hughesimport struct
138*e1fe3e4aSElliott Hughesfrom collections import OrderedDict
139*e1fe3e4aSElliott Hughes
140*e1fe3e4aSElliott Hughes
141*e1fe3e4aSElliott Hughes_headDefaults = dict(
142*e1fe3e4aSElliott Hughes    tableVersion=1.0,
143*e1fe3e4aSElliott Hughes    fontRevision=1.0,
144*e1fe3e4aSElliott Hughes    checkSumAdjustment=0,
145*e1fe3e4aSElliott Hughes    magicNumber=0x5F0F3CF5,
146*e1fe3e4aSElliott Hughes    flags=0x0003,
147*e1fe3e4aSElliott Hughes    unitsPerEm=1000,
148*e1fe3e4aSElliott Hughes    created=0,
149*e1fe3e4aSElliott Hughes    modified=0,
150*e1fe3e4aSElliott Hughes    xMin=0,
151*e1fe3e4aSElliott Hughes    yMin=0,
152*e1fe3e4aSElliott Hughes    xMax=0,
153*e1fe3e4aSElliott Hughes    yMax=0,
154*e1fe3e4aSElliott Hughes    macStyle=0,
155*e1fe3e4aSElliott Hughes    lowestRecPPEM=3,
156*e1fe3e4aSElliott Hughes    fontDirectionHint=2,
157*e1fe3e4aSElliott Hughes    indexToLocFormat=0,
158*e1fe3e4aSElliott Hughes    glyphDataFormat=0,
159*e1fe3e4aSElliott Hughes)
160*e1fe3e4aSElliott Hughes
161*e1fe3e4aSElliott Hughes_maxpDefaultsTTF = dict(
162*e1fe3e4aSElliott Hughes    tableVersion=0x00010000,
163*e1fe3e4aSElliott Hughes    numGlyphs=0,
164*e1fe3e4aSElliott Hughes    maxPoints=0,
165*e1fe3e4aSElliott Hughes    maxContours=0,
166*e1fe3e4aSElliott Hughes    maxCompositePoints=0,
167*e1fe3e4aSElliott Hughes    maxCompositeContours=0,
168*e1fe3e4aSElliott Hughes    maxZones=2,
169*e1fe3e4aSElliott Hughes    maxTwilightPoints=0,
170*e1fe3e4aSElliott Hughes    maxStorage=0,
171*e1fe3e4aSElliott Hughes    maxFunctionDefs=0,
172*e1fe3e4aSElliott Hughes    maxInstructionDefs=0,
173*e1fe3e4aSElliott Hughes    maxStackElements=0,
174*e1fe3e4aSElliott Hughes    maxSizeOfInstructions=0,
175*e1fe3e4aSElliott Hughes    maxComponentElements=0,
176*e1fe3e4aSElliott Hughes    maxComponentDepth=0,
177*e1fe3e4aSElliott Hughes)
178*e1fe3e4aSElliott Hughes_maxpDefaultsOTF = dict(
179*e1fe3e4aSElliott Hughes    tableVersion=0x00005000,
180*e1fe3e4aSElliott Hughes    numGlyphs=0,
181*e1fe3e4aSElliott Hughes)
182*e1fe3e4aSElliott Hughes
183*e1fe3e4aSElliott Hughes_postDefaults = dict(
184*e1fe3e4aSElliott Hughes    formatType=3.0,
185*e1fe3e4aSElliott Hughes    italicAngle=0,
186*e1fe3e4aSElliott Hughes    underlinePosition=0,
187*e1fe3e4aSElliott Hughes    underlineThickness=0,
188*e1fe3e4aSElliott Hughes    isFixedPitch=0,
189*e1fe3e4aSElliott Hughes    minMemType42=0,
190*e1fe3e4aSElliott Hughes    maxMemType42=0,
191*e1fe3e4aSElliott Hughes    minMemType1=0,
192*e1fe3e4aSElliott Hughes    maxMemType1=0,
193*e1fe3e4aSElliott Hughes)
194*e1fe3e4aSElliott Hughes
195*e1fe3e4aSElliott Hughes_hheaDefaults = dict(
196*e1fe3e4aSElliott Hughes    tableVersion=0x00010000,
197*e1fe3e4aSElliott Hughes    ascent=0,
198*e1fe3e4aSElliott Hughes    descent=0,
199*e1fe3e4aSElliott Hughes    lineGap=0,
200*e1fe3e4aSElliott Hughes    advanceWidthMax=0,
201*e1fe3e4aSElliott Hughes    minLeftSideBearing=0,
202*e1fe3e4aSElliott Hughes    minRightSideBearing=0,
203*e1fe3e4aSElliott Hughes    xMaxExtent=0,
204*e1fe3e4aSElliott Hughes    caretSlopeRise=1,
205*e1fe3e4aSElliott Hughes    caretSlopeRun=0,
206*e1fe3e4aSElliott Hughes    caretOffset=0,
207*e1fe3e4aSElliott Hughes    reserved0=0,
208*e1fe3e4aSElliott Hughes    reserved1=0,
209*e1fe3e4aSElliott Hughes    reserved2=0,
210*e1fe3e4aSElliott Hughes    reserved3=0,
211*e1fe3e4aSElliott Hughes    metricDataFormat=0,
212*e1fe3e4aSElliott Hughes    numberOfHMetrics=0,
213*e1fe3e4aSElliott Hughes)
214*e1fe3e4aSElliott Hughes
215*e1fe3e4aSElliott Hughes_vheaDefaults = dict(
216*e1fe3e4aSElliott Hughes    tableVersion=0x00010000,
217*e1fe3e4aSElliott Hughes    ascent=0,
218*e1fe3e4aSElliott Hughes    descent=0,
219*e1fe3e4aSElliott Hughes    lineGap=0,
220*e1fe3e4aSElliott Hughes    advanceHeightMax=0,
221*e1fe3e4aSElliott Hughes    minTopSideBearing=0,
222*e1fe3e4aSElliott Hughes    minBottomSideBearing=0,
223*e1fe3e4aSElliott Hughes    yMaxExtent=0,
224*e1fe3e4aSElliott Hughes    caretSlopeRise=0,
225*e1fe3e4aSElliott Hughes    caretSlopeRun=0,
226*e1fe3e4aSElliott Hughes    reserved0=0,
227*e1fe3e4aSElliott Hughes    reserved1=0,
228*e1fe3e4aSElliott Hughes    reserved2=0,
229*e1fe3e4aSElliott Hughes    reserved3=0,
230*e1fe3e4aSElliott Hughes    reserved4=0,
231*e1fe3e4aSElliott Hughes    metricDataFormat=0,
232*e1fe3e4aSElliott Hughes    numberOfVMetrics=0,
233*e1fe3e4aSElliott Hughes)
234*e1fe3e4aSElliott Hughes
235*e1fe3e4aSElliott Hughes_nameIDs = dict(
236*e1fe3e4aSElliott Hughes    copyright=0,
237*e1fe3e4aSElliott Hughes    familyName=1,
238*e1fe3e4aSElliott Hughes    styleName=2,
239*e1fe3e4aSElliott Hughes    uniqueFontIdentifier=3,
240*e1fe3e4aSElliott Hughes    fullName=4,
241*e1fe3e4aSElliott Hughes    version=5,
242*e1fe3e4aSElliott Hughes    psName=6,
243*e1fe3e4aSElliott Hughes    trademark=7,
244*e1fe3e4aSElliott Hughes    manufacturer=8,
245*e1fe3e4aSElliott Hughes    designer=9,
246*e1fe3e4aSElliott Hughes    description=10,
247*e1fe3e4aSElliott Hughes    vendorURL=11,
248*e1fe3e4aSElliott Hughes    designerURL=12,
249*e1fe3e4aSElliott Hughes    licenseDescription=13,
250*e1fe3e4aSElliott Hughes    licenseInfoURL=14,
251*e1fe3e4aSElliott Hughes    # reserved = 15,
252*e1fe3e4aSElliott Hughes    typographicFamily=16,
253*e1fe3e4aSElliott Hughes    typographicSubfamily=17,
254*e1fe3e4aSElliott Hughes    compatibleFullName=18,
255*e1fe3e4aSElliott Hughes    sampleText=19,
256*e1fe3e4aSElliott Hughes    postScriptCIDFindfontName=20,
257*e1fe3e4aSElliott Hughes    wwsFamilyName=21,
258*e1fe3e4aSElliott Hughes    wwsSubfamilyName=22,
259*e1fe3e4aSElliott Hughes    lightBackgroundPalette=23,
260*e1fe3e4aSElliott Hughes    darkBackgroundPalette=24,
261*e1fe3e4aSElliott Hughes    variationsPostScriptNamePrefix=25,
262*e1fe3e4aSElliott Hughes)
263*e1fe3e4aSElliott Hughes
264*e1fe3e4aSElliott Hughes# to insert in setupNameTable doc string:
265*e1fe3e4aSElliott Hughes# print("\n".join(("%s (nameID %s)" % (k, v)) for k, v in sorted(_nameIDs.items(), key=lambda x: x[1])))
266*e1fe3e4aSElliott Hughes
267*e1fe3e4aSElliott Hughes_panoseDefaults = Panose()
268*e1fe3e4aSElliott Hughes
269*e1fe3e4aSElliott Hughes_OS2Defaults = dict(
270*e1fe3e4aSElliott Hughes    version=3,
271*e1fe3e4aSElliott Hughes    xAvgCharWidth=0,
272*e1fe3e4aSElliott Hughes    usWeightClass=400,
273*e1fe3e4aSElliott Hughes    usWidthClass=5,
274*e1fe3e4aSElliott Hughes    fsType=0x0004,  # default: Preview & Print embedding
275*e1fe3e4aSElliott Hughes    ySubscriptXSize=0,
276*e1fe3e4aSElliott Hughes    ySubscriptYSize=0,
277*e1fe3e4aSElliott Hughes    ySubscriptXOffset=0,
278*e1fe3e4aSElliott Hughes    ySubscriptYOffset=0,
279*e1fe3e4aSElliott Hughes    ySuperscriptXSize=0,
280*e1fe3e4aSElliott Hughes    ySuperscriptYSize=0,
281*e1fe3e4aSElliott Hughes    ySuperscriptXOffset=0,
282*e1fe3e4aSElliott Hughes    ySuperscriptYOffset=0,
283*e1fe3e4aSElliott Hughes    yStrikeoutSize=0,
284*e1fe3e4aSElliott Hughes    yStrikeoutPosition=0,
285*e1fe3e4aSElliott Hughes    sFamilyClass=0,
286*e1fe3e4aSElliott Hughes    panose=_panoseDefaults,
287*e1fe3e4aSElliott Hughes    ulUnicodeRange1=0,
288*e1fe3e4aSElliott Hughes    ulUnicodeRange2=0,
289*e1fe3e4aSElliott Hughes    ulUnicodeRange3=0,
290*e1fe3e4aSElliott Hughes    ulUnicodeRange4=0,
291*e1fe3e4aSElliott Hughes    achVendID="????",
292*e1fe3e4aSElliott Hughes    fsSelection=0,
293*e1fe3e4aSElliott Hughes    usFirstCharIndex=0,
294*e1fe3e4aSElliott Hughes    usLastCharIndex=0,
295*e1fe3e4aSElliott Hughes    sTypoAscender=0,
296*e1fe3e4aSElliott Hughes    sTypoDescender=0,
297*e1fe3e4aSElliott Hughes    sTypoLineGap=0,
298*e1fe3e4aSElliott Hughes    usWinAscent=0,
299*e1fe3e4aSElliott Hughes    usWinDescent=0,
300*e1fe3e4aSElliott Hughes    ulCodePageRange1=0,
301*e1fe3e4aSElliott Hughes    ulCodePageRange2=0,
302*e1fe3e4aSElliott Hughes    sxHeight=0,
303*e1fe3e4aSElliott Hughes    sCapHeight=0,
304*e1fe3e4aSElliott Hughes    usDefaultChar=0,  # .notdef
305*e1fe3e4aSElliott Hughes    usBreakChar=32,  # space
306*e1fe3e4aSElliott Hughes    usMaxContext=0,
307*e1fe3e4aSElliott Hughes    usLowerOpticalPointSize=0,
308*e1fe3e4aSElliott Hughes    usUpperOpticalPointSize=0,
309*e1fe3e4aSElliott Hughes)
310*e1fe3e4aSElliott Hughes
311*e1fe3e4aSElliott Hughes
312*e1fe3e4aSElliott Hughesclass FontBuilder(object):
313*e1fe3e4aSElliott Hughes    def __init__(self, unitsPerEm=None, font=None, isTTF=True, glyphDataFormat=0):
314*e1fe3e4aSElliott Hughes        """Initialize a FontBuilder instance.
315*e1fe3e4aSElliott Hughes
316*e1fe3e4aSElliott Hughes        If the `font` argument is not given, a new `TTFont` will be
317*e1fe3e4aSElliott Hughes        constructed, and `unitsPerEm` must be given. If `isTTF` is True,
318*e1fe3e4aSElliott Hughes        the font will be a glyf-based TTF; if `isTTF` is False it will be
319*e1fe3e4aSElliott Hughes        a CFF-based OTF.
320*e1fe3e4aSElliott Hughes
321*e1fe3e4aSElliott Hughes        The `glyphDataFormat` argument corresponds to the `head` table field
322*e1fe3e4aSElliott Hughes        that defines the format of the TrueType `glyf` table (default=0).
323*e1fe3e4aSElliott Hughes        TrueType glyphs historically can only contain quadratic splines and static
324*e1fe3e4aSElliott Hughes        components, but there's a proposal to add support for cubic Bezier curves as well
325*e1fe3e4aSElliott Hughes        as variable composites/components at
326*e1fe3e4aSElliott Hughes        https://github.com/harfbuzz/boring-expansion-spec/blob/main/glyf1.md
327*e1fe3e4aSElliott Hughes        You can experiment with the new features by setting `glyphDataFormat` to 1.
328*e1fe3e4aSElliott Hughes        A ValueError is raised if `glyphDataFormat` is left at 0 but glyphs are added
329*e1fe3e4aSElliott Hughes        that contain cubic splines or varcomposites. This is to prevent accidentally
330*e1fe3e4aSElliott Hughes        creating fonts that are incompatible with existing TrueType implementations.
331*e1fe3e4aSElliott Hughes
332*e1fe3e4aSElliott Hughes        If `font` is given, it must be a `TTFont` instance and `unitsPerEm`
333*e1fe3e4aSElliott Hughes        must _not_ be given. The `isTTF` and `glyphDataFormat` arguments will be ignored.
334*e1fe3e4aSElliott Hughes        """
335*e1fe3e4aSElliott Hughes        if font is None:
336*e1fe3e4aSElliott Hughes            self.font = TTFont(recalcTimestamp=False)
337*e1fe3e4aSElliott Hughes            self.isTTF = isTTF
338*e1fe3e4aSElliott Hughes            now = timestampNow()
339*e1fe3e4aSElliott Hughes            assert unitsPerEm is not None
340*e1fe3e4aSElliott Hughes            self.setupHead(
341*e1fe3e4aSElliott Hughes                unitsPerEm=unitsPerEm,
342*e1fe3e4aSElliott Hughes                created=now,
343*e1fe3e4aSElliott Hughes                modified=now,
344*e1fe3e4aSElliott Hughes                glyphDataFormat=glyphDataFormat,
345*e1fe3e4aSElliott Hughes            )
346*e1fe3e4aSElliott Hughes            self.setupMaxp()
347*e1fe3e4aSElliott Hughes        else:
348*e1fe3e4aSElliott Hughes            assert unitsPerEm is None
349*e1fe3e4aSElliott Hughes            self.font = font
350*e1fe3e4aSElliott Hughes            self.isTTF = "glyf" in font
351*e1fe3e4aSElliott Hughes
352*e1fe3e4aSElliott Hughes    def save(self, file):
353*e1fe3e4aSElliott Hughes        """Save the font. The 'file' argument can be either a pathname or a
354*e1fe3e4aSElliott Hughes        writable file object.
355*e1fe3e4aSElliott Hughes        """
356*e1fe3e4aSElliott Hughes        self.font.save(file)
357*e1fe3e4aSElliott Hughes
358*e1fe3e4aSElliott Hughes    def _initTableWithValues(self, tableTag, defaults, values):
359*e1fe3e4aSElliott Hughes        table = self.font[tableTag] = newTable(tableTag)
360*e1fe3e4aSElliott Hughes        for k, v in defaults.items():
361*e1fe3e4aSElliott Hughes            setattr(table, k, v)
362*e1fe3e4aSElliott Hughes        for k, v in values.items():
363*e1fe3e4aSElliott Hughes            setattr(table, k, v)
364*e1fe3e4aSElliott Hughes        return table
365*e1fe3e4aSElliott Hughes
366*e1fe3e4aSElliott Hughes    def _updateTableWithValues(self, tableTag, values):
367*e1fe3e4aSElliott Hughes        table = self.font[tableTag]
368*e1fe3e4aSElliott Hughes        for k, v in values.items():
369*e1fe3e4aSElliott Hughes            setattr(table, k, v)
370*e1fe3e4aSElliott Hughes
371*e1fe3e4aSElliott Hughes    def setupHead(self, **values):
372*e1fe3e4aSElliott Hughes        """Create a new `head` table and initialize it with default values,
373*e1fe3e4aSElliott Hughes        which can be overridden by keyword arguments.
374*e1fe3e4aSElliott Hughes        """
375*e1fe3e4aSElliott Hughes        self._initTableWithValues("head", _headDefaults, values)
376*e1fe3e4aSElliott Hughes
377*e1fe3e4aSElliott Hughes    def updateHead(self, **values):
378*e1fe3e4aSElliott Hughes        """Update the head table with the fields and values passed as
379*e1fe3e4aSElliott Hughes        keyword arguments.
380*e1fe3e4aSElliott Hughes        """
381*e1fe3e4aSElliott Hughes        self._updateTableWithValues("head", values)
382*e1fe3e4aSElliott Hughes
383*e1fe3e4aSElliott Hughes    def setupGlyphOrder(self, glyphOrder):
384*e1fe3e4aSElliott Hughes        """Set the glyph order for the font."""
385*e1fe3e4aSElliott Hughes        self.font.setGlyphOrder(glyphOrder)
386*e1fe3e4aSElliott Hughes
387*e1fe3e4aSElliott Hughes    def setupCharacterMap(self, cmapping, uvs=None, allowFallback=False):
388*e1fe3e4aSElliott Hughes        """Build the `cmap` table for the font. The `cmapping` argument should
389*e1fe3e4aSElliott Hughes        be a dict mapping unicode code points as integers to glyph names.
390*e1fe3e4aSElliott Hughes
391*e1fe3e4aSElliott Hughes        The `uvs` argument, when passed, must be a list of tuples, describing
392*e1fe3e4aSElliott Hughes        Unicode Variation Sequences. These tuples have three elements:
393*e1fe3e4aSElliott Hughes            (unicodeValue, variationSelector, glyphName)
394*e1fe3e4aSElliott Hughes        `unicodeValue` and `variationSelector` are integer code points.
395*e1fe3e4aSElliott Hughes        `glyphName` may be None, to indicate this is the default variation.
396*e1fe3e4aSElliott Hughes        Text processors will then use the cmap to find the glyph name.
397*e1fe3e4aSElliott Hughes        Each Unicode Variation Sequence should be an officially supported
398*e1fe3e4aSElliott Hughes        sequence, but this is not policed.
399*e1fe3e4aSElliott Hughes        """
400*e1fe3e4aSElliott Hughes        subTables = []
401*e1fe3e4aSElliott Hughes        highestUnicode = max(cmapping) if cmapping else 0
402*e1fe3e4aSElliott Hughes        if highestUnicode > 0xFFFF:
403*e1fe3e4aSElliott Hughes            cmapping_3_1 = dict((k, v) for k, v in cmapping.items() if k < 0x10000)
404*e1fe3e4aSElliott Hughes            subTable_3_10 = buildCmapSubTable(cmapping, 12, 3, 10)
405*e1fe3e4aSElliott Hughes            subTables.append(subTable_3_10)
406*e1fe3e4aSElliott Hughes        else:
407*e1fe3e4aSElliott Hughes            cmapping_3_1 = cmapping
408*e1fe3e4aSElliott Hughes        format = 4
409*e1fe3e4aSElliott Hughes        subTable_3_1 = buildCmapSubTable(cmapping_3_1, format, 3, 1)
410*e1fe3e4aSElliott Hughes        try:
411*e1fe3e4aSElliott Hughes            subTable_3_1.compile(self.font)
412*e1fe3e4aSElliott Hughes        except struct.error:
413*e1fe3e4aSElliott Hughes            # format 4 overflowed, fall back to format 12
414*e1fe3e4aSElliott Hughes            if not allowFallback:
415*e1fe3e4aSElliott Hughes                raise ValueError(
416*e1fe3e4aSElliott Hughes                    "cmap format 4 subtable overflowed; sort glyph order by unicode to fix."
417*e1fe3e4aSElliott Hughes                )
418*e1fe3e4aSElliott Hughes            format = 12
419*e1fe3e4aSElliott Hughes            subTable_3_1 = buildCmapSubTable(cmapping_3_1, format, 3, 1)
420*e1fe3e4aSElliott Hughes        subTables.append(subTable_3_1)
421*e1fe3e4aSElliott Hughes        subTable_0_3 = buildCmapSubTable(cmapping_3_1, format, 0, 3)
422*e1fe3e4aSElliott Hughes        subTables.append(subTable_0_3)
423*e1fe3e4aSElliott Hughes
424*e1fe3e4aSElliott Hughes        if uvs is not None:
425*e1fe3e4aSElliott Hughes            uvsDict = {}
426*e1fe3e4aSElliott Hughes            for unicodeValue, variationSelector, glyphName in uvs:
427*e1fe3e4aSElliott Hughes                if cmapping.get(unicodeValue) == glyphName:
428*e1fe3e4aSElliott Hughes                    # this is a default variation
429*e1fe3e4aSElliott Hughes                    glyphName = None
430*e1fe3e4aSElliott Hughes                if variationSelector not in uvsDict:
431*e1fe3e4aSElliott Hughes                    uvsDict[variationSelector] = []
432*e1fe3e4aSElliott Hughes                uvsDict[variationSelector].append((unicodeValue, glyphName))
433*e1fe3e4aSElliott Hughes            uvsSubTable = buildCmapSubTable({}, 14, 0, 5)
434*e1fe3e4aSElliott Hughes            uvsSubTable.uvsDict = uvsDict
435*e1fe3e4aSElliott Hughes            subTables.append(uvsSubTable)
436*e1fe3e4aSElliott Hughes
437*e1fe3e4aSElliott Hughes        self.font["cmap"] = newTable("cmap")
438*e1fe3e4aSElliott Hughes        self.font["cmap"].tableVersion = 0
439*e1fe3e4aSElliott Hughes        self.font["cmap"].tables = subTables
440*e1fe3e4aSElliott Hughes
441*e1fe3e4aSElliott Hughes    def setupNameTable(self, nameStrings, windows=True, mac=True):
442*e1fe3e4aSElliott Hughes        """Create the `name` table for the font. The `nameStrings` argument must
443*e1fe3e4aSElliott Hughes        be a dict, mapping nameIDs or descriptive names for the nameIDs to name
444*e1fe3e4aSElliott Hughes        record values. A value is either a string, or a dict, mapping language codes
445*e1fe3e4aSElliott Hughes        to strings, to allow localized name table entries.
446*e1fe3e4aSElliott Hughes
447*e1fe3e4aSElliott Hughes        By default, both Windows (platformID=3) and Macintosh (platformID=1) name
448*e1fe3e4aSElliott Hughes        records are added, unless any of `windows` or `mac` arguments is False.
449*e1fe3e4aSElliott Hughes
450*e1fe3e4aSElliott Hughes        The following descriptive names are available for nameIDs:
451*e1fe3e4aSElliott Hughes
452*e1fe3e4aSElliott Hughes            copyright (nameID 0)
453*e1fe3e4aSElliott Hughes            familyName (nameID 1)
454*e1fe3e4aSElliott Hughes            styleName (nameID 2)
455*e1fe3e4aSElliott Hughes            uniqueFontIdentifier (nameID 3)
456*e1fe3e4aSElliott Hughes            fullName (nameID 4)
457*e1fe3e4aSElliott Hughes            version (nameID 5)
458*e1fe3e4aSElliott Hughes            psName (nameID 6)
459*e1fe3e4aSElliott Hughes            trademark (nameID 7)
460*e1fe3e4aSElliott Hughes            manufacturer (nameID 8)
461*e1fe3e4aSElliott Hughes            designer (nameID 9)
462*e1fe3e4aSElliott Hughes            description (nameID 10)
463*e1fe3e4aSElliott Hughes            vendorURL (nameID 11)
464*e1fe3e4aSElliott Hughes            designerURL (nameID 12)
465*e1fe3e4aSElliott Hughes            licenseDescription (nameID 13)
466*e1fe3e4aSElliott Hughes            licenseInfoURL (nameID 14)
467*e1fe3e4aSElliott Hughes            typographicFamily (nameID 16)
468*e1fe3e4aSElliott Hughes            typographicSubfamily (nameID 17)
469*e1fe3e4aSElliott Hughes            compatibleFullName (nameID 18)
470*e1fe3e4aSElliott Hughes            sampleText (nameID 19)
471*e1fe3e4aSElliott Hughes            postScriptCIDFindfontName (nameID 20)
472*e1fe3e4aSElliott Hughes            wwsFamilyName (nameID 21)
473*e1fe3e4aSElliott Hughes            wwsSubfamilyName (nameID 22)
474*e1fe3e4aSElliott Hughes            lightBackgroundPalette (nameID 23)
475*e1fe3e4aSElliott Hughes            darkBackgroundPalette (nameID 24)
476*e1fe3e4aSElliott Hughes            variationsPostScriptNamePrefix (nameID 25)
477*e1fe3e4aSElliott Hughes        """
478*e1fe3e4aSElliott Hughes        nameTable = self.font["name"] = newTable("name")
479*e1fe3e4aSElliott Hughes        nameTable.names = []
480*e1fe3e4aSElliott Hughes
481*e1fe3e4aSElliott Hughes        for nameName, nameValue in nameStrings.items():
482*e1fe3e4aSElliott Hughes            if isinstance(nameName, int):
483*e1fe3e4aSElliott Hughes                nameID = nameName
484*e1fe3e4aSElliott Hughes            else:
485*e1fe3e4aSElliott Hughes                nameID = _nameIDs[nameName]
486*e1fe3e4aSElliott Hughes            if isinstance(nameValue, str):
487*e1fe3e4aSElliott Hughes                nameValue = dict(en=nameValue)
488*e1fe3e4aSElliott Hughes            nameTable.addMultilingualName(
489*e1fe3e4aSElliott Hughes                nameValue, ttFont=self.font, nameID=nameID, windows=windows, mac=mac
490*e1fe3e4aSElliott Hughes            )
491*e1fe3e4aSElliott Hughes
492*e1fe3e4aSElliott Hughes    def setupOS2(self, **values):
493*e1fe3e4aSElliott Hughes        """Create a new `OS/2` table and initialize it with default values,
494*e1fe3e4aSElliott Hughes        which can be overridden by keyword arguments.
495*e1fe3e4aSElliott Hughes        """
496*e1fe3e4aSElliott Hughes        self._initTableWithValues("OS/2", _OS2Defaults, values)
497*e1fe3e4aSElliott Hughes        if "xAvgCharWidth" not in values:
498*e1fe3e4aSElliott Hughes            assert (
499*e1fe3e4aSElliott Hughes                "hmtx" in self.font
500*e1fe3e4aSElliott Hughes            ), "the 'hmtx' table must be setup before the 'OS/2' table"
501*e1fe3e4aSElliott Hughes            self.font["OS/2"].recalcAvgCharWidth(self.font)
502*e1fe3e4aSElliott Hughes        if not (
503*e1fe3e4aSElliott Hughes            "ulUnicodeRange1" in values
504*e1fe3e4aSElliott Hughes            or "ulUnicodeRange2" in values
505*e1fe3e4aSElliott Hughes            or "ulUnicodeRange3" in values
506*e1fe3e4aSElliott Hughes            or "ulUnicodeRange3" in values
507*e1fe3e4aSElliott Hughes        ):
508*e1fe3e4aSElliott Hughes            assert (
509*e1fe3e4aSElliott Hughes                "cmap" in self.font
510*e1fe3e4aSElliott Hughes            ), "the 'cmap' table must be setup before the 'OS/2' table"
511*e1fe3e4aSElliott Hughes            self.font["OS/2"].recalcUnicodeRanges(self.font)
512*e1fe3e4aSElliott Hughes
513*e1fe3e4aSElliott Hughes    def setupCFF(self, psName, fontInfo, charStringsDict, privateDict):
514*e1fe3e4aSElliott Hughes        from .cffLib import (
515*e1fe3e4aSElliott Hughes            CFFFontSet,
516*e1fe3e4aSElliott Hughes            TopDictIndex,
517*e1fe3e4aSElliott Hughes            TopDict,
518*e1fe3e4aSElliott Hughes            CharStrings,
519*e1fe3e4aSElliott Hughes            GlobalSubrsIndex,
520*e1fe3e4aSElliott Hughes            PrivateDict,
521*e1fe3e4aSElliott Hughes        )
522*e1fe3e4aSElliott Hughes
523*e1fe3e4aSElliott Hughes        assert not self.isTTF
524*e1fe3e4aSElliott Hughes        self.font.sfntVersion = "OTTO"
525*e1fe3e4aSElliott Hughes        fontSet = CFFFontSet()
526*e1fe3e4aSElliott Hughes        fontSet.major = 1
527*e1fe3e4aSElliott Hughes        fontSet.minor = 0
528*e1fe3e4aSElliott Hughes        fontSet.otFont = self.font
529*e1fe3e4aSElliott Hughes        fontSet.fontNames = [psName]
530*e1fe3e4aSElliott Hughes        fontSet.topDictIndex = TopDictIndex()
531*e1fe3e4aSElliott Hughes
532*e1fe3e4aSElliott Hughes        globalSubrs = GlobalSubrsIndex()
533*e1fe3e4aSElliott Hughes        fontSet.GlobalSubrs = globalSubrs
534*e1fe3e4aSElliott Hughes        private = PrivateDict()
535*e1fe3e4aSElliott Hughes        for key, value in privateDict.items():
536*e1fe3e4aSElliott Hughes            setattr(private, key, value)
537*e1fe3e4aSElliott Hughes        fdSelect = None
538*e1fe3e4aSElliott Hughes        fdArray = None
539*e1fe3e4aSElliott Hughes
540*e1fe3e4aSElliott Hughes        topDict = TopDict()
541*e1fe3e4aSElliott Hughes        topDict.charset = self.font.getGlyphOrder()
542*e1fe3e4aSElliott Hughes        topDict.Private = private
543*e1fe3e4aSElliott Hughes        topDict.GlobalSubrs = fontSet.GlobalSubrs
544*e1fe3e4aSElliott Hughes        for key, value in fontInfo.items():
545*e1fe3e4aSElliott Hughes            setattr(topDict, key, value)
546*e1fe3e4aSElliott Hughes        if "FontMatrix" not in fontInfo:
547*e1fe3e4aSElliott Hughes            scale = 1 / self.font["head"].unitsPerEm
548*e1fe3e4aSElliott Hughes            topDict.FontMatrix = [scale, 0, 0, scale, 0, 0]
549*e1fe3e4aSElliott Hughes
550*e1fe3e4aSElliott Hughes        charStrings = CharStrings(
551*e1fe3e4aSElliott Hughes            None, topDict.charset, globalSubrs, private, fdSelect, fdArray
552*e1fe3e4aSElliott Hughes        )
553*e1fe3e4aSElliott Hughes        for glyphName, charString in charStringsDict.items():
554*e1fe3e4aSElliott Hughes            charString.private = private
555*e1fe3e4aSElliott Hughes            charString.globalSubrs = globalSubrs
556*e1fe3e4aSElliott Hughes            charStrings[glyphName] = charString
557*e1fe3e4aSElliott Hughes        topDict.CharStrings = charStrings
558*e1fe3e4aSElliott Hughes
559*e1fe3e4aSElliott Hughes        fontSet.topDictIndex.append(topDict)
560*e1fe3e4aSElliott Hughes
561*e1fe3e4aSElliott Hughes        self.font["CFF "] = newTable("CFF ")
562*e1fe3e4aSElliott Hughes        self.font["CFF "].cff = fontSet
563*e1fe3e4aSElliott Hughes
564*e1fe3e4aSElliott Hughes    def setupCFF2(self, charStringsDict, fdArrayList=None, regions=None):
565*e1fe3e4aSElliott Hughes        from .cffLib import (
566*e1fe3e4aSElliott Hughes            CFFFontSet,
567*e1fe3e4aSElliott Hughes            TopDictIndex,
568*e1fe3e4aSElliott Hughes            TopDict,
569*e1fe3e4aSElliott Hughes            CharStrings,
570*e1fe3e4aSElliott Hughes            GlobalSubrsIndex,
571*e1fe3e4aSElliott Hughes            PrivateDict,
572*e1fe3e4aSElliott Hughes            FDArrayIndex,
573*e1fe3e4aSElliott Hughes            FontDict,
574*e1fe3e4aSElliott Hughes        )
575*e1fe3e4aSElliott Hughes
576*e1fe3e4aSElliott Hughes        assert not self.isTTF
577*e1fe3e4aSElliott Hughes        self.font.sfntVersion = "OTTO"
578*e1fe3e4aSElliott Hughes        fontSet = CFFFontSet()
579*e1fe3e4aSElliott Hughes        fontSet.major = 2
580*e1fe3e4aSElliott Hughes        fontSet.minor = 0
581*e1fe3e4aSElliott Hughes
582*e1fe3e4aSElliott Hughes        cff2GetGlyphOrder = self.font.getGlyphOrder
583*e1fe3e4aSElliott Hughes        fontSet.topDictIndex = TopDictIndex(None, cff2GetGlyphOrder, None)
584*e1fe3e4aSElliott Hughes
585*e1fe3e4aSElliott Hughes        globalSubrs = GlobalSubrsIndex()
586*e1fe3e4aSElliott Hughes        fontSet.GlobalSubrs = globalSubrs
587*e1fe3e4aSElliott Hughes
588*e1fe3e4aSElliott Hughes        if fdArrayList is None:
589*e1fe3e4aSElliott Hughes            fdArrayList = [{}]
590*e1fe3e4aSElliott Hughes        fdSelect = None
591*e1fe3e4aSElliott Hughes        fdArray = FDArrayIndex()
592*e1fe3e4aSElliott Hughes        fdArray.strings = None
593*e1fe3e4aSElliott Hughes        fdArray.GlobalSubrs = globalSubrs
594*e1fe3e4aSElliott Hughes        for privateDict in fdArrayList:
595*e1fe3e4aSElliott Hughes            fontDict = FontDict()
596*e1fe3e4aSElliott Hughes            fontDict.setCFF2(True)
597*e1fe3e4aSElliott Hughes            private = PrivateDict()
598*e1fe3e4aSElliott Hughes            for key, value in privateDict.items():
599*e1fe3e4aSElliott Hughes                setattr(private, key, value)
600*e1fe3e4aSElliott Hughes            fontDict.Private = private
601*e1fe3e4aSElliott Hughes            fdArray.append(fontDict)
602*e1fe3e4aSElliott Hughes
603*e1fe3e4aSElliott Hughes        topDict = TopDict()
604*e1fe3e4aSElliott Hughes        topDict.cff2GetGlyphOrder = cff2GetGlyphOrder
605*e1fe3e4aSElliott Hughes        topDict.FDArray = fdArray
606*e1fe3e4aSElliott Hughes        scale = 1 / self.font["head"].unitsPerEm
607*e1fe3e4aSElliott Hughes        topDict.FontMatrix = [scale, 0, 0, scale, 0, 0]
608*e1fe3e4aSElliott Hughes
609*e1fe3e4aSElliott Hughes        private = fdArray[0].Private
610*e1fe3e4aSElliott Hughes        charStrings = CharStrings(None, None, globalSubrs, private, fdSelect, fdArray)
611*e1fe3e4aSElliott Hughes        for glyphName, charString in charStringsDict.items():
612*e1fe3e4aSElliott Hughes            charString.private = private
613*e1fe3e4aSElliott Hughes            charString.globalSubrs = globalSubrs
614*e1fe3e4aSElliott Hughes            charStrings[glyphName] = charString
615*e1fe3e4aSElliott Hughes        topDict.CharStrings = charStrings
616*e1fe3e4aSElliott Hughes
617*e1fe3e4aSElliott Hughes        fontSet.topDictIndex.append(topDict)
618*e1fe3e4aSElliott Hughes
619*e1fe3e4aSElliott Hughes        self.font["CFF2"] = newTable("CFF2")
620*e1fe3e4aSElliott Hughes        self.font["CFF2"].cff = fontSet
621*e1fe3e4aSElliott Hughes
622*e1fe3e4aSElliott Hughes        if regions:
623*e1fe3e4aSElliott Hughes            self.setupCFF2Regions(regions)
624*e1fe3e4aSElliott Hughes
625*e1fe3e4aSElliott Hughes    def setupCFF2Regions(self, regions):
626*e1fe3e4aSElliott Hughes        from .varLib.builder import buildVarRegionList, buildVarData, buildVarStore
627*e1fe3e4aSElliott Hughes        from .cffLib import VarStoreData
628*e1fe3e4aSElliott Hughes
629*e1fe3e4aSElliott Hughes        assert "fvar" in self.font, "fvar must to be set up first"
630*e1fe3e4aSElliott Hughes        assert "CFF2" in self.font, "CFF2 must to be set up first"
631*e1fe3e4aSElliott Hughes        axisTags = [a.axisTag for a in self.font["fvar"].axes]
632*e1fe3e4aSElliott Hughes        varRegionList = buildVarRegionList(regions, axisTags)
633*e1fe3e4aSElliott Hughes        varData = buildVarData(list(range(len(regions))), None, optimize=False)
634*e1fe3e4aSElliott Hughes        varStore = buildVarStore(varRegionList, [varData])
635*e1fe3e4aSElliott Hughes        vstore = VarStoreData(otVarStore=varStore)
636*e1fe3e4aSElliott Hughes        topDict = self.font["CFF2"].cff.topDictIndex[0]
637*e1fe3e4aSElliott Hughes        topDict.VarStore = vstore
638*e1fe3e4aSElliott Hughes        for fontDict in topDict.FDArray:
639*e1fe3e4aSElliott Hughes            fontDict.Private.vstore = vstore
640*e1fe3e4aSElliott Hughes
641*e1fe3e4aSElliott Hughes    def setupGlyf(self, glyphs, calcGlyphBounds=True, validateGlyphFormat=True):
642*e1fe3e4aSElliott Hughes        """Create the `glyf` table from a dict, that maps glyph names
643*e1fe3e4aSElliott Hughes        to `fontTools.ttLib.tables._g_l_y_f.Glyph` objects, for example
644*e1fe3e4aSElliott Hughes        as made by `fontTools.pens.ttGlyphPen.TTGlyphPen`.
645*e1fe3e4aSElliott Hughes
646*e1fe3e4aSElliott Hughes        If `calcGlyphBounds` is True, the bounds of all glyphs will be
647*e1fe3e4aSElliott Hughes        calculated. Only pass False if your glyph objects already have
648*e1fe3e4aSElliott Hughes        their bounding box values set.
649*e1fe3e4aSElliott Hughes
650*e1fe3e4aSElliott Hughes        If `validateGlyphFormat` is True, raise ValueError if any of the glyphs contains
651*e1fe3e4aSElliott Hughes        cubic curves or is a variable composite but head.glyphDataFormat=0.
652*e1fe3e4aSElliott Hughes        Set it to False to skip the check if you know in advance all the glyphs are
653*e1fe3e4aSElliott Hughes        compatible with the specified glyphDataFormat.
654*e1fe3e4aSElliott Hughes        """
655*e1fe3e4aSElliott Hughes        assert self.isTTF
656*e1fe3e4aSElliott Hughes
657*e1fe3e4aSElliott Hughes        if validateGlyphFormat and self.font["head"].glyphDataFormat == 0:
658*e1fe3e4aSElliott Hughes            for name, g in glyphs.items():
659*e1fe3e4aSElliott Hughes                if g.isVarComposite():
660*e1fe3e4aSElliott Hughes                    raise ValueError(
661*e1fe3e4aSElliott Hughes                        f"Glyph {name!r} is a variable composite, but glyphDataFormat=0"
662*e1fe3e4aSElliott Hughes                    )
663*e1fe3e4aSElliott Hughes                elif g.numberOfContours > 0 and any(f & flagCubic for f in g.flags):
664*e1fe3e4aSElliott Hughes                    raise ValueError(
665*e1fe3e4aSElliott Hughes                        f"Glyph {name!r} has cubic Bezier outlines, but glyphDataFormat=0; "
666*e1fe3e4aSElliott Hughes                        "either convert to quadratics with cu2qu or set glyphDataFormat=1."
667*e1fe3e4aSElliott Hughes                    )
668*e1fe3e4aSElliott Hughes
669*e1fe3e4aSElliott Hughes        self.font["loca"] = newTable("loca")
670*e1fe3e4aSElliott Hughes        self.font["glyf"] = newTable("glyf")
671*e1fe3e4aSElliott Hughes        self.font["glyf"].glyphs = glyphs
672*e1fe3e4aSElliott Hughes        if hasattr(self.font, "glyphOrder"):
673*e1fe3e4aSElliott Hughes            self.font["glyf"].glyphOrder = self.font.glyphOrder
674*e1fe3e4aSElliott Hughes        if calcGlyphBounds:
675*e1fe3e4aSElliott Hughes            self.calcGlyphBounds()
676*e1fe3e4aSElliott Hughes
677*e1fe3e4aSElliott Hughes    def setupFvar(self, axes, instances):
678*e1fe3e4aSElliott Hughes        """Adds an font variations table to the font.
679*e1fe3e4aSElliott Hughes
680*e1fe3e4aSElliott Hughes        Args:
681*e1fe3e4aSElliott Hughes            axes (list): See below.
682*e1fe3e4aSElliott Hughes            instances (list): See below.
683*e1fe3e4aSElliott Hughes
684*e1fe3e4aSElliott Hughes        ``axes`` should be a list of axes, with each axis either supplied as
685*e1fe3e4aSElliott Hughes        a py:class:`.designspaceLib.AxisDescriptor` object, or a tuple in the
686*e1fe3e4aSElliott Hughes        format ```tupletag, minValue, defaultValue, maxValue, name``.
687*e1fe3e4aSElliott Hughes        The ``name`` is either a string, or a dict, mapping language codes
688*e1fe3e4aSElliott Hughes        to strings, to allow localized name table entries.
689*e1fe3e4aSElliott Hughes
690*e1fe3e4aSElliott Hughes        ```instances`` should be a list of instances, with each instance either
691*e1fe3e4aSElliott Hughes        supplied as a py:class:`.designspaceLib.InstanceDescriptor` object, or a
692*e1fe3e4aSElliott Hughes        dict with keys ``location`` (mapping of axis tags to float values),
693*e1fe3e4aSElliott Hughes        ``stylename`` and (optionally) ``postscriptfontname``.
694*e1fe3e4aSElliott Hughes        The ``stylename`` is either a string, or a dict, mapping language codes
695*e1fe3e4aSElliott Hughes        to strings, to allow localized name table entries.
696*e1fe3e4aSElliott Hughes        """
697*e1fe3e4aSElliott Hughes
698*e1fe3e4aSElliott Hughes        addFvar(self.font, axes, instances)
699*e1fe3e4aSElliott Hughes
700*e1fe3e4aSElliott Hughes    def setupAvar(self, axes, mappings=None):
701*e1fe3e4aSElliott Hughes        """Adds an axis variations table to the font.
702*e1fe3e4aSElliott Hughes
703*e1fe3e4aSElliott Hughes        Args:
704*e1fe3e4aSElliott Hughes            axes (list): A list of py:class:`.designspaceLib.AxisDescriptor` objects.
705*e1fe3e4aSElliott Hughes        """
706*e1fe3e4aSElliott Hughes        from .varLib import _add_avar
707*e1fe3e4aSElliott Hughes
708*e1fe3e4aSElliott Hughes        if "fvar" not in self.font:
709*e1fe3e4aSElliott Hughes            raise KeyError("'fvar' table is missing; can't add 'avar'.")
710*e1fe3e4aSElliott Hughes
711*e1fe3e4aSElliott Hughes        axisTags = [axis.axisTag for axis in self.font["fvar"].axes]
712*e1fe3e4aSElliott Hughes        axes = OrderedDict(enumerate(axes))  # Only values are used
713*e1fe3e4aSElliott Hughes        _add_avar(self.font, axes, mappings, axisTags)
714*e1fe3e4aSElliott Hughes
715*e1fe3e4aSElliott Hughes    def setupGvar(self, variations):
716*e1fe3e4aSElliott Hughes        gvar = self.font["gvar"] = newTable("gvar")
717*e1fe3e4aSElliott Hughes        gvar.version = 1
718*e1fe3e4aSElliott Hughes        gvar.reserved = 0
719*e1fe3e4aSElliott Hughes        gvar.variations = variations
720*e1fe3e4aSElliott Hughes
721*e1fe3e4aSElliott Hughes    def calcGlyphBounds(self):
722*e1fe3e4aSElliott Hughes        """Calculate the bounding boxes of all glyphs in the `glyf` table.
723*e1fe3e4aSElliott Hughes        This is usually not called explicitly by client code.
724*e1fe3e4aSElliott Hughes        """
725*e1fe3e4aSElliott Hughes        glyphTable = self.font["glyf"]
726*e1fe3e4aSElliott Hughes        for glyph in glyphTable.glyphs.values():
727*e1fe3e4aSElliott Hughes            glyph.recalcBounds(glyphTable)
728*e1fe3e4aSElliott Hughes
729*e1fe3e4aSElliott Hughes    def setupHorizontalMetrics(self, metrics):
730*e1fe3e4aSElliott Hughes        """Create a new `hmtx` table, for horizontal metrics.
731*e1fe3e4aSElliott Hughes
732*e1fe3e4aSElliott Hughes        The `metrics` argument must be a dict, mapping glyph names to
733*e1fe3e4aSElliott Hughes        `(width, leftSidebearing)` tuples.
734*e1fe3e4aSElliott Hughes        """
735*e1fe3e4aSElliott Hughes        self.setupMetrics("hmtx", metrics)
736*e1fe3e4aSElliott Hughes
737*e1fe3e4aSElliott Hughes    def setupVerticalMetrics(self, metrics):
738*e1fe3e4aSElliott Hughes        """Create a new `vmtx` table, for horizontal metrics.
739*e1fe3e4aSElliott Hughes
740*e1fe3e4aSElliott Hughes        The `metrics` argument must be a dict, mapping glyph names to
741*e1fe3e4aSElliott Hughes        `(height, topSidebearing)` tuples.
742*e1fe3e4aSElliott Hughes        """
743*e1fe3e4aSElliott Hughes        self.setupMetrics("vmtx", metrics)
744*e1fe3e4aSElliott Hughes
745*e1fe3e4aSElliott Hughes    def setupMetrics(self, tableTag, metrics):
746*e1fe3e4aSElliott Hughes        """See `setupHorizontalMetrics()` and `setupVerticalMetrics()`."""
747*e1fe3e4aSElliott Hughes        assert tableTag in ("hmtx", "vmtx")
748*e1fe3e4aSElliott Hughes        mtxTable = self.font[tableTag] = newTable(tableTag)
749*e1fe3e4aSElliott Hughes        roundedMetrics = {}
750*e1fe3e4aSElliott Hughes        for gn in metrics:
751*e1fe3e4aSElliott Hughes            w, lsb = metrics[gn]
752*e1fe3e4aSElliott Hughes            roundedMetrics[gn] = int(round(w)), int(round(lsb))
753*e1fe3e4aSElliott Hughes        mtxTable.metrics = roundedMetrics
754*e1fe3e4aSElliott Hughes
755*e1fe3e4aSElliott Hughes    def setupHorizontalHeader(self, **values):
756*e1fe3e4aSElliott Hughes        """Create a new `hhea` table initialize it with default values,
757*e1fe3e4aSElliott Hughes        which can be overridden by keyword arguments.
758*e1fe3e4aSElliott Hughes        """
759*e1fe3e4aSElliott Hughes        self._initTableWithValues("hhea", _hheaDefaults, values)
760*e1fe3e4aSElliott Hughes
761*e1fe3e4aSElliott Hughes    def setupVerticalHeader(self, **values):
762*e1fe3e4aSElliott Hughes        """Create a new `vhea` table initialize it with default values,
763*e1fe3e4aSElliott Hughes        which can be overridden by keyword arguments.
764*e1fe3e4aSElliott Hughes        """
765*e1fe3e4aSElliott Hughes        self._initTableWithValues("vhea", _vheaDefaults, values)
766*e1fe3e4aSElliott Hughes
767*e1fe3e4aSElliott Hughes    def setupVerticalOrigins(self, verticalOrigins, defaultVerticalOrigin=None):
768*e1fe3e4aSElliott Hughes        """Create a new `VORG` table. The `verticalOrigins` argument must be
769*e1fe3e4aSElliott Hughes        a dict, mapping glyph names to vertical origin values.
770*e1fe3e4aSElliott Hughes
771*e1fe3e4aSElliott Hughes        The `defaultVerticalOrigin` argument should be the most common vertical
772*e1fe3e4aSElliott Hughes        origin value. If omitted, this value will be derived from the actual
773*e1fe3e4aSElliott Hughes        values in the `verticalOrigins` argument.
774*e1fe3e4aSElliott Hughes        """
775*e1fe3e4aSElliott Hughes        if defaultVerticalOrigin is None:
776*e1fe3e4aSElliott Hughes            # find the most frequent vorg value
777*e1fe3e4aSElliott Hughes            bag = {}
778*e1fe3e4aSElliott Hughes            for gn in verticalOrigins:
779*e1fe3e4aSElliott Hughes                vorg = verticalOrigins[gn]
780*e1fe3e4aSElliott Hughes                if vorg not in bag:
781*e1fe3e4aSElliott Hughes                    bag[vorg] = 1
782*e1fe3e4aSElliott Hughes                else:
783*e1fe3e4aSElliott Hughes                    bag[vorg] += 1
784*e1fe3e4aSElliott Hughes            defaultVerticalOrigin = sorted(
785*e1fe3e4aSElliott Hughes                bag, key=lambda vorg: bag[vorg], reverse=True
786*e1fe3e4aSElliott Hughes            )[0]
787*e1fe3e4aSElliott Hughes        self._initTableWithValues(
788*e1fe3e4aSElliott Hughes            "VORG",
789*e1fe3e4aSElliott Hughes            {},
790*e1fe3e4aSElliott Hughes            dict(VOriginRecords={}, defaultVertOriginY=defaultVerticalOrigin),
791*e1fe3e4aSElliott Hughes        )
792*e1fe3e4aSElliott Hughes        vorgTable = self.font["VORG"]
793*e1fe3e4aSElliott Hughes        vorgTable.majorVersion = 1
794*e1fe3e4aSElliott Hughes        vorgTable.minorVersion = 0
795*e1fe3e4aSElliott Hughes        for gn in verticalOrigins:
796*e1fe3e4aSElliott Hughes            vorgTable[gn] = verticalOrigins[gn]
797*e1fe3e4aSElliott Hughes
798*e1fe3e4aSElliott Hughes    def setupPost(self, keepGlyphNames=True, **values):
799*e1fe3e4aSElliott Hughes        """Create a new `post` table and initialize it with default values,
800*e1fe3e4aSElliott Hughes        which can be overridden by keyword arguments.
801*e1fe3e4aSElliott Hughes        """
802*e1fe3e4aSElliott Hughes        isCFF2 = "CFF2" in self.font
803*e1fe3e4aSElliott Hughes        postTable = self._initTableWithValues("post", _postDefaults, values)
804*e1fe3e4aSElliott Hughes        if (self.isTTF or isCFF2) and keepGlyphNames:
805*e1fe3e4aSElliott Hughes            postTable.formatType = 2.0
806*e1fe3e4aSElliott Hughes            postTable.extraNames = []
807*e1fe3e4aSElliott Hughes            postTable.mapping = {}
808*e1fe3e4aSElliott Hughes        else:
809*e1fe3e4aSElliott Hughes            postTable.formatType = 3.0
810*e1fe3e4aSElliott Hughes
811*e1fe3e4aSElliott Hughes    def setupMaxp(self):
812*e1fe3e4aSElliott Hughes        """Create a new `maxp` table. This is called implicitly by FontBuilder
813*e1fe3e4aSElliott Hughes        itself and is usually not called by client code.
814*e1fe3e4aSElliott Hughes        """
815*e1fe3e4aSElliott Hughes        if self.isTTF:
816*e1fe3e4aSElliott Hughes            defaults = _maxpDefaultsTTF
817*e1fe3e4aSElliott Hughes        else:
818*e1fe3e4aSElliott Hughes            defaults = _maxpDefaultsOTF
819*e1fe3e4aSElliott Hughes        self._initTableWithValues("maxp", defaults, {})
820*e1fe3e4aSElliott Hughes
821*e1fe3e4aSElliott Hughes    def setupDummyDSIG(self):
822*e1fe3e4aSElliott Hughes        """This adds an empty DSIG table to the font to make some MS applications
823*e1fe3e4aSElliott Hughes        happy. This does not properly sign the font.
824*e1fe3e4aSElliott Hughes        """
825*e1fe3e4aSElliott Hughes        values = dict(
826*e1fe3e4aSElliott Hughes            ulVersion=1,
827*e1fe3e4aSElliott Hughes            usFlag=0,
828*e1fe3e4aSElliott Hughes            usNumSigs=0,
829*e1fe3e4aSElliott Hughes            signatureRecords=[],
830*e1fe3e4aSElliott Hughes        )
831*e1fe3e4aSElliott Hughes        self._initTableWithValues("DSIG", {}, values)
832*e1fe3e4aSElliott Hughes
833*e1fe3e4aSElliott Hughes    def addOpenTypeFeatures(self, features, filename=None, tables=None, debug=False):
834*e1fe3e4aSElliott Hughes        """Add OpenType features to the font from a string containing
835*e1fe3e4aSElliott Hughes        Feature File syntax.
836*e1fe3e4aSElliott Hughes
837*e1fe3e4aSElliott Hughes        The `filename` argument is used in error messages and to determine
838*e1fe3e4aSElliott Hughes        where to look for "include" files.
839*e1fe3e4aSElliott Hughes
840*e1fe3e4aSElliott Hughes        The optional `tables` argument can be a list of OTL tables tags to
841*e1fe3e4aSElliott Hughes        build, allowing the caller to only build selected OTL tables. See
842*e1fe3e4aSElliott Hughes        `fontTools.feaLib` for details.
843*e1fe3e4aSElliott Hughes
844*e1fe3e4aSElliott Hughes        The optional `debug` argument controls whether to add source debugging
845*e1fe3e4aSElliott Hughes        information to the font in the `Debg` table.
846*e1fe3e4aSElliott Hughes        """
847*e1fe3e4aSElliott Hughes        from .feaLib.builder import addOpenTypeFeaturesFromString
848*e1fe3e4aSElliott Hughes
849*e1fe3e4aSElliott Hughes        addOpenTypeFeaturesFromString(
850*e1fe3e4aSElliott Hughes            self.font, features, filename=filename, tables=tables, debug=debug
851*e1fe3e4aSElliott Hughes        )
852*e1fe3e4aSElliott Hughes
853*e1fe3e4aSElliott Hughes    def addFeatureVariations(self, conditionalSubstitutions, featureTag="rvrn"):
854*e1fe3e4aSElliott Hughes        """Add conditional substitutions to a Variable Font.
855*e1fe3e4aSElliott Hughes
856*e1fe3e4aSElliott Hughes        See `fontTools.varLib.featureVars.addFeatureVariations`.
857*e1fe3e4aSElliott Hughes        """
858*e1fe3e4aSElliott Hughes        from .varLib import featureVars
859*e1fe3e4aSElliott Hughes
860*e1fe3e4aSElliott Hughes        if "fvar" not in self.font:
861*e1fe3e4aSElliott Hughes            raise KeyError("'fvar' table is missing; can't add FeatureVariations.")
862*e1fe3e4aSElliott Hughes
863*e1fe3e4aSElliott Hughes        featureVars.addFeatureVariations(
864*e1fe3e4aSElliott Hughes            self.font, conditionalSubstitutions, featureTag=featureTag
865*e1fe3e4aSElliott Hughes        )
866*e1fe3e4aSElliott Hughes
867*e1fe3e4aSElliott Hughes    def setupCOLR(
868*e1fe3e4aSElliott Hughes        self,
869*e1fe3e4aSElliott Hughes        colorLayers,
870*e1fe3e4aSElliott Hughes        version=None,
871*e1fe3e4aSElliott Hughes        varStore=None,
872*e1fe3e4aSElliott Hughes        varIndexMap=None,
873*e1fe3e4aSElliott Hughes        clipBoxes=None,
874*e1fe3e4aSElliott Hughes        allowLayerReuse=True,
875*e1fe3e4aSElliott Hughes    ):
876*e1fe3e4aSElliott Hughes        """Build new COLR table using color layers dictionary.
877*e1fe3e4aSElliott Hughes
878*e1fe3e4aSElliott Hughes        Cf. `fontTools.colorLib.builder.buildCOLR`.
879*e1fe3e4aSElliott Hughes        """
880*e1fe3e4aSElliott Hughes        from fontTools.colorLib.builder import buildCOLR
881*e1fe3e4aSElliott Hughes
882*e1fe3e4aSElliott Hughes        glyphMap = self.font.getReverseGlyphMap()
883*e1fe3e4aSElliott Hughes        self.font["COLR"] = buildCOLR(
884*e1fe3e4aSElliott Hughes            colorLayers,
885*e1fe3e4aSElliott Hughes            version=version,
886*e1fe3e4aSElliott Hughes            glyphMap=glyphMap,
887*e1fe3e4aSElliott Hughes            varStore=varStore,
888*e1fe3e4aSElliott Hughes            varIndexMap=varIndexMap,
889*e1fe3e4aSElliott Hughes            clipBoxes=clipBoxes,
890*e1fe3e4aSElliott Hughes            allowLayerReuse=allowLayerReuse,
891*e1fe3e4aSElliott Hughes        )
892*e1fe3e4aSElliott Hughes
893*e1fe3e4aSElliott Hughes    def setupCPAL(
894*e1fe3e4aSElliott Hughes        self,
895*e1fe3e4aSElliott Hughes        palettes,
896*e1fe3e4aSElliott Hughes        paletteTypes=None,
897*e1fe3e4aSElliott Hughes        paletteLabels=None,
898*e1fe3e4aSElliott Hughes        paletteEntryLabels=None,
899*e1fe3e4aSElliott Hughes    ):
900*e1fe3e4aSElliott Hughes        """Build new CPAL table using list of palettes.
901*e1fe3e4aSElliott Hughes
902*e1fe3e4aSElliott Hughes        Optionally build CPAL v1 table using paletteTypes, paletteLabels and
903*e1fe3e4aSElliott Hughes        paletteEntryLabels.
904*e1fe3e4aSElliott Hughes
905*e1fe3e4aSElliott Hughes        Cf. `fontTools.colorLib.builder.buildCPAL`.
906*e1fe3e4aSElliott Hughes        """
907*e1fe3e4aSElliott Hughes        from fontTools.colorLib.builder import buildCPAL
908*e1fe3e4aSElliott Hughes
909*e1fe3e4aSElliott Hughes        self.font["CPAL"] = buildCPAL(
910*e1fe3e4aSElliott Hughes            palettes,
911*e1fe3e4aSElliott Hughes            paletteTypes=paletteTypes,
912*e1fe3e4aSElliott Hughes            paletteLabels=paletteLabels,
913*e1fe3e4aSElliott Hughes            paletteEntryLabels=paletteEntryLabels,
914*e1fe3e4aSElliott Hughes            nameTable=self.font.get("name"),
915*e1fe3e4aSElliott Hughes        )
916*e1fe3e4aSElliott Hughes
917*e1fe3e4aSElliott Hughes    def setupStat(self, axes, locations=None, elidedFallbackName=2):
918*e1fe3e4aSElliott Hughes        """Build a new 'STAT' table.
919*e1fe3e4aSElliott Hughes
920*e1fe3e4aSElliott Hughes        See `fontTools.otlLib.builder.buildStatTable` for details about
921*e1fe3e4aSElliott Hughes        the arguments.
922*e1fe3e4aSElliott Hughes        """
923*e1fe3e4aSElliott Hughes        from .otlLib.builder import buildStatTable
924*e1fe3e4aSElliott Hughes
925*e1fe3e4aSElliott Hughes        buildStatTable(self.font, axes, locations, elidedFallbackName)
926*e1fe3e4aSElliott Hughes
927*e1fe3e4aSElliott Hughes
928*e1fe3e4aSElliott Hughesdef buildCmapSubTable(cmapping, format, platformID, platEncID):
929*e1fe3e4aSElliott Hughes    subTable = cmap_classes[format](format)
930*e1fe3e4aSElliott Hughes    subTable.cmap = cmapping
931*e1fe3e4aSElliott Hughes    subTable.platformID = platformID
932*e1fe3e4aSElliott Hughes    subTable.platEncID = platEncID
933*e1fe3e4aSElliott Hughes    subTable.language = 0
934*e1fe3e4aSElliott Hughes    return subTable
935*e1fe3e4aSElliott Hughes
936*e1fe3e4aSElliott Hughes
937*e1fe3e4aSElliott Hughesdef addFvar(font, axes, instances):
938*e1fe3e4aSElliott Hughes    from .ttLib.tables._f_v_a_r import Axis, NamedInstance
939*e1fe3e4aSElliott Hughes
940*e1fe3e4aSElliott Hughes    assert axes
941*e1fe3e4aSElliott Hughes
942*e1fe3e4aSElliott Hughes    fvar = newTable("fvar")
943*e1fe3e4aSElliott Hughes    nameTable = font["name"]
944*e1fe3e4aSElliott Hughes
945*e1fe3e4aSElliott Hughes    for axis_def in axes:
946*e1fe3e4aSElliott Hughes        axis = Axis()
947*e1fe3e4aSElliott Hughes
948*e1fe3e4aSElliott Hughes        if isinstance(axis_def, tuple):
949*e1fe3e4aSElliott Hughes            (
950*e1fe3e4aSElliott Hughes                axis.axisTag,
951*e1fe3e4aSElliott Hughes                axis.minValue,
952*e1fe3e4aSElliott Hughes                axis.defaultValue,
953*e1fe3e4aSElliott Hughes                axis.maxValue,
954*e1fe3e4aSElliott Hughes                name,
955*e1fe3e4aSElliott Hughes            ) = axis_def
956*e1fe3e4aSElliott Hughes        else:
957*e1fe3e4aSElliott Hughes            (axis.axisTag, axis.minValue, axis.defaultValue, axis.maxValue, name) = (
958*e1fe3e4aSElliott Hughes                axis_def.tag,
959*e1fe3e4aSElliott Hughes                axis_def.minimum,
960*e1fe3e4aSElliott Hughes                axis_def.default,
961*e1fe3e4aSElliott Hughes                axis_def.maximum,
962*e1fe3e4aSElliott Hughes                axis_def.name,
963*e1fe3e4aSElliott Hughes            )
964*e1fe3e4aSElliott Hughes            if axis_def.hidden:
965*e1fe3e4aSElliott Hughes                axis.flags = 0x0001  # HIDDEN_AXIS
966*e1fe3e4aSElliott Hughes
967*e1fe3e4aSElliott Hughes        if isinstance(name, str):
968*e1fe3e4aSElliott Hughes            name = dict(en=name)
969*e1fe3e4aSElliott Hughes
970*e1fe3e4aSElliott Hughes        axis.axisNameID = nameTable.addMultilingualName(name, ttFont=font)
971*e1fe3e4aSElliott Hughes        fvar.axes.append(axis)
972*e1fe3e4aSElliott Hughes
973*e1fe3e4aSElliott Hughes    for instance in instances:
974*e1fe3e4aSElliott Hughes        if isinstance(instance, dict):
975*e1fe3e4aSElliott Hughes            coordinates = instance["location"]
976*e1fe3e4aSElliott Hughes            name = instance["stylename"]
977*e1fe3e4aSElliott Hughes            psname = instance.get("postscriptfontname")
978*e1fe3e4aSElliott Hughes        else:
979*e1fe3e4aSElliott Hughes            coordinates = instance.location
980*e1fe3e4aSElliott Hughes            name = instance.localisedStyleName or instance.styleName
981*e1fe3e4aSElliott Hughes            psname = instance.postScriptFontName
982*e1fe3e4aSElliott Hughes
983*e1fe3e4aSElliott Hughes        if isinstance(name, str):
984*e1fe3e4aSElliott Hughes            name = dict(en=name)
985*e1fe3e4aSElliott Hughes
986*e1fe3e4aSElliott Hughes        inst = NamedInstance()
987*e1fe3e4aSElliott Hughes        inst.subfamilyNameID = nameTable.addMultilingualName(name, ttFont=font)
988*e1fe3e4aSElliott Hughes        if psname is not None:
989*e1fe3e4aSElliott Hughes            inst.postscriptNameID = nameTable.addName(psname)
990*e1fe3e4aSElliott Hughes        inst.coordinates = coordinates
991*e1fe3e4aSElliott Hughes        fvar.instances.append(inst)
992*e1fe3e4aSElliott Hughes
993*e1fe3e4aSElliott Hughes    font["fvar"] = fvar
994