diff --git a/src/Mod/Draft/importSVG.py b/src/Mod/Draft/importSVG.py index f44252600f..8dfbaaae0b 100644 --- a/src/Mod/Draft/importSVG.py +++ b/src/Mod/Draft/importSVG.py @@ -1233,7 +1233,8 @@ class svgHandler(xml.sax.ContentHandler): # Process ellipses if (name == "ellipse") : - if not pathname: pathname = 'Ellipse' + if not pathname: + pathname = 'Ellipse' c = Vector(data.get('cx',0),-data.get('cy',0),0) rx = data['rx'] ry = data['ry'] @@ -1243,7 +1244,8 @@ class svgHandler(xml.sax.ContentHandler): sh = Part.Ellipse(c,ry,rx).toShape() m3=FreeCAD.Matrix() m3.move(c) - rot90=FreeCAD.Matrix(0,-1,0,0,1,0) #90 + # 90 + rot90=FreeCAD.Matrix(0,-1,0,0,1,0) m3=m3.multiply(rot90) m3.move(c.multiply(-1)) sh.transformShape(m3) @@ -1258,11 +1260,10 @@ class svgHandler(xml.sax.ContentHandler): if self.currentsymbol: self.symbols[self.currentsymbol].append(obj) - - # processing circles - + # Process circles if (name == "circle") and (not ("freecad:skip" in data)) : - if not pathname: pathname = 'Circle' + if not pathname: + pathname = 'Circle' c = Vector(data.get('cx',0),-data.get('cy',0),0) r = data['r'] sh = Part.makeCircle(r) @@ -1277,8 +1278,7 @@ class svgHandler(xml.sax.ContentHandler): if self.currentsymbol: self.symbols[self.currentsymbol].append(obj) - # processing texts - + # Process texts if name in ["text","tspan"]: if not("freecad:skip" in data): FreeCAD.Console.PrintMessage("processing a text\n") @@ -1298,13 +1298,12 @@ class svgHandler(xml.sax.ContentHandler): else: if self.lastdim: self.lastdim.ViewObject.FontSize = int(getsize(data['font-size'])) - - # processing symbols - + + # Process symbols if name == "symbol": self.symbols[pathname] = [] self.currentsymbol = pathname - + if name == "use": if "xlink:href" in data: symbol = data["xlink:href"][0][1:] @@ -1326,8 +1325,10 @@ class svgHandler(xml.sax.ContentHandler): FreeCAD.Console.PrintMessage("no symbol data\n") FreeCAD.Console.PrintMessage("done processing element %d\n"%self.count) - + # startElement() + def characters(self,content): + """Read characters from the given string.""" if self.text: FreeCAD.Console.PrintMessage("reading characters %s\n" % content) obj=self.doc.addObject("App::Annotation",'Text') @@ -1345,10 +1346,19 @@ class svgHandler(xml.sax.ContentHandler): obj.Position = vec if gui: obj.ViewObject.FontSize = int(self.text) - if self.fill: obj.ViewObject.TextColor = self.fill - else: obj.ViewObject.TextColor = (0.0,0.0,0.0,0.0) + if self.fill: + obj.ViewObject.TextColor = self.fill + else: + obj.ViewObject.TextColor = (0.0,0.0,0.0,0.0) def endElement(self, name): + """Finish procesing the element indicated by the name. + + Parameters + ---------- + name : str + The name of the element + """ if not name in ["tspan"]: self.transform = None self.text = None @@ -1366,6 +1376,13 @@ class svgHandler(xml.sax.ContentHandler): def applyTrans(self,sh): + """Apply transformation to the shape and return the new shape. + + Parameters + ---------- + sh : Part.Shape or Draft.Dimension + Object to be transformed + """ if isinstance(sh,Part.Shape): if self.transform: FreeCAD.Console.PrintMessage("applying object transform: %s\n" % self.transform) @@ -1394,11 +1411,33 @@ class svgHandler(xml.sax.ContentHandler): sh.Dimline = pts[2] def translateVec(self,vec,mat): + """Translate a point or vector by a matrix. + + Parameters + ---------- + vec : Base::Vector3 + The original vector + mat : Base::Matrix4D + The translation matrix, from which only the elements 14, 24, 34 + are used. + """ v = Vector(mat.A14,mat.A24,mat.A34) return vec.add(v) def getMatrix(self,tr): - "returns a FreeCAD matrix from a svg transform attribute" + """Return a FreeCAD matrix from an SVG transform attribute. + + Parameters + ---------- + tr : str + The type of transform: 'matrix', 'translate', 'scale', + 'rotate', 'skewX', 'skewY' and its value + + Returns + ------- + Base::Matrix4D + The translated matrix. + """ transformre=re.compile('(matrix|translate|scale|rotate|skewX|skewY)\s*?\((.*?)\)',re.DOTALL) m = FreeCAD.Matrix() for transformation, arguments in transformre.findall(tr): @@ -1421,7 +1460,9 @@ class svgHandler(xml.sax.ContentHandler): cx = argsplit[1] cy = argsplit[2] m.move(Vector(cx,-cy,0)) - m.rotateZ(math.radians(-angle)) #mirroring one axis equals changing the direction of rotation + # Mirroring one axis is equal to changing the direction + # of rotation + m.rotateZ(math.radians(-angle)) if len(argsplit) >= 3: m.move(Vector(-cx,cy,0)) elif transformation == 'skewX': @@ -1429,12 +1470,12 @@ class svgHandler(xml.sax.ContentHandler): elif transformation == 'skewY': m=m.multiply(FreeCAD.Matrix(1,0,0,0,-math.tan(math.radians(argsplit[0])))) elif transformation == 'matrix': -# '''transformation matrix: -# FreeCAD SVG -# (+A -C +0 +E) (A C 0 E) -# (-B +D -0 -F) = (-Y) * (B D 0 F) *(-Y) -# (+0 -0 +1 +0) (0 0 1 0) -# (+0 -0 +0 +1) (0 0 0 1)''' +# # transformation matrix: +# # FreeCAD SVG +# # (+A -C +0 +E) (A C 0 E) +# # (-B +D -0 -F) = (-Y) * (B D 0 F) *(-Y) +# # (+0 -0 +1 +0) (0 0 1 0) +# # (+0 -0 +0 +1) (0 0 0 1) m=m.multiply(FreeCAD.Matrix(argsplit[0],-argsplit[2],0,argsplit[4],-argsplit[1],argsplit[3],0,-argsplit[5])) #else: #print 'SKIPPED %s' % transformation @@ -1442,8 +1483,22 @@ class svgHandler(xml.sax.ContentHandler): #print "generating transformation: ",m return m + def decodeName(name): - "decodes encoded strings" + """Decode encoded name. + + Parameters + ---------- + name : str + The string to decode. + + Returns + ------- + tuple + (string) + A tuple containing the decoded `name` in 'utf8', otherwise in 'latin1'. + If it fails it returns the original `name`. + """ try: decodedName = (name.decode("utf8")) except UnicodeDecodeError: @@ -1451,19 +1506,43 @@ def decodeName(name): decodedName = (name.decode("latin1")) except UnicodeDecodeError: FreeCAD.Console.PrintError("svg: error: couldn't determine character encoding\n") - decodedName = name return decodedName + def getContents(filename,tag,stringmode=False): - "gets the contents of all the occurrences of the given tag in the given file" + """Get the contents of all occurrences of the given tag in the file. + + Parameters + ---------- + filename : str + A filename to scan for tags. + tag : str + An SVG tag to find inside a file, for example, `some` + in information + stringmode : bool, optional + The default is False. + If False, `filename` is a path to a file. + If True, `filename` is already a pointer to an open file. + + Returns + ------- + dict + A dictionary with tagids and the information associated with that id + results[tagid] = information + """ result = {} if stringmode: contents = filename else: + # Use the native Python open which was saved as `pythonopen`. f = pythonopen(filename) contents = f.read() f.close() + + # Replace the newline character with a string + # so that it's easiert to parse; later on the newline character + # will be restored contents = contents.replace('\n','_linebreak') searchpat = '<'+tag+'.*?' tags = re.findall(searchpat,contents) @@ -1477,42 +1556,99 @@ def getContents(filename,tag,stringmode=False): result[tagid] = res return result + def open(filename): + """Open filename and parse using the svgHandler(). + + Parameters + ---------- + filename : str + The path to the filename to be opened. + + Returns + ------- + App::Document + The new FreeCAD document object created, with the parsed information. + """ docname=os.path.split(filename)[1] doc=FreeCAD.newDocument(docname) doc.Label = docname[:-4] + + # Set up the parser parser = xml.sax.make_parser() parser.setFeature(xml.sax.handler.feature_external_ges, False) parser.setContentHandler(svgHandler()) parser._cont_handler.doc = doc + + # Use the native Python open which was saved as `pythonopen`. f = pythonopen(filename) parser.parse(f) f.close() doc.recompute() return doc + def insert(filename,docname): + """Get an active document and parse using the svgHandler(). + + If no document exist, it is created. + + Parameters + ---------- + filename : str + The path to the filename to be opened. + docname : str + The name of the active App::Document if one exists, or + of the new one created. + + Returns + ------- + App::Document + The active FreeCAD document, or the document created if none exists, + with the parsed information. + """ try: doc=FreeCAD.getDocument(docname) except NameError: doc=FreeCAD.newDocument(docname) FreeCAD.ActiveDocument = doc + + # Set up the parser parser = xml.sax.make_parser() parser.setFeature(xml.sax.handler.feature_external_ges, False) parser.setContentHandler(svgHandler()) parser._cont_handler.doc = doc + + # Use the native Python open which was saved as `pythonopen` parser.parse(pythonopen(filename)) doc.recompute() -def export(exportList,filename): - "called when freecad exports a file" +def export(exportList,filename): + """Export the SVG file with a given list of objects. + + The objects must be derived from Part::Feature, in order to be processed + and exported. + + Parameters + ---------- + exportList : list + List of document objects to export. + filename : str + Path to the new file. + + Returns + ------- + None + If `exportList` doesn't have shapes to export. + """ svg_export_style = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetInt("svg_export_style") if svg_export_style != 0 and svg_export_style != 1: FreeCAD.Console.PrintMessage(translate("Unknown SVG export style, switching to Translated")+"\n") svg_export_style = 0 - # finding sheet size + # Determine the size of the page by adding the bounding boxes + # of all shapes bb = None for ob in exportList: if ob.isDerivedFrom("Part::Feature"): @@ -1544,10 +1680,12 @@ def export(exportList,filename): sizey = maxy-miny miny += margin - # writing header - # we specify the svg width and height in FreeCAD's physical units (mm), - # and specify the viewBox so that user units maps one-to-one to mm - svg = pythonopen(filename,'w') + # Use the native Python open which was saved as `pythonopen` + svg = pythonopen(filename,'w') + + # Write header. + # We specify the SVG width and height in FreeCAD's physical units (mm), + # and specify the viewBox so that user units maps one-to-one to mm. svg.write('\n') svg.write('\n') @@ -1564,22 +1702,22 @@ def export(exportList,filename): svg.write(' xmlns="http://www.w3.org/2000/svg" version="1.1"') svg.write('>\n') - # writing paths + # Write paths for ob in exportList: if svg_export_style == 0: - # translated-style exports have the entire sketch translated to fit in the X>0, Y>0 quadrant + # translated-style exports have the entire sketch translated + # to fit in the X>0, Y>0 quadrant #svg.write('\n') svg.write('\n'% (ob.Name,-minx,maxy)) + 'scale(1,-1)">\n'% (ob.Name,-minx,maxy)) else: # raw-style exports do not translate the sketch - svg.write('\n' %\ - ob.Name) + svg.write('\n' % ob.Name) svg.write(Draft.getSVG(ob)) - svg.write('%s\n' % str(ob.Label.encode('utf8'))\ - .replace('<','<').replace('>','>')) - # replace('"',\ """) + svg.write('%s\n' % str(ob.Label.encode('utf8')).replace('<','<').replace('>','>')) + # replace('"', """) svg.write('\n') - # closing + + # Close the file svg.write('') svg.close()