
The Draft and Arch modules now load heavy modules such as Part or Sketch only when they use them, not anymore at Init time.
848 lines
32 KiB
Python
848 lines
32 KiB
Python
|
|
#***************************************************************************
|
|
#* *
|
|
#* Copyright (c) 2009 Yorik van Havre <yorik@gmx.fr> *
|
|
#* *
|
|
#* This program is free software; you can redistribute it and/or modify *
|
|
#* it under the terms of the GNU General Public License (GPL) *
|
|
#* as published by the Free Software Foundation; either version 2 of *
|
|
#* the License, or (at your option) any later version. *
|
|
#* for detail see the LICENCE text file. *
|
|
#* *
|
|
#* This program is distributed in the hope that it will be useful, *
|
|
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
#* GNU Library General Public License for more details. *
|
|
#* *
|
|
#* You should have received a copy of the GNU Library General Public *
|
|
#* License along with this program; if not, write to the Free Software *
|
|
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
|
#* USA *
|
|
#* *
|
|
#***************************************************************************
|
|
|
|
__title__="FreeCAD Draft Workbench - SVG importer/exporter"
|
|
__author__ = "Yorik van Havre <yorik@gmx.fr>"
|
|
__url__ = ["http://yorik.orgfree.com","http://free-cad.sourceforge.net"]
|
|
|
|
'''
|
|
This script imports SVG files in FreeCAD. Currently only reads the following entities:
|
|
paths, lines, arcs and rects.
|
|
Bezier curves are skipped.
|
|
'''
|
|
|
|
import xml.sax, string, FreeCAD, os, math, re, Draft
|
|
from draftlibs import fcvec
|
|
from FreeCAD import Vector
|
|
|
|
try: import FreeCADGui
|
|
except: gui = False
|
|
else: gui = True
|
|
try: draftui = FreeCADGui.draftToolBar
|
|
except: draftui = None
|
|
|
|
pythonopen = open
|
|
|
|
svgcolors = {
|
|
'Pink': [255, 192, 203],
|
|
'Blue': [0, 0, 255],
|
|
'Honeydew': [240, 255, 240],
|
|
'Purple': [128, 0, 128],
|
|
'Fuchsia': [255, 0, 255],
|
|
'LawnGreen': [124, 252, 0],
|
|
'Amethyst': [153, 102, 204],
|
|
'Crimson': [220, 20, 60],
|
|
'White': [255, 255, 255],
|
|
'NavajoWhite': [255, 222, 173],
|
|
'Cornsilk': [255, 248, 220],
|
|
'Bisque': [255, 228, 196],
|
|
'PaleGreen': [152, 251, 152],
|
|
'Brown': [165, 42, 42],
|
|
'DarkTurquoise': [0, 206, 209],
|
|
'DarkGreen': [0, 100, 0],
|
|
'MediumOrchid': [186, 85, 211],
|
|
'Chocolate': [210, 105, 30],
|
|
'PapayaWhip': [255, 239, 213],
|
|
'Olive': [128, 128, 0],
|
|
'Silver': [192, 192, 192],
|
|
'PeachPuff': [255, 218, 185],
|
|
'Plum': [221, 160, 221],
|
|
'DarkGoldenrod': [184, 134, 11],
|
|
'SlateGrey': [112, 128, 144],
|
|
'MintCream': [245, 255, 250],
|
|
'CornflowerBlue': [100, 149, 237],
|
|
'Gold': [255, 215, 0],
|
|
'HotPink': [255, 105, 180],
|
|
'DarkBlue': [0, 0, 139],
|
|
'LimeGreen': [50, 205, 50],
|
|
'DeepSkyBlue': [0, 191, 255],
|
|
'DarkKhaki': [189, 183, 107],
|
|
'LightGrey': [211, 211, 211],
|
|
'Yellow': [255, 255, 0],
|
|
'Gainsboro': [220, 220, 220],
|
|
'MistyRose': [255, 228, 225],
|
|
'SandyBrown': [244, 164, 96],
|
|
'DeepPink': [255, 20, 147],
|
|
'Magenta': [255, 0, 255],
|
|
'AliceBlue': [240, 248, 255],
|
|
'DarkCyan': [0, 139, 139],
|
|
'DarkSlateGrey': [47, 79, 79],
|
|
'GreenYellow': [173, 255, 47],
|
|
'DarkOrchid': [153, 50, 204],
|
|
'OliveDrab': [107, 142, 35],
|
|
'Chartreuse': [127, 255, 0],
|
|
'Peru': [205, 133, 63],
|
|
'Orange': [255, 165, 0],
|
|
'Red': [255, 0, 0],
|
|
'Wheat': [245, 222, 179],
|
|
'LightCyan': [224, 255, 255],
|
|
'LightSeaGreen': [32, 178, 170],
|
|
'BlueViolet': [138, 43, 226],
|
|
'LightSlateGrey': [119, 136, 153],
|
|
'Cyan': [0, 255, 255],
|
|
'MediumPurple': [147, 112, 219],
|
|
'MidnightBlue': [25, 25, 112],
|
|
'FireBrick': [178, 34, 34],
|
|
'PaleTurquoise': [175, 238, 238],
|
|
'PaleGoldenrod': [238, 232, 170],
|
|
'Gray': [128, 128, 128],
|
|
'MediumSeaGreen': [60, 179, 113],
|
|
'Moccasin': [255, 228, 181],
|
|
'Ivory': [255, 255, 240],
|
|
'DarkSlateBlue': [72, 61, 139],
|
|
'Beige': [245, 245, 220],
|
|
'Green': [0, 128, 0],
|
|
'SlateBlue': [106, 90, 205],
|
|
'Teal': [0, 128, 128],
|
|
'Azure': [240, 255, 255],
|
|
'LightSteelBlue': [176, 196, 222],
|
|
'DimGrey': [105, 105, 105],
|
|
'Tan': [210, 180, 140],
|
|
'AntiqueWhite': [250, 235, 215],
|
|
'SkyBlue': [135, 206, 235],
|
|
'GhostWhite': [248, 248, 255],
|
|
'MediumTurquoise': [72, 209, 204],
|
|
'FloralWhite': [255, 250, 240],
|
|
'LavenderBlush': [255, 240, 245],
|
|
'SeaGreen': [46, 139, 87],
|
|
'Lavender': [230, 230, 250],
|
|
'BlanchedAlmond': [255, 235, 205],
|
|
'DarkOliveGreen': [85, 107, 47],
|
|
'DarkSeaGreen': [143, 188, 143],
|
|
'SpringGreen': [0, 255, 127],
|
|
'Navy': [0, 0, 128],
|
|
'Orchid': [218, 112, 214],
|
|
'SaddleBrown': [139, 69, 19],
|
|
'IndianRed': [205, 92, 92],
|
|
'Snow': [255, 250, 250],
|
|
'SteelBlue': [70, 130, 180],
|
|
'MediumSlateBlue': [123, 104, 238],
|
|
'Black': [0, 0, 0],
|
|
'LightBlue': [173, 216, 230],
|
|
'Turquoise': [64, 224, 208],
|
|
'MediumVioletRed': [199, 21, 133],
|
|
'DarkViolet': [148, 0, 211],
|
|
'DarkGray': [169, 169, 169],
|
|
'Salmon': [250, 128, 114],
|
|
'DarkMagenta': [139, 0, 139],
|
|
'Tomato': [255, 99, 71],
|
|
'WhiteSmoke': [245, 245, 245],
|
|
'Goldenrod': [218, 165, 32],
|
|
'MediumSpringGreen': [0, 250, 154],
|
|
'DodgerBlue': [30, 144, 255],
|
|
'Aqua': [0, 255, 255],
|
|
'ForestGreen': [34, 139, 34],
|
|
'LemonChiffon': [255, 250, 205],
|
|
'LightSlateGray': [119, 136, 153],
|
|
'SlateGray': [112, 128, 144],
|
|
'LightGray': [211, 211, 211],
|
|
'Indigo': [75, 0, 130],
|
|
'CadetBlue': [95, 158, 160],
|
|
'LightYellow': [255, 255, 224],
|
|
'DarkOrange': [255, 140, 0],
|
|
'PowderBlue': [176, 224, 230],
|
|
'RoyalBlue': [65, 105, 225],
|
|
'Sienna': [160, 82, 45],
|
|
'Thistle': [216, 191, 216],
|
|
'Lime': [0, 255, 0],
|
|
'Seashell': [255, 245, 238],
|
|
'DarkRed': [139, 0, 0],
|
|
'LightSkyBlue': [135, 206, 250],
|
|
'YellowGreen': [154, 205, 50],
|
|
'Aquamarine': [127, 255, 212],
|
|
'LightCoral': [240, 128, 128],
|
|
'DarkSlateGray': [47, 79, 79],
|
|
'Khaki': [240, 230, 140],
|
|
'DarkGrey': [169, 169, 169],
|
|
'BurlyWood': [222, 184, 135],
|
|
'LightGoldenrodYellow': [250, 250, 210],
|
|
'MediumBlue': [0, 0, 205],
|
|
'DarkSalmon': [233, 150, 122],
|
|
'RosyBrown': [188, 143, 143],
|
|
'LightSalmon': [255, 160, 122],
|
|
'PaleVioletRed': [219, 112, 147],
|
|
'Coral': [255, 127, 80],
|
|
'Violet': [238, 130, 238],
|
|
'Grey': [128, 128, 128],
|
|
'LightGreen': [144, 238, 144],
|
|
'Linen': [250, 240, 230],
|
|
'OrangeRed': [255, 69, 0],
|
|
'DimGray': [105, 105, 105],
|
|
'Maroon': [128, 0, 0],
|
|
'LightPink': [255, 182, 193],
|
|
'MediumAquamarine': [102, 205, 170],
|
|
'OldLace': [253, 245, 230]
|
|
}
|
|
|
|
def getcolor(color):
|
|
"checks if the given string is a RGB value, or if it is a named color. returns 1-based RGBA tuple."
|
|
if (color[:1] == "#"):
|
|
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)
|
|
return (r,g,b,0.0)
|
|
else:
|
|
for k,v in svgcolors.iteritems():
|
|
if (k.lower() == color.lower()):
|
|
r = float(v[0]/255.0)
|
|
g = float(v[1]/255.0)
|
|
b = float(v[2]/255.0)
|
|
return (r,g,b,0.0)
|
|
|
|
def getsize(width):
|
|
"extracts a number from the given string (removes suffixes)"
|
|
if width[-1] == "%":
|
|
return float(width[:-1])
|
|
elif len(width) > 1:
|
|
for s in ['pt','pc','mm','cm','in','px']:
|
|
if width[-2:] == s:
|
|
return float(width[:-2])
|
|
try:
|
|
s = float(width)
|
|
return s
|
|
except ValueError:
|
|
return width
|
|
|
|
def getrgb(color):
|
|
"returns a rgb value #000000 from a freecad color"
|
|
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"
|
|
|
|
def __init__(self):
|
|
"retrieving Draft parameters"
|
|
params = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft")
|
|
self.style = params.GetInt("svgstyle")
|
|
self.count = 0
|
|
self.transform = None
|
|
self.grouptransform = []
|
|
self.lastdim = None
|
|
|
|
import Part
|
|
|
|
if gui and draftui:
|
|
r = float(draftui.color.red()/255.0)
|
|
g = float(draftui.color.green()/255.0)
|
|
b = float(draftui.color.blue()/255.0)
|
|
self.lw = float(draftui.widthButton.value())
|
|
else:
|
|
self.lw = float(params.GetInt("linewidth"))
|
|
c = params.GetUnsigned("color")
|
|
r = float(((c>>24)&0xFF)/255)
|
|
g = float(((c>>16)&0xFF)/255)
|
|
b = float(((c>>8)&0xFF)/255)
|
|
self.col = (r,g,b,0.0)
|
|
|
|
def format(self,obj):
|
|
"applies styles to passed object"
|
|
if self.style and 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
|
|
|
|
def startElement(self, name, attrs):
|
|
|
|
# reorganizing data into a nice clean dictionary
|
|
|
|
self.count += 1
|
|
|
|
print "processing element ",self.count,": ",name
|
|
print "existing group transform: ", self.grouptransform
|
|
|
|
data = {}
|
|
for (keyword,content) in attrs.items():
|
|
content = content.replace(',',' ')
|
|
content = content.split()
|
|
data[keyword]=content
|
|
|
|
if 'style' in data:
|
|
content = data['style'][0].replace(' ','')
|
|
content = content.split(';')
|
|
for i in content:
|
|
pair = i.split(':')
|
|
if len(pair)>1: data[pair[0]]=pair[1]
|
|
|
|
for k in ['x','y','x1','y1','x2','y2','width','height']:
|
|
if k in data:
|
|
data[k] = getsize(data[k][0])
|
|
|
|
for k in ['fill','stroke','stroke-width','font-size']:
|
|
if k in data:
|
|
if isinstance(data[k],list):
|
|
data[k]=data[k][0]
|
|
|
|
# extracting style info
|
|
|
|
self.fill = None
|
|
self.color = None
|
|
self.width = None
|
|
self.text = None
|
|
|
|
if 'fill' in data:
|
|
if data['fill'][0] != 'none':
|
|
self.fill = getcolor(data['fill'])
|
|
if 'stroke' in data:
|
|
if data['stroke'][0] != 'none':
|
|
self.color = getcolor(data['stroke'])
|
|
if 'stroke-width' in data:
|
|
if data['stroke-width'] != 'none':
|
|
self.width = getsize(data['stroke-width'])
|
|
if 'transform' in data:
|
|
m = self.getMatrix(data['transform'])
|
|
if name == "g":
|
|
self.grouptransform.append(m)
|
|
else:
|
|
self.transform = m
|
|
else:
|
|
if name == "g":
|
|
self.grouptransform.append(FreeCAD.Matrix())
|
|
|
|
'''
|
|
print "existing grouptransform: ",self.grouptransform
|
|
print "existing transform: ",self.transform
|
|
if "translate" in tr:
|
|
i0 = tr.index("translate")
|
|
print "getting translate ",tr
|
|
if "translate" in self.transform:
|
|
self.transform['translate'] = self.transform['translate'].add(Vector(float(tr[i0+1]),-float(tr[i0+2]),0))
|
|
else:
|
|
self.transform['translate'] = Vector(float(tr[i0+1]),-float(tr[i0+2]),0)
|
|
if "translate" in self.grouptransform:
|
|
print "adding to group ",self.grouptransform['translate']
|
|
self.transform['translate'] = self.grouptransform['translate'].add(self.transform['translate'])
|
|
else:
|
|
if "translate" in self.grouptransform:
|
|
print "adding to group ",self.grouptransform['translate']
|
|
self.transform['translate'] = self.grouptransform['translate']
|
|
if "scale" in tr:
|
|
i0 = tr.index("scale")
|
|
if "scale" in self.transform:
|
|
self.transform['scale'] = self.transform['scale'].add(Vector(float(tr[i0+1]),float(tr[i0+2]),0))
|
|
else:
|
|
print tr
|
|
self.transform['scale'] = Vector(float(tr[i0+1]),float(tr[i0+2]),0)
|
|
if "scale" in self.grouptransform:
|
|
self.transform['scale'] = self.transform['scale'].add(self.grouptransform['scale'])
|
|
else:
|
|
if "scale" in self.grouptransform:
|
|
self.transform['scale'] = self.grouptransform['scale']
|
|
'''
|
|
|
|
if (self.style == 1):
|
|
self.color = self.col
|
|
self.width = self.lw
|
|
|
|
pathname = None
|
|
if 'id' in data:
|
|
pathname = data['id'][0]
|
|
print "name: ",pathname
|
|
|
|
# processing paths
|
|
|
|
if name == "path":
|
|
print data
|
|
|
|
if not pathname: pathname = 'Path'
|
|
|
|
path = []
|
|
point = []
|
|
lastvec = Vector(0,0,0)
|
|
command = None
|
|
relative = False
|
|
firstvec = None
|
|
|
|
pathdata = []
|
|
for d in data['d']:
|
|
if (len(d) == 1) and (d in ['m','M','l','L','h','H','v','V','a','A','c','C']):
|
|
pathdata.append(d)
|
|
else:
|
|
try:
|
|
f = float(d)
|
|
pathdata.append(f)
|
|
except ValueError:
|
|
if d[0].isdigit():
|
|
pathdata.append(d[:-1])
|
|
pathdata.append(d[-1])
|
|
else:
|
|
pathdata.append(d[0])
|
|
pathdata.append(d[1:])
|
|
|
|
# print "debug: pathdata:",pathdata
|
|
|
|
if "freecad:basepoint1" in data:
|
|
p1 = data["freecad:basepoint1"]
|
|
p1 = Vector(float(p1[0]),-float(p1[1]),0)
|
|
p2 = data["freecad:basepoint2"]
|
|
p2 = Vector(float(p2[0]),-float(p2[1]),0)
|
|
p3 = data["freecad:dimpoint"]
|
|
p3 = Vector(float(p3[0]),-float(p3[1]),0)
|
|
obj = Draft.makeDimension(p1,p2,p3)
|
|
self.applyTrans(obj)
|
|
self.format(obj)
|
|
pathdata = []
|
|
self.lastdim = obj
|
|
|
|
for d in pathdata:
|
|
if (d == "M"):
|
|
command = "move"
|
|
relative = False
|
|
point = []
|
|
elif (d == "m"):
|
|
command = "move"
|
|
relative = True
|
|
point = []
|
|
elif (d == "L"):
|
|
command = "line"
|
|
relative = False
|
|
point = []
|
|
elif (d == "l"):
|
|
command = "line"
|
|
relative = True
|
|
point = []
|
|
elif (d == "H"):
|
|
command = "horizontal"
|
|
relative = False
|
|
point = []
|
|
elif (d == "h"):
|
|
command = "horizontal"
|
|
relative = True
|
|
point = []
|
|
elif (d == "V"):
|
|
command = "vertical"
|
|
relative = False
|
|
point = []
|
|
elif (d == "v"):
|
|
command = "vertical"
|
|
relative = True
|
|
point = []
|
|
elif (d == "A"):
|
|
command = "arc"
|
|
relative = False
|
|
point = []
|
|
elif (d == "a"):
|
|
command = "arc"
|
|
relative = True
|
|
point = []
|
|
elif (d == "Z") or (d == "z"):
|
|
command = "close"
|
|
point = []
|
|
elif (d == "C"):
|
|
command = "curve"
|
|
relative = False
|
|
point = []
|
|
elif (d == "c"):
|
|
command = "curve"
|
|
relative = True
|
|
point = []
|
|
else:
|
|
try:
|
|
point.append(float(d))
|
|
except ValueError:
|
|
pass
|
|
|
|
print "command: ",command, ' point: ',point
|
|
|
|
if (len(point)==2) and (command=="move"):
|
|
if path:
|
|
sh = Part.Wire(path)
|
|
if self.fill: sh = Part.Face(sh)
|
|
sh = self.applyTrans(sh)
|
|
obj = self.doc.addObject("Part::Feature",pathname)
|
|
obj.Shape = sh
|
|
self.format(obj)
|
|
path = []
|
|
if relative:
|
|
lastvec = lastvec.add(Vector(point[0],-point[1],0))
|
|
command="line"
|
|
else:
|
|
lastvec = Vector(point[0],-point[1],0)
|
|
firstvec = lastvec
|
|
print "move ",lastvec
|
|
command = "line"
|
|
point = []
|
|
elif (len(point)==2) and (command=="line"):
|
|
if relative:
|
|
currentvec = lastvec.add(Vector(point[0],-point[1],0))
|
|
else:
|
|
currentvec = Vector(point[0],-point[1],0)
|
|
if not fcvec.equals(lastvec,currentvec):
|
|
seg = Part.Line(lastvec,currentvec).toShape()
|
|
print "line ",lastvec,currentvec
|
|
lastvec = currentvec
|
|
path.append(seg)
|
|
point = []
|
|
elif (len(point)==1) and (command=="horizontal"):
|
|
if relative:
|
|
currentvec = lastvec.add(Vector(point[0],0,0))
|
|
else:
|
|
lasty = path[-1].y
|
|
currentvec = Vector(point[0],lasty,0)
|
|
seg = Part.Line(lastvec,currentvec).toShape()
|
|
lastvec = currentvec
|
|
path.append(seg)
|
|
point = []
|
|
elif (len(point)==1) and (command=="vertical"):
|
|
if relative:
|
|
currentvec = lastvec.add(Vector(0,-point[0],0))
|
|
else:
|
|
lastx = path[-1].x
|
|
currentvec = Vector(lastx,-point[0],0)
|
|
seg = Part.Line(lastvec,currentvec).toShape()
|
|
lastvec = currentvec
|
|
path.append(seg)
|
|
point = []
|
|
elif (len(point)==7) and (command=="arc"):
|
|
if relative:
|
|
currentvec = lastvec.add(Vector(point[-2],-point[-1],0))
|
|
else:
|
|
currentvec = Vector(point[-2],-point[-1],0)
|
|
chord = currentvec.sub(lastvec)
|
|
# perp = chord.cross(Vector(0,0,-1))
|
|
# here is a better way to find the perpendicular
|
|
if point[4] == 1:
|
|
# clockwise
|
|
perp = fcvec.rotate2D(chord,-math.pi/2)
|
|
else:
|
|
# anticlockwise
|
|
perp = fcvec.rotate2D(chord,math.pi/2)
|
|
chord = fcvec.scale(chord,.5)
|
|
if chord.Length > point[0]: a = 0
|
|
else: a = math.sqrt(point[0]**2-chord.Length**2)
|
|
s = point[0] - a
|
|
perp = fcvec.scale(perp,s/perp.Length)
|
|
midpoint = lastvec.add(chord.add(perp))
|
|
seg = Part.Arc(lastvec,midpoint,currentvec).toShape()
|
|
lastvec = currentvec
|
|
path.append(seg)
|
|
point = []
|
|
elif (command == "close"):
|
|
if not fcvec.equals(lastvec,firstvec):
|
|
seg = Part.Line(lastvec,firstvec).toShape()
|
|
path.append(seg)
|
|
if path:
|
|
sh = Part.Wire(path)
|
|
if self.fill: sh = Part.Face(sh)
|
|
sh = self.applyTrans(sh)
|
|
obj = self.doc.addObject("Part::Feature",pathname)
|
|
obj.Shape = sh
|
|
self.format(obj)
|
|
path = []
|
|
point = []
|
|
command = None
|
|
elif (len(point)==6) and (command=="curve"):
|
|
if relative:
|
|
currentvec = lastvec.add(Vector(point[4],-point[5],0))
|
|
pole1 = lastvec.add(Vector(point[0],-point[1],0))
|
|
pole2 = lastvec.add(Vector(point[2],-point[3],0))
|
|
else:
|
|
currentvec = Vector(point[4],-point[5],0)
|
|
pole1 = Vector(point[0],point[1],0)
|
|
pole2 = Vector(point[2],point[3],0)
|
|
if not fcvec.equals(currentvec,lastvec):
|
|
mainv = currentvec.sub(lastvec)
|
|
pole1v = lastvec.add(pole1)
|
|
pole2v = currentvec.add(pole2)
|
|
print "curve data:",mainv.normalize(),pole1v.normalize(),pole2v.normalize()
|
|
if (round(mainv.getAngle(pole1v),4) in [0,round(math.pi,4)]) \
|
|
and (round(mainv.getAngle(pole2v),4) in [0,round(math.pi,4)]):
|
|
print "straight segment"
|
|
seg = Part.Line(lastvec,currentvec).toShape()
|
|
else:
|
|
print "bezier segment"
|
|
b = Part.BezierCurve()
|
|
b.setPoles([lastvec,pole1,pole2,currentvec])
|
|
seg = b.toShape()
|
|
print "connect ",lastvec,currentvec
|
|
lastvec = currentvec
|
|
path.append(seg)
|
|
point = []
|
|
|
|
if path:
|
|
sh = Part.Wire(path)
|
|
if self.fill: sh = Part.Face(sh)
|
|
sh = self.applyTrans(sh)
|
|
obj = self.doc.addObject("Part::Feature",pathname)
|
|
obj.Shape = sh
|
|
self.format(obj)
|
|
|
|
# processing rects
|
|
|
|
if name == "rect":
|
|
if not pathname: pathname = 'Rectangle'
|
|
p1 = Vector(data['x'],-data['y'],0)
|
|
p2 = Vector(data['x']+data['width'],-data['y'],0)
|
|
p3 = Vector(data['x']+data['width'],-data['y']-data['height'],0)
|
|
p4 = Vector(data['x'],-data['y']-data['height'],0)
|
|
edges = []
|
|
edges.append(Part.Line(p1,p2).toShape())
|
|
edges.append(Part.Line(p2,p3).toShape())
|
|
edges.append(Part.Line(p3,p4).toShape())
|
|
edges.append(Part.Line(p4,p1).toShape())
|
|
sh = Part.Wire(edges)
|
|
if self.fill: sh = Part.Face(sh)
|
|
sh = self.applyTrans(sh)
|
|
obj = self.doc.addObject("Part::Feature",pathname)
|
|
obj.Shape = sh
|
|
self.format(obj)
|
|
|
|
# processing lines
|
|
|
|
if name == "line":
|
|
if not pathname: pathname = 'Line'
|
|
p1 = Vector(float(data['x1'][0]),-float(data['y1'][0]),0)
|
|
p2 = Vector(float(data['x2'][0]),-float(data['y2'][0]),0)
|
|
sh = Part.Line(p1,p2).toShape()
|
|
sh = self.applyTrans(sh)
|
|
obj = self.doc.addObject("Part::Feature",pathname)
|
|
obj.Shape = sh
|
|
self.format(obj)
|
|
|
|
# processing circles
|
|
|
|
if (name == "circle") and (not ("freecad:skip" in data)) :
|
|
if not pathname: pathname = 'Circle'
|
|
c = Vector(float(data['cx'][0]),-float(data['cy'][0]),0)
|
|
r = float(data['r'][0])
|
|
sh = Part.makeCircle(r)
|
|
if self.fill:
|
|
sh = Part.Wire([sh])
|
|
sh = Part.Face(sh)
|
|
sh.translate(c)
|
|
sh = self.applyTrans(sh)
|
|
obj = self.doc.addObject("Part::Feature",pathname)
|
|
obj.Shape = sh
|
|
self.format(obj)
|
|
|
|
# processing texts
|
|
|
|
if name in ["text","tspan"]:
|
|
if not("freecad:skip" in data):
|
|
print "processing a text"
|
|
if 'x' in data:
|
|
self.x = data['x']
|
|
else:
|
|
self.x = 0
|
|
if 'y' in data:
|
|
self.y = data['y']
|
|
else:
|
|
self.y = 0
|
|
if 'font-size' in data:
|
|
if data['font-size'] != 'none':
|
|
self.text = getsize(data['font-size'])
|
|
else:
|
|
self.text = 1
|
|
else:
|
|
if self.lastdim:
|
|
self.lastdim.ViewObject.FontSize = int(getsize(data['font-size']))
|
|
|
|
print "done processing element ",self.count
|
|
|
|
def characters(self,content):
|
|
if self.text:
|
|
print "reading characters", str(content)
|
|
obj=self.doc.addObject("App::Annotation",'Text')
|
|
obj.LabelText = content.encode('latin1')
|
|
vec = Vector(self.x,-self.y,0)
|
|
if self.transform:
|
|
vec = self.translateVec(vec,self.transform)
|
|
print "own transform: ",self.transform, vec
|
|
for i in range(len(self.grouptransform)):
|
|
#vec = self.translateVec(vec,self.grouptransform[-i-1])
|
|
vec = self.grouptransform[-i-1].multiply(vec)
|
|
print "applying vector: ",vec
|
|
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)
|
|
|
|
def endElement(self, name):
|
|
if not name in ["tspan"]:
|
|
self.transform = None
|
|
self.text = None
|
|
if name == "g":
|
|
print "closing group"
|
|
self.grouptransform.pop()
|
|
|
|
def applyTrans(self,sh):
|
|
if isinstance(sh,Part.Shape):
|
|
if self.transform:
|
|
print "applying object transform: ",self.transform
|
|
sh = sh.transformGeometry(self.transform)
|
|
for i in range(len(self.grouptransform)):
|
|
print "applying group transform: ",self.grouptransform[-i-1]
|
|
sh = sh.transformGeometry(self.grouptransform[-i-1])
|
|
return sh
|
|
elif Draft.getType(sh) == "Dimension":
|
|
pts = []
|
|
for p in [sh.Start,sh.End,sh.Dimline]:
|
|
cp = Vector(p)
|
|
if self.transform:
|
|
print "applying object transform: ",self.transform
|
|
cp = self.transform.multiply(cp)
|
|
for i in range(len(self.grouptransform)):
|
|
print "applying group transform: ",self.grouptransform[-i-1]
|
|
cp = self.grouptransform[-i-1].multiply(cp)
|
|
pts.append(cp)
|
|
sh.Start = pts[0]
|
|
sh.End = pts[1]
|
|
sh.Dimline = pts[2]
|
|
|
|
def translateVec(self,vec,mat):
|
|
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"
|
|
s = ""
|
|
for l in tr:
|
|
s += l
|
|
s += " "
|
|
s=s.replace("("," ")
|
|
s=s.replace(")"," ")
|
|
s = s.strip()
|
|
tr = s.split()
|
|
m = FreeCAD.Matrix()
|
|
for i in range(len(tr)):
|
|
if tr[i] == 'translate':
|
|
vec = Vector(float(tr[i+1]),-float(tr[i+2]),0)
|
|
m.move(vec)
|
|
elif tr[i] == 'scale':
|
|
vec = Vector(float(tr[i+1]),float(tr[i+2]),0)
|
|
m.scale(vec)
|
|
#elif tr[i] == 'rotate':
|
|
# m.rotateZ(float(tr[i+1]))
|
|
print "generating transformation: ",m
|
|
return m
|
|
|
|
def decodeName(name):
|
|
"decodes encoded strings"
|
|
try:
|
|
decodedName = (name.decode("utf8"))
|
|
except UnicodeDecodeError:
|
|
try:
|
|
decodedName = (name.decode("latin1"))
|
|
except UnicodeDecodeError:
|
|
print "dxf: error: couldn't determine character encoding"
|
|
decodedName = name
|
|
return decodedName
|
|
|
|
def getContents(filename,tag,stringmode=False):
|
|
"gets the contents of all the occurences of the given tag in the given file"
|
|
result = {}
|
|
if stringmode:
|
|
contents = filename
|
|
else:
|
|
f = pythonopen(filename)
|
|
contents = ''
|
|
for line in f: contents += line
|
|
f.close()
|
|
contents = contents.replace('\n','_linebreak')
|
|
searchpat = '<'+tag+'.*?</'+tag+'>'
|
|
tags = re.findall(searchpat,contents)
|
|
for t in tags:
|
|
tagid = re.findall('id="(.*?)"',t)
|
|
if tagid:
|
|
tagid = tagid[0]
|
|
else:
|
|
tagid = 'none'
|
|
res = t.replace('_linebreak','\n')
|
|
result[tagid] = res
|
|
return result
|
|
|
|
def open(filename):
|
|
docname=os.path.split(filename)[1]
|
|
doc=FreeCAD.newDocument(docname)
|
|
doc.Label = decodeName(docname[:-4])
|
|
parser = xml.sax.make_parser()
|
|
parser.setContentHandler(svgHandler())
|
|
parser._cont_handler.doc = doc
|
|
f = pythonopen(filename)
|
|
parser.parse(f)
|
|
f.close()
|
|
doc.recompute()
|
|
return doc
|
|
|
|
def insert(filename,docname):
|
|
try:
|
|
doc=FreeCAD.getDocument(docname)
|
|
except:
|
|
doc=FreeCAD.newDocument(docname)
|
|
parser = xml.sax.make_parser()
|
|
parser.setContentHandler(svgHandler())
|
|
parser._cont_handler.doc = doc
|
|
parser.parse(pythonopen(filename))
|
|
doc.recompute()
|
|
|
|
def export(exportList,filename):
|
|
"called when freecad exports a file"
|
|
|
|
# finding sheet size
|
|
minx = 10000
|
|
miny = 10000
|
|
maxx = 0
|
|
maxy = 0
|
|
for ob in exportList:
|
|
if ob.isDerivedFrom("Part::Feature"):
|
|
for v in ob.Shape.Vertexes:
|
|
if v.Point.x < minx: minx = v.Point.x
|
|
if v.Point.x > maxx: maxx = v.Point.x
|
|
if v.Point.y < miny: miny = v.Point.y
|
|
if v.Point.y > maxy: maxy = v.Point.y
|
|
margin = (maxx-minx)*.01
|
|
minx -= margin
|
|
maxx += margin
|
|
miny -= margin
|
|
maxy += margin
|
|
sizex = maxx-minx
|
|
sizey = maxy-miny
|
|
miny += margin
|
|
boty = sizey+miny
|
|
|
|
# writing header
|
|
svg = pythonopen(filename,'wb')
|
|
svg.write('<?xml version="1.0"?>\n')
|
|
svg.write('<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"')
|
|
svg.write(' "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n')
|
|
svg.write('<svg')
|
|
svg.write(' width="' + str(sizex) + '" height="' + str(sizey) + '"')
|
|
svg.write(' viewBox="0 0 ' + str(sizex) + ' ' + str(sizey) + '"')
|
|
svg.write(' xmlns="http://www.w3.org/2000/svg" version="1.1"')
|
|
svg.write('>\n')
|
|
|
|
# writing paths
|
|
for ob in exportList:
|
|
svg.write('<g transform="translate('+str(-minx)+','+str(-miny+(2*margin))+') scale(1,-1)">\n')
|
|
svg.write(Draft.getSVG(ob))
|
|
svg.write('</g>\n')
|
|
|
|
# closing
|
|
svg.write('</svg>')
|
|
svg.close()
|
|
FreeCAD.Console.PrintMessage("successfully exported "+filename)
|