diff --git a/svgpathtools/svg_to_paths.py b/svgpathtools/svg_to_paths.py index 65591af2..5cca289d 100644 --- a/svgpathtools/svg_to_paths.py +++ b/svgpathtools/svg_to_paths.py @@ -3,7 +3,7 @@ # External dependencies from __future__ import division, absolute_import, print_function -from xml.dom.minidom import parse +from xml.dom.minidom import parse, Node import os from io import StringIO import re @@ -62,6 +62,7 @@ def polyline2pathd(polyline, is_polygon=False): else: points = COORD_PAIR_TMPLT.findall(polyline.get('points', '')) + if not points or len(points) < 2: return "" closed = (float(points[0][0]) == float(points[-1][0]) and float(points[0][1]) == float(points[-1][1])) @@ -136,6 +137,8 @@ def line2pathd(l): def svg2paths(svg_file_location, return_svg_attributes=False, + return_other_tags=None, + include_parent_info=False, convert_circles_to_paths=True, convert_ellipses_to_paths=True, convert_lines_to_paths=True, @@ -182,61 +185,81 @@ def svg2paths(svg_file_location, doc = parse(svg_file_location) - def dom2dict(element): - """Converts DOM elements to dictionaries of attributes.""" - keys = list(element.attributes.keys()) - values = [val.value for val in list(element.attributes.values())] + def dom2dict(element, include_parents=False): + """Converts DOM elements to dictionaries of attributes, including parent information.""" + if element.attributes is not None: + keys = list(element.attributes.keys()) + values = [val.value for val in list(element.attributes.values())] + else: + keys = [] + values = [] + if include_parents: + keys.append('_tag') + values.append(element.tagName) + parent = element.parentNode + if parent is not None and parent.nodeType == Node.ELEMENT_NODE and parent.tagName != 'svg': + keys.append('_parent') + values.append(dom2dict(parent, True)) return dict(list(zip(keys, values))) - # Use minidom to extract path strings from input SVG - paths = [dom2dict(el) 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) 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) 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) 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) 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) 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) for el in doc.getElementsByTagName('rect')] - d_strings += [rect2pathd(r) for r in rectangles] - attribute_dictionary_list += rectangles + # Short name for all the dom2dict calls below + ip = include_parent_info + + 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] if return_svg_attributes: svg_attributes = dom2dict(doc.getElementsByTagName('svg')[0]) - doc.unlink() - path_list = [parse_path(d) for d in d_strings] - return path_list, attribute_dictionary_list, svg_attributes - else: - doc.unlink() - path_list = [parse_path(d) for d in d_strings] - return path_list, attribute_dictionary_list + retval.append(svg_attributes) + + if return_other_tags is not None: + other_tags = {} + for other in return_other_tags: + other_tags[other] = [] + elements = doc.getElementsByTagName(other) + for el in elements: + taginfo = dom2dict(el,ip) + taginfo['_value'] = el.firstChild.nodeValue if el.firstChild is not None else None + other_tags[other].append(taginfo) + retval.append(other_tags) + + doc.unlink() + return retval def svg2paths2(svg_file_location, @@ -262,6 +285,8 @@ def svg2paths2(svg_file_location, def svgstr2paths(svg_string, return_svg_attributes=False, + return_other_tags=None, + include_parent_info=False, convert_circles_to_paths=True, convert_ellipses_to_paths=True, convert_lines_to_paths=True, @@ -275,6 +300,8 @@ def svgstr2paths(svg_string, svg_file_obj = StringIO(svg_string) return svg2paths(svg_file_location=svg_file_obj, return_svg_attributes=return_svg_attributes, + return_other_tags=return_other_tags, + include_parent_info=include_parent_info, convert_circles_to_paths=convert_circles_to_paths, convert_ellipses_to_paths=convert_ellipses_to_paths, convert_lines_to_paths=convert_lines_to_paths,