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