diff options
Diffstat (limited to '')
-rw-r--r-- | share/extensions/ink2canvas_lib/svg.py | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/share/extensions/ink2canvas_lib/svg.py b/share/extensions/ink2canvas_lib/svg.py new file mode 100644 index 0000000..75796fc --- /dev/null +++ b/share/extensions/ink2canvas_lib/svg.py @@ -0,0 +1,293 @@ +# coding=utf-8 +# +# Copyright (C) 2011 Karlisson Bezerra <contact@hacktoon.com> +# +# This program 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 2 of the License, or +# (at your option) any later version. +# +# This program 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 this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +""" +Element parsing and context for ink2canvas extensions +""" + +from __future__ import unicode_literals + +import inkex + +class Element(object): + """Base Element""" + def __init__(self, node): + self.node = node + + def attr(self, val): + """Get attribute""" + try: + attr = float(self.node.get(val)) + except: + attr = self.node.get(val) + return attr + + +class GradientDef(Element): + def __init__(self, node, stops): + self.node = node + self.stops = stops + + +class LinearGradientDef(GradientDef): + def get_data(self): + x1 = self.attr("x1") + y1 = self.attr("y1") + x2 = self.attr("x2") + y2 = self.attr("y2") + # self.createLinearGradient(href, x1, y1, x2, y2) + + def draw(self): + pass + + +class RadialGradientDef(GradientDef): + def get_data(self): + cx = self.attr("cx") + cy = self.attr("cy") + r = self.attr("r") + # self.createRadialGradient(href, cx, cy, r, cx, cy, r) + + def draw(self): + pass + + +class AbstractShape(Element): + def __init__(self, command, node, ctx): + self.node = node + self.command = command + self.ctx = ctx + + def get_data(self): + return + + def get_style(self): + return self.node.style + + def set_style(self, style): + """Translates style properties names into method calls""" + self.ctx.style = style + for key in style: + tmp_list = [s.capitalize() for s in key.split("-")] + method = "set" + "".join(tmp_list) + if hasattr(self.ctx, method) and style[key] != "none": + getattr(self.ctx, method)(style[key]) + # saves style to compare in next iteration + self.ctx.style_cache = style + + def has_transform(self): + return bool(self.attr("transform")) + + def get_transform(self): + return self.node.transform.to_hexad() + + def has_gradient(self): + style = self.get_style() + if "fill" in style: + fill = style["fill"] + return fill.startswith("url(#linear") or fill.startswith("url(#radial") + return False + + def get_gradient_href(self): + style = self.get_style() + if "fill" in style: + return style["fill"][5:-1] + return + + def has_clip(self): + return bool(self.attr("clip-path")) + + def start(self, gradient): + self.gradient = gradient + self.ctx.write("\n// #%s" % self.attr("id")) + if self.has_transform() or self.has_clip(): + self.ctx.save() + + def draw(self): + data = self.get_data() + style = self.get_style() + self.ctx.beginPath() + if self.has_transform(): + trans_matrix = self.get_transform() + self.ctx.transform(*trans_matrix) # unpacks argument list + if self.has_gradient(): + self.gradient.draw() + self.set_style(style) + # unpacks "data" in parameters to given method + getattr(self.ctx, self.command)(*data) + self.ctx.closePath() + + def end(self): + if self.has_transform() or self.has_clip(): + self.ctx.restore() + + +class G(AbstractShape): + def draw(self): + # get layer label, if exists + if self.has_transform(): + trans_matrix = self.get_transform() + self.ctx.transform(*trans_matrix) + + +class Rect(AbstractShape): + def get_data(self): + x = self.attr("x") + y = self.attr("y") + w = self.attr("width") + h = self.attr("height") + rx = self.attr("rx") or 0 + ry = self.attr("ry") or 0 + return x, y, w, h, rx, ry + + +class Circle(AbstractShape): + def __init__(self, command, node, ctx): + AbstractShape.__init__(self, command, node, ctx) + self.command = "arc" + + def get_data(self): + import math + cx = self.attr("cx") + cy = self.attr("cy") + r = self.attr("r") + return cx, cy, r, 0, math.pi * 2, True + + +class Ellipse(AbstractShape): + def get_data(self): + cx = self.attr("cx") + cy = self.attr("cy") + rx = self.attr("rx") + ry = self.attr("ry") + return cx, cy, rx, ry + + def draw(self): + import math + cx, cy, rx, ry = self.get_data() + style = self.get_style() + self.ctx.beginPath() + if self.has_transform(): + trans_matrix = self.get_transform() + self.ctx.transform(*trans_matrix) # unpacks argument list + self.set_style(style) + + KAPPA = 4 * ((math.sqrt(2) - 1) / 3) + self.ctx.moveTo(cx, cy - ry) + self.ctx.bezierCurveTo(cx + (KAPPA * rx), cy - ry, cx + rx, cy - (KAPPA * ry), cx + rx, cy) + self.ctx.bezierCurveTo(cx + rx, cy + (KAPPA * ry), cx + (KAPPA * rx), cy + ry, cx, cy + ry) + self.ctx.bezierCurveTo(cx - (KAPPA * rx), cy + ry, cx - rx, cy + (KAPPA * ry), cx - rx, cy) + self.ctx.bezierCurveTo(cx - rx, cy - (KAPPA * ry), cx - (KAPPA * rx), cy - ry, cx, cy - ry) + self.ctx.closePath() + + +class Path(AbstractShape): + def pathMoveTo(self, data): + self.ctx.moveTo(data[0], data[1]) + self.currentPosition = data[0], data[1] + + def pathLineTo(self, data): + self.ctx.lineTo(data[0], data[1]) + self.currentPosition = data[0], data[1] + + def pathCurveTo(self, data): + x1, y1, x2, y2 = data[0], data[1], data[2], data[3] + x, y = data[4], data[5] + self.ctx.bezierCurveTo(x1, y1, x2, y2, x, y) + self.currentPosition = x, y + + def draw(self): + """Gets the node type and calls the given method""" + style = self.get_style() + self.ctx.beginPath() + if self.has_transform(): + trans_matrix = self.get_transform() + self.ctx.transform(*trans_matrix) # unpacks argument list + self.set_style(style) + + # Draws path commands + path_command = {"M": self.pathMoveTo, + "L": self.pathLineTo, + "C": self.pathCurveTo} + # Make sure we only have Lines and curves (no arcs etc) + for comm, data in self.node.path.to_superpath().to_path().to_arrays(): + if comm in path_command: + path_command[comm](data) + + self.ctx.closePath() + + +class Line(Path): + def get_data(self): + x1 = self.attr("x1") + y1 = self.attr("y1") + x2 = self.attr("x2") + y2 = self.attr("y2") + return ("M", (x1, y1)), ("L", (x2, y2)) + + +class Polygon(Path): + def get_data(self): + points = self.attr("points").strip().split(" ") + points = map(lambda x: x.split(","), points) + comm = [] + for pt in points: # creating path command similar + pt = list(map(float, pt)) + comm.append(["L", pt]) + comm[0][0] = "M" # first command must be a 'M' => moveTo + return comm + + +class Polyline(Polygon): + pass + + +class Text(AbstractShape): + def text_helper(self, tspan): + if not len(tspan): + return tspan.text + for ts in tspan: + return ts.text + self.text_helper(ts) + ts.tail + + def set_text_style(self, style): + keys = ("font-style", "font-weight", "font-size", "font-family") + text = [] + for key in keys: + if key in style: + text.append(style[key]) + self.ctx.setFont(" ".join(text)) + + def get_data(self): + x = self.attr("x") + y = self.attr("y") + return x, y + + def draw(self): + x, y = self.get_data() + style = self.get_style() + if self.has_transform(): + trans_matrix = self.get_transform() + self.ctx.transform(*trans_matrix) # unpacks argument list + self.set_style(style) + self.set_text_style(style) + + for tspan in self.node: + text = self.text_helper(tspan) + _x = float(tspan.get("x").split()[0]) + _y = float(tspan.get("y").split()[0]) + self.ctx.fillText(text, _x, _y) |