[test.pens_test] add test module for Cu2Qu pens
This commit is contained in:
parent
5f8bb98107
commit
a21c388355
336
Lib/cu2qu/test/pens_test.py
Normal file
336
Lib/cu2qu/test/pens_test.py
Normal file
@ -0,0 +1,336 @@
|
||||
from __future__ import print_function, division, absolute_import
|
||||
import unittest
|
||||
|
||||
from cu2qu.pens import Cu2QuPen, Cu2QuPointPen
|
||||
from cu2qu.test import CUBIC_GLYPHS, QUAD_GLYPHS
|
||||
from cu2qu.test.utils import DummyGlyph, DummyPointGlyph
|
||||
from cu2qu.test.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('4' in stats)
|
||||
self.assertEqual(type(stats['4']), 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()
|
Loading…
x
Reference in New Issue
Block a user