diff --git a/src/Mod/Draft/importSVG.py b/src/Mod/Draft/importSVG.py index 3e9c58d740..d9d4860827 100644 --- a/src/Mod/Draft/importSVG.py +++ b/src/Mod/Draft/importSVG.py @@ -1,4 +1,18 @@ +## @package importSVG +# \ingroup DRAFT +# \brief SVG file importer & exporter +''' +This module provides support for importing and exporting SVG files. It +enables importing/exporting objects directly to/from the 3D document, but +doesn't handle the SVG output from the Drawng and TechDraw modules. + +Currently it only reads the following entities: +* paths, lines, circular arcs, rects, circles, ellipses, polygons, polylines. + +Currently unsupported: +* use, image. +''' #*************************************************************************** #* * #* Copyright (c) 2009 Yorik van Havre * @@ -25,19 +39,6 @@ __title__="FreeCAD Draft Workbench - SVG importer/exporter" __author__ = "Yorik van Havre, Sebastian Hoogen" __url__ = ["http://www.freecadweb.org"] -## @package importSVG -# \ingroup DRAFT -# \brief SVG file importer & exporter -# -# This module provides support for importing and exporting SVG files. It -# enables importing/exporting objects directly to/from the 3D document, but -# doesn't handle the SVG output from the Drawng and TechDraw modules. - -''' -This script imports SVG files in FreeCAD. Currently only reads the following entities: -paths, lines, circular arcs ,rects, circles, ellipses, polygons, polylines. -currently unsupported: use, image -''' #ToDo: # ignoring CDATA # handle image element (external references and inline base64) @@ -240,15 +241,19 @@ def getcolor(color): RGBA float tuple, where each value is between 0.0 and 1.0. """ if (color[0] == "#"): + # Color string '#12ab9f' if len(color) == 7: r = float(int(color[1:3],16)/255.0) g = float(int(color[3:5],16)/255.0) b = float(int(color[5:],16)/255.0) - elif len(color) == 4: #expand the hex digits + # Color string '#1af' + elif len(color) == 4: + # Expand the hex digits r = float(int(color[1],16)*17/255.0) g = float(int(color[2],16)*17/255.0) b = float(int(color[3],16)*17/255.0) return (r,g,b,0.0) + # Color string 'rgb(0.12,0.23,0.3,0.0)' elif color.lower().startswith('rgb('): cvalues=color[3:].lstrip('(').rstrip(')').replace('%','').split(',') if '%' in color: @@ -256,13 +261,16 @@ def getcolor(color): else: r,g,b = [int(float(cv))/255.0 for cv in cvalues] return (r,g,b,0.0) + # Color string 'MediumAquamarine' else: v=svgcolorslower.get(color.lower()) if v: r,g,b = [float(vf)/255.0 for vf in v] return (r,g,b,0.0) #for k,v in svgcolors.items(): - # if (k.lower() == color.lower()): pass + # if (k.lower() == color.lower()): + # pass + def transformCopyShape(shape,m): """Apply transformation matrix m on given shape. @@ -286,14 +294,17 @@ def transformCopyShape(shape,m): shape : Part::TopoShape The shape transformed by the matrix """ + # If there is no shear, these matrix operations will be very small if abs(m.A11**2+m.A12**2 -m.A21**2-m.A22**2) < 1e-8 and \ - abs(m.A11*m.A21+m.A12*m.A22) < 1e-8: #no shear + abs(m.A11*m.A21+m.A12*m.A22) < 1e-8: try: newshape=shape.copy() newshape.transformShape(m) return newshape - except Part.OCCError: # older versions of OCCT will refuse to work on - pass # non-orthogonal matrices + # Older versions of OCCT will refuse to work on + # non-orthogonal matrices + except Part.OCCError: + pass return shape.transformGeometry(m) @@ -434,7 +445,8 @@ def makewire(path,checkclosed=False,donttry=False): isok = (not checkclosed) or sh.isClosed() if len(sh.Edges) != len(path): isok = False - except Part.OCCError:# BRep_API:command not done + # BRep_API:command not done + except Part.OCCError: isok = False if donttry or not isok: #Code from wmayer forum p15549 to fix the tolerance problem @@ -582,20 +594,32 @@ def arcend2center(lastvec,currentvec,rx,ry,xrotation=0.0,correction=False): def getrgb(color): - "returns a rgb value #000000 from a freecad color" + """Return an RGB headecimal string '#00aaff' from a FreeCAD color. + + Parameters + ---------- + color : App::Color::Color + FreeCAD color. + + Returns + ------- + str + The hexadecimal string representation of the color '#00aaff'. + """ r = str(hex(int(color[0]*255)))[2:].zfill(2) g = str(hex(int(color[1]*255)))[2:].zfill(2) b = str(hex(int(color[2]*255)))[2:].zfill(2) return "#"+r+g+b class svgHandler(xml.sax.ContentHandler): - "this handler parses the svg files and creates freecad objects" + """Parse SVG files and create FreeCAD objects.""" def __init__(self): - "retrieving Draft parameters" + """Retrieve Draft parameters and initialize.""" params = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft") self.style = params.GetInt("svgstyle") - self.disableUnitScaling = params.GetBool("svgDisableUnitScaling",False) + self.disableUnitScaling = params.GetBool("svgDisableUnitScaling", + False) self.count = 0 self.transform = None self.grouptransform = [] @@ -607,7 +631,7 @@ class svgHandler(xml.sax.ContentHandler): global Part import Part - + if gui and draftui: r = float(draftui.color.red()/255.0) g = float(draftui.color.green()/255.0) @@ -622,22 +646,31 @@ class svgHandler(xml.sax.ContentHandler): self.col = (r,g,b,0.0) def format(self,obj): - "applies styles to passed object" + """Apply styles to the object if the graphical interface is up.""" if gui: v = obj.ViewObject - if self.color: v.LineColor = self.color - if self.width: v.LineWidth = self.width - if self.fill: v.ShapeColor = self.fill - + if self.color: + v.LineColor = self.color + if self.width: + v.LineWidth = self.width + if self.fill: + v.ShapeColor = self.fill + def startElement(self, name, attrs): + """Re-organize data into a nice clean dictionary. - # reorganizing data into a nice clean dictionary - + Parameters + ---------- + name : str + Name of the element: 'path', 'rect', 'line', 'polyline', + 'polygon', 'ellipse', 'circle', 'text', 'tspan', 'symbol' + attrs : iterable + Dictionary of content of the elements + """ self.count += 1 - FreeCAD.Console.PrintMessage('processing element %d: %s\n'%(self.count,name)) FreeCAD.Console.PrintMessage('existing group transform: %s\n'%(str(self.grouptransform))) - + data = {} for (keyword,content) in list(attrs.items()): #print keyword,content @@ -647,11 +680,17 @@ class svgHandler(xml.sax.ContentHandler): #print keyword,content data[keyword]=content + # If it's the first element, which is , + # check if the file is created by Inkscape, and its version, + # in order to consider some attributes of the SVG file. if self.count == 1 and name == 'svg': if 'inkscape:version' in data: InksDocName = attrs.getValue('sodipodi:docname') InksFullver = attrs.getValue('inkscape:version')[:4] InksFullverlst = InksFullver.split('.') + + # Inkscape before 0.92 used 90 dpi as resolution + # Newer versions use 96 dpi if ( int(InksFullverlst[0]) == 0 and int(InksFullverlst[1]) > 91 @@ -682,9 +721,11 @@ class svgHandler(xml.sax.ContentHandler): if self.svgdpi == 1.0: FreeCAD.Console.PrintWarning("This SVG file ("+InksDocName+") has an unrecognised format which means the dpi could not be determined; therefore importing with 96dpi\n") self.svgdpi = 96.0 + if 'style' in data: if not data['style']: - pass#empty style attribute stops inhertig from parent + # Empty style attribute stops inheriting from parent + pass else: content = data['style'].replace(' ','') content = content.split(';') @@ -704,8 +745,7 @@ class svgHandler(xml.sax.ContentHandler): else: data[k]=data[k][0] - # extracting style info - + # Extract style info self.fill = None self.color = None self.width = None @@ -714,8 +754,9 @@ class svgHandler(xml.sax.ContentHandler): if name == 'svg': m=FreeCAD.Matrix() if not self.disableUnitScaling: - if 'width' in data and 'height' in data and \ - 'viewBox' in data: + if 'width' in data \ + and 'height' in data \ + and 'viewBox' in data: vbw=float(data['viewBox'][2]) vbh=float(data['viewBox'][3]) w=attrs.getValue('width') @@ -723,7 +764,8 @@ class svgHandler(xml.sax.ContentHandler): self.viewbox=(vbw,vbh) if len(self.grouptransform)==0: unitmode='mm'+str(self.svgdpi) - else: #nested svg element + else: + # nested svg element unitmode='css'+str(self.svgdpi) abw = getsize(w,unitmode) abh = getsize(h,unitmode) @@ -737,14 +779,15 @@ class svgHandler(xml.sax.ContentHandler): FreeCAD.Console.PrintWarning('Scaling Factors do not match!!!\n') if preservearstr.startswith('none'): m.scale(Vector(sx,sy,1)) - else: #preserve the aspect ratio + else: + # preserve the aspect ratio if preservearstr.endswith('slice'): sxy=max(sx,sy) else: sxy=min(sx,sy) m.scale(Vector(sxy,sxy,1)) elif len(self.grouptransform)==0: - #fallback to current dpi + # fallback to current dpi m.scale(Vector(25.4/self.svgdpi,25.4/self.svgdpi,1)) self.grouptransform.append(m) if 'fill' in data: @@ -755,7 +798,8 @@ class svgHandler(xml.sax.ContentHandler): self.color = getcolor(data['stroke']) if 'stroke-width' in data: if data['stroke-width'] != 'none': - self.width = getsize(data['stroke-width'],'css'+str(self.svgdpi)) + self.width = getsize(data['stroke-width'], + 'css'+str(self.svgdpi)) if 'transform' in data: m = self.getMatrix(attrs.getValue('transform')) if name == "g": @@ -774,13 +818,13 @@ class svgHandler(xml.sax.ContentHandler): if 'id' in data: pathname = data['id'][0] FreeCAD.Console.PrintMessage('name: %s\n'%pathname) - - # processing paths - + + # Process paths if name == "path": FreeCAD.Console.PrintMessage('data: %s\n'%str(data)) - - if not pathname: pathname = 'Path' + + if not pathname: + pathname = 'Path' path = [] point = [] @@ -802,6 +846,7 @@ class svgHandler(xml.sax.ContentHandler): self.format(obj) self.lastdim = obj data['d']=[] + pathcommandsre=re.compile('\s*?([mMlLhHvVaAcCqQsStTzZ])\s*?([^mMlLhHvVaAcCqQsStTzZ]*)\s*?',re.DOTALL) pointsre=re.compile('([-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?)',re.DOTALL) for d,pointsstr in pathcommandsre.findall(' '.join(data['d'])): @@ -823,8 +868,10 @@ class svgHandler(xml.sax.ContentHandler): if self.currentsymbol: self.symbols[self.currentsymbol].append(obj) path = [] - #if firstvec: - # lastvec = firstvec #Move relative to last move command not last draw command + # if firstvec: + # Move relative to last move command + # not last draw command + # lastvec = firstvec if relative: lastvec = lastvec.add(Vector(x,-y,0)) else: