1from __future__ import print_function, division, absolute_import, unicode_literals
2from fontTools.misc.py23 import *
3from fontTools.misc.testTools import FakeFont, getXML, parseXML
4from fontTools.misc.textTools import deHexStr, hexStr
5from fontTools.ttLib import newTable
6import unittest
7
8
9# Example: Format 0 Optical Bounds Table
10# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6opbd.html
11OPBD_FORMAT_0_DATA = deHexStr(
12    '0001 0000 0000 '       #  0: Version=1.0, Format=0
13    '0006 0004 0002 '       #  6: LookupFormat=6, UnitSize=4, NUnits=2
14    '0008 0001 0000 '       # 12: SearchRange=8, EntrySelector=1, RangeShift=0
15    '000A 001E '            # 18: Glyph=10(=C), OffsetOfOpticalBoundsDeltas=30
16    '002B 0026 '            # 22: Glyph=43(=A), OffsetOfOpticalBoundsDeltas=38
17    'FFFF 0000 '            # 26: Glyph=<end>, OffsetOfOpticalBoundsDeltas=0
18    'FFCE 0005 0037 FFFB '  # 30: Bounds[C].Left=-50 .Top=5 .Right=55 .Bottom=-5
19    'FFF6 000F 0000 0000 '  # 38: Bounds[A].Left=-10 .Top=15 .Right=0 .Bottom=0
20)                           # 46: <end>
21assert(len(OPBD_FORMAT_0_DATA) == 46)
22
23
24OPBD_FORMAT_0_XML = [
25    '<Version value="0x00010000"/>',
26    '<OpticalBounds Format="0">',
27    '  <OpticalBoundsDeltas>',
28    '    <Lookup glyph="A">',
29    '      <Left value="-10"/>',
30    '      <Top value="15"/>',
31    '      <Right value="0"/>',
32    '      <Bottom value="0"/>',
33    '    </Lookup>',
34    '    <Lookup glyph="C">',
35    '      <Left value="-50"/>',
36    '      <Top value="5"/>',
37    '      <Right value="55"/>',
38    '      <Bottom value="-5"/>',
39    '    </Lookup>',
40    '  </OpticalBoundsDeltas>',
41    '</OpticalBounds>',
42]
43
44
45# Example: Format 1 Optical Bounds Table
46# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6opbd.html
47OPBD_FORMAT_1_DATA = deHexStr(
48    '0001 0000 0001 '       #  0: Version=1.0, Format=1
49    '0006 0004 0002 '       #  6: LookupFormat=6, UnitSize=4, NUnits=2
50    '0008 0001 0000 '       # 12: SearchRange=8, EntrySelector=1, RangeShift=0
51    '000A 001E '            # 18: Glyph=10(=C), OffsetOfOpticalBoundsPoints=30
52    '002B 0026 '            # 22: Glyph=43(=A), OffsetOfOpticalBoundsPoints=38
53    'FFFF 0000 '            # 26: Glyph=<end>, OffsetOfOpticalBoundsPoints=0
54    '0024 0025 0026 0027 '  # 30: Bounds[C].Left=36 .Top=37 .Right=38 .Bottom=39
55    '0020 0029 FFFF FFFF '  # 38: Bounds[A].Left=32 .Top=41 .Right=-1 .Bottom=-1
56)                           # 46: <end>
57assert(len(OPBD_FORMAT_1_DATA) == 46)
58
59
60OPBD_FORMAT_1_XML = [
61    '<Version value="0x00010000"/>',
62    '<OpticalBounds Format="1">',
63    '  <OpticalBoundsPoints>',
64    '    <Lookup glyph="A">',
65    '      <Left value="32"/>',
66    '      <Top value="41"/>',
67    '      <Right value="-1"/>',
68    '      <Bottom value="-1"/>',
69    '    </Lookup>',
70    '    <Lookup glyph="C">',
71    '      <Left value="36"/>',
72    '      <Top value="37"/>',
73    '      <Right value="38"/>',
74    '      <Bottom value="39"/>',
75    '    </Lookup>',
76    '  </OpticalBoundsPoints>',
77    '</OpticalBounds>',
78]
79
80
81# This is the content of the Optical Bounds table in AppleChancery.ttf,
82# font version 8.0d1e1 of 2013-02-06. An early version of fontTools
83# was crashing when trying to decompile this table.
84# https://github.com/fonttools/fonttools/issues/1031
85OPBD_APPLE_CHANCERY_DATA = deHexStr(
86    '0001 0000 0000 '  #   0: Version=1.0, Format=0
87    '0004 0006 0011 '  #   6: LookupFormat=4, UnitSize=6, NUnits=17
88    '0060 0004 0006 '  #  12: SearchRange=96, EntrySelector=4, RangeShift=6
89    '017d 017d 0072 '  #  18: Seg[0].LastGlyph=381, FirstGlyph=381, Off=114(+6)
90    '0183 0180 0074 '  #  24: Seg[1].LastGlyph=387, FirstGlyph=384, Off=116(+6)
91    '0186 0185 007c '  #  30: Seg[2].LastGlyph=390, FirstGlyph=389, Off=124(+6)
92    '018f 018b 0080 '  #  36: Seg[3].LastGlyph=399, FirstGlyph=395, Off=128(+6)
93    '01a0 0196 008a '  #  42: Seg[4].LastGlyph=416, FirstGlyph=406, Off=138(+6)
94    '01a5 01a3 00a0 '  #  48: Seg[5].LastGlyph=421, FirstGlyph=419, Off=160(+6)
95    '01aa 01aa 00a6 '  #  54: Seg[6].LastGlyph=426, FirstGlyph=426, Off=166(+6)
96    '01ac 01ac 00a8 '  #  60: Seg[7].LastGlyph=428, FirstGlyph=428, Off=168(+6)
97    '01fb 01f1 00aa '  #  66: Seg[8].LastGlyph=507, FirstGlyph=497, Off=170(+6)
98    '0214 0209 00c0 '  #  72: Seg[9].LastGlyph=532, FirstGlyph=521, Off=192(+6)
99    '021d 0216 00d8 '  #  78: Seg[10].LastGlyph=541, FirstGlyph=534, Off=216(+6)
100    '0222 0220 00e8 '  #  84: Seg[11].LastGlyph=546, FirstGlyph=544, Off=232(+6)
101    '0227 0225 00ee '  #  90: Seg[12].LastGlyph=551, FirstGlyph=549, Off=238(+6)
102    '0229 0229 00f4 '  #  96: Seg[13].LastGlyph=553, FirstGlyph=553, Off=244(+6)
103    '023b 023b 00f6 '  # 102: Seg[14].LastGlyph=571, FirstGlyph=571, Off=246(+6)
104    '023e 023e 00f8 '  # 108: Seg[15].LastGlyph=574, FirstGlyph=574, Off=248(+6)
105    'ffff ffff 00fa '  # 114: Seg[16]=<end>
106    '0100 0108 0110 0118 0120 0128 0130 0138 0140 0148 0150 0158 '
107    '0160 0168 0170 0178 0180 0188 0190 0198 01a0 01a8 01b0 01b8 '
108    '01c0 01c8 01d0 01d8 01e0 01e8 01f0 01f8 0200 0208 0210 0218 '
109    '0220 0228 0230 0238 0240 0248 0250 0258 0260 0268 0270 0278 '
110    '0280 0288 0290 0298 02a0 02a8 02b0 02b8 02c0 02c8 02d0 02d8 '
111    '02e0 02e8 02f0 02f8 0300 0308 0310 0318 fd98 0000 0000 0000 '
112    'fdbc 0000 0000 0000 fdbc 0000 0000 0000 fdbf 0000 0000 0000 '
113    'fdbc 0000 0000 0000 fd98 0000 0000 0000 fda9 0000 0000 0000 '
114    'fd98 0000 0000 0000 fd98 0000 0000 0000 fd98 0000 0000 0000 '
115    '0000 0000 0205 0000 0000 0000 0205 0000 0000 0000 02a4 0000 '
116    '0000 0000 027e 0000 0000 0000 02f4 0000 0000 0000 02a4 0000 '
117    '0000 0000 0365 0000 0000 0000 0291 0000 0000 0000 0291 0000 '
118    '0000 0000 026a 0000 0000 0000 02b8 0000 0000 0000 02cb 0000 '
119    '0000 0000 02a4 0000 0000 0000 01a9 0000 0000 0000 0244 0000 '
120    '0000 0000 02a4 0000 0000 0000 02cb 0000 0000 0000 0244 0000 '
121    '0000 0000 0307 0000 0000 0000 0307 0000 0000 0000 037f 0000 '
122    '0000 0000 0307 0000 0000 0000 0307 0000 0000 0000 0307 0000 '
123    '0000 0000 0307 0000 0000 0000 0307 0000 0000 0000 03e3 0000 '
124    '0000 0000 030c 0000 0000 0000 0307 0000 fe30 0000 0000 0000 '
125    'fe7e 0000 0000 0000 fe91 0000 0000 0000 fe6a 0000 0000 0000 '
126    'fe6a 0000 0000 0000 fecb 0000 0000 0000 fe6a 0000 0000 0000 '
127    'fe7e 0000 0000 0000 fea4 0000 0000 0000 fe7e 0000 0000 0000 '
128    'fe44 0000 0000 0000 fea4 0000 0000 0000 feb8 0000 0000 0000 '
129    'fe7e 0000 0000 0000 fe5e 0000 0000 0000 fe37 0000 0000 0000 '
130    'fe37 0000 0000 0000 fcbd 0000 0000 0000 fd84 0000 0000 0000 '
131    'fd98 0000 0000 0000 fd82 0000 0000 0000 fcbd 0000 0000 0000 '
132    'fd84 0000 0000 0000 fcbd 0000 0000 0000 fcbd 0000 0000 0000 '
133    'fe72 0000 0000 0000 ff9d 0000 0000 0000 0000 0000 032f 0000 '
134    '0000 0000 03ba 0000 '
135)
136assert len(OPBD_APPLE_CHANCERY_DATA) == 800
137
138
139class OPBDTest(unittest.TestCase):
140
141    @classmethod
142    def setUpClass(cls):
143        cls.maxDiff = None
144        glyphs = ['.notdef'] + ['X.alt%d' for g in range(1, 50)]
145        glyphs[10] = 'C'
146        glyphs[43] = 'A'
147        cls.font = FakeFont(glyphs)
148
149    def test_decompile_toXML_format0(self):
150        table = newTable('opbd')
151        table.decompile(OPBD_FORMAT_0_DATA, self.font)
152        self.assertEqual(getXML(table.toXML), OPBD_FORMAT_0_XML)
153
154    def test_compile_fromXML_format0(self):
155        table = newTable('opbd')
156        for name, attrs, content in parseXML(OPBD_FORMAT_0_XML):
157            table.fromXML(name, attrs, content, font=self.font)
158        self.assertEqual(hexStr(table.compile(self.font)),
159                         hexStr(OPBD_FORMAT_0_DATA))
160
161    def test_decompile_toXML_format1(self):
162        table = newTable('opbd')
163        table.decompile(OPBD_FORMAT_1_DATA, self.font)
164        self.assertEqual(getXML(table.toXML), OPBD_FORMAT_1_XML)
165
166    def test_compile_fromXML_format1(self):
167        table = newTable('opbd')
168        for name, attrs, content in parseXML(OPBD_FORMAT_1_XML):
169            table.fromXML(name, attrs, content, font=self.font)
170        self.assertEqual(hexStr(table.compile(self.font)),
171                         hexStr(OPBD_FORMAT_1_DATA))
172
173    def test_decompile_AppleChancery(self):
174        # Make sure we do not crash when decompiling the 'opbd' table of
175        # AppleChancery.ttf. https://github.com/fonttools/fonttools/issues/1031
176        table = newTable('opbd')
177        table.decompile(OPBD_APPLE_CHANCERY_DATA, self.font)
178        self.assertIn('<OpticalBounds Format="0">', getXML(table.toXML))
179
180
181if __name__ == '__main__':
182    import sys
183    sys.exit(unittest.main())
184