2 * Raphael.Export https://github.com/ElbertF/Raphael.Export
4 * Licensed under the MIT license:
5 * http://www.opensource.org/licenses/mit-license.php
9 importz = imports['seed/importz.js'].importz;
10 Raphael = importz('Raphael');
14 * Escapes string for XML interpolation
15 * @param value string or number value to escape
16 * @returns string escaped
18 function escapeXML(s) {
19 if (typeof s === 'number')
30 for (var entity in replace) {
31 s = s.replace(new RegExp(entity, 'g'), '&' + replace[entity] + ';');
38 * Generic map function
39 * @param iterable the array or object to be mapped
40 * @param callback the callback function(element, key)
43 function map(iterable, callback) {
44 var mapped = new Array;
46 for (var i in iterable) {
47 if (iterable.hasOwnProperty(i)) {
48 var value = callback.call(this, iterable[i], i);
59 * Generic reduce function
60 * @param iterable array or object to be reduced
61 * @param callback the callback function(initial, element, i)
62 * @param initial the initial value
63 * @return the reduced value
65 function reduce(iterable, callback, initial) {
66 for (var i in iterable) {
67 if (iterable.hasOwnProperty(i)) {
68 initial = callback.call(this, initial, iterable[i], i);
76 * Utility method for creating a tag
77 * @param name the tag name, e.g., 'text'
78 * @param attrs the attribute string, e.g., name1="val1" name2="val2"
79 * or attribute map, e.g., { name1 : 'val1', name2 : 'val2' }
80 * @param content the content string inside the tag
81 * @returns string of the tag
83 function tag(name, attrs, matrix, content) {
84 if (typeof content === 'undefined' || content === null) {
88 if (typeof attrs === 'object') {
89 attrs = map(attrs, function (element, name) {
90 if (name === 'transform')
93 return name + '="' + escapeXML(element) + '"';
97 return '<' + name + (matrix ? ' transform="matrix(' + matrix.toString().replace(/^matrix\(|\)$/g, '') + ')" ' : ' ') + attrs + '>' +
99 '</' + name + '>' + "\n";
103 * @return object the style object
105 function extractStyle(node) {
106 //Roo.log(JSON.stringify(style));
109 family: typeof node.attrs['font-family'] === 'undefined' ? null : node.attrs['font-family'],
110 size: typeof node.attrs['font-size'] === 'undefined' ? null : (node.attrs['font-size']),
111 weight: typeof node.attrs['font-weight'] === 'undefined' ? null : (node.attrs['font-weight']),
112 anchor: typeof node.attrs['text-anchor'] === 'undefined' ? null : node.attrs['text-anchor']
118 * @param style object from style()
121 function styleToString(style) {
122 // TODO figure out what is 'normal'
123 //Roo.log(JSON.stringify(style));
125 'font-family:' + style.font.family,
127 'font-stretch:normal',
128 'font-variant:normal'
130 if (style.font.size !== null) {
131 r.push('font-size: ' + style.font.size + 'px')
133 if (style.font.weight !== null) {
134 r.push('font-weight: ' + style.font.weight);
137 r.push('font-weight: normal');
145 * Computes tspan dy using font size. This formula was empircally determined
146 * using a best-fit line. Works well in both VML and SVG browsers.
147 * @param fontSize number
150 function computeTSpanDy(fontSize, line, lines) {
151 if (fontSize === null)
154 //return fontSize * 4.5 / 13
155 return fontSize * 4.5 / 13 * (line - .2 - lines / 2) * 4.5;
159 'text': function (node) {
160 var style = extractStyle(node);
162 var tags = new Array;
166 var textArray = node.attrs['text'].split('\n');
168 textArray.forEach(function(v,k) {
169 content.push(tag('tspan',
172 dy: computeTSpanDy(style.font.size, k + 1, textArray.length)
184 function (initial, value, name) {
185 if (name !== 'text' && name !== 'w' && name !== 'h') {
186 if (name === 'font-size')
187 value = value + 'px';
189 initial[name] = escapeXML(value.toString());
195 style: 'text-anchor: ' + (style.font.anchor ? (style.font.anchor + ';') : 'middle;') +
196 styleToString(style) + ';'
205 'path': function (node) {
206 var initial = (node.matrix.a === 1 && node.matrix.d === 1) ? {} : {'transform': node.matrix.toString()};
214 function (initial, value, name) {
215 if (name === 'path') {
219 initial[name] = (typeof (value) == 'undefined') ? '' : value.toString();
224 style: 'fill:' + Raphael.color(node.attrs.fill).hex + ';'
230 // Other serializers should go here
235 svg = '<svg style="overflow: hidden; position: relative;" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="' + paper.width + '" version="1.1" height="' + paper.height + '">';
238 for (var node = paper.bottom; node != null; node = node.next) {
239 //if ( node.node.style.display === 'none' ) continue;
244 if (typeof serializer[node.type] === 'function') {
245 svg += serializer[node.type](node);
252 attrs += ' preserveAspectRatio="none"';
255 //Roo.log(JSON.stringify(node, null,4));
257 for (i in node.attrs) {
260 var val = node.attrs[i].toString();
272 //s(JSON.stringify(node, null,4));
273 val = typeof (node.node.attributes[i]) == 'undefined' ? val : node.node.attributes[i];
275 val = Raphael.color.getRGB(val).hex;
276 //Roo.log("fill: " + val);
281 attrs += ' ' + name + '="' + escapeXML(val) + '"';
285 svg += '<' + node.type + ' transform="matrix(' + node.matrix.toString().replace(/^matrix\(|\)$/g, '') + ')"' + attrs + '></' + node.type + '>' + "\n";
291 return svg.replace(/[\u00ff-\uffff]/g, function(c) {
292 return '&#'+c.charCodeAt(0) + ';';