/**
* @private
*/
Ext.define('Ext.draw.engine.SvgExporter', {
singleton: true,
statics: (function(){
var surface, len, width,
height,
init = function(s){
surface = s;
len = surface.length;
width = surface.width;
height = surface.height;
},
spriteProcessor = {
path: function(sprite){
var attr = sprite.attr,
path = attr.path,
pathString = '',
props, p, pLen;
if(Ext.isArray(path[0])){
pLen = path.length;
for (p = 0; p < pLen; p++) {
pathString += path[p].join(' ');
}
}else if(Ext.isArray(path)){
pathString = path.join(' ');
}else{
pathString = path.replace(/,/g,' ');
}
props = toPropertyString({
d: pathString,
fill: attr.fill || 'none',
stroke: attr.stroke,
'fill-opacity': attr.opacity,
'stroke-width': attr['stroke-width'],
'stroke-opacity': attr['stroke-opacity'],
"z-index": attr.zIndex,
transform: sprite.matrix.toSvg()
});
return '<path ' + props + '/>';
},
text: function(sprite){
// TODO
// implement multi line support (@see Svg.js tuneText)
var attr = sprite.attr,
fontRegex = /(-?\d*\.?\d*){1}(em|ex|px|in|cm|mm|pt|pc|%)\s('*.*'*)/,
match = fontRegex.exec(attr.font),
size = (match && match[1]) || "12",
// default font family is Arial
family = (match && match[3]) || 'Arial',
text = attr.text,
factor = (Ext.isFF3_0 || Ext.isFF3_5) ? 2 : 4,
tspanString = '',
props;
sprite.getBBox();
tspanString += '<tspan x="' + (attr.x || '') + '" dy="';
tspanString += (size/factor)+'">';
tspanString += Ext.htmlEncode(text) + '</tspan>';
props = toPropertyString({
x: attr.x,
y: attr.y,
'font-size': size,
'font-family': family,
'font-weight': attr['font-weight'],
'text-anchor': attr['text-anchor'],
// if no fill property is set it will be black
fill: attr.fill || '#000',
'fill-opacity': attr.opacity,
transform: sprite.matrix.toSvg()
});
return '<text '+ props + '>' + tspanString + '</text>';
},
rect: function(sprite){
var attr = sprite.attr,
props = toPropertyString({
x: attr.x,
y: attr.y,
rx: attr.rx,
ry: attr.ry,
width: attr.width,
height: attr.height,
fill: attr.fill || 'none',
'fill-opacity': attr.opacity,
stroke: attr.stroke,
'stroke-opacity': attr['stroke-opacity'],
'stroke-width':attr['stroke-width'],
transform: sprite.matrix && sprite.matrix.toSvg()
});
return '<rect ' + props + '/>';
},
circle: function(sprite){
var attr = sprite.attr,
props = toPropertyString({
cx: attr.x,
cy: attr.y,
r: attr.radius,
fill: attr.translation.fill || attr.fill || 'none',
'fill-opacity': attr.opacity,
stroke: attr.stroke,
'stroke-opacity': attr['stroke-opacity'],
'stroke-width':attr['stroke-width'],
transform: sprite.matrix.toSvg()
});
return '<circle ' + props + ' />';
},
image: function(sprite){
var attr = sprite.attr,
props = toPropertyString({
x: attr.x - (attr.width/2 >> 0),
y: attr.y - (attr.height/2 >> 0),
width: attr.width,
height: attr.height,
'xlink:href': attr.src,
transform: sprite.matrix.toSvg()
});
return '<image ' + props + ' />';
}
},
svgHeader = function(){
var svg = '<?xml version="1.0" standalone="yes"?>';
svg += '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">';
return svg;
},
svgContent = function(){
var svg = '<svg width="'+width+'px" height="'+height+'px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">',
defs = '', item, itemsLen, items, gradient,
getSvgString, colorstops, stop,
coll, keys, colls, k, kLen, key, collI, i, j, stopsLen, sortedItems, za, zb;
items = surface.items.items;
itemsLen = items.length;
getSvgString = function(node){
var childs = node.childNodes,
childLength = childs.length,
i = 0,
attrLength,
j,
svgString = '', child, attr, tagName, attrItem;
for(; i < childLength; i++){
child = childs[i];
attr = child.attributes;
tagName = child.tagName;
svgString += '<' +tagName;
for(j = 0, attrLength = attr.length; j < attrLength; j++){
attrItem = attr.item(j);
svgString += ' '+attrItem.name+'="'+attrItem.value+'"';
}
svgString += '>';
if(child.childNodes.length > 0){
svgString += getSvgString(child);
}
svgString += '</' + tagName + '>';
}
return svgString;
};
if(surface.getDefs){
defs = getSvgString(surface.getDefs());
}else{
// IE
coll = surface.gradientsColl;
if (coll) {
keys = coll.keys;
colls = coll.items;
k = 0;
kLen = keys.length;
}
for (; k < kLen; k++) {
key = keys[k];
collI = colls[k];
gradient = surface.gradientsColl.getByKey(key);
defs += '<linearGradient id="' + key + '" x1="0" y1="0" x2="1" y2="1">';
var color = gradient.colors.replace(/rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/g, 'rgb($1|$2|$3)');
color = color.replace(/rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,([\d\.]+)\)/g, 'rgba($1|$2|$3|$4)')
colorstops = color.split(',');
for(i=0, stopsLen = colorstops.length; i < stopsLen; i++){
stop = colorstops[i].split(' ');
color = Ext.draw.Color.fromString(stop[1].replace(/\|/g,','));
defs += '<stop offset="'+stop[0]+'" stop-color="' + color.toString() + '" stop-opacity="1"></stop>';
}
defs += '</linearGradient>';
}
}
svg += '<defs>' + defs + '</defs>';
// thats the background rectangle
svg += spriteProcessor.rect({
attr: {
width: '100%',
height: '100%',
fill: '#fff',
stroke: 'none',
opacity: '0'
}
});
// Sort the items (stable sort guaranteed)
sortedItems = new Array(itemsLen);
for(i = 0; i < itemsLen; i++){
sortedItems[i] = i;
}
sortedItems.sort(function (a, b) {
za = items[a].attr.zIndex || 0;
zb = items[b].attr.zIndex || 0;
if (za == zb) {
return a - b;
}
return za - zb;
});
for(i = 0; i < itemsLen; i++){
item = items[sortedItems[i]];
if(!item.attr.hidden){
svg += spriteProcessor[item.type](item);
}
}
svg += '</svg>';
return svg;
},
toPropertyString = function(obj){
var propString = '',
key;
for(key in obj){
if(obj.hasOwnProperty(key) && obj[key] != null){
propString += key +'="'+ obj[key]+'" ';
}
}
return propString;
};
return {
generate: function(config, surface){
init(surface);
return svgHeader() + svgContent();
}
};
}())
});