• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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