From: Marcel Date: Tue, 10 May 2016 07:41:42 +0000 (+0200) Subject: moved to tools X-Git-Url: https://i11git.iti.kit.edu/anon-gitweb/?p=Misc%2Fipe.git;a=commitdiff_plain;h=24f14753f7e282d003769de540eff1b7121497a7 moved to tools --- diff --git a/pdftoipe.sh b/pdftoipe.sh deleted file mode 100755 index 16f641a..0000000 --- a/pdftoipe.sh +++ /dev/null @@ -1,5 +0,0 @@ -# !/bin/bash -echo $1 -inkscape --verb=ObjectToPath --select=EditSelectAll --export-plain-svg=/tmp/$1.svg $1 -svgtoipe.py /tmp/$1.svg $2 -rm /tmp/$1.svg diff --git a/svgtoipe.py b/svgtoipe.py deleted file mode 100755 index 45f3e98..0000000 --- a/svgtoipe.py +++ /dev/null @@ -1,865 +0,0 @@ -#!/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() - -# -------------------------------------------------------------------- diff --git a/tools/pdftoipe.sh b/tools/pdftoipe.sh new file mode 100755 index 0000000..5dc732b --- /dev/null +++ b/tools/pdftoipe.sh @@ -0,0 +1,5 @@ +# !/bin/bash +echo $1 +inkscape --verb=ObjectToPath --select=EditSelectAll --export-plain-svg=/tmp/$1.svg $1 +./svgtoipe.py /tmp/$1.svg $2 +rm /tmp/$1.svg diff --git a/tools/svgtoipe.py b/tools/svgtoipe.py new file mode 100755 index 0000000..45f3e98 --- /dev/null +++ b/tools/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() + +# --------------------------------------------------------------------