Merge branch 'master' into addmultilingualnames-mac

This commit is contained in:
justvanrossum 2018-11-02 12:30:24 +01:00
commit 3b7ac3dd78
5 changed files with 590 additions and 27 deletions

View File

@ -17,7 +17,7 @@ that works:
fb.setupGlyphOrder(...)
fb.setupCharacterMap(...)
fb.setupGlyf(...) --or-- fb.setupCFF(...)
fb.setupMetrics("hmtx", ...)
fb.setupHorizontalMetrics(...)
fb.setupHorizontalHeader()
fb.setupNameTable(...)
fb.setupOS2()
@ -59,9 +59,9 @@ metrics = {}
glyphTable = fb.font["glyf"]
for gn, advanceWidth in advanceWidths.items():
metrics[gn] = (advanceWidth, glyphTable[gn].xMin)
fb.setupMetrics("hmtx", metrics)
fb.setupHorizontalMetrics(metrics)
fb.setupHorizontalHeader()
fb.setupHorizontalHeader(ascent=824, descent=200)
fb.setupNameTable(nameStrings)
fb.setupOS2()
fb.setupPost()
@ -103,9 +103,9 @@ fb.setupCFF(nameStrings['psName'], {"FullName": nameStrings['psName']}, charStri
metrics = {}
for gn, advanceWidth in advanceWidths.items():
metrics[gn] = (advanceWidth, 100) # XXX lsb from glyph
fb.setupMetrics("hmtx", metrics)
fb.setupHorizontalMetrics(metrics)
fb.setupHorizontalHeader()
fb.setupHorizontalHeader(ascent=824, descent=200)
fb.setupNameTable(nameStrings)
fb.setupOS2()
fb.setupPost()
@ -217,20 +217,37 @@ _vheaDefaults = dict(
)
_nameIDs = dict(
copyright = 0,
familyName = 1,
styleName = 2,
identifier = 3,
fullName = 4,
version = 5,
psName = 6,
trademark = 7,
manufacturer = 8,
typographicFamily = 16,
typographicSubfamily = 17,
# XXX this needs to be extended with legal things, etc.
copyright = 0,
familyName = 1,
styleName = 2,
uniqueFontIdentifier = 3,
fullName = 4,
version = 5,
psName = 6,
trademark = 7,
manufacturer = 8,
designer = 9,
description = 10,
vendorURL = 11,
designerURL = 12,
licenseDescription = 13,
licenseInfoURL = 14,
# reserved = 15,
typographicFamily = 16,
typographicSubfamily = 17,
compatibleFullName = 18,
sampleText = 19,
postScriptCIDFindfontName = 20,
wwsFamilyName = 21,
wwsSubfamilyName = 22,
lightBackgroundPalette = 23,
darkBackgroundPalette = 24,
variationsPostScriptNamePrefix = 25,
)
# to insert in setupNameTable doc string:
# print("\n".join(("%s (nameID %s)" % (k, v)) for k, v in sorted(_nameIDs.items(), key=lambda x: x[1])))
_panoseDefaults = dict(
bFamilyType = 0,
bSerifStyle = 0,
@ -312,8 +329,11 @@ class FontBuilder(object):
self.font = font
self.isTTF = "glyf" in font
def save(self, path):
self.font.save(path)
def save(self, file):
"""Save the font. The 'file' argument can be either a pathname or a
writable file object.
"""
self.font.save(file)
def _initTableWithValues(self, tableTag, defaults, values):
table = self.font[tableTag] = newTable(tableTag)
@ -329,15 +349,25 @@ class FontBuilder(object):
setattr(table, k, v)
def setupHead(self, **values):
"""Create a new `head` table and initialize it with default values,
which can be overridden by keyword arguments.
"""
self._initTableWithValues("head", _headDefaults, values)
def updateHead(self, **values):
"""Update the head table with the fields and values passed as
keyword arguments.
"""
self._updateTableWithValues("head", values)
def setupGlyphOrder(self, glyphOrder):
"""Set the glyph order for the font."""
self.font.setGlyphOrder(glyphOrder)
def setupCharacterMap(self, cmapping, allowFallback=False):
"""Build the `cmap` table for the font. The `cmapping` argument should
be a dict mapping unicode code points as integers to glyph names.
"""
subTables = []
highestUnicode = max(cmapping)
if highestUnicode > 0xffff:
@ -365,6 +395,39 @@ class FontBuilder(object):
self.font["cmap"].tables = subTables
def setupNameTable(self, nameStrings):
"""Create the `name` table for the font. The `nameStrings` argument must
be a dict, mapping nameIDs or descriptive names for the nameIDs to name
record values. A value is either a string, or a dict, mapping language codes
to strings, to allow localized name table entries.
The following descriptive names are available for nameIDs:
copyright (nameID 0)
familyName (nameID 1)
styleName (nameID 2)
uniqueFontIdentifier (nameID 3)
fullName (nameID 4)
version (nameID 5)
psName (nameID 6)
trademark (nameID 7)
manufacturer (nameID 8)
designer (nameID 9)
description (nameID 10)
vendorURL (nameID 11)
designerURL (nameID 12)
licenseDescription (nameID 13)
licenseInfoURL (nameID 14)
typographicFamily (nameID 16)
typographicSubfamily (nameID 17)
compatibleFullName (nameID 18)
sampleText (nameID 19)
postScriptCIDFindfontName (nameID 20)
wwsFamilyName (nameID 21)
wwsSubfamilyName (nameID 22)
lightBackgroundPalette (nameID 23)
darkBackgroundPalette (nameID 24)
variationsPostScriptNamePrefix (nameID 25)
"""
nameTable = self.font["name"] = newTable("name")
nameTable.names = []
@ -378,6 +441,9 @@ class FontBuilder(object):
nameTable.addMultilingualName(nameValue, ttFont=self.font, nameID=nameID)
def setupOS2(self, **values):
"""Create a new `OS/2` table and initialize it with default values,
which can be overridden by keyword arguments.
"""
if "xAvgCharWidth" not in values:
gs = self.font.getGlyphSet()
widths = [gs[glyphName].width for glyphName in gs.keys() if gs[glyphName].width > 0]
@ -426,6 +492,14 @@ class FontBuilder(object):
self.font["CFF "].cff = fontSet
def setupGlyf(self, glyphs, calcGlyphBounds=True):
"""Create the `glyf` table from a dict, that maps glyph names
to `fontTools.ttLib.tables._g_l_y_f.Glyph` objects, for example
as made by `fontTools.pens.ttGlyphPen.TTGlyphPen`.
If `calcGlyphBounds` is True, the bounds of all glyphs will be
calculated. Only pass False if your glyph objects already have
their bounding box values set.
"""
assert self.isTTF
self.font["loca"] = newTable("loca")
self.font["glyf"] = newTable("glyf")
@ -445,11 +519,31 @@ class FontBuilder(object):
gvar.variations = variations
def calcGlyphBounds(self):
"""Calculate the bounding boxes of all glyphs in the `glyf` table.
This is usually not called explicitly by client code.
"""
glyphTable = self.font["glyf"]
for glyph in glyphTable.glyphs.values():
glyph.recalcBounds(glyphTable)
def setupHorizontalMetrics(self, metrics):
"""Create a new `hmtx` table, for horizontal metrics.
The `metrics` argument must be a dict, mapping glyph names to
`(width, leftSidebearing)` tuples.
"""
self.setupMetrics('hmtx', metrics)
def setupVerticalMetrics(self, metrics):
"""Create a new `vmtx` table, for horizontal metrics.
The `metrics` argument must be a dict, mapping glyph names to
`(height, topSidebearing)` tuples.
"""
self.setupMetrics('vmtx', metrics)
def setupMetrics(self, tableTag, metrics):
"""See `setupHorizontalMetrics()` and `setupVerticalMetrics()`."""
assert tableTag in ("hmtx", "vmtx")
mtxTable = self.font[tableTag] = newTable(tableTag)
roundedMetrics = {}
@ -459,12 +553,25 @@ class FontBuilder(object):
mtxTable.metrics = roundedMetrics
def setupHorizontalHeader(self, **values):
"""Create a new `hhea` table initialize it with default values,
which can be overridden by keyword arguments.
"""
self._initTableWithValues("hhea", _hheaDefaults, values)
def setupVerticalHeader(self, **values):
"""Create a new `vhea` table initialize it with default values,
which can be overridden by keyword arguments.
"""
self._initTableWithValues("vhea", _vheaDefaults, values)
def setupVerticalOrigins(self, verticalOrigins, defaultVerticalOrigin=None):
"""Create a new `VORG` table. The `verticalOrigins` argument must be
a dict, mapping glyph names to vertical origin values.
The `defaultVerticalOrigin` argument should be the most common vertical
origin value. If omitted, this value will be derived from the actual
values in the `verticalOrigins` argument.
"""
if defaultVerticalOrigin is None:
# find the most frequent vorg value
bag = {}
@ -483,6 +590,9 @@ class FontBuilder(object):
vorgTable[gn] = verticalOrigins[gn]
def setupPost(self, keepGlyphNames=True, **values):
"""Create a new `post` table and initialize it with default values,
which can be overridden by keyword arguments.
"""
postTable = self._initTableWithValues("post", _postDefaults, values)
if self.isTTF and keepGlyphNames:
postTable.formatType = 2.0
@ -492,6 +602,9 @@ class FontBuilder(object):
postTable.formatType = 3.0
def setupMaxp(self):
"""Create a new `maxp` table. This is called implicitly by FontBuilder
itself and is usually not called by client code.
"""
if self.isTTF:
defaults = _maxpDefaultsTTF
else:
@ -522,6 +635,16 @@ class FontBuilder(object):
self._initTableWithValues("DSIG", {}, values)
def addOpenTypeFeatures(self, features, filename=None, tables=None):
"""Add OpenType features to the font from a string containing
Feature File syntax.
The `filename` argument is used in error messages and to determine
where to look for "include" files.
The optional `tables` argument can be a list of OTL tables tags to
build, allowing the caller to only build selected OTL tables. See
`fontTools.feaLib` for details.
"""
from .feaLib.builder import addOpenTypeFeaturesFromString
addOpenTypeFeaturesFromString(self.font, features, filename=filename, tables=tables)

View File

@ -32,8 +32,8 @@
<hhea>
<tableVersion value="0x00010000"/>
<ascent value="0"/>
<descent value="0"/>
<ascent value="824"/>
<descent value="200"/>
<lineGap value="0"/>
<advanceWidthMax value="600"/>
<minLeftSideBearing value="100"/>

View File

@ -32,8 +32,8 @@
<hhea>
<tableVersion value="0x00010000"/>
<ascent value="0"/>
<descent value="0"/>
<ascent value="824"/>
<descent value="200"/>
<lineGap value="0"/>
<advanceWidthMax value="600"/>
<minLeftSideBearing value="100"/>

View File

@ -0,0 +1,361 @@
<?xml version="1.0" encoding="UTF-8"?>
<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.32">
<GlyphOrder>
<!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
<GlyphID id="0" name=".notdef"/>
<GlyphID id="1" name=".null"/>
<GlyphID id="2" name="A"/>
<GlyphID id="3" name="a"/>
</GlyphOrder>
<head>
<!-- Most of this table will be recalculated by the compiler -->
<tableVersion value="1.0"/>
<fontRevision value="1.0"/>
<checkSumAdjustment value="0x18e72247"/>
<magicNumber value="0x5f0f3cf5"/>
<flags value="00000000 00000011"/>
<unitsPerEm value="1024"/>
<created value="Thu Nov 1 20:29:01 2018"/>
<modified value="Thu Nov 1 20:29:01 2018"/>
<xMin value="100"/>
<yMin value="0"/>
<xMax value="500"/>
<yMax value="400"/>
<macStyle value="00000000 00000000"/>
<lowestRecPPEM value="3"/>
<fontDirectionHint value="2"/>
<indexToLocFormat value="0"/>
<glyphDataFormat value="0"/>
</head>
<hhea>
<tableVersion value="0x00010000"/>
<ascent value="824"/>
<descent value="200"/>
<lineGap value="0"/>
<advanceWidthMax value="600"/>
<minLeftSideBearing value="100"/>
<minRightSideBearing value="100"/>
<xMaxExtent value="500"/>
<caretSlopeRise value="1"/>
<caretSlopeRun value="0"/>
<caretOffset value="0"/>
<reserved0 value="0"/>
<reserved1 value="0"/>
<reserved2 value="0"/>
<reserved3 value="0"/>
<metricDataFormat value="0"/>
<numberOfHMetrics value="1"/>
</hhea>
<maxp>
<!-- Most of this table will be recalculated by the compiler -->
<tableVersion value="0x10000"/>
<numGlyphs value="4"/>
<maxPoints value="4"/>
<maxContours value="1"/>
<maxCompositePoints value="0"/>
<maxCompositeContours value="0"/>
<maxZones value="2"/>
<maxTwilightPoints value="0"/>
<maxStorage value="0"/>
<maxFunctionDefs value="0"/>
<maxInstructionDefs value="0"/>
<maxStackElements value="0"/>
<maxSizeOfInstructions value="0"/>
<maxComponentElements value="0"/>
<maxComponentDepth value="0"/>
</maxp>
<OS_2>
<!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
will be recalculated by the compiler -->
<version value="3"/>
<xAvgCharWidth value="600"/>
<usWeightClass value="400"/>
<usWidthClass value="5"/>
<fsType value="00000000 00000100"/>
<ySubscriptXSize value="0"/>
<ySubscriptYSize value="0"/>
<ySubscriptXOffset value="0"/>
<ySubscriptYOffset value="0"/>
<ySuperscriptXSize value="0"/>
<ySuperscriptYSize value="0"/>
<ySuperscriptXOffset value="0"/>
<ySuperscriptYOffset value="0"/>
<yStrikeoutSize value="0"/>
<yStrikeoutPosition value="0"/>
<sFamilyClass value="0"/>
<panose>
<bFamilyType value="0"/>
<bSerifStyle value="0"/>
<bWeight value="0"/>
<bProportion value="0"/>
<bContrast value="0"/>
<bStrokeVariation value="0"/>
<bArmStyle value="0"/>
<bLetterForm value="0"/>
<bMidline value="0"/>
<bXHeight value="0"/>
</panose>
<ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
<ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
<ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
<ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
<achVendID value="????"/>
<fsSelection value="00000000 00000000"/>
<usFirstCharIndex value="65"/>
<usLastCharIndex value="97"/>
<sTypoAscender value="0"/>
<sTypoDescender value="0"/>
<sTypoLineGap value="0"/>
<usWinAscent value="0"/>
<usWinDescent value="0"/>
<ulCodePageRange1 value="00000000 00000000 00000000 00000000"/>
<ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
<sxHeight value="0"/>
<sCapHeight value="0"/>
<usDefaultChar value="0"/>
<usBreakChar value="32"/>
<usMaxContext value="2"/>
</OS_2>
<hmtx>
<mtx name=".notdef" width="600" lsb="0"/>
<mtx name=".null" width="600" lsb="0"/>
<mtx name="A" width="600" lsb="100"/>
<mtx name="a" width="600" lsb="100"/>
</hmtx>
<cmap>
<tableVersion version="0"/>
<cmap_format_4 platformID="0" platEncID="3" language="0">
<map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
<map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
</cmap_format_4>
<cmap_format_4 platformID="3" platEncID="1" language="0">
<map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
<map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
</cmap_format_4>
</cmap>
<loca>
<!-- The 'loca' table will be calculated by the compiler -->
</loca>
<glyf>
<!-- The xMin, yMin, xMax and yMax values
will be recalculated by the compiler. -->
<TTGlyph name=".notdef"/><!-- contains no outline data -->
<TTGlyph name=".null"/><!-- contains no outline data -->
<TTGlyph name="A" xMin="100" yMin="0" xMax="500" yMax="400">
<contour>
<pt x="100" y="0" on="1"/>
<pt x="100" y="400" on="1"/>
<pt x="500" y="400" on="1"/>
<pt x="500" y="0" on="1"/>
</contour>
<instructions/>
</TTGlyph>
<TTGlyph name="a" xMin="100" yMin="0" xMax="500" yMax="400">
<contour>
<pt x="100" y="0" on="1"/>
<pt x="100" y="400" on="1"/>
<pt x="500" y="400" on="1"/>
<pt x="500" y="0" on="1"/>
</contour>
<instructions/>
</TTGlyph>
</glyf>
<name>
<namerecord nameID="256" platformID="1" platEncID="0" langID="0x0" unicode="True">
Left
</namerecord>
<namerecord nameID="257" platformID="1" platEncID="0" langID="0x0" unicode="True">
Right
</namerecord>
<namerecord nameID="258" platformID="1" platEncID="0" langID="0x0" unicode="True">
Up
</namerecord>
<namerecord nameID="259" platformID="1" platEncID="0" langID="0x0" unicode="True">
Down
</namerecord>
<namerecord nameID="260" platformID="1" platEncID="0" langID="0x0" unicode="True">
TotallyNormal
</namerecord>
<namerecord nameID="261" platformID="1" platEncID="0" langID="0x0" unicode="True">
Right Up
</namerecord>
<namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
HelloTestFont
</namerecord>
<namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
TotallyNormal
</namerecord>
<namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
HelloTestFont-TotallyNormal
</namerecord>
<namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
Left
</namerecord>
<namerecord nameID="257" platformID="3" platEncID="1" langID="0x409">
Right
</namerecord>
<namerecord nameID="258" platformID="3" platEncID="1" langID="0x409">
Up
</namerecord>
<namerecord nameID="259" platformID="3" platEncID="1" langID="0x409">
Down
</namerecord>
<namerecord nameID="260" platformID="3" platEncID="1" langID="0x409">
TotallyNormal
</namerecord>
<namerecord nameID="261" platformID="3" platEncID="1" langID="0x409">
Right Up
</namerecord>
<namerecord nameID="1" platformID="3" platEncID="1" langID="0x413">
HalloTestFont
</namerecord>
<namerecord nameID="2" platformID="3" platEncID="1" langID="0x413">
TotaalNormaal
</namerecord>
</name>
<post>
<formatType value="2.0"/>
<italicAngle value="0.0"/>
<underlinePosition value="0"/>
<underlineThickness value="0"/>
<isFixedPitch value="0"/>
<minMemType42 value="0"/>
<maxMemType42 value="0"/>
<minMemType1 value="0"/>
<maxMemType1 value="0"/>
<psNames>
<!-- This file uses unique glyph names based on the information
found in the 'post' table. Since these names might not be unique,
we have to invent artificial names in case of clashes. In order to
be able to retain the original information, we need a name to
ps name mapping for those cases where they differ. That's what
you see below.
-->
</psNames>
<extraNames>
<!-- following are the name that are not taken from the standard Mac glyph order -->
</extraNames>
</post>
<fvar>
<!-- Left -->
<Axis>
<AxisTag>LEFT</AxisTag>
<Flags>0x0</Flags>
<MinValue>0.0</MinValue>
<DefaultValue>0.0</DefaultValue>
<MaxValue>100.0</MaxValue>
<AxisNameID>256</AxisNameID>
</Axis>
<!-- Right -->
<Axis>
<AxisTag>RGHT</AxisTag>
<Flags>0x0</Flags>
<MinValue>0.0</MinValue>
<DefaultValue>0.0</DefaultValue>
<MaxValue>100.0</MaxValue>
<AxisNameID>257</AxisNameID>
</Axis>
<!-- Up -->
<Axis>
<AxisTag>UPPP</AxisTag>
<Flags>0x0</Flags>
<MinValue>0.0</MinValue>
<DefaultValue>0.0</DefaultValue>
<MaxValue>100.0</MaxValue>
<AxisNameID>258</AxisNameID>
</Axis>
<!-- Down -->
<Axis>
<AxisTag>DOWN</AxisTag>
<Flags>0x0</Flags>
<MinValue>0.0</MinValue>
<DefaultValue>0.0</DefaultValue>
<MaxValue>100.0</MaxValue>
<AxisNameID>259</AxisNameID>
</Axis>
<!-- TotallyNormal -->
<NamedInstance flags="0x0" subfamilyNameID="260">
<coord axis="LEFT" value="0.0"/>
<coord axis="RGHT" value="0.0"/>
<coord axis="UPPP" value="0.0"/>
<coord axis="DOWN" value="0.0"/>
</NamedInstance>
<!-- Right Up -->
<NamedInstance flags="0x0" subfamilyNameID="261">
<coord axis="LEFT" value="0.0"/>
<coord axis="RGHT" value="100.0"/>
<coord axis="UPPP" value="100.0"/>
<coord axis="DOWN" value="0.0"/>
</NamedInstance>
</fvar>
<gvar>
<version value="1"/>
<reserved value="0"/>
<glyphVariations glyph="a">
<tuple>
<coord axis="RGHT" value="1.0"/>
<delta pt="0" x="0" y="0"/>
<delta pt="1" x="0" y="0"/>
<delta pt="2" x="200" y="0"/>
<delta pt="3" x="200" y="0"/>
</tuple>
<tuple>
<coord axis="LEFT" value="1.0"/>
<delta pt="0" x="-200" y="0"/>
<delta pt="1" x="-200" y="0"/>
<delta pt="2" x="0" y="0"/>
<delta pt="3" x="0" y="0"/>
</tuple>
<tuple>
<coord axis="UPPP" value="1.0"/>
<delta pt="0" x="0" y="0"/>
<delta pt="1" x="0" y="200"/>
<delta pt="2" x="0" y="200"/>
<delta pt="3" x="0" y="0"/>
</tuple>
<tuple>
<coord axis="DOWN" value="1.0"/>
<delta pt="0" x="0" y="-200"/>
<delta pt="1" x="0" y="0"/>
<delta pt="2" x="0" y="0"/>
<delta pt="3" x="0" y="-200"/>
</tuple>
</glyphVariations>
</gvar>
<DSIG>
<!-- note that the Digital Signature will be invalid after recompilation! -->
<tableHeader flag="0x1" numSigs="1" version="1"/>
<SignatureRecord format="1">
-----BEGIN PKCS7-----
0000000100000000
-----END PKCS7-----
</SignatureRecord>
</DSIG>
</ttFont>

View File

@ -8,6 +8,8 @@ from fontTools.ttLib import TTFont
from fontTools.pens.ttGlyphPen import TTGlyphPen
from fontTools.pens.t2CharStringPen import T2CharStringPen
from fontTools.fontBuilder import FontBuilder
from fontTools.ttLib.tables.TupleVariation import TupleVariation
from fontTools.ttLib.tables._g_l_y_f import GlyphCoordinates
def getTestData(fileName, mode="r"):
@ -64,9 +66,9 @@ def test_build_ttf(tmpdir):
glyphTable = fb.font["glyf"]
for gn, advanceWidth in advanceWidths.items():
metrics[gn] = (advanceWidth, glyphTable[gn].xMin)
fb.setupMetrics("hmtx", metrics)
fb.setupHorizontalMetrics(metrics)
fb.setupHorizontalHeader()
fb.setupHorizontalHeader(ascent=824, descent=200)
fb.setupNameTable(nameStrings)
fb.setupOS2()
fb.setupPost()
@ -95,9 +97,9 @@ def test_build_otf(tmpdir):
metrics = {}
for gn, advanceWidth in advanceWidths.items():
metrics[gn] = (advanceWidth, 100) # XXX lsb from glyph
fb.setupMetrics("hmtx", metrics)
fb.setupHorizontalMetrics(metrics)
fb.setupHorizontalHeader()
fb.setupHorizontalHeader(ascent=824, descent=200)
fb.setupNameTable(nameStrings)
fb.setupOS2()
fb.setupPost()
@ -111,3 +113,80 @@ def test_build_otf(tmpdir):
testData = strip_VariableItems(f.read())
refData = strip_VariableItems(getTestData("test.otf.ttx"))
assert refData == testData
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"),
]
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"),
]
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