from fontTools.misc.testTools import parseXML, getXML from fontTools.misc.textTools import deHexStr from fontTools.ttLib import TTFont, TTLibError from fontTools.ttLib.tables._t_r_a_k import * from fontTools.ttLib.tables._n_a_m_e import table__n_a_m_e, NameRecord import unittest # /Library/Fonts/Osaka.ttf from OSX has trak table with both horiz and vertData OSAKA_TRAK_TABLE_DATA = deHexStr( "00 01 00 00 00 00 00 0c 00 40 00 00 00 03 00 02 00 00 00 2c ff ff " "00 00 01 06 00 34 00 00 00 00 01 07 00 38 00 01 00 00 01 08 00 3c " "00 0c 00 00 00 18 00 00 ff f4 ff f4 00 00 00 00 00 0c 00 0c 00 03 " "00 02 00 00 00 60 ff ff 00 00 01 09 00 68 00 00 00 00 01 0a 00 6c " "00 01 00 00 01 0b 00 70 00 0c 00 00 00 18 00 00 ff f4 ff f4 00 00 " "00 00 00 0c 00 0c" ) # decompiled horizData and vertData entries from Osaka.ttf OSAKA_HORIZ_TRACK_ENTRIES = { -1.0: TrackTableEntry({24.0: -12, 12.0: -12}, nameIndex=262), 0.0: TrackTableEntry({24.0: 0, 12.0: 0}, nameIndex=263), 1.0: TrackTableEntry({24.0: 12, 12.0: 12}, nameIndex=264), } OSAKA_VERT_TRACK_ENTRIES = { -1.0: TrackTableEntry({24.0: -12, 12.0: -12}, nameIndex=265), 0.0: TrackTableEntry({24.0: 0, 12.0: 0}, nameIndex=266), 1.0: TrackTableEntry({24.0: 12, 12.0: 12}, nameIndex=267), } OSAKA_TRAK_TABLE_XML = [ '', '', "", " ", ' ', " ", ' ', ' ', " ", ' ', " ", ' ', ' ', " ", ' ', " ", ' ', ' ', " ", "", "", " ", ' ', " ", ' ', ' ', " ", ' ', " ", ' ', ' ', " ", ' ', " ", ' ', ' ', " ", "", ] # made-up table containing only vertData (no horizData) OSAKA_VERT_ONLY_TRAK_TABLE_DATA = deHexStr( "00 01 00 00 00 00 00 00 00 0c 00 00 00 03 00 02 00 00 00 2c ff ff " "00 00 01 09 00 34 00 00 00 00 01 0a 00 38 00 01 00 00 01 0b 00 3c " "00 0c 00 00 00 18 00 00 ff f4 ff f4 00 00 00 00 00 0c 00 0c" ) OSAKA_VERT_ONLY_TRAK_TABLE_XML = [ '', '', "", " ", "", "", " ", ' ', " ", ' ', ' ', " ", ' ', " ", ' ', ' ', " ", ' ', " ", ' ', ' ', " ", "", ] # also /Library/Fonts/Skia.ttf contains a trak table with horizData SKIA_TRAK_TABLE_DATA = deHexStr( "00 01 00 00 00 00 00 0c 00 00 00 00 00 03 00 05 00 00 00 2c ff ff " "00 00 01 13 00 40 00 00 00 00 01 2f 00 4a 00 01 00 00 01 14 00 54 " "00 09 00 00 00 0a 00 00 00 0c 00 00 00 12 00 00 00 13 00 00 ff f6 " "ff e2 ff c4 ff c1 ff c1 00 0f 00 00 ff fb ff e7 ff e7 00 8c 00 82 " "00 7d 00 73 00 73" ) SKIA_TRACK_ENTRIES = { -1.0: TrackTableEntry( {9.0: -10, 10.0: -30, 19.0: -63, 12.0: -60, 18.0: -63}, nameIndex=275 ), 0.0: TrackTableEntry( {9.0: 15, 10.0: 0, 19.0: -25, 12.0: -5, 18.0: -25}, nameIndex=303 ), 1.0: TrackTableEntry( {9.0: 140, 10.0: 130, 19.0: 115, 12.0: 125, 18.0: 115}, nameIndex=276 ), } SKIA_TRAK_TABLE_XML = [ '', '', "", " ", ' ', " ", ' ', ' ', ' ', ' ', ' ', " ", ' ', " ", ' ', ' ', ' ', ' ', ' ', " ", ' ', " ", ' ', ' ', ' ', ' ', ' ', " ", "", "", " ", "", ] class TrackingTableTest(unittest.TestCase): def __init__(self, methodName): unittest.TestCase.__init__(self, methodName) # Python 3 renamed assertRaisesRegexp to assertRaisesRegex, # and fires deprecation warnings if a program uses the old name. if not hasattr(self, "assertRaisesRegex"): self.assertRaisesRegex = self.assertRaisesRegexp def setUp(self): table = table__t_r_a_k() table.version = 1.0 table.format = 0 self.font = {"trak": table} def test_compile_horiz(self): table = self.font["trak"] table.horizData = TrackData(SKIA_TRACK_ENTRIES) trakData = table.compile(self.font) self.assertEqual(trakData, SKIA_TRAK_TABLE_DATA) def test_compile_vert(self): table = self.font["trak"] table.vertData = TrackData(OSAKA_VERT_TRACK_ENTRIES) trakData = table.compile(self.font) self.assertEqual(trakData, OSAKA_VERT_ONLY_TRAK_TABLE_DATA) def test_compile_horiz_and_vert(self): table = self.font["trak"] table.horizData = TrackData(OSAKA_HORIZ_TRACK_ENTRIES) table.vertData = TrackData(OSAKA_VERT_TRACK_ENTRIES) trakData = table.compile(self.font) self.assertEqual(trakData, OSAKA_TRAK_TABLE_DATA) def test_compile_longword_aligned(self): table = self.font["trak"] # without padding, this 'horizData' would end up 46 byte long table.horizData = TrackData( {0.0: TrackTableEntry(nameIndex=256, values={12.0: 0, 24.0: 0, 36.0: 0})} ) table.vertData = TrackData( {0.0: TrackTableEntry(nameIndex=257, values={12.0: 0, 24.0: 0, 36.0: 0})} ) trakData = table.compile(self.font) self.assertTrue(table.vertOffset % 4 == 0) def test_compile_sizes_mismatch(self): table = self.font["trak"] table.horizData = TrackData( { -1.0: TrackTableEntry(nameIndex=256, values={9.0: -10, 10.0: -30}), 0.0: TrackTableEntry(nameIndex=257, values={8.0: 20, 12.0: 0}), } ) with self.assertRaisesRegex(TTLibError, "entries must specify the same sizes"): table.compile(self.font) def test_decompile_horiz(self): table = self.font["trak"] table.decompile(SKIA_TRAK_TABLE_DATA, self.font) self.assertEqual(table.horizData, SKIA_TRACK_ENTRIES) self.assertEqual(table.vertData, TrackData()) def test_decompile_vert(self): table = self.font["trak"] table.decompile(OSAKA_VERT_ONLY_TRAK_TABLE_DATA, self.font) self.assertEqual(table.horizData, TrackData()) self.assertEqual(table.vertData, OSAKA_VERT_TRACK_ENTRIES) def test_decompile_horiz_and_vert(self): table = self.font["trak"] table.decompile(OSAKA_TRAK_TABLE_DATA, self.font) self.assertEqual(table.horizData, OSAKA_HORIZ_TRACK_ENTRIES) self.assertEqual(table.vertData, OSAKA_VERT_TRACK_ENTRIES) def test_roundtrip_decompile_compile(self): for trakData in ( OSAKA_TRAK_TABLE_DATA, OSAKA_VERT_ONLY_TRAK_TABLE_DATA, SKIA_TRAK_TABLE_DATA, ): table = table__t_r_a_k() table.decompile(trakData, ttFont=None) newTrakData = table.compile(ttFont=None) self.assertEqual(trakData, newTrakData) def test_fromXML_horiz(self): table = self.font["trak"] for name, attrs, content in parseXML(SKIA_TRAK_TABLE_XML): table.fromXML(name, attrs, content, self.font) self.assertEqual(table.version, 1.0) self.assertEqual(table.format, 0) self.assertEqual(table.horizData, SKIA_TRACK_ENTRIES) self.assertEqual(table.vertData, TrackData()) def test_fromXML_horiz_and_vert(self): table = self.font["trak"] for name, attrs, content in parseXML(OSAKA_TRAK_TABLE_XML): table.fromXML(name, attrs, content, self.font) self.assertEqual(table.version, 1.0) self.assertEqual(table.format, 0) self.assertEqual(table.horizData, OSAKA_HORIZ_TRACK_ENTRIES) self.assertEqual(table.vertData, OSAKA_VERT_TRACK_ENTRIES) def test_fromXML_vert(self): table = self.font["trak"] for name, attrs, content in parseXML(OSAKA_VERT_ONLY_TRAK_TABLE_XML): table.fromXML(name, attrs, content, self.font) self.assertEqual(table.version, 1.0) self.assertEqual(table.format, 0) self.assertEqual(table.horizData, TrackData()) self.assertEqual(table.vertData, OSAKA_VERT_TRACK_ENTRIES) def test_toXML_horiz(self): table = self.font["trak"] table.horizData = TrackData(SKIA_TRACK_ENTRIES) add_name(self.font, "Tight", nameID=275) add_name(self.font, "Normal", nameID=303) add_name(self.font, "Loose", nameID=276) self.assertEqual(SKIA_TRAK_TABLE_XML, getXML(table.toXML, self.font)) def test_toXML_horiz_and_vert(self): table = self.font["trak"] table.horizData = TrackData(OSAKA_HORIZ_TRACK_ENTRIES) table.vertData = TrackData(OSAKA_VERT_TRACK_ENTRIES) add_name(self.font, "Tight", nameID=262) add_name(self.font, "Normal", nameID=263) add_name(self.font, "Loose", nameID=264) add_name(self.font, "Tight", nameID=265) add_name(self.font, "Normal", nameID=266) add_name(self.font, "Loose", nameID=267) self.assertEqual(OSAKA_TRAK_TABLE_XML, getXML(table.toXML, self.font)) def test_toXML_vert(self): table = self.font["trak"] table.vertData = TrackData(OSAKA_VERT_TRACK_ENTRIES) add_name(self.font, "Tight", nameID=265) add_name(self.font, "Normal", nameID=266) add_name(self.font, "Loose", nameID=267) self.assertEqual(OSAKA_VERT_ONLY_TRAK_TABLE_XML, getXML(table.toXML, self.font)) def test_roundtrip_fromXML_toXML(self): font = {} add_name(font, "Tight", nameID=275) add_name(font, "Normal", nameID=303) add_name(font, "Loose", nameID=276) add_name(font, "Tight", nameID=262) add_name(font, "Normal", nameID=263) add_name(font, "Loose", nameID=264) add_name(font, "Tight", nameID=265) add_name(font, "Normal", nameID=266) add_name(font, "Loose", nameID=267) for input_xml in ( SKIA_TRAK_TABLE_XML, OSAKA_TRAK_TABLE_XML, OSAKA_VERT_ONLY_TRAK_TABLE_XML, ): table = table__t_r_a_k() font["trak"] = table for name, attrs, content in parseXML(input_xml): table.fromXML(name, attrs, content, font) output_xml = getXML(table.toXML, font) self.assertEqual(input_xml, output_xml) def add_name(font, string, nameID): nameTable = font.get("name") if nameTable is None: nameTable = font["name"] = table__n_a_m_e() nameTable.names = [] namerec = NameRecord() namerec.nameID = nameID namerec.string = string.encode("mac_roman") namerec.platformID, namerec.platEncID, namerec.langID = (1, 0, 0) nameTable.names.append(namerec) if __name__ == "__main__": import sys sys.exit(unittest.main())