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()