265 lines
10 KiB
Python
265 lines
10 KiB
Python
from __future__ import print_function, division, absolute_import, unicode_literals
|
|
from fontTools.misc.py23 import *
|
|
from fontTools.misc.testTools import parseXML
|
|
from fontTools.misc.textTools import deHexStr
|
|
from fontTools.misc.xmlWriter import XMLWriter
|
|
from fontTools.ttLib import TTLibError
|
|
from fontTools.ttLib.tables._f_v_a_r import table__f_v_a_r, Axis, NamedInstance
|
|
from fontTools.ttLib.tables._n_a_m_e import table__n_a_m_e, NameRecord
|
|
import unittest
|
|
|
|
|
|
|
|
FVAR_DATA = deHexStr(
|
|
"00 01 00 00 00 10 00 02 00 02 00 14 00 02 00 0C "
|
|
"77 67 68 74 00 64 00 00 01 90 00 00 03 84 00 00 00 00 01 01 "
|
|
"77 64 74 68 00 32 00 00 00 64 00 00 00 c8 00 00 00 00 01 02 "
|
|
"01 03 00 00 01 2c 00 00 00 64 00 00 "
|
|
"01 04 00 00 01 2c 00 00 00 4b 00 00")
|
|
|
|
FVAR_AXIS_DATA = deHexStr(
|
|
"6F 70 73 7a ff ff 80 00 00 01 4c cd 00 01 80 00 00 00 01 59")
|
|
|
|
FVAR_INSTANCE_DATA_WITHOUT_PSNAME = deHexStr(
|
|
"01 59 00 00 00 00 b3 33 00 00 80 00")
|
|
|
|
FVAR_INSTANCE_DATA_WITH_PSNAME = (
|
|
FVAR_INSTANCE_DATA_WITHOUT_PSNAME + deHexStr("02 34"))
|
|
|
|
|
|
def xml_lines(writer):
|
|
content = writer.file.getvalue().decode("utf-8")
|
|
return [line.strip() for line in content.splitlines()][1:]
|
|
|
|
|
|
def AddName(font, name):
|
|
nameTable = font.get("name")
|
|
if nameTable is None:
|
|
nameTable = font["name"] = table__n_a_m_e()
|
|
nameTable.names = []
|
|
namerec = NameRecord()
|
|
namerec.nameID = 1 + max([n.nameID for n in nameTable.names] + [256])
|
|
namerec.string = name.encode('mac_roman')
|
|
namerec.platformID, namerec.platEncID, namerec.langID = (1, 0, 0)
|
|
nameTable.names.append(namerec)
|
|
return namerec
|
|
|
|
|
|
def MakeFont():
|
|
axes = [("wght", "Weight", 100, 400, 900), ("wdth", "Width", 50, 100, 200)]
|
|
instances = [("Light", 300, 100), ("Light Condensed", 300, 75)]
|
|
fvarTable = table__f_v_a_r()
|
|
font = {"fvar": fvarTable}
|
|
for tag, name, minValue, defaultValue, maxValue in axes:
|
|
axis = Axis()
|
|
axis.axisTag = tag
|
|
axis.defaultValue = defaultValue
|
|
axis.minValue, axis.maxValue = minValue, maxValue
|
|
axis.axisNameID = AddName(font, name).nameID
|
|
fvarTable.axes.append(axis)
|
|
for name, weight, width in instances:
|
|
inst = NamedInstance()
|
|
inst.subfamilyNameID = AddName(font, name).nameID
|
|
inst.coordinates = {"wght": weight, "wdth": width}
|
|
fvarTable.instances.append(inst)
|
|
return font
|
|
|
|
|
|
class FontVariationTableTest(unittest.TestCase):
|
|
def test_compile(self):
|
|
font = MakeFont()
|
|
h = font["fvar"].compile(font)
|
|
self.assertEqual(FVAR_DATA, font["fvar"].compile(font))
|
|
|
|
def test_decompile(self):
|
|
fvar = table__f_v_a_r()
|
|
fvar.decompile(FVAR_DATA, ttFont={"fvar": fvar})
|
|
self.assertEqual(["wght", "wdth"], [a.axisTag for a in fvar.axes])
|
|
self.assertEqual([259, 260], [i.subfamilyNameID for i in fvar.instances])
|
|
|
|
def test_toXML(self):
|
|
font = MakeFont()
|
|
writer = XMLWriter(BytesIO())
|
|
font["fvar"].toXML(writer, font)
|
|
xml = writer.file.getvalue().decode("utf-8")
|
|
self.assertEqual(2, xml.count("<Axis>"))
|
|
self.assertTrue("<AxisTag>wght</AxisTag>" in xml)
|
|
self.assertTrue("<AxisTag>wdth</AxisTag>" in xml)
|
|
self.assertEqual(2, xml.count("<NamedInstance "))
|
|
self.assertTrue("<!-- Light -->" in xml)
|
|
self.assertTrue("<!-- Light Condensed -->" in xml)
|
|
|
|
def test_fromXML(self):
|
|
fvar = table__f_v_a_r()
|
|
for name, attrs, content in parseXML(
|
|
'<Axis>'
|
|
' <AxisTag>opsz</AxisTag>'
|
|
'</Axis>'
|
|
'<Axis>'
|
|
' <AxisTag>slnt</AxisTag>'
|
|
' <Flags>0x123</Flags>'
|
|
'</Axis>'
|
|
'<NamedInstance subfamilyNameID="765"/>'
|
|
'<NamedInstance subfamilyNameID="234"/>'):
|
|
fvar.fromXML(name, attrs, content, ttFont=None)
|
|
self.assertEqual(["opsz", "slnt"], [a.axisTag for a in fvar.axes])
|
|
self.assertEqual([0, 0x123], [a.flags for a in fvar.axes])
|
|
self.assertEqual([765, 234], [i.subfamilyNameID for i in fvar.instances])
|
|
|
|
|
|
class AxisTest(unittest.TestCase):
|
|
def test_compile(self):
|
|
axis = Axis()
|
|
axis.axisTag, axis.axisNameID = ('opsz', 345)
|
|
axis.minValue, axis.defaultValue, axis.maxValue = (-0.5, 1.3, 1.5)
|
|
self.assertEqual(FVAR_AXIS_DATA, axis.compile())
|
|
|
|
def test_decompile(self):
|
|
axis = Axis()
|
|
axis.decompile(FVAR_AXIS_DATA)
|
|
self.assertEqual("opsz", axis.axisTag)
|
|
self.assertEqual(345, axis.axisNameID)
|
|
self.assertEqual(-0.5, axis.minValue)
|
|
self.assertEqual(1.3, axis.defaultValue)
|
|
self.assertEqual(1.5, axis.maxValue)
|
|
|
|
def test_toXML(self):
|
|
font = MakeFont()
|
|
axis = Axis()
|
|
axis.decompile(FVAR_AXIS_DATA)
|
|
AddName(font, "Optical Size").nameID = 256
|
|
axis.axisNameID = 256
|
|
axis.flags = 0xABC
|
|
writer = XMLWriter(BytesIO())
|
|
axis.toXML(writer, font)
|
|
self.assertEqual([
|
|
'',
|
|
'<!-- Optical Size -->',
|
|
'<Axis>',
|
|
'<AxisTag>opsz</AxisTag>',
|
|
'<Flags>0xABC</Flags>',
|
|
'<MinValue>-0.5</MinValue>',
|
|
'<DefaultValue>1.3</DefaultValue>',
|
|
'<MaxValue>1.5</MaxValue>',
|
|
'<AxisNameID>256</AxisNameID>',
|
|
'</Axis>'
|
|
], xml_lines(writer))
|
|
|
|
def test_fromXML(self):
|
|
axis = Axis()
|
|
for name, attrs, content in parseXML(
|
|
'<Axis>'
|
|
' <AxisTag>wght</AxisTag>'
|
|
' <Flags>0x123ABC</Flags>'
|
|
' <MinValue>100</MinValue>'
|
|
' <DefaultValue>400</DefaultValue>'
|
|
' <MaxValue>900</MaxValue>'
|
|
' <AxisNameID>256</AxisNameID>'
|
|
'</Axis>'):
|
|
axis.fromXML(name, attrs, content, ttFont=None)
|
|
self.assertEqual("wght", axis.axisTag)
|
|
self.assertEqual(0x123ABC, axis.flags)
|
|
self.assertEqual(100, axis.minValue)
|
|
self.assertEqual(400, axis.defaultValue)
|
|
self.assertEqual(900, axis.maxValue)
|
|
self.assertEqual(256, axis.axisNameID)
|
|
|
|
|
|
class NamedInstanceTest(unittest.TestCase):
|
|
def test_compile_withPostScriptName(self):
|
|
inst = NamedInstance()
|
|
inst.subfamilyNameID = 345
|
|
inst.postscriptNameID = 564
|
|
inst.coordinates = {"wght": 0.7, "wdth": 0.5}
|
|
self.assertEqual(FVAR_INSTANCE_DATA_WITH_PSNAME,
|
|
inst.compile(["wght", "wdth"], True))
|
|
|
|
def test_compile_withoutPostScriptName(self):
|
|
inst = NamedInstance()
|
|
inst.subfamilyNameID = 345
|
|
inst.postscriptNameID = 564
|
|
inst.coordinates = {"wght": 0.7, "wdth": 0.5}
|
|
self.assertEqual(FVAR_INSTANCE_DATA_WITHOUT_PSNAME,
|
|
inst.compile(["wght", "wdth"], False))
|
|
|
|
def test_decompile_withPostScriptName(self):
|
|
inst = NamedInstance()
|
|
inst.decompile(FVAR_INSTANCE_DATA_WITH_PSNAME, ["wght", "wdth"])
|
|
self.assertEqual(564, inst.postscriptNameID)
|
|
self.assertEqual(345, inst.subfamilyNameID)
|
|
self.assertEqual({"wght": 0.7, "wdth": 0.5}, inst.coordinates)
|
|
|
|
def test_decompile_withoutPostScriptName(self):
|
|
inst = NamedInstance()
|
|
inst.decompile(FVAR_INSTANCE_DATA_WITHOUT_PSNAME, ["wght", "wdth"])
|
|
self.assertEqual(0xFFFF, inst.postscriptNameID)
|
|
self.assertEqual(345, inst.subfamilyNameID)
|
|
self.assertEqual({"wght": 0.7, "wdth": 0.5}, inst.coordinates)
|
|
|
|
def test_toXML_withPostScriptName(self):
|
|
font = MakeFont()
|
|
inst = NamedInstance()
|
|
inst.flags = 0xE9
|
|
inst.subfamilyNameID = AddName(font, "Light Condensed").nameID
|
|
inst.postscriptNameID = AddName(font, "Test-LightCondensed").nameID
|
|
inst.coordinates = {"wght": 0.7, "wdth": 0.5}
|
|
writer = XMLWriter(BytesIO())
|
|
inst.toXML(writer, font)
|
|
self.assertEqual([
|
|
'',
|
|
'<!-- Light Condensed -->',
|
|
'<!-- PostScript: Test-LightCondensed -->',
|
|
'<NamedInstance flags="0xE9" postscriptNameID="%s" subfamilyNameID="%s">' % (
|
|
inst.postscriptNameID, inst.subfamilyNameID),
|
|
'<coord axis="wght" value="0.7"/>',
|
|
'<coord axis="wdth" value="0.5"/>',
|
|
'</NamedInstance>'
|
|
], xml_lines(writer))
|
|
|
|
def test_toXML_withoutPostScriptName(self):
|
|
font = MakeFont()
|
|
inst = NamedInstance()
|
|
inst.flags = 0xABC
|
|
inst.subfamilyNameID = AddName(font, "Light Condensed").nameID
|
|
inst.coordinates = {"wght": 0.7, "wdth": 0.5}
|
|
writer = XMLWriter(BytesIO())
|
|
inst.toXML(writer, font)
|
|
self.assertEqual([
|
|
'',
|
|
'<!-- Light Condensed -->',
|
|
'<NamedInstance flags="0xABC" subfamilyNameID="%s">' %
|
|
inst.subfamilyNameID,
|
|
'<coord axis="wght" value="0.7"/>',
|
|
'<coord axis="wdth" value="0.5"/>',
|
|
'</NamedInstance>'
|
|
], xml_lines(writer))
|
|
|
|
def test_fromXML_withPostScriptName(self):
|
|
inst = NamedInstance()
|
|
for name, attrs, content in parseXML(
|
|
'<NamedInstance flags="0x0" postscriptNameID="257" subfamilyNameID="345">'
|
|
' <coord axis="wght" value="0.7"/>'
|
|
' <coord axis="wdth" value="0.5"/>'
|
|
'</NamedInstance>'):
|
|
inst.fromXML(name, attrs, content, ttFont=MakeFont())
|
|
self.assertEqual(257, inst.postscriptNameID)
|
|
self.assertEqual(345, inst.subfamilyNameID)
|
|
self.assertEqual({"wght": 0.7, "wdth": 0.5}, inst.coordinates)
|
|
|
|
def test_fromXML_withoutPostScriptName(self):
|
|
inst = NamedInstance()
|
|
for name, attrs, content in parseXML(
|
|
'<NamedInstance flags="0x123ABC" subfamilyNameID="345">'
|
|
' <coord axis="wght" value="0.7"/>'
|
|
' <coord axis="wdth" value="0.5"/>'
|
|
'</NamedInstance>'):
|
|
inst.fromXML(name, attrs, content, ttFont=MakeFont())
|
|
self.assertEqual(0x123ABC, inst.flags)
|
|
self.assertEqual(345, inst.subfamilyNameID)
|
|
self.assertEqual({"wght": 0.7, "wdth": 0.5}, inst.coordinates)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import sys
|
|
sys.exit(unittest.main())
|