[Tests/misc/svgPath] add tests for SVGPath and parse_path
This commit is contained in:
parent
ce530a51d3
commit
69a75f5f3a
0
Tests/misc/svgPath/__init__.py
Normal file
0
Tests/misc/svgPath/__init__.py
Normal file
297
Tests/misc/svgPath/parser_test.py
Normal file
297
Tests/misc/svgPath/parser_test.py
Normal file
@ -0,0 +1,297 @@
|
||||
from __future__ import print_function, absolute_import, division
|
||||
|
||||
from fontTools.misc.py23 import *
|
||||
from fontTools.pens.recordingPen import RecordingPen
|
||||
from fontTools.misc.svgPath import parse_path
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"pathdef, expected",
|
||||
[
|
||||
|
||||
# Examples from the SVG spec
|
||||
|
||||
(
|
||||
"M 100 100 L 300 100 L 200 300 z",
|
||||
[
|
||||
("moveTo", ((100.0, 100.0),)),
|
||||
("lineTo", ((300.0, 100.0),)),
|
||||
("lineTo", ((200.0, 300.0),)),
|
||||
("lineTo", ((100.0, 100.0),)),
|
||||
("closePath", ()),
|
||||
]
|
||||
),
|
||||
# for Z command behavior when there is multiple subpaths
|
||||
(
|
||||
"M 0 0 L 50 20 M 100 100 L 300 100 L 200 300 z",
|
||||
[
|
||||
("moveTo", ((0.0, 0.0),)),
|
||||
("lineTo", ((50.0, 20.0),)),
|
||||
("endPath", ()),
|
||||
("moveTo", ((100.0, 100.0),)),
|
||||
("lineTo", ((300.0, 100.0),)),
|
||||
("lineTo", ((200.0, 300.0),)),
|
||||
("lineTo", ((100.0, 100.0),)),
|
||||
("closePath", ()),
|
||||
]
|
||||
),
|
||||
(
|
||||
"M100,200 C100,100 250,100 250,200 S400,300 400,200",
|
||||
[
|
||||
("moveTo", ((100.0, 200.0),)),
|
||||
("curveTo", ((100.0, 100.0),
|
||||
(250.0, 100.0),
|
||||
(250.0, 200.0))),
|
||||
("curveTo", ((250.0, 300.0),
|
||||
(400.0, 300.0),
|
||||
(400.0, 200.0))),
|
||||
("endPath", ()),
|
||||
]
|
||||
),
|
||||
(
|
||||
"M100,200 C100,100 400,100 400,200",
|
||||
[
|
||||
("moveTo", ((100.0, 200.0),)),
|
||||
("curveTo", ((100.0, 100.0),
|
||||
(400.0, 100.0),
|
||||
(400.0, 200.0))),
|
||||
("endPath", ()),
|
||||
]
|
||||
),
|
||||
(
|
||||
"M100,500 C25,400 475,400 400,500",
|
||||
[
|
||||
("moveTo", ((100.0, 500.0),)),
|
||||
("curveTo", ((25.0, 400.0),
|
||||
(475.0, 400.0),
|
||||
(400.0, 500.0))),
|
||||
("endPath", ()),
|
||||
]
|
||||
),
|
||||
(
|
||||
"M100,800 C175,700 325,700 400,800",
|
||||
[
|
||||
("moveTo", ((100.0, 800.0),)),
|
||||
("curveTo", ((175.0, 700.0),
|
||||
(325.0, 700.0),
|
||||
(400.0, 800.0))),
|
||||
("endPath", ()),
|
||||
]
|
||||
),
|
||||
(
|
||||
"M600,200 C675,100 975,100 900,200",
|
||||
[
|
||||
("moveTo", ((600.0, 200.0),)),
|
||||
("curveTo", ((675.0, 100.0),
|
||||
(975.0, 100.0),
|
||||
(900.0, 200.0))),
|
||||
("endPath", ()),
|
||||
]
|
||||
),
|
||||
(
|
||||
"M600,500 C600,350 900,650 900,500",
|
||||
[
|
||||
("moveTo", ((600.0, 500.0),)),
|
||||
("curveTo", ((600.0, 350.0),
|
||||
(900.0, 650.0),
|
||||
(900.0, 500.0))),
|
||||
("endPath", ()),
|
||||
]
|
||||
),
|
||||
(
|
||||
"M600,800 C625,700 725,700 750,800 S875,900 900,800",
|
||||
[
|
||||
("moveTo", ((600.0, 800.0),)),
|
||||
("curveTo", ((625.0, 700.0),
|
||||
(725.0, 700.0),
|
||||
(750.0, 800.0))),
|
||||
("curveTo", ((775.0, 900.0),
|
||||
(875.0, 900.0),
|
||||
(900.0, 800.0))),
|
||||
("endPath", ()),
|
||||
]
|
||||
),
|
||||
(
|
||||
"M200,300 Q400,50 600,300 T1000,300",
|
||||
[
|
||||
("moveTo", ((200.0, 300.0),)),
|
||||
("qCurveTo", ((400.0, 50.0),
|
||||
(600.0, 300.0))),
|
||||
("qCurveTo", ((800.0, 550.0),
|
||||
(1000.0, 300.0))),
|
||||
("endPath", ()),
|
||||
]
|
||||
),
|
||||
# End examples from SVG spec
|
||||
|
||||
# Relative moveto
|
||||
(
|
||||
"M 0 0 L 50 20 m 50 80 L 300 100 L 200 300 z",
|
||||
[
|
||||
("moveTo", ((0.0, 0.0),)),
|
||||
("lineTo", ((50.0, 20.0),)),
|
||||
("endPath", ()),
|
||||
("moveTo", ((100.0, 100.0),)),
|
||||
("lineTo", ((300.0, 100.0),)),
|
||||
("lineTo", ((200.0, 300.0),)),
|
||||
("lineTo", ((100.0, 100.0),)),
|
||||
("closePath", ()),
|
||||
]
|
||||
),
|
||||
# Initial smooth and relative curveTo
|
||||
(
|
||||
"M100,200 s 150,-100 150,0",
|
||||
[
|
||||
("moveTo", ((100.0, 200.0),)),
|
||||
("curveTo", ((100.0, 200.0),
|
||||
(250.0, 100.0),
|
||||
(250.0, 200.0))),
|
||||
("endPath", ()),
|
||||
]
|
||||
),
|
||||
# Initial smooth and relative qCurveTo
|
||||
(
|
||||
"M100,200 t 150,0",
|
||||
[
|
||||
("moveTo", ((100.0, 200.0),)),
|
||||
("qCurveTo", ((100.0, 200.0),
|
||||
(250.0, 200.0))),
|
||||
("endPath", ()),
|
||||
]
|
||||
),
|
||||
# relative l command
|
||||
(
|
||||
"M 100 100 L 300 100 l -100 200 z",
|
||||
[
|
||||
("moveTo", ((100.0, 100.0),)),
|
||||
("lineTo", ((300.0, 100.0),)),
|
||||
("lineTo", ((200.0, 300.0),)),
|
||||
("lineTo", ((100.0, 100.0),)),
|
||||
("closePath", ()),
|
||||
]
|
||||
),
|
||||
# relative q command
|
||||
(
|
||||
"M200,300 q200,-250 400,0",
|
||||
[
|
||||
("moveTo", ((200.0, 300.0),)),
|
||||
("qCurveTo", ((400.0, 50.0),
|
||||
(600.0, 300.0))),
|
||||
("endPath", ()),
|
||||
]
|
||||
),
|
||||
# absolute H command
|
||||
(
|
||||
"M 100 100 H 300 L 200 300 z",
|
||||
[
|
||||
("moveTo", ((100.0, 100.0),)),
|
||||
("lineTo", ((300.0, 100.0),)),
|
||||
("lineTo", ((200.0, 300.0),)),
|
||||
("lineTo", ((100.0, 100.0),)),
|
||||
("closePath", ()),
|
||||
]
|
||||
),
|
||||
# relative h command
|
||||
(
|
||||
"M 100 100 h 200 L 200 300 z",
|
||||
[
|
||||
("moveTo", ((100.0, 100.0),)),
|
||||
("lineTo", ((300.0, 100.0),)),
|
||||
("lineTo", ((200.0, 300.0),)),
|
||||
("lineTo", ((100.0, 100.0),)),
|
||||
("closePath", ()),
|
||||
]
|
||||
),
|
||||
# absolute V command
|
||||
(
|
||||
"M 100 100 V 300 L 200 300 z",
|
||||
[
|
||||
("moveTo", ((100.0, 100.0),)),
|
||||
("lineTo", ((100.0, 300.0),)),
|
||||
("lineTo", ((200.0, 300.0),)),
|
||||
("lineTo", ((100.0, 100.0),)),
|
||||
("closePath", ()),
|
||||
]
|
||||
),
|
||||
# relative v command
|
||||
(
|
||||
"M 100 100 v 200 L 200 300 z",
|
||||
[
|
||||
("moveTo", ((100.0, 100.0),)),
|
||||
("lineTo", ((100.0, 300.0),)),
|
||||
("lineTo", ((200.0, 300.0),)),
|
||||
("lineTo", ((100.0, 100.0),)),
|
||||
("closePath", ()),
|
||||
]
|
||||
),
|
||||
]
|
||||
)
|
||||
def test_parse_path(pathdef, expected):
|
||||
pen = RecordingPen()
|
||||
parse_path(pathdef, pen)
|
||||
|
||||
assert pen.value == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"pathdef1, pathdef2",
|
||||
[
|
||||
# don't need spaces between numbers and commands
|
||||
(
|
||||
"M 100 100 L 200 200",
|
||||
"M100 100L200 200",
|
||||
),
|
||||
# repeated implicit command
|
||||
(
|
||||
"M 100 200 L 200 100 L -100 -200",
|
||||
"M 100 200 L 200 100 -100 -200"
|
||||
),
|
||||
# don't need spaces before a minus-sign
|
||||
(
|
||||
"M100,200c10-5,20-10,30-20",
|
||||
"M 100 200 c 10 -5 20 -10 30 -20"
|
||||
),
|
||||
# closed paths have an implicit lineTo if they don't
|
||||
# end on the same point as the initial moveTo
|
||||
(
|
||||
"M 100 100 L 300 100 L 200 300 z",
|
||||
"M 100 100 L 300 100 L 200 300 L 100 100 z"
|
||||
)
|
||||
]
|
||||
)
|
||||
def test_equivalent_paths(pathdef1, pathdef2):
|
||||
pen1 = RecordingPen()
|
||||
parse_path(pathdef1, pen1)
|
||||
|
||||
pen2 = RecordingPen()
|
||||
parse_path(pathdef2, pen2)
|
||||
|
||||
assert pen1.value == pen2.value
|
||||
|
||||
|
||||
def test_exponents():
|
||||
# It can be e or E, the plus is optional, and a minimum of +/-3.4e38 must be supported.
|
||||
pen = RecordingPen()
|
||||
parse_path("M-3.4e38 3.4E+38L-3.4E-38,3.4e-38", pen)
|
||||
expected = [
|
||||
("moveTo", ((-3.4e+38, 3.4e+38),)),
|
||||
("lineTo", ((-3.4e-38, 3.4e-38),)),
|
||||
("endPath", ()),
|
||||
]
|
||||
|
||||
assert pen.value == expected
|
||||
|
||||
|
||||
def test_invalid_implicit_command():
|
||||
with pytest.raises(ValueError) as exc_info:
|
||||
parse_path("M 100 100 L 200 200 Z 100 200", RecordingPen())
|
||||
assert exc_info.match("Unallowed implicit command")
|
||||
|
||||
|
||||
def test_arc_not_implemented():
|
||||
pathdef = "M300,200 h-150 a150,150 0 1,0 150,-150 z"
|
||||
with pytest.raises(NotImplementedError) as exc_info:
|
||||
parse_path(pathdef, RecordingPen())
|
||||
assert exc_info.match("arcs are not supported")
|
80
Tests/misc/svgPath/svgPath_test.py
Normal file
80
Tests/misc/svgPath/svgPath_test.py
Normal file
@ -0,0 +1,80 @@
|
||||
from __future__ import print_function, absolute_import, division
|
||||
|
||||
from fontTools.misc.py23 import *
|
||||
from fontTools.pens.recordingPen import RecordingPen
|
||||
from fontTools.misc.svgPath import SVGPath
|
||||
|
||||
import os
|
||||
from tempfile import NamedTemporaryFile
|
||||
|
||||
|
||||
SVG_DATA = """\
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="1000.0" height="1000.0">
|
||||
<path d="M 100 100 L 300 100 L 200 300 z"/>
|
||||
<path d="M100,200 C100,100 250,100 250,200 S400,300 400,200"/>
|
||||
</svg>
|
||||
"""
|
||||
|
||||
EXPECTED_PEN_COMMANDS = [
|
||||
("moveTo", ((100.0, 100.0),)),
|
||||
("lineTo", ((300.0, 100.0),)),
|
||||
("lineTo", ((200.0, 300.0),)),
|
||||
("lineTo", ((100.0, 100.0),)),
|
||||
("closePath", ()),
|
||||
("moveTo", ((100.0, 200.0),)),
|
||||
("curveTo", ((100.0, 100.0),
|
||||
(250.0, 100.0),
|
||||
(250.0, 200.0))),
|
||||
("curveTo", ((250.0, 300.0),
|
||||
(400.0, 300.0),
|
||||
(400.0, 200.0))),
|
||||
("endPath", ())
|
||||
]
|
||||
|
||||
|
||||
class SVGPathTest(object):
|
||||
|
||||
def test_from_svg_file(self):
|
||||
pen = RecordingPen()
|
||||
with NamedTemporaryFile(delete=False) as tmp:
|
||||
tmp.write(tobytes(SVG_DATA))
|
||||
try:
|
||||
svg = SVGPath(tmp.name)
|
||||
svg.draw(pen)
|
||||
finally:
|
||||
os.remove(tmp.name)
|
||||
|
||||
assert pen.value == EXPECTED_PEN_COMMANDS
|
||||
|
||||
def test_fromstring(self):
|
||||
pen = RecordingPen()
|
||||
svg = SVGPath.fromstring(SVG_DATA)
|
||||
svg.draw(pen)
|
||||
|
||||
assert pen.value == EXPECTED_PEN_COMMANDS
|
||||
|
||||
def test_transform(self):
|
||||
pen = RecordingPen()
|
||||
svg = SVGPath.fromstring(SVG_DATA,
|
||||
transform=(1.0, 0, 0, -1.0, 0, 1000))
|
||||
svg.draw(pen)
|
||||
|
||||
assert pen.value == [
|
||||
("moveTo", ((100.0, 900.0),)),
|
||||
("lineTo", ((300.0, 900.0),)),
|
||||
("lineTo", ((200.0, 700.0),)),
|
||||
("lineTo", ((100.0, 900.0),)),
|
||||
("closePath", ()),
|
||||
("moveTo", ((100.0, 800.0),)),
|
||||
("curveTo", ((100.0, 900.0),
|
||||
(250.0, 900.0),
|
||||
(250.0, 800.0))),
|
||||
("curveTo", ((250.0, 700.0),
|
||||
(400.0, 700.0),
|
||||
(400.0, 800.0))),
|
||||
("endPath", ())
|
||||
]
|
Loading…
x
Reference in New Issue
Block a user