One way to work around https://github.com/googlei18n/cu2qu/issues/51 when using the ufoLib's ReverseConturPointPen via the converter pens would be to pass the outputImpliedClosingLine=True argument (False by default) to the PointToSegmentPen. This way, all the final lineTos are explicitly outputted, even when they are redundant and could be implied (i.e. when they are not duplicate points). Note that this test is skipped by the CI, because ufoLib is not a dependency of fonttools; you can run locally if you wish.
309 lines
8.2 KiB
Python
309 lines
8.2 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)
|
|
assert recpen.value == expected
|