Skip to content

Instantly share code, notes, and snippets.

@tamask
Created August 24, 2012 22:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tamask/3456543 to your computer and use it in GitHub Desktop.
Save tamask/3456543 to your computer and use it in GitHub Desktop.
SVG to Raphael.js converter
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