1# coding: utf-8 2from __future__ import print_function, division, absolute_import, unicode_literals 3from fontTools.misc.py23 import * 4from fontTools.misc.testTools import FakeFont, getXML, parseXML 5from fontTools.misc.textTools import deHexStr, hexStr 6from fontTools.ttLib import newTable 7import unittest 8 9 10# This is the anchor points table of the first font file in 11# “/Library/Fonts/Devanagari Sangam MN.ttc” on macOS 10.12.6. 12# For testing, we’ve changed the GlyphIDs to smaller values. 13# Also, in the AATLookup, we’ve changed GlyphDataOffset value 14# for the end-of-table marker from 0xFFFF to 0 since that is 15# what our encoder emits. (The value for end-of-table markers 16# does not actually matter). 17ANKR_FORMAT_0_DATA = deHexStr( 18 '0000 0000 ' # 0: Format=0, Flags=0 19 '0000 000C ' # 4: LookupTableOffset=12 20 '0000 0024 ' # 8: GlyphDataTableOffset=36 21 '0006 0004 0002 ' # 12: LookupFormat=6, UnitSize=4, NUnits=2 22 '0008 0001 0000 ' # 18: SearchRange=8, EntrySelector=1, RangeShift=0 23 '0001 0000 ' # 24: Glyph=A, Offset=0 (+GlyphDataTableOffset=36) 24 '0003 0008 ' # 28: Glyph=C, Offset=8 (+GlyphDataTableOffset=44) 25 'FFFF 0000 ' # 32: Glyph=<end>, Offset=<n/a> 26 '0000 0001 ' # 36: GlyphData[A].NumPoints=1 27 '0235 045E ' # 40: GlyphData[A].Points[0].X=565, .Y=1118 28 '0000 0001 ' # 44: GlyphData[C].NumPoints=1 29 'FED2 045E ' # 48: GlyphData[C].Points[0].X=-302, .Y=1118 30) # 52: <end> 31assert len(ANKR_FORMAT_0_DATA) == 52 32 33 34ANKR_FORMAT_0_XML = [ 35 '<AnchorPoints Format="0">', 36 ' <Flags value="0"/>', 37 ' <Anchors>', 38 ' <Lookup glyph="A">', 39 ' <!-- AnchorPointCount=1 -->', 40 ' <AnchorPoint index="0">', 41 ' <XCoordinate value="565"/>', 42 ' <YCoordinate value="1118"/>', 43 ' </AnchorPoint>', 44 ' </Lookup>', 45 ' <Lookup glyph="C">', 46 ' <!-- AnchorPointCount=1 -->', 47 ' <AnchorPoint index="0">', 48 ' <XCoordinate value="-302"/>', 49 ' <YCoordinate value="1118"/>', 50 ' </AnchorPoint>', 51 ' </Lookup>', 52 ' </Anchors>', 53 '</AnchorPoints>', 54] 55 56 57# Same data as ANKR_FORMAT_0_DATA, but with chunks of unused data 58# whose presence should not stop us from decompiling the table. 59ANKR_FORMAT_0_STRAY_DATA = deHexStr( 60 '0000 0000 ' # 0: Format=0, Flags=0 61 '0000 0018 ' # 4: LookupTableOffset=24 62 '0000 0034 ' # 8: GlyphDataTableOffset=52 63 'DEAD BEEF CAFE ' # 12: <stray data> 64 'DEAD BEEF CAFE ' # 18: <stray data> 65 '0006 0004 0002 ' # 24: LookupFormat=6, UnitSize=4, NUnits=2 66 '0008 0001 0000 ' # 30: SearchRange=8, EntrySelector=1, RangeShift=0 67 '0001 0000 ' # 36: Glyph=A, Offset=0 (+GlyphDataTableOffset=52) 68 '0003 0008 ' # 40: Glyph=C, Offset=8 (+GlyphDataTableOffset=60) 69 'FFFF 0000 ' # 44: Glyph=<end>, Offset=<n/a> 70 'BEEF F00D ' # 48: <stray data> 71 '0000 0001 ' # 52: GlyphData[A].NumPoints=1 72 '0235 045E ' # 56: GlyphData[A].Points[0].X=565, .Y=1118 73 '0000 0001 ' # 60: GlyphData[C].NumPoints=1 74 'FED2 045E ' # 64: GlyphData[C].Points[0].X=-302, .Y=1118 75) # 68: <end> 76assert len(ANKR_FORMAT_0_STRAY_DATA) == 68 77 78 79# Constructed test case where glyphs A and D share the same anchor data. 80ANKR_FORMAT_0_SHARING_DATA = deHexStr( 81 '0000 0000 ' # 0: Format=0, Flags=0 82 '0000 000C ' # 4: LookupTableOffset=12 83 '0000 0028 ' # 8: GlyphDataTableOffset=40 84 '0006 0004 0003 ' # 12: LookupFormat=6, UnitSize=4, NUnits=3 85 '0008 0001 0004 ' # 18: SearchRange=8, EntrySelector=1, RangeShift=4 86 '0001 0000 ' # 24: Glyph=A, Offset=0 (+GlyphDataTableOffset=36) 87 '0003 0008 ' # 28: Glyph=C, Offset=8 (+GlyphDataTableOffset=44) 88 '0004 0000 ' # 32: Glyph=D, Offset=0 (+GlyphDataTableOffset=36) 89 'FFFF 0000 ' # 36: Glyph=<end>, Offset=<n/a> 90 '0000 0001 ' # 40: GlyphData[A].NumPoints=1 91 '0235 045E ' # 44: GlyphData[A].Points[0].X=565, .Y=1118 92 '0000 0002 ' # 48: GlyphData[C].NumPoints=2 93 '000B 000C ' # 52: GlyphData[C].Points[0].X=11, .Y=12 94 '001B 001C ' # 56: GlyphData[C].Points[1].X=27, .Y=28 95) # 60: <end> 96assert len(ANKR_FORMAT_0_SHARING_DATA) == 60 97 98 99ANKR_FORMAT_0_SHARING_XML = [ 100 '<AnchorPoints Format="0">', 101 ' <Flags value="0"/>', 102 ' <Anchors>', 103 ' <Lookup glyph="A">', 104 ' <!-- AnchorPointCount=1 -->', 105 ' <AnchorPoint index="0">', 106 ' <XCoordinate value="565"/>', 107 ' <YCoordinate value="1118"/>', 108 ' </AnchorPoint>', 109 ' </Lookup>', 110 ' <Lookup glyph="C">', 111 ' <!-- AnchorPointCount=2 -->', 112 ' <AnchorPoint index="0">', 113 ' <XCoordinate value="11"/>', 114 ' <YCoordinate value="12"/>', 115 ' </AnchorPoint>', 116 ' <AnchorPoint index="1">', 117 ' <XCoordinate value="27"/>', 118 ' <YCoordinate value="28"/>', 119 ' </AnchorPoint>', 120 ' </Lookup>', 121 ' <Lookup glyph="D">', 122 ' <!-- AnchorPointCount=1 -->', 123 ' <AnchorPoint index="0">', 124 ' <XCoordinate value="565"/>', 125 ' <YCoordinate value="1118"/>', 126 ' </AnchorPoint>', 127 ' </Lookup>', 128 ' </Anchors>', 129 '</AnchorPoints>', 130] 131 132 133class ANKRTest(unittest.TestCase): 134 135 @classmethod 136 def setUpClass(cls): 137 cls.maxDiff = None 138 cls.font = FakeFont(['.notdef', 'A', 'B', 'C', 'D']) 139 140 def decompileToXML(self, data, xml): 141 table = newTable('ankr') 142 table.decompile(data, self.font) 143 self.assertEqual(getXML(table.toXML), xml) 144 145 def compileFromXML(self, xml, data): 146 table = newTable('ankr') 147 for name, attrs, content in parseXML(xml): 148 table.fromXML(name, attrs, content, font=self.font) 149 self.assertEqual(hexStr(table.compile(self.font)), hexStr(data)) 150 151 def roundtrip(self, data, xml): 152 self.decompileToXML(data, xml) 153 self.compileFromXML(xml, data) 154 155 def testFormat0(self): 156 self.roundtrip(ANKR_FORMAT_0_DATA, ANKR_FORMAT_0_XML) 157 158 def testFormat0_stray(self): 159 self.decompileToXML(ANKR_FORMAT_0_STRAY_DATA, ANKR_FORMAT_0_XML) 160 161 def testFormat0_sharing(self): 162 self.roundtrip(ANKR_FORMAT_0_SHARING_DATA, ANKR_FORMAT_0_SHARING_XML) 163 164 165if __name__ == '__main__': 166 import sys 167 sys.exit(unittest.main()) 168