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("")) self.assertTrue("wght" in xml) self.assertTrue("wdth" in xml) self.assertEqual(2, xml.count("" in xml) self.assertTrue("" in xml) def test_fromXML(self): fvar = table__f_v_a_r() for name, attrs, content in parseXML( '' ' opsz' '' '' ' slnt' ' 0x123' '' '' ''): 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([ '', '', '', 'opsz', '0xABC', '-0.5', '1.3', '1.5', '256', '' ], xml_lines(writer)) def test_fromXML(self): axis = Axis() for name, attrs, content in parseXML( '' ' wght' ' 0x123ABC' ' 100' ' 400' ' 900' ' 256' ''): 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([ '', '', '', '' % ( inst.postscriptNameID, inst.subfamilyNameID), '', '', '' ], 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([ '', '', '' % inst.subfamilyNameID, '', '', '' ], xml_lines(writer)) def test_fromXML_withPostScriptName(self): inst = NamedInstance() for name, attrs, content in parseXML( '' ' ' ' ' ''): 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( '' ' ' ' ' ''): 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())