• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1from fontTools.svgLib.path import shapes
2from fontTools.misc import etree
3import pytest
4
5
6@pytest.mark.parametrize(
7    "svg_xml, expected_path, expected_transform",
8    [
9        # path: direct passthrough
10        (
11            "<path d='I love kittens'/>",
12            "I love kittens",
13            None
14        ),
15        # path no @d
16        (
17            "<path duck='Mallard'/>",
18            None,
19            None
20        ),
21        # line
22        (
23            '<line x1="10" x2="50" y1="110" y2="150"/>',
24            'M10,110 L50,150',
25            None
26        ),
27        # line, decimal positioning
28        (
29            '<line x1="10.0" x2="50.5" y1="110.2" y2="150.7"/>',
30            'M10,110.2 L50.5,150.7',
31            None
32        ),
33        # rect: minimal valid example
34        (
35            "<rect width='1' height='1'/>",
36            "M0,0 H1 V1 H0 V0 z",
37            None
38        ),
39        # rect: sharp corners
40        (
41            "<rect x='10' y='11' width='17' height='11'/>",
42            "M10,11 H27 V22 H10 V11 z",
43            None
44        ),
45        # rect: round corners
46        (
47            "<rect x='9' y='9' width='11' height='7' rx='2'/>",
48            "M11,9 H18 A2,2 0 0 1 20,11 V14 A2,2 0 0 1 18,16 H11"
49            " A2,2 0 0 1 9,14 V11 A2,2 0 0 1 11,9 z",
50            None
51        ),
52        # rect: simple
53        (
54            "<rect x='11.5' y='16' width='11' height='2'/>",
55            "M11.5,16 H22.5 V18 H11.5 V16 z",
56            None
57        ),
58        # rect: the one above plus a rotation
59        (
60            "<rect x='11.5' y='16' transform='matrix(0.7071 -0.7071 0.7071 0.7071 -7.0416 16.9999)' width='11' height='2'/>",
61            "M11.5,16 H22.5 V18 H11.5 V16 z",
62            (0.7071, -0.7071, 0.7071, 0.7071, -7.0416, 16.9999)
63        ),
64        # polygon
65        (
66            "<polygon points='30,10 50,30 10,30'/>",
67            "M30,10 50,30 10,30 z",
68            None
69        ),
70        # polyline
71        (
72            "<polyline points='30,10 50,30 10,30'/>",
73            "M30,10 50,30 10,30",
74            None
75        ),
76        # circle, minimal valid example
77        (
78            "<circle r='1'/>",
79            "M-1,0 A1,1 0 1 1 1,0 A1,1 0 1 1 -1,0",
80            None
81        ),
82        # circle
83        (
84            "<circle cx='600' cy='200' r='100'/>",
85            "M500,200 A100,100 0 1 1 700,200 A100,100 0 1 1 500,200",
86            None
87        ),
88        # circle, decimal positioning
89        (
90            "<circle cx='12' cy='6.5' r='1.5'></circle>",
91            "M10.5,6.5 A1.5,1.5 0 1 1 13.5,6.5 A1.5,1.5 0 1 1 10.5,6.5",
92            None
93        ),
94        # circle, with transform
95        (
96            '<circle transform="matrix(0.9871 -0.1602 0.1602 0.9871 -7.525 8.6516)" cx="49.9" cy="51" r="14.3"/>',
97            'M35.6,51 A14.3,14.3 0 1 1 64.2,51 A14.3,14.3 0 1 1 35.6,51',
98            (0.9871, -0.1602, 0.1602, 0.9871, -7.525, 8.6516)
99        ),
100        # ellipse
101        (
102            '<ellipse cx="100" cy="50" rx="100" ry="50"/>',
103            'M0,50 A100,50 0 1 1 200,50 A100,50 0 1 1 0,50',
104            None
105        ),
106        # ellipse, decimal positioning
107        (
108            '<ellipse cx="100.5" cy="50" rx="10" ry="50.5"/>',
109            'M90.5,50 A10,50.5 0 1 1 110.5,50 A10,50.5 0 1 1 90.5,50',
110            None
111        ),
112        # ellipse, with transform
113        (
114            '<ellipse transform="matrix(0.9557 -0.2945 0.2945 0.9557 -14.7694 20.1454)" cx="59.5" cy="59.1" rx="30.9" ry="11.9"/>',
115            'M28.6,59.1 A30.9,11.9 0 1 1 90.4,59.1 A30.9,11.9 0 1 1 28.6,59.1',
116            (0.9557, -0.2945, 0.2945, 0.9557, -14.7694, 20.1454)
117        ),
118    ]
119)
120def test_el_to_path(svg_xml, expected_path, expected_transform):
121    pb = shapes.PathBuilder()
122    pb.add_path_from_element(etree.fromstring(svg_xml))
123    if expected_path:
124        expected_paths = [expected_path]
125        expected_transforms = [expected_transform]
126    else:
127        expected_paths = []
128        expected_transforms = []
129    assert pb.paths == expected_paths
130    assert pb.transforms == expected_transforms
131