From 2dcfa88cf2acceb4702eb295c46203f554d0a201 Mon Sep 17 00:00:00 2001 From: Dmitry_Milk Date: Sun, 27 May 2018 11:47:44 +0300 Subject: [PATCH 1/2] Adjust polyline/polygon parsing to W3C specification https://www.w3.org/TR/SVG11/shapes.html#PointsBNF --- svgpathtools/svg2paths.py | 44 ++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/svgpathtools/svg2paths.py b/svgpathtools/svg2paths.py index 8f62932..7c9285a 100644 --- a/svgpathtools/svg2paths.py +++ b/svgpathtools/svg2paths.py @@ -5,10 +5,16 @@ The main tool being the svg2paths() function.""" from __future__ import division, absolute_import, print_function from xml.dom.minidom import parse from os import path as os_path, getcwd +import re # Internal dependencies from .parser import parse_path +COORD_PAIR_TMPLT = re.compile( + r'([\+-]?\d*[\.\d]\d*[eE][\+-]?\d+|[\+-]?\d*[\.\d]\d*)' + + r'(?:\s*,\s*|\s+|(?=-))' + + r'([\+-]?\d*[\.\d]\d*[eE][\+-]?\d+|[\+-]?\d*[\.\d]\d*)' +) def ellipse2pathd(ellipse): """converts the parameters from an ellipse or a circle to a string for a @@ -37,19 +43,21 @@ def ellipse2pathd(ellipse): return d -def polyline2pathd(polyline_d): +def polyline2pathd(polyline_d, is_polygon=False): """converts the string from a polyline points-attribute to a string for a Path object d-attribute""" - points = polyline_d.replace(', ', ',') - points = points.replace(' ,', ',') - points = points.split() + points = COORD_PAIR_TMPLT.findall(polyline_d) + closed = (float(points[0][0]) == float(points[-1][0]) and + float(points[0][1]) == float(points[-1][1])) - closed = points[0] == points[-1] + # The `parse_path` call ignores redundant 'z' (closure) commands + # e.g. `parse_path('M0 0L100 100Z') == parse_path('M0 0L100 100L0 0Z')` + # This check ensures that an n-point polygon is converted to an n-Line path. + if is_polygon and closed: + points.append(points[0]) - d = 'M' + points.pop(0).replace(',', ' ') - for p in points: - d += 'L' + p.replace(',', ' ') - if closed: + d = 'M' + 'L'.join('{0} {1}'.format(x,y) for x,y in points) + if is_polygon or closed: d += 'z' return d @@ -59,23 +67,7 @@ def polygon2pathd(polyline_d): 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).""" - points = polyline_d.replace(', ', ',') - points = points.replace(' ,', ',') - points = points.split() - - reduntantly_closed = points[0] == points[-1] - - d = 'M' + points[0].replace(',', ' ') - for p in points[1:]: - d += 'L' + p.replace(',', ' ') - - # The `parse_path` call ignores redundant 'z' (closure) commands - # e.g. `parse_path('M0 0L100 100Z') == parse_path('M0 0L100 100L0 0Z')` - # This check ensures that an n-point polygon is converted to an n-Line path. - if reduntantly_closed: - d += 'L' + points[0].replace(',', ' ') - - return d + 'z' + return polyline2pathd(polyline_d, True) def rect2pathd(rect): From 321e097a4ee6f3894f51d6cc1ea01851aeb70a32 Mon Sep 17 00:00:00 2001 From: Dmitry_Milk Date: Sun, 27 May 2018 12:34:12 +0300 Subject: [PATCH 2/2] polygon/polyline parsing tests added --- test/polygons.svg | 2 +- test/test_svg2paths.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/polygons.svg b/test/polygons.svg index ab6c9fd..c310e29 100644 --- a/test/polygons.svg +++ b/test/polygons.svg @@ -1,5 +1,5 @@ - + diff --git a/test/test_svg2paths.py b/test/test_svg2paths.py index 45a4b48..90a23e0 100644 --- a/test/test_svg2paths.py +++ b/test/test_svg2paths.py @@ -20,9 +20,9 @@ class TestSVG2Paths(unittest.TestCase): # triangular quadrilateral (with a redundant 4th "closure" point) path = paths[1] - path_correct = Path(Line(0+0j, 0+100j), - Line(0+100j, 100+100j), - Line(100+100j, 0+0j), + path_correct = Path(Line(0+0j, 0-100j), + Line(0-100j, 0.1-100j), + Line(0.1-100j, 0+0j), Line(0+0j, 0+0j) # result of redundant point ) self.assertTrue(path.isclosed())