Fixed #16
Groups are now taken into account during parsing and transform attributes of groups are applied to the paths before they are returned.pull/20/merge^2
parent
7be111bba8
commit
dcf8203a42
|
@ -5,9 +5,12 @@ The main tool being the svg2paths() function."""
|
||||||
from __future__ import division, absolute_import, print_function
|
from __future__ import division, absolute_import, print_function
|
||||||
from xml.dom.minidom import parse
|
from xml.dom.minidom import parse
|
||||||
from os import path as os_path, getcwd
|
from os import path as os_path, getcwd
|
||||||
|
from shutil import copyfile
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
# Internal dependencies
|
# Internal dependencies
|
||||||
from .parser import parse_path
|
from .parser import parse_path
|
||||||
|
from .path import Path, bpoints2bezier
|
||||||
|
|
||||||
|
|
||||||
def polyline2pathd(polyline_d):
|
def polyline2pathd(polyline_d):
|
||||||
|
@ -71,51 +74,122 @@ def svg2paths(svg_file_location,
|
||||||
svg-attributes will be extracted and returned
|
svg-attributes will be extracted and returned
|
||||||
:return: list of Path objects, list of path attribute dictionaries, and
|
:return: list of Path objects, list of path attribute dictionaries, and
|
||||||
(optionally) a dictionary of svg-attributes
|
(optionally) a dictionary of svg-attributes
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if os_path.dirname(svg_file_location) == '':
|
if os_path.dirname(svg_file_location) == '':
|
||||||
svg_file_location = os_path.join(getcwd(), svg_file_location)
|
svg_file_location = os_path.join(getcwd(), svg_file_location)
|
||||||
|
|
||||||
|
# if pathless_svg:
|
||||||
|
# copyfile(svg_file_location, pathless_svg)
|
||||||
|
# doc = parse(pathless_svg)
|
||||||
|
# else:
|
||||||
doc = parse(svg_file_location)
|
doc = parse(svg_file_location)
|
||||||
|
|
||||||
|
# Parse a list of paths
|
||||||
def dom2dict(element):
|
def dom2dict(element):
|
||||||
"""Converts DOM elements to dictionaries of attributes."""
|
"""Converts DOM elements to dictionaries of attributes."""
|
||||||
keys = list(element.attributes.keys())
|
keys = list(element.attributes.keys())
|
||||||
values = [val.value for val in list(element.attributes.values())]
|
values = [val.value for val in list(element.attributes.values())]
|
||||||
return dict(list(zip(keys, values)))
|
return dict(list(zip(keys, values)))
|
||||||
|
|
||||||
# Use minidom to extract path strings from input SVG
|
def parseTrafo(trafoStr):
|
||||||
paths = [dom2dict(el) for el in doc.getElementsByTagName('path')]
|
"""Returns six matrix elements for a matrix transformation for any valid SVG transformation string."""
|
||||||
d_strings = [el['d'] for el in paths]
|
#print(trafoStr)
|
||||||
attribute_dictionary_list = paths
|
valueStr = trafoStr.split('(')[1].split(')')[0]
|
||||||
|
values = list(map(float, valueStr.split(',')))
|
||||||
|
if 'translate' in trafoStr:
|
||||||
|
x = values[0]
|
||||||
|
y = values[1] if (len(values) > 1) else 0.
|
||||||
|
return [1.,0.,0.,1.,x,y]
|
||||||
|
elif 'scale' in trafoStr:
|
||||||
|
x = values[0]
|
||||||
|
y = values[1] if (len(values) > 1) else 0.
|
||||||
|
return [x,0.,0.,y,0.,0.]
|
||||||
|
elif 'rotate' in trafoStr:
|
||||||
|
a = values[0]
|
||||||
|
x = values[1] if (len(values) > 1) else 0.
|
||||||
|
y = values[2] if (len(values) > 2) else 0.
|
||||||
|
A = np.dot([cos(a),sin(a),-sin(a),cos(a),0.,0.,0.,0.,1.].reshape((3,3)),[1.,0.,0.,1.,-x,-y,0.,0.,1.].reshape((3,3)))
|
||||||
|
A = list(np.dot([1.,0.,0.,1.,x,y,0.,0.,1.].reshape((3,3)),A).reshape((9,))[:6])
|
||||||
|
return A
|
||||||
|
elif 'skewX' in trafoStr:
|
||||||
|
a = values[0]
|
||||||
|
return [1.,0.,tan(a),1.,0.,0.]
|
||||||
|
elif 'skewY' in trafoStr:
|
||||||
|
a = values[0]
|
||||||
|
return [1.,tan(a),0.,1.,0.,0.]
|
||||||
|
else:
|
||||||
|
while len(values) < 6:
|
||||||
|
values += [0.]
|
||||||
|
return values
|
||||||
|
|
||||||
# Use minidom to extract polyline strings from input SVG, convert to
|
def parseNode(node):
|
||||||
# path strings, add to list
|
"""Recursively iterate over nodes. Parse the groups individually to apply group transformations."""
|
||||||
if convert_polylines_to_paths:
|
# Get everything in this tag
|
||||||
plins = [dom2dict(el) for el in doc.getElementsByTagName('polyline')]
|
#ret_list, attribute_dictionary_list = [parseNode(child) for child in node.childNodes]
|
||||||
d_strings += [polyline2pathd(pl['points']) for pl in plins]
|
data = [parseNode(child) for child in node.childNodes]
|
||||||
attribute_dictionary_list += plins
|
if len(data) == 0:
|
||||||
|
ret_list = []
|
||||||
|
attribute_dictionary_list = []
|
||||||
|
else:
|
||||||
|
# Flatten the lists
|
||||||
|
ret_list = []
|
||||||
|
attribute_dictionary_list = []
|
||||||
|
for item in data:
|
||||||
|
if type(item) == tuple:
|
||||||
|
if len(item[0]) > 0:
|
||||||
|
ret_list += item[0]
|
||||||
|
attribute_dictionary_list += item[1]
|
||||||
|
|
||||||
# Use minidom to extract polygon strings from input SVG, convert to
|
if node.nodeName == 'g':
|
||||||
# path strings, add to list
|
# Group found
|
||||||
if convert_polygons_to_paths:
|
# Analyse group properties
|
||||||
pgons = [dom2dict(el) for el in doc.getElementsByTagName('polygon')]
|
group = dom2dict(node)
|
||||||
d_strings += [polygon2pathd(pg['points']) for pg in pgons]
|
if 'transform' in group.keys():
|
||||||
attribute_dictionary_list += pgons
|
trafo = group['transform']
|
||||||
|
|
||||||
if convert_lines_to_paths:
|
# Convert all transformations into a matrix operation
|
||||||
lines = [dom2dict(el) for el in doc.getElementsByTagName('line')]
|
A = parseTrafo(trafo)
|
||||||
d_strings += [('M' + l['x1'] + ' ' + l['y1'] +
|
A = np.array([A[::2],A[1::2],[0.,0.,1.]])
|
||||||
'L' + l['x2'] + ' ' + l['y2']) for l in lines]
|
|
||||||
attribute_dictionary_list += lines
|
|
||||||
|
|
||||||
|
# Apply transformation to all elements of the paths
|
||||||
|
xy = lambda z: np.array([z.real, z.imag, 1.])
|
||||||
|
z = lambda xy: xy[0] + 1j*xy[1]
|
||||||
|
|
||||||
|
ret_list = [Path(*[bpoints2bezier([z(np.dot(A,xy(pt)))
|
||||||
|
for pt in seg.bpoints()])
|
||||||
|
for seg in path])
|
||||||
|
for path in ret_list]
|
||||||
|
return ret_list, attribute_dictionary_list
|
||||||
|
elif node.nodeName == 'path':
|
||||||
|
# Path found; parsing it
|
||||||
|
path = dom2dict(node)
|
||||||
|
d_string = path['d']
|
||||||
|
return [parse_path(d_string)]+ret_list, [path]+attribute_dictionary_list
|
||||||
|
elif convert_polylines_to_paths and node.nodeName == 'polyline':
|
||||||
|
attrs = dom2dict(node)
|
||||||
|
path = parse_path(polyline2pathd(node['points']))
|
||||||
|
return [path]+ret_list, [attrs]+attribute_dictionary_list
|
||||||
|
elif convert_polygons_to_paths and node.nodeName == 'polygon':
|
||||||
|
attrs = dom2dict(node)
|
||||||
|
path = parse_path(polygon2pathd(node['points']))
|
||||||
|
return [path]+ret_list, [attrs]+attribute_dictionary_list
|
||||||
|
elif convert_lines_to_paths and node.nodeName == 'line':
|
||||||
|
line = dom2dict(node)
|
||||||
|
d_string = ('M' + line['x1'] + ' ' + line['y1'] +
|
||||||
|
'L' + line['x2'] + ' ' + line['y2'])
|
||||||
|
path = parse_path(d_string)
|
||||||
|
return [path]+ret_list, [line]+attribute_dictionary_list
|
||||||
|
else:
|
||||||
|
return ret_list, attribute_dictionary_list
|
||||||
|
|
||||||
|
path_list, attribute_dictionary_list = parseNode(doc)
|
||||||
if return_svg_attributes:
|
if return_svg_attributes:
|
||||||
svg_attributes = dom2dict(doc.getElementsByTagName('svg')[0])
|
svg_attributes = dom2dict(doc.getElementsByTagName('svg')[0])
|
||||||
doc.unlink()
|
doc.unlink()
|
||||||
path_list = [parse_path(d) for d in d_strings]
|
|
||||||
return path_list, attribute_dictionary_list, svg_attributes
|
return path_list, attribute_dictionary_list, svg_attributes
|
||||||
else:
|
else:
|
||||||
doc.unlink()
|
doc.unlink()
|
||||||
path_list = [parse_path(d) for d in d_strings]
|
|
||||||
return path_list, attribute_dictionary_list
|
return path_list, attribute_dictionary_list
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue