From 351b6e23bd09fd98dc9731333e8da7d88aa0539b Mon Sep 17 00:00:00 2001 From: Marcel Date: Tue, 10 May 2016 09:10:29 +0200 Subject: [PATCH] fixed svg to ipe script --- svgtoipe.py | 865 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 865 insertions(+) create mode 100755 svgtoipe.py diff --git a/svgtoipe.py b/svgtoipe.py new file mode 100755 index 0000000..45f3e98 --- /dev/null +++ b/svgtoipe.py @@ -0,0 +1,865 @@ +#!/usr/bin/env python +# -------------------------------------------------------------------- +# convert SVG to Ipe format +# -------------------------------------------------------------------- +# +# Copyright (C) 2009-2014 Otfried Cheong +# +# svgtoipe is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# svgtoipe 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 +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with svgtoipe; if not, you can find it at +# "http://www.gnu.org/copyleft/gpl.html", or write to the Free +# Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# -------------------------------------------------------------------- + +svgtoipe_version = "20091018" + +import sys +import xml.dom.minidom as xml +from xml.dom.minidom import Node +import re +import math + +import base64 +import cStringIO + +try: + from PIL import Image + have_pil = True +except: + have_pil = False + +# -------------------------------------------------------------------- + +color_keywords = { + "black" : "rgb(0, 0, 0)", + "green" :"rgb(0, 128, 0)", + "silver" :"rgb(192, 192, 192)", + "lime" :"rgb(0, 255, 0)", + "gray" :"rgb(128, 128, 128)", + "olive" :"rgb(128, 128, 0)", + "white" :"rgb(255, 255, 255)", + "yellow" :"rgb(255, 255, 0)", + "maroon" :"rgb(128, 0, 0)", + "navy" :"rgb(0, 0, 128)", + "red" :"rgb(255, 0, 0)", + "blue" :"rgb(0, 0, 255)", + "purple" :"rgb(128, 0, 128)", + "teal" :"rgb(0, 128, 128)", + "fuchsia" :"rgb(255, 0, 255)", + "aqua" :"rgb(0, 255, 255)", +} + +attribute_names = [ "stroke", + "fill", + "stroke-opacity", + "fill-opacity", + "stroke-width", + "fill-rule", + "stroke-linecap", + "stroke-linejoin", + "stroke-dasharray", + "stroke-dashoffset", + "stroke-miterlimit", + "opacity", + "font-size" ] + +def printAttributes(n): + a = n.attributes + for i in range(a.length): + name = a.item(i).name + if name[:9] != "sodipodi:" and name[:9] != "inkscape:": + print " ", name, n.getAttribute(name) + +def parse_float(txt): + if not txt: + return None + if txt.endswith('px') or txt.endswith('pt'): + return float(txt[:-2]) + elif txt.endswith('pc'): + return 12 * float(txt[:-2]) + elif txt.endswith('mm'): + return 72.0 * float(txt[:-2]) / 25.4 + elif txt.endswith('cm'): + return 72.0 * float(txt[:-2]) / 2.54 + elif txt.endswith('in'): + return 72.0 * float(txt[:-2]) + else: + return float(txt) + +def parse_opacity(txt): + if not txt: + return None + m = int(10 * (float(txt) + 0.05)) + if m == 0: m = 1 + return 10 * m + +def parse_list(string): + return re.findall("([A-Za-z]|-?[0-9]+\.?[0-9]*(?:e-?[0-9]*)?)", string) + +def parse_style(string): + sdict = {} + for item in string.split(';'): + if ':' in item: + key, value = item.split(':') + sdict[key.strip()] = value.strip() + return sdict + +def parse_color_component(txt): + if txt.endswith("%"): + return float(txt[:-1]) / 100.0 + else: + return int(txt) / 255.0 + +def parse_color(c): + if not c or c == 'none': + return None + if c in color_keywords: + c = color_keywords[c] + m = re.match(r"rgb\(([0-9\.]+%?),\s*([0-9\.]+%?),\s*([0-9\.]+%?)\s*\)", c) + if m: + r = parse_color_component(m.group(1)) + g = parse_color_component(m.group(2)) + b = parse_color_component(m.group(3)) + return (r, g, b) + m = re.match(r"#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])$", c) + if m: + r = int(m.group(1), 16) / 15.0 + g = int(m.group(2), 16) / 15.0 + b = int(m.group(3), 16) / 15.0 + return (r, g, b) + m = re.match(r"#([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])" + + r"([0-9a-fA-F][0-9a-fA-F])$", c) + if m: + r = int(m.group(1), 16) / 255.0 + g = int(m.group(2), 16) / 255.0 + b = int(m.group(3), 16) / 255.0 + return (r, g, b) + sys.stderr.write("Unknown color: %s\n" % c) + return None + +def pnext(d, n): + l = [] + while n > 0: + l.append(float(d.pop(0))) + n -= 1 + return tuple(l) + +def parse_path(out, d): + d = re.findall("([A-Za-z]|-?[0-9]+\.?[0-9]*(?:e-?[0-9]*)?)", d) + x, y = 0.0, 0.0 + xs, ys = 0.0, 0.0 + while d: + if not d[0][0] in "01234567890.-": + opcode = d.pop(0) + if opcode == 'M': + x, y = pnext(d, 2) + out.write("%g %g m\n" % (x, y)) + opcode = 'L' + elif opcode == 'm': + x1, y1 = pnext(d, 2) + x += x1 + y += y1 + out.write("%g %g m\n" % (x, y)) + opcode = 'l' + elif opcode == 'L': + x, y = pnext(d, 2) + out.write("%g %g l\n" % (x, y)) + elif opcode == 'l': + x1, y1 = pnext(d, 2) + x += x1 + y += y1 + out.write("%g %g l\n" % (x, y)) + elif opcode == 'H': + x = pnext(d, 1)[0] + out.write("%g %g l\n" % (x, y)) + elif opcode == 'h': + x += pnext(d, 1)[0] + out.write("%g %g l\n" % (x, y)) + elif opcode == 'V': + y = pnext(d, 1)[0] + out.write("%g %g l\n" % (x, y)) + elif opcode == 'v': + y += pnext(d, 1)[0] + out.write("%g %g l\n" % (x, y)) + elif opcode == 'C': + x1, y1, xs, ys, x, y = pnext(d, 6) + out.write("%g %g %g %g %g %g c\n" % (x1, y1, xs, ys, x, y)) + elif opcode == 'c': + x1, y1, xs, ys, xf, yf = pnext(d, 6) + x1 += x; y1 += y + xs += x; ys += y + x += xf; y += yf + out.write("%g %g %g %g %g %g c\n" % (x1, y1, xs, ys, x, y)) + elif opcode == 'S' or opcode == 's': + x2, y2, xf, yf = pnext(d, 4) + if opcode == 's': + x2 += x; y2 += y + xf += x; yf += y + x1 = x + (x - xs); y1 = y + (y - ys) + out.write("%g %g %g %g %g %g c\n" % (x1, y1, x2, y2, xf, yf)) + xs, ys = x2, y2 + x, y = xf, yf + elif opcode == 'Q': + xs, ys, x, y = pnext(d, 4) + out.write("%g %g %g %g q\n" % (xs, ys, x, y)) + elif opcode == 'q': + xs, ys, xf, yf = pnext(d, 4) + xs += x; ys += y + x += xf; y += yf + out.write("%g %g %g %g q\n" % (xs, ys, x, y)) + elif opcode == 'T' or opcode == 't': + xf, yf = pnext(d, 2) + if opcode == 't': + xf += x; yf += y + x1 = x + (x - xs); y1 = y + (y - ys) + out.write("%g %g %g %g q\n" % (x1, y1, xf, yf)) + xs, ys = x1, y1 + x, y = xf, yf + elif opcode == 'A' or opcode == 'a': + rx, ry, phi, large_arc, sweep, x2, y2 = pnext(d, 7) + if opcode == 'a': + x2 += x; y2 += y + draw_arc(out, x, y, rx, ry, phi, large_arc, sweep, x2, y2) + x, y = x2, y2 + elif opcode in 'zZ': + out.write("h\n") + else: + sys.stderr.write("Unrecognised opcode: %s\n" % opcode) + +def parse_transformation(txt): + d = re.findall("[a-zA-Z]+\([^)]*\)", txt) + m = Matrix() + while d: + m1 = Matrix(d.pop(0)) + m = m * m1 + return m + +def get_gradientTransform(n): + if n.hasAttribute("gradientTransform"): + return parse_transformation(n.getAttribute("gradientTransform")) + return Matrix() + +def parse_transform(n): + if n.hasAttribute("transform"): + return parse_transformation(n.getAttribute("transform")) + return None + +# Convert from endpoint to center parameterization +# www.w3.org/TR/2003/REC-SVG11-20030114/implnote.html#ArcImplementationNotes +def draw_arc(out, x1, y1, rx, ry, phi, large_arc, sweep, x2, y2): + phi = math.pi * phi / 180.0 + cp = math.cos(phi); sp = math.sin(phi) + dx = .5 * (x1 - x2); dy = .5 * (y1 - y2) + x1p = cp * dx + sp * dy; y1p = -sp * dx + cp * dy + r2 = (((rx * ry)**2 - (rx * y1p)**2 - (ry * x1p)**2)/ + ((rx * y1p)**2 + (ry * x1p)**2)) + if r2 < 0: r2 = 0 + r = math.sqrt(r2) + if large_arc == sweep: + r = -r + cxp = r * rx * y1p / ry; cyp = -r * ry * x1p / rx + cx = cp * cxp - sp * cyp + .5 * (x1 + x2) + cy = sp * cxp + cp * cyp + .5 * (y1 + y2) + m = Matrix([rx, 0, 0, ry, 0, 0]) + m = Matrix([cp, sp, -sp, cp, cx, cy]) * m + if sweep == 0: + m = m * Matrix([1, 0, 0, -1, 0, 0]) + out.write("%s %g %g a\n" % (str(m), x2, y2)) + +# -------------------------------------------------------------------- + +class Matrix(object): + + # Default is identity matrix + def __init__(self, string = None): + self.values = [1, 0, 0, 1, 0, 0] + if not string or string == "": + return + if isinstance(string, list): + self.values = string + return + mat = re.match(r"([a-zA-Z]+)\(([^)]*)\)$", string) + if not mat: + sys.stderr.write("Unknown transform: %s\n" % string) + op = mat.group(1) + d = [float(x) for x in parse_list(mat.group(2))] + if op == "matrix": + self.values = d + elif op == "translate": + if len(d) == 1: d.append(0.0) + self.values = [1, 0, 0, 1, d[0], d[1]] + elif op == "scale": + if len(d) == 1: d.append(d[0]) + sx, sy = d + self.values = [sx, 0, 0, sy, 0, 0] + elif op == "rotate": + phi = math.pi * d[0] / 180.0 + self.values = [math.cos(phi), math.sin(phi), + -math.sin(phi), math.cos(phi), 0, 0] + elif op == "skewX": + tphi = math.tan(math.pi * d[0] / 180.0) + self.values = [1, 0, tphi, 1, 0, 0] + elif op == "skewY": + tphi = math.tan(math.pi * d[0] / 180.0) + self.values = [1, tphi, 0, 1, 0, 0] + else: + sys.stderr.write("Unknown transform: %s\n" % string) + + def __call__(self, other): + return (self.values[0]*other[0] + self.values[2]*other[1] + self.values[4], + self.values[1]*other[0] + self.values[3]*other[1] + self.values[5]) + + def inverse(self): + d = float(self.values[0]*self.values[3] - self.values[1]*self.values[2]) + return Matrix([self.values[3]/d, -self.values[1]/d, + -self.values[2]/d, self.values[0]/d, + (self.values[2]*self.values[5] - + self.values[3]*self.values[4])/d, + (self.values[1]*self.values[4] - + self.values[0]*self.values[5])/d]) + + def __mul__(self, other): + a, b, c, d, e, f = self.values + u, v, w, x, y, z = other.values + return Matrix([a*u + c*v, b*u + d*v, a*w + c*x, + b*w + d*x, a*y + c*z + e, b*y + d*z + f]) + + def __str__(self): + a, b, c, d, e, f = self.values + return "%g %g %g %g %g %g" % (a, b, c, d, e, f) + +# -------------------------------------------------------------------- + +class Svg(): + + def __init__(self, fname): + self.dom = xml.parse(fname) + attr = { } + for a in attribute_names: + attr[a] = None + self.attributes = [ attr ] + self.defs = { } + for n in self.dom.childNodes: + if n.nodeType == Node.ELEMENT_NODE and n.tagName == "svg": + if n.hasAttribute("viewBox"): + x, y, w, h = [float(x) for x in parse_list(n.getAttribute("viewBox"))] + self.width = w + self.height = h + self.origin = (x, y) + else: + self.width = parse_float(n.getAttribute("width")) + self.height = parse_float(n.getAttribute("height")) + self.origin = (0, 0) + self.root = n + return + +# -------------------------------------------------------------------- + + def parse_svg(self, outname): + self.out = open(outname, "w") + self.out.write('\n') + self.out.write('\n') + self.out.write('\n' % + svgtoipe_version) + self.out.write('\n') + self.out.write(('\n') % + (self.width, self.height, self.width, self.height)) + for t in range(10, 100, 10): + self.out.write('\n' % (t, t)) + # set SVG defaults + self.out.write('\n') + self.out.write('\n') + # collect definitions + for n in self.root.childNodes: + if n.nodeType != Node.ELEMENT_NODE: + continue + if hasattr(self, "def_" + n.tagName): + getattr(self, "def_" + n.tagName)(n) + # write definitions into stylesheet + if len(self.defs) > 0: + self.out.write('\n') + for k in self.defs: + if self.defs[k][0] == "linearGradient": + self.write_linear_gradient(k) + elif self.defs[k][0] == "radialGradient": + self.write_radial_gradient(k) + self.out.write('\n') + # start real data + self.out.write('\n') + m = Matrix([1, 0, 0, 1, 0, self.height / 2.0]) + m = m * Matrix([1, 0, 0, -1, 0, 0]) + m = m * Matrix([1, 0, 0, 1, + -self.origin[0], -(self.origin[1] + self.height / 2.0)]) + self.out.write('\n' % str(m)) + for n in self.root.childNodes: + if n.nodeType != Node.ELEMENT_NODE: + continue + if hasattr(self, "node_" + n.tagName): + getattr(self, "node_" + n.tagName)(n) + else: + sys.stderr.write("Unhandled node: %s\n" % n.tagName) + self.out.write('\n') + self.out.write('\n') + self.out.write('\n') + self.out.close() + +# -------------------------------------------------------------------- + + def write_linear_gradient(self, k): + typ, x1, x2, y1, y2, stops, matrix = self.defs[k] + self.out.write('\n' % (x1, y1, x2, y2)) + for s in stops: + offset, color = s + self.out.write(' \n' % + (offset, color[0], color[1], color[2])) + self.out.write('\n') + + def write_radial_gradient(self, k): + typ, cx, cy, r, fx, fy, stops, matrix = self.defs[k] + self.out.write('\n' % (fx, fy, 0, cx, cy, r)) + for s in stops: + offset, color = s + self.out.write(' \n' % + (offset, color[0], color[1], color[2])) + self.out.write('\n') + + def get_stops(self, n): + stops = [] + for m in n.childNodes: + if m.nodeType != Node.ELEMENT_NODE: + continue + if m.tagName != "stop": + continue # should not happen + offs = m.getAttribute("offset") + if offs.endswith("%"): + offs = float(offs[:-1]) / 100.0 + else: + offs = float(offs) + color = parse_color(m.getAttribute("stop-color")) + if m.hasAttribute("style"): + sdict = parse_style(m.getAttribute("style")) + if "stop-color" in sdict: + color = parse_color(sdict["stop-color"]) + stops.append((offs, color)) + if len(stops) == 0: + if n.hasAttribute("xlink:href"): + ref = n.getAttribute("xlink:href") + if ref.startswith("#") and ref[1:] in self.defs: + stops = self.defs[ref[1:]][5] + return stops + + def def_linearGradient(self, n): + #printAttributes(n) + kid = n.getAttribute("id") + x1 = 0; y1 = 0 + x2 = self.width; y2 = self.height + if n.hasAttribute("x1"): + s = n.getAttribute("x1") + if s.endswith("%"): + x1 = self.width * float(s[:-1]) / 100.0 + else: + x1 = parse_float(s) + if n.hasAttribute("x2"): + s = n.getAttribute("x2") + if s.endswith("%"): + x2 = self.width * float(s[:-1]) / 100.0 + else: + x2 = parse_float(s) + if n.hasAttribute("y1"): + s = n.getAttribute("y1") + if s.endswith("%"): + y1 = self.width * float(s[:-1]) / 100.0 + else: + y1 = parse_float(s) + if n.hasAttribute("y2"): + s = n.getAttribute("y2") + if s.endswith("%"): + y2 = self.width * float(s[:-1]) / 100.0 + else: + y2 = parse_float(s) + matrix = get_gradientTransform(n) + stops = self.get_stops(n) + self.defs[kid] = ("linearGradient", x1, x2, y1, y2, stops, matrix) + + def def_radialGradient(self, n): + #printAttributes(n) + kid = n.getAttribute("id") + cx = "50%"; cy = "50%"; r = "50%" + if n.hasAttribute("cx"): + cx = n.getAttribute("cx") + if cx.endswith("%"): + cx = self.width * float(cx[:-1]) / 100.0 + else: + cx = parse_float(cx) + if n.hasAttribute("cy"): + cy = n.getAttribute("cy") + if cy.endswith("%"): + cy = self.width * float(cy[:-1]) / 100.0 + else: + cy = parse_float(cy) + if n.hasAttribute("r"): + r = n.getAttribute("r") + if r.endswith("%"): + r = self.width * float(r[:-1]) / 100.0 + else: + r = parse_float(r) + if n.hasAttribute("fx"): + s = n.getAttribute("fx") + if s.endswith("%"): + fx = self.width * float(s[:-1]) / 100.0 + else: + fx = parse_float(s) + else: + fx = cx + if n.hasAttribute("fy"): + s = n.getAttribute("fy") + if s.endswith("%"): + fy = self.width * float(s[:-1]) / 100.0 + else: + fy = parse_float(s) + else: + fy = cy + matrix = get_gradientTransform(n) + stops = self.get_stops(n) + self.defs[kid] = ("radialGradient", cx, cy, r, fx, fy, stops, matrix) + + def def_clipPath(self, node): + kid = node.getAttribute("id") + # only a single path is implemented + for n in node.childNodes: + if n.nodeType != Node.ELEMENT_NODE or n.tagName != "path": + continue + m = parse_transform(n) + d = n.getAttribute("d") + output = cStringIO.StringIO() + parse_path(output, d) + path = output.getvalue() + output.close() + self.defs[kid] = ("clipPath", m, path) + return + + def def_g(self, group): + for n in group.childNodes: + if n.nodeType != Node.ELEMENT_NODE: + continue + if hasattr(self, "def_" + n.tagName): + getattr(self, "def_" + n.tagName)(n) + + def def_defs(self, node): + self.def_g(node) + +# -------------------------------------------------------------------- + + def parse_attributes(self, n): + pattr = self.attributes[-1] + attr = { } + for a in attribute_names: + if n.hasAttribute(a): + attr[a] = n.getAttribute(a) + else: + attr[a] = pattr[a] + if n.hasAttribute("style"): + sdict = parse_style(n.getAttribute("style")) + for a in attribute_names: + if a in sdict: + attr[a] = sdict[a] + return attr + + def write_pathattributes(self, a): + stroke = parse_color(a["stroke"]) + if stroke: + self.out.write(' stroke="%g %g %g"' % stroke) + fill = a["fill"] + if fill and fill.startswith("url("): + mat = re.match("url\(#([^)]+)\).*", fill) + if mat: + grad = mat.group(1) + if grad in self.defs and (self.defs[grad][0] == "linearGradient" or + self.defs[grad][0] == "radialGradient"): + self.out.write(' fill="1" gradient="g%s"' % grad) + else: + fill = parse_color(a["fill"]) + if fill: + self.out.write(' fill="%g %g %g"' % fill) + opacity = parse_opacity(a["opacity"]) + fill_opacity = parse_opacity(a["fill-opacity"]) + stroke_opacity = parse_opacity(a["stroke-opacity"]) + if fill and fill_opacity: + opacity = fill_opacity + if not fill and stroke and stroke_opacity: + opacity = stroke_opacity + if opacity and opacity != 100: + self.out.write(' opacity="%d%%"' % opacity) + stroke_width = parse_float(a["stroke-width"]) + if a["stroke-width"]: + self.out.write(' pen="%g"' % stroke_width) + if a["fill-rule"] == "nonzero": + self.out.write(' fillrule="wind"') + k = {"butt" : 0, "round" : 1, "square" : 2 } + if a["stroke-linecap"] in k: + self.out.write(' cap="%d"' % k[a["stroke-linecap"]]) + k = {"miter" : 0, "round" : 1, "bevel" : 2 } + if a["stroke-linejoin"] in k: + self.out.write(' join="%d"' % k[a["stroke-linejoin"]]) + dasharray = a["stroke-dasharray"] + dashoffset = a["stroke-dashoffset"] + if dasharray and dashoffset and dasharray != "none": + d = parse_list(dasharray) + off = parse_float(dashoffset) + self.out.write(' dash="[%s] %g"' % (" ".join(d), off)) + +# -------------------------------------------------------------------- + + def node_g(self, group): + # printAttributes(group) + attr = self.parse_attributes(group) + self.attributes.append(attr) + self.out.write('\n') + for n in group.childNodes: + if n.nodeType != Node.ELEMENT_NODE: + continue + if hasattr(self, "node_" + n.tagName): + getattr(self, "node_" + n.tagName)(n) + else: + sys.stderr.write("Unhandled node: %s\n" % n.tagName) + self.out.write('\n') + self.attributes.pop() + + def collect_text(self, root): + for n in root.childNodes: + if n.nodeType == Node.TEXT_NODE: + self.text += n.data + if n.nodeType != Node.ELEMENT_NODE: + continue + if n.tagName == "tspan": # recurse + self.collect_text(n) + + def node_text(self, t): + if not t.hasAttribute("x") or not t.hasAttribute("y"): + sys.stderr.write("Text without coordinates ignored\n") + return + x = float(t.getAttribute("x")) + y = float(t.getAttribute("y")) + attr = self.parse_attributes(t) + self.out.write('%s\n' % self.text.encode("UTF-8")) + + def node_image(self, node): + if not have_pil: + sys.stderr.write("No Python image library, ignored\n") + return + href = node.getAttribute("xlink:href") + if not href.startswith("data:image/png;base64,"): + sys.stderr.write("Image ignored, href = %s...\n" % href[:40]) + return + x = float(node.getAttribute("x")) + y = float(node.getAttribute("y")) + w = float(node.getAttribute("width")) + h = float(node.getAttribute("height")) + clipped = False + if node.hasAttribute("clip-path"): + mat = re.match("url\(#([^)]+)\).*", node.getAttribute("clip-path")) + if mat: + cp = mat.group(1) + if cp in self.defs and self.defs[cp][0] == "clipPath": + cp, m, path = self.defs[cp] + clipped = True + self.out.write('\n' % (str(m), path)) + self.out.write('\n' % str(m.inverse())) + self.out.write(' \n') + if True: + data = cStringIO.StringIO() + for pixel in image.getdata(): + data.write("%c%c%c" % pixel[:3]) + self.out.write(base64.b64encode(data.getvalue())) + data.close() + else: + count = 0 + for pixel in image.getdata(): + self.out.write("%02x%02x%02x" % pixel[:3]) + count += 1 + if count == 10: + self.out.write("\n") + count = 0 + fin.close() + self.out.write('\n') + if clipped: + self.out.write('\n\n') + + # handled in def pass + def node_linearGradient(self, n): + pass + + def node_radialGradient(self, n): + pass + + def node_rect(self, n): + attr = self.parse_attributes(n) + self.out.write('\n') + x = float(n.getAttribute("x")) + y = float(n.getAttribute("y")) + w = float(n.getAttribute("width")) + h = float(n.getAttribute("height")) + self.out.write("%g %g m %g %g l %g %g l %g %g l h\n" % + (x, y, x + w, y, x + w, y + h, x, y + h)) + self.out.write('\n') + + def node_circle(self, n): + self.out.write('\n') + cx = float(n.getAttribute("cx")) + cy = float(n.getAttribute("cy")) + r = float(n.getAttribute("r")) + self.out.write("%g 0 0 %g %g %g e\n" % (r, r, cx, cy)) + self.out.write('\n') + + def node_ellipse(self, n): + self.out.write('\n') + cx = 0 + cy = 0 + if n.hasAttribute("cx"): + cx = float(n.getAttribute("cx")) + if n.hasAttribute("cy"): + cy = float(n.getAttribute("cy")) + rx = float(n.getAttribute("rx")) + ry = float(n.getAttribute("ry")) + self.out.write("%g 0 0 %g %g %g e\n" % (rx, ry, cx, cy)) + self.out.write('\n') + + def node_line(self, n): + self.out.write('\n') + x1 = 0; y1 = 0; x2 = 0; y2 = 0 + if n.hasAttribute("x1"): + x1 = float(n.getAttribute("x1")) + if n.hasAttribute("y1"): + y1 = float(n.getAttribute("y1")) + if n.hasAttribute("x2"): + x2 = float(n.getAttribute("x2")) + if n.hasAttribute("y2"): + y2 = float(n.getAttribute("y2")) + self.out.write("%g %g m %g %g l\n" % (x1, y1, x2, y2)) + self.out.write('\n') + + def node_polyline(self, n): + self.polygon(n, closed=False) + + def node_polygon(self, n): + self.polygon(n, closed=True) + + def polygon(self, n, closed): + self.out.write('\n') + d = parse_list(n.getAttribute("points")) + op = "m" + while d: + x = float(d.pop(0)) + y = float(d.pop(0)) + self.out.write("%g %g %s\n" % (x, y, op)) + op = "l" + if closed: + self.out.write("h\n") + self.out.write('\n') + + def node_path(self, n): + self.out.write('\n') + d = n.getAttribute("d") + parse_path(self.out, d) + self.out.write('\n') + +# -------------------------------------------------------------------- + +def main(): + if len(sys.argv) != 2 and len(sys.argv) != 3: + sys.stderr.write("Usage: svgtoipe [ ]\n") + return + fname = sys.argv[1] + if len(sys.argv) > 2: + outname = sys.argv[2] + else: + if fname[-4:].lower() == ".svg": + outname = fname[:-4] + ".ipe" + else: + outname = fname + ".ipe" + svg = Svg(fname) + svg.parse_svg(outname) + +if __name__ == '__main__': + main() + +# -------------------------------------------------------------------- -- 2.34.1