1*e1fe3e4aSElliott Hughesimport os 2*e1fe3e4aSElliott Hughesimport pytest 3*e1fe3e4aSElliott Hughesfrom fontTools.designspaceLib import AxisDescriptor 4*e1fe3e4aSElliott Hughesfrom fontTools.ttLib import TTFont 5*e1fe3e4aSElliott Hughesfrom fontTools.pens.ttGlyphPen import TTGlyphPen 6*e1fe3e4aSElliott Hughesfrom fontTools.pens.t2CharStringPen import T2CharStringPen 7*e1fe3e4aSElliott Hughesfrom fontTools.fontBuilder import FontBuilder 8*e1fe3e4aSElliott Hughesfrom fontTools.ttLib.tables.TupleVariation import TupleVariation 9*e1fe3e4aSElliott Hughesfrom fontTools.misc.psCharStrings import T2CharString 10*e1fe3e4aSElliott Hughesfrom fontTools.misc.testTools import stripVariableItemsFromTTX 11*e1fe3e4aSElliott Hughes 12*e1fe3e4aSElliott Hughes 13*e1fe3e4aSElliott Hughesdef getTestData(fileName, mode="r"): 14*e1fe3e4aSElliott Hughes path = os.path.join(os.path.dirname(__file__), "data", fileName) 15*e1fe3e4aSElliott Hughes with open(path, mode) as f: 16*e1fe3e4aSElliott Hughes return f.read() 17*e1fe3e4aSElliott Hughes 18*e1fe3e4aSElliott Hughes 19*e1fe3e4aSElliott Hughesdef drawTestGlyph(pen): 20*e1fe3e4aSElliott Hughes pen.moveTo((100, 100)) 21*e1fe3e4aSElliott Hughes pen.lineTo((100, 1000)) 22*e1fe3e4aSElliott Hughes pen.qCurveTo((200, 900), (400, 900), (500, 1000)) 23*e1fe3e4aSElliott Hughes pen.lineTo((500, 100)) 24*e1fe3e4aSElliott Hughes pen.closePath() 25*e1fe3e4aSElliott Hughes 26*e1fe3e4aSElliott Hughes 27*e1fe3e4aSElliott Hughesdef _setupFontBuilder(isTTF, unitsPerEm=1024): 28*e1fe3e4aSElliott Hughes fb = FontBuilder(unitsPerEm, isTTF=isTTF) 29*e1fe3e4aSElliott Hughes fb.setupGlyphOrder([".notdef", ".null", "A", "a"]) 30*e1fe3e4aSElliott Hughes fb.setupCharacterMap({65: "A", 97: "a"}) 31*e1fe3e4aSElliott Hughes 32*e1fe3e4aSElliott Hughes advanceWidths = {".notdef": 600, "A": 600, "a": 600, ".null": 600} 33*e1fe3e4aSElliott Hughes 34*e1fe3e4aSElliott Hughes familyName = "HelloTestFont" 35*e1fe3e4aSElliott Hughes styleName = "TotallyNormal" 36*e1fe3e4aSElliott Hughes nameStrings = dict( 37*e1fe3e4aSElliott Hughes familyName=dict(en="HelloTestFont", nl="HalloTestFont"), 38*e1fe3e4aSElliott Hughes styleName=dict(en="TotallyNormal", nl="TotaalNormaal"), 39*e1fe3e4aSElliott Hughes ) 40*e1fe3e4aSElliott Hughes nameStrings["psName"] = familyName + "-" + styleName 41*e1fe3e4aSElliott Hughes 42*e1fe3e4aSElliott Hughes return fb, advanceWidths, nameStrings 43*e1fe3e4aSElliott Hughes 44*e1fe3e4aSElliott Hughes 45*e1fe3e4aSElliott Hughesdef _setupFontBuilderFvar(fb): 46*e1fe3e4aSElliott Hughes assert "name" in fb.font, "Must run setupNameTable() first." 47*e1fe3e4aSElliott Hughes 48*e1fe3e4aSElliott Hughes testAxis = AxisDescriptor() 49*e1fe3e4aSElliott Hughes testAxis.name = "Test Axis" 50*e1fe3e4aSElliott Hughes testAxis.tag = "TEST" 51*e1fe3e4aSElliott Hughes testAxis.minimum = 0 52*e1fe3e4aSElliott Hughes testAxis.default = 0 53*e1fe3e4aSElliott Hughes testAxis.maximum = 100 54*e1fe3e4aSElliott Hughes testAxis.map = [(0, 0), (40, 60), (100, 100)] 55*e1fe3e4aSElliott Hughes axes = [testAxis] 56*e1fe3e4aSElliott Hughes instances = [ 57*e1fe3e4aSElliott Hughes dict(location=dict(TEST=0), stylename="TotallyNormal"), 58*e1fe3e4aSElliott Hughes dict(location=dict(TEST=100), stylename="TotallyTested"), 59*e1fe3e4aSElliott Hughes ] 60*e1fe3e4aSElliott Hughes fb.setupFvar(axes, instances) 61*e1fe3e4aSElliott Hughes fb.setupAvar(axes) 62*e1fe3e4aSElliott Hughes 63*e1fe3e4aSElliott Hughes return fb 64*e1fe3e4aSElliott Hughes 65*e1fe3e4aSElliott Hughes 66*e1fe3e4aSElliott Hughesdef _setupFontBuilderCFF2(fb): 67*e1fe3e4aSElliott Hughes assert "fvar" in fb.font, "Must run _setupFontBuilderFvar() first." 68*e1fe3e4aSElliott Hughes 69*e1fe3e4aSElliott Hughes pen = T2CharStringPen(None, None, CFF2=True) 70*e1fe3e4aSElliott Hughes drawTestGlyph(pen) 71*e1fe3e4aSElliott Hughes charString = pen.getCharString() 72*e1fe3e4aSElliott Hughes 73*e1fe3e4aSElliott Hughes program = [ 74*e1fe3e4aSElliott Hughes 200, 75*e1fe3e4aSElliott Hughes 200, 76*e1fe3e4aSElliott Hughes -200, 77*e1fe3e4aSElliott Hughes -200, 78*e1fe3e4aSElliott Hughes 2, 79*e1fe3e4aSElliott Hughes "blend", 80*e1fe3e4aSElliott Hughes "rmoveto", 81*e1fe3e4aSElliott Hughes 400, 82*e1fe3e4aSElliott Hughes 400, 83*e1fe3e4aSElliott Hughes 1, 84*e1fe3e4aSElliott Hughes "blend", 85*e1fe3e4aSElliott Hughes "hlineto", 86*e1fe3e4aSElliott Hughes 400, 87*e1fe3e4aSElliott Hughes 400, 88*e1fe3e4aSElliott Hughes 1, 89*e1fe3e4aSElliott Hughes "blend", 90*e1fe3e4aSElliott Hughes "vlineto", 91*e1fe3e4aSElliott Hughes -400, 92*e1fe3e4aSElliott Hughes -400, 93*e1fe3e4aSElliott Hughes 1, 94*e1fe3e4aSElliott Hughes "blend", 95*e1fe3e4aSElliott Hughes "hlineto", 96*e1fe3e4aSElliott Hughes ] 97*e1fe3e4aSElliott Hughes charStringVariable = T2CharString(program=program) 98*e1fe3e4aSElliott Hughes 99*e1fe3e4aSElliott Hughes charStrings = { 100*e1fe3e4aSElliott Hughes ".notdef": charString, 101*e1fe3e4aSElliott Hughes "A": charString, 102*e1fe3e4aSElliott Hughes "a": charStringVariable, 103*e1fe3e4aSElliott Hughes ".null": charString, 104*e1fe3e4aSElliott Hughes } 105*e1fe3e4aSElliott Hughes fb.setupCFF2(charStrings, regions=[{"TEST": (0, 1, 1)}]) 106*e1fe3e4aSElliott Hughes 107*e1fe3e4aSElliott Hughes return fb 108*e1fe3e4aSElliott Hughes 109*e1fe3e4aSElliott Hughes 110*e1fe3e4aSElliott Hughesdef _verifyOutput(outPath, tables=None): 111*e1fe3e4aSElliott Hughes f = TTFont(outPath) 112*e1fe3e4aSElliott Hughes f.saveXML(outPath + ".ttx", tables=tables) 113*e1fe3e4aSElliott Hughes with open(outPath + ".ttx") as f: 114*e1fe3e4aSElliott Hughes testData = stripVariableItemsFromTTX(f.read()) 115*e1fe3e4aSElliott Hughes refData = stripVariableItemsFromTTX(getTestData(os.path.basename(outPath) + ".ttx")) 116*e1fe3e4aSElliott Hughes assert refData == testData 117*e1fe3e4aSElliott Hughes 118*e1fe3e4aSElliott Hughes 119*e1fe3e4aSElliott Hughesdef test_build_ttf(tmpdir): 120*e1fe3e4aSElliott Hughes outPath = os.path.join(str(tmpdir), "test.ttf") 121*e1fe3e4aSElliott Hughes 122*e1fe3e4aSElliott Hughes fb, advanceWidths, nameStrings = _setupFontBuilder(True) 123*e1fe3e4aSElliott Hughes 124*e1fe3e4aSElliott Hughes pen = TTGlyphPen(None) 125*e1fe3e4aSElliott Hughes drawTestGlyph(pen) 126*e1fe3e4aSElliott Hughes glyph = pen.glyph() 127*e1fe3e4aSElliott Hughes glyphs = {".notdef": glyph, "A": glyph, "a": glyph, ".null": glyph} 128*e1fe3e4aSElliott Hughes fb.setupGlyf(glyphs) 129*e1fe3e4aSElliott Hughes metrics = {} 130*e1fe3e4aSElliott Hughes glyphTable = fb.font["glyf"] 131*e1fe3e4aSElliott Hughes for gn, advanceWidth in advanceWidths.items(): 132*e1fe3e4aSElliott Hughes metrics[gn] = (advanceWidth, glyphTable[gn].xMin) 133*e1fe3e4aSElliott Hughes fb.setupHorizontalMetrics(metrics) 134*e1fe3e4aSElliott Hughes 135*e1fe3e4aSElliott Hughes fb.setupHorizontalHeader(ascent=824, descent=200) 136*e1fe3e4aSElliott Hughes fb.setupNameTable(nameStrings) 137*e1fe3e4aSElliott Hughes fb.setupOS2() 138*e1fe3e4aSElliott Hughes fb.addOpenTypeFeatures("feature salt { sub A by a; } salt;") 139*e1fe3e4aSElliott Hughes fb.setupPost() 140*e1fe3e4aSElliott Hughes fb.setupDummyDSIG() 141*e1fe3e4aSElliott Hughes 142*e1fe3e4aSElliott Hughes fb.save(outPath) 143*e1fe3e4aSElliott Hughes 144*e1fe3e4aSElliott Hughes _verifyOutput(outPath) 145*e1fe3e4aSElliott Hughes 146*e1fe3e4aSElliott Hughes 147*e1fe3e4aSElliott Hughesdef test_build_cubic_ttf(tmp_path): 148*e1fe3e4aSElliott Hughes pen = TTGlyphPen(None) 149*e1fe3e4aSElliott Hughes pen.moveTo((100, 100)) 150*e1fe3e4aSElliott Hughes pen.curveTo((200, 200), (300, 300), (400, 400)) 151*e1fe3e4aSElliott Hughes pen.closePath() 152*e1fe3e4aSElliott Hughes glyph = pen.glyph() 153*e1fe3e4aSElliott Hughes glyphs = {"A": glyph} 154*e1fe3e4aSElliott Hughes 155*e1fe3e4aSElliott Hughes # cubic outlines are not allowed in glyf table format 0 156*e1fe3e4aSElliott Hughes fb = FontBuilder(1000, isTTF=True, glyphDataFormat=0) 157*e1fe3e4aSElliott Hughes with pytest.raises( 158*e1fe3e4aSElliott Hughes ValueError, match="Glyph 'A' has cubic Bezier outlines, but glyphDataFormat=0" 159*e1fe3e4aSElliott Hughes ): 160*e1fe3e4aSElliott Hughes fb.setupGlyf(glyphs) 161*e1fe3e4aSElliott Hughes # can skip check if feeling adventurous 162*e1fe3e4aSElliott Hughes fb.setupGlyf(glyphs, validateGlyphFormat=False) 163*e1fe3e4aSElliott Hughes 164*e1fe3e4aSElliott Hughes # cubics are (will be) allowed in glyf table format 1 165*e1fe3e4aSElliott Hughes fb = FontBuilder(1000, isTTF=True, glyphDataFormat=1) 166*e1fe3e4aSElliott Hughes fb.setupGlyf(glyphs) 167*e1fe3e4aSElliott Hughes assert "A" in fb.font["glyf"].glyphs 168*e1fe3e4aSElliott Hughes 169*e1fe3e4aSElliott Hughes 170*e1fe3e4aSElliott Hughesdef test_build_otf(tmpdir): 171*e1fe3e4aSElliott Hughes outPath = os.path.join(str(tmpdir), "test.otf") 172*e1fe3e4aSElliott Hughes 173*e1fe3e4aSElliott Hughes fb, advanceWidths, nameStrings = _setupFontBuilder(False) 174*e1fe3e4aSElliott Hughes 175*e1fe3e4aSElliott Hughes pen = T2CharStringPen(600, None) 176*e1fe3e4aSElliott Hughes drawTestGlyph(pen) 177*e1fe3e4aSElliott Hughes charString = pen.getCharString() 178*e1fe3e4aSElliott Hughes charStrings = { 179*e1fe3e4aSElliott Hughes ".notdef": charString, 180*e1fe3e4aSElliott Hughes "A": charString, 181*e1fe3e4aSElliott Hughes "a": charString, 182*e1fe3e4aSElliott Hughes ".null": charString, 183*e1fe3e4aSElliott Hughes } 184*e1fe3e4aSElliott Hughes fb.setupCFF( 185*e1fe3e4aSElliott Hughes nameStrings["psName"], {"FullName": nameStrings["psName"]}, charStrings, {} 186*e1fe3e4aSElliott Hughes ) 187*e1fe3e4aSElliott Hughes 188*e1fe3e4aSElliott Hughes lsb = {gn: cs.calcBounds(None)[0] for gn, cs in charStrings.items()} 189*e1fe3e4aSElliott Hughes metrics = {} 190*e1fe3e4aSElliott Hughes for gn, advanceWidth in advanceWidths.items(): 191*e1fe3e4aSElliott Hughes metrics[gn] = (advanceWidth, lsb[gn]) 192*e1fe3e4aSElliott Hughes fb.setupHorizontalMetrics(metrics) 193*e1fe3e4aSElliott Hughes 194*e1fe3e4aSElliott Hughes fb.setupHorizontalHeader(ascent=824, descent=200) 195*e1fe3e4aSElliott Hughes fb.setupNameTable(nameStrings) 196*e1fe3e4aSElliott Hughes fb.setupOS2() 197*e1fe3e4aSElliott Hughes fb.addOpenTypeFeatures("feature kern { pos A a -50; } kern;") 198*e1fe3e4aSElliott Hughes fb.setupPost() 199*e1fe3e4aSElliott Hughes fb.setupDummyDSIG() 200*e1fe3e4aSElliott Hughes 201*e1fe3e4aSElliott Hughes fb.save(outPath) 202*e1fe3e4aSElliott Hughes 203*e1fe3e4aSElliott Hughes _verifyOutput(outPath) 204*e1fe3e4aSElliott Hughes 205*e1fe3e4aSElliott Hughes 206*e1fe3e4aSElliott Hughesdef test_build_var(tmpdir): 207*e1fe3e4aSElliott Hughes outPath = os.path.join(str(tmpdir), "test_var.ttf") 208*e1fe3e4aSElliott Hughes 209*e1fe3e4aSElliott Hughes fb, advanceWidths, nameStrings = _setupFontBuilder(True) 210*e1fe3e4aSElliott Hughes 211*e1fe3e4aSElliott Hughes pen = TTGlyphPen(None) 212*e1fe3e4aSElliott Hughes pen.moveTo((100, 0)) 213*e1fe3e4aSElliott Hughes pen.lineTo((100, 400)) 214*e1fe3e4aSElliott Hughes pen.lineTo((500, 400)) 215*e1fe3e4aSElliott Hughes pen.lineTo((500, 000)) 216*e1fe3e4aSElliott Hughes pen.closePath() 217*e1fe3e4aSElliott Hughes glyph1 = pen.glyph() 218*e1fe3e4aSElliott Hughes 219*e1fe3e4aSElliott Hughes pen = TTGlyphPen(None) 220*e1fe3e4aSElliott Hughes pen.moveTo((50, 0)) 221*e1fe3e4aSElliott Hughes pen.lineTo((50, 200)) 222*e1fe3e4aSElliott Hughes pen.lineTo((250, 200)) 223*e1fe3e4aSElliott Hughes pen.lineTo((250, 0)) 224*e1fe3e4aSElliott Hughes pen.closePath() 225*e1fe3e4aSElliott Hughes glyph2 = pen.glyph() 226*e1fe3e4aSElliott Hughes 227*e1fe3e4aSElliott Hughes pen = TTGlyphPen(None) 228*e1fe3e4aSElliott Hughes emptyGlyph = pen.glyph() 229*e1fe3e4aSElliott Hughes 230*e1fe3e4aSElliott Hughes glyphs = {".notdef": emptyGlyph, "A": glyph1, "a": glyph2, ".null": emptyGlyph} 231*e1fe3e4aSElliott Hughes fb.setupGlyf(glyphs) 232*e1fe3e4aSElliott Hughes metrics = {} 233*e1fe3e4aSElliott Hughes glyphTable = fb.font["glyf"] 234*e1fe3e4aSElliott Hughes for gn, advanceWidth in advanceWidths.items(): 235*e1fe3e4aSElliott Hughes metrics[gn] = (advanceWidth, glyphTable[gn].xMin) 236*e1fe3e4aSElliott Hughes fb.setupHorizontalMetrics(metrics) 237*e1fe3e4aSElliott Hughes 238*e1fe3e4aSElliott Hughes fb.setupHorizontalHeader(ascent=824, descent=200) 239*e1fe3e4aSElliott Hughes fb.setupNameTable(nameStrings) 240*e1fe3e4aSElliott Hughes 241*e1fe3e4aSElliott Hughes axes = [ 242*e1fe3e4aSElliott Hughes ("LEFT", 0, 0, 100, "Left"), 243*e1fe3e4aSElliott Hughes ("RGHT", 0, 0, 100, "Right"), 244*e1fe3e4aSElliott Hughes ("UPPP", 0, 0, 100, "Up"), 245*e1fe3e4aSElliott Hughes ("DOWN", 0, 0, 100, "Down"), 246*e1fe3e4aSElliott Hughes ] 247*e1fe3e4aSElliott Hughes instances = [ 248*e1fe3e4aSElliott Hughes dict(location=dict(LEFT=0, RGHT=0, UPPP=0, DOWN=0), stylename="TotallyNormal"), 249*e1fe3e4aSElliott Hughes dict(location=dict(LEFT=0, RGHT=100, UPPP=100, DOWN=0), stylename="Right Up"), 250*e1fe3e4aSElliott Hughes ] 251*e1fe3e4aSElliott Hughes fb.setupFvar(axes, instances) 252*e1fe3e4aSElliott Hughes variations = {} 253*e1fe3e4aSElliott Hughes # Four (x, y) pairs and four phantom points: 254*e1fe3e4aSElliott Hughes leftDeltas = [(-200, 0), (-200, 0), (0, 0), (0, 0), None, None, None, None] 255*e1fe3e4aSElliott Hughes rightDeltas = [(0, 0), (0, 0), (200, 0), (200, 0), None, None, None, None] 256*e1fe3e4aSElliott Hughes upDeltas = [(0, 0), (0, 200), (0, 200), (0, 0), None, None, None, None] 257*e1fe3e4aSElliott Hughes downDeltas = [(0, -200), (0, 0), (0, 0), (0, -200), None, None, None, None] 258*e1fe3e4aSElliott Hughes variations["a"] = [ 259*e1fe3e4aSElliott Hughes TupleVariation(dict(RGHT=(0, 1, 1)), rightDeltas), 260*e1fe3e4aSElliott Hughes TupleVariation(dict(LEFT=(0, 1, 1)), leftDeltas), 261*e1fe3e4aSElliott Hughes TupleVariation(dict(UPPP=(0, 1, 1)), upDeltas), 262*e1fe3e4aSElliott Hughes TupleVariation(dict(DOWN=(0, 1, 1)), downDeltas), 263*e1fe3e4aSElliott Hughes ] 264*e1fe3e4aSElliott Hughes fb.setupGvar(variations) 265*e1fe3e4aSElliott Hughes 266*e1fe3e4aSElliott Hughes fb.addFeatureVariations( 267*e1fe3e4aSElliott Hughes [ 268*e1fe3e4aSElliott Hughes ( 269*e1fe3e4aSElliott Hughes [ 270*e1fe3e4aSElliott Hughes {"LEFT": (0.8, 1), "DOWN": (0.8, 1)}, 271*e1fe3e4aSElliott Hughes {"RGHT": (0.8, 1), "UPPP": (0.8, 1)}, 272*e1fe3e4aSElliott Hughes ], 273*e1fe3e4aSElliott Hughes {"A": "a"}, 274*e1fe3e4aSElliott Hughes ) 275*e1fe3e4aSElliott Hughes ], 276*e1fe3e4aSElliott Hughes featureTag="rclt", 277*e1fe3e4aSElliott Hughes ) 278*e1fe3e4aSElliott Hughes 279*e1fe3e4aSElliott Hughes statAxes = [] 280*e1fe3e4aSElliott Hughes for tag, minVal, defaultVal, maxVal, name in axes: 281*e1fe3e4aSElliott Hughes values = [ 282*e1fe3e4aSElliott Hughes dict(name="Neutral", value=defaultVal, flags=0x2), 283*e1fe3e4aSElliott Hughes dict(name=name, value=maxVal), 284*e1fe3e4aSElliott Hughes ] 285*e1fe3e4aSElliott Hughes statAxes.append(dict(tag=tag, name=name, values=values)) 286*e1fe3e4aSElliott Hughes fb.setupStat(statAxes) 287*e1fe3e4aSElliott Hughes 288*e1fe3e4aSElliott Hughes fb.setupOS2() 289*e1fe3e4aSElliott Hughes fb.setupPost() 290*e1fe3e4aSElliott Hughes fb.setupDummyDSIG() 291*e1fe3e4aSElliott Hughes 292*e1fe3e4aSElliott Hughes fb.save(outPath) 293*e1fe3e4aSElliott Hughes 294*e1fe3e4aSElliott Hughes _verifyOutput(outPath) 295*e1fe3e4aSElliott Hughes 296*e1fe3e4aSElliott Hughes 297*e1fe3e4aSElliott Hughesdef test_build_cff2(tmpdir): 298*e1fe3e4aSElliott Hughes outPath = os.path.join(str(tmpdir), "test_var.otf") 299*e1fe3e4aSElliott Hughes 300*e1fe3e4aSElliott Hughes fb, advanceWidths, nameStrings = _setupFontBuilder(False, 1000) 301*e1fe3e4aSElliott Hughes fb.setupNameTable(nameStrings) 302*e1fe3e4aSElliott Hughes fb = _setupFontBuilderFvar(fb) 303*e1fe3e4aSElliott Hughes fb = _setupFontBuilderCFF2(fb) 304*e1fe3e4aSElliott Hughes 305*e1fe3e4aSElliott Hughes metrics = {gn: (advanceWidth, 0) for gn, advanceWidth in advanceWidths.items()} 306*e1fe3e4aSElliott Hughes fb.setupHorizontalMetrics(metrics) 307*e1fe3e4aSElliott Hughes 308*e1fe3e4aSElliott Hughes fb.setupHorizontalHeader(ascent=824, descent=200) 309*e1fe3e4aSElliott Hughes fb.setupOS2( 310*e1fe3e4aSElliott Hughes sTypoAscender=825, sTypoDescender=200, usWinAscent=824, usWinDescent=200 311*e1fe3e4aSElliott Hughes ) 312*e1fe3e4aSElliott Hughes fb.setupPost() 313*e1fe3e4aSElliott Hughes 314*e1fe3e4aSElliott Hughes fb.save(outPath) 315*e1fe3e4aSElliott Hughes 316*e1fe3e4aSElliott Hughes _verifyOutput(outPath) 317*e1fe3e4aSElliott Hughes 318*e1fe3e4aSElliott Hughes 319*e1fe3e4aSElliott Hughesdef test_build_cff_to_cff2(tmpdir): 320*e1fe3e4aSElliott Hughes fb, _, _ = _setupFontBuilder(False, 1000) 321*e1fe3e4aSElliott Hughes 322*e1fe3e4aSElliott Hughes pen = T2CharStringPen(600, None) 323*e1fe3e4aSElliott Hughes drawTestGlyph(pen) 324*e1fe3e4aSElliott Hughes charString = pen.getCharString() 325*e1fe3e4aSElliott Hughes charStrings = { 326*e1fe3e4aSElliott Hughes ".notdef": charString, 327*e1fe3e4aSElliott Hughes "A": charString, 328*e1fe3e4aSElliott Hughes "a": charString, 329*e1fe3e4aSElliott Hughes ".null": charString, 330*e1fe3e4aSElliott Hughes } 331*e1fe3e4aSElliott Hughes fb.setupCFF("TestFont", {}, charStrings, {}) 332*e1fe3e4aSElliott Hughes 333*e1fe3e4aSElliott Hughes from fontTools.varLib.cff import convertCFFtoCFF2 334*e1fe3e4aSElliott Hughes 335*e1fe3e4aSElliott Hughes convertCFFtoCFF2(fb.font) 336*e1fe3e4aSElliott Hughes 337*e1fe3e4aSElliott Hughes 338*e1fe3e4aSElliott Hughesdef test_setupNameTable_no_mac(): 339*e1fe3e4aSElliott Hughes fb, _, nameStrings = _setupFontBuilder(True) 340*e1fe3e4aSElliott Hughes fb.setupNameTable(nameStrings, mac=False) 341*e1fe3e4aSElliott Hughes 342*e1fe3e4aSElliott Hughes assert all(n for n in fb.font["name"].names if n.platformID == 3) 343*e1fe3e4aSElliott Hughes assert not any(n for n in fb.font["name"].names if n.platformID == 1) 344*e1fe3e4aSElliott Hughes 345*e1fe3e4aSElliott Hughes 346*e1fe3e4aSElliott Hughesdef test_setupNameTable_no_windows(): 347*e1fe3e4aSElliott Hughes fb, _, nameStrings = _setupFontBuilder(True) 348*e1fe3e4aSElliott Hughes fb.setupNameTable(nameStrings, windows=False) 349*e1fe3e4aSElliott Hughes 350*e1fe3e4aSElliott Hughes assert all(n for n in fb.font["name"].names if n.platformID == 1) 351*e1fe3e4aSElliott Hughes assert not any(n for n in fb.font["name"].names if n.platformID == 3) 352*e1fe3e4aSElliott Hughes 353*e1fe3e4aSElliott Hughes 354*e1fe3e4aSElliott Hughes@pytest.mark.parametrize( 355*e1fe3e4aSElliott Hughes "is_ttf, keep_glyph_names, make_cff2, post_format", 356*e1fe3e4aSElliott Hughes [ 357*e1fe3e4aSElliott Hughes (True, True, False, 2), # TTF with post table format 2.0 358*e1fe3e4aSElliott Hughes (True, False, False, 3), # TTF with post table format 3.0 359*e1fe3e4aSElliott Hughes (False, True, False, 3), # CFF with post table format 3.0 360*e1fe3e4aSElliott Hughes (False, False, False, 3), # CFF with post table format 3.0 361*e1fe3e4aSElliott Hughes (False, True, True, 2), # CFF2 with post table format 2.0 362*e1fe3e4aSElliott Hughes (False, False, True, 3), # CFF2 with post table format 3.0 363*e1fe3e4aSElliott Hughes ], 364*e1fe3e4aSElliott Hughes) 365*e1fe3e4aSElliott Hughesdef test_setupPost(is_ttf, keep_glyph_names, make_cff2, post_format): 366*e1fe3e4aSElliott Hughes fb, _, nameStrings = _setupFontBuilder(is_ttf) 367*e1fe3e4aSElliott Hughes 368*e1fe3e4aSElliott Hughes if make_cff2: 369*e1fe3e4aSElliott Hughes fb.setupNameTable(nameStrings) 370*e1fe3e4aSElliott Hughes fb = _setupFontBuilderCFF2(_setupFontBuilderFvar(fb)) 371*e1fe3e4aSElliott Hughes 372*e1fe3e4aSElliott Hughes if keep_glyph_names: 373*e1fe3e4aSElliott Hughes fb.setupPost() 374*e1fe3e4aSElliott Hughes else: 375*e1fe3e4aSElliott Hughes fb.setupPost(keepGlyphNames=keep_glyph_names) 376*e1fe3e4aSElliott Hughes 377*e1fe3e4aSElliott Hughes assert fb.isTTF is is_ttf 378*e1fe3e4aSElliott Hughes assert ("CFF2" in fb.font) is make_cff2 379*e1fe3e4aSElliott Hughes assert fb.font["post"].formatType == post_format 380*e1fe3e4aSElliott Hughes 381*e1fe3e4aSElliott Hughes 382*e1fe3e4aSElliott Hughesdef test_unicodeVariationSequences(tmpdir): 383*e1fe3e4aSElliott Hughes familyName = "UVSTestFont" 384*e1fe3e4aSElliott Hughes styleName = "Regular" 385*e1fe3e4aSElliott Hughes nameStrings = dict(familyName=familyName, styleName=styleName) 386*e1fe3e4aSElliott Hughes nameStrings["psName"] = familyName + "-" + styleName 387*e1fe3e4aSElliott Hughes glyphOrder = [".notdef", "space", "zero", "zero.slash"] 388*e1fe3e4aSElliott Hughes cmap = {ord(" "): "space", ord("0"): "zero"} 389*e1fe3e4aSElliott Hughes uvs = [ 390*e1fe3e4aSElliott Hughes (0x0030, 0xFE00, "zero.slash"), 391*e1fe3e4aSElliott Hughes (0x0030, 0xFE01, None), # not an official sequence, just testing 392*e1fe3e4aSElliott Hughes ] 393*e1fe3e4aSElliott Hughes metrics = {gn: (600, 0) for gn in glyphOrder} 394*e1fe3e4aSElliott Hughes pen = TTGlyphPen(None) 395*e1fe3e4aSElliott Hughes glyph = pen.glyph() # empty placeholder 396*e1fe3e4aSElliott Hughes glyphs = {gn: glyph for gn in glyphOrder} 397*e1fe3e4aSElliott Hughes 398*e1fe3e4aSElliott Hughes fb = FontBuilder(1024, isTTF=True) 399*e1fe3e4aSElliott Hughes fb.setupGlyphOrder(glyphOrder) 400*e1fe3e4aSElliott Hughes fb.setupCharacterMap(cmap, uvs) 401*e1fe3e4aSElliott Hughes fb.setupGlyf(glyphs) 402*e1fe3e4aSElliott Hughes fb.setupHorizontalMetrics(metrics) 403*e1fe3e4aSElliott Hughes fb.setupHorizontalHeader(ascent=824, descent=200) 404*e1fe3e4aSElliott Hughes fb.setupNameTable(nameStrings) 405*e1fe3e4aSElliott Hughes fb.setupOS2() 406*e1fe3e4aSElliott Hughes fb.setupPost() 407*e1fe3e4aSElliott Hughes 408*e1fe3e4aSElliott Hughes outPath = os.path.join(str(tmpdir), "test_uvs.ttf") 409*e1fe3e4aSElliott Hughes fb.save(outPath) 410*e1fe3e4aSElliott Hughes _verifyOutput(outPath, tables=["cmap"]) 411*e1fe3e4aSElliott Hughes 412*e1fe3e4aSElliott Hughes uvs = [ 413*e1fe3e4aSElliott Hughes (0x0030, 0xFE00, "zero.slash"), 414*e1fe3e4aSElliott Hughes ( 415*e1fe3e4aSElliott Hughes 0x0030, 416*e1fe3e4aSElliott Hughes 0xFE01, 417*e1fe3e4aSElliott Hughes "zero", 418*e1fe3e4aSElliott Hughes ), # should result in the exact same subtable data, due to cmap[0x0030] == "zero" 419*e1fe3e4aSElliott Hughes ] 420*e1fe3e4aSElliott Hughes fb.setupCharacterMap(cmap, uvs) 421*e1fe3e4aSElliott Hughes fb.save(outPath) 422*e1fe3e4aSElliott Hughes _verifyOutput(outPath, tables=["cmap"]) 423*e1fe3e4aSElliott Hughes 424*e1fe3e4aSElliott Hughes 425*e1fe3e4aSElliott Hughesdef test_setupPanose(): 426*e1fe3e4aSElliott Hughes from fontTools.ttLib.tables.O_S_2f_2 import Panose 427*e1fe3e4aSElliott Hughes 428*e1fe3e4aSElliott Hughes fb, advanceWidths, nameStrings = _setupFontBuilder(True) 429*e1fe3e4aSElliott Hughes 430*e1fe3e4aSElliott Hughes pen = TTGlyphPen(None) 431*e1fe3e4aSElliott Hughes drawTestGlyph(pen) 432*e1fe3e4aSElliott Hughes glyph = pen.glyph() 433*e1fe3e4aSElliott Hughes glyphs = {".notdef": glyph, "A": glyph, "a": glyph, ".null": glyph} 434*e1fe3e4aSElliott Hughes fb.setupGlyf(glyphs) 435*e1fe3e4aSElliott Hughes metrics = {} 436*e1fe3e4aSElliott Hughes glyphTable = fb.font["glyf"] 437*e1fe3e4aSElliott Hughes for gn, advanceWidth in advanceWidths.items(): 438*e1fe3e4aSElliott Hughes metrics[gn] = (advanceWidth, glyphTable[gn].xMin) 439*e1fe3e4aSElliott Hughes fb.setupHorizontalMetrics(metrics) 440*e1fe3e4aSElliott Hughes 441*e1fe3e4aSElliott Hughes fb.setupHorizontalHeader(ascent=824, descent=200) 442*e1fe3e4aSElliott Hughes fb.setupNameTable(nameStrings) 443*e1fe3e4aSElliott Hughes fb.setupOS2() 444*e1fe3e4aSElliott Hughes fb.setupPost() 445*e1fe3e4aSElliott Hughes 446*e1fe3e4aSElliott Hughes panoseValues = { # sample value of Times New Roman from https://www.w3.org/Printing/stevahn.html 447*e1fe3e4aSElliott Hughes "bFamilyType": 2, 448*e1fe3e4aSElliott Hughes "bSerifStyle": 2, 449*e1fe3e4aSElliott Hughes "bWeight": 6, 450*e1fe3e4aSElliott Hughes "bProportion": 3, 451*e1fe3e4aSElliott Hughes "bContrast": 5, 452*e1fe3e4aSElliott Hughes "bStrokeVariation": 4, 453*e1fe3e4aSElliott Hughes "bArmStyle": 5, 454*e1fe3e4aSElliott Hughes "bLetterForm": 2, 455*e1fe3e4aSElliott Hughes "bMidline": 3, 456*e1fe3e4aSElliott Hughes "bXHeight": 4, 457*e1fe3e4aSElliott Hughes } 458*e1fe3e4aSElliott Hughes panoseObj = Panose(**panoseValues) 459*e1fe3e4aSElliott Hughes 460*e1fe3e4aSElliott Hughes for name in panoseValues: 461*e1fe3e4aSElliott Hughes assert getattr(fb.font["OS/2"].panose, name) == 0 462*e1fe3e4aSElliott Hughes 463*e1fe3e4aSElliott Hughes fb.setupOS2(panose=panoseObj) 464*e1fe3e4aSElliott Hughes fb.setupPost() 465*e1fe3e4aSElliott Hughes 466*e1fe3e4aSElliott Hughes for name, value in panoseValues.items(): 467*e1fe3e4aSElliott Hughes assert getattr(fb.font["OS/2"].panose, name) == value 468