2 # --------------------------------------------------------------------
3 # convert SVG to Ipe format
4 # --------------------------------------------------------------------
6 # Copyright (C) 2009-2014 Otfried Cheong
8 # svgtoipe is free software; you can redistribute it and/or modify it
9 # under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 3 of the License, or
11 # (at your option) any later version.
13 # svgtoipe is distributed in the hope that it will be useful, but
14 # WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 # General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with svgtoipe; if not, you can find it at
20 # "http://www.gnu.org/copyleft/gpl.html", or write to the Free
21 # Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 # --------------------------------------------------------------------
25 svgtoipe_version = "20091018"
28 import xml.dom.minidom as xml
29 from xml.dom.minidom import Node
42 # --------------------------------------------------------------------
45 "black" : "rgb(0, 0, 0)",
46 "green" :"rgb(0, 128, 0)",
47 "silver" :"rgb(192, 192, 192)",
48 "lime" :"rgb(0, 255, 0)",
49 "gray" :"rgb(128, 128, 128)",
50 "olive" :"rgb(128, 128, 0)",
51 "white" :"rgb(255, 255, 255)",
52 "yellow" :"rgb(255, 255, 0)",
53 "maroon" :"rgb(128, 0, 0)",
54 "navy" :"rgb(0, 0, 128)",
55 "red" :"rgb(255, 0, 0)",
56 "blue" :"rgb(0, 0, 255)",
57 "purple" :"rgb(128, 0, 128)",
58 "teal" :"rgb(0, 128, 128)",
59 "fuchsia" :"rgb(255, 0, 255)",
60 "aqua" :"rgb(0, 255, 255)",
63 attribute_names = [ "stroke",
77 def printAttributes(n):
79 for i in range(a.length):
81 if name[:9] != "sodipodi:" and name[:9] != "inkscape:":
82 print " ", name, n.getAttribute(name)
87 if txt.endswith('px') or txt.endswith('pt'):
88 return float(txt[:-2])
89 elif txt.endswith('pc'):
90 return 12 * float(txt[:-2])
91 elif txt.endswith('mm'):
92 return 72.0 * float(txt[:-2]) / 25.4
93 elif txt.endswith('cm'):
94 return 72.0 * float(txt[:-2]) / 2.54
95 elif txt.endswith('in'):
96 return 72.0 * float(txt[:-2])
100 def parse_opacity(txt):
103 m = int(10 * (float(txt) + 0.05))
107 def parse_list(string):
108 return re.findall("([A-Za-z]|-?[0-9]+\.?[0-9]*(?:e-?[0-9]*)?)", string)
110 def parse_style(string):
112 for item in string.split(';'):
114 key, value = item.split(':')
115 sdict[key.strip()] = value.strip()
118 def parse_color_component(txt):
119 if txt.endswith("%"):
120 return float(txt[:-1]) / 100.0
122 return int(txt) / 255.0
125 if not c or c == 'none':
127 if c in color_keywords:
128 c = color_keywords[c]
129 m = re.match(r"rgb\(([0-9\.]+%?),\s*([0-9\.]+%?),\s*([0-9\.]+%?)\s*\)", c)
131 r = parse_color_component(m.group(1))
132 g = parse_color_component(m.group(2))
133 b = parse_color_component(m.group(3))
135 m = re.match(r"#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])$", c)
137 r = int(m.group(1), 16) / 15.0
138 g = int(m.group(2), 16) / 15.0
139 b = int(m.group(3), 16) / 15.0
141 m = re.match(r"#([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])"
142 + r"([0-9a-fA-F][0-9a-fA-F])$", c)
144 r = int(m.group(1), 16) / 255.0
145 g = int(m.group(2), 16) / 255.0
146 b = int(m.group(3), 16) / 255.0
148 sys.stderr.write("Unknown color: %s\n" % c)
154 l.append(float(d.pop(0)))
158 def parse_path(out, d):
159 d = re.findall("([A-Za-z]|-?[0-9]+\.?[0-9]*(?:e-?[0-9]*)?)", d)
163 if not d[0][0] in "01234567890.-":
167 out.write("%g %g m\n" % (x, y))
173 out.write("%g %g m\n" % (x, y))
177 out.write("%g %g l\n" % (x, y))
182 out.write("%g %g l\n" % (x, y))
185 out.write("%g %g l\n" % (x, y))
188 out.write("%g %g l\n" % (x, y))
191 out.write("%g %g l\n" % (x, y))
194 out.write("%g %g l\n" % (x, y))
196 x1, y1, xs, ys, x, y = pnext(d, 6)
197 out.write("%g %g %g %g %g %g c\n" % (x1, y1, xs, ys, x, y))
199 x1, y1, xs, ys, xf, yf = pnext(d, 6)
203 out.write("%g %g %g %g %g %g c\n" % (x1, y1, xs, ys, x, y))
204 elif opcode == 'S' or opcode == 's':
205 x2, y2, xf, yf = pnext(d, 4)
209 x1 = x + (x - xs); y1 = y + (y - ys)
210 out.write("%g %g %g %g %g %g c\n" % (x1, y1, x2, y2, xf, yf))
214 xs, ys, x, y = pnext(d, 4)
215 out.write("%g %g %g %g q\n" % (xs, ys, x, y))
217 xs, ys, xf, yf = pnext(d, 4)
220 out.write("%g %g %g %g q\n" % (xs, ys, x, y))
221 elif opcode == 'T' or opcode == 't':
225 x1 = x + (x - xs); y1 = y + (y - ys)
226 out.write("%g %g %g %g q\n" % (x1, y1, xf, yf))
229 elif opcode == 'A' or opcode == 'a':
230 rx, ry, phi, large_arc, sweep, x2, y2 = pnext(d, 7)
233 draw_arc(out, x, y, rx, ry, phi, large_arc, sweep, x2, y2)
238 sys.stderr.write("Unrecognised opcode: %s\n" % opcode)
240 def parse_transformation(txt):
241 d = re.findall("[a-zA-Z]+\([^)]*\)", txt)
244 m1 = Matrix(d.pop(0))
248 def get_gradientTransform(n):
249 if n.hasAttribute("gradientTransform"):
250 return parse_transformation(n.getAttribute("gradientTransform"))
253 def parse_transform(n):
254 if n.hasAttribute("transform"):
255 return parse_transformation(n.getAttribute("transform"))
258 # Convert from endpoint to center parameterization
259 # www.w3.org/TR/2003/REC-SVG11-20030114/implnote.html#ArcImplementationNotes
260 def draw_arc(out, x1, y1, rx, ry, phi, large_arc, sweep, x2, y2):
261 phi = math.pi * phi / 180.0
262 cp = math.cos(phi); sp = math.sin(phi)
263 dx = .5 * (x1 - x2); dy = .5 * (y1 - y2)
264 x1p = cp * dx + sp * dy; y1p = -sp * dx + cp * dy
265 r2 = (((rx * ry)**2 - (rx * y1p)**2 - (ry * x1p)**2)/
266 ((rx * y1p)**2 + (ry * x1p)**2))
269 if large_arc == sweep:
271 cxp = r * rx * y1p / ry; cyp = -r * ry * x1p / rx
272 cx = cp * cxp - sp * cyp + .5 * (x1 + x2)
273 cy = sp * cxp + cp * cyp + .5 * (y1 + y2)
274 m = Matrix([rx, 0, 0, ry, 0, 0])
275 m = Matrix([cp, sp, -sp, cp, cx, cy]) * m
277 m = m * Matrix([1, 0, 0, -1, 0, 0])
278 out.write("%s %g %g a\n" % (str(m), x2, y2))
280 # --------------------------------------------------------------------
282 class Matrix(object):
284 # Default is identity matrix
285 def __init__(self, string = None):
286 self.values = [1, 0, 0, 1, 0, 0]
287 if not string or string == "":
289 if isinstance(string, list):
292 mat = re.match(r"([a-zA-Z]+)\(([^)]*)\)$", string)
294 sys.stderr.write("Unknown transform: %s\n" % string)
296 d = [float(x) for x in parse_list(mat.group(2))]
299 elif op == "translate":
300 if len(d) == 1: d.append(0.0)
301 self.values = [1, 0, 0, 1, d[0], d[1]]
303 if len(d) == 1: d.append(d[0])
305 self.values = [sx, 0, 0, sy, 0, 0]
307 phi = math.pi * d[0] / 180.0
308 self.values = [math.cos(phi), math.sin(phi),
309 -math.sin(phi), math.cos(phi), 0, 0]
311 tphi = math.tan(math.pi * d[0] / 180.0)
312 self.values = [1, 0, tphi, 1, 0, 0]
314 tphi = math.tan(math.pi * d[0] / 180.0)
315 self.values = [1, tphi, 0, 1, 0, 0]
317 sys.stderr.write("Unknown transform: %s\n" % string)
319 def __call__(self, other):
320 return (self.values[0]*other[0] + self.values[2]*other[1] + self.values[4],
321 self.values[1]*other[0] + self.values[3]*other[1] + self.values[5])
324 d = float(self.values[0]*self.values[3] - self.values[1]*self.values[2])
325 return Matrix([self.values[3]/d, -self.values[1]/d,
326 -self.values[2]/d, self.values[0]/d,
327 (self.values[2]*self.values[5] -
328 self.values[3]*self.values[4])/d,
329 (self.values[1]*self.values[4] -
330 self.values[0]*self.values[5])/d])
332 def __mul__(self, other):
333 a, b, c, d, e, f = self.values
334 u, v, w, x, y, z = other.values
335 return Matrix([a*u + c*v, b*u + d*v, a*w + c*x,
336 b*w + d*x, a*y + c*z + e, b*y + d*z + f])
339 a, b, c, d, e, f = self.values
340 return "%g %g %g %g %g %g" % (a, b, c, d, e, f)
342 # --------------------------------------------------------------------
346 def __init__(self, fname):
347 self.dom = xml.parse(fname)
349 for a in attribute_names:
351 self.attributes = [ attr ]
353 for n in self.dom.childNodes:
354 if n.nodeType == Node.ELEMENT_NODE and n.tagName == "svg":
355 if n.hasAttribute("viewBox"):
356 x, y, w, h = [float(x) for x in parse_list(n.getAttribute("viewBox"))]
361 self.width = parse_float(n.getAttribute("width"))
362 self.height = parse_float(n.getAttribute("height"))
367 # --------------------------------------------------------------------
369 def parse_svg(self, outname):
370 self.out = open(outname, "w")
371 self.out.write('<?xml version="1.0"?>\n')
372 self.out.write('<!DOCTYPE ipe SYSTEM "ipe.dtd">\n')
373 self.out.write('<ipe version="70005" creator="svgtoipe %s">\n' %
375 self.out.write('<ipestyle>\n')
376 self.out.write(('<layout paper="%d %d" frame="%d %d" ' +
377 'origin="0 0" crop="no"/>\n') %
378 (self.width, self.height, self.width, self.height))
379 for t in range(10, 100, 10):
380 self.out.write('<opacity name="%d%%" value="0.%d"/>\n' % (t, t))
382 self.out.write('<pathstyle cap="0" join="0" fillrule="wind"/>\n')
383 self.out.write('</ipestyle>\n')
384 # collect definitions
385 for n in self.root.childNodes:
386 if n.nodeType != Node.ELEMENT_NODE:
388 if hasattr(self, "def_" + n.tagName):
389 getattr(self, "def_" + n.tagName)(n)
390 # write definitions into stylesheet
391 if len(self.defs) > 0:
392 self.out.write('<ipestyle>\n')
394 if self.defs[k][0] == "linearGradient":
395 self.write_linear_gradient(k)
396 elif self.defs[k][0] == "radialGradient":
397 self.write_radial_gradient(k)
398 self.out.write('</ipestyle>\n')
400 self.out.write('<page>\n')
401 m = Matrix([1, 0, 0, 1, 0, self.height / 2.0])
402 m = m * Matrix([1, 0, 0, -1, 0, 0])
403 m = m * Matrix([1, 0, 0, 1,
404 -self.origin[0], -(self.origin[1] + self.height / 2.0)])
405 self.out.write('<group matrix="%s">\n' % str(m))
406 for n in self.root.childNodes:
407 if n.nodeType != Node.ELEMENT_NODE:
409 if hasattr(self, "node_" + n.tagName):
410 getattr(self, "node_" + n.tagName)(n)
412 sys.stderr.write("Unhandled node: %s\n" % n.tagName)
413 self.out.write('</group>\n')
414 self.out.write('</page>\n')
415 self.out.write('</ipe>\n')
418 # --------------------------------------------------------------------
420 def write_linear_gradient(self, k):
421 typ, x1, x2, y1, y2, stops, matrix = self.defs[k]
422 self.out.write('<gradient name="g%s" type="axial" extend="yes"\n' % k)
423 self.out.write(' matrix="%s"' % str(matrix))
424 self.out.write(' coords="%g %g %g %g">\n' % (x1, y1, x2, y2))
427 self.out.write(' <stop offset="%g" color="%g %g %g"/>\n' %
428 (offset, color[0], color[1], color[2]))
429 self.out.write('</gradient>\n')
431 def write_radial_gradient(self, k):
432 typ, cx, cy, r, fx, fy, stops, matrix = self.defs[k]
433 self.out.write('<gradient name="g%s" type="radial" extend="yes"\n' % k)
434 self.out.write(' matrix="%s"' % str(matrix))
435 self.out.write(' coords="%g %g %g %g %g %g">\n' % (fx, fy, 0, cx, cy, r))
438 self.out.write(' <stop offset="%g" color="%g %g %g"/>\n' %
439 (offset, color[0], color[1], color[2]))
440 self.out.write('</gradient>\n')
442 def get_stops(self, n):
444 for m in n.childNodes:
445 if m.nodeType != Node.ELEMENT_NODE:
447 if m.tagName != "stop":
448 continue # should not happen
449 offs = m.getAttribute("offset")
450 if offs.endswith("%"):
451 offs = float(offs[:-1]) / 100.0
454 color = parse_color(m.getAttribute("stop-color"))
455 if m.hasAttribute("style"):
456 sdict = parse_style(m.getAttribute("style"))
457 if "stop-color" in sdict:
458 color = parse_color(sdict["stop-color"])
459 stops.append((offs, color))
461 if n.hasAttribute("xlink:href"):
462 ref = n.getAttribute("xlink:href")
463 if ref.startswith("#") and ref[1:] in self.defs:
464 stops = self.defs[ref[1:]][5]
467 def def_linearGradient(self, n):
469 kid = n.getAttribute("id")
471 x2 = self.width; y2 = self.height
472 if n.hasAttribute("x1"):
473 s = n.getAttribute("x1")
475 x1 = self.width * float(s[:-1]) / 100.0
478 if n.hasAttribute("x2"):
479 s = n.getAttribute("x2")
481 x2 = self.width * float(s[:-1]) / 100.0
484 if n.hasAttribute("y1"):
485 s = n.getAttribute("y1")
487 y1 = self.width * float(s[:-1]) / 100.0
490 if n.hasAttribute("y2"):
491 s = n.getAttribute("y2")
493 y2 = self.width * float(s[:-1]) / 100.0
496 matrix = get_gradientTransform(n)
497 stops = self.get_stops(n)
498 self.defs[kid] = ("linearGradient", x1, x2, y1, y2, stops, matrix)
500 def def_radialGradient(self, n):
502 kid = n.getAttribute("id")
503 cx = "50%"; cy = "50%"; r = "50%"
504 if n.hasAttribute("cx"):
505 cx = n.getAttribute("cx")
507 cx = self.width * float(cx[:-1]) / 100.0
510 if n.hasAttribute("cy"):
511 cy = n.getAttribute("cy")
513 cy = self.width * float(cy[:-1]) / 100.0
516 if n.hasAttribute("r"):
517 r = n.getAttribute("r")
519 r = self.width * float(r[:-1]) / 100.0
522 if n.hasAttribute("fx"):
523 s = n.getAttribute("fx")
525 fx = self.width * float(s[:-1]) / 100.0
530 if n.hasAttribute("fy"):
531 s = n.getAttribute("fy")
533 fy = self.width * float(s[:-1]) / 100.0
538 matrix = get_gradientTransform(n)
539 stops = self.get_stops(n)
540 self.defs[kid] = ("radialGradient", cx, cy, r, fx, fy, stops, matrix)
542 def def_clipPath(self, node):
543 kid = node.getAttribute("id")
544 # only a single path is implemented
545 for n in node.childNodes:
546 if n.nodeType != Node.ELEMENT_NODE or n.tagName != "path":
548 m = parse_transform(n)
549 d = n.getAttribute("d")
550 output = cStringIO.StringIO()
551 parse_path(output, d)
552 path = output.getvalue()
554 self.defs[kid] = ("clipPath", m, path)
557 def def_g(self, group):
558 for n in group.childNodes:
559 if n.nodeType != Node.ELEMENT_NODE:
561 if hasattr(self, "def_" + n.tagName):
562 getattr(self, "def_" + n.tagName)(n)
564 def def_defs(self, node):
567 # --------------------------------------------------------------------
569 def parse_attributes(self, n):
570 pattr = self.attributes[-1]
572 for a in attribute_names:
573 if n.hasAttribute(a):
574 attr[a] = n.getAttribute(a)
577 if n.hasAttribute("style"):
578 sdict = parse_style(n.getAttribute("style"))
579 for a in attribute_names:
584 def write_pathattributes(self, a):
585 stroke = parse_color(a["stroke"])
587 self.out.write(' stroke="%g %g %g"' % stroke)
589 if fill and fill.startswith("url("):
590 mat = re.match("url\(#([^)]+)\).*", fill)
593 if grad in self.defs and (self.defs[grad][0] == "linearGradient" or
594 self.defs[grad][0] == "radialGradient"):
595 self.out.write(' fill="1" gradient="g%s"' % grad)
597 fill = parse_color(a["fill"])
599 self.out.write(' fill="%g %g %g"' % fill)
600 opacity = parse_opacity(a["opacity"])
601 fill_opacity = parse_opacity(a["fill-opacity"])
602 stroke_opacity = parse_opacity(a["stroke-opacity"])
603 if fill and fill_opacity:
604 opacity = fill_opacity
605 if not fill and stroke and stroke_opacity:
606 opacity = stroke_opacity
607 if opacity and opacity != 100:
608 self.out.write(' opacity="%d%%"' % opacity)
609 stroke_width = parse_float(a["stroke-width"])
610 if a["stroke-width"]:
611 self.out.write(' pen="%g"' % stroke_width)
612 if a["fill-rule"] == "nonzero":
613 self.out.write(' fillrule="wind"')
614 k = {"butt" : 0, "round" : 1, "square" : 2 }
615 if a["stroke-linecap"] in k:
616 self.out.write(' cap="%d"' % k[a["stroke-linecap"]])
617 k = {"miter" : 0, "round" : 1, "bevel" : 2 }
618 if a["stroke-linejoin"] in k:
619 self.out.write(' join="%d"' % k[a["stroke-linejoin"]])
620 dasharray = a["stroke-dasharray"]
621 dashoffset = a["stroke-dashoffset"]
622 if dasharray and dashoffset and dasharray != "none":
623 d = parse_list(dasharray)
624 off = parse_float(dashoffset)
625 self.out.write(' dash="[%s] %g"' % (" ".join(d), off))
627 # --------------------------------------------------------------------
629 def node_g(self, group):
630 # printAttributes(group)
631 attr = self.parse_attributes(group)
632 self.attributes.append(attr)
633 self.out.write('<group')
634 m = parse_transform(group)
636 self.out.write(' matrix="%s"' % m)
637 self.out.write('>\n')
638 for n in group.childNodes:
639 if n.nodeType != Node.ELEMENT_NODE:
641 if hasattr(self, "node_" + n.tagName):
642 getattr(self, "node_" + n.tagName)(n)
644 sys.stderr.write("Unhandled node: %s\n" % n.tagName)
645 self.out.write('</group>\n')
646 self.attributes.pop()
648 def collect_text(self, root):
649 for n in root.childNodes:
650 if n.nodeType == Node.TEXT_NODE:
652 if n.nodeType != Node.ELEMENT_NODE:
654 if n.tagName == "tspan": # recurse
657 def node_text(self, t):
658 if not t.hasAttribute("x") or not t.hasAttribute("y"):
659 sys.stderr.write("Text without coordinates ignored\n")
661 x = float(t.getAttribute("x"))
662 y = float(t.getAttribute("y"))
663 attr = self.parse_attributes(t)
664 self.out.write('<text pos="%g %g"' % (x,y))
665 self.out.write(' transformations="affine" valign="baseline"')
666 m = parse_transform(t)
667 if not m: m = Matrix()
668 m = m * Matrix([1, 0, 0, -1, x, y]) * Matrix([1, 0, 0, 1, -x, -y])
669 self.out.write(' matrix="%s"' % m)
670 if attr["font-size"]:
671 self.out.write(' size="%g"' % parse_float(attr["font-size"]))
672 color = parse_color(attr["fill"])
674 self.out.write(' stroke="%g %g %g"' % color)
677 self.out.write('>%s</text>\n' % self.text.encode("UTF-8"))
679 def node_image(self, node):
681 sys.stderr.write("No Python image library, <image> ignored\n")
683 href = node.getAttribute("xlink:href")
684 if not href.startswith("data:image/png;base64,"):
685 sys.stderr.write("Image ignored, href = %s...\n" % href[:40])
687 x = float(node.getAttribute("x"))
688 y = float(node.getAttribute("y"))
689 w = float(node.getAttribute("width"))
690 h = float(node.getAttribute("height"))
692 if node.hasAttribute("clip-path"):
693 mat = re.match("url\(#([^)]+)\).*", node.getAttribute("clip-path"))
696 if cp in self.defs and self.defs[cp][0] == "clipPath":
697 cp, m, path = self.defs[cp]
699 self.out.write('<group matrix="%s" clip="%s">\n' % (str(m), path))
700 self.out.write('<group matrix="%s">\n' % str(m.inverse()))
701 self.out.write('<image rect="%g %g %g %g"' % (x, y, x + w, y + h))
702 data = base64.b64decode(href[22:])
703 fin = cStringIO.StringIO(data)
704 image = Image.open(fin)
705 m = parse_transform(node)
708 m = m * Matrix([1, 0, 0, -1, x, y+h]) * Matrix([1, 0, 0, 1, -x, -y])
709 self.out.write(' matrix="%s"' % m)
710 self.out.write(' width="%d" height="%d" ColorSpace="DeviceRGB"' %
712 self.out.write(' BitsPerComponent="8" encoding="base64"> \n')
714 data = cStringIO.StringIO()
715 for pixel in image.getdata():
716 data.write("%c%c%c" % pixel[:3])
717 self.out.write(base64.b64encode(data.getvalue()))
721 for pixel in image.getdata():
722 self.out.write("%02x%02x%02x" % pixel[:3])
728 self.out.write('</image>\n')
730 self.out.write('</group>\n</group>\n')
732 # handled in def pass
733 def node_linearGradient(self, n):
736 def node_radialGradient(self, n):
739 def node_rect(self, n):
740 attr = self.parse_attributes(n)
741 self.out.write('<path')
742 m = parse_transform(n)
744 self.out.write(' matrix="%s"' % m)
745 self.write_pathattributes(attr)
746 self.out.write('>\n')
747 x = float(n.getAttribute("x"))
748 y = float(n.getAttribute("y"))
749 w = float(n.getAttribute("width"))
750 h = float(n.getAttribute("height"))
751 self.out.write("%g %g m %g %g l %g %g l %g %g l h\n" %
752 (x, y, x + w, y, x + w, y + h, x, y + h))
753 self.out.write('</path>\n')
755 def node_circle(self, n):
756 self.out.write('<path')
757 m = parse_transform(n)
759 self.out.write(' matrix="%s"' % m)
760 attr = self.parse_attributes(n)
761 self.write_pathattributes(attr)
762 self.out.write('>\n')
763 cx = float(n.getAttribute("cx"))
764 cy = float(n.getAttribute("cy"))
765 r = float(n.getAttribute("r"))
766 self.out.write("%g 0 0 %g %g %g e\n" % (r, r, cx, cy))
767 self.out.write('</path>\n')
769 def node_ellipse(self, n):
770 self.out.write('<path')
771 m = parse_transform(n)
773 self.out.write(' matrix="%s"' % m)
774 attr = self.parse_attributes(n)
775 self.write_pathattributes(attr)
776 self.out.write('>\n')
779 if n.hasAttribute("cx"):
780 cx = float(n.getAttribute("cx"))
781 if n.hasAttribute("cy"):
782 cy = float(n.getAttribute("cy"))
783 rx = float(n.getAttribute("rx"))
784 ry = float(n.getAttribute("ry"))
785 self.out.write("%g 0 0 %g %g %g e\n" % (rx, ry, cx, cy))
786 self.out.write('</path>\n')
788 def node_line(self, n):
789 self.out.write('<path')
790 m = parse_transform(n)
792 self.out.write(' matrix="%s"' % m)
793 attr = self.parse_attributes(n)
794 self.write_pathattributes(attr)
795 self.out.write('>\n')
796 x1 = 0; y1 = 0; x2 = 0; y2 = 0
797 if n.hasAttribute("x1"):
798 x1 = float(n.getAttribute("x1"))
799 if n.hasAttribute("y1"):
800 y1 = float(n.getAttribute("y1"))
801 if n.hasAttribute("x2"):
802 x2 = float(n.getAttribute("x2"))
803 if n.hasAttribute("y2"):
804 y2 = float(n.getAttribute("y2"))
805 self.out.write("%g %g m %g %g l\n" % (x1, y1, x2, y2))
806 self.out.write('</path>\n')
808 def node_polyline(self, n):
809 self.polygon(n, closed=False)
811 def node_polygon(self, n):
812 self.polygon(n, closed=True)
814 def polygon(self, n, closed):
815 self.out.write('<path')
816 m = parse_transform(n)
818 self.out.write(' matrix="%s"' % m)
819 attr = self.parse_attributes(n)
820 self.write_pathattributes(attr)
821 self.out.write('>\n')
822 d = parse_list(n.getAttribute("points"))
827 self.out.write("%g %g %s\n" % (x, y, op))
830 self.out.write("h\n")
831 self.out.write('</path>\n')
833 def node_path(self, n):
834 self.out.write('<path')
835 m = parse_transform(n)
837 self.out.write(' matrix="%s"' % m)
838 attr = self.parse_attributes(n)
839 self.write_pathattributes(attr)
840 self.out.write('>\n')
841 d = n.getAttribute("d")
842 parse_path(self.out, d)
843 self.out.write('</path>\n')
845 # --------------------------------------------------------------------
848 if len(sys.argv) != 2 and len(sys.argv) != 3:
849 sys.stderr.write("Usage: svgtoipe <figure.svg> [ <figure.ipe> ]\n")
852 if len(sys.argv) > 2:
853 outname = sys.argv[2]
855 if fname[-4:].lower() == ".svg":
856 outname = fname[:-4] + ".ipe"
858 outname = fname + ".ipe"
860 svg.parse_svg(outname)
862 if __name__ == '__main__':
865 # --------------------------------------------------------------------