Merge pull request #1434 from justvanrossum/fontbuilder-uvs

[fontBuilder] adding support for cmap format 14 Unicode Variation Sequences
This commit is contained in:
Just van Rossum 2019-01-09 16:27:21 +01:00 committed by GitHub
commit b920b3b36f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 84 additions and 3 deletions

View File

@ -364,9 +364,18 @@ class FontBuilder(object):
"""Set the glyph order for the font."""
self.font.setGlyphOrder(glyphOrder)
def setupCharacterMap(self, cmapping, allowFallback=False):
def setupCharacterMap(self, cmapping, uvs=None, 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.
The `uvs` argument, when passed, must be a list of tuples, describing
Unicode Variation Sequences. These tuples have three elements:
(unicodeValue, variationSelector, glyphName)
`unicodeValue` and `variationSelector` are integer code points.
`glyphName` may be None, to indicate this is the default variation.
Text processors will then use the cmap to find the glyph name.
Each Unicode Variation Sequence should be an officially supported
sequence, but this is not policed.
"""
subTables = []
highestUnicode = max(cmapping)
@ -390,6 +399,19 @@ class FontBuilder(object):
subTable_0_3 = buildCmapSubTable(cmapping_3_1, format, 0, 3)
subTables.append(subTable_0_3)
if uvs is not None:
uvsDict = {}
for unicodeValue, variationSelector, glyphName in uvs:
if cmapping.get(unicodeValue) == glyphName:
# this is a default variation
glyphName = None
if variationSelector not in uvsDict:
uvsDict[variationSelector] = []
uvsDict[variationSelector].append((unicodeValue, glyphName))
uvsSubTable = buildCmapSubTable({}, 14, 0, 5)
uvsSubTable.uvsDict = uvsDict
subTables.append(uvsSubTable)
self.font["cmap"] = newTable("cmap")
self.font["cmap"].tableVersion = 0
self.font["cmap"].tables = subTables

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.35">
<cmap>
<tableVersion version="0"/>
<cmap_format_4 platformID="0" platEncID="3" language="0">
<map code="0x20" name="space"/><!-- SPACE -->
<map code="0x30" name="zero"/><!-- DIGIT ZERO -->
</cmap_format_4>
<cmap_format_14 platformID="0" platEncID="5" format="14" length="49" numVarSelectorRecords="2">
<map uvs="0xfe00" uv="0x30" name="zero.slash"/>
<map uvs="0xfe01" uv="0x30" name="None"/>
</cmap_format_14>
<cmap_format_4 platformID="3" platEncID="1" language="0">
<map code="0x20" name="space"/><!-- SPACE -->
<map code="0x30" name="zero"/><!-- DIGIT ZERO -->
</cmap_format_4>
</cmap>
</ttFont>

View File

@ -53,9 +53,9 @@ def _setupFontBuilder(isTTF, unitsPerEm=1024):
return fb, advanceWidths, nameStrings
def _verifyOutput(outPath):
def _verifyOutput(outPath, tables=None):
f = TTFont(outPath)
f.saveXML(outPath + ".ttx")
f.saveXML(outPath + ".ttx", tables=tables)
with open(outPath + ".ttx") as f:
testData = strip_VariableItems(f.read())
refData = strip_VariableItems(getTestData(os.path.basename(outPath) + ".ttx"))
@ -244,3 +244,42 @@ def test_setupNameTable_no_windows():
assert all(n for n in fb.font["name"].names if n.platformID == 1)
assert not any(n for n in fb.font["name"].names if n.platformID == 3)
def test_unicodeVariationSequences(tmpdir):
familyName = "UVSTestFont"
styleName = "Regular"
nameStrings = dict(familyName=familyName, styleName=styleName)
nameStrings['psName'] = familyName + "-" + styleName
glyphOrder = [".notdef", "space", "zero", "zero.slash"]
cmap = {ord(" "): "space", ord("0"): "zero"}
uvs = [
(0x0030, 0xFE00, "zero.slash"),
(0x0030, 0xFE01, None), # not an official sequence, just testing
]
metrics = {gn: (600, 0) for gn in glyphOrder}
pen = TTGlyphPen(None)
glyph = pen.glyph() # empty placeholder
glyphs = {gn: glyph for gn in glyphOrder}
fb = FontBuilder(1024, isTTF=True)
fb.setupGlyphOrder(glyphOrder)
fb.setupCharacterMap(cmap, uvs)
fb.setupGlyf(glyphs)
fb.setupHorizontalMetrics(metrics)
fb.setupHorizontalHeader(ascent=824, descent=200)
fb.setupNameTable(nameStrings)
fb.setupOS2()
fb.setupPost()
outPath = os.path.join(str(tmpdir), "test_uvs.ttf")
fb.save(outPath)
_verifyOutput(outPath, tables=["cmap"])
uvs = [
(0x0030, 0xFE00, "zero.slash"),
(0x0030, 0xFE01, "zero"), # should result in the exact same subtable data, due to cmap[0x0030] == "zero"
]
fb.setupCharacterMap(cmap, uvs)
fb.save(outPath)
_verifyOutput(outPath, tables=["cmap"])