Created
August 24, 2012 22:35
-
-
Save tamask/3456543 to your computer and use it in GitHub Desktop.
SVG to Raphael.js converter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import re | |
import sys | |
import json | |
import math | |
from lxml import etree | |
# TODO | |
# - text nodes | |
# - image nodes | |
# - transforms | |
def ws(text): | |
return re.sub('\s+', ' ', text) | |
class Raphael(object): | |
default_svg_attr = { | |
'fill': '#000', | |
'stroke': 0, | |
} | |
def __init__(self, dom): | |
self.dom = dom | |
self.index = {} | |
def get_attr(self, node): | |
attr = {} | |
for key, value in node.attrib.items(): | |
key = key[key.find('}') + 1:] | |
if value.startswith('url(#'): | |
name = value[5:-1] | |
if name in self.index: | |
value = self.index[name]['serialized'] | |
attr[key] = value | |
return attr | |
def get_json(self, obj): | |
return json.dumps(obj, separators=(',',':')) | |
def output(self, out): | |
self.index = {} | |
root = self.dom.getroot() | |
out.write('{') | |
if 'width' in root.attrib: | |
out.write('"width":"%s",' % root.attrib['width']) | |
if 'height' in root.attrib: | |
out.write('"height":"%s",' % root.attrib['height']) | |
out.write('"fn":') | |
out.write('(function(p){') | |
out.write('var x={};') | |
out.write('var a=[];') | |
out.write('var e=p.set();') | |
self.output_attr(out, self.default_svg_attr) | |
out.write('e.push(') | |
i = 0 | |
for node in root.iterchildren(): | |
output = self.output_node(out, node) | |
if output and i < len(root) - 1: | |
out.write(',') | |
i += 1 | |
out.write(');') | |
out.write('for(var i=0;i<a.length;i++)') | |
out.write('a[i]();') | |
out.write('return {"set":e,"index":x}') | |
out.write('})') | |
out.write('}') | |
def output_attr(self, out, attr): | |
attr = attr.copy() | |
if attr: | |
node_id = attr.pop('id', None) | |
if node_id: | |
name = self.get_json(node_id) | |
out.write('x[%s]=e;' % name) | |
out.write('e.node.id=%s;' % name) | |
out.write('a.push(function(){') | |
out.write('e.attr(') | |
out.write(self.get_json(attr)) | |
out.write(')') | |
out.write('});') | |
def output_node(self, out, node): | |
tag = node.tag[node.tag.find('}') + 1:] | |
attname = tag.replace('-', '_').lower() | |
try: | |
output_fn = getattr(self, 'output_%s' % attname) | |
except AttributeError: | |
input_fn = getattr(self, 'input_%s' % attname) | |
input_fn(node) | |
return False | |
else: | |
out.write('(function(){') | |
output_fn(out, node) | |
out.write('return e') | |
out.write('})()') | |
return True | |
def output_g(self, out, node): | |
attr = self.get_attr(node) | |
attr.pop('id', None) | |
out.write('var e=p.set();') | |
out.write('e.push(') | |
i = 0 | |
for child in node.iterchildren(): | |
output = self.output_node(out, child) | |
if output and i < len(node) - 1: | |
out.write(',') | |
i += 1 | |
out.write(');') | |
self.output_attr(out, attr) | |
def output_path(self, out, node): | |
attr = self.get_attr(node) | |
attr.pop('d', None) | |
out.write('var e=p.path(') | |
out.write(self.get_json(ws(node.attrib['d']))) | |
out.write(');') | |
self.output_attr(out, attr) | |
def output_line(self, out, node): | |
attr = self.get_attr(node) | |
x1 = attr.pop('x1') | |
y1 = attr.pop('y1') | |
x2 = attr.pop('x2') | |
y2 = attr.pop('y2') | |
path = 'M%s,%sL%s,%s' % (x1, y1, x2, y2) | |
out.write('var e=p.path(') | |
out.write(self.get_json(path)) | |
out.write(');') | |
self.output_attr(out, attr) | |
def output_polygon(self, out, node): | |
attr = self.get_attr(node) | |
points = attr.pop('points', '').split() | |
if points: | |
o = ['M'] + points[:1] + ['L'] + points[1:] + ['Z'] | |
path = ' '.join(o) | |
out.write('var e=p.path(') | |
out.write(self.get_json(path)) | |
out.write(');') | |
self.output_attr(out, attr) | |
def output_rect(self, out, node): | |
attr = self.get_attr(node) | |
out.write('var e=p.rect();') | |
self.output_attr(out, attr) | |
def output_circle(self, out, node): | |
attr = self.get_attr(node) | |
out.write('var e=p.circle();') | |
self.output_attr(out, attr) | |
def output_ellipse(self, out, node): | |
attr = self.get_attr(node) | |
out.write('var e=p.ellipse();') | |
self.output_attr(out, attr) | |
def input_lineargradient(self, node): | |
name = node.attrib['id'] | |
x1 = float(node.attrib['x1']) | |
y1 = float(node.attrib['y1']) | |
x2 = float(node.attrib['x2']) | |
y2 = float(node.attrib['y2']) | |
angle = -math.degrees(math.atan2(y2 - y1, x2 - x1)) | |
stops = [] | |
for s in node.iterchildren(): | |
stop = {} | |
if 'stop-color' in s.attrib: | |
stop['stop-color'] = s.attrib['stop-color'] | |
if 'stop-opacity' in s.attrib: | |
stop['stop-opacity'] = s.attrib['stop-opacity'] | |
if 'style' in s.attrib: | |
token = s.attrib['style'] | |
for param in token.split(';'): | |
param = param.strip() | |
if ':' in param: | |
k, v = param.split(':', 1) | |
stop[k.strip()] = v.strip() | |
stop['offset'] = float(s.attrib['offset']) | |
stops.append(stop) | |
if angle < 0 or angle > 90: | |
angle = 180 - abs(angle) | |
for stop in stops: | |
stop['offset'] = 1 - stop['offset'] | |
stops.reverse() | |
serialized = ['%.1f' % abs(angle)] | |
for stop in stops: | |
token = '%s:%i' % ( | |
stop['stop-color'], | |
stop['offset'] * 100) | |
serialized.append(token) | |
serialized = '-'.join(serialized) | |
self.index[name] = { | |
'type': 'linear', | |
'node': node, | |
'x1': x1, 'y1': y1, | |
'x2': x2, 'y2': y2, | |
'stops': stops, | |
'serialized': serialized, | |
} | |
def input_radialgradient(self, node): | |
name = node.attrib['id'] | |
cx = float(node.attrib['cx']) | |
cy = float(node.attrib['cy']) | |
r = float(node.attrib['r']) | |
stops = [] | |
for s in node.iterchildren(): | |
stop = {} | |
if 'stop-color' in s.attrib: | |
stop['stop-color'] = s.attrib['stop-color'] | |
if 'stop-opacity' in s.attrib: | |
stop['stop-opacity'] = s.attrib['stop-opacity'] | |
if 'style' in s.attrib: | |
token = s.attrib['style'] | |
for param in token.split(';'): | |
param = param.strip() | |
if ':' in param: | |
k, v = param.split(':', 1) | |
stop[k.strip()] = v.strip() | |
stop['offset'] = float(s.attrib['offset']) | |
stops.append(stop) | |
serialized = [] | |
for stop in stops: | |
token = '%s:%i' % ( | |
stop['stop-color'], | |
stop['offset'] * 100) | |
serialized.append(token) | |
serialized = 'r' + '-'.join(serialized) | |
self.index[name] = { | |
'type': 'radial', | |
'node': node, | |
'cx': cx, 'cy': cy, 'r': r, | |
'stops': stops, | |
'serialized': serialized, | |
} | |
def main(argv, out): | |
out.write('{') | |
i = 0 | |
for name in argv: | |
out.write(json.dumps(name)) | |
out.write(':') | |
with open(name) as fp: | |
Raphael(etree.parse(fp)).output(out) | |
if i < len(argv) - 1: | |
out.write(',') | |
i += 1 | |
out.write('}') | |
if __name__ == '__main__': | |
main(sys.argv[1:], sys.stdout) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment