Merge branch 'master' into addmultilingualnames-mac
This commit is contained in:
commit
3b7ac3dd78
@ -17,7 +17,7 @@ that works:
|
|||||||
fb.setupGlyphOrder(...)
|
fb.setupGlyphOrder(...)
|
||||||
fb.setupCharacterMap(...)
|
fb.setupCharacterMap(...)
|
||||||
fb.setupGlyf(...) --or-- fb.setupCFF(...)
|
fb.setupGlyf(...) --or-- fb.setupCFF(...)
|
||||||
fb.setupMetrics("hmtx", ...)
|
fb.setupHorizontalMetrics(...)
|
||||||
fb.setupHorizontalHeader()
|
fb.setupHorizontalHeader()
|
||||||
fb.setupNameTable(...)
|
fb.setupNameTable(...)
|
||||||
fb.setupOS2()
|
fb.setupOS2()
|
||||||
@ -59,9 +59,9 @@ metrics = {}
|
|||||||
glyphTable = fb.font["glyf"]
|
glyphTable = fb.font["glyf"]
|
||||||
for gn, advanceWidth in advanceWidths.items():
|
for gn, advanceWidth in advanceWidths.items():
|
||||||
metrics[gn] = (advanceWidth, glyphTable[gn].xMin)
|
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.setupNameTable(nameStrings)
|
||||||
fb.setupOS2()
|
fb.setupOS2()
|
||||||
fb.setupPost()
|
fb.setupPost()
|
||||||
@ -103,9 +103,9 @@ fb.setupCFF(nameStrings['psName'], {"FullName": nameStrings['psName']}, charStri
|
|||||||
metrics = {}
|
metrics = {}
|
||||||
for gn, advanceWidth in advanceWidths.items():
|
for gn, advanceWidth in advanceWidths.items():
|
||||||
metrics[gn] = (advanceWidth, 100) # XXX lsb from glyph
|
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.setupNameTable(nameStrings)
|
||||||
fb.setupOS2()
|
fb.setupOS2()
|
||||||
fb.setupPost()
|
fb.setupPost()
|
||||||
@ -217,20 +217,37 @@ _vheaDefaults = dict(
|
|||||||
)
|
)
|
||||||
|
|
||||||
_nameIDs = dict(
|
_nameIDs = dict(
|
||||||
copyright = 0,
|
copyright = 0,
|
||||||
familyName = 1,
|
familyName = 1,
|
||||||
styleName = 2,
|
styleName = 2,
|
||||||
identifier = 3,
|
uniqueFontIdentifier = 3,
|
||||||
fullName = 4,
|
fullName = 4,
|
||||||
version = 5,
|
version = 5,
|
||||||
psName = 6,
|
psName = 6,
|
||||||
trademark = 7,
|
trademark = 7,
|
||||||
manufacturer = 8,
|
manufacturer = 8,
|
||||||
typographicFamily = 16,
|
designer = 9,
|
||||||
typographicSubfamily = 17,
|
description = 10,
|
||||||
# XXX this needs to be extended with legal things, etc.
|
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(
|
_panoseDefaults = dict(
|
||||||
bFamilyType = 0,
|
bFamilyType = 0,
|
||||||
bSerifStyle = 0,
|
bSerifStyle = 0,
|
||||||
@ -312,8 +329,11 @@ class FontBuilder(object):
|
|||||||
self.font = font
|
self.font = font
|
||||||
self.isTTF = "glyf" in font
|
self.isTTF = "glyf" in font
|
||||||
|
|
||||||
def save(self, path):
|
def save(self, file):
|
||||||
self.font.save(path)
|
"""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):
|
def _initTableWithValues(self, tableTag, defaults, values):
|
||||||
table = self.font[tableTag] = newTable(tableTag)
|
table = self.font[tableTag] = newTable(tableTag)
|
||||||
@ -329,15 +349,25 @@ class FontBuilder(object):
|
|||||||
setattr(table, k, v)
|
setattr(table, k, v)
|
||||||
|
|
||||||
def setupHead(self, **values):
|
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)
|
self._initTableWithValues("head", _headDefaults, values)
|
||||||
|
|
||||||
def updateHead(self, **values):
|
def updateHead(self, **values):
|
||||||
|
"""Update the head table with the fields and values passed as
|
||||||
|
keyword arguments.
|
||||||
|
"""
|
||||||
self._updateTableWithValues("head", values)
|
self._updateTableWithValues("head", values)
|
||||||
|
|
||||||
def setupGlyphOrder(self, glyphOrder):
|
def setupGlyphOrder(self, glyphOrder):
|
||||||
|
"""Set the glyph order for the font."""
|
||||||
self.font.setGlyphOrder(glyphOrder)
|
self.font.setGlyphOrder(glyphOrder)
|
||||||
|
|
||||||
def setupCharacterMap(self, cmapping, allowFallback=False):
|
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 = []
|
subTables = []
|
||||||
highestUnicode = max(cmapping)
|
highestUnicode = max(cmapping)
|
||||||
if highestUnicode > 0xffff:
|
if highestUnicode > 0xffff:
|
||||||
@ -365,6 +395,39 @@ class FontBuilder(object):
|
|||||||
self.font["cmap"].tables = subTables
|
self.font["cmap"].tables = subTables
|
||||||
|
|
||||||
def setupNameTable(self, nameStrings):
|
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 = self.font["name"] = newTable("name")
|
||||||
nameTable.names = []
|
nameTable.names = []
|
||||||
|
|
||||||
@ -378,6 +441,9 @@ class FontBuilder(object):
|
|||||||
nameTable.addMultilingualName(nameValue, ttFont=self.font, nameID=nameID)
|
nameTable.addMultilingualName(nameValue, ttFont=self.font, nameID=nameID)
|
||||||
|
|
||||||
def setupOS2(self, **values):
|
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:
|
if "xAvgCharWidth" not in values:
|
||||||
gs = self.font.getGlyphSet()
|
gs = self.font.getGlyphSet()
|
||||||
widths = [gs[glyphName].width for glyphName in gs.keys() if gs[glyphName].width > 0]
|
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
|
self.font["CFF "].cff = fontSet
|
||||||
|
|
||||||
def setupGlyf(self, glyphs, calcGlyphBounds=True):
|
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
|
assert self.isTTF
|
||||||
self.font["loca"] = newTable("loca")
|
self.font["loca"] = newTable("loca")
|
||||||
self.font["glyf"] = newTable("glyf")
|
self.font["glyf"] = newTable("glyf")
|
||||||
@ -445,11 +519,31 @@ class FontBuilder(object):
|
|||||||
gvar.variations = variations
|
gvar.variations = variations
|
||||||
|
|
||||||
def calcGlyphBounds(self):
|
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"]
|
glyphTable = self.font["glyf"]
|
||||||
for glyph in glyphTable.glyphs.values():
|
for glyph in glyphTable.glyphs.values():
|
||||||
glyph.recalcBounds(glyphTable)
|
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):
|
def setupMetrics(self, tableTag, metrics):
|
||||||
|
"""See `setupHorizontalMetrics()` and `setupVerticalMetrics()`."""
|
||||||
assert tableTag in ("hmtx", "vmtx")
|
assert tableTag in ("hmtx", "vmtx")
|
||||||
mtxTable = self.font[tableTag] = newTable(tableTag)
|
mtxTable = self.font[tableTag] = newTable(tableTag)
|
||||||
roundedMetrics = {}
|
roundedMetrics = {}
|
||||||
@ -459,12 +553,25 @@ class FontBuilder(object):
|
|||||||
mtxTable.metrics = roundedMetrics
|
mtxTable.metrics = roundedMetrics
|
||||||
|
|
||||||
def setupHorizontalHeader(self, **values):
|
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)
|
self._initTableWithValues("hhea", _hheaDefaults, values)
|
||||||
|
|
||||||
def setupVerticalHeader(self, **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)
|
self._initTableWithValues("vhea", _vheaDefaults, values)
|
||||||
|
|
||||||
def setupVerticalOrigins(self, verticalOrigins, defaultVerticalOrigin=None):
|
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:
|
if defaultVerticalOrigin is None:
|
||||||
# find the most frequent vorg value
|
# find the most frequent vorg value
|
||||||
bag = {}
|
bag = {}
|
||||||
@ -483,6 +590,9 @@ class FontBuilder(object):
|
|||||||
vorgTable[gn] = verticalOrigins[gn]
|
vorgTable[gn] = verticalOrigins[gn]
|
||||||
|
|
||||||
def setupPost(self, keepGlyphNames=True, **values):
|
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)
|
postTable = self._initTableWithValues("post", _postDefaults, values)
|
||||||
if self.isTTF and keepGlyphNames:
|
if self.isTTF and keepGlyphNames:
|
||||||
postTable.formatType = 2.0
|
postTable.formatType = 2.0
|
||||||
@ -492,6 +602,9 @@ class FontBuilder(object):
|
|||||||
postTable.formatType = 3.0
|
postTable.formatType = 3.0
|
||||||
|
|
||||||
def setupMaxp(self):
|
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:
|
if self.isTTF:
|
||||||
defaults = _maxpDefaultsTTF
|
defaults = _maxpDefaultsTTF
|
||||||
else:
|
else:
|
||||||
@ -522,6 +635,16 @@ class FontBuilder(object):
|
|||||||
self._initTableWithValues("DSIG", {}, values)
|
self._initTableWithValues("DSIG", {}, values)
|
||||||
|
|
||||||
def addOpenTypeFeatures(self, features, filename=None, tables=None):
|
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
|
from .feaLib.builder import addOpenTypeFeaturesFromString
|
||||||
addOpenTypeFeaturesFromString(self.font, features, filename=filename, tables=tables)
|
addOpenTypeFeaturesFromString(self.font, features, filename=filename, tables=tables)
|
||||||
|
|
||||||
|
@ -32,8 +32,8 @@
|
|||||||
|
|
||||||
<hhea>
|
<hhea>
|
||||||
<tableVersion value="0x00010000"/>
|
<tableVersion value="0x00010000"/>
|
||||||
<ascent value="0"/>
|
<ascent value="824"/>
|
||||||
<descent value="0"/>
|
<descent value="200"/>
|
||||||
<lineGap value="0"/>
|
<lineGap value="0"/>
|
||||||
<advanceWidthMax value="600"/>
|
<advanceWidthMax value="600"/>
|
||||||
<minLeftSideBearing value="100"/>
|
<minLeftSideBearing value="100"/>
|
||||||
|
@ -32,8 +32,8 @@
|
|||||||
|
|
||||||
<hhea>
|
<hhea>
|
||||||
<tableVersion value="0x00010000"/>
|
<tableVersion value="0x00010000"/>
|
||||||
<ascent value="0"/>
|
<ascent value="824"/>
|
||||||
<descent value="0"/>
|
<descent value="200"/>
|
||||||
<lineGap value="0"/>
|
<lineGap value="0"/>
|
||||||
<advanceWidthMax value="600"/>
|
<advanceWidthMax value="600"/>
|
||||||
<minLeftSideBearing value="100"/>
|
<minLeftSideBearing value="100"/>
|
||||||
|
361
Tests/fontBuilder/data/test_var.ttf.ttx
Normal file
361
Tests/fontBuilder/data/test_var.ttf.ttx
Normal 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>
|
@ -8,6 +8,8 @@ from fontTools.ttLib import TTFont
|
|||||||
from fontTools.pens.ttGlyphPen import TTGlyphPen
|
from fontTools.pens.ttGlyphPen import TTGlyphPen
|
||||||
from fontTools.pens.t2CharStringPen import T2CharStringPen
|
from fontTools.pens.t2CharStringPen import T2CharStringPen
|
||||||
from fontTools.fontBuilder import FontBuilder
|
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"):
|
def getTestData(fileName, mode="r"):
|
||||||
@ -64,9 +66,9 @@ def test_build_ttf(tmpdir):
|
|||||||
glyphTable = fb.font["glyf"]
|
glyphTable = fb.font["glyf"]
|
||||||
for gn, advanceWidth in advanceWidths.items():
|
for gn, advanceWidth in advanceWidths.items():
|
||||||
metrics[gn] = (advanceWidth, glyphTable[gn].xMin)
|
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.setupNameTable(nameStrings)
|
||||||
fb.setupOS2()
|
fb.setupOS2()
|
||||||
fb.setupPost()
|
fb.setupPost()
|
||||||
@ -95,9 +97,9 @@ def test_build_otf(tmpdir):
|
|||||||
metrics = {}
|
metrics = {}
|
||||||
for gn, advanceWidth in advanceWidths.items():
|
for gn, advanceWidth in advanceWidths.items():
|
||||||
metrics[gn] = (advanceWidth, 100) # XXX lsb from glyph
|
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.setupNameTable(nameStrings)
|
||||||
fb.setupOS2()
|
fb.setupOS2()
|
||||||
fb.setupPost()
|
fb.setupPost()
|
||||||
@ -111,3 +113,80 @@ def test_build_otf(tmpdir):
|
|||||||
testData = strip_VariableItems(f.read())
|
testData = strip_VariableItems(f.read())
|
||||||
refData = strip_VariableItems(getTestData("test.otf.ttx"))
|
refData = strip_VariableItems(getTestData("test.otf.ttx"))
|
||||||
assert refData == testData
|
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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user