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