You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

337 lines
9.3 KiB

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', ())
]
),
# Test case from https://github.com/googlefonts/fontmake/issues/572
# An additional closing lineTo is required to disambiguate a duplicate
# point at the end of a contour from the implied closing line.
(
[
('moveTo', ((0, 651),)),
('lineTo', ((0, 101),)),
('lineTo', ((0, 101),)),
('lineTo', ((0, 651),)),
('lineTo', ((0, 651),)),
('closePath', ())
],
[
('moveTo', ((0, 651),)),
('lineTo', ((0, 651),)),
('lineTo', ((0, 101),)),
('lineTo', ((0, 101),)),
('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):
from fontTools.ufoLib.pointPen import (
ReverseContourPointPen, PointToSegmentPen, SegmentToPointPen)
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