344 lines
13 KiB
Python
344 lines
13 KiB
Python
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 = [
|
|
'<version value="1.0"/>',
|
|
'<format value="0"/>',
|
|
"<horizData>",
|
|
" <!-- nTracks=3, nSizes=2 -->",
|
|
' <trackEntry value="-1.0" nameIndex="262">',
|
|
" <!-- Tight -->",
|
|
' <track size="12.0" value="-12"/>',
|
|
' <track size="24.0" value="-12"/>',
|
|
" </trackEntry>",
|
|
' <trackEntry value="0.0" nameIndex="263">',
|
|
" <!-- Normal -->",
|
|
' <track size="12.0" value="0"/>',
|
|
' <track size="24.0" value="0"/>',
|
|
" </trackEntry>",
|
|
' <trackEntry value="1.0" nameIndex="264">',
|
|
" <!-- Loose -->",
|
|
' <track size="12.0" value="12"/>',
|
|
' <track size="24.0" value="12"/>',
|
|
" </trackEntry>",
|
|
"</horizData>",
|
|
"<vertData>",
|
|
" <!-- nTracks=3, nSizes=2 -->",
|
|
' <trackEntry value="-1.0" nameIndex="265">',
|
|
" <!-- Tight -->",
|
|
' <track size="12.0" value="-12"/>',
|
|
' <track size="24.0" value="-12"/>',
|
|
" </trackEntry>",
|
|
' <trackEntry value="0.0" nameIndex="266">',
|
|
" <!-- Normal -->",
|
|
' <track size="12.0" value="0"/>',
|
|
' <track size="24.0" value="0"/>',
|
|
" </trackEntry>",
|
|
' <trackEntry value="1.0" nameIndex="267">',
|
|
" <!-- Loose -->",
|
|
' <track size="12.0" value="12"/>',
|
|
' <track size="24.0" value="12"/>',
|
|
" </trackEntry>",
|
|
"</vertData>",
|
|
]
|
|
|
|
# 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 = [
|
|
'<version value="1.0"/>',
|
|
'<format value="0"/>',
|
|
"<horizData>",
|
|
" <!-- nTracks=0, nSizes=0 -->",
|
|
"</horizData>",
|
|
"<vertData>",
|
|
" <!-- nTracks=3, nSizes=2 -->",
|
|
' <trackEntry value="-1.0" nameIndex="265">',
|
|
" <!-- Tight -->",
|
|
' <track size="12.0" value="-12"/>',
|
|
' <track size="24.0" value="-12"/>',
|
|
" </trackEntry>",
|
|
' <trackEntry value="0.0" nameIndex="266">',
|
|
" <!-- Normal -->",
|
|
' <track size="12.0" value="0"/>',
|
|
' <track size="24.0" value="0"/>',
|
|
" </trackEntry>",
|
|
' <trackEntry value="1.0" nameIndex="267">',
|
|
" <!-- Loose -->",
|
|
' <track size="12.0" value="12"/>',
|
|
' <track size="24.0" value="12"/>',
|
|
" </trackEntry>",
|
|
"</vertData>",
|
|
]
|
|
|
|
|
|
# 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 = [
|
|
'<version value="1.0"/>',
|
|
'<format value="0"/>',
|
|
"<horizData>",
|
|
" <!-- nTracks=3, nSizes=5 -->",
|
|
' <trackEntry value="-1.0" nameIndex="275">',
|
|
" <!-- Tight -->",
|
|
' <track size="9.0" value="-10"/>',
|
|
' <track size="10.0" value="-30"/>',
|
|
' <track size="12.0" value="-60"/>',
|
|
' <track size="18.0" value="-63"/>',
|
|
' <track size="19.0" value="-63"/>',
|
|
" </trackEntry>",
|
|
' <trackEntry value="0.0" nameIndex="303">',
|
|
" <!-- Normal -->",
|
|
' <track size="9.0" value="15"/>',
|
|
' <track size="10.0" value="0"/>',
|
|
' <track size="12.0" value="-5"/>',
|
|
' <track size="18.0" value="-25"/>',
|
|
' <track size="19.0" value="-25"/>',
|
|
" </trackEntry>",
|
|
' <trackEntry value="1.0" nameIndex="276">',
|
|
" <!-- Loose -->",
|
|
' <track size="9.0" value="140"/>',
|
|
' <track size="10.0" value="130"/>',
|
|
' <track size="12.0" value="125"/>',
|
|
' <track size="18.0" value="115"/>',
|
|
' <track size="19.0" value="115"/>',
|
|
" </trackEntry>",
|
|
"</horizData>",
|
|
"<vertData>",
|
|
" <!-- nTracks=0, nSizes=0 -->",
|
|
"</vertData>",
|
|
]
|
|
|
|
|
|
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())
|