From ed0f70e3ed056fbe49d74dec2fc6048d23228949 Mon Sep 17 00:00:00 2001 From: Anand Chitipothu Date: Tue, 23 Mar 2021 07:11:22 +0000 Subject: [PATCH] Replaced drawSvg library with custom svg library. Even after so many attempts to fix drawSvg, couldn't get it to work with top-left corner as the origin. Replaced it with a custom library. --- community/lms/doctype/lms_sketch/livecode.py | 12 +- community/lms/doctype/lms_sketch/svg.py | 143 +++++++++++++++++++ requirements.txt | 1 - 3 files changed, 149 insertions(+), 7 deletions(-) create mode 100644 community/lms/doctype/lms_sketch/svg.py diff --git a/community/lms/doctype/lms_sketch/livecode.py b/community/lms/doctype/lms_sketch/livecode.py index 91879000..6815c269 100644 --- a/community/lms/doctype/lms_sketch/livecode.py +++ b/community/lms/doctype/lms_sketch/livecode.py @@ -2,7 +2,7 @@ """ import websocket import json -import drawSvg as draw +from .svg import SVG def livecode_to_svg(livecode_ws_url, code, *, timeout=1): """Renders the code as svg. @@ -22,7 +22,7 @@ def livecode_to_svg(livecode_ws_url, code, *, timeout=1): messages = _read_messages(ws) commands = [m['cmd'] for m in messages if m['msgtype'] == 'draw'] img = draw_image(commands) - return img.asSvg() + return img.tostring() def _read_messages(ws): messages = [] @@ -37,12 +37,12 @@ def _read_messages(ws): return messages def draw_image(commands): - img = draw.Drawing(300, 300, origin=(0, -300), fill='none', stroke='black') + img = SVG(width=300, height=300, fill='none', stroke='black') for c in commands: if c['function'] == 'circle': - img.append(draw.Circle(cx=c['x'], cy=c['y'], r=c['d']/2)) + img.circle(cx=c['x'], cy=c['y'], r=c['d']/2) elif c['function'] == 'line': - img.append(draw.Line(sx=c['x1'], sy=c['y1'], ex=c['x2'], ey=c['y2'])) + img.line(sx=c['x1'], sy=c['y1'], ex=c['x2'], ey=c['y2']) elif c['function'] == 'rect': - img.append(draw.Rectangle(x=c['x'], y=c['y'], width=c['w'], height=c['h'])) + img.rect(x=c['x'], y=c['y'], width=c['w'], height=c['h']) return img diff --git a/community/lms/doctype/lms_sketch/svg.py b/community/lms/doctype/lms_sketch/svg.py new file mode 100644 index 00000000..596be7ef --- /dev/null +++ b/community/lms/doctype/lms_sketch/svg.py @@ -0,0 +1,143 @@ +"""SVG rendering library. + +USAGE: + from svg import SVG + + svg = SVG(width=200, height=200) + svg.circle(cx=100, cy=200, r=50) + print(svg.tostring()) +""" +from xml.etree import ElementTree + +TAGNAMES = set([ + "circle", "ellipse", + "line", "path", "rect", "polygon", "polyline", + "text", "textPath", "title", + "marker", "defs", + "g" +]) + +class Node: + """SVG Node""" + def __init__(self, tag, **attrs): + self.tag = tag + self.attrs = dict((k.replace('_', '-'), str(v)) for k, v in attrs.items()) + self.children = [] + + def node(self, tag, **attrs): + n = Node(tag, **attrs) + self.children.append(n) + return n + + def apply(self, func): + """Applies a function to this node and + all the children recursively. + """ + func(self) + for n in self.children: + n.apply(func) + + def clone(self): + node = Node(self.tag, **self.attrs) + node.children = [n.clone() for n in self.children] + return node + + def add_node(self, node): + if not isinstance(node, Node): + node = Text(node) + self.children.append(node) + + def __getattr__(self, tag): + if tag not in TAGNAMES: + raise AttributeError(tag) + return lambda **attrs: self.node(tag, **attrs) + + def translate(self, x, y): + return self.g(transform="translate(%s, %s)" % (x, y)) + + def scale(self, *args): + return self.g(transform="scale(%s)" % ", ".join(str(a) for a in args)) + + def __repr__(self): + return "<%s .../>" % self.tag + + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + pass + + def build_tree(self, builder): + builder.start(self.tag, self.attrs) + for node in self.children: + node.build_tree(builder) + return builder.end(self.tag) + + def _indent(self, elem, level=0): + """Indent etree node for prettyprinting.""" + + i = "\n" + level*" " + if len(elem): + if not elem.text or not elem.text.strip(): + elem.text = i + " " + if not elem.tail or not elem.tail.strip(): + elem.tail = i + for elem in elem: + self._indent(elem, level+1) + if not elem.tail or not elem.tail.strip(): + elem.tail = i + else: + if level and (not elem.tail or not elem.tail.strip()): + elem.tail = i + + def save(self, filename, encoding='utf-8'): + f = open(filename, 'w') + f.write(self.tostring()) + f.close() + + def tostring(self, encoding='utf-8'): + builder = ElementTree.TreeBuilder() + self.build_tree(builder) + e = builder.close() + self._indent(e) + return ElementTree.tostring(e, encoding).decode(encoding) + +class Text(Node): + """Text Node + + >>> p = Node("p") + >>> p.add_node("hello, world!") + >>> p.tostring() + '

hello, world!

' + """ + def __init__(self, text): + Node.__init__(self, "__text__") + self._text = text + + def build_tree(self, builder): + builder.data(str(self._text)) + +class SVG(Node): + """ + >>> svg = SVG(width=200, height=200) + >>> svg.rect(x=0, y=0, width=200, height=200, fill="blue") + + >>> with svg.translate(-50, -50) as g: + ... g.rect(x=0, y=0, width=50, height=100, fill="red") + ... g.rect(x=50, y=0, width=50, height=100, fill="green") + + + >>> print(svg.tostring()) + + + + + + + + + """ + def __init__(self, **attrs): + attrs['xmlns'] = "http://www.w3.org/2000/svg" + Node.__init__(self, 'svg', **attrs) + diff --git a/requirements.txt b/requirements.txt index 6da7c51b..900c38db 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,2 @@ frappe websocket_client -drawSvg