diff options
Diffstat (limited to 'Tests/pens/cu2quPen_test.py')
-rw-r--r-- | Tests/pens/cu2quPen_test.py | 386 |
1 files changed, 210 insertions, 176 deletions
diff --git a/Tests/pens/cu2quPen_test.py b/Tests/pens/cu2quPen_test.py index 4ce5b512..779254c3 100644 --- a/Tests/pens/cu2quPen_test.py +++ b/Tests/pens/cu2quPen_test.py @@ -15,13 +15,19 @@ import sys import unittest -from fontTools.pens.cu2quPen import Cu2QuPen, Cu2QuPointPen -from . import CUBIC_GLYPHS, QUAD_GLYPHS -from .utils import DummyGlyph, DummyPointGlyph -from .utils import DummyPen, DummyPointPen +from fontTools.pens.cu2quPen import Cu2QuPen, Cu2QuPointPen, Cu2QuMultiPen +from fontTools.pens.recordingPen import RecordingPen, RecordingPointPen from fontTools.misc.loggingTools import CapturingLogHandler from textwrap import dedent import logging +import pytest + +try: + from .utils import CUBIC_GLYPHS, QUAD_GLYPHS + from .utils import DummyGlyph, DummyPointGlyph + from .utils import DummyPen, DummyPointPen +except ImportError as e: + pytest.skip(str(e), allow_module_level=True) MAX_ERR = 1.0 @@ -36,10 +42,12 @@ class _TestPenMixin(object): def diff(self, expected, actual): import difflib + expected = str(self.Glyph(expected)).splitlines(True) actual = str(self.Glyph(actual)).splitlines(True) diff = difflib.unified_diff( - expected, actual, fromfile='expected', tofile='actual') + expected, actual, fromfile="expected", tofile="actual" + ) return "".join(diff) def convert_glyph(self, glyph, **kwargs): @@ -58,28 +66,27 @@ class _TestPenMixin(object): self.fail("converted glyph is different from expected") def test_convert_simple_glyph(self): - self.expect_glyph(CUBIC_GLYPHS['a'], QUAD_GLYPHS['a']) - self.expect_glyph(CUBIC_GLYPHS['A'], QUAD_GLYPHS['A']) + self.expect_glyph(CUBIC_GLYPHS["a"], QUAD_GLYPHS["a"]) + self.expect_glyph(CUBIC_GLYPHS["A"], QUAD_GLYPHS["A"]) def test_convert_composite_glyph(self): - source = CUBIC_GLYPHS['Aacute'] + source = CUBIC_GLYPHS["Aacute"] converted = self.convert_glyph(source) # components don't change after quadratic conversion self.assertEqual(converted, source) def test_convert_mixed_glyph(self): # this contains a mix of contours and components - self.expect_glyph(CUBIC_GLYPHS['Eacute'], QUAD_GLYPHS['Eacute']) + self.expect_glyph(CUBIC_GLYPHS["Eacute"], QUAD_GLYPHS["Eacute"]) def test_reverse_direction(self): - for name in ('a', 'A', 'Eacute'): + for name in ("a", "A", "Eacute"): source = CUBIC_GLYPHS[name] normal_glyph = self.convert_glyph(source) reversed_glyph = self.convert_glyph(source, reverse_direction=True) # the number of commands is the same, just their order is iverted - self.assertTrue( - len(normal_glyph.outline), len(reversed_glyph.outline)) + self.assertTrue(len(normal_glyph.outline), len(reversed_glyph.outline)) self.assertNotEqual(normal_glyph, reversed_glyph) def test_stats(self): @@ -89,8 +96,8 @@ class _TestPenMixin(object): self.convert_glyph(source, stats=stats) self.assertTrue(stats) - self.assertTrue('1' in stats) - self.assertEqual(type(stats['1']), int) + self.assertTrue("1" in stats) + self.assertEqual(type(stats["1"]), int) def test_addComponent(self): pen = self.Pen() @@ -98,65 +105,22 @@ class _TestPenMixin(object): quadpen.addComponent("a", (1, 2, 3, 4, 5.0, 6.0)) # components are passed through without changes - self.assertEqual(str(pen).splitlines(), [ - "pen.addComponent('a', (1, 2, 3, 4, 5.0, 6.0))", - ]) + self.assertEqual( + str(pen).splitlines(), + [ + "pen.addComponent('a', (1, 2, 3, 4, 5.0, 6.0))", + ], + ) class TestCu2QuPen(unittest.TestCase, _TestPenMixin): - def __init__(self, *args, **kwargs): super(TestCu2QuPen, self).__init__(*args, **kwargs) self.Glyph = DummyGlyph self.Pen = DummyPen self.Cu2QuPen = Cu2QuPen - self.pen_getter_name = 'getPen' - self.draw_method_name = 'draw' - - def test__check_contour_is_open(self): - msg = "moveTo is required" - quadpen = Cu2QuPen(DummyPen(), MAX_ERR) - - with self.assertRaisesRegex(AssertionError, msg): - quadpen.lineTo((0, 0)) - with self.assertRaisesRegex(AssertionError, msg): - quadpen.qCurveTo((0, 0), (1, 1)) - with self.assertRaisesRegex(AssertionError, msg): - quadpen.curveTo((0, 0), (1, 1), (2, 2)) - with self.assertRaisesRegex(AssertionError, msg): - quadpen.closePath() - with self.assertRaisesRegex(AssertionError, msg): - quadpen.endPath() - - quadpen.moveTo((0, 0)) # now it works - quadpen.lineTo((1, 1)) - quadpen.qCurveTo((2, 2), (3, 3)) - quadpen.curveTo((4, 4), (5, 5), (6, 6)) - quadpen.closePath() - - def test__check_contour_closed(self): - msg = "closePath or endPath is required" - quadpen = Cu2QuPen(DummyPen(), MAX_ERR) - quadpen.moveTo((0, 0)) - - with self.assertRaisesRegex(AssertionError, msg): - quadpen.moveTo((1, 1)) - with self.assertRaisesRegex(AssertionError, msg): - quadpen.addComponent("a", (1, 0, 0, 1, 0, 0)) - - # it works if contour is closed - quadpen.closePath() - quadpen.moveTo((1, 1)) - quadpen.endPath() - quadpen.addComponent("a", (1, 0, 0, 1, 0, 0)) - - def test_qCurveTo_no_points(self): - quadpen = Cu2QuPen(DummyPen(), MAX_ERR) - quadpen.moveTo((0, 0)) - - with self.assertRaisesRegex( - AssertionError, "illegal qcurve segment point count: 0"): - quadpen.qCurveTo() + self.pen_getter_name = "getPen" + self.draw_method_name = "draw" def test_qCurveTo_1_point(self): pen = DummyPen() @@ -164,10 +128,13 @@ class TestCu2QuPen(unittest.TestCase, _TestPenMixin): quadpen.moveTo((0, 0)) quadpen.qCurveTo((1, 1)) - self.assertEqual(str(pen).splitlines(), [ - "pen.moveTo((0, 0))", - "pen.lineTo((1, 1))", - ]) + self.assertEqual( + str(pen).splitlines(), + [ + "pen.moveTo((0, 0))", + "pen.qCurveTo((1, 1))", + ], + ) def test_qCurveTo_more_than_1_point(self): pen = DummyPen() @@ -175,18 +142,13 @@ class TestCu2QuPen(unittest.TestCase, _TestPenMixin): quadpen.moveTo((0, 0)) quadpen.qCurveTo((1, 1), (2, 2)) - self.assertEqual(str(pen).splitlines(), [ - "pen.moveTo((0, 0))", - "pen.qCurveTo((1, 1), (2, 2))", - ]) - - def test_curveTo_no_points(self): - quadpen = Cu2QuPen(DummyPen(), MAX_ERR) - quadpen.moveTo((0, 0)) - - with self.assertRaisesRegex( - AssertionError, "illegal curve segment point count: 0"): - quadpen.curveTo() + self.assertEqual( + str(pen).splitlines(), + [ + "pen.moveTo((0, 0))", + "pen.qCurveTo((1, 1), (2, 2))", + ], + ) def test_curveTo_1_point(self): pen = DummyPen() @@ -194,10 +156,13 @@ class TestCu2QuPen(unittest.TestCase, _TestPenMixin): quadpen.moveTo((0, 0)) quadpen.curveTo((1, 1)) - self.assertEqual(str(pen).splitlines(), [ - "pen.moveTo((0, 0))", - "pen.lineTo((1, 1))", - ]) + self.assertEqual( + str(pen).splitlines(), + [ + "pen.moveTo((0, 0))", + "pen.qCurveTo((1, 1))", + ], + ) def test_curveTo_2_points(self): pen = DummyPen() @@ -205,10 +170,13 @@ class TestCu2QuPen(unittest.TestCase, _TestPenMixin): quadpen.moveTo((0, 0)) quadpen.curveTo((1, 1), (2, 2)) - self.assertEqual(str(pen).splitlines(), [ - "pen.moveTo((0, 0))", - "pen.qCurveTo((1, 1), (2, 2))", - ]) + self.assertEqual( + str(pen).splitlines(), + [ + "pen.moveTo((0, 0))", + "pen.qCurveTo((1, 1), (2, 2))", + ], + ) def test_curveTo_3_points(self): pen = DummyPen() @@ -216,10 +184,13 @@ class TestCu2QuPen(unittest.TestCase, _TestPenMixin): quadpen.moveTo((0, 0)) quadpen.curveTo((1, 1), (2, 2), (3, 3)) - self.assertEqual(str(pen).splitlines(), [ - "pen.moveTo((0, 0))", - "pen.qCurveTo((0.75, 0.75), (2.25, 2.25), (3, 3))", - ]) + self.assertEqual( + str(pen).splitlines(), + [ + "pen.moveTo((0, 0))", + "pen.qCurveTo((0.75, 0.75), (2.25, 2.25), (3, 3))", + ], + ) def test_curveTo_more_than_3_points(self): # a 'SuperBezier' as described in fontTools.basePen.AbstractPen @@ -228,71 +199,24 @@ class TestCu2QuPen(unittest.TestCase, _TestPenMixin): quadpen.moveTo((0, 0)) quadpen.curveTo((1, 1), (2, 2), (3, 3), (4, 4)) - self.assertEqual(str(pen).splitlines(), [ - "pen.moveTo((0, 0))", - "pen.qCurveTo((0.75, 0.75), (1.625, 1.625), (2, 2))", - "pen.qCurveTo((2.375, 2.375), (3.25, 3.25), (4, 4))", - ]) - - def test_addComponent(self): - pen = DummyPen() - quadpen = Cu2QuPen(pen, MAX_ERR) - quadpen.addComponent("a", (1, 2, 3, 4, 5.0, 6.0)) - - # components are passed through without changes - self.assertEqual(str(pen).splitlines(), [ - "pen.addComponent('a', (1, 2, 3, 4, 5.0, 6.0))", - ]) - - def test_ignore_single_points(self): - pen = DummyPen() - try: - logging.captureWarnings(True) - with CapturingLogHandler("py.warnings", level="WARNING") as log: - quadpen = Cu2QuPen(pen, MAX_ERR, ignore_single_points=True) - finally: - logging.captureWarnings(False) - quadpen.moveTo((0, 0)) - quadpen.endPath() - quadpen.moveTo((1, 1)) - quadpen.closePath() - - self.assertGreaterEqual(len(log.records), 1) - if sys.version_info < (3, 11): - self.assertIn("ignore_single_points is deprecated", - log.records[0].args[0]) - else: - self.assertIn("ignore_single_points is deprecated", - log.records[0].msg) - - # single-point contours were ignored, so the pen commands are empty - self.assertFalse(pen.commands) - - # redraw without ignoring single points - quadpen.ignore_single_points = False - quadpen.moveTo((0, 0)) - quadpen.endPath() - quadpen.moveTo((1, 1)) - quadpen.closePath() - - self.assertTrue(pen.commands) - self.assertEqual(str(pen).splitlines(), [ - "pen.moveTo((0, 0))", - "pen.endPath()", - "pen.moveTo((1, 1))", - "pen.closePath()" - ]) + self.assertEqual( + str(pen).splitlines(), + [ + "pen.moveTo((0, 0))", + "pen.qCurveTo((0.75, 0.75), (1.625, 1.625), (2, 2))", + "pen.qCurveTo((2.375, 2.375), (3.25, 3.25), (4, 4))", + ], + ) class TestCu2QuPointPen(unittest.TestCase, _TestPenMixin): - def __init__(self, *args, **kwargs): super(TestCu2QuPointPen, self).__init__(*args, **kwargs) self.Glyph = DummyPointGlyph self.Pen = DummyPointPen self.Cu2QuPen = Cu2QuPointPen - self.pen_getter_name = 'getPointPen' - self.draw_method_name = 'drawPoints' + self.pen_getter_name = "getPointPen" + self.draw_method_name = "drawPoints" def test_super_bezier_curve(self): pen = DummyPointPen() @@ -303,10 +227,13 @@ class TestCu2QuPointPen(unittest.TestCase, _TestPenMixin): quadpen.addPoint((2, 2)) quadpen.addPoint((3, 3)) quadpen.addPoint( - (4, 4), segmentType="curve", smooth=False, name="up", selected=1) + (4, 4), segmentType="curve", smooth=False, name="up", selected=1 + ) quadpen.endPath() - self.assertEqual(str(pen).splitlines(), """\ + self.assertEqual( + str(pen).splitlines(), + """\ pen.beginPath() pen.addPoint((0, 0), name=None, segmentType='move', smooth=False) pen.addPoint((0.75, 0.75), name=None, segmentType=None, smooth=False) @@ -315,7 +242,8 @@ pen.addPoint((2, 2), name=None, segmentType='qcurve', smooth=True) pen.addPoint((2.375, 2.375), name=None, segmentType=None, smooth=False) pen.addPoint((3.25, 3.25), name=None, segmentType=None, smooth=False) pen.addPoint((4, 4), name='up', segmentType='qcurve', selected=1, smooth=False) -pen.endPath()""".splitlines()) +pen.endPath()""".splitlines(), + ) def test__flushContour_restore_starting_point(self): pen = DummyPointPen() @@ -323,24 +251,34 @@ pen.endPath()""".splitlines()) # collect the output of _flushContour before it's sent to _drawPoints new_segments = [] + def _drawPoints(segments): new_segments.extend(segments) Cu2QuPointPen._drawPoints(quadpen, segments) + quadpen._drawPoints = _drawPoints # a closed path (ie. no "move" segmentType) - quadpen._flushContour([ - ("curve", [ - ((2, 2), False, None, {}), - ((1, 1), False, None, {}), - ((0, 0), False, None, {}), - ]), - ("curve", [ - ((1, 1), False, None, {}), - ((2, 2), False, None, {}), - ((3, 3), False, None, {}), - ]), - ]) + quadpen._flushContour( + [ + ( + "curve", + [ + ((2, 2), False, None, {}), + ((1, 1), False, None, {}), + ((0, 0), False, None, {}), + ], + ), + ( + "curve", + [ + ((1, 1), False, None, {}), + ((2, 2), False, None, {}), + ((3, 3), False, None, {}), + ], + ), + ] + ) # the original starting point is restored: the last segment has become # the first @@ -349,16 +287,24 @@ pen.endPath()""".splitlines()) new_segments = [] # an open path (ie. starting with "move") - quadpen._flushContour([ - ("move", [ - ((0, 0), False, None, {}), - ]), - ("curve", [ - ((1, 1), False, None, {}), - ((2, 2), False, None, {}), - ((3, 3), False, None, {}), - ]), - ]) + quadpen._flushContour( + [ + ( + "move", + [ + ((0, 0), False, None, {}), + ], + ), + ( + "curve", + [ + ((1, 1), False, None, {}), + ((2, 2), False, None, {}), + ((3, 3), False, None, {}), + ], + ), + ] + ) # the segment order stays the same before and after _flushContour self.assertEqual(new_segments[0][1][-1][0], (0, 0)) @@ -387,9 +333,97 @@ pen.endPath()""".splitlines()) pen.addPoint((2, 2), name=None, segmentType=None, smooth=False) pen.addPoint((3, 3), name=None, segmentType=None, smooth=False) pen.endPath()""" - ) + ), ) +class TestCu2QuMultiPen(unittest.TestCase): + def test_multi_pen(self): + pens = [RecordingPen(), RecordingPen()] + pen = Cu2QuMultiPen(pens, 0.1) + pen.moveTo([((0, 0),), ((0, 0),)]) + pen.lineTo([((0, 1),), ((0, 1),)]) + pen.qCurveTo([((0, 2),), ((0, 2),)]) + pen.qCurveTo([((0, 3), (1, 3)), ((0, 3), (1, 4))]) + pen.curveTo([((2, 3), (0, 3), (0, 0)), ((1.1, 4), (0, 4), (0, 0))]) + pen.closePath() + + assert len(pens[0].value) == 6 + assert len(pens[1].value) == 6 + + for op0, op1 in zip(pens[0].value, pens[1].value): + assert op0[0] == op0[0] + assert op0[0] != "curveTo" + + +class TestAllQuadraticFalse(unittest.TestCase): + def test_segment_pen_cubic(self): + rpen = RecordingPen() + pen = Cu2QuPen(rpen, 0.1, all_quadratic=False) + + pen.moveTo((0, 0)) + pen.curveTo((0, 1), (2, 1), (2, 0)) + pen.closePath() + + assert rpen.value == [ + ("moveTo", ((0, 0),)), + ("curveTo", ((0, 1), (2, 1), (2, 0))), + ("closePath", ()), + ] + + def test_segment_pen_quadratic(self): + rpen = RecordingPen() + pen = Cu2QuPen(rpen, 0.1, all_quadratic=False) + + pen.moveTo((0, 0)) + pen.curveTo((2, 2), (4, 2), (6, 0)) + pen.closePath() + + assert rpen.value == [ + ("moveTo", ((0, 0),)), + ("qCurveTo", ((3, 3), (6, 0))), + ("closePath", ()), + ] + + def test_point_pen_cubic(self): + rpen = RecordingPointPen() + pen = Cu2QuPointPen(rpen, 0.1, all_quadratic=False) + + pen.beginPath() + pen.addPoint((0, 0), "move") + pen.addPoint((0, 1)) + pen.addPoint((2, 1)) + pen.addPoint((2, 0), "curve") + pen.endPath() + + assert rpen.value == [ + ("beginPath", (), {}), + ("addPoint", ((0, 0), "move", False, None), {}), + ("addPoint", ((0, 1), None, False, None), {}), + ("addPoint", ((2, 1), None, False, None), {}), + ("addPoint", ((2, 0), "curve", False, None), {}), + ("endPath", (), {}), + ] + + def test_point_pen_quadratic(self): + rpen = RecordingPointPen() + pen = Cu2QuPointPen(rpen, 0.1, all_quadratic=False) + + pen.beginPath() + pen.addPoint((0, 0), "move") + pen.addPoint((2, 2)) + pen.addPoint((4, 2)) + pen.addPoint((6, 0), "curve") + pen.endPath() + + assert rpen.value == [ + ("beginPath", (), {}), + ("addPoint", ((0, 0), "move", False, None), {}), + ("addPoint", ((3, 3), None, False, None), {}), + ("addPoint", ((6, 0), "qcurve", False, None), {}), + ("endPath", (), {}), + ] + + if __name__ == "__main__": unittest.main() |