Home | History | Annotate | Download | only in tables
      1 from __future__ import print_function, division, absolute_import, unicode_literals
      2 from fontTools.misc.py23 import *
      3 from fontTools.misc.testTools import parseXML, getXML
      4 from fontTools.misc.textTools import deHexStr
      5 from fontTools.ttLib import TTFont, TTLibError
      6 from fontTools.ttLib.tables._t_r_a_k import *
      7 from fontTools.ttLib.tables._n_a_m_e import table__n_a_m_e, NameRecord
      8 import unittest
      9 
     10 
     11 # /Library/Fonts/Osaka.ttf from OSX has trak table with both horiz and vertData
     12 OSAKA_TRAK_TABLE_DATA = deHexStr(
     13 	'00 01 00 00 00 00 00 0c 00 40 00 00 00 03 00 02 00 00 00 2c ff ff '
     14 	'00 00 01 06 00 34 00 00 00 00 01 07 00 38 00 01 00 00 01 08 00 3c '
     15 	'00 0c 00 00 00 18 00 00 ff f4 ff f4 00 00 00 00 00 0c 00 0c 00 03 '
     16 	'00 02 00 00 00 60 ff ff 00 00 01 09 00 68 00 00 00 00 01 0a 00 6c '
     17 	'00 01 00 00 01 0b 00 70 00 0c 00 00 00 18 00 00 ff f4 ff f4 00 00 '
     18 	'00 00 00 0c 00 0c')
     19 
     20 # decompiled horizData and vertData entries from Osaka.ttf 
     21 OSAKA_HORIZ_TRACK_ENTRIES = {
     22 	-1.0: TrackTableEntry({24.0: -12, 12.0: -12}, nameIndex=262),
     23 	 0.0: TrackTableEntry({24.0: 0, 12.0: 0}, nameIndex=263),
     24 	 1.0: TrackTableEntry({24.0: 12, 12.0: 12}, nameIndex=264)
     25 	}
     26 
     27 OSAKA_VERT_TRACK_ENTRIES = {
     28 	-1.0: TrackTableEntry({24.0: -12, 12.0: -12}, nameIndex=265),
     29 	 0.0: TrackTableEntry({24.0: 0, 12.0: 0}, nameIndex=266),
     30 	 1.0: TrackTableEntry({24.0: 12, 12.0: 12}, nameIndex=267)
     31 	}
     32 
     33 OSAKA_TRAK_TABLE_XML = [
     34 	'<version value="1.0"/>',
     35 	'<format value="0"/>',
     36 	'<horizData>',
     37 	'  <!-- nTracks=3, nSizes=2 -->',
     38 	'  <trackEntry value="-1.0" nameIndex="262">',
     39 	'    <!-- Tight -->',
     40 	'    <track size="12.0" value="-12"/>',
     41 	'    <track size="24.0" value="-12"/>',
     42 	'  </trackEntry>',
     43 	'  <trackEntry value="0.0" nameIndex="263">',
     44 	'    <!-- Normal -->',
     45 	'    <track size="12.0" value="0"/>',
     46 	'    <track size="24.0" value="0"/>',
     47 	'  </trackEntry>',
     48 	'  <trackEntry value="1.0" nameIndex="264">',
     49 	'    <!-- Loose -->',
     50 	'    <track size="12.0" value="12"/>',
     51 	'    <track size="24.0" value="12"/>',
     52 	'  </trackEntry>',
     53 	'</horizData>',
     54 	'<vertData>',
     55 	'  <!-- nTracks=3, nSizes=2 -->',
     56 	'  <trackEntry value="-1.0" nameIndex="265">',
     57 	'    <!-- Tight -->',
     58 	'    <track size="12.0" value="-12"/>',
     59 	'    <track size="24.0" value="-12"/>',
     60 	'  </trackEntry>',
     61 	'  <trackEntry value="0.0" nameIndex="266">',
     62 	'    <!-- Normal -->',
     63 	'    <track size="12.0" value="0"/>',
     64 	'    <track size="24.0" value="0"/>',
     65 	'  </trackEntry>',
     66 	'  <trackEntry value="1.0" nameIndex="267">',
     67 	'    <!-- Loose -->',
     68 	'    <track size="12.0" value="12"/>',
     69 	'    <track size="24.0" value="12"/>',
     70 	'  </trackEntry>',
     71 	'</vertData>',
     72 ]
     73 
     74 # made-up table containing only vertData (no horizData)
     75 OSAKA_VERT_ONLY_TRAK_TABLE_DATA = deHexStr(
     76 	'00 01 00 00 00 00 00 00 00 0c 00 00 00 03 00 02 00 00 00 2c ff ff '
     77 	'00 00 01 09 00 34 00 00 00 00 01 0a 00 38 00 01 00 00 01 0b 00 3c '
     78 	'00 0c 00 00 00 18 00 00 ff f4 ff f4 00 00 00 00 00 0c 00 0c')
     79 
     80 OSAKA_VERT_ONLY_TRAK_TABLE_XML = [
     81 	'<version value="1.0"/>',
     82 	'<format value="0"/>',
     83 	'<horizData>',
     84 	'  <!-- nTracks=0, nSizes=0 -->',
     85 	'</horizData>',
     86 	'<vertData>',
     87 	'  <!-- nTracks=3, nSizes=2 -->',
     88 	'  <trackEntry value="-1.0" nameIndex="265">',
     89 	'    <!-- Tight -->',
     90 	'    <track size="12.0" value="-12"/>',
     91 	'    <track size="24.0" value="-12"/>',
     92 	'  </trackEntry>',
     93 	'  <trackEntry value="0.0" nameIndex="266">',
     94 	'    <!-- Normal -->',
     95 	'    <track size="12.0" value="0"/>',
     96 	'    <track size="24.0" value="0"/>',
     97 	'  </trackEntry>',
     98 	'  <trackEntry value="1.0" nameIndex="267">',
     99 	'    <!-- Loose -->',
    100 	'    <track size="12.0" value="12"/>',
    101 	'    <track size="24.0" value="12"/>',
    102 	'  </trackEntry>',
    103 	'</vertData>',
    104 ]
    105 
    106 
    107 # also /Library/Fonts/Skia.ttf contains a trak table with horizData
    108 SKIA_TRAK_TABLE_DATA = deHexStr(
    109 	'00 01 00 00 00 00 00 0c 00 00 00 00 00 03 00 05 00 00 00 2c ff ff '
    110 	'00 00 01 13 00 40 00 00 00 00 01 2f 00 4a 00 01 00 00 01 14 00 54 '
    111 	'00 09 00 00 00 0a 00 00 00 0c 00 00 00 12 00 00 00 13 00 00 ff f6 '
    112 	'ff e2 ff c4 ff c1 ff c1 00 0f 00 00 ff fb ff e7 ff e7 00 8c 00 82 '
    113 	'00 7d 00 73 00 73')
    114 
    115 SKIA_TRACK_ENTRIES = {
    116 	-1.0: TrackTableEntry(
    117 		{9.0: -10, 10.0: -30, 19.0: -63, 12.0: -60, 18.0: -63}, nameIndex=275),
    118 	 0.0: TrackTableEntry(
    119 	 	{9.0: 15, 10.0: 0, 19.0: -25, 12.0: -5, 18.0: -25}, nameIndex=303),
    120 	 1.0: TrackTableEntry(
    121 	 	{9.0: 140, 10.0: 130, 19.0: 115, 12.0: 125, 18.0: 115}, nameIndex=276)
    122 	}
    123 
    124 SKIA_TRAK_TABLE_XML = [
    125 	'<version value="1.0"/>',
    126 	'<format value="0"/>',
    127 	'<horizData>',
    128 	'  <!-- nTracks=3, nSizes=5 -->',
    129 	'  <trackEntry value="-1.0" nameIndex="275">',
    130 	'    <!-- Tight -->',
    131 	'    <track size="9.0" value="-10"/>',
    132 	'    <track size="10.0" value="-30"/>',
    133 	'    <track size="12.0" value="-60"/>',
    134 	'    <track size="18.0" value="-63"/>',
    135 	'    <track size="19.0" value="-63"/>',
    136 	'  </trackEntry>',
    137 	'  <trackEntry value="0.0" nameIndex="303">',
    138 	'    <!-- Normal -->',
    139 	'    <track size="9.0" value="15"/>',
    140 	'    <track size="10.0" value="0"/>',
    141 	'    <track size="12.0" value="-5"/>',
    142 	'    <track size="18.0" value="-25"/>',
    143 	'    <track size="19.0" value="-25"/>',
    144 	'  </trackEntry>',
    145 	'  <trackEntry value="1.0" nameIndex="276">',
    146 	'    <!-- Loose -->',
    147 	'    <track size="9.0" value="140"/>',
    148 	'    <track size="10.0" value="130"/>',
    149 	'    <track size="12.0" value="125"/>',
    150 	'    <track size="18.0" value="115"/>',
    151 	'    <track size="19.0" value="115"/>',
    152 	'  </trackEntry>',
    153 	'</horizData>',
    154 	'<vertData>',
    155 	'  <!-- nTracks=0, nSizes=0 -->',
    156 	'</vertData>',
    157 ]
    158 
    159 
    160 class TrackingTableTest(unittest.TestCase):
    161 
    162 	def __init__(self, methodName):
    163 		unittest.TestCase.__init__(self, methodName)
    164 		# Python 3 renamed assertRaisesRegexp to assertRaisesRegex,
    165 		# and fires deprecation warnings if a program uses the old name.
    166 		if not hasattr(self, "assertRaisesRegex"):
    167 			self.assertRaisesRegex = self.assertRaisesRegexp
    168 
    169 	def setUp(self):
    170 		table = table__t_r_a_k()
    171 		table.version = 1.0
    172 		table.format = 0
    173 		self.font = {'trak': table}
    174 
    175 	def test_compile_horiz(self):
    176 		table = self.font['trak']
    177 		table.horizData = TrackData(SKIA_TRACK_ENTRIES)
    178 		trakData = table.compile(self.font)
    179 		self.assertEqual(trakData, SKIA_TRAK_TABLE_DATA)
    180 
    181 	def test_compile_vert(self):
    182 		table = self.font['trak']
    183 		table.vertData = TrackData(OSAKA_VERT_TRACK_ENTRIES)
    184 		trakData = table.compile(self.font)
    185 		self.assertEqual(trakData, OSAKA_VERT_ONLY_TRAK_TABLE_DATA)
    186 
    187 	def test_compile_horiz_and_vert(self):
    188 		table = self.font['trak']
    189 		table.horizData = TrackData(OSAKA_HORIZ_TRACK_ENTRIES)
    190 		table.vertData = TrackData(OSAKA_VERT_TRACK_ENTRIES)
    191 		trakData = table.compile(self.font)
    192 		self.assertEqual(trakData, OSAKA_TRAK_TABLE_DATA)
    193 
    194 	def test_compile_longword_aligned(self):
    195 		table = self.font['trak']
    196 		# without padding, this 'horizData' would end up 46 byte long
    197 		table.horizData = TrackData({
    198 			0.0: TrackTableEntry(nameIndex=256, values={12.0: 0, 24.0: 0, 36.0: 0})
    199 			})
    200 		table.vertData = TrackData({
    201 			0.0: TrackTableEntry(nameIndex=257, values={12.0: 0, 24.0: 0, 36.0: 0})
    202 			})
    203 		trakData = table.compile(self.font)
    204 		self.assertTrue(table.vertOffset % 4 == 0)
    205 
    206 	def test_compile_sizes_mismatch(self):
    207 		table = self.font['trak']
    208 		table.horizData = TrackData({
    209 			-1.0: TrackTableEntry(nameIndex=256, values={9.0: -10, 10.0: -30}),
    210 			 0.0: TrackTableEntry(nameIndex=257, values={8.0: 20, 12.0: 0})
    211 			})
    212 		with self.assertRaisesRegex(TTLibError, 'entries must specify the same sizes'):
    213 			table.compile(self.font)
    214 
    215 	def test_decompile_horiz(self):
    216 		table = self.font['trak']
    217 		table.decompile(SKIA_TRAK_TABLE_DATA, self.font)
    218 		self.assertEqual(table.horizData, SKIA_TRACK_ENTRIES)
    219 		self.assertEqual(table.vertData, TrackData())
    220 
    221 	def test_decompile_vert(self):
    222 		table = self.font['trak']
    223 		table.decompile(OSAKA_VERT_ONLY_TRAK_TABLE_DATA, self.font)
    224 		self.assertEqual(table.horizData, TrackData())
    225 		self.assertEqual(table.vertData, OSAKA_VERT_TRACK_ENTRIES)
    226 
    227 	def test_decompile_horiz_and_vert(self):
    228 		table = self.font['trak']
    229 		table.decompile(OSAKA_TRAK_TABLE_DATA, self.font)
    230 		self.assertEqual(table.horizData, OSAKA_HORIZ_TRACK_ENTRIES)
    231 		self.assertEqual(table.vertData, OSAKA_VERT_TRACK_ENTRIES)
    232 
    233 	def test_roundtrip_decompile_compile(self):
    234 		for trakData in (
    235 				OSAKA_TRAK_TABLE_DATA,
    236 				OSAKA_VERT_ONLY_TRAK_TABLE_DATA,
    237 				SKIA_TRAK_TABLE_DATA):
    238 			table = table__t_r_a_k()
    239 			table.decompile(trakData, ttFont=None)
    240 			newTrakData = table.compile(ttFont=None)
    241 			self.assertEqual(trakData, newTrakData)
    242 
    243 	def test_fromXML_horiz(self):
    244 		table = self.font['trak']
    245 		for name, attrs, content in parseXML(SKIA_TRAK_TABLE_XML):
    246 			table.fromXML(name, attrs, content, self.font)
    247 		self.assertEqual(table.version, 1.0)
    248 		self.assertEqual(table.format, 0)
    249 		self.assertEqual(table.horizData, SKIA_TRACK_ENTRIES)
    250 		self.assertEqual(table.vertData, TrackData())
    251 
    252 	def test_fromXML_horiz_and_vert(self):
    253 		table = self.font['trak']
    254 		for name, attrs, content in parseXML(OSAKA_TRAK_TABLE_XML):
    255 			table.fromXML(name, attrs, content, self.font)
    256 		self.assertEqual(table.version, 1.0)
    257 		self.assertEqual(table.format, 0)
    258 		self.assertEqual(table.horizData, OSAKA_HORIZ_TRACK_ENTRIES)
    259 		self.assertEqual(table.vertData, OSAKA_VERT_TRACK_ENTRIES)
    260 
    261 	def test_fromXML_vert(self):
    262 		table = self.font['trak']
    263 		for name, attrs, content in parseXML(OSAKA_VERT_ONLY_TRAK_TABLE_XML):
    264 			table.fromXML(name, attrs, content, self.font)
    265 		self.assertEqual(table.version, 1.0)
    266 		self.assertEqual(table.format, 0)
    267 		self.assertEqual(table.horizData, TrackData())
    268 		self.assertEqual(table.vertData, OSAKA_VERT_TRACK_ENTRIES)
    269 
    270 	def test_toXML_horiz(self):
    271 		table = self.font['trak']
    272 		table.horizData = TrackData(SKIA_TRACK_ENTRIES)
    273 		add_name(self.font, 'Tight', nameID=275)
    274 		add_name(self.font, 'Normal', nameID=303)
    275 		add_name(self.font, 'Loose', nameID=276)
    276 		self.assertEqual(
    277 			SKIA_TRAK_TABLE_XML,
    278 			getXML(table.toXML, self.font))
    279 
    280 	def test_toXML_horiz_and_vert(self):
    281 		table = self.font['trak']
    282 		table.horizData = TrackData(OSAKA_HORIZ_TRACK_ENTRIES)
    283 		table.vertData = TrackData(OSAKA_VERT_TRACK_ENTRIES)
    284 		add_name(self.font, 'Tight', nameID=262)
    285 		add_name(self.font, 'Normal', nameID=263)
    286 		add_name(self.font, 'Loose', nameID=264)
    287 		add_name(self.font, 'Tight', nameID=265)
    288 		add_name(self.font, 'Normal', nameID=266)
    289 		add_name(self.font, 'Loose', nameID=267)
    290 		self.assertEqual(
    291 			OSAKA_TRAK_TABLE_XML,
    292 			getXML(table.toXML, self.font))
    293 
    294 	def test_toXML_vert(self):
    295 		table = self.font['trak']
    296 		table.vertData = TrackData(OSAKA_VERT_TRACK_ENTRIES)
    297 		add_name(self.font, 'Tight', nameID=265)
    298 		add_name(self.font, 'Normal', nameID=266)
    299 		add_name(self.font, 'Loose', nameID=267)
    300 		self.assertEqual(
    301 			OSAKA_VERT_ONLY_TRAK_TABLE_XML,
    302 			getXML(table.toXML, self.font))
    303 
    304 	def test_roundtrip_fromXML_toXML(self):
    305 		font = {}
    306 		add_name(font, 'Tight', nameID=275)
    307 		add_name(font, 'Normal', nameID=303)
    308 		add_name(font, 'Loose', nameID=276)
    309 		add_name(font, 'Tight', nameID=262)
    310 		add_name(font, 'Normal', nameID=263)
    311 		add_name(font, 'Loose', nameID=264)
    312 		add_name(font, 'Tight', nameID=265)
    313 		add_name(font, 'Normal', nameID=266)
    314 		add_name(font, 'Loose', nameID=267)
    315 		for input_xml in (
    316 				SKIA_TRAK_TABLE_XML,
    317 				OSAKA_TRAK_TABLE_XML,
    318 				OSAKA_VERT_ONLY_TRAK_TABLE_XML):
    319 			table = table__t_r_a_k()
    320 			font['trak'] = table
    321 			for name, attrs, content in parseXML(input_xml):
    322 				table.fromXML(name, attrs, content, font)
    323 			output_xml = getXML(table.toXML, font)
    324 			self.assertEqual(input_xml, output_xml)
    325 
    326 
    327 def add_name(font, string, nameID):
    328 	nameTable = font.get("name")
    329 	if nameTable is None:
    330 		nameTable = font["name"] = table__n_a_m_e()
    331 		nameTable.names = []
    332 	namerec = NameRecord()
    333 	namerec.nameID = nameID
    334 	namerec.string = string.encode('mac_roman')
    335 	namerec.platformID, namerec.platEncID, namerec.langID = (1, 0, 0)
    336 	nameTable.names.append(namerec)
    337 
    338 
    339 if __name__ == "__main__":
    340 	import sys
    341 	sys.exit(unittest.main())
    342