diff --git a/svgpathtools/svg_to_paths.py b/svgpathtools/svg_to_paths.py
index c6ac6df..aa55869 100644
--- a/svgpathtools/svg_to_paths.py
+++ b/svgpathtools/svg_to_paths.py
@@ -27,7 +27,7 @@ def path2pathd(path):
return path.get('d', '')
-def ellipse2pathd(ellipse):
+def ellipse2pathd(ellipse, use_cubics=False):
"""converts the parameters from an ellipse or a circle to a string for a
Path object d-attribute"""
@@ -46,10 +46,32 @@ def ellipse2pathd(ellipse):
cx = float(cx)
cy = float(cy)
- d = ''
- d += 'M' + str(cx - rx) + ',' + str(cy)
- d += 'a' + str(rx) + ',' + str(ry) + ' 0 1,0 ' + str(2 * rx) + ',0'
- d += 'a' + str(rx) + ',' + str(ry) + ' 0 1,0 ' + str(-2 * rx) + ',0'
+ if use_cubics:
+ # Modified by NXP 2024, 2025
+ PATH_KAPPA = 0.552284
+ rxKappa = rx * PATH_KAPPA;
+ ryKappa = ry * PATH_KAPPA;
+
+ #According to the SVG specification (https://lists.w3.org/Archives/Public/www-archive/2005May/att-0005/SVGT12_Main.pdf),
+ #Section 9.4, "The 'ellipse' element": "The arc of an 'ellipse' element begins at the "3 o'clock" point on
+ #the radius and progresses towards the "9 o'clock". Therefore, the ellipse begins at the rightmost point
+ #and progresses clockwise.
+ d = ''
+ # Move to the rightmost point
+ d += 'M' + str(cx + rx) + ' ' + str(cy)
+ # Draw bottom-right quadrant
+ d += 'C' + str(cx + rx) + ' ' + str(cy + ryKappa) + ' ' + str(cx + rxKappa) + ' ' + str(cy + ry) + ' ' + str(cx) + ' ' + str(cy + ry)
+ # Draw bottom-left quadrant
+ d += 'C' + str(cx - rxKappa) + ' ' + str(cy + ry) + ' ' + str(cx - rx) + ' ' + str(cy + ryKappa) + ' ' + str(cx - rx) + ' ' + str(cy)
+ # Draw top-left quadrant
+ d += 'C' + str(cx - rx) + ' ' + str(cy - ryKappa) + ' ' + str(cx - rxKappa) + ' ' + str(cy - ry) + ' ' + str(cx) + ' ' + str(cy - ry)
+ # Draw top-right quadrant
+ d += 'C' + str(cx + rxKappa) + ' ' + str(cy - ry) + ' ' + str(cx + rx) + ' ' + str(cy - ryKappa) + ' ' + str(cx + rx) + ' ' + str(cy)
+ else:
+ d = ''
+ d += 'M' + str(cx - rx) + ',' + str(cy)
+ d += 'a' + str(rx) + ',' + str(ry) + ' 0 1,0 ' + str(2 * rx) + ',0'
+ d += 'a' + str(rx) + ',' + str(ry) + ' 0 1,0 ' + str(-2 * rx) + ',0'
return d + 'z'
@@ -62,6 +84,9 @@ def polyline2pathd(polyline, is_polygon=False):
else:
points = COORD_PAIR_TMPLT.findall(polyline.get('points', ''))
+ if len(points) == 0:
+ return ''
+
closed = (float(points[0][0]) == float(points[-1][0]) and
float(points[0][1]) == float(points[-1][1]))
@@ -77,13 +102,13 @@ def polyline2pathd(polyline, is_polygon=False):
return d
-def polygon2pathd(polyline):
+def polygon2pathd(polyline, is_polygon=True):
"""converts the string from a polygon points-attribute to a string
for a Path object d-attribute.
Note: For a polygon made from n points, the resulting path will be
composed of n lines (even if some of these lines have length zero).
"""
- return polyline2pathd(polyline, True)
+ return polyline2pathd(polyline, is_polygon)
def rect2pathd(rect):
@@ -205,7 +230,7 @@ def dom2dict(element):
# path strings, add to list
if convert_polygons_to_paths:
pgons = [dom2dict(el) for el in doc.getElementsByTagName('polygon')]
- d_strings += [polygon2pathd(pg) for pg in pgons]
+ d_strings += [polygon2pathd(pg, True) for pg in pgons]
attribute_dictionary_list += pgons
if convert_lines_to_paths:
diff --git a/test/polygons_no_points.svg b/test/polygons_no_points.svg
new file mode 100644
index 0000000..284c254
--- /dev/null
+++ b/test/polygons_no_points.svg
@@ -0,0 +1,5 @@
+
+
diff --git a/test/polyline.svg b/test/polyline.svg
new file mode 100644
index 0000000..761aa29
--- /dev/null
+++ b/test/polyline.svg
@@ -0,0 +1,7 @@
+
+
diff --git a/test/test_svg2paths.py b/test/test_svg2paths.py
index 2caae61..2d176bd 100644
--- a/test/test_svg2paths.py
+++ b/test/test_svg2paths.py
@@ -168,6 +168,44 @@ def test_from_string(self):
self.assertEqual(len(paths), 2)
+ def test_svg2paths_polygon_no_points(self):
+
+ paths, _ = svg2paths(join(dirname(__file__), 'polygons_no_points.svg'))
+
+ path = paths[0]
+ path_correct = Path()
+ self.assertTrue(len(path)==0)
+ self.assertTrue(path==path_correct)
+
+ path = paths[1]
+ self.assertTrue(len(path)==0)
+ self.assertTrue(path==path_correct)
+
+ def test_svg2paths_polyline_tests(self):
+
+ paths, _ = svg2paths(join(dirname(__file__), 'polyline.svg'))
+
+ path = paths[0]
+ path_correct = Path(Line(59+185j, 98+203j),
+ Line(98+203j, 108+245j),
+ Line(108+245j, 82+279j),
+ Line(82+279j, 39+280j),
+ Line(39+280j, 11+247j),
+ Line(11+247j, 19+205j))
+ self.assertFalse(path.isclosed())
+ self.assertTrue(len(path)==6)
+ self.assertTrue(path==path_correct)
+
+ path = paths[1]
+ path_correct = Path(Line(220+50j, 267+84j),
+ Line(267+84j, 249+140j),
+ Line(249+140j, 190+140j),
+ Line(190+140j, 172+84j),
+ Line(172+84j, 220+50j))
+ self.assertTrue(path.isclosed())
+ self.assertTrue(len(path)==5)
+ self.assertTrue(path==path_correct)
+
if __name__ == '__main__':
unittest.main()