There is no need to install the test suite with the package. Let's put it outside like it's recommended by pytest.
337 lines
11 KiB
Python
337 lines
11 KiB
Python
from __future__ import print_function, division, absolute_import
|
|
import unittest
|
|
|
|
from cu2qu.pens import Cu2QuPen, Cu2QuPointPen
|
|
from . import CUBIC_GLYPHS, QUAD_GLYPHS
|
|
from .utils import DummyGlyph, DummyPointGlyph
|
|
from .utils import DummyPen, DummyPointPen
|
|
|
|
MAX_ERR = 1.0
|
|
|
|
|
|
class _TestPenMixin(object):
|
|
"""Collection of tests that are shared by both the SegmentPen and the
|
|
PointPen test cases, plus some helper methods.
|
|
"""
|
|
|
|
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')
|
|
return "".join(diff)
|
|
|
|
def convert_glyph(self, glyph, **kwargs):
|
|
# draw source glyph onto a new glyph using a Cu2Qu pen and return it
|
|
converted = self.Glyph()
|
|
pen = getattr(converted, self.pen_getter_name)()
|
|
quadpen = self.Cu2QuPen(pen, MAX_ERR, **kwargs)
|
|
getattr(glyph, self.draw_method_name)(quadpen)
|
|
return converted
|
|
|
|
def expect_glyph(self, source, expected):
|
|
converted = self.convert_glyph(source)
|
|
self.assertNotEqual(converted, source)
|
|
if converted != expected:
|
|
print(self.diff(expected, converted))
|
|
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'])
|
|
|
|
def test_convert_composite_glyph(self):
|
|
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'])
|
|
|
|
def test_reverse_direction(self):
|
|
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.assertNotEqual(normal_glyph, reversed_glyph)
|
|
|
|
def test_stats(self):
|
|
stats = {}
|
|
for name in CUBIC_GLYPHS.keys():
|
|
source = CUBIC_GLYPHS[name]
|
|
self.convert_glyph(source, stats=stats)
|
|
|
|
self.assertTrue(stats)
|
|
self.assertTrue('1' in stats)
|
|
self.assertEqual(type(stats['1']), int)
|
|
|
|
def test_addComponent(self):
|
|
pen = self.Pen()
|
|
quadpen = self.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))",
|
|
])
|
|
|
|
|
|
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()
|
|
|
|
def test_qCurveTo_1_point(self):
|
|
pen = DummyPen()
|
|
quadpen = Cu2QuPen(pen, MAX_ERR)
|
|
quadpen.moveTo((0, 0))
|
|
quadpen.qCurveTo((1, 1))
|
|
|
|
self.assertEqual(str(pen).splitlines(), [
|
|
"pen.moveTo((0, 0))",
|
|
"pen.lineTo((1, 1))",
|
|
])
|
|
|
|
def test_qCurveTo_more_than_1_point(self):
|
|
pen = DummyPen()
|
|
quadpen = Cu2QuPen(pen, MAX_ERR)
|
|
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()
|
|
|
|
def test_curveTo_1_point(self):
|
|
pen = DummyPen()
|
|
quadpen = Cu2QuPen(pen, MAX_ERR)
|
|
quadpen.moveTo((0, 0))
|
|
quadpen.curveTo((1, 1))
|
|
|
|
self.assertEqual(str(pen).splitlines(), [
|
|
"pen.moveTo((0, 0))",
|
|
"pen.lineTo((1, 1))",
|
|
])
|
|
|
|
def test_curveTo_2_points(self):
|
|
pen = DummyPen()
|
|
quadpen = Cu2QuPen(pen, MAX_ERR)
|
|
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))",
|
|
])
|
|
|
|
def test_curveTo_3_points(self):
|
|
pen = DummyPen()
|
|
quadpen = Cu2QuPen(pen, MAX_ERR)
|
|
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))",
|
|
])
|
|
|
|
def test_curveTo_more_than_3_points(self):
|
|
# a 'SuperBezier' as described in fontTools.basePen.AbstractPen
|
|
pen = DummyPen()
|
|
quadpen = Cu2QuPen(pen, MAX_ERR)
|
|
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()
|
|
quadpen = Cu2QuPen(pen, MAX_ERR, ignore_single_points=True)
|
|
quadpen.moveTo((0, 0))
|
|
quadpen.endPath()
|
|
quadpen.moveTo((1, 1))
|
|
quadpen.closePath()
|
|
|
|
# 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()"
|
|
])
|
|
|
|
|
|
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'
|
|
|
|
def test_super_bezier_curve(self):
|
|
pen = DummyPointPen()
|
|
quadpen = Cu2QuPointPen(pen, MAX_ERR)
|
|
quadpen.beginPath()
|
|
quadpen.addPoint((0, 0), segmentType="move")
|
|
quadpen.addPoint((1, 1))
|
|
quadpen.addPoint((2, 2))
|
|
quadpen.addPoint((3, 3))
|
|
quadpen.addPoint(
|
|
(4, 4), segmentType="curve", smooth=False, name="up", selected=1)
|
|
quadpen.endPath()
|
|
|
|
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)
|
|
pen.addPoint((1.625, 1.625), name=None, segmentType=None, smooth=False)
|
|
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())
|
|
|
|
def test__flushContour_restore_starting_point(self):
|
|
pen = DummyPointPen()
|
|
quadpen = Cu2QuPointPen(pen, MAX_ERR)
|
|
|
|
# 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, {}),
|
|
]),
|
|
])
|
|
|
|
# the original starting point is restored: the last segment has become
|
|
# the first
|
|
self.assertEqual(new_segments[0][1][-1][0], (3, 3))
|
|
self.assertEqual(new_segments[-1][1][-1][0], (0, 0))
|
|
|
|
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, {}),
|
|
]),
|
|
])
|
|
|
|
# the segment order stays the same before and after _flushContour
|
|
self.assertEqual(new_segments[0][1][-1][0], (0, 0))
|
|
self.assertEqual(new_segments[-1][1][-1][0], (3, 3))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|