chiark / gitweb /
Some minor drawing cleanup (I know I know. Still unused.)
[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(drawing.Drawing):
56         def __init__(self, filename):
57                 super(SVG, self).__init__()
58                 self.tagProcess = {}
59                 self.tagProcess['rect'] = self._processRectTag
60                 self.tagProcess['line'] = self._processLineTag
61                 self.tagProcess['polyline'] = self._processPolylineTag
62                 self.tagProcess['polygon'] = self._processPolygonTag
63                 self.tagProcess['elipse'] = self._processPolygonTag
64                 self.tagProcess['circle'] = self._processCircleTag
65                 self.tagProcess['ellipse'] = self._processEllipseTag
66                 self.tagProcess['path'] = self._processPathTag
67                 self.tagProcess['use'] = self._processUseTag
68                 self.tagProcess['g'] = self._processGTag
69                 self.tagProcess['a'] = self._processGTag
70                 self.tagProcess['svg'] = self._processGTag
71                 self.tagProcess['text'] = None #No text implementation yet
72                 self.tagProcess['image'] = None
73                 self.tagProcess['metadata'] = None
74                 self.tagProcess['defs'] = None
75                 self.tagProcess['style'] = None
76                 self.tagProcess['marker'] = None
77                 self.tagProcess['desc'] = None
78                 self.tagProcess['filter'] = None
79                 self.tagProcess['linearGradient'] = None
80                 self.tagProcess['radialGradient'] = None
81                 self.tagProcess['pattern'] = None
82                 self.tagProcess['title'] = None
83                 self.tagProcess['animate'] = None
84                 self.tagProcess['animateColor'] = None
85                 self.tagProcess['animateTransform'] = None
86                 self.tagProcess['set'] = None
87                 self.tagProcess['script'] = None
88
89                 #From Inkscape
90                 self.tagProcess['namedview'] = None
91                 #From w3c testsuite
92                 self.tagProcess['SVGTestCase'] = None
93
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 = self.addPath(x1, y1, matrix)
141                 p.addLineTo(x2, y2)
142
143         def _processPolylineTag(self, tag, matrix):
144                 values = map(toFloat, re.split('[, \t]+', tag.get('points', '').strip()))
145                 p = self.addPath(values[0], values[1], matrix)
146                 for n in xrange(2, len(values)-1, 2):
147                         p.addLineTo(values[n], values[n+1])
148
149         def _processPolygonTag(self, tag, matrix):
150                 values = map(toFloat, re.split('[, \t]+', tag.get('points', '').strip()))
151                 p = self.addPath(values[0], values[1], matrix)
152                 for n in xrange(2, len(values)-1, 2):
153                         p.addLineTo(values[n], values[n+1])
154                 p.closePath()
155
156         def _processCircleTag(self, tag, matrix):
157                 cx = toFloat(tag.get('cx', '0'))
158                 cy = toFloat(tag.get('cy', '0'))
159                 r = toFloat(tag.get('r', '0'))
160                 p = self.addPath(cx-r, cy, matrix)
161                 p.addArcTo(cx+r, cy, 0, r, r, False, False)
162                 p.addArcTo(cx-r, cy, 0, r, r, False, False)
163
164         def _processEllipseTag(self, tag, matrix):
165                 cx = toFloat(tag.get('cx', '0'))
166                 cy = toFloat(tag.get('cy', '0'))
167                 rx = toFloat(tag.get('rx', '0'))
168                 ry = toFloat(tag.get('rx', '0'))
169                 p = self.addPath(cx-rx, cy, matrix)
170                 p.addArcTo(cx+rx, cy, 0, rx, ry, False, False)
171                 p.addArcTo(cx-rx, cy, 0, rx, ry, False, False)
172
173         def _processRectTag(self, tag, matrix):
174                 x = toFloat(tag.get('x', '0'))
175                 y = toFloat(tag.get('y', '0'))
176                 width = toFloat(tag.get('width', '0'))
177                 height = toFloat(tag.get('height', '0'))
178                 if width <= 0 or height <= 0:
179                         return
180                 rx = tag.get('rx')
181                 ry = tag.get('ry')
182                 if rx is not None or ry is not None:
183                         if ry is None:
184                                 ry = rx
185                         if rx is None:
186                                 rx = ry
187                         rx = float(rx)
188                         ry = float(ry)
189                         if rx > width / 2:
190                                 rx = width / 2
191                         if ry > height / 2:
192                                 ry = height / 2
193                 else:
194                         rx = 0.0
195                         ry = 0.0
196
197                 if rx > 0 and ry > 0:
198                         p = self.addPath(x+width-rx, y, matrix)
199                         p.addArcTo(x+width,y+ry, 0, rx, ry, False, True)
200                         p.addLineTo(x+width, y+height-ry)
201                         p.addArcTo(x+width-rx,y+height, 0, rx, ry, False, True)
202                         p.addLineTo(x+rx, y+height)
203                         p.addArcTo(x,y+height-ry, 0, rx, ry, False, True)
204                         p.addLineTo(x, y+ry)
205                         p.addArcTo(x+rx,y, 0, rx, ry, False, True)
206                         p.closePath()
207                 else:
208                         p = self.addPath(x, y, matrix)
209                         p.addLineTo(x,y+height)
210                         p.addLineTo(x+width,y+height)
211                         p.addLineTo(x+width,y)
212                         p.closePath()
213
214         def _processPathTag(self, tag, matrix):
215                 pathString = tag.get('d', '').replace(',', ' ')
216                 x = 0
217                 y = 0
218                 c2x = 0
219                 c2y = 0
220                 path = None
221                 for command in re.findall('[a-df-zA-DF-Z][^a-df-zA-DF-Z]*', pathString):
222                         params = re.split(' +', command[1:].strip())
223                         if len(params) > 0 and params[0] == '':
224                                 params = params[1:]
225                         if len(params) > 0 and params[-1] == '':
226                                 params = params[:-1]
227                         params = map(toFloat, params)
228                         command = command[0]
229
230                         if command == 'm':
231                                 x += params[0]
232                                 y += params[1]
233                                 path = self.addPath(x, y, matrix)
234                                 params = params[2:]
235                                 while len(params) > 1:
236                                         x += params[0]
237                                         y += params[1]
238                                         params = params[2:]
239                                         path.addLineTo(x, y)
240                                 c2x, c2y = x, y
241                         elif command == 'M':
242                                 x = params[0]
243                                 y = params[1]
244                                 path = self.addPath(x, y, matrix)
245                                 params = params[2:]
246                                 while len(params) > 1:
247                                         x = params[0]
248                                         y = params[1]
249                                         params = params[2:]
250                                         path.addLineTo(x, y)
251                                 c2x, c2y = x, y
252                         elif command == 'l':
253                                 while len(params) > 1:
254                                         x += params[0]
255                                         y += params[1]
256                                         params = params[2:]
257                                         path.addLineTo(x, y)
258                                 c2x, c2y = x, y
259                         elif command == 'L':
260                                 while len(params) > 1:
261                                         x = params[0]
262                                         y = params[1]
263                                         params = params[2:]
264                                         path.addLineTo(x, y)
265                                 c2x, c2y = x, y
266                         elif command == 'h':
267                                 x += params[0]
268                                 path.addLineTo(x, y)
269                                 c2x, c2y = x, y
270                         elif command == 'H':
271                                 x = params[0]
272                                 path.addLineTo(x, y)
273                                 c2x, c2y = x, y
274                         elif command == 'v':
275                                 y += params[0]
276                                 path.addLineTo(x, y)
277                                 c2x, c2y = x, y
278                         elif command == 'V':
279                                 y = params[0]
280                                 path.addLineTo(x, y)
281                                 c2x, c2y = x, y
282                         elif command == 'a':
283                                 while len(params) > 6:
284                                         x += params[5]
285                                         y += params[6]
286                                         path.addArcTo(x, y, params[2], params[0], params[1], params[3] > 0, params[4] > 0)
287                                         params = params[7:]
288                                 c2x, c2y = x, y
289                         elif command == 'A':
290                                 while len(params) > 6:
291                                         x = params[5]
292                                         y = params[6]
293                                         path.addArcTo(x, y, params[2], params[0], params[1], params[3] > 0, params[4] > 0)
294                                         params = params[7:]
295                                 c2x, c2y = x, y
296                         elif command == 'c':
297                                 while len(params) > 5:
298                                         c1x = x + params[0]
299                                         c1y = y + params[1]
300                                         c2x = x + params[2]
301                                         c2y = y + params[3]
302                                         x += params[4]
303                                         y += params[5]
304                                         path.addCurveTo(x, y, c1x, c1y, c2x, c2y)
305                                         params = params[6:]
306                         elif command == 'C':
307                                 while len(params) > 5:
308                                         c1x = params[0]
309                                         c1y = params[1]
310                                         c2x = params[2]
311                                         c2y = params[3]
312                                         x = params[4]
313                                         y = params[5]
314                                         path.addCurveTo(x, y, c1x, c1y, c2x, c2y)
315                                         params = params[6:]
316                         elif command == 's':
317                                 while len(params) > 3:
318                                         c1x = x - (c2x - x)
319                                         c1y = y - (c2y - y)
320                                         c2x = x + params[0]
321                                         c2y = y + params[1]
322                                         x += params[2]
323                                         y += params[3]
324                                         path.addCurveTo(x, y, c1x, c1y, c2x, c2y)
325                                         params = params[4:]
326                         elif command == 'S':
327                                 while len(params) > 3:
328                                         c1x = x - (c2x - x)
329                                         c1y = y - (c2y - y)
330                                         c2x = params[0]
331                                         c2y = params[1]
332                                         x = params[2]
333                                         y = params[3]
334                                         path.addCurveTo(x, y, c1x, c1y, c2x, c2y)
335                                         params = params[4:]
336                         elif command == 'q':
337                                 while len(params) > 3:
338                                         c1x = x + params[0]
339                                         c1y = y + params[1]
340                                         c2x = c1x
341                                         c2y = c1y
342                                         x += params[2]
343                                         y += params[3]
344                                         path.addCurveTo(x, y, c1x, c1y, c2x, c2y)
345                                         params = params[4:]
346                         elif command == 'Q':
347                                 while len(params) > 3:
348                                         c1x = params[0]
349                                         c1y = params[1]
350                                         c2x = c1x
351                                         c2y = c1y
352                                         x = params[2]
353                                         y = params[3]
354                                         path.addCurveTo(x, y, c1x, c1y, c2x, c2y)
355                                         params = params[4:]
356                         elif command == 't':
357                                 while len(params) > 1:
358                                         c1x = x - (c2x - x)
359                                         c1y = y - (c2y - y)
360                                         c2x = c1x
361                                         c2y = c1y
362                                         x += params[0]
363                                         y += params[1]
364                                         path.addCurveTo(x, y, c1x, c1y, c2x, c2y)
365                                         params = params[2:]
366                         elif command == 'T':
367                                 while len(params) > 1:
368                                         c1x = x - (c2x - x)
369                                         c1y = y - (c2y - y)
370                                         c2x = c1x
371                                         c2y = c1y
372                                         x = params[0]
373                                         y = params[1]
374                                         path.addCurveTo(x, y, c1x, c1y, c2x, c2y)
375                                         params = params[2:]
376                         elif command == 'z' or command == 'Z':
377                                 path.closePath()
378                                 x = path._startPoint.real
379                                 y = path._startPoint.imag
380                         else:
381                                 print 'Unknown path command:', command, params
382
383
384 if __name__ == '__main__':
385         for n in xrange(1, len(sys.argv)):
386                 print 'File: %s' % (sys.argv[n])
387                 svg = SVG(sys.argv[n])
388
389         svg.saveAsHtml("test_export.html")