added support for cmap format 14 Unicode Variation Sequences
This commit is contained in:
parent
a4ca702733
commit
391f7a76c0
@ -364,9 +364,18 @@ class FontBuilder(object):
|
|||||||
"""Set the glyph order for the font."""
|
"""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, uvs=None, allowFallback=False):
|
||||||
"""Build the `cmap` table for the font. The `cmapping` argument should
|
"""Build the `cmap` table for the font. The `cmapping` argument should
|
||||||
be a dict mapping unicode code points as integers to glyph names.
|
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 = []
|
subTables = []
|
||||||
highestUnicode = max(cmapping)
|
highestUnicode = max(cmapping)
|
||||||
@ -390,6 +399,19 @@ class FontBuilder(object):
|
|||||||
subTable_0_3 = buildCmapSubTable(cmapping_3_1, format, 0, 3)
|
subTable_0_3 = buildCmapSubTable(cmapping_3_1, format, 0, 3)
|
||||||
subTables.append(subTable_0_3)
|
subTables.append(subTable_0_3)
|
||||||
|
|
||||||
|
if uvs is not None:
|
||||||
|
uvsDict = {}
|
||||||
|
for unicodeValue, variationSelector, glyphName in uvs:
|
||||||
|
if variationSelector not in uvsDict:
|
||||||
|
uvsDict[variationSelector] = []
|
||||||
|
if cmapping.get(unicodeValue) == glyphName:
|
||||||
|
# this is a default variation
|
||||||
|
glyphName = None
|
||||||
|
uvsDict[variationSelector].append((unicodeValue, glyphName))
|
||||||
|
uvsSubTable = buildCmapSubTable({}, 14, 0, 5)
|
||||||
|
uvsSubTable.uvsDict = uvsDict
|
||||||
|
subTables.append(uvsSubTable)
|
||||||
|
|
||||||
self.font["cmap"] = newTable("cmap")
|
self.font["cmap"] = newTable("cmap")
|
||||||
self.font["cmap"].tableVersion = 0
|
self.font["cmap"].tableVersion = 0
|
||||||
self.font["cmap"].tables = subTables
|
self.font["cmap"].tables = subTables
|
||||||
|
213
Tests/fontBuilder/data/test_uvs.ttf.ttx
Normal file
213
Tests/fontBuilder/data/test_uvs.ttf.ttx
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.35">
|
||||||
|
|
||||||
|
<GlyphOrder>
|
||||||
|
<!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
|
||||||
|
<GlyphID id="0" name=".notdef"/>
|
||||||
|
<GlyphID id="1" name="space"/>
|
||||||
|
<GlyphID id="2" name="zero"/>
|
||||||
|
<GlyphID id="3" name="zero.slash"/>
|
||||||
|
</GlyphOrder>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<!-- Most of this table will be recalculated by the compiler -->
|
||||||
|
<tableVersion value="1.0"/>
|
||||||
|
<fontRevision value="1.0"/>
|
||||||
|
<checkSumAdjustment value="0xc09562d2"/>
|
||||||
|
<magicNumber value="0x5f0f3cf5"/>
|
||||||
|
<flags value="00000000 00000011"/>
|
||||||
|
<unitsPerEm value="1024"/>
|
||||||
|
<created value="Wed Jan 9 13:45:25 2019"/>
|
||||||
|
<modified value="Wed Jan 9 13:45:25 2019"/>
|
||||||
|
<xMin value="0"/>
|
||||||
|
<yMin value="0"/>
|
||||||
|
<xMax value="0"/>
|
||||||
|
<yMax value="0"/>
|
||||||
|
<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="0"/>
|
||||||
|
<minRightSideBearing value="0"/>
|
||||||
|
<xMaxExtent value="0"/>
|
||||||
|
<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="0"/>
|
||||||
|
<maxContours value="0"/>
|
||||||
|
<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="32"/>
|
||||||
|
<usLastCharIndex value="48"/>
|
||||||
|
<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="space" width="600" lsb="0"/>
|
||||||
|
<mtx name="zero" width="600" lsb="0"/>
|
||||||
|
<mtx name="zero.slash" width="600" lsb="0"/>
|
||||||
|
</hmtx>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<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="space"/><!-- contains no outline data -->
|
||||||
|
|
||||||
|
<TTGlyph name="zero"/><!-- contains no outline data -->
|
||||||
|
|
||||||
|
<TTGlyph name="zero.slash"/><!-- contains no outline data -->
|
||||||
|
|
||||||
|
</glyf>
|
||||||
|
|
||||||
|
<name>
|
||||||
|
<namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
|
||||||
|
UVSTestFont
|
||||||
|
</namerecord>
|
||||||
|
<namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
|
||||||
|
Regular
|
||||||
|
</namerecord>
|
||||||
|
<namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
|
||||||
|
UVSTestFont-Regular
|
||||||
|
</namerecord>
|
||||||
|
<namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
|
||||||
|
UVSTestFont
|
||||||
|
</namerecord>
|
||||||
|
<namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
|
||||||
|
Regular
|
||||||
|
</namerecord>
|
||||||
|
<namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
|
||||||
|
UVSTestFont-Regular
|
||||||
|
</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 -->
|
||||||
|
<psName name="zero.slash"/>
|
||||||
|
</extraNames>
|
||||||
|
</post>
|
||||||
|
|
||||||
|
</ttFont>
|
@ -244,3 +244,34 @@ def test_setupNameTable_no_windows():
|
|||||||
|
|
||||||
assert all(n for n in fb.font["name"].names if n.platformID == 1)
|
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)
|
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, "zero"), # 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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user