From a8228341bbf39b1cc8805155969aff4976b24690 Mon Sep 17 00:00:00 2001 From: Harikrushna Parmar Date: Wed, 29 May 2024 12:23:39 +0530 Subject: [PATCH 1/3] MGG-1299 [#ccc] svgpathtools: Make arc as default, and cubic curve as option References: MGG-1481, MGG-1656 When H/W does not have 'arc' command, circrle or ellispe can be drawn using cubic curves. Start ellipse path from rightmost point and draw clockwise Signed-off-by: Harikrushna Parmar --- svgpathtools/svg_to_paths.py | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/svgpathtools/svg_to_paths.py b/svgpathtools/svg_to_paths.py index c6ac6df..0bfbf39 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' From b174f99e511f18a0b7fcbc2b4ac0c49b60b88a56 Mon Sep 17 00:00:00 2001 From: Harikrushna Parmar Date: Mon, 2 Sep 2024 10:19:44 +0530 Subject: [PATCH 2/3] MGG-1402 [#ccc] svgpathtools:bugfix: Skip the end path in a polyline if no dedicated end path is provided References: MGG-1402, MGG-1448 * Handle case when no points are specified in polygon. * Update is_polygon properly for rectangle, polygon and polyline. Signed-off-by: Harikrushna Parmar --- svgpathtools/svg_to_paths.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/svgpathtools/svg_to_paths.py b/svgpathtools/svg_to_paths.py index 0bfbf39..aa55869 100644 --- a/svgpathtools/svg_to_paths.py +++ b/svgpathtools/svg_to_paths.py @@ -84,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])) @@ -99,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): @@ -227,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: From aeb799b5aa34249eb8bf4188f8e04c62bfe934c0 Mon Sep 17 00:00:00 2001 From: Nilam Gaikwad Date: Mon, 19 May 2025 16:37:33 +0530 Subject: [PATCH 3/3] MGG-1656 [#ccc] svgpathtools: Adding test cases to validate fixes * Added polyline.svg and polygons_no_poits.svg to validate recent changes to svgpathtools Signed-off-by: Nilam Gaikwad --- test/polygons_no_points.svg | 5 +++++ test/polyline.svg | 7 +++++++ test/test_svg2paths.py | 38 +++++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 test/polygons_no_points.svg create mode 100644 test/polyline.svg 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()