This test is not normally run. It is skipped when ufoLib is not importable, as it's the case from the test runner's virtual environment. The only reason it exists is so that I could check that the ReverseContourPen I was writing behaves the same as using ufoLib's ReverseContourPointPen when the latter is run through the SegmentToPointPen and PointToSegmentPen converters. The two methods for reversing contours now diverge since https://github.com/fonttools/fonttools/pull/1080, but only nominally because both produce effectively the same results. The only difference is that, when using the point pens with outputImpliedClosingLine=True, the closing lineTo is always there even when it's redundant. In our implmentation, we only output the closing lineTo when it is the same as moveTo (this was necessary in order to fix https://github.com/googlei18n/cu2qu/issues/51) Nevertheless, the total number of points is the same in both cases. Maybe this test should not be here, as it's testing functionalities from a different package. Closes https://github.com/fonttools/fonttools/issues/1081
320 lines
8.8 KiB
Python
320 lines
8.8 KiB
Python
from fontTools.pens.recordingPen import RecordingPen
|
|
from fontTools.pens.reverseContourPen import ReverseContourPen
|
|
import pytest
|
|
|
|
|
|
TEST_DATA = [
|
|
(
|
|
[
|
|
('moveTo', ((0, 0),)),
|
|
('lineTo', ((1, 1),)),
|
|
('lineTo', ((2, 2),)),
|
|
('lineTo', ((3, 3),)), # last not on move, line is implied
|
|
('closePath', ()),
|
|
],
|
|
[
|
|
('moveTo', ((0, 0),)),
|
|
('lineTo', ((3, 3),)),
|
|
('lineTo', ((2, 2),)),
|
|
('lineTo', ((1, 1),)),
|
|
('closePath', ()),
|
|
]
|
|
),
|
|
(
|
|
[
|
|
('moveTo', ((0, 0),)),
|
|
('lineTo', ((1, 1),)),
|
|
('lineTo', ((2, 2),)),
|
|
('lineTo', ((0, 0),)), # last on move, no implied line
|
|
('closePath', ()),
|
|
],
|
|
[
|
|
('moveTo', ((0, 0),)),
|
|
('lineTo', ((2, 2),)),
|
|
('lineTo', ((1, 1),)),
|
|
('closePath', ()),
|
|
]
|
|
),
|
|
(
|
|
[
|
|
('moveTo', ((0, 0),)),
|
|
('lineTo', ((0, 0),)),
|
|
('lineTo', ((1, 1),)),
|
|
('lineTo', ((2, 2),)),
|
|
('closePath', ()),
|
|
],
|
|
[
|
|
('moveTo', ((0, 0),)),
|
|
('lineTo', ((2, 2),)),
|
|
('lineTo', ((1, 1),)),
|
|
('lineTo', ((0, 0),)),
|
|
('lineTo', ((0, 0),)),
|
|
('closePath', ()),
|
|
]
|
|
),
|
|
(
|
|
[
|
|
('moveTo', ((0, 0),)),
|
|
('lineTo', ((1, 1),)),
|
|
('closePath', ()),
|
|
],
|
|
[
|
|
('moveTo', ((0, 0),)),
|
|
('lineTo', ((1, 1),)),
|
|
('closePath', ()),
|
|
]
|
|
),
|
|
(
|
|
[
|
|
('moveTo', ((0, 0),)),
|
|
('curveTo', ((1, 1), (2, 2), (3, 3))),
|
|
('curveTo', ((4, 4), (5, 5), (0, 0))),
|
|
('closePath', ()),
|
|
],
|
|
[
|
|
('moveTo', ((0, 0),)),
|
|
('curveTo', ((5, 5), (4, 4), (3, 3))),
|
|
('curveTo', ((2, 2), (1, 1), (0, 0))),
|
|
('closePath', ()),
|
|
]
|
|
),
|
|
(
|
|
[
|
|
('moveTo', ((0, 0),)),
|
|
('curveTo', ((1, 1), (2, 2), (3, 3))),
|
|
('curveTo', ((4, 4), (5, 5), (6, 6))),
|
|
('closePath', ()),
|
|
],
|
|
[
|
|
('moveTo', ((0, 0),)),
|
|
('lineTo', ((6, 6),)), # implied line
|
|
('curveTo', ((5, 5), (4, 4), (3, 3))),
|
|
('curveTo', ((2, 2), (1, 1), (0, 0))),
|
|
('closePath', ()),
|
|
]
|
|
),
|
|
(
|
|
[
|
|
('moveTo', ((0, 0),)),
|
|
('lineTo', ((1, 1),)), # this line becomes implied
|
|
('curveTo', ((2, 2), (3, 3), (4, 4))),
|
|
('curveTo', ((5, 5), (6, 6), (7, 7))),
|
|
('closePath', ()),
|
|
],
|
|
[
|
|
('moveTo', ((0, 0),)),
|
|
('lineTo', ((7, 7),)),
|
|
('curveTo', ((6, 6), (5, 5), (4, 4))),
|
|
('curveTo', ((3, 3), (2, 2), (1, 1))),
|
|
('closePath', ()),
|
|
]
|
|
),
|
|
(
|
|
[
|
|
('moveTo', ((0, 0),)),
|
|
('qCurveTo', ((1, 1), (2, 2))),
|
|
('qCurveTo', ((3, 3), (0, 0))),
|
|
('closePath', ()),
|
|
],
|
|
[
|
|
('moveTo', ((0, 0),)),
|
|
('qCurveTo', ((3, 3), (2, 2))),
|
|
('qCurveTo', ((1, 1), (0, 0))),
|
|
('closePath', ()),
|
|
]
|
|
),
|
|
(
|
|
[
|
|
('moveTo', ((0, 0),)),
|
|
('qCurveTo', ((1, 1), (2, 2))),
|
|
('qCurveTo', ((3, 3), (4, 4))),
|
|
('closePath', ()),
|
|
],
|
|
[
|
|
('moveTo', ((0, 0),)),
|
|
('lineTo', ((4, 4),)),
|
|
('qCurveTo', ((3, 3), (2, 2))),
|
|
('qCurveTo', ((1, 1), (0, 0))),
|
|
('closePath', ()),
|
|
]
|
|
),
|
|
(
|
|
[
|
|
('moveTo', ((0, 0),)),
|
|
('lineTo', ((1, 1),)),
|
|
('qCurveTo', ((2, 2), (3, 3))),
|
|
('closePath', ()),
|
|
],
|
|
[
|
|
('moveTo', ((0, 0),)),
|
|
('lineTo', ((3, 3),)),
|
|
('qCurveTo', ((2, 2), (1, 1))),
|
|
('closePath', ()),
|
|
]
|
|
),
|
|
(
|
|
[
|
|
('addComponent', ('a', (1, 0, 0, 1, 0, 0)))
|
|
],
|
|
[
|
|
('addComponent', ('a', (1, 0, 0, 1, 0, 0)))
|
|
]
|
|
),
|
|
(
|
|
[], []
|
|
),
|
|
(
|
|
[
|
|
('moveTo', ((0, 0),)),
|
|
('endPath', ()),
|
|
],
|
|
[
|
|
('moveTo', ((0, 0),)),
|
|
('endPath', ()),
|
|
],
|
|
),
|
|
(
|
|
[
|
|
('moveTo', ((0, 0),)),
|
|
('closePath', ()),
|
|
],
|
|
[
|
|
('moveTo', ((0, 0),)),
|
|
('endPath', ()), # single-point paths is always open
|
|
],
|
|
),
|
|
(
|
|
[
|
|
('moveTo', ((0, 0),)),
|
|
('lineTo', ((1, 1),)),
|
|
('endPath', ())
|
|
],
|
|
[
|
|
('moveTo', ((1, 1),)),
|
|
('lineTo', ((0, 0),)),
|
|
('endPath', ())
|
|
]
|
|
),
|
|
(
|
|
[
|
|
('moveTo', ((0, 0),)),
|
|
('curveTo', ((1, 1), (2, 2), (3, 3))),
|
|
('endPath', ())
|
|
],
|
|
[
|
|
('moveTo', ((3, 3),)),
|
|
('curveTo', ((2, 2), (1, 1), (0, 0))),
|
|
('endPath', ())
|
|
]
|
|
),
|
|
(
|
|
[
|
|
('moveTo', ((0, 0),)),
|
|
('curveTo', ((1, 1), (2, 2), (3, 3))),
|
|
('lineTo', ((4, 4),)),
|
|
('endPath', ())
|
|
],
|
|
[
|
|
('moveTo', ((4, 4),)),
|
|
('lineTo', ((3, 3),)),
|
|
('curveTo', ((2, 2), (1, 1), (0, 0))),
|
|
('endPath', ())
|
|
]
|
|
),
|
|
(
|
|
[
|
|
('moveTo', ((0, 0),)),
|
|
('lineTo', ((1, 1),)),
|
|
('curveTo', ((2, 2), (3, 3), (4, 4))),
|
|
('endPath', ())
|
|
],
|
|
[
|
|
('moveTo', ((4, 4),)),
|
|
('curveTo', ((3, 3), (2, 2), (1, 1))),
|
|
('lineTo', ((0, 0),)),
|
|
('endPath', ())
|
|
]
|
|
),
|
|
(
|
|
[
|
|
('qCurveTo', ((0, 0), (1, 1), (2, 2), None)),
|
|
('closePath', ())
|
|
],
|
|
[
|
|
('qCurveTo', ((0, 0), (2, 2), (1, 1), None)),
|
|
('closePath', ())
|
|
]
|
|
),
|
|
(
|
|
[
|
|
('qCurveTo', ((0, 0), (1, 1), (2, 2), None)),
|
|
('endPath', ())
|
|
],
|
|
[
|
|
('qCurveTo', ((0, 0), (2, 2), (1, 1), None)),
|
|
('closePath', ()) # this is always "closed"
|
|
]
|
|
),
|
|
# Test case from:
|
|
# https://github.com/googlei18n/cu2qu/issues/51#issue-179370514
|
|
(
|
|
[
|
|
('moveTo', ((848, 348),)),
|
|
('lineTo', ((848, 348),)), # duplicate lineTo point after moveTo
|
|
('qCurveTo', ((848, 526), (649, 704), (449, 704))),
|
|
('qCurveTo', ((449, 704), (248, 704), (50, 526), (50, 348))),
|
|
('lineTo', ((50, 348),)),
|
|
('qCurveTo', ((50, 348), (50, 171), (248, -3), (449, -3))),
|
|
('qCurveTo', ((449, -3), (649, -3), (848, 171), (848, 348))),
|
|
('closePath', ())
|
|
],
|
|
[
|
|
('moveTo', ((848, 348),)),
|
|
('qCurveTo', ((848, 171), (649, -3), (449, -3), (449, -3))),
|
|
('qCurveTo', ((248, -3), (50, 171), (50, 348), (50, 348))),
|
|
('lineTo', ((50, 348),)),
|
|
('qCurveTo', ((50, 526), (248, 704), (449, 704), (449, 704))),
|
|
('qCurveTo', ((649, 704), (848, 526), (848, 348))),
|
|
('lineTo', ((848, 348),)), # the duplicate point is kept
|
|
('closePath', ())
|
|
]
|
|
)
|
|
]
|
|
|
|
|
|
@pytest.mark.parametrize("contour, expected", TEST_DATA)
|
|
def test_reverse_pen(contour, expected):
|
|
recpen = RecordingPen()
|
|
revpen = ReverseContourPen(recpen)
|
|
for operator, operands in contour:
|
|
getattr(revpen, operator)(*operands)
|
|
assert recpen.value == expected
|
|
|
|
|
|
@pytest.mark.parametrize("contour, expected", TEST_DATA)
|
|
def test_reverse_point_pen(contour, expected):
|
|
try:
|
|
from ufoLib.pointPen import (
|
|
ReverseContourPointPen, PointToSegmentPen, SegmentToPointPen)
|
|
except ImportError:
|
|
pytest.skip("ufoLib not installed")
|
|
|
|
recpen = RecordingPen()
|
|
pt2seg = PointToSegmentPen(recpen, outputImpliedClosingLine=True)
|
|
revpen = ReverseContourPointPen(pt2seg)
|
|
seg2pt = SegmentToPointPen(revpen)
|
|
for operator, operands in contour:
|
|
getattr(seg2pt, operator)(*operands)
|
|
|
|
# for closed contours that have a lineTo following the moveTo,
|
|
# and whose points don't overlap, our current implementation diverges
|
|
# from the ReverseContourPointPen as wrapped by ufoLib's pen converters.
|
|
# In the latter case, an extra lineTo is added because of
|
|
# outputImpliedClosingLine=True. This is redundant but not incorrect,
|
|
# as the number of points is the same in both.
|
|
if (contour and contour[-1][0] == "closePath" and
|
|
contour[1][0] == "lineTo" and contour[1][1] != contour[0][1]):
|
|
expected = expected[:-1] + [("lineTo", contour[0][1])] + expected[-1:]
|
|
|
|
assert recpen.value == expected
|