Просмотр исходного кода

Merged in sopak/flatcam/tcl-commands (pull request #40)

Tcl commands - import_svg
jpcgt 9 лет назад
Родитель
Сommit
b5162b502f

+ 44 - 28
svgparse.py

@@ -64,6 +64,7 @@ def path2shapely(path, res=1.0):
     :param res: Resolution (minimum step along path)
     :param res: Resolution (minimum step along path)
     :return: Shapely geometry object
     :return: Shapely geometry object
     """
     """
+
     points = []
     points = []
 
 
     for component in path:
     for component in path:
@@ -86,6 +87,12 @@ def path2shapely(path, res=1.0):
             # How many points to use in the dicrete representation.
             # How many points to use in the dicrete representation.
             length = component.length(res / 10.0)
             length = component.length(res / 10.0)
             steps = int(length / res + 0.5)
             steps = int(length / res + 0.5)
+
+            # solve error when step is below 1,
+            # it may cause other problems, but LineString needs at least  two points
+            if steps == 0:
+                steps = 1
+
             frac = 1.0 / steps
             frac = 1.0 / steps
 
 
             # print length, steps, frac
             # print length, steps, frac
@@ -117,9 +124,16 @@ def svgrect2shapely(rect, n_points=32):
     """
     """
     w = svgparselength(rect.get('width'))[0]
     w = svgparselength(rect.get('width'))[0]
     h = svgparselength(rect.get('height'))[0]
     h = svgparselength(rect.get('height'))[0]
-    x = svgparselength(rect.get('x'))[0]
-    y = svgparselength(rect.get('y'))[0]
-
+    x_obj = rect.get('x')
+    if x_obj is not None:
+        x = svgparselength(x_obj)[0]
+    else:
+        x = 0
+    y_obj = rect.get('y')
+    if y_obj is not None:
+        y = svgparselength(y_obj)[0]
+    else:
+        y = 0
     rxstr = rect.get('rx')
     rxstr = rect.get('rx')
     rystr = rect.get('ry')
     rystr = rect.get('ry')
 
 
@@ -305,29 +319,31 @@ def getsvggeo(node):
         log.warning("Unknown kind: " + kind)
         log.warning("Unknown kind: " + kind)
         geo = None
         geo = None
 
 
-    # Transformations
-    if 'transform' in node.attrib:
-        trstr = node.get('transform')
-        trlist = parse_svg_transform(trstr)
-        #log.debug(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)
+    # ignore transformation for unknown kind
+    if geo is not None:
+        # Transformations
+        if 'transform' in node.attrib:
+            trstr = node.get('transform')
+            trlist = parse_svg_transform(trstr)
+            #log.debug(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
     return geo
 
 
@@ -346,7 +362,7 @@ def parse_svg_point_list(ptliststr):
     pos = 0
     pos = 0
     i = 0
     i = 0
 
 
-    for match in re.finditer(r'(\s*,\s*)|(\s+)', ptliststr):
+    for match in re.finditer(r'(\s*,\s*)|(\s+)', ptliststr.strip(' ')):
 
 
         val = float(ptliststr[pos:match.start()])
         val = float(ptliststr[pos:match.start()])
 
 
@@ -435,7 +451,7 @@ def parse_svg_transform(trstr):
                     r'(?:' + comma_or_space_re_str + \
                     r'(?:' + comma_or_space_re_str + \
                     r'(' + number_re_str + r')' + \
                     r'(' + number_re_str + r')' + \
                     comma_or_space_re_str + \
                     comma_or_space_re_str + \
-                    r'(' + number_re_str + r'))?\*\)'
+                    r'(' + number_re_str + r'))?\s*\)'
     matrix_re_str = r'matrix\s*\(\s*' + \
     matrix_re_str = r'matrix\s*\(\s*' + \
                     r'(' + number_re_str + r')' + comma_or_space_re_str + \
                     r'(' + number_re_str + r')' + comma_or_space_re_str + \
                     r'(' + number_re_str + r')' + comma_or_space_re_str + \
                     r'(' + number_re_str + r')' + comma_or_space_re_str + \

+ 5 - 1
tclCommands/TclCommand.py

@@ -388,7 +388,11 @@ class TclCommandSignaled(TclCommand):
             return self.output
             return self.output
 
 
         except Exception as unknown:
         except Exception as unknown:
-            error_info=sys.exc_info()
+            # if error happens inside thread execution, then pass correct error_info to display
+            if self.error_info is not None:
+                error_info = self.error_info
+            else:
+                error_info=sys.exc_info()
             self.log.error("TCL command '%s' failed." % str(self))
             self.log.error("TCL command '%s' failed." % str(self))
             self.app.display_tcl_error(unknown, error_info)
             self.app.display_tcl_error(unknown, error_info)
             self.raise_tcl_unknown_error(unknown)
             self.raise_tcl_unknown_error(unknown)

+ 81 - 0
tclCommands/TclCommandImportSvg.py

@@ -0,0 +1,81 @@
+from ObjectCollection import *
+import TclCommand
+
+
+class TclCommandImportSvg(TclCommand.TclCommandSignaled):
+    """
+    Tcl shell command to import an SVG file as a Geometry Object.
+    """
+
+    # array of all command aliases, to be able use  old names for backward compatibility (add_poly, add_polygon)
+    aliases = ['import_svg']
+
+    # dictionary of types from Tcl command, needs to be ordered
+    arg_names = collections.OrderedDict([
+        ('filename', str)
+    ])
+
+    # dictionary of types from Tcl command, needs to be ordered , this  is  for options  like -optionname value
+    option_types = collections.OrderedDict([
+        ('type', str),
+        ('outname', str)
+    ])
+
+    # array of mandatory options for current Tcl command: required = {'name','outname'}
+    required = ['filename']
+
+    # structured help for current command, args needs to be ordered
+    help = {
+        'main': "Import an SVG file as a Geometry Object..",
+        'args':  collections.OrderedDict([
+            ('filename', 'Path to file to open.'),
+            ('type', 'Import as gerber or geometry(default).'),
+            ('outname', 'Name of the resulting Geometry object.')
+        ]),
+        'examples': []
+    }
+
+    def execute(self, args, unnamed_args):
+        """
+        execute current TCL shell command
+
+        :param args: array of known named arguments and options
+        :param unnamed_args: array of other values which were passed into command
+            without -somename and  we do not have them in known arg_names
+        :return: None or exception
+        """
+
+        # How the object should be initialized
+        def obj_init(geo_obj, app_obj):
+
+            if not isinstance(geo_obj, Geometry):
+                self.raise_tcl_error('Expected Geometry or Gerber, got %s %s.' % (outname, type(geo_obj)))
+
+            geo_obj.import_svg(filename)
+
+        filename = args['filename']
+
+        if 'outname' in args:
+            outname = args['outname']
+        else:
+            outname = filename.split('/')[-1].split('\\')[-1]
+
+        if 'type' in args:
+            obj_type = args['type']
+        else:
+            obj_type = 'geometry'
+
+        if obj_type != "geometry" and  obj_type != "gerber":
+            self.raise_tcl_error("Option type can be 'geopmetry' or 'gerber' only, got '%s'." % obj_type)
+
+        with self.app.proc_container.new("Import SVG"):
+
+            # Object creation
+            self.app.new_object(obj_type, outname, obj_init)
+
+            # Register recent file
+            self.app.file_opened.emit("svg", filename)
+
+            # GUI feedback
+            self.app.inform.emit("Opened: " + filename)
+

+ 1 - 0
tclCommands/__init__.py

@@ -8,6 +8,7 @@ import tclCommands.TclCommandCncjob
 import tclCommands.TclCommandDrillcncjob
 import tclCommands.TclCommandDrillcncjob
 import tclCommands.TclCommandExportGcode
 import tclCommands.TclCommandExportGcode
 import tclCommands.TclCommandExteriors
 import tclCommands.TclCommandExteriors
+import tclCommands.TclCommandImportSvg
 import tclCommands.TclCommandInteriors
 import tclCommands.TclCommandInteriors
 import tclCommands.TclCommandIsolate
 import tclCommands.TclCommandIsolate
 import tclCommands.TclCommandNew
 import tclCommands.TclCommandNew

+ 34 - 0
tests/svg/7segment_9,9.svg

@@ -0,0 +1,34 @@
+<?xml version='1.0' encoding='utf-8'?>
+<!DOCTYPE svg>
+<!-- Generator: Adobe Illustrator 14.0.0, SVG Export Plug-In  -->
+<svg xmlns="http://www.w3.org/2000/svg" width="0.402778in" xml:space="preserve" xmlns:xml="http://www.w3.org/XML/1998/namespace" x="0px" version="1.2" y="0px" height="0.527778in" viewBox="0 0 29 38" baseProfile="tiny">
+
+ <g id="copper0">
+  <g id="copper1">
+	 <rect x="1.362" y="2.091" fill="none" stroke="#F7BD13" stroke-width="1.224" width="4.104" height="4.104"/>	
+   <circle fill="none" cx="3.362" cy="4.091" stroke="#F7BD13"   id="connector1pin" r="2.052" stroke-width="1.224"/>
+   <circle fill="none" cx="3.362" cy="11.29" stroke="#F7BD13"   id="connector2pin" r="2.052" stroke-width="1.224"/>
+   <circle fill="none" cx="3.362" cy="18.491" stroke="#F7BD13"  id="connector3pin" r="2.052" stroke-width="1.224"/>
+   <circle fill="none" cx="3.362" cy="25.69" stroke="#F7BD13"   id="connector4pin" r="2.052" stroke-width="1.224"/>
+   <circle fill="none" cx="3.365" cy="32.81" stroke="#F7BD13"   id="connector5pin" r="2.052" stroke-width="1.224"/>
+   <circle fill="none" cx="25.001" cy="32.821" stroke="#F7BD13" id="connector6pin" r="2.052" stroke-width="1.224"/>
+   <circle fill="none" cx="24.999" cy="25.702" stroke="#F7BD13" id="connector7pin" r="2.052" stroke-width="1.224"/>
+   <circle fill="none" cx="24.999" cy="18.503" stroke="#F7BD13" id="connector8pin" r="2.052" stroke-width="1.224"/>
+   <circle fill="none" cx="24.999" cy="11.302" stroke="#F7BD13" id="connector9pin" r="2.052" stroke-width="1.224"/>
+   <circle fill="none" cx="25.029" cy="4.077" stroke="#F7BD13" id="connector10pin" r="2.052" stroke-width="1.224"/>
+  </g>
+  <circle fill="none" cx="3.362" cy="4.091" stroke="#F7BD13"   id="connector1pin" r="2.052" stroke-width="1.224"/>
+  <circle fill="none" cx="3.362" cy="11.29" stroke="#F7BD13"   id="connector2pin" r="2.052" stroke-width="1.224"/>
+  <circle fill="none" cx="3.362" cy="18.491" stroke="#F7BD13"  id="connector3pin" r="2.052" stroke-width="1.224"/>
+  <circle fill="none" cx="3.362" cy="25.69" stroke="#F7BD13"   id="connector4pin" r="2.052" stroke-width="1.224"/>
+  <circle fill="none" cx="3.365" cy="32.81" stroke="#F7BD13"   id="connector5pin" r="2.052" stroke-width="1.224"/>
+  <circle fill="none" cx="25.001" cy="32.821" stroke="#F7BD13" id="connector6pin" r="2.052" stroke-width="1.224"/>
+  <circle fill="none" cx="24.999" cy="25.702" stroke="#F7BD13" id="connector7pin" r="2.052" stroke-width="1.224"/>
+  <circle fill="none" cx="24.999" cy="18.503" stroke="#F7BD13" id="connector8pin" r="2.052" stroke-width="1.224"/>
+  <circle fill="none" cx="24.999" cy="11.302" stroke="#F7BD13" id="connector9pin" r="2.052" stroke-width="1.224"/>
+  <circle fill="none" cx="25.029" cy="4.077" stroke="#F7BD13" id="connector10pin" r="2.052" stroke-width="1.224"/>
+ </g>
+ <g id="silkscreen">
+  <rect width="28.347" x="0.024" y="0.024" height="36.851" fill="none" stroke="#FFFFFF" stroke-width="0.7087"/>
+ </g>
+</svg>

+ 468 - 0
tests/svg/Arduino Nano3_pcb.svg

@@ -0,0 +1,468 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 width="50.4px" height="122.448px" viewBox="0 0 50.4 122.448" enable-background="new 0 0 50.4 122.448" xml:space="preserve">
+<desc>Fritzing footprint generated by brd2svg</desc>
+<g id="silkscreen">
+	<path fill="none" stroke="#FFFFFF" stroke-width="0.576" d="M0.288,122.136h49.824V0.288H0.288V122.136"/>
+	<g>
+		<title>element:J1</title>
+		<g>
+			<title>package:HEAD15-NOSS</title>
+		</g>
+	</g>
+	<g>
+		<title>element:J2</title>
+		<g>
+			<title>package:HEAD15-NOSS-1</title>
+		</g>
+	</g>
+	<g>
+		<title>element:U2</title>
+		<g>
+			<title>package:SSOP28</title>
+		</g>
+	</g>
+	<g>
+		<title>element:U3</title>
+		<g>
+			<title>package:SOT223</title>
+		</g>
+	</g>
+</g>
+<g id="copper1">
+	<g id="copper0">
+		<circle id="connector16pad" fill="none" stroke="#F7BD13" stroke-width="1.224" cx="3.6" cy="10.8" r="1.908"/>
+		<rect x="1.692" y="8.892" fill="none" stroke="#F7BD13" stroke-width="1.224" width="3.814" height="3.816"/>
+		<circle id="connector17pad" fill="none" stroke="#F7BD13" stroke-width="1.224" cx="3.6" cy="18" r="1.908"/>
+		<circle id="connector18pad" fill="none" stroke="#F7BD13" stroke-width="1.224" cx="3.6" cy="25.2" r="1.908"/>
+		<circle id="connector19pad" fill="none" stroke="#F7BD13" stroke-width="1.224" cx="3.6" cy="32.4" r="1.908"/>
+		<circle id="connector20pad" fill="none" stroke="#F7BD13" stroke-width="1.224" cx="3.6" cy="39.6" r="1.908"/>
+		<circle id="connector21pad" fill="none" stroke="#F7BD13" stroke-width="1.224" cx="3.6" cy="46.8" r="1.908"/>
+		<circle id="connector22pad" fill="none" stroke="#F7BD13" stroke-width="1.224" cx="3.6" cy="54" r="1.908"/>
+		<circle id="connector23pad" fill="none" stroke="#F7BD13" stroke-width="1.224" cx="3.6" cy="61.2" r="1.908"/>
+		<circle id="connector24pad" fill="none" stroke="#F7BD13" stroke-width="1.224" cx="3.6" cy="68.4" r="1.908"/>
+		<circle id="connector25pad" fill="none" stroke="#F7BD13" stroke-width="1.224" cx="3.6" cy="75.6" r="1.908"/>
+		<circle id="connector26pad" fill="none" stroke="#F7BD13" stroke-width="1.224" cx="3.6" cy="82.8" r="1.908"/>
+		<circle id="connector27pad" fill="none" stroke="#F7BD13" stroke-width="1.224" cx="3.6" cy="90" r="1.908"/>
+		<circle id="connector28pad" fill="none" stroke="#F7BD13" stroke-width="1.224" cx="3.6" cy="97.2" r="1.908"/>
+		<circle id="connector29pad" fill="none" stroke="#F7BD13" stroke-width="1.224" cx="3.6" cy="104.4" r="1.908"/>
+		<circle id="connector30pad" fill="none" stroke="#F7BD13" stroke-width="1.224" cx="3.6" cy="111.6" r="1.908"/>
+		<circle id="connector31pad" fill="none" stroke="#F7BD13" stroke-width="1.224" cx="46.8" cy="10.8" r="1.908"/>
+		<circle id="connector32pad" fill="none" stroke="#F7BD13" stroke-width="1.224" cx="46.8" cy="18" r="1.908"/>
+		<circle id="connector33pad" fill="none" stroke="#F7BD13" stroke-width="1.224" cx="46.8" cy="25.2" r="1.908"/>
+		<circle id="connector34pad" fill="none" stroke="#F7BD13" stroke-width="1.224" cx="46.8" cy="32.4" r="1.908"/>
+		<circle id="connector35pad" fill="none" stroke="#F7BD13" stroke-width="1.224" cx="46.8" cy="39.6" r="1.908"/>
+		<circle id="connector36pad" fill="none" stroke="#F7BD13" stroke-width="1.224" cx="46.8" cy="46.8" r="1.908"/>
+		<circle id="connector37pad" fill="none" stroke="#F7BD13" stroke-width="1.224" cx="46.8" cy="54" r="1.908"/>
+		<circle id="connector38pad" fill="none" stroke="#F7BD13" stroke-width="1.224" cx="46.8" cy="61.2" r="1.908"/>
+		<circle id="connector39pad" fill="none" stroke="#F7BD13" stroke-width="1.224" cx="46.8" cy="68.4" r="1.908"/>
+		<circle id="connector40pad" fill="none" stroke="#F7BD13" stroke-width="1.224" cx="46.8" cy="75.6" r="1.908"/>
+		<circle id="connector41pad" fill="none" stroke="#F7BD13" stroke-width="1.224" cx="46.8" cy="82.8" r="1.908"/>
+		<circle id="connector42pad" fill="none" stroke="#F7BD13" stroke-width="1.224" cx="46.8" cy="90" r="1.908"/>
+		<circle id="connector43pad" fill="none" stroke="#F7BD13" stroke-width="1.224" cx="46.8" cy="97.2" r="1.908"/>
+		<circle id="connector44pad" fill="none" stroke="#F7BD13" stroke-width="1.224" cx="46.8" cy="104.4" r="1.908"/>
+		<circle id="connector45pad" fill="none" stroke="#F7BD13" stroke-width="1.224" cx="46.8" cy="111.6" r="1.908"/>
+	</g>
+</g>
+<g>
+	<title>layer 21</title>
+	<g>
+		<title>text:TX1</title>
+		<g transform="matrix(1, 0, 0, 1, 9.216, 13.68)">
+			<g transform="rotate(270)">
+				<text transform="matrix(1 0 0 1 -6.713867e-004 -4.272461e-004)" fill="#FFFFFF" font-family="'OCRA'" font-size="2.6726">TX1</text>
+			</g>
+		</g>
+	</g>
+	<g>
+		<title>text:RX0</title>
+		<g transform="matrix(1, 0, 0, 1, 9.216, 20.88)">
+			<g transform="rotate(270)">
+				<text transform="matrix(1 0 0 1 1.220703e-004 -4.272461e-004)" fill="#FFFFFF" font-family="'OCRA'" font-size="2.6726">RX0</text>
+			</g>
+		</g>
+	</g>
+	<g>
+		<title>text:RST</title>
+		<g transform="matrix(1, 0, 0, 1, 9.216, 28.008)">
+			<g transform="rotate(270)">
+				<text transform="matrix(1 0 0 1 -7.934570e-004 -4.272461e-004)" fill="#FFFFFF" font-family="'OCRA'" font-size="2.6726">RST</text>
+			</g>
+		</g>
+	</g>
+	<g>
+		<title>text:GND</title>
+		<g transform="matrix(1, 0, 0, 1, 9.216, 35.208)">
+			<g transform="rotate(270)">
+				<text transform="matrix(1 0 0 1 0 -4.272461e-004)" fill="#FFFFFF" font-family="'OCRA'" font-size="2.6726">GND</text>
+			</g>
+		</g>
+	</g>
+	<g>
+		<title>text:D2</title>
+		<g transform="matrix(1, 0, 0, 1, 9.216, 41.544)">
+			<g transform="rotate(270)">
+				<text transform="matrix(1 0 0 1 6.103516e-005 -4.272461e-004)" fill="#FFFFFF" font-family="'OCRA'" font-size="2.6726">D2</text>
+			</g>
+		</g>
+	</g>
+	<g>
+		<title>text:D3</title>
+		<g transform="matrix(1, 0, 0, 1, 9.216, 48.6)">
+			<g transform="rotate(270)">
+				<text transform="matrix(1 0 0 1 -5.798340e-004 -4.272461e-004)" fill="#FFFFFF" font-family="'OCRA'" font-size="2.6726">D3</text>
+			</g>
+		</g>
+	</g>
+	<g>
+		<title>text:D4</title>
+		<g transform="matrix(1, 0, 0, 1, 9.216, 55.872)">
+			<g transform="rotate(270)">
+				<text transform="matrix(1 0 0 1 -6.103516e-005 -4.272461e-004)" fill="#FFFFFF" font-family="'OCRA'" font-size="2.6726">D4</text>
+			</g>
+		</g>
+	</g>
+	<g>
+		<title>text:D5</title>
+		<g transform="matrix(1, 0, 0, 1, 9.216, 62.928)">
+			<g transform="rotate(270)">
+				<text transform="matrix(1 0 0 1 -7.019043e-004 -4.272461e-004)" fill="#FFFFFF" font-family="'OCRA'" font-size="2.6726">D5</text>
+			</g>
+		</g>
+	</g>
+	<g>
+		<title>text:D6</title>
+		<g transform="matrix(1, 0, 0, 1, 9.216, 70.272)">
+			<g transform="rotate(270)">
+				<text transform="matrix(1 0 0 1 -4.577637e-004 -4.272461e-004)" fill="#FFFFFF" font-family="'OCRA'" font-size="2.6726">D6</text>
+			</g>
+		</g>
+	</g>
+	<g>
+		<title>text:D7</title>
+		<g transform="matrix(1, 0, 0, 1, 9.216, 77.544)">
+			<g transform="rotate(270)">
+				<text transform="matrix(1 0 0 1 6.103516e-005 -4.272461e-004)" fill="#FFFFFF" font-family="'OCRA'" font-size="2.6726">D7</text>
+			</g>
+		</g>
+	</g>
+	<g>
+		<title>text:D8</title>
+		<g transform="matrix(1, 0, 0, 1, 9.216, 84.6)">
+			<g transform="rotate(270)">
+				<text transform="matrix(1 0 0 1 -5.798340e-004 -4.272461e-004)" fill="#FFFFFF" font-family="'OCRA'" font-size="2.6726">D8</text>
+			</g>
+		</g>
+	</g>
+	<g>
+		<title>text:D9</title>
+		<g transform="matrix(1, 0, 0, 1, 9.216, 91.872)">
+			<g transform="rotate(270)">
+				<text transform="matrix(1 0 0 1 -6.103516e-005 -4.272461e-004)" fill="#FFFFFF" font-family="'OCRA'" font-size="2.6726">D9</text>
+			</g>
+		</g>
+	</g>
+	<g>
+		<title>text:D10</title>
+		<g transform="matrix(1, 0, 0, 1, 9.216, 100.224)">
+			<g transform="rotate(270)">
+				<text transform="matrix(1 0 0 1 -0.0016 -4.272461e-004)" fill="#FFFFFF" font-family="'OCRA'" font-size="2.6726">D10</text>
+			</g>
+		</g>
+	</g>
+	<g>
+		<title>text:D11</title>
+		<g transform="matrix(1, 0, 0, 1, 9.216, 107.352)">
+			<g transform="rotate(270)">
+				<text transform="matrix(1 0 0 1 -5.493164e-004 -4.272461e-004)" fill="#FFFFFF" font-family="'OCRA'" font-size="2.6726">D11</text>
+			</g>
+		</g>
+	</g>
+	<g>
+		<title>text:D12</title>
+		<g transform="matrix(1, 0, 0, 1, 9.216, 114.552)">
+			<g transform="rotate(270)">
+				<text transform="matrix(1 0 0 1 -0.0017 -4.272461e-004)" fill="#FFFFFF" font-family="'OCRA'" font-size="2.6726">D12</text>
+			</g>
+		</g>
+	</g>
+	<g>
+		<title>text:D13</title>
+		<g transform="matrix(1, 0, 0, 1, 43.488, 114.552)">
+			<g transform="rotate(270)">
+				<text transform="matrix(1 0 0 1 -0.0017 -9.460449e-004)" fill="#FFFFFF" font-family="'OCRA'" font-size="2.6726">D13</text>
+			</g>
+		</g>
+	</g>
+	<g>
+		<title>text:3V3</title>
+		<g transform="matrix(1, 0, 0, 1, 43.488, 107.28)">
+			<g transform="rotate(270)">
+				<text transform="matrix(1 0 0 1 -2.746582e-004 -9.460449e-004)" fill="#FFFFFF" font-family="'OCRA'" font-size="2.6726">3V3</text>
+			</g>
+		</g>
+	</g>
+	<g>
+		<title>text:REF</title>
+		<g transform="matrix(1, 0, 0, 1, 43.488, 100.152)">
+			<g transform="rotate(270)">
+				<text transform="matrix(1 0 0 1 -0.0013 -9.460449e-004)" fill="#FFFFFF" font-family="'OCRA'" font-size="2.6726">REF</text>
+			</g>
+		</g>
+	</g>
+	<g>
+		<title>text:A0</title>
+		<g transform="matrix(1, 0, 0, 1, 43.488, 91.944)">
+			<g transform="rotate(270)">
+				<text transform="matrix(1 0 0 1 -3.356934e-004 -9.460449e-004)" fill="#FFFFFF" font-family="'OCRA'" font-size="2.6726">A0</text>
+			</g>
+		</g>
+	</g>
+	<g>
+		<title>text:A1</title>
+		<g transform="matrix(1, 0, 0, 1, 43.488, 84.672)">
+			<g transform="rotate(270)">
+				<text transform="matrix(1 0 0 1 -8.544922e-004 -9.460449e-004)" fill="#FFFFFF" font-family="'OCRA'" font-size="2.6726">A1</text>
+			</g>
+		</g>
+	</g>
+	<g>
+		<title>text:A2</title>
+		<g transform="matrix(1, 0, 0, 1, 43.488, 77.544)">
+			<g transform="rotate(270)">
+				<text transform="matrix(1 0 0 1 6.103516e-005 -9.460449e-004)" fill="#FFFFFF" font-family="'OCRA'" font-size="2.6726">A2</text>
+			</g>
+		</g>
+	</g>
+	<g>
+		<title>text:A3</title>
+		<g transform="matrix(1, 0, 0, 1, 43.488, 70.344)">
+			<g transform="rotate(270)">
+				<text transform="matrix(1 0 0 1 -7.324219e-004 -9.460449e-004)" fill="#FFFFFF" font-family="'OCRA'" font-size="2.6726">A3</text>
+			</g>
+		</g>
+	</g>
+	<g>
+		<title>text:A4</title>
+		<g transform="matrix(1, 0, 0, 1, 43.488, 63.216)">
+			<g transform="rotate(270)">
+				<text transform="matrix(1 0 0 1 1.831055e-004 -9.460449e-004)" fill="#FFFFFF" font-family="'OCRA'" font-size="2.6726">A4</text>
+			</g>
+		</g>
+	</g>
+	<g>
+		<title>text:A5</title>
+		<g transform="matrix(1, 0, 0, 1, 43.488, 55.944)">
+			<g transform="rotate(270)">
+				<text transform="matrix(1 0 0 1 -3.356934e-004 -9.460449e-004)" fill="#FFFFFF" font-family="'OCRA'" font-size="2.6726">A5</text>
+			</g>
+		</g>
+	</g>
+	<g>
+		<title>text:A6</title>
+		<g transform="matrix(1, 0, 0, 1, 43.488, 48.744)">
+			<g transform="rotate(270)">
+				<text transform="matrix(1 0 0 1 -0.0011 -9.460449e-004)" fill="#FFFFFF" font-family="'OCRA'" font-size="2.6726">A6</text>
+			</g>
+		</g>
+	</g>
+	<g>
+		<title>text:A7</title>
+		<g transform="matrix(1, 0, 0, 1, 43.488, 41.616)">
+			<g transform="rotate(270)">
+				<text transform="matrix(1 0 0 1 -2.136230e-004 -9.460449e-004)" fill="#FFFFFF" font-family="'OCRA'" font-size="2.6726">A7</text>
+			</g>
+		</g>
+	</g>
+	<g>
+		<title>text:5V</title>
+		<g transform="matrix(1, 0, 0, 1, 43.488, 34.416)">
+			<g transform="rotate(270)">
+				<text transform="matrix(1 0 0 1 -9.765625e-004 -9.460449e-004)" fill="#FFFFFF" font-family="'OCRA'" font-size="2.6726">5V</text>
+			</g>
+		</g>
+	</g>
+	<g>
+		<title>text:RST</title>
+		<g transform="matrix(1, 0, 0, 1, 43.488, 27.216)">
+			<g transform="rotate(270)">
+				<text transform="matrix(1 0 0 1 1.831055e-004 -9.460449e-004)" fill="#FFFFFF" font-family="'OCRA'" font-size="2.6726">RST</text>
+			</g>
+		</g>
+	</g>
+	<g>
+		<title>text:GND</title>
+		<g transform="matrix(1, 0, 0, 1, 43.488, 19.8)">
+			<g transform="rotate(270)">
+				<text transform="matrix(1 0 0 1 1.831055e-004 -9.460449e-004)" fill="#FFFFFF" font-family="'OCRA'" font-size="2.6726">GND</text>
+			</g>
+		</g>
+	</g>
+	<g>
+		<title>text:VIN</title>
+		<g transform="matrix(1, 0, 0, 1, 43.488, 12.672)">
+			<g transform="rotate(270)">
+				<text transform="matrix(1 0 0 1 -8.544922e-004 -9.460449e-004)" fill="#FFFFFF" font-family="'OCRA'" font-size="2.6726">VIN</text>
+			</g>
+		</g>
+	</g>
+	<g>
+		<title>text:*</title>
+		<g transform="matrix(1, 0, 0, 1, 7.056, 92.664)">
+			<g transform="rotate(270)">
+				<text transform="matrix(1 0 0 1 -0.001 -5.798340e-004)" fill="#FFFFFF" font-family="'OCRA'" font-size="1.6704">*</text>
+			</g>
+		</g>
+	</g>
+	<g>
+		<title>text:*</title>
+		<g transform="matrix(1, 0, 0, 1, 7.056, 99.936)">
+			<g transform="rotate(270)">
+				<text transform="matrix(1 0 0 1 -5.187988e-004 -5.798340e-004)" fill="#FFFFFF" font-family="'OCRA'" font-size="1.6704">*</text>
+			</g>
+		</g>
+	</g>
+	<g>
+		<title>text:*</title>
+		<g transform="matrix(1, 0, 0, 1, 7.056, 107.064)">
+			<g transform="rotate(270)">
+				<text transform="matrix(1 0 0 1 -0.0014 -5.798340e-004)" fill="#FFFFFF" font-family="'OCRA'" font-size="1.6704">*</text>
+			</g>
+		</g>
+	</g>
+	<g>
+		<title>text:*</title>
+		<g transform="matrix(1, 0, 0, 1, 7.056, 71.064)">
+			<g transform="rotate(270)">
+				<text transform="matrix(1 0 0 1 -0.0014 -5.798340e-004)" fill="#FFFFFF" font-family="'OCRA'" font-size="1.6704">*</text>
+			</g>
+		</g>
+	</g>
+	<g>
+		<title>text:*</title>
+		<g transform="matrix(1, 0, 0, 1, 7.056, 63.792)">
+			<g transform="rotate(270)">
+				<text transform="matrix(1 0 0 1 0 -5.798340e-004)" fill="#FFFFFF" font-family="'OCRA'" font-size="1.6704">*</text>
+			</g>
+		</g>
+	</g>
+	<g>
+		<title>text:*</title>
+		<g transform="matrix(1, 0, 0, 1, 7.056, 49.464)">
+			<g transform="rotate(270)">
+				<text transform="matrix(1 0 0 1 1.220703e-004 -5.798340e-004)" fill="#FFFFFF" font-family="'OCRA'" font-size="1.6704">*</text>
+			</g>
+		</g>
+	</g>
+	<g>
+		<title>element:C1</title>
+		<g>
+			<title>package:CAP0805-NP</title>
+		</g>
+	</g>
+	<g>
+		<title>element:C2</title>
+		<g>
+			<title>package:TAN-A</title>
+		</g>
+	</g>
+	<g>
+		<title>element:C3</title>
+		<g>
+			<title>package:CAP0805-NP</title>
+		</g>
+	</g>
+	<g>
+		<title>element:C4</title>
+		<g>
+			<title>package:CAP0805-NP</title>
+		</g>
+	</g>
+	<g>
+		<title>element:C7</title>
+		<g>
+			<title>package:CAP0805-NP</title>
+		</g>
+	</g>
+	<g>
+		<title>element:C8</title>
+		<g>
+			<title>package:TAN-A</title>
+		</g>
+	</g>
+	<g>
+		<title>element:C9</title>
+		<g>
+			<title>package:CAP0805-NP</title>
+		</g>
+	</g>
+	<g>
+		<title>element:D1</title>
+		<g>
+			<title>package:SOD-123</title>
+		</g>
+	</g>
+	<g>
+		<title>element:J1</title>
+		<g>
+			<title>package:HEAD15-NOSS</title>
+		</g>
+	</g>
+	<g>
+		<title>element:J2</title>
+		<g>
+			<title>package:HEAD15-NOSS-1</title>
+		</g>
+	</g>
+	<g>
+		<title>element:RP1</title>
+		<g>
+			<title>package:RES4NT</title>
+		</g>
+	</g>
+	<g>
+		<title>element:RP2</title>
+		<g>
+			<title>package:RES4NT</title>
+		</g>
+	</g>
+	<g>
+		<title>element:U$4</title>
+		<g>
+			<title>package:FIDUCIAL-1X2</title>
+		</g>
+	</g>
+	<g>
+		<title>element:U$37</title>
+		<g>
+			<title>package:FIDUCIAL-1X2</title>
+		</g>
+	</g>
+	<g>
+		<title>element:U$53</title>
+		<g>
+			<title>package:FIDUCIAL-1X2</title>
+		</g>
+	</g>
+	<g>
+		<title>element:U$54</title>
+		<g>
+			<title>package:FIDUCIAL-1X2</title>
+		</g>
+	</g>
+	<g>
+		<title>element:U2</title>
+		<g>
+			<title>package:SSOP28</title>
+		</g>
+	</g>
+	<g>
+		<title>element:U3</title>
+		<g>
+			<title>package:SOT223</title>
+		</g>
+	</g>
+</g>
+</svg>

+ 77 - 0
tests/svg/usb_connector.svg

@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg  PUBLIC '-//W3C//DTD SVG 1.1 Basic//EN'  'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-basic.dtd'><svg baseProfile="basic" height="0.59in" id="svg" version="1.1" viewBox="0 0 67.502 42.52" width="0.94in" x="0px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" y="0px">
+<g id="breadboard">
+<rect fill="none" height="5.372" id="connector0pin" width="2.16" x="60.644" y="32.178"/>
+<rect fill="none" height="5.309" id="connector1pin" width="2.16" x="53.496" y="5.002"/>
+<rect fill="none" height="5.309" id="connector2pin" width="2.16" x="60.644" y="5.002"/>
+<rect fill="none" height="5.372" id="connector3pin" width="2.159" x="53.506" y="32.178"/>
+<rect fill="none" height="5.372" id="connector5pin" width="2.159" x="53.506" y="32.178"/>
+<rect fill="none" height="5.372" id="connector4pin" width="2.159" x="53.506" y="32.178"/>
+<rect fill="none" height="3.714" id="connector0terminal" width="2.16" x="60.644" y="33.836"/>
+<rect fill="none" height="3.669" id="connector1terminal" width="2.16" x="53.496" y="5.002"/>
+<rect fill="none" height="3.669" id="connector2terminal" width="2.16" x="60.644" y="5.002"/>
+<rect fill="none" height="3.714" id="connector3terminal" width="2.159" x="53.506" y="33.836"/>
+<rect fill="none" height="3.714" id="connector5terminal" width="2.159" x="53.506" y="33.836"/>
+<rect fill="none" height="3.714" id="connector4terminal" width="2.159" x="53.506" y="33.836"/>
+	<g>
+		<polygon fill="#1F7A34" points="49.528,34.417 67.502,34.417 67.502,8.102 49.528,8.102 49.528,0 20.776,0 20.776,42.52     49.528,42.52   "/>
+	</g>
+	<g>
+		<g>
+			<path d="M30.783,4.96c0-1.988-1.609-3.598-3.598-3.598c-1.985,0-3.598,1.609-3.598,3.598     c0,1.985,1.612,3.598,3.598,3.598C29.173,8.558,30.783,6.945,30.783,4.96z" fill="#9A916C"/>
+		</g>
+		<g>
+			<circle cx="27.182" cy="4.96" fill="#3A3A3A" r="2.708"/>
+		</g>
+	</g>
+	<g>
+		<g>
+			<path d="M30.783,37.56c0-1.988-1.609-3.598-3.598-3.598c-1.985,0-3.598,1.609-3.598,3.598     c0,1.985,1.612,3.598,3.598,3.598C29.173,41.157,30.783,39.545,30.783,37.56z" fill="#9A916C"/>
+		</g>
+		<g>
+			<circle cx="27.182" cy="37.56" fill="#3A3A3A" r="2.708"/>
+		</g>
+	</g>
+	<g>
+		<rect fill="#898989" height="34.016" width="45.355" x="0.001" y="4.252"/>
+	</g>
+	<g>
+		<rect fill="#DDDDDD" height="0.743" width="45.355" x="0.001" y="4.252"/>
+	</g>
+	<g>
+		<rect fill="#C6C6C6" height="0.889" width="45.355" x="0.001" y="4.991"/>
+	</g>
+	<g>
+		<rect fill="#ADADAD" height="31.342" width="45.356" y="5.88"/>
+	</g>
+	<g>
+		<line fill="#919191" stroke="#4D4D4D" stroke-width="0.1" x1="34.173" x2="34.173" y1="4.252" y2="38.268"/>
+	</g>
+	<g>
+		<rect fill="#8C8C8C" height="5.349" width="4.667" x="52.252" y="4.961"/>
+	</g>
+	<g>
+		<rect fill="#8C8C8C" height="5.349" width="4.668" x="59.418" y="4.961"/>
+	</g>
+	<g>
+		<rect fill="#8C8C8C" height="5.349" width="4.667" x="52.252" y="32.177"/>
+	</g>
+	<g>
+		<rect fill="#8C8C8C" height="5.349" width="4.668" x="59.418" y="32.177"/>
+	</g>
+	<g>
+		<path d="M30.074,21.386l-2.64-1.524v1.134H13.468c0.515-0.416,1.008-0.965,1.493-1.505    c0.802-0.894,1.631-1.819,2.338-1.819h2.277c0.141,0.521,0.597,0.913,1.163,0.913c0.677,0,1.226-0.548,1.226-1.225    s-0.549-1.226-1.226-1.226c-0.566,0-1.022,0.392-1.163,0.914h-2.277c-0.985,0-1.868,0.984-2.803,2.026    c-0.744,0.83-1.509,1.675-2.255,1.922h-1.82c-0.185-1.02-1.073-1.794-2.145-1.794c-1.206,0-2.184,0.978-2.184,2.184    c0,1.207,0.978,2.184,2.184,2.184c1.072,0,1.96-0.774,2.145-1.794h5.196c0.746,0.247,1.511,1.093,2.254,1.922    c0.934,1.043,1.817,2.026,2.802,2.026h2.142v0.985h2.595v-2.595h-2.595v0.985h-2.142c-0.707,0-1.536-0.925-2.337-1.818    c-0.485-0.541-0.978-1.09-1.493-1.506h10.592v1.134L30.074,21.386z" fill="#4D4D4D"/>
+	</g>
+	<g>
+		<polyline fill="none" points="54.586,10.31 54.586,17.006 45.357,17.006   " stroke="#8C8C8C" stroke-width="1"/>
+	</g>
+	<g>
+		<polyline fill="none" points="61.732,10.31 61.732,19.841 45.357,19.841   " stroke="#8C8C8C" stroke-width="1"/>
+	</g>
+	<g>
+		<polyline fill="none" points="54.586,32.177 54.586,25.479 45.357,25.479   " stroke="#8C8C8C" stroke-width="1"/>
+	</g>
+	<g>
+		<polyline fill="none" points="61.732,32.177 61.732,22.646 45.357,22.646   " stroke="#8C8C8C" stroke-width="1"/>
+	</g>
+</g>
+</svg>

+ 68 - 1
tests/test_tcl_shell.py

@@ -4,6 +4,8 @@ from PyQt4 import QtGui
 from PyQt4.QtCore import QThread
 from PyQt4.QtCore import QThread
 
 
 from FlatCAMApp import App
 from FlatCAMApp import App
+from os import listdir
+from os.path import isfile
 from FlatCAMObj import FlatCAMGerber, FlatCAMGeometry, FlatCAMCNCjob, FlatCAMExcellon
 from FlatCAMObj import FlatCAMGerber, FlatCAMGeometry, FlatCAMCNCjob, FlatCAMExcellon
 from ObjectUI import GerberObjectUI, GeometryObjectUI
 from ObjectUI import GerberObjectUI, GeometryObjectUI
 from time import sleep
 from time import sleep
@@ -12,11 +14,15 @@ import tempfile
 
 
 class TclShellTest(unittest.TestCase):
 class TclShellTest(unittest.TestCase):
 
 
+    svg_files = 'tests/svg'
+    svg_filename = 'Arduino Nano3_pcb.svg'
     gerber_files = 'tests/gerber_files'
     gerber_files = 'tests/gerber_files'
     copper_bottom_filename = 'detector_copper_bottom.gbr'
     copper_bottom_filename = 'detector_copper_bottom.gbr'
     copper_top_filename = 'detector_copper_top.gbr'
     copper_top_filename = 'detector_copper_top.gbr'
     cutout_filename = 'detector_contour.gbr'
     cutout_filename = 'detector_contour.gbr'
     excellon_filename = 'detector_drill.txt'
     excellon_filename = 'detector_drill.txt'
+    gerber_name = "gerber"
+    geometry_name = "geometry"
     excellon_name = "excellon"
     excellon_name = "excellon"
     gerber_top_name = "top"
     gerber_top_name = "top"
     gerber_bottom_name = "bottom"
     gerber_bottom_name = "bottom"
@@ -177,4 +183,65 @@ class TclShellTest(unittest.TestCase):
         # mirror bottom excellon
         # mirror bottom excellon
         self.fc.exec_command_test('mirror %s -box %s -axis X' % (self.excellon_name, self.gerber_cutout_name))
         self.fc.exec_command_test('mirror %s -box %s -axis X' % (self.excellon_name, self.gerber_cutout_name))
 
 
-        # TODO: tests for tcl
+        # TODO: tests for tcl
+
+    def test_import_svg(self):
+        """
+        Test all SVG files inside svg directory.
+        Problematic SVG files shold be put there as test reference.
+        :return:
+        """
+
+        self.fc.exec_command_test('set_sys units MM')
+        self.fc.exec_command_test('new')
+
+        file_list = listdir(self.svg_files)
+
+        for svg_file in file_list:
+
+            # import  without outname
+            self.fc.exec_command_test('import_svg "%s/%s"' % (self.svg_files, svg_file))
+
+            obj = self.fc.collection.get_by_name(svg_file)
+            self.assertTrue(isinstance(obj, FlatCAMGeometry),
+                        "Expected FlatCAMGeometry, instead, %s is %s" %
+                        (svg_file, type(obj)))
+
+            # import  with outname
+            outname='%s-%s' % (self.geometry_name, svg_file)
+            self.fc.exec_command_test('import_svg "%s/%s" -outname "%s"' % (self.svg_files, svg_file, outname))
+
+            obj = self.fc.collection.get_by_name(outname)
+            self.assertTrue(isinstance(obj, FlatCAMGeometry),
+                        "Expected FlatCAMGeometry, instead, %s is %s" %
+                        (outname, type(obj)))
+
+        names = self.fc.collection.get_names()
+        self.assertEqual(len(names), len(file_list)*2,
+                         "Expected %d objects, found %d" % (len(file_list)*2, len(file_list)))
+
+    def test_import_svg_as_geometry(self):
+        self.fc.exec_command_test('set_sys units MM')
+        self.fc.exec_command_test('new')
+        self.fc.exec_command_test('import_svg "%s/%s" -type geometry -outname "%s"' % (self.svg_files, self.svg_filename, self.geometry_name))
+
+        obj = self.fc.collection.get_by_name(self.geometry_name)
+        self.assertTrue(isinstance(obj, FlatCAMGeometry) and not isinstance(obj, FlatCAMGerber),
+                    "Expected FlatCAMGeometry, instead, %s is %s" %
+                    (self.geometry_name, type(obj)))
+
+    def test_import_svg_as_gerber(self):
+        self.fc.exec_command_test('set_sys units MM')
+        self.fc.exec_command_test('new')
+        self.fc.exec_command_test('import_svg "%s/%s" -type gerber -outname "%s"' % (self.svg_files, self.svg_filename, self.gerber_name))
+
+        obj = self.fc.collection.get_by_name(self.gerber_name)
+        self.assertTrue(isinstance(obj, FlatCAMGerber),
+                    "Expected FlatCAMGerber, instead, %s is %s" %
+                    (self.gerber_name, type(obj)))
+
+        self.fc.exec_command_test('isolate "%s"' % self.gerber_name)
+        obj = self.fc.collection.get_by_name(self.gerber_name+'_iso')
+        self.assertTrue(isinstance(obj, FlatCAMGeometry),
+                    "Expected FlatCAMGeometry, instead, %s is %s" %
+                    (self.gerber_name+'_iso', type(obj)))