2018-10-31 20:54:34 +01:00
|
|
|
from __future__ import print_function, division, absolute_import
|
|
|
|
from __future__ import unicode_literals
|
|
|
|
|
|
|
|
import os
|
|
|
|
import shutil
|
|
|
|
import re
|
|
|
|
from fontTools.ttLib import TTFont
|
|
|
|
from fontTools.pens.ttGlyphPen import TTGlyphPen
|
|
|
|
from fontTools.pens.t2CharStringPen import T2CharStringPen
|
|
|
|
from fontTools.fontBuilder import FontBuilder
|
2018-11-01 21:33:08 +01:00
|
|
|
from fontTools.ttLib.tables.TupleVariation import TupleVariation
|
|
|
|
from fontTools.ttLib.tables._g_l_y_f import GlyphCoordinates
|
2018-10-31 20:54:34 +01:00
|
|
|
|
|
|
|
|
|
|
|
def getTestData(fileName, mode="r"):
|
|
|
|
path = os.path.join(os.path.dirname(__file__), "data", fileName)
|
|
|
|
with open(path, mode) as f:
|
|
|
|
return f.read()
|
|
|
|
|
|
|
|
|
|
|
|
def strip_VariableItems(string):
|
|
|
|
# ttlib changes with the fontTools version
|
|
|
|
string = re.sub(' ttLibVersion=".*"', '', string)
|
|
|
|
# head table checksum and creation and mod date changes with each save.
|
|
|
|
string = re.sub('<checkSumAdjustment value="[^"]+"/>', '', string)
|
|
|
|
string = re.sub('<modified value="[^"]+"/>', '', string)
|
|
|
|
string = re.sub('<created value="[^"]+"/>', '', string)
|
|
|
|
return string
|
|
|
|
|
|
|
|
|
|
|
|
def drawTestGlyph(pen):
|
|
|
|
pen.moveTo((100, 100))
|
|
|
|
pen.lineTo((100, 1000))
|
|
|
|
pen.qCurveTo((200, 900), (400, 900), (500, 1000))
|
|
|
|
pen.lineTo((500, 100))
|
|
|
|
pen.closePath()
|
|
|
|
|
|
|
|
|
|
|
|
def _setupFontBuilder(isTTF):
|
|
|
|
fb = FontBuilder(1024, isTTF=isTTF)
|
|
|
|
fb.setupGlyphOrder([".notdef", ".null", "A", "a"])
|
|
|
|
fb.setupCharacterMap({65: "A", 97: "a"})
|
|
|
|
|
|
|
|
advanceWidths = {".notdef": 600, "A": 600, "a": 600, ".null": 600}
|
|
|
|
|
2018-11-01 09:52:06 +01:00
|
|
|
familyName = "HelloTestFont"
|
|
|
|
styleName = "TotallyNormal"
|
2018-11-01 09:55:27 +01:00
|
|
|
nameStrings = dict(familyName=dict(en="HelloTestFont", nl="HalloTestFont"),
|
|
|
|
styleName=dict(en="TotallyNormal", nl="TotaalNormaal"))
|
2018-11-01 09:52:06 +01:00
|
|
|
nameStrings['psName'] = familyName + "-" + styleName
|
2018-10-31 20:54:34 +01:00
|
|
|
|
|
|
|
return fb, advanceWidths, nameStrings
|
|
|
|
|
|
|
|
|
|
|
|
def test_build_ttf(tmpdir):
|
2018-10-31 21:09:27 +01:00
|
|
|
outPath = os.path.join(str(tmpdir), "test.ttf")
|
2018-10-31 20:54:34 +01:00
|
|
|
|
|
|
|
fb, advanceWidths, nameStrings = _setupFontBuilder(True)
|
|
|
|
|
|
|
|
pen = TTGlyphPen(None)
|
|
|
|
drawTestGlyph(pen)
|
|
|
|
glyph = pen.glyph()
|
|
|
|
glyphs = {".notdef": glyph, "A": glyph, "a": glyph, ".null": glyph}
|
|
|
|
fb.setupGlyf(glyphs)
|
|
|
|
metrics = {}
|
|
|
|
glyphTable = fb.font["glyf"]
|
|
|
|
for gn, advanceWidth in advanceWidths.items():
|
|
|
|
metrics[gn] = (advanceWidth, glyphTable[gn].xMin)
|
2018-11-01 20:30:21 +01:00
|
|
|
fb.setupHorizontalMetrics(metrics)
|
2018-10-31 20:54:34 +01:00
|
|
|
|
2018-11-01 21:33:08 +01:00
|
|
|
fb.setupHorizontalHeader(ascent=824, descent=200)
|
2018-10-31 20:54:34 +01:00
|
|
|
fb.setupNameTable(nameStrings)
|
|
|
|
fb.setupOS2()
|
2018-11-01 09:57:35 +01:00
|
|
|
fb.setupPost()
|
2018-11-01 13:19:27 +01:00
|
|
|
fb.setupDummyDSIG()
|
2018-10-31 20:54:34 +01:00
|
|
|
|
|
|
|
fb.save(outPath)
|
|
|
|
|
|
|
|
f = TTFont(outPath)
|
|
|
|
f.saveXML(outPath + ".ttx")
|
|
|
|
with open(outPath + ".ttx") as f:
|
|
|
|
testData = strip_VariableItems(f.read())
|
|
|
|
refData = strip_VariableItems(getTestData("test.ttf.ttx"))
|
|
|
|
assert refData == testData
|
|
|
|
|
|
|
|
|
|
|
|
def test_build_otf(tmpdir):
|
2018-10-31 21:09:27 +01:00
|
|
|
outPath = os.path.join(str(tmpdir), "test.otf")
|
2018-10-31 20:54:34 +01:00
|
|
|
|
|
|
|
fb, advanceWidths, nameStrings = _setupFontBuilder(False)
|
|
|
|
|
|
|
|
pen = T2CharStringPen(600, None)
|
|
|
|
drawTestGlyph(pen)
|
|
|
|
charString = pen.getCharString()
|
|
|
|
charStrings = {".notdef": charString, "A": charString, "a": charString, ".null": charString}
|
|
|
|
fb.setupCFF(nameStrings['psName'], {"FullName": nameStrings['psName']}, charStrings, {})
|
|
|
|
metrics = {}
|
|
|
|
for gn, advanceWidth in advanceWidths.items():
|
|
|
|
metrics[gn] = (advanceWidth, 100) # XXX lsb from glyph
|
2018-11-01 20:30:21 +01:00
|
|
|
fb.setupHorizontalMetrics(metrics)
|
2018-10-31 20:54:34 +01:00
|
|
|
|
2018-11-01 21:33:08 +01:00
|
|
|
fb.setupHorizontalHeader(ascent=824, descent=200)
|
2018-10-31 20:54:34 +01:00
|
|
|
fb.setupNameTable(nameStrings)
|
|
|
|
fb.setupOS2()
|
2018-11-01 09:57:35 +01:00
|
|
|
fb.setupPost()
|
2018-11-01 13:19:27 +01:00
|
|
|
fb.setupDummyDSIG()
|
2018-10-31 20:54:34 +01:00
|
|
|
|
|
|
|
fb.save(outPath)
|
|
|
|
|
|
|
|
f = TTFont(outPath)
|
|
|
|
f.saveXML(outPath + ".ttx")
|
|
|
|
with open(outPath + ".ttx") as f:
|
|
|
|
testData = strip_VariableItems(f.read())
|
|
|
|
refData = strip_VariableItems(getTestData("test.otf.ttx"))
|
|
|
|
assert refData == testData
|
2018-11-01 21:33:08 +01:00
|
|
|
|
|
|
|
|
|
|
|
def test_build_var(tmpdir):
|
|
|
|
outPath = os.path.join(str(tmpdir), "test_var.ttf")
|
|
|
|
|
|
|
|
fb = FontBuilder(1024, isTTF=True)
|
|
|
|
fb.setupGlyphOrder([".notdef", ".null", "A", "a"])
|
|
|
|
fb.setupCharacterMap({65: "A", 97: "a"})
|
|
|
|
|
|
|
|
advanceWidths = {".notdef": 600, "A": 600, "a": 600, ".null": 600}
|
|
|
|
|
|
|
|
familyName = "HelloTestFont"
|
|
|
|
styleName = "TotallyNormal"
|
|
|
|
nameStrings = dict(familyName=dict(en="HelloTestFont", nl="HalloTestFont"),
|
|
|
|
styleName=dict(en="TotallyNormal", nl="TotaalNormaal"))
|
|
|
|
nameStrings['psName'] = familyName + "-" + styleName
|
|
|
|
|
|
|
|
pen = TTGlyphPen(None)
|
|
|
|
pen.moveTo((100, 0))
|
|
|
|
pen.lineTo((100, 400))
|
|
|
|
pen.lineTo((500, 400))
|
|
|
|
pen.lineTo((500, 000))
|
|
|
|
pen.closePath()
|
|
|
|
|
|
|
|
glyph = pen.glyph()
|
|
|
|
|
|
|
|
pen = TTGlyphPen(None)
|
|
|
|
emptyGlyph = pen.glyph()
|
|
|
|
|
|
|
|
glyphs = {".notdef": emptyGlyph, "A": glyph, "a": glyph, ".null": emptyGlyph}
|
|
|
|
fb.setupGlyf(glyphs)
|
|
|
|
metrics = {}
|
|
|
|
glyphTable = fb.font["glyf"]
|
|
|
|
for gn, advanceWidth in advanceWidths.items():
|
|
|
|
metrics[gn] = (advanceWidth, glyphTable[gn].xMin)
|
|
|
|
fb.setupHorizontalMetrics(metrics)
|
|
|
|
|
|
|
|
fb.setupHorizontalHeader(ascent=824, descent=200)
|
|
|
|
fb.setupNameTable(nameStrings)
|
|
|
|
|
|
|
|
axes = [
|
|
|
|
('LEFT', 0, 0, 100, "Left"),
|
|
|
|
('RGHT', 0, 0, 100, "Right"),
|
|
|
|
('UPPP', 0, 0, 100, "Up"),
|
|
|
|
('DOWN', 0, 0, 100, "Down"),
|
|
|
|
]
|
2018-11-02 08:02:34 +01:00
|
|
|
instances = [
|
|
|
|
dict(location=dict(LEFT=0, RGHT=0, UPPP=0, DOWN=0), stylename="TotallyNormal"),
|
|
|
|
dict(location=dict(LEFT=0, RGHT=100, UPPP=100, DOWN=0), stylename="Right Up"),
|
|
|
|
]
|
2018-11-01 21:33:08 +01:00
|
|
|
fb.setupFvar(axes, instances)
|
|
|
|
variations = {}
|
|
|
|
# Four (x, y) pairs and four phantom points:
|
|
|
|
leftDeltas = [(-200, 0), (-200, 0), (0, 0), (0, 0), None, None, None, None]
|
|
|
|
rightDeltas = [(0, 0), (0, 0), (200, 0), (200, 0), None, None, None, None]
|
|
|
|
upDeltas = [(0, 0), (0, 200), (0, 200), (0, 0), None, None, None, None]
|
|
|
|
downDeltas = [(0, -200), (0, 0), (0, 0), (0, -200), None, None, None, None]
|
|
|
|
variations['a'] = [
|
|
|
|
TupleVariation(dict(RGHT=(0, 1, 1)), rightDeltas),
|
|
|
|
TupleVariation(dict(LEFT=(0, 1, 1)), leftDeltas),
|
|
|
|
TupleVariation(dict(UPPP=(0, 1, 1)), upDeltas),
|
|
|
|
TupleVariation(dict(DOWN=(0, 1, 1)), downDeltas),
|
|
|
|
]
|
|
|
|
fb.setupGvar(variations)
|
|
|
|
|
|
|
|
fb.setupOS2()
|
|
|
|
fb.setupPost()
|
|
|
|
fb.setupDummyDSIG()
|
|
|
|
|
|
|
|
fb.save(outPath)
|
|
|
|
|
|
|
|
f = TTFont(outPath)
|
|
|
|
f.saveXML(outPath + ".ttx")
|
|
|
|
with open(outPath + ".ttx") as f:
|
|
|
|
testData = strip_VariableItems(f.read())
|
|
|
|
refData = strip_VariableItems(getTestData("test_var.ttf.ttx"))
|
|
|
|
assert refData == testData
|