/*
* SVGExporter
* Visit http://gskinner.com/ for documentation, updates & examples.
*
* Copyright ©2014 gskinner.com, inc.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
// http://tutorials.jenkov.com/svg/svg-transformation.html
(function () {
"use strict";
/**
* Description
* @class SVGExporter
* @constructor
**/
var SVGExporter = function (stage) {
this.initialize(stage);
};
var p = SVGExporter.prototype;
// shortcuts:
var c = createjs;
var att = function(el, attr, value) {
el.setAttribute(attr, value);
return el;
};
var atts = function(el, o) {
for (var n in o) {
el.setAttribute(n, o[n]);
}
return el;
};
var create = function(name, o) {
return atts(document.createElementNS(SVGExporter.SVG_NS, name), o);
};
var add = function(parent, child) {
if (!child) { return; }
parent.appendChild(child);
return child;
};
var mtx = null;
// static:
SVGExporter.SVG_NS = "http://www.w3.org/2000/svg";
SVGExporter.XLINK_NS = "http://www.w3.org/1999/xlink";
SVGExporter.BLEND_MAP = {multiply:1, screen:1, overlay:1, darken:1, lighten:1, "color-dodge":1, "color-burn":1, "hard-light":1, "soft-light":1, difference:1, exclusion:1, hue:1, saturation:1, color:1, luminosity:1};
// properties:
p.svg = null;
p.stage = null;
p.defs = null;
p.uids = {};
// initialization:
/**
* Initialization method.
* @method initialize
* @protected
**/
p.initialize = function (stage) {
this.stage = stage;
mtx = stage.getMatrix();
var svg = this.svg = create("svg");
svg.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:xlink", SVGExporter.XLINK_NS);
atts(svg, {width:stage.canvas.width, height:stage.canvas.height, style:"border: 1px solid black; overflow: hidden;"});
add(svg, this.defs = create("defs"));
add(svg, this.exportContainer(stage));
};
// public properties:
// private properties:
// public methods:
p.getUID = function(id) {
if (this.uids[id] == null) {
this.uids[id] = 0;
return id;
}
return id+"_"+(++this.uids[id]);
};
// makes it easy to override this to add unsupported types:
p.exportElement = function(o) {
if (o instanceof c.Container) { return this.exportContainer(o); }
if (o instanceof c.Bitmap) { return this.exportBitmap(o); }
if (o instanceof c.Sprite) { return this.exportSprite(o); }
if (o instanceof c.Shape) { return this.exportShape(o); }
};
p.exportContainer = function(o) {
var group = this.addTransform(create("g"), o);
for (var i= 0, l=o.getNumChildren(); i<l; i++) {
add(group, this.exportElement(o.getChildAt(i)));
}
return group;
};
p.exportBitmap = function(o) {
return this.addTransform(this._getImage(o.image), o);
};
p.exportSprite = function(o) {
var ss = o.spriteSheet;
var frame = ss.getFrame(o.currentFrame);
var r = frame.rect;
var sprite = this.addTransform(this._getImage(frame.image), o, -r.x - frame.regX, -r.y - frame.regY);
var id = this.getUID(sprite.id+"_mask");
var mask = create("clipPath", {id:id});
add(mask, create("rect", {x: r.x, y: r.y, width: r.width, height: r.height}));
add(this.defs, mask);
att(sprite, "clip-path", "url(#"+id+")");
return sprite;
};
p.exportShape = function(o) {
var shape = this.addTransform(create("g"), o), G = c.Graphics;
var q = o.graphics.getInstructions();
var active = [], fill=null, stroke=null, strokeStyle=null, closed = false;
for (var i= 0, l= q.length; i<l; i++) {
var cmd = q[i], isStrokeOrPath = false;
if (cmd instanceof G.Fill) {
fill = cmd;
isStrokeOrPath = closed = true;
} else if (cmd instanceof G.Stroke) {
stroke = cmd;
isStrokeOrPath = closed = true;
} else if (cmd instanceof G.StrokeStyle) {
strokeStyle = cmd;
isStrokeOrPath = closed = true;
}
if ((closed && !isStrokeOrPath) || i == l-1) {
this.exportPath(active, this.getFillAndStroke(fill, stroke, strokeStyle), shape);
active.length = 0;
closed = false;
}
if (!isStrokeOrPath) {
active.push(cmd);
}
}
return shape;
};
p.exportPath = function(arr, style, parent) {
var o, G = c.Graphics;
for (var i= 0, l=arr.length; i<l; i++) {
var cmd = arr[i];
if (cmd instanceof G.Rect) { o = create("rect", {x:cmd.x, y:cmd.y, width:cmd.w, height:cmd.h}); }
else if (cmd instanceof G.Circle) { o = create("circle", {cx:cmd.x, cy:cmd.y, r:cmd.radius}); }
else if (cmd instanceof G.Ellipse) { o = create("ellipse", {cx:cmd.x+cmd.w/2, cy:cmd.y+cmd.h/2, rx:cmd.w/2, ry:cmd.h/2}); }
else if (cmd instanceof G.RoundRect) {
if (cmd.radiusTL == cmd.radiusTR && cmd.radiusTL == cmd.radiusBR && cmd.radiusTL == cmd.radiusBL) { o = create("rect", {x:cmd.x, y:cmd.y, width:cmd.w, height:cmd.h, rx:cmd.radiusTL}); }
else { /* TODO: complex drawRoundRect */ }
}
if (o) {
if (parent) { add(parent, o); }
if (style) { att(o, "style", style); }
}
}
};
p.getFillAndStroke = function(fill, stroke, strokeStyle) {
var style = "";
if (fill) { style += "fill:"+fill.style+";"; }
if (stroke) { style += "stroke:"+stroke.style+";"; }
if (strokeStyle) {
if (strokeStyle.width != 1) { style += "stroke-width:"+strokeStyle.width+";"; }
var caps = strokeStyle.caps == null ? "butt" : strokeStyle.caps;
if (caps != "none") { style += "stroke-linecap:"+caps+";" }
if (strokeStyle.joints && strokeStyle.joints != "miter") { style += "stroke-linejoin:"+strokeStyle.joints+";"; }
else if (strokeStyle.miterLimit != 4) { style += "stroke-miterlimit:"+(strokeStyle.miterLimit?strokeStyle.miterLimit:10)+";"; }
}
return style;
};
p._getImage = function(image) {
var bmp = create("image");
bmp.setAttributeNS(SVGExporter.XLINK_NS, "xlink:href", image.src);
atts(bmp, {width: image.width, height: image.height});
return bmp;
};
p.addTransform = function(el, o, x, y) {
if (o.alpha != 1) { att(el, "opacity", o.alpha.toFixed(4)); }
if (!o.visible) { att(el, "display", "none"); }
att(el, "id", this.getUID(o.name||"element"));
var blend = SVGExporter.BLEND_MAP[o.compositeOperation];
if (blend) {
console.log(o.compositeOperation);
att(el, "style", "mix-blend-mode:"+ o.compositeOperation);
}
mtx = o.getMatrix(mtx);
if (x && y) { mtx.append(1,0,0,1,x,y); }
if (mtx.isIdentity()) { return el; }
att(el, "transform", "matrix("+[mtx.a.toFixed(4), mtx.b.toFixed(4), mtx.c.toFixed(4), mtx.d.toFixed(4), mtx.tx.toFixed(4), mtx.ty.toFixed(2)]+")");
return el;
};
// private methods:
window.SVGExporter = SVGExporter;
})();