Basic support for importing SVG. Via shell only at this time. See issue #179.
This commit is contained in:
@@ -1604,6 +1604,34 @@ class App(QtCore.QObject):
|
|||||||
else:
|
else:
|
||||||
self.inform.emit("Project copy saved to: " + self.project_filename)
|
self.inform.emit("Project copy saved to: " + self.project_filename)
|
||||||
|
|
||||||
|
def import_svg(self, filename, outname=None):
|
||||||
|
"""
|
||||||
|
Adds a new Geometry Object to the projects and populates
|
||||||
|
it with shapes extracted from the SVG file.
|
||||||
|
|
||||||
|
:param filename: Path to the SVG file.
|
||||||
|
:param outname:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
def obj_init(geo_obj, app_obj):
|
||||||
|
|
||||||
|
geo_obj.import_svg(filename)
|
||||||
|
|
||||||
|
with self.proc_container.new("Importing SVG") as proc:
|
||||||
|
|
||||||
|
# Object name
|
||||||
|
name = outname or filename.split('/')[-1].split('\\')[-1]
|
||||||
|
|
||||||
|
self.new_object("geometry", name, obj_init)
|
||||||
|
|
||||||
|
# TODO: No support for this yet.
|
||||||
|
# Register recent file
|
||||||
|
# self.file_opened.emit("gerber", filename)
|
||||||
|
|
||||||
|
# GUI feedback
|
||||||
|
self.inform.emit("Opened: " + filename)
|
||||||
|
|
||||||
def open_gerber(self, filename, follow=False, outname=None):
|
def open_gerber(self, filename, follow=False, outname=None):
|
||||||
"""
|
"""
|
||||||
Opens a Gerber file, parses it and creates a new object for
|
Opens a Gerber file, parses it and creates a new object for
|
||||||
@@ -1959,6 +1987,17 @@ class App(QtCore.QObject):
|
|||||||
|
|
||||||
return a, kwa
|
return a, kwa
|
||||||
|
|
||||||
|
def import_svg(filename, *args):
|
||||||
|
a, kwa = h(*args)
|
||||||
|
types = {'outname': str}
|
||||||
|
|
||||||
|
for key in kwa:
|
||||||
|
if key not in types:
|
||||||
|
return 'Unknown parameter: %s' % key
|
||||||
|
kwa[key] = types[key](kwa[key])
|
||||||
|
|
||||||
|
self.import_svg(str(filename), **kwa)
|
||||||
|
|
||||||
def open_gerber(filename, *args):
|
def open_gerber(filename, *args):
|
||||||
a, kwa = h(*args)
|
a, kwa = h(*args)
|
||||||
types = {'follow': bool,
|
types = {'follow': bool,
|
||||||
@@ -2556,6 +2595,12 @@ class App(QtCore.QObject):
|
|||||||
'fcn': shelp,
|
'fcn': shelp,
|
||||||
'help': "Shows list of commands."
|
'help': "Shows list of commands."
|
||||||
},
|
},
|
||||||
|
'import_svg': {
|
||||||
|
'fcn': import_svg,
|
||||||
|
'help': "Import an SVG file as a Geometry Object.\n" +
|
||||||
|
"> import_svg <filename>" +
|
||||||
|
" filename: Path to the file to import."
|
||||||
|
},
|
||||||
'open_gerber': {
|
'open_gerber': {
|
||||||
'fcn': open_gerber,
|
'fcn': open_gerber,
|
||||||
'help': "Opens a Gerber file.\n' +"
|
'help': "Opens a Gerber file.\n' +"
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ class PlotCanvas:
|
|||||||
|
|
||||||
def connect(self, event_name, callback):
|
def connect(self, event_name, callback):
|
||||||
"""
|
"""
|
||||||
Attach an event handler to the canvas through the native GTK interface.
|
Attach an event handler to the canvas through the native Qt interface.
|
||||||
|
|
||||||
:param event_name: Name of the event
|
:param event_name: Name of the event
|
||||||
:type event_name: str
|
:type event_name: str
|
||||||
|
|||||||
41
camlib.py
41
camlib.py
@@ -42,6 +42,16 @@ import simplejson as json
|
|||||||
# TODO: Commented for FlatCAM packaging with cx_freeze
|
# TODO: Commented for FlatCAM packaging with cx_freeze
|
||||||
#from matplotlib.pyplot import plot, subplot
|
#from matplotlib.pyplot import plot, subplot
|
||||||
|
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
from svg.path import Path, Line, Arc, CubicBezier, QuadraticBezier, parse_path
|
||||||
|
import itertools
|
||||||
|
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
from svg.path import Path, Line, Arc, CubicBezier, QuadraticBezier, parse_path
|
||||||
|
|
||||||
|
|
||||||
|
from svgparse import *
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
log = logging.getLogger('base2')
|
log = logging.getLogger('base2')
|
||||||
@@ -193,7 +203,6 @@ class Geometry(object):
|
|||||||
|
|
||||||
return interiors
|
return interiors
|
||||||
|
|
||||||
|
|
||||||
def get_exteriors(self, geometry=None):
|
def get_exteriors(self, geometry=None):
|
||||||
"""
|
"""
|
||||||
Returns all exteriors of polygons in geometry. Uses
|
Returns all exteriors of polygons in geometry. Uses
|
||||||
@@ -344,6 +353,36 @@ class Geometry(object):
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def import_svg(self, filename):
|
||||||
|
"""
|
||||||
|
Imports shapes from an SVG file into the object's geometry.
|
||||||
|
|
||||||
|
:param filename: Path to the SVG file.
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Parse into list of shapely objects
|
||||||
|
svg_tree = ET.parse(filename)
|
||||||
|
svg_root = svg_tree.getroot()
|
||||||
|
|
||||||
|
# Change origin to bottom left
|
||||||
|
h = float(svg_root.get('height'))
|
||||||
|
# w = float(svg_root.get('width'))
|
||||||
|
geos = getsvggeo(svg_root)
|
||||||
|
geo_flip = [translate(scale(g, 1.0, -1.0, origin=(0, 0)), yoff=h) for g in geos]
|
||||||
|
|
||||||
|
# Add to object
|
||||||
|
if self.solid_geometry is None:
|
||||||
|
self.solid_geometry = []
|
||||||
|
|
||||||
|
if type(self.solid_geometry) is list:
|
||||||
|
self.solid_geometry.append(cascaded_union(geo_flip))
|
||||||
|
else: # It's shapely geometry
|
||||||
|
self.solid_geometry = cascaded_union([self.solid_geometry,
|
||||||
|
cascaded_union(geo_flip)])
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
def size(self):
|
def size(self):
|
||||||
"""
|
"""
|
||||||
Returns (width, height) of rectangular
|
Returns (width, height) of rectangular
|
||||||
|
|||||||
268
svgparse.py
Normal file
268
svgparse.py
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
############################################################
|
||||||
|
# FlatCAM: 2D Post-processing for Manufacturing #
|
||||||
|
# http://flatcam.org #
|
||||||
|
# Author: Juan Pablo Caram (c) #
|
||||||
|
# Date: 12/18/2015 #
|
||||||
|
# MIT Licence #
|
||||||
|
############################################################
|
||||||
|
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
import re
|
||||||
|
import itertools
|
||||||
|
from svg.path import Path, Line, Arc, CubicBezier, QuadraticBezier, parse_path
|
||||||
|
from shapely.geometry import LinearRing, LineString
|
||||||
|
from shapely.affinity import translate, rotate, scale, skew, affine_transform
|
||||||
|
|
||||||
|
|
||||||
|
def path2shapely(path, res=1.0):
|
||||||
|
"""
|
||||||
|
Converts an svg.path.Path into a Shapely
|
||||||
|
LinearRing or LinearString.
|
||||||
|
|
||||||
|
:rtype : LinearRing
|
||||||
|
:rtype : LineString
|
||||||
|
:param path: svg.path.Path instance
|
||||||
|
:param res: Resolution (minimum step along path)
|
||||||
|
:return: Shapely geometry object
|
||||||
|
"""
|
||||||
|
points = []
|
||||||
|
|
||||||
|
for component in path:
|
||||||
|
|
||||||
|
# Line
|
||||||
|
if isinstance(component, Line):
|
||||||
|
start = component.start
|
||||||
|
x, y = start.real, start.imag
|
||||||
|
if len(points) == 0 or points[-1] != (x, y):
|
||||||
|
points.append((x, y))
|
||||||
|
end = component.end
|
||||||
|
points.append((end.real, end.imag))
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Arc, CubicBezier or QuadraticBezier
|
||||||
|
if isinstance(component, Arc) or \
|
||||||
|
isinstance(component, CubicBezier) or \
|
||||||
|
isinstance(component, QuadraticBezier):
|
||||||
|
length = component.length(res / 10.0)
|
||||||
|
steps = int(length / res + 0.5)
|
||||||
|
frac = 1.0 / steps
|
||||||
|
print length, steps, frac
|
||||||
|
for i in range(steps):
|
||||||
|
point = component.point(i * frac)
|
||||||
|
x, y = point.real, point.imag
|
||||||
|
if len(points) == 0 or points[-1] != (x, y):
|
||||||
|
points.append((x, y))
|
||||||
|
end = component.point(1.0)
|
||||||
|
points.append((end.real, end.imag))
|
||||||
|
continue
|
||||||
|
|
||||||
|
print "I don't know what this is:", component
|
||||||
|
continue
|
||||||
|
|
||||||
|
if path.closed:
|
||||||
|
return LinearRing(points)
|
||||||
|
else:
|
||||||
|
return LineString(points)
|
||||||
|
|
||||||
|
|
||||||
|
def svgrect2shapely(rect):
|
||||||
|
w = float(rect.get('width'))
|
||||||
|
h = float(rect.get('height'))
|
||||||
|
x = float(rect.get('x'))
|
||||||
|
y = float(rect.get('y'))
|
||||||
|
pts = [
|
||||||
|
(x, y), (x + w, y), (x + w, y + h), (x, y + h), (x, y)
|
||||||
|
]
|
||||||
|
return LinearRing(pts)
|
||||||
|
|
||||||
|
|
||||||
|
def getsvggeo(node):
|
||||||
|
"""
|
||||||
|
Extracts and flattens all geometry from an SVG node
|
||||||
|
into a list of Shapely geometry.
|
||||||
|
|
||||||
|
:param node:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
kind = re.search('(?:\{.*\})?(.*)$', node.tag).group(1)
|
||||||
|
geo = []
|
||||||
|
|
||||||
|
# Recurse
|
||||||
|
if len(node) > 0:
|
||||||
|
for child in node:
|
||||||
|
subgeo = getsvggeo(child)
|
||||||
|
if subgeo is not None:
|
||||||
|
geo += subgeo
|
||||||
|
|
||||||
|
# Parse
|
||||||
|
elif kind == 'path':
|
||||||
|
print "***PATH***"
|
||||||
|
P = parse_path(node.get('d'))
|
||||||
|
P = path2shapely(P)
|
||||||
|
geo = [P]
|
||||||
|
|
||||||
|
elif kind == 'rect':
|
||||||
|
print "***RECT***"
|
||||||
|
R = svgrect2shapely(node)
|
||||||
|
geo = [R]
|
||||||
|
|
||||||
|
else:
|
||||||
|
print "Unknown kind:", kind
|
||||||
|
geo = None
|
||||||
|
|
||||||
|
# Transformations
|
||||||
|
if 'transform' in node.attrib:
|
||||||
|
trstr = node.get('transform')
|
||||||
|
trlist = parse_svg_transform(trstr)
|
||||||
|
print trlist
|
||||||
|
|
||||||
|
# Transformations are applied in reverse order
|
||||||
|
for tr in trlist[::-1]:
|
||||||
|
if tr[0] == 'translate':
|
||||||
|
geo = [translate(geoi, tr[1], tr[2]) for geoi in geo]
|
||||||
|
elif tr[0] == 'scale':
|
||||||
|
geo = [scale(geoi, tr[0], tr[1], origin=(0, 0))
|
||||||
|
for geoi in geo]
|
||||||
|
elif tr[0] == 'rotate':
|
||||||
|
geo = [rotate(geoi, tr[1], origin=(tr[2], tr[3]))
|
||||||
|
for geoi in geo]
|
||||||
|
elif tr[0] == 'skew':
|
||||||
|
geo = [skew(geoi, tr[1], tr[2], origin=(0, 0))
|
||||||
|
for geoi in geo]
|
||||||
|
elif tr[0] == 'matrix':
|
||||||
|
geo = [affine_transform(geoi, tr[1:]) for geoi in geo]
|
||||||
|
else:
|
||||||
|
raise Exception('Unknown transformation: %s', tr)
|
||||||
|
|
||||||
|
return geo
|
||||||
|
|
||||||
|
|
||||||
|
def parse_svg_transform(trstr):
|
||||||
|
"""
|
||||||
|
Parses an SVG transform string into a list
|
||||||
|
of transform names and their parameters.
|
||||||
|
|
||||||
|
Possible transformations are:
|
||||||
|
|
||||||
|
* Translate: translate(<tx> [<ty>]), which specifies
|
||||||
|
a translation by tx and ty. If <ty> is not provided,
|
||||||
|
it is assumed to be zero. Result is
|
||||||
|
['translate', tx, ty]
|
||||||
|
|
||||||
|
* Scale: scale(<sx> [<sy>]), which specifies a scale operation
|
||||||
|
by sx and sy. If <sy> is not provided, it is assumed to be
|
||||||
|
equal to <sx>. Result is: ['scale', sx, sy]
|
||||||
|
|
||||||
|
* Rotate: rotate(<rotate-angle> [<cx> <cy>]), which specifies
|
||||||
|
a rotation by <rotate-angle> degrees about a given point.
|
||||||
|
If optional parameters <cx> and <cy> are not supplied,
|
||||||
|
the rotate is about the origin of the current user coordinate
|
||||||
|
system. Result is: ['rotate', rotate-angle, cx, cy]
|
||||||
|
|
||||||
|
* Skew: skewX(<skew-angle>), which specifies a skew
|
||||||
|
transformation along the x-axis. skewY(<skew-angle>), which
|
||||||
|
specifies a skew transformation along the y-axis.
|
||||||
|
Result is ['skew', angle-x, angle-y]
|
||||||
|
|
||||||
|
* Matrix: matrix(<a> <b> <c> <d> <e> <f>), which specifies a
|
||||||
|
transformation in the form of a transformation matrix of six
|
||||||
|
values. matrix(a,b,c,d,e,f) is equivalent to applying the
|
||||||
|
transformation matrix [a b c d e f]. Result is
|
||||||
|
['matrix', a, b, c, d, e, f]
|
||||||
|
|
||||||
|
:param trstr: SVG transform string.
|
||||||
|
:type trstr: str
|
||||||
|
:return: List of transforms.
|
||||||
|
:rtype: list
|
||||||
|
"""
|
||||||
|
trlist = []
|
||||||
|
|
||||||
|
assert isinstance(trstr, str)
|
||||||
|
trstr = trstr.strip(' ')
|
||||||
|
|
||||||
|
num_re_str = r'[\+\-]?[0-9\.e]+' # TODO: Negative exponents missing
|
||||||
|
comma_or_space_re_str = r'(?:(?:\s+)|(?:\s*,\s*))'
|
||||||
|
translate_re_str = r'translate\s*\(\s*(' + \
|
||||||
|
num_re_str + r')' + \
|
||||||
|
r'(?:' + comma_or_space_re_str + \
|
||||||
|
r'(' + num_re_str + r'))?\s*\)'
|
||||||
|
scale_re_str = r'scale\s*\(\s*(' + \
|
||||||
|
num_re_str + r')' + \
|
||||||
|
r'(?:' + comma_or_space_re_str + \
|
||||||
|
r'(' + num_re_str + r'))?\s*\)'
|
||||||
|
skew_re_str = r'skew([XY])\s*\(\s*(' + \
|
||||||
|
num_re_str + r')\s*\)'
|
||||||
|
rotate_re_str = r'rotate\s*\(\s*(' + \
|
||||||
|
num_re_str + r')' + \
|
||||||
|
r'(?:' + comma_or_space_re_str + \
|
||||||
|
r'(' + num_re_str + r')' + \
|
||||||
|
comma_or_space_re_str + \
|
||||||
|
r'(' + num_re_str + r'))?\*\)'
|
||||||
|
matrix_re_str = r'matrix\s*\(\s*' + \
|
||||||
|
r'(' + num_re_str + r')' + comma_or_space_re_str + \
|
||||||
|
r'(' + num_re_str + r')' + comma_or_space_re_str + \
|
||||||
|
r'(' + num_re_str + r')' + comma_or_space_re_str + \
|
||||||
|
r'(' + num_re_str + r')' + comma_or_space_re_str + \
|
||||||
|
r'(' + num_re_str + r')' + comma_or_space_re_str + \
|
||||||
|
r'(' + num_re_str + r')\s*\)'
|
||||||
|
|
||||||
|
while len(trstr) > 0:
|
||||||
|
match = re.search(r'^' + translate_re_str, trstr)
|
||||||
|
if match:
|
||||||
|
trlist.append([
|
||||||
|
'translate',
|
||||||
|
float(match.group(1)),
|
||||||
|
float(match.group(2)) if match.group else 0.0
|
||||||
|
])
|
||||||
|
trstr = trstr[len(match.group(0)):].strip(' ')
|
||||||
|
continue
|
||||||
|
|
||||||
|
match = re.search(r'^' + scale_re_str, trstr)
|
||||||
|
if match:
|
||||||
|
trlist.append([
|
||||||
|
'translate',
|
||||||
|
float(match.group(1)),
|
||||||
|
float(match.group(2)) if match.group else float(match.group(1))
|
||||||
|
])
|
||||||
|
trstr = trstr[len(match.group(0)):].strip(' ')
|
||||||
|
continue
|
||||||
|
|
||||||
|
match = re.search(r'^' + skew_re_str, trstr)
|
||||||
|
if match:
|
||||||
|
trlist.append([
|
||||||
|
'skew',
|
||||||
|
float(match.group(2)) if match.group(1) == 'X' else 0.0,
|
||||||
|
float(match.group(2)) if match.group(1) == 'Y' else 0.0
|
||||||
|
])
|
||||||
|
trstr = trstr[len(match.group(0)):].strip(' ')
|
||||||
|
continue
|
||||||
|
|
||||||
|
match = re.search(r'^' + rotate_re_str, trstr)
|
||||||
|
if match:
|
||||||
|
trlist.append([
|
||||||
|
'rotate',
|
||||||
|
float(match.group(1)),
|
||||||
|
float(match.group(2)) if match.group(2) else 0.0,
|
||||||
|
float(match.group(3)) if match.group(3) else 0.0
|
||||||
|
])
|
||||||
|
trstr = trstr[len(match.group(0)):].strip(' ')
|
||||||
|
continue
|
||||||
|
|
||||||
|
match = re.search(r'^' + matrix_re_str, trstr)
|
||||||
|
if match:
|
||||||
|
trlist.append(['matrix'] + [float(x) for x in match.groups()])
|
||||||
|
trstr = trstr[len(match.group(0)):].strip(' ')
|
||||||
|
continue
|
||||||
|
|
||||||
|
raise Exception("Don't know how to parse: %s" % trstr)
|
||||||
|
|
||||||
|
return trlist
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
tree = ET.parse('tests/svg/drawing.svg')
|
||||||
|
root = tree.getroot()
|
||||||
|
ns = re.search(r'\{(.*)\}', root.tag).group(1)
|
||||||
|
print ns
|
||||||
|
for geo in getsvggeo(root):
|
||||||
|
print geo
|
||||||
126
tests/svg/drawing.svg
Normal file
126
tests/svg/drawing.svg
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="744.09448819"
|
||||||
|
height="1052.3622047"
|
||||||
|
id="svg2"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.48.4 r9939"
|
||||||
|
sodipodi:docname="drawing.svg">
|
||||||
|
<defs
|
||||||
|
id="defs4" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="1.4"
|
||||||
|
inkscape:cx="436.65332"
|
||||||
|
inkscape:cy="798.58794"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:window-width="968"
|
||||||
|
inkscape:window-height="759"
|
||||||
|
inkscape:window-x="1949"
|
||||||
|
inkscape:window-y="142"
|
||||||
|
inkscape:window-maximized="0" />
|
||||||
|
<metadata
|
||||||
|
id="metadata7">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1">
|
||||||
|
<path
|
||||||
|
sodipodi:type="arc"
|
||||||
|
style="fill:none;stroke:#999999;stroke-width:0.69999999;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||||
|
id="path2985"
|
||||||
|
sodipodi:cx="210.11172"
|
||||||
|
sodipodi:cy="201.81374"
|
||||||
|
sodipodi:rx="70.710678"
|
||||||
|
sodipodi:ry="70.710678"
|
||||||
|
d="m 280.8224,201.81374 a 70.710678,70.710678 0 1 1 -141.42135,0 70.710678,70.710678 0 1 1 141.42135,0 z" />
|
||||||
|
<rect
|
||||||
|
style="fill:none;stroke:#999999;stroke-width:0.69999999;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||||
|
id="rect2987"
|
||||||
|
width="82.832512"
|
||||||
|
height="72.73098"
|
||||||
|
x="343.45187"
|
||||||
|
y="127.06245" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="m 261.70701,300.10548 137.14286,-68.57143 -13.57143,104.28572 105.71429,2.85714 -232.14286,29.28572 z"
|
||||||
|
id="path2991"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<g
|
||||||
|
id="g3018"
|
||||||
|
transform="translate(-37.142857,-103.57143)">
|
||||||
|
<rect
|
||||||
|
y="222.01678"
|
||||||
|
x="508.10672"
|
||||||
|
height="72.73098"
|
||||||
|
width="82.832512"
|
||||||
|
id="rect2987-8"
|
||||||
|
style="fill:none;stroke:#999999;stroke-width:0.69999999;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||||
|
<rect
|
||||||
|
y="177.86595"
|
||||||
|
x="534.49329"
|
||||||
|
height="72.73098"
|
||||||
|
width="82.832512"
|
||||||
|
id="rect2987-4"
|
||||||
|
style="fill:none;stroke:#999999;stroke-width:0.69999999;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#999999;stroke-width:0.69999999;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
|
||||||
|
d="M 550.71875 258.84375 L 550.71875 286 L 513.59375 286 L 513.59375 358.71875 L 596.40625 358.71875 L 596.40625 331.59375 L 633.5625 331.59375 L 633.5625 258.84375 L 550.71875 258.84375 z "
|
||||||
|
id="rect2987-5" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="M 276.42857,98.076465 430.71429,83.076464"
|
||||||
|
id="path3037"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="m 164.28571,391.64789 c 12.85715,-54.28571 55.00001,21.42858 84.28572,22.85715 29.28571,1.42857 30.71429,-14.28572 30.71429,-14.28572"
|
||||||
|
id="path3039"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<g
|
||||||
|
id="g3018-3"
|
||||||
|
transform="matrix(0.54511991,0,0,0.54511991,308.96645,74.66094)">
|
||||||
|
<rect
|
||||||
|
y="222.01678"
|
||||||
|
x="508.10672"
|
||||||
|
height="72.73098"
|
||||||
|
width="82.832512"
|
||||||
|
id="rect2987-8-5"
|
||||||
|
style="fill:none;stroke:#999999;stroke-width:1.28412116;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||||
|
<rect
|
||||||
|
y="177.86595"
|
||||||
|
x="534.49329"
|
||||||
|
height="72.73098"
|
||||||
|
width="82.832512"
|
||||||
|
id="rect2987-4-6"
|
||||||
|
style="fill:none;stroke:#999999;stroke-width:1.28412116;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.9 KiB |
Reference in New Issue
Block a user