-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathpysvgag.py
executable file
·134 lines (120 loc) · 4.13 KB
/
pysvgag.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#!/usr/bin/python
from xml.dom.minidom import parse;
from svg.path import parse_path;
from math import hypot;
from re import split;
from itertools import izip;
def f(svg, create, duration):
elements = []
for node in svg.childNodes:
try:
node.tagName; #only on Elements
elements.append(node);
except AttributeError:
pass #Text or Comment
if len(elements) > 0:
animate(elements, create, duration);
def animate(shapes, create, duration):
unit_time = float(duration) / len(shapes);
previous = None
for node in shapes:
current = _getId(node);
length = computeLength(node);
updateStyle(node, {'stroke-dasharray': str(length)});
child = create('animate');
previous = initializeAnimationNode(child, length, unit_time, current, previous);
node.appendChild(child);
node.setAttribute('visibility', 'hidden');
child = create('set');
initializeSetNode(child, previous);
node.appendChild(child);
def computeLength(shape):
if shape.tagName == 'path':
path = parse_path(shape.getAttribute('d'));
return path.length(); # this is by far is the program most costly instruction
if shape.tagName == 'line':
return hypot(float(shape.getAttribute('x2')) - float(shape.getAttribute('x1')),
float(shape.getAttribute('y2')) - float(shape.getAttribute('y1')));
if shape.tagName == 'polyline':
length = 0.0;
points = _parsePoints(shape);
px = py = None;
for x,y in points:
if px is not None:
length += hypot(x-px, y-py);
px, py = x, y;
return length;
if shape.tagName == 'polygon':
length = 0.0;
points = _parsePoints(shape);
px = py = fx = fy = None;
for x,y in points:
if fx is None:
fx, fy = x, y;
if px is not None:
length += hypot(x-px, y-py);
px, py = x, y;
length += hypot(fx-px, fy-py);
return length;
print("Unsupported shape \"%s\" will not be animated."%shape.tagName);
def _parsePoints(shape):
expr = ' '.join(shape.getAttribute('points').split(','));
coords = [float(n) for n in expr.split(' ') if len(n) > 0];
l = iter(coords);
return izip(l, l);
def updateStyle(node, style):
attrs = {};
result = node.getAttribute('style');
attrs = {}
if len(result) > 0:
attrs = dict(item.split(':') for item in result.split(';'));
attrs.update(style);
result = ';'.join(['%s:%s'%(k,v) for k,v in attrs.items()])
node.setAttribute('style', result);
_counter = 0;
def _getId(node):
identifier = node.getAttribute('id');
if len(identifier) is 0:
global _counter;
_counter += 1;
identifier = 'shape%d'%_counter;
node.setAttribute('id', identifier);
return identifier;
def initializeAnimationNode(animation, length, duration, pathId, previousAnimation):
identifier = '%s_animation'%pathId;
animation.setAttribute('id', identifier);
animation.setAttribute('attributeName', 'stroke-dashoffset');
animation.setAttribute('attributeType', 'XML');
animation.setAttribute('from', str(length));
animation.setAttribute('to', '0.0');
begin = '0s';
if (previousAnimation is not None):
begin = '%s.end'%previousAnimation;
animation.setAttribute('begin', begin); #TODO previous.end
animation.setAttribute('dur', '%ss'%duration);
return identifier;
def initializeSetNode(node, correspondingAnimation):
node.setAttribute('attributeName', 'visibility');
node.setAttribute('from', 'hidden');
node.setAttribute('to', 'visible');
node.setAttribute('begin', '%s.begin'%correspondingAnimation);
from argparse import ArgumentParser
if __name__ == '__main__':
parser = ArgumentParser(description="SVG Animator");
parser.add_argument('svg_file', help="SVG image input file");
parser.add_argument('-o', '--output', help="SVG image output file", default='pysvgag_output.svg');
parser.add_argument('-t', '--total-time', help="Animation total duration in seconds", default=3.0);
args = parser.parse_args();
svg = [];
try:
with open(args.svg_file) as source:
dom = parse(source);
svg = dom.getElementsByTagName('svg');
except:
exit("Error: %s seems not to be a valid SVG file."%args.svg_file);
if svg.length < 1:
exit("Error: %s seems not to be a valid SVG file."%args.svg_file);
for image in svg:
f(image, dom.createElement, args.total_time)
with open(args.output, 'wb') as f:
dom.writexml(f);