From ac2413e905fd7d634690de12dcbd460b09c30ee1 Mon Sep 17 00:00:00 2001 From: justvanrossum Date: Sun, 17 Mar 2019 15:30:20 +0100 Subject: [PATCH 1/3] [ttLib/glyf] raise TTLibError with the offending glyph name in the error message when a component (indirectly) references itself --- Lib/fontTools/ttLib/tables/_g_l_y_f.py | 5 ++++- Tests/ttLib/tables/_g_l_y_f_test.py | 27 +++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/Lib/fontTools/ttLib/tables/_g_l_y_f.py b/Lib/fontTools/ttLib/tables/_g_l_y_f.py index 8b3605047..148d7ec00 100644 --- a/Lib/fontTools/ttLib/tables/_g_l_y_f.py +++ b/Lib/fontTools/ttLib/tables/_g_l_y_f.py @@ -841,7 +841,10 @@ class Glyph(object): allEndPts = [] for compo in self.components: g = glyfTable[compo.glyphName] - coordinates, endPts, flags = g.getCoordinates(glyfTable) + try: + coordinates, endPts, flags = g.getCoordinates(glyfTable) + except RuntimeError: + raise ttLib.TTLibError("glyph '%s' contains recursive component reference" % compo.glyphName) if hasattr(compo, "firstPt"): # move according to two reference points x1,y1 = allCoords[compo.firstPt] diff --git a/Tests/ttLib/tables/_g_l_y_f_test.py b/Tests/ttLib/tables/_g_l_y_f_test.py index d07869241..e3d09b7e1 100644 --- a/Tests/ttLib/tables/_g_l_y_f_test.py +++ b/Tests/ttLib/tables/_g_l_y_f_test.py @@ -1,7 +1,8 @@ from __future__ import print_function, division, absolute_import from fontTools.misc.py23 import * from fontTools.misc.fixedTools import otRound -from fontTools.ttLib import TTFont, newTable +from fontTools.pens.ttGlyphPen import TTGlyphPen +from fontTools.ttLib import TTFont, newTable, TTLibError from fontTools.ttLib.tables._g_l_y_f import GlyphCoordinates import sys import array @@ -180,6 +181,13 @@ def strip_ttLibVersion(string): class glyfTableTest(unittest.TestCase): + def __init__(self, methodName): + unittest.TestCase.__init__(self, methodName) + # Python 3 renamed assertRaisesRegexp to assertRaisesRegex, + # and fires deprecation warnings if a program uses the old name. + if not hasattr(self, "assertRaisesRegex"): + self.assertRaisesRegex = self.assertRaisesRegexp + @classmethod def setUpClass(cls): with open(GLYF_BIN, 'rb') as f: @@ -215,6 +223,23 @@ class glyfTableTest(unittest.TestCase): glyfData = glyfTable.compile(font) self.assertEqual(glyfData, self.glyfData) + def test_recursiveComponent(self): + glyphSet = {} + pen_dummy = TTGlyphPen(glyphSet) + glyph_dummy = pen_dummy.glyph() + glyphSet["A"] = glyph_dummy + glyphSet["B"] = glyph_dummy + pen_A = TTGlyphPen(glyphSet) + pen_A.addComponent("B", (1, 0, 0, 1, 0, 0)) + pen_B = TTGlyphPen(glyphSet) + pen_B.addComponent("A", (1, 0, 0, 1, 0, 0)) + glyph_A = pen_A.glyph() + glyph_B = pen_B.glyph() + glyphSet["A"] = glyph_A + glyphSet["B"] = glyph_B + with self.assertRaisesRegex(TTLibError, "glyph '.' contains recursive component reference"): + glyph_A.getCoordinates(glyphSet) + if __name__ == "__main__": import sys From 12ec7f539eab2a54d454c0d980eca396f53a3f61 Mon Sep 17 00:00:00 2001 From: justvanrossum Date: Sun, 17 Mar 2019 15:35:35 +0100 Subject: [PATCH 2/3] improve error message --- Lib/fontTools/ttLib/tables/_g_l_y_f.py | 2 +- Tests/ttLib/tables/_g_l_y_f_test.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/fontTools/ttLib/tables/_g_l_y_f.py b/Lib/fontTools/ttLib/tables/_g_l_y_f.py index 148d7ec00..4a629230e 100644 --- a/Lib/fontTools/ttLib/tables/_g_l_y_f.py +++ b/Lib/fontTools/ttLib/tables/_g_l_y_f.py @@ -844,7 +844,7 @@ class Glyph(object): try: coordinates, endPts, flags = g.getCoordinates(glyfTable) except RuntimeError: - raise ttLib.TTLibError("glyph '%s' contains recursive component reference" % compo.glyphName) + raise ttLib.TTLibError("glyph '%s' contains a recursive component reference" % compo.glyphName) if hasattr(compo, "firstPt"): # move according to two reference points x1,y1 = allCoords[compo.firstPt] diff --git a/Tests/ttLib/tables/_g_l_y_f_test.py b/Tests/ttLib/tables/_g_l_y_f_test.py index e3d09b7e1..00c74bcf5 100644 --- a/Tests/ttLib/tables/_g_l_y_f_test.py +++ b/Tests/ttLib/tables/_g_l_y_f_test.py @@ -237,7 +237,7 @@ class glyfTableTest(unittest.TestCase): glyph_B = pen_B.glyph() glyphSet["A"] = glyph_A glyphSet["B"] = glyph_B - with self.assertRaisesRegex(TTLibError, "glyph '.' contains recursive component reference"): + with self.assertRaisesRegex(TTLibError, "glyph '.' contains a recursive component reference"): glyph_A.getCoordinates(glyphSet) From 20c93b9fdb005e89ffd39240adb2ba39e1f9762f Mon Sep 17 00:00:00 2001 From: justvanrossum Date: Sun, 17 Mar 2019 18:18:10 +0100 Subject: [PATCH 3/3] use RecursionError, and provide an alias for RuntimeError for Py2 --- Lib/fontTools/misc/py23.py | 8 +++++++- Lib/fontTools/ttLib/tables/_g_l_y_f.py | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Lib/fontTools/misc/py23.py b/Lib/fontTools/misc/py23.py index 37020555e..5cddfebca 100644 --- a/Lib/fontTools/misc/py23.py +++ b/Lib/fontTools/misc/py23.py @@ -7,7 +7,7 @@ import sys __all__ = ['basestring', 'unicode', 'unichr', 'byteord', 'bytechr', 'BytesIO', 'StringIO', 'UnicodeIO', 'strjoin', 'bytesjoin', 'tobytes', 'tostr', 'tounicode', 'Tag', 'open', 'range', 'xrange', 'round', 'Py23Error', - 'SimpleNamespace', 'zip'] + 'SimpleNamespace', 'zip', 'RecursionError'] class Py23Error(NotImplementedError): @@ -514,6 +514,12 @@ else: _stream = "stderr" +try: + RecursionError = RecursionError +except NameError: + RecursionError = RuntimeError + + if __name__ == "__main__": import doctest, sys sys.exit(doctest.testmod().failed) diff --git a/Lib/fontTools/ttLib/tables/_g_l_y_f.py b/Lib/fontTools/ttLib/tables/_g_l_y_f.py index 4a629230e..b8020ca7b 100644 --- a/Lib/fontTools/ttLib/tables/_g_l_y_f.py +++ b/Lib/fontTools/ttLib/tables/_g_l_y_f.py @@ -843,7 +843,7 @@ class Glyph(object): g = glyfTable[compo.glyphName] try: coordinates, endPts, flags = g.getCoordinates(glyfTable) - except RuntimeError: + except RecursionError: raise ttLib.TTLibError("glyph '%s' contains a recursive component reference" % compo.glyphName) if hasattr(compo, "firstPt"): # move according to two reference points