Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Order is Not Preserved #210

Open
bcwhite-code opened this issue Jun 4, 2023 · 1 comment
Open

Order is Not Preserved #210

bcwhite-code opened this issue Jun 4, 2023 · 1 comment

Comments

@bcwhite-code
Copy link

The processing of SVG elements occurs by type (in order: path, polyline, polygon, line, ellipse, circle, and rect) by calling doc.getElementsByTagName() for each type. This ends up returning the paths to the caller in a possibly very different order than they were specified in the original SVG file.

For simple lines, this typically isn't a problem but if some of those shapes are styled to "fill" then it is a big problem. For example, a rectangle at the bottom (e.g. a base color) will end up on top and obscure everything else.

@bcwhite-code
Copy link
Author

Here's my fix for it:

diff --git a/svgpathtools/svg_to_paths.py b/svgpathtools/svg_to_paths.py
index 108cd98..5cca289 100644
--- a/svgpathtools/svg_to_paths.py
+++ b/svgpathtools/svg_to_paths.py
@@ -205,45 +205,40 @@ def svg2paths(svg_file_location,
     # Short name for all the dom2dict calls below
     ip = include_parent_info

-    # Use minidom to extract path strings from input SVG
-    paths = [dom2dict(el,ip) for el in doc.getElementsByTagName('path')]
-    d_strings = [el['d'] for el in paths]
-    attribute_dictionary_list = paths
-
-    # Use minidom to extract polyline strings from input SVG, convert to
-    # path strings, add to list
-    if convert_polylines_to_paths:
-        plins = [dom2dict(el,ip) for el in doc.getElementsByTagName('polyline')]
-        d_strings += [polyline2pathd(pl) for pl in plins]
-        attribute_dictionary_list += plins
-
-    # Use minidom to extract polygon strings from input SVG, convert to
-    # path strings, add to list
-    if convert_polygons_to_paths:
-        pgons = [dom2dict(el,ip) for el in doc.getElementsByTagName('polygon')]
-        d_strings += [polygon2pathd(pg) for pg in pgons]
-        attribute_dictionary_list += pgons
-
-    if convert_lines_to_paths:
-        lines = [dom2dict(el,ip) for el in doc.getElementsByTagName('line')]
-        d_strings += [('M' + l['x1'] + ' ' + l['y1'] +
-                       'L' + l['x2'] + ' ' + l['y2']) for l in lines]
-        attribute_dictionary_list += lines
-
-    if convert_ellipses_to_paths:
-        ellipses = [dom2dict(el,ip) for el in doc.getElementsByTagName('ellipse')]
-        d_strings += [ellipse2pathd(e) for e in ellipses]
-        attribute_dictionary_list += ellipses
-
-    if convert_circles_to_paths:
-        circles = [dom2dict(el,ip) for el in doc.getElementsByTagName('circle')]
-        d_strings += [ellipse2pathd(c) for c in circles]
-        attribute_dictionary_list += circles
-
-    if convert_rectangles_to_paths:
-        rectangles = [dom2dict(el,ip) for el in doc.getElementsByTagName('rect')]
-        d_strings += [rect2pathd(r) for r in rectangles]
-        attribute_dictionary_list += rectangles
+    d_strings = []
+    attribute_dictionary_list = []
+
+    # Use minidom to extract path strings from input SVG.
+    # Each type is handled seperately but the overall order is preserved.
+    for el in doc.getElementsByTagName('*'):
+        if el.tagName == 'path':
+            path_data = dom2dict(el, ip)
+            d_strings.append(path_data['d'])
+            attribute_dictionary_list.append(path_data)
+        elif el.tagName == 'polyline' and convert_polylines_to_paths:
+            polyline_data = dom2dict(el, ip)
+            d_strings.append(polyline2pathd(polyline_data))
+            attribute_dictionary_list.append(polyline_data)
+        elif el.tagName == 'polygon' and convert_polygons_to_paths:
+            polygon_data = dom2dict(el, ip)
+            d_strings.append(polygon2pathd(polygon_data))
+            attribute_dictionary_list.append(polygon_data)
+        elif el.tagName == 'line' and convert_lines_to_paths:
+            line_data = dom2dict(el, ip)
+            d_strings.append(f"M{line_data['x1']} {line_data['y1']} L{line_data['x2']} {line_data['y2']}")
+            attribute_dictionary_list.append(line_data)
+        elif el.tagName == 'ellipse' and convert_ellipses_to_paths:
+            ellipse_data = dom2dict(el, ip)
+            d_strings.append(ellipse2pathd(ellipse_data))
+            attribute_dictionary_list.append(ellipse_data)
+        elif el.tagName == 'circle' and convert_circles_to_paths:
+            circle_data = dom2dict(el, ip)
+            d_strings.append(ellipse2pathd(circle_data))
+            attribute_dictionary_list.append(circle_data)
+        elif el.tagName == 'rect' and convert_rectangles_to_paths:
+            rect_data = dom2dict(el, ip)
+            d_strings.append(rect2pathd(rect_data))
+            attribute_dictionary_list.append(rect_data)

     path_list = [parse_path(d) for d in d_strings]
     retval = [path_list, attribute_dictionary_list]

Note that this includes the ip ("include parents") flag to dom2dict() that I described in another issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant