summaryrefslogtreecommitdiffstats
path: root/share/extensions/ink2canvas_lib
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:24:48 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:24:48 +0000
commitcca66b9ec4e494c1d919bff0f71a820d8afab1fa (patch)
tree146f39ded1c938019e1ed42d30923c2ac9e86789 /share/extensions/ink2canvas_lib
parentInitial commit. (diff)
downloadinkscape-upstream.tar.xz
inkscape-upstream.zip
Adding upstream version 1.2.2.upstream/1.2.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'share/extensions/ink2canvas_lib')
-rw-r--r--share/extensions/ink2canvas_lib/__init__.py0
-rw-r--r--share/extensions/ink2canvas_lib/canvas.py200
-rw-r--r--share/extensions/ink2canvas_lib/svg.py326
3 files changed, 526 insertions, 0 deletions
diff --git a/share/extensions/ink2canvas_lib/__init__.py b/share/extensions/ink2canvas_lib/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/share/extensions/ink2canvas_lib/__init__.py
diff --git a/share/extensions/ink2canvas_lib/canvas.py b/share/extensions/ink2canvas_lib/canvas.py
new file mode 100644
index 0000000..9429420
--- /dev/null
+++ b/share/extensions/ink2canvas_lib/canvas.py
@@ -0,0 +1,200 @@
+# 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.
+#
+"""
+Canvas module for ink2canvas extension
+"""
+
+from textwrap import dedent
+
+from inkex import Color, Style
+
+# pylint: disable=too-many-public-methods, disable=missing-function-docstring
+
+
+class Canvas:
+ """Canvas API helper class"""
+
+ def __init__(self, parent, width, height, context="ctx"):
+ self.obj = context
+ self.code = [] # stores the code
+ self.style = Style()
+ self.style_cache = Style() # stores the previous style applied
+ self.parent = parent
+ self.width = width
+ self.height = height
+
+ def write(self, text):
+ self.code.append("\t" + text.replace("ctx", self.obj) + "\n")
+
+ def output(self):
+
+ html = """
+ <!DOCTYPE html>
+ <html>
+ <head>
+ <title>Inkscape Output</title>
+ </head>
+ <body>
+ <canvas id='canvas' width='%d' height='%d'></canvas>
+ <script>
+ var %s = document.getElementById("canvas").getContext("2d");
+ %s
+ </script>
+ </body>
+ </html>
+ """
+ return dedent(html) % (self.width, self.height, self.obj, "".join(self.code))
+
+ def equal_style(self, style, key):
+ """Checks if the last style used is the same or there's no style yet"""
+ if key in self.style_cache:
+ return True
+ if key not in style:
+ return True
+ return style[key] == self.style_cache[key]
+
+ def begin_path(self):
+ self.write("ctx.beginPath();")
+
+ def create_linear_gradient(self, href, x1, y1, x2, y2):
+ data = (href, x1, y1, x2, y2)
+ self.write(
+ "var %s = \
+ ctx.createLinearGradient(%f,%f,%f,%f);"
+ % data
+ )
+
+ def create_radial_gradient(self, href, cx1, cy1, rx, cx2, cy2, ry):
+ data = (href, cx1, cy1, rx, cx2, cy2, ry)
+ self.write(
+ "var %s = ctx.createRadialGradient\
+ (%f,%f,%f,%f,%f,%f);"
+ % data
+ )
+
+ def add_color_stop(self, href, pos, color):
+ self.write("%s.addColorStop(%f, %s);" % (href, pos, color))
+
+ @staticmethod
+ def get_color(rgb, alpha):
+ return "'{}'".format(str(Color(rgb).to_rgba(alpha)))
+
+ def set_gradient(self, href):
+ """
+ for stop in gstops:
+ style = simplestyle.parseStyle(stop.get("style"))
+ stop_color = style["stop-color"]
+ opacity = style["stop-opacity"]
+ color = self.get_color(stop_color, opacity)
+ pos = float(stop.get("offset"))
+ self.add_color_stop(href, pos, color)
+ """
+ return None # href
+
+ def set_opacity(self, value):
+ self.write("ctx.globalAlpha = %.1f;" % float(value))
+
+ def set_fill(self, value):
+ alpha = self.style("fill-opacity")
+ if not value.startswith("url("):
+ fill = self.get_color(value, alpha)
+ self.write("ctx.fillStyle = %s;" % fill)
+
+ def set_stroke(self, value):
+ alpha = self.style("stroke-opacity")
+ self.write("ctx.strokeStyle = %s;" % self.get_color(value, alpha))
+
+ def set_stroke_width(self, value):
+ self.write("ctx.lineWidth = %f;" % self.parent.svg.unittouu(value))
+
+ def set_stroke_linecap(self, value):
+ self.write("ctx.lineCap = '%s';" % value)
+
+ def set_stroke_linejoin(self, value):
+ self.write("ctx.lineJoin = '%s';" % value)
+
+ def set_stroke_miterlimit(self, value):
+ self.write("ctx.miterLimit = %s;" % value)
+
+ def set_font(self, value):
+ self.write('ctx.font = "%s";' % value)
+
+ def move_to(self, x, y):
+ self.write("ctx.moveTo(%f, %f);" % (x, y))
+
+ def line_to(self, x, y):
+ self.write("ctx.lineTo(%f, %f);" % (x, y))
+
+ def close_path(self):
+ self.write("ctx.closePath();")
+
+ def quadratic_curve_to(self, cpx, cpy, x, y):
+ data = (cpx, cpy, x, y)
+ self.write("ctx.quadraticCurveTo(%f, %f, %f, %f);" % data)
+
+ def bezier_curve_to(self, x1, y1, x2, y2, x, y):
+ data = (x1, y1, x2, y2, x, y)
+ self.write("ctx.bezierCurveTo(%f, %f, %f, %f, %f, %f);" % data)
+
+ def rect(self, x, y, width, height, rx=0, ry=0):
+ if rx or ry:
+ # rounded rectangle, starts top-left anticlockwise
+ self.move_to(x, y + ry)
+ self.line_to(x, y + height - ry)
+ self.quadratic_curve_to(x, y + height, x + rx, y + height)
+ self.line_to(x + width - rx, y + height)
+ self.quadratic_curve_to(x + width, y + height, x + width, y + height - ry)
+ self.line_to(x + width, y + ry)
+ self.quadratic_curve_to(x + width, y, x + width - rx, y)
+ self.line_to(x + rx, y)
+ self.quadratic_curve_to(x, y, x, y + ry)
+ else:
+ self.write("ctx.rect(%f, %f, %f, %f);" % (x, y, width, height))
+
+ def arc(self, x, y, r, a1, a2, flag):
+ data = (x, y, r, a1, a2, flag)
+ self.write("ctx.arc(%f, %f, %f, %f, %.8f, %d);" % data)
+
+ def fill_text(self, text, x, y):
+ self.write('ctx.fillText("%s", %f, %f);' % (text, x, y))
+
+ def translate(self, cx, cy):
+ self.write("ctx.translate(%f, %f);" % (cx, cy))
+
+ def rotate(self, angle):
+ self.write("ctx.rotate(%f);" % angle)
+
+ def scale(self, rx, ry):
+ self.write("ctx.scale(%f, %f);" % (rx, ry))
+
+ def transform(self, m11, m12, m21, m22, dx, dy):
+ data = (m11, m12, m21, m22, dx, dy)
+ self.write("ctx.transform(%f, %f, %f, %f, %f, %f);" % data)
+
+ def save(self):
+ self.write("ctx.save();")
+
+ def restore(self):
+ self.write("ctx.restore();")
+
+ def finish_path(self):
+ if self.style("fill") is not None:
+ self.write("ctx.fill();")
+ if self.style("stroke") is not None:
+ self.write("ctx.stroke();")
diff --git a/share/extensions/ink2canvas_lib/svg.py b/share/extensions/ink2canvas_lib/svg.py
new file mode 100644
index 0000000..3d3fafb
--- /dev/null
+++ b/share/extensions/ink2canvas_lib/svg.py
@@ -0,0 +1,326 @@
+# 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 math
+
+import inkex
+from inkex.localization import inkex_gettext as _
+
+
+# pylint: disable=missing-function-docstring, missing-class-docstring
+# pylint: disable=too-few-public-methods
+
+
+class Element:
+ """Base Element"""
+
+ def __init__(self, node):
+ self.node = node
+
+ def attr(self, val):
+ """Get attribute"""
+ try:
+ attr = float(self.node.get(val))
+ except (ValueError, TypeError, AttributeError):
+ attr = self.node.get(val)
+ return attr
+
+
+class GradientDef(Element):
+ def __init__(self, node, stops):
+ super().__init__(node)
+ self.stops = stops
+
+
+class LinearGradientDef(GradientDef):
+ def get_data(self):
+ # pylint: disable=unused-variable
+ x1 = self.attr("x1")
+ y1 = self.attr("y1")
+ x2 = self.attr("x2")
+ y2 = self.attr("y2")
+ # self.create_linear_gradient(href, x1, y1, x2, y2)
+
+ def draw(self):
+ pass
+
+
+class RadialGradientDef(GradientDef):
+ def get_data(self):
+ # pylint: disable=unused-variable
+ cx = self.attr("cx")
+ cy = self.attr("cy")
+ r = self.attr("r")
+ # self.create_radial_gradient(href, cx, cy, r, cx, cy, r)
+
+ def draw(self):
+ pass
+
+
+class AbstractShape(Element):
+ def __init__(self, command, node, ctx):
+ super().__init__(node)
+ self.command = command
+ self.ctx = ctx
+ self.gradient = None
+
+ def get_data(self): # pylint: disable=no-self-use
+ return None
+
+ def get_style(self):
+ return self.node.specified_style()
+
+ def set_style(self, style):
+ """Translates style properties names into method calls"""
+ self.ctx.style = style
+ for key in style:
+ method = "set_" + "_".join(key.split("-"))
+ if hasattr(self.ctx, method) and style[key] != "none":
+ getattr(self.ctx, method)(style[key])
+ # saves style to compare in next iteration
+ if (
+ hasattr(self.ctx, "style_cache")
+ and "opacity" not in style
+ and self.ctx.style_cache("opacity") != style("opacity")
+ ):
+ self.ctx.set_opacity(
+ style("opacity")
+ ) # opacity is kept in memory, need to reset
+ 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()
+ fill = style("fill")
+ return fill is not None and isinstance(fill, inkex.Gradient)
+
+ def get_gradient_href(self):
+ style = self.get_style()
+ return style("fill").get_id()
+
+ 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() # pylint: disable=assignment-from-none
+ style = self.get_style()
+ self.ctx.begin_path()
+ 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.finish_path()
+
+ def end(self):
+ if self.has_transform() or self.has_clip():
+ self.ctx.restore()
+
+
+class G(AbstractShape): # pylint: disable=invalid-name
+ 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")
+ width = self.attr("width")
+ height = self.attr("height")
+ rx = self.attr("rx") or 0
+ ry = self.attr("ry") or 0
+ return x, y, width, height, rx, ry
+
+
+class Circle(AbstractShape):
+ def __init__(self, command, node, ctx):
+ AbstractShape.__init__(self, command, node, ctx)
+ self.command = "arc"
+
+ def get_data(self):
+
+ 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):
+ cx, cy, rx, ry = self.get_data()
+ style = self.get_style()
+ self.ctx.begin_path()
+ 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.move_to(cx, cy - ry)
+ self.ctx.bezier_curve_to(
+ cx + (kappa * rx), cy - ry, cx + rx, cy - (kappa * ry), cx + rx, cy
+ )
+ self.ctx.bezier_curve_to(
+ cx + rx, cy + (kappa * ry), cx + (kappa * rx), cy + ry, cx, cy + ry
+ )
+ self.ctx.bezier_curve_to(
+ cx - (kappa * rx), cy + ry, cx - rx, cy + (kappa * ry), cx - rx, cy
+ )
+ self.ctx.bezier_curve_to(
+ cx - rx, cy - (kappa * ry), cx - (kappa * rx), cy - ry, cx, cy - ry
+ )
+ self.ctx.finish_path()
+
+
+class Path(AbstractShape):
+ def __init__(self, command, node, ctx):
+ AbstractShape.__init__(self, command, node, ctx)
+ self.current_position = 0, 0
+
+ def path_move_to(self, data):
+ self.ctx.move_to(data[0], data[1])
+ self.current_position = data[0], data[1]
+
+ def path_line_to(self, data):
+ self.ctx.line_to(data[0], data[1])
+ self.current_position = data[0], data[1]
+
+ def path_curve_to(self, data):
+ x1, y1, x2, y2 = data[0], data[1], data[2], data[3]
+ x, y = data[4], data[5]
+ self.ctx.bezier_curve_to(x1, y1, x2, y2, x, y)
+ self.current_position = x, y
+
+ def path_close(self, data): # pylint: disable=unused-argument
+ self.ctx.close_path()
+
+ def draw(self):
+ """Gets the node type and calls the given method"""
+ style = self.get_style()
+ self.ctx.begin_path()
+ 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.path_move_to,
+ "L": self.path_line_to,
+ "C": self.path_curve_to,
+ "Z": self.path_close,
+ }
+ # 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.finish_path()
+
+
+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 point in points: # creating path command similar
+ point = list(map(float, point))
+ comm.append(["L", point])
+ 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 tspan is not None:
+ return tspan.text
+ for ts_cur in tspan:
+ return ts_cur.text + self.text_helper(ts_cur) + ts_cur.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.set_font(" ".join(text))
+
+ def get_data(self):
+ x = self.attr("x")
+ y = self.attr("y")
+ return x, y
+
+ def draw(self):
+ for tspan in self.node:
+ if isinstance(tspan, inkex.TextPath):
+ raise ValueError(_("TextPath elements are not supported"))
+ 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)
+ cur_x = float(tspan.get("x").split()[0])
+ cur_y = float(tspan.get("y").split()[0])
+ self.ctx.fill_text(text, cur_x, cur_y)