diff --git a/Lib/fontTools/ttLib/tables/__init__.py b/Lib/fontTools/ttLib/tables/__init__.py index 0858bd371..92b12d2d2 100644 --- a/Lib/fontTools/ttLib/tables/__init__.py +++ b/Lib/fontTools/ttLib/tables/__init__.py @@ -71,6 +71,7 @@ def _moduleFinderHint(): from . import _m_a_x_p from . import _m_e_t_a from . import _n_a_m_e + from . import _o_p_b_d from . import _p_o_s_t from . import _p_r_e_p from . import _p_r_o_p diff --git a/Lib/fontTools/ttLib/tables/_o_p_b_d.py b/Lib/fontTools/ttLib/tables/_o_p_b_d.py new file mode 100644 index 000000000..60bb6c522 --- /dev/null +++ b/Lib/fontTools/ttLib/tables/_o_p_b_d.py @@ -0,0 +1,8 @@ +from __future__ import print_function, division, absolute_import +from fontTools.misc.py23 import * +from .otBase import BaseTTXConverter + + +# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6opbd.html +class table__o_p_b_d(BaseTTXConverter): + pass diff --git a/Lib/fontTools/ttLib/tables/otData.py b/Lib/fontTools/ttLib/tables/otData.py index 1ef357795..1e1b5c810 100755 --- a/Lib/fontTools/ttLib/tables/otData.py +++ b/Lib/fontTools/ttLib/tables/otData.py @@ -1337,6 +1337,40 @@ otData = [ ('uint16', 'Format', None, None, 'Format, = 1.'), ('uint16', 'DefaultProperties', None, None, 'Default properties applied to a glyph if that glyph is not present in the Properties lookup table.'), ('AATLookup(uint16)', 'Properties', None, None, 'Lookup data associating glyphs with their properties.'), + ]), + + + # + # opbd + # + + ('opbd', [ + ('Version', 'Version', None, None, 'Version number of the optical bounds table (0x00010000 for the initial version).'), + ('struct', 'OpticalBounds', None, None, 'Optical bounds table.'), + ]), + + ('OpticalBoundsFormat0', [ + ('uint16', 'Format', None, None, 'Format of the optical bounds table, = 0.'), + ('AATLookup(OpticalBoundsDeltas)', 'OpticalBoundsDeltas', None, None, 'Lookup table associating glyphs with their optical bounds, given as deltas in font units.'), + ]), + + ('OpticalBoundsFormat1', [ + ('uint16', 'Format', None, None, 'Format of the optical bounds table, = 1.'), + ('AATLookup(OpticalBoundsPoints)', 'OpticalBoundsPoints', None, None, 'Lookup table associating glyphs with their optical bounds, given as references to control points.'), + ]), + + ('OpticalBoundsDeltas', [ + ('int16', 'Left', None, None, 'Delta value for the left-side optical edge.'), + ('int16', 'Top', None, None, 'Delta value for the top-side optical edge.'), + ('int16', 'Right', None, None, 'Delta value for the right-side optical edge.'), + ('int16', 'Bottom', None, None, 'Delta value for the bottom-side optical edge.'), + ]), + + ('OpticalBoundsPoints', [ + ('int16', 'Left', None, None, 'Control point index for the left-side optical edge, or -1 if this glyph has none.'), + ('int16', 'Top', None, None, 'Control point index for the top-side optical edge, or -1 if this glyph has none.'), + ('int16', 'Right', None, None, 'Control point index for the right-side optical edge, or -1 if this glyph has none.'), + ('int16', 'Bottom', None, None, 'Control point index for the bottom-side optical edge, or -1 if this glyph has none.'), ]), ] diff --git a/README.rst b/README.rst index a79a1967f..d27a33c27 100644 --- a/README.rst +++ b/README.rst @@ -102,8 +102,8 @@ The following tables are currently supported: OS/2, SING, STAT, SVG, TSI0, TSI1, TSI2, TSI3, TSI5, TSIB, TSID, TSIJ, TSIP, TSIS, TSIV, TTFA, VDMX, VORG, VVAR, avar, cmap, cvar, cvt, feat, fpgm, fvar, gasp, glyf, gvar, hdmx, head, hhea, hmtx, - kern, lcar, loca, ltag, maxp, meta, name, post, prep, prop, sbix, - trak, vhea and vmtx + kern, lcar, loca, ltag, maxp, meta, name, opbd, post, prep, prop, + sbix, trak, vhea and vmtx .. end table list Other tables are dumped as hexadecimal data. diff --git a/Tests/ttLib/tables/_o_p_b_d_test.py b/Tests/ttLib/tables/_o_p_b_d_test.py new file mode 100644 index 000000000..55d5126d8 --- /dev/null +++ b/Tests/ttLib/tables/_o_p_b_d_test.py @@ -0,0 +1,118 @@ +from __future__ import print_function, division, absolute_import, unicode_literals +from fontTools.misc.py23 import * +from fontTools.misc.testTools import FakeFont, getXML, parseXML +from fontTools.misc.textTools import deHexStr, hexStr +from fontTools.ttLib import newTable +import unittest + + +# Example: Format 0 Optical Bounds Table +# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6opbd.html +OPBD_FORMAT_0_DATA = deHexStr( + '0001 0000 0000 ' # 0: Version=1.0, Format=0 + '0006 0004 0002 ' # 6: LookupFormat=6, UnitSize=4, NUnits=2 + '0008 0001 0000 ' # 12: SearchRange=8, EntrySelector=1, RangeShift=0 + '000A 001E ' # 18: Glyph=10(=C), OffsetOfOpticalBoundsDeltas=30 + '002B 0026 ' # 22: Glyph=43(=A), OffsetOfOpticalBoundsDeltas=38 + 'FFFF 0000 ' # 26: Glyph=, OffsetOfOpticalBoundsDeltas=0 + 'FFCE 0005 0037 FFFB ' # 30: Bounds[C].Left=-50 .Top=5 .Right=55 .Bottom=-5 + 'FFF6 000F 0000 0000 ' # 38: Bounds[A].Left=-10 .Top=15 .Right=0 .Bottom=0 +) # 46: +assert(len(OPBD_FORMAT_0_DATA) == 46) + + +OPBD_FORMAT_0_XML = [ + '', + '', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + '', +] + + +# Example: Format 1 Optical Bounds Table +# https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6opbd.html +OPBD_FORMAT_1_DATA = deHexStr( + '0001 0000 0001 ' # 0: Version=1.0, Format=1 + '0006 0004 0002 ' # 6: LookupFormat=6, UnitSize=4, NUnits=2 + '0008 0001 0000 ' # 12: SearchRange=8, EntrySelector=1, RangeShift=0 + '000A 001E ' # 18: Glyph=10(=C), OffsetOfOpticalBoundsPoints=30 + '002B 0026 ' # 22: Glyph=43(=A), OffsetOfOpticalBoundsPoints=38 + 'FFFF 0000 ' # 26: Glyph=, OffsetOfOpticalBoundsPoints=0 + '0024 0025 0026 0027 ' # 30: Bounds[C].Left=36 .Top=37 .Right=38 .Bottom=39 + '0020 0029 FFFF FFFF ' # 38: Bounds[A].Left=32 .Top=41 .Right=-1 .Bottom=-1 +) # 46: +assert(len(OPBD_FORMAT_1_DATA) == 46) + + +OPBD_FORMAT_1_XML = [ + '', + '', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + ' ', + '', +] + + +class OPBDTest(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.maxDiff = None + glyphs = ['.notdef'] + ['X.alt%d' for g in range(1, 50)] + glyphs[10] = 'C' + glyphs[43] = 'A' + cls.font = FakeFont(glyphs) + + def test_decompile_toXML_format0(self): + table = newTable('opbd') + table.decompile(OPBD_FORMAT_0_DATA, self.font) + self.assertEqual(getXML(table.toXML), OPBD_FORMAT_0_XML) + + def test_compile_fromXML_format0(self): + table = newTable('opbd') + for name, attrs, content in parseXML(OPBD_FORMAT_0_XML): + table.fromXML(name, attrs, content, font=self.font) + self.assertEqual(hexStr(table.compile(self.font)), + hexStr(OPBD_FORMAT_0_DATA)) + + def test_decompile_toXML_format1(self): + table = newTable('opbd') + table.decompile(OPBD_FORMAT_1_DATA, self.font) + self.assertEqual(getXML(table.toXML), OPBD_FORMAT_1_XML) + + def test_compile_fromXML_format1(self): + table = newTable('opbd') + for name, attrs, content in parseXML(OPBD_FORMAT_1_XML): + table.fromXML(name, attrs, content, font=self.font) + self.assertEqual(hexStr(table.compile(self.font)), + hexStr(OPBD_FORMAT_1_DATA)) + + +if __name__ == '__main__': + import sys + sys.exit(unittest.main())