import FreeCAD, math, sys, os, DraftVecUtils, WorkingPlane import Part, DraftGeomUtils from FreeCAD import Vector from Draft import getType, getrgb, svgpatterns def getLineStyle(linestyle, scale): "returns a linestyle" p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft") l = None if linestyle == "Dashed": l = p.GetString("svgDashedLine","0.09,0.05") elif linestyle == "Dashdot": l = p.GetString("svgDashdotLine","0.09,0.05,0.02,0.05") elif linestyle == "Dotted": l = p.GetString("svgDottedLine","0.02,0.02") elif linestyle: if "," in linestyle: l = linestyle if l: l = l.split(",") try: # scale dashes l = ",".join([str(float(d)/scale) for d in l]) #print "lstyle ",l except: return "none" else: return l return "none" def getProj(vec, plane): if not plane: return vec nx = DraftVecUtils.project(vec,plane.u) lx = nx.Length if abs(nx.getAngle(plane.u)) > 0.1: lx = -lx ny = DraftVecUtils.project(vec,plane.v) ly = ny.Length if abs(ny.getAngle(plane.v)) > 0.1: ly = -ly #if techdraw: buggy - we now simply do it at the end # ly = -ly return Vector(lx,ly,0) def getDiscretized(edge, plane): ml = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft").GetFloat("svgDiscretization",10.0) if ml == 0: ml = 10 d = int(edge.Length/ml) if d == 0: d = 1 edata = "" for i in range(d+1): v = getProj(edge.valueAt(edge.FirstParameter+((float(i)/d)*(edge.LastParameter-edge.FirstParameter))), plane) if not edata: edata += 'M ' + str(v.x) +' '+ str(v.y) + ' ' else: edata += 'L ' + str(v.x) +' '+ str(v.y) + ' ' return edata def getPattern(pat): if pat in svgpatterns(): return svgpatterns()[pat][0] return '' def getSVG(obj,scale=1,linewidth=0.35,fontsize=12,fillstyle="shape color",direction=None,linestyle=None,color=None,linespacing=None,techdraw=False,rotation=0): '''getSVG(object,[scale], [linewidth],[fontsize],[fillstyle],[direction],[linestyle],[color],[linespacing]): returns a string containing a SVG representation of the given object, with the given linewidth and fontsize (used if the given object contains any text). You can also supply an arbitrary projection vector. the scale parameter allows to scale linewidths down, so they are resolution-independant.''' # if this is a group, gather all the svg views of its children if hasattr(obj,"isDerivedFrom"): if obj.isDerivedFrom("App::DocumentObjectGroup"): svg = "" for child in obj.Group: svg += getSVG(child,scale,linewidth,fontsize,fillstyle,direction,linestyle,color,linespacing,techdraw) return svg pathdata = [] svg = "" linewidth = float(linewidth)/scale fontsize = (float(fontsize)/scale)/2 if linespacing: linespacing = float(linespacing)/scale else: linespacing = 0.5 #print obj.Label," line spacing ",linespacing,"scale ",scale pointratio = .75 # the number of times the dots are smaller than the arrow size plane = None if direction: if isinstance(direction,FreeCAD.Vector): if direction != Vector(0,0,0): plane = WorkingPlane.plane() plane.alignToPointAndAxis_SVG(Vector(0,0,0),direction.negative().negative(),0) elif isinstance(direction,WorkingPlane.plane): plane = direction stroke = "#000000" if color: if "#" in color: stroke = color else: stroke = getrgb(color) elif gui: if hasattr(obj,"ViewObject"): if hasattr(obj.ViewObject,"LineColor"): stroke = getrgb(obj.ViewObject.LineColor) def getPath(edges=[],wires=[],pathname=None): import Part,DraftGeomUtils svg = " 1e-6: vs.reverse() if edgeindex == 0: v = getProj(vs[0].Point, plane) edata += 'M '+ str(v.x) +' '+ str(v.y) + ' ' else: if (vs[0].Point-previousvs[-1].Point).Length > 1e-6: raise ValueError('edges not ordered') iscircle = DraftGeomUtils.geomType(e) == "Circle" isellipse = DraftGeomUtils.geomType(e) == "Ellipse" if iscircle or isellipse: import math if hasattr(FreeCAD,"DraftWorkingPlane"): drawing_plane_normal = FreeCAD.DraftWorkingPlane.axis else: drawing_plane_normal = FreeCAD.Vector(0,0,1) if plane: drawing_plane_normal = plane.axis c = e.Curve if round(c.Axis.getAngle(drawing_plane_normal),2) in [0,3.14]: occversion = Part.OCC_VERSION.split(".") done = False if (int(occversion[0]) >= 7) and (int(occversion[1]) >= 1): # if using occ >= 7.1, use HLR algorithm import Drawing snip = Drawing.projectToSVG(e,drawing_plane_normal) if snip: try: a = "A " + snip.split("path d=\"")[1].split("\"")[0].split("A")[1] except: pass else: edata += a done = True if not done: if len(e.Vertexes) == 1 and iscircle: #complete curve svg = getCircle(e) return svg elif len(e.Vertexes) == 1 and isellipse: #svg = getEllipse(e) #return svg endpoints = (getProj(c.value((c.LastParameter-\ c.FirstParameter)/2.0)), \ getProj(vs[-1].Point), plane) else: endpoints = (getProj(vs[-1].Point), plane) # arc if iscircle: rx = ry = c.Radius rot = 0 else: #ellipse rx = c.MajorRadius ry = c.MinorRadius rot = math.degrees(c.AngleXU * (c.Axis * \ FreeCAD.Vector(0,0,1))) if rot > 90: rot -=180 if rot < -90: rot += 180 #be careful with the sweep flag flag_large_arc = (((e.ParameterRange[1] - \ e.ParameterRange[0]) / math.pi) % 2) > 1 #flag_sweep = (c.Axis * drawing_plane_normal >= 0) \ # == (e.LastParameter > e.FirstParameter) # == (e.Orientation == "Forward") # other method: check the direction of the angle between tangents t1 = e.tangentAt(e.FirstParameter) t2 = e.tangentAt(e.FirstParameter + (e.LastParameter-e.FirstParameter)/10) flag_sweep = (DraftVecUtils.angle(t1,t2,drawing_plane_normal) < 0) for v in endpoints: edata += 'A %s %s %s %s %s %s %s ' % \ (str(rx),str(ry),str(rot),\ str(int(flag_large_arc)),\ str(int(flag_sweep)),str(v.x),str(v.y)) else: edata += getDiscretized(e, plane) elif DraftGeomUtils.geomType(e) == "Line": v = getProj(vs[-1].Point, plane) edata += 'L '+ str(v.x) +' '+ str(v.y) + ' ' else: bspline=e.Curve.toBSpline(e.FirstParameter,e.LastParameter) if bspline.Degree > 3 or bspline.isRational(): try: bspline=bspline.approximateBSpline(0.05,50, 3,'C0') except RuntimeError: print("Debug: unable to approximate bspline") if bspline.Degree <= 3 and not bspline.isRational(): for bezierseg in bspline.toBezier(): if bezierseg.Degree>3: #should not happen raise AssertionError elif bezierseg.Degree==1: edata +='L ' elif bezierseg.Degree==2: edata +='Q ' elif bezierseg.Degree==3: edata +='C ' for pole in bezierseg.getPoles()[1:]: v = getProj(pole, plane) edata += str(v.x) +' '+ str(v.y) + ' ' else: print("Debug: one edge (hash ",e.hashCode(),\ ") has been discretized with parameter 0.1") for linepoint in bspline.discretize(0.1)[1:]: v = getProj(linepoint, plane) edata += 'L '+ str(v.x) +' '+ str(v.y) + ' ' if fill != 'none': edata += 'Z ' if edata in pathdata: # do not draw a path on another identical path return "" else: svg += edata pathdata.append(edata) svg += '" ' svg += 'stroke="' + stroke + '" ' svg += 'stroke-width="' + str(linewidth) + ' px" ' svg += 'style="stroke-width:'+ str(linewidth) svg += ';stroke-miterlimit:4' svg += ';stroke-dasharray:' + lstyle svg += ';fill:' + fill try: svg += ';fill-opacity:' + str(fill_opacity) except NameError: pass svg += ';fill-rule: evenodd "' svg += '/>\n' return svg def getCircle(edge): cen = getProj(edge.Curve.Center, plane) rad = edge.Curve.Radius if hasattr(FreeCAD,"DraftWorkingPlane"): drawing_plane_normal = FreeCAD.DraftWorkingPlane.axis else: drawing_plane_normal = FreeCAD.Vector(0,0,1) if plane: drawing_plane_normal = plane.axis if round(edge.Curve.Axis.getAngle(drawing_plane_normal),2) == 0: # perpendicular projection: circle svg = '\n' else: print("getSVG: arrow type not implemented") return svg def getOvershoot(point,shootsize,color,linewidth,angle=0): svg = '\n' return svg def getText(color,fontsize,fontname,angle,base,text,linespacing=0.5,align="center",flip=True): if isinstance(angle,FreeCAD.Rotation): if not plane: angle = angle.Angle else: if plane.axis.getAngle(angle.Axis) < 0.001: angle = angle.Angle elif abs(plane.axis.getAngle(angle.Axis)-math.pi) < 0.001: return "" # text is perpendicular to view, so it shouldn't appear else: angle = 0 #TODO maybe there is something better to do here? if not isinstance(text,list): text = text.split("\n") if align.lower() == "center": anchor = "middle" elif align.lower() == "left": anchor = "start" else: anchor = "end" if techdraw: svg = "" for i in range(len(text)): t = text[i] if sys.version_info.major < 3 and (not isinstance(t,unicode)): t = t.decode("utf8") # possible workaround if UTF8 is unsupported # import unicodedata # t = u"".join([c for c in unicodedata.normalize("NFKD",t) if not unicodedata.combining(c)]).encode("utf8") svg += '\n' else: svg = '' try: svg += text[i] except: svg += text[i].decode("utf8") svg += '\n' svg += '\n' return svg if not obj: pass elif isinstance(obj,Part.Shape): if "#" in fillstyle: fill = fillstyle elif fillstyle == "shape color": fill = "#888888" else: fill = 'url(#'+fillstyle+')' lstyle = getLineStyle(linestyle, scale) svg += getPath(obj.Edges,pathname="") elif getType(obj) == "Dimension": if gui: if not obj.ViewObject: print ("export of dimensions to SVG is only available in GUI mode") elif obj.ViewObject.Proxy: if hasattr(obj.ViewObject.Proxy,"p1"): prx = obj.ViewObject.Proxy ts = (len(prx.string)*obj.ViewObject.FontSize.Value)/4.0 rm = ((prx.p3.sub(prx.p2)).Length/2.0)-ts p2a = getProj(prx.p2.add(DraftVecUtils.scaleTo(prx.p3.sub(prx.p2),rm)), plane) p2b = getProj(prx.p3.add(DraftVecUtils.scaleTo(prx.p2.sub(prx.p3),rm)), plane) p1 = getProj(prx.p1, plane) p2 = getProj(prx.p2, plane) p3 = getProj(prx.p3, plane) p4 = getProj(prx.p4, plane) tbase = getProj(prx.tbase, plane) r = prx.textpos.rotation.getValue().getValue() rv = FreeCAD.Rotation(r[0],r[1],r[2],r[3]).multVec(FreeCAD.Vector(1,0,0)) angle = -DraftVecUtils.angle(getProj(rv, plane)) #angle = -DraftVecUtils.angle(p3.sub(p2)) # drawing lines svg = ' math.pi/2: tangle = tangle-math.pi #elif (tangle <= -math.pi/2) or (tangle > math.pi/2): # tangle = tangle+math.pi #tbase = tbase.add(DraftVecUtils.rotate(Vector(0,2/scale,0),tangle)) if rotation != 0: #print "dim: tangle:",tangle," rot: ",rotation," text: ",prx.string if abs(tangle+math.radians(rotation)) < 0.0001: tangle += math.pi tbase = tbase.add(DraftVecUtils.rotate(Vector(0,2/scale,0),tangle)) svg += 'd="M '+str(p1.x)+' '+str(p1.y)+' ' svg += 'L '+str(p2.x)+' '+str(p2.y)+' ' svg += 'L '+str(p3.x)+' '+str(p3.y)+' ' svg += 'L '+str(p4.x)+' '+str(p4.y)+'" ' else: tangle = 0 if rotation != 0: tangle = -math.radians(rotation) tbase = tbase.add(Vector(0,-2.0/scale,0)) svg += 'd="M '+str(p1.x)+' '+str(p1.y)+' ' svg += 'L '+str(p2.x)+' '+str(p2.y)+' ' svg += 'L '+str(p2a.x)+' '+str(p2a.y)+' ' svg += 'M '+str(p2b.x)+' '+str(p2b.y)+' ' svg += 'L '+str(p3.x)+' '+str(p3.y)+' ' svg += 'L '+str(p4.x)+' '+str(p4.y)+'" ' svg += 'fill="none" stroke="' svg += stroke + '" ' svg += 'stroke-width="' + str(linewidth) + ' px" ' svg += 'style="stroke-width:'+ str(linewidth) svg += ';stroke-miterlimit:4;stroke-dasharray:none" ' svg += 'freecad:basepoint1="'+str(p1.x)+' '+str(p1.y)+'" ' svg += 'freecad:basepoint2="'+str(p4.x)+' '+str(p4.y)+'" ' svg += 'freecad:dimpoint="'+str(p2.x)+' '+str(p2.y)+'"' svg += '/>\n' # drawing dimension and extension lines overshoots if hasattr(obj.ViewObject,"DimOvershoot") and obj.ViewObject.DimOvershoot.Value: shootsize = obj.ViewObject.DimOvershoot.Value/pointratio svg += getOvershoot(p2,shootsize,stroke,linewidth,angle) svg += getOvershoot(p3,shootsize,stroke,linewidth,angle+math.pi) if hasattr(obj.ViewObject,"ExtOvershoot") and obj.ViewObject.ExtOvershoot.Value: shootsize = obj.ViewObject.ExtOvershoot.Value/pointratio shootangle = -DraftVecUtils.angle(p1.sub(p2)) svg += getOvershoot(p2,shootsize,stroke,linewidth,shootangle) svg += getOvershoot(p3,shootsize,stroke,linewidth,shootangle) # drawing arrows if hasattr(obj.ViewObject,"ArrowType"): arrowsize = obj.ViewObject.ArrowSize.Value/pointratio if hasattr(obj.ViewObject,"FlipArrows"): if obj.ViewObject.FlipArrows: angle = angle+math.pi svg += getArrow(obj.ViewObject.ArrowType,p2,arrowsize,stroke,linewidth,angle) svg += getArrow(obj.ViewObject.ArrowType,p3,arrowsize,stroke,linewidth,angle+math.pi) # drawing text svg += getText(stroke,fontsize,obj.ViewObject.FontName,tangle,tbase,prx.string) elif getType(obj) == "AngularDimension": if gui: if not obj.ViewObject: print ("export of dimensions to SVG is only available in GUI mode") elif obj.ViewObject.Proxy: if hasattr(obj.ViewObject.Proxy,"circle"): prx = obj.ViewObject.Proxy # drawing arc fill= "none" lstyle = getLineStyle(linestyle, scale) if obj.ViewObject.DisplayMode == "2D": svg += getPath([prx.circle]) else: if hasattr(prx,"circle1"): svg += getPath([prx.circle1]) svg += getPath([prx.circle2]) else: svg += getPath([prx.circle]) # drawing arrows if hasattr(obj.ViewObject,"ArrowType"): p2 = getProj(prx.p2, plane) p3 = getProj(prx.p3, plane) arrowsize = obj.ViewObject.ArrowSize.Value/pointratio arrowlength = 4*obj.ViewObject.ArrowSize.Value u1 = getProj((prx.circle.valueAt(prx.circle.FirstParameter+arrowlength)).sub(prx.circle.valueAt(prx.circle.FirstParameter)), plane) u2 = getProj((prx.circle.valueAt(prx.circle.LastParameter-arrowlength)).sub(prx.circle.valueAt(prx.circle.LastParameter)), plane) angle1 = -DraftVecUtils.angle(u1) angle2 = -DraftVecUtils.angle(u2) if hasattr(obj.ViewObject,"FlipArrows"): if obj.ViewObject.FlipArrows: angle1 = angle1+math.pi angle2 = angle2+math.pi svg += getArrow(obj.ViewObject.ArrowType,p2,arrowsize,stroke,linewidth,angle1) svg += getArrow(obj.ViewObject.ArrowType,p3,arrowsize,stroke,linewidth,angle2) # drawing text if obj.ViewObject.DisplayMode == "2D": t = prx.circle.tangentAt(prx.circle.FirstParameter+(prx.circle.LastParameter-prx.circle.FirstParameter)/2.0) t = getProj(t, plane) tangle = DraftVecUtils.angle(t) if (tangle <= -math.pi/2) or (tangle > math.pi/2): tangle = tangle + math.pi tbase = getProj(prx.circle.valueAt(prx.circle.FirstParameter+(prx.circle.LastParameter-prx.circle.FirstParameter)/2.0), plane) tbase = tbase.add(DraftVecUtils.rotate(Vector(0,2.0/scale,0),tangle)) #print(tbase) else: tangle = 0 tbase = getProj(prx.tbase, plane) svg += getText(stroke,fontsize,obj.ViewObject.FontName,tangle,tbase,prx.string) elif getType(obj) == "Label": if getattr(obj.ViewObject, "Line", True): # some Labels may have no Line property def format_point(coords, action='L'): return "{action}{x},{y}".format( x=coords.x, y=coords.y, action=action ) # Draw multisegment line proj_points = list(map(getProj, obj.Points), plane) path_dir_list = [format_point(proj_points[0], action='M')] path_dir_list += map(format_point, proj_points[1:]) path_dir_str = " ".join(path_dir_list) svg_path = ''.format( stroke=stroke, linewidth=linewidth, directions=path_dir_str ) svg += svg_path # Draw arrow. # We are different here from 3D view # if Line is set to 'off', no arrow is drawn if hasattr(obj.ViewObject, "ArrowType") and len(obj.Points) >= 2: last_segment = FreeCAD.Vector(obj.Points[-1] - obj.Points[-2]) angle = -DraftVecUtils.angle(getProj(last_segment, plane)) + math.pi svg += getArrow( arrowtype=obj.ViewObject.ArrowType, point=proj_points[-1], arrowsize=obj.ViewObject.ArrowSize.Value/pointratio, color=stroke, linewidth=linewidth, angle=angle ) # print text if gui: if not obj.ViewObject: print("export of texts to SVG is only available in GUI mode") else: fontname = obj.ViewObject.TextFont position = getProj(obj.Placement.Base, plane) rotation = obj.Placement.Rotation justification = obj.ViewObject.TextAlignment text = obj.Text svg += getText(stroke, fontsize, fontname, rotation, position, text, linespacing, justification) elif getType(obj) in ["Annotation","DraftText"]: "returns an svg representation of a document annotation" if gui: if not obj.ViewObject: print ("export of texts to SVG is only available in GUI mode") else: n = obj.ViewObject.FontName if getType(obj) == "Annotation": p = getProj(obj.Position, plane) r = obj.ViewObject.Rotation.getValueAs("rad") t = obj.LabelText else: # DraftText p = getProj(obj.Placement.Base, plane) r = obj.Placement.Rotation t = obj.Text j = obj.ViewObject.Justification svg += getText(stroke,fontsize,n,r,p,t,linespacing,j) elif getType(obj) == "Axis": "returns the SVG representation of an Arch Axis system" if gui: if not obj.ViewObject: print ("export of axes to SVG is only available in GUI mode") else: vobj = obj.ViewObject lorig = getLineStyle(linestyle, scale) fill = 'none' rad = vobj.BubbleSize.Value/2 n = 0 for e in obj.Shape.Edges: lstyle = lorig svg += getPath([e]) lstyle = "none" pos = ["Start"] if hasattr(vobj,"BubblePosition"): if vobj.BubblePosition == "Both": pos = ["Start","End"] else: pos = [vobj.BubblePosition] for p in pos: if p == "Start": p1 = e.Vertexes[0].Point p2 = e.Vertexes[1].Point else: p1 = e.Vertexes[1].Point p2 = e.Vertexes[0].Point dv = p2.sub(p1) dv.normalize() center = p2.add(dv.scale(rad,rad,rad)) svg += getCircle(Part.makeCircle(rad,center)) if hasattr(vobj.Proxy,"bubbletexts"): if len (vobj.Proxy.bubbletexts) >= n: svg += '\n' svg += '\n' n += 1 elif getType(obj) == "Pipe": fill = stroke lstyle = getLineStyle(linestyle, scale) if obj.Base and obj.Diameter: svg += getPath(obj.Base.Shape.Edges) for f in obj.Shape.Faces: if len(f.Edges) == 1: if isinstance(f.Edges[0].Curve,Part.Circle): svg += getCircle(f.Edges[0]) elif getType(obj) == "Rebar": fill = "none" lstyle = getLineStyle(linestyle, scale) if obj.Proxy: if not hasattr(obj.Proxy,"wires"): obj.Proxy.execute(obj) if hasattr(obj.Proxy,"wires"): svg += getPath(wires=obj.Proxy.wires) elif getType(obj) == "PipeConnector": pass elif getType(obj) == "Space": "returns an SVG fragment for the text of a space" if gui: if not obj.ViewObject: print ("export of spaces to SVG is only available in GUI mode") else: c = getrgb(obj.ViewObject.TextColor) n = obj.ViewObject.FontName a = 0 if rotation != 0: a = math.radians(rotation) t1 = obj.ViewObject.Proxy.text1.string.getValues() t2 = obj.ViewObject.Proxy.text2.string.getValues() scale = obj.ViewObject.FirstLine.Value/obj.ViewObject.FontSize.Value f1 = fontsize*scale p2 = FreeCAD.Vector(obj.ViewObject.Proxy.coords.translation.getValue().getValue()) lspc = FreeCAD.Vector(obj.ViewObject.Proxy.header.translation.getValue().getValue()) p1 = p2.add(lspc) j = obj.ViewObject.TextAlign svg += getText(c,f1,n,a,getProj(p1, plane),t1,linespacing,j,flip=True) if t2: ofs = FreeCAD.Vector(0,lspc.Length,0) if a: ofs = FreeCAD.Rotation(FreeCAD.Vector(0,0,1),-rotation).multVec(ofs) svg += getText(c,fontsize,n,a,getProj(p1, plane).add(ofs),t2,linespacing,j,flip=True) elif obj.isDerivedFrom('Part::Feature'): if obj.Shape.isNull(): return '' # setting fill if obj.Shape.Faces: if gui: try: m = obj.ViewObject.DisplayMode except AttributeError: m = None if (m != "Wireframe"): if fillstyle == "shape color": fill = getrgb(obj.ViewObject.ShapeColor,testbw=False) fill_opacity = 1 - (obj.ViewObject.Transparency / 100.0) else: fill = 'url(#'+fillstyle+')' svg += getPattern(fillstyle) else: fill = "none" else: fill = "#888888" else: fill = 'none' lstyle = getLineStyle(linestyle, scale) if len(obj.Shape.Vertexes) > 1: wiredEdges = [] if obj.Shape.Faces: for i,f in enumerate(obj.Shape.Faces): svg += getPath(wires=f.Wires,pathname='%s_f%04d' % \ (obj.Name,i)) wiredEdges.extend(f.Edges) else: for i,w in enumerate(obj.Shape.Wires): svg += getPath(w.Edges,pathname='%s_w%04d' % \ (obj.Name,i)) wiredEdges.extend(w.Edges) if len(wiredEdges) != len(obj.Shape.Edges): for i,e in enumerate(obj.Shape.Edges): if (DraftGeomUtils.findEdge(e,wiredEdges) == None): svg += getPath([e],pathname='%s_nwe%04d' % \ (obj.Name,i)) else: # closed circle or spline if obj.Shape.Edges: if isinstance(obj.Shape.Edges[0].Curve,Part.Circle): svg = getCircle(obj.Shape.Edges[0]) else: svg = getPath(obj.Shape.Edges) if FreeCAD.GuiUp: if hasattr(obj.ViewObject,"EndArrow") and hasattr(obj.ViewObject,"ArrowType") and (len(obj.Shape.Vertexes) > 1): if obj.ViewObject.EndArrow: p1 = getProj(obj.Shape.Vertexes[-2].Point, plane) p2 = getProj(obj.Shape.Vertexes[-1].Point, plane) angle = -DraftVecUtils.angle(p2.sub(p1)) arrowsize = obj.ViewObject.ArrowSize.Value/pointratio svg += getArrow(obj.ViewObject.ArrowType,p2,arrowsize,stroke,linewidth,angle) # techdraw expects bottom-to-top coordinates if techdraw: svg = ''+svg+'' return svg