chiark / gitweb /
e57b35333ace4ea8664f921c016124ed2bca4bff
[cura.git] / Cura / util / drawingLoader / svg.py
1 from __future__ import absolute_import
2 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
3
4 import math
5 import re
6 import sys
7 import numpy
8 from xml.etree import ElementTree
9
10 from Cura.util.drawingLoader import drawing
11
12 def applyTransformString(matrix, transform):
13         while transform != '':
14                 if transform[0] == ',':
15                         transform = transform[1:].strip()
16                 s = transform.find('(')
17                 e = transform.find(')')
18                 if s < 0 or e < 0:
19                         print 'Unknown transform: %s' % (transform)
20                         return matrix
21                 tag = transform[:s]
22                 data = map(float, re.split('[ \t,]+', transform[s+1:e].strip()))
23                 if tag == 'matrix' and len(data) == 6:
24                         matrix = numpy.matrix([[data[0],data[1],0],[data[2],data[3],0],[data[4],data[5],1]], numpy.float64) * matrix
25                 elif tag == 'translate' and len(data) == 1:
26                         matrix = numpy.matrix([[1,0,data[0]],[0,1,0],[0,0,1]], numpy.float64) * matrix
27                 elif tag == 'translate' and len(data) == 2:
28                         matrix = numpy.matrix([[1,0,0],[0,1,0],[data[0],data[1],1]], numpy.float64) * matrix
29                 elif tag == 'scale' and len(data) == 1:
30                         matrix = numpy.matrix([[data[0],0,0],[0,data[0],0],[0,0,1]], numpy.float64) * matrix
31                 elif tag == 'scale' and len(data) == 2:
32                         matrix = numpy.matrix([[data[0],0,0],[0,data[1],0],[0,0,1]], numpy.float64) * matrix
33                 elif tag == 'rotate' and len(data) == 1:
34                         r = math.radians(data[0])
35                         matrix = numpy.matrix([[math.cos(r),math.sin(r),0],[-math.sin(r),math.cos(r),0],[0,0,1]], numpy.float64) * matrix
36                 elif tag == 'rotate' and len(data) == 3:
37                         matrix = numpy.matrix([[1,0,0],[0,1,0],[data[1],data[2],1]], numpy.float64) * matrix
38                         r = math.radians(data[0])
39                         matrix = numpy.matrix([[math.cos(r),math.sin(r),0],[-math.sin(r),math.cos(r),0],[0,0,1]], numpy.float64) * matrix
40                         matrix = numpy.matrix([[1,0,0],[0,1,0],[-data[1],-data[2],1]], numpy.float64) * matrix
41                 elif tag == 'skewX' and len(data) == 1:
42                         matrix = numpy.matrix([[1,0,0],[math.tan(data[0]),1,0],[0,0,1]], numpy.float64) * matrix
43                 elif tag == 'skewY' and len(data) == 1:
44                         matrix = numpy.matrix([[1,math.tan(data[0]),0],[0,1,0],[0,0,1]], numpy.float64) * matrix
45                 else:
46                         print 'Unknown transform: %s' % (transform)
47                         return matrix
48                 transform = transform[e+1:].strip()
49         return matrix
50
51 def toFloat(f):
52         f = re.search('^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?', f).group(0)
53         return float(f)
54
55 class SVG(object):
56         def __init__(self, filename):
57                 self.tagProcess = {}
58                 self.tagProcess['rect'] = self._processRectTag
59                 self.tagProcess['line'] = self._processLineTag
60                 self.tagProcess['polyline'] = self._processPolylineTag
61                 self.tagProcess['polygon'] = self._processPolygonTag
62                 self.tagProcess['elipse'] = self._processPolygonTag
63                 self.tagProcess['circle'] = self._processCircleTag
64                 self.tagProcess['ellipse'] = self._processEllipseTag
65                 self.tagProcess['path'] = self._processPathTag
66                 self.tagProcess['use'] = self._processUseTag
67                 self.tagProcess['g'] = self._processGTag
68                 self.tagProcess['a'] = self._processGTag
69                 self.tagProcess['svg'] = self._processGTag
70                 self.tagProcess['text'] = None #No text implementation yet
71                 self.tagProcess['image'] = None
72                 self.tagProcess['metadata'] = None
73                 self.tagProcess['defs'] = None
74                 self.tagProcess['style'] = None
75                 self.tagProcess['marker'] = None
76                 self.tagProcess['desc'] = None
77                 self.tagProcess['filter'] = None
78                 self.tagProcess['linearGradient'] = None
79                 self.tagProcess['radialGradient'] = None
80                 self.tagProcess['pattern'] = None
81                 self.tagProcess['title'] = None
82                 self.tagProcess['animate'] = None
83                 self.tagProcess['animateColor'] = None
84                 self.tagProcess['animateTransform'] = None
85                 self.tagProcess['set'] = None
86                 self.tagProcess['script'] = None
87
88                 #From Inkscape
89                 self.tagProcess['namedview'] = None
90                 #From w3c testsuite
91                 self.tagProcess['SVGTestCase'] = None
92
93                 self.paths = []
94                 f = open(filename, "r")
95                 self._xml = ElementTree.parse(f)
96                 self._recursiveCount = 0
97                 self._processGTag(self._xml.getroot(), numpy.matrix(numpy.identity(3, numpy.float64)))
98                 self._xml = None
99                 f.close()
100
101                 for path in self.paths:
102                         if not path.isClosed():
103                                 path.checkClosed()
104
105         def _processGTag(self, tag, baseMatrix):
106                 for e in tag:
107                         if e.get('transform') is None:
108                                 matrix = baseMatrix
109                         else:
110                                 matrix = applyTransformString(baseMatrix, e.get('transform'))
111                         tagName = e.tag[e.tag.find('}')+1:]
112                         if not tagName in self.tagProcess:
113                                 print 'unknown tag: %s' % (tagName)
114                         elif self.tagProcess[tagName] is not None:
115                                 self.tagProcess[tagName](e, matrix)
116
117         def _processUseTag(self, tag, baseMatrix):
118                 if self._recursiveCount > 16:
119                         return
120                 self._recursiveCount += 1
121                 id = tag.get('{http://www.w3.org/1999/xlink}href')
122                 if id[0] == '#':
123                         for e in self._xml.findall(".//*[@id='%s']" % (id[1:])):
124                                 if e.get('transform') is None:
125                                         matrix = baseMatrix
126                                 else:
127                                         matrix = applyTransformString(baseMatrix, e.get('transform'))
128                                 tagName = e.tag[e.tag.find('}')+1:]
129                                 if not tagName in self.tagProcess:
130                                         print 'unknown tag: %s' % (tagName)
131                                 elif self.tagProcess[tagName] is not None:
132                                         self.tagProcess[tagName](e, matrix)
133                 self._recursiveCount -= 1
134
135         def _processLineTag(self, tag, matrix):
136                 x1 = toFloat(tag.get('x1', '0'))
137                 y1 = toFloat(tag.get('y1', '0'))
138                 x2 = toFloat(tag.get('x2', '0'))
139                 y2 = toFloat(tag.get('y2', '0'))
140                 p = drawing.Path(x1, y1, matrix)
141                 p.addLineTo(x2, y2)
142                 self.paths.append(p)
143
144         def _processPolylineTag(self, tag, matrix):
145                 values = map(toFloat, re.split('[, \t]+', tag.get('points', '').strip()))
146                 p = drawing.Path(values[0], values[1], matrix)
147                 for n in xrange(2, len(values)-1, 2):
148                         p.addLineTo(values[n], values[n+1])
149                 self.paths.append(p)
150
151         def _processPolygonTag(self, tag, matrix):
152                 values = map(toFloat, re.split('[, \t]+', tag.get('points', '').strip()))
153                 p = drawing.Path(values[0], values[1], matrix)
154                 for n in xrange(2, len(values)-1, 2):
155                         p.addLineTo(values[n], values[n+1])
156                 p.closePath()
157                 self.paths.append(p)
158
159         def _processCircleTag(self, tag, matrix):
160                 cx = toFloat(tag.get('cx', '0'))
161                 cy = toFloat(tag.get('cy', '0'))
162                 r = toFloat(tag.get('r', '0'))
163                 p = drawing.Path(cx-r, cy, matrix)
164                 p.addArcTo(cx+r, cy, 0, r, r, False, False)
165                 p.addArcTo(cx-r, cy, 0, r, r, False, False)
166                 self.paths.append(p)
167
168         def _processEllipseTag(self, tag, matrix):
169                 cx = toFloat(tag.get('cx', '0'))
170                 cy = toFloat(tag.get('cy', '0'))
171                 rx = toFloat(tag.get('rx', '0'))
172                 ry = toFloat(tag.get('rx', '0'))
173                 p = drawing.Path(cx-rx, cy, matrix)
174                 p.addArcTo(cx+rx, cy, 0, rx, ry, False, False)
175                 p.addArcTo(cx-rx, cy, 0, rx, ry, False, False)
176                 self.paths.append(p)
177
178         def _processRectTag(self, tag, matrix):
179                 x = toFloat(tag.get('x', '0'))
180                 y = toFloat(tag.get('y', '0'))
181                 width = toFloat(tag.get('width', '0'))
182                 height = toFloat(tag.get('height', '0'))
183                 if width <= 0 or height <= 0:
184                         return
185                 rx = tag.get('rx')
186                 ry = tag.get('ry')
187                 if rx is not None or ry is not None:
188                         if ry is None:
189                                 ry = rx
190                         if rx is None:
191                                 rx = ry
192                         rx = float(rx)
193                         ry = float(ry)
194                         if rx > width / 2:
195                                 rx = width / 2
196                         if ry > height / 2:
197                                 ry = height / 2
198                 else:
199                         rx = 0.0
200                         ry = 0.0
201
202                 if rx > 0 and ry > 0:
203                         p = drawing.Path(x+width-rx, y, matrix)
204                         p.addArcTo(x+width,y+ry, 0, rx, ry, False, True)
205                         p.addLineTo(x+width, y+height-ry)
206                         p.addArcTo(x+width-rx,y+height, 0, rx, ry, False, True)
207                         p.addLineTo(x+rx, y+height)
208                         p.addArcTo(x,y+height-ry, 0, rx, ry, False, True)
209                         p.addLineTo(x, y+ry)
210                         p.addArcTo(x+rx,y, 0, rx, ry, False, True)
211                         p.closePath()
212                         self.paths.append(p)
213                 else:
214                         p = drawing.Path(x, y, matrix)
215                         p.addLineTo(x,y+height)
216                         p.addLineTo(x+width,y+height)
217                         p.addLineTo(x+width,y)
218                         p.closePath()
219                         self.paths.append(p)
220
221         def _processPathTag(self, tag, matrix):
222                 pathString = tag.get('d', '').replace(',', ' ')
223                 x = 0
224                 y = 0
225                 c2x = 0
226                 c2y = 0
227                 path = None
228                 for command in re.findall('[a-df-zA-DF-Z][^a-df-zA-DF-Z]*', pathString):
229                         params = re.split(' +', command[1:].strip())
230                         if len(params) > 0 and params[0] == '':
231                                 params = params[1:]
232                         if len(params) > 0 and params[-1] == '':
233                                 params = params[:-1]
234                         params = map(toFloat, params)
235                         command = command[0]
236
237                         if command == 'm':
238                                 x += params[0]
239                                 y += params[1]
240                                 path = drawing.Path(x, y, matrix)
241                                 self.paths.append(path)
242                                 params = params[2:]
243                                 while len(params) > 1:
244                                         x += params[0]
245                                         y += params[1]
246                                         params = params[2:]
247                                         path.addLineTo(x, y)
248                                 c2x, c2y = x, y
249                         elif command == 'M':
250                                 x = params[0]
251                                 y = params[1]
252                                 path = drawing.Path(x, y, matrix)
253                                 self.paths.append(path)
254                                 params = params[2:]
255                                 while len(params) > 1:
256                                         x = params[0]
257                                         y = params[1]
258                                         params = params[2:]
259                                         path.addLineTo(x, y)
260                                 c2x, c2y = x, y
261                         elif command == 'l':
262                                 while len(params) > 1:
263                                         x += params[0]
264                                         y += params[1]
265                                         params = params[2:]
266                                         path.addLineTo(x, y)
267                                 c2x, c2y = x, y
268                         elif command == 'L':
269                                 while len(params) > 1:
270                                         x = params[0]
271                                         y = params[1]
272                                         params = params[2:]
273                                         path.addLineTo(x, y)
274                                 c2x, c2y = x, y
275                         elif command == 'h':
276                                 x += params[0]
277                                 path.addLineTo(x, y)
278                                 c2x, c2y = x, y
279                         elif command == 'H':
280                                 x = params[0]
281                                 path.addLineTo(x, y)
282                                 c2x, c2y = x, y
283                         elif command == 'v':
284                                 y += params[0]
285                                 path.addLineTo(x, y)
286                                 c2x, c2y = x, y
287                         elif command == 'V':
288                                 y = params[0]
289                                 path.addLineTo(x, y)
290                                 c2x, c2y = x, y
291                         elif command == 'a':
292                                 while len(params) > 6:
293                                         x += params[5]
294                                         y += params[6]
295                                         path.addArcTo(x, y, params[2], params[0], params[1], params[3] > 0, params[4] > 0)
296                                         params = params[7:]
297                                 c2x, c2y = x, y
298                         elif command == 'A':
299                                 while len(params) > 6:
300                                         x = params[5]
301                                         y = params[6]
302                                         path.addArcTo(x, y, params[2], params[0], params[1], params[3] > 0, params[4] > 0)
303                                         params = params[7:]
304                                 c2x, c2y = x, y
305                         elif command == 'c':
306                                 while len(params) > 5:
307                                         c1x = x + params[0]
308                                         c1y = y + params[1]
309                                         c2x = x + params[2]
310                                         c2y = y + params[3]
311                                         x += params[4]
312                                         y += params[5]
313                                         path.addCurveTo(x, y, c1x, c1y, c2x, c2y)
314                                         params = params[6:]
315                         elif command == 'C':
316                                 while len(params) > 5:
317                                         c1x = params[0]
318                                         c1y = params[1]
319                                         c2x = params[2]
320                                         c2y = params[3]
321                                         x = params[4]
322                                         y = params[5]
323                                         path.addCurveTo(x, y, c1x, c1y, c2x, c2y)
324                                         params = params[6:]
325                         elif command == 's':
326                                 while len(params) > 3:
327                                         c1x = x - (c2x - x)
328                                         c1y = y - (c2y - y)
329                                         c2x = x + params[0]
330                                         c2y = y + params[1]
331                                         x += params[2]
332                                         y += params[3]
333                                         path.addCurveTo(x, y, c1x, c1y, c2x, c2y)
334                                         params = params[4:]
335                         elif command == 'S':
336                                 while len(params) > 3:
337                                         c1x = x - (c2x - x)
338                                         c1y = y - (c2y - y)
339                                         c2x = params[0]
340                                         c2y = params[1]
341                                         x = params[2]
342                                         y = params[3]
343                                         path.addCurveTo(x, y, c1x, c1y, c2x, c2y)
344                                         params = params[4:]
345                         elif command == 'q':
346                                 while len(params) > 3:
347                                         c1x = x + params[0]
348                                         c1y = y + params[1]
349                                         c2x = c1x
350                                         c2y = c1y
351                                         x += params[2]
352                                         y += params[3]
353                                         path.addCurveTo(x, y, c1x, c1y, c2x, c2y)
354                                         params = params[4:]
355                         elif command == 'Q':
356                                 while len(params) > 3:
357                                         c1x = params[0]
358                                         c1y = params[1]
359                                         c2x = c1x
360                                         c2y = c1y
361                                         x = params[2]
362                                         y = params[3]
363                                         path.addCurveTo(x, y, c1x, c1y, c2x, c2y)
364                                         params = params[4:]
365                         elif command == 't':
366                                 while len(params) > 1:
367                                         c1x = x - (c2x - x)
368                                         c1y = y - (c2y - y)
369                                         c2x = c1x
370                                         c2y = c1y
371                                         x += params[0]
372                                         y += params[1]
373                                         path.addCurveTo(x, y, c1x, c1y, c2x, c2y)
374                                         params = params[2:]
375                         elif command == 'T':
376                                 while len(params) > 1:
377                                         c1x = x - (c2x - x)
378                                         c1y = y - (c2y - y)
379                                         c2x = c1x
380                                         c2y = c1y
381                                         x = params[0]
382                                         y = params[1]
383                                         path.addCurveTo(x, y, c1x, c1y, c2x, c2y)
384                                         params = params[2:]
385                         elif command == 'z' or command == 'Z':
386                                 path.closePath()
387                                 x = path._startPoint.real
388                                 y = path._startPoint.imag
389                         else:
390                                 print 'Unknown path command:', command, params
391
392
393 if __name__ == '__main__':
394         for n in xrange(1, len(sys.argv)):
395                 print 'File: %s' % (sys.argv[n])
396                 svg = SVG(sys.argv[n])
397
398         drawing.saveAsHtml(svg, "test_export.html")