diff --git a/canvas2svg.js b/canvas2svg.js index f71d359..5ce04ed 100644 --- a/canvas2svg.js +++ b/canvas2svg.js @@ -215,11 +215,11 @@ * ctx - existing Context2D to wrap around * width - width of your canvas (defaults to 500) * height - height of your canvas (defaults to 500) - * enableMirroring - enables canvas mirroring (get image data) (defaults to false) + * embedImages - enables embedding images as data URLs (defaults to false) * document - the document object (defaults to the current document) */ ctx = function (o) { - var defaultOptions = { width:500, height:500, enableMirroring : false}, options; + var defaultOptions = { width:500, height:500, embedImages : false}, options; //keep support for this way of calling C2S: new C2S(width,height) if (arguments.length > 1) { @@ -240,7 +240,7 @@ //setup options this.width = options.width || defaultOptions.width; this.height = options.height || defaultOptions.height; - this.enableMirroring = options.enableMirroring !== undefined ? options.enableMirroring : defaultOptions.enableMirroring; + this.embedImages = options.embedImages !== undefined ? options.embedImages : defaultOptions.embedImages; this.canvas = this; ///point back to this instance! this.__document = options.document || document; @@ -422,6 +422,70 @@ } }; + function startRequest() + { + outstandingRequests++; + } + + function completeRequest() + { + outstandingRequests--; + if(outstandingRequests == 0) + { + completeCallbacks.forEach(function(callback) + { + callback(); + }); + completeCallbacks = []; + } + } + + var outstandingRequests = 0; + var completeCallbacks = []; + + function toDataUrl(url, callback){ + var xhr = new XMLHttpRequest(); + xhr.open('get', url); + xhr.responseType = 'blob'; + xhr.onload = function(){ + var fr = new FileReader(); + + fr.onload = function() + { + callback(this.result); + completeRequest(); + }; + + fr.onerror = function(e) + { + console.error("error reading image " + url, e); + completeRequest(); + }; + + fr.readAsDataURL(xhr.response); // async call + }; + xhr.onerror = function(e){ + console.error("error loading image " + url + " status " + e.target.status); + completeRequest(); + }; + startRequest(); + xhr.send(); + } + + /** + * Waits for all outstanding asyc calls such as image download + * @param callback - will be called when complete + */ + ctx.prototype.waitForComplete = function (callback) + { + if(outstandingRequests > 0) + { + completeCallbacks.push(callback); + return; + } + callback(); + }; + /** * Returns the serialized value of the svg so far * @param fixNamedEntities - Standalone SVG doesn't support named entities, which document.createTextNode encodes. @@ -1152,9 +1216,38 @@ context.drawImage(image, sx, sy, sw, sh, 0, 0, dw, dh); image = canvas; } + + if (image.nodeName === "CANVAS") { + svgImage.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", image.toDataURL()); + } + else if(this.embedImages) + { + var orgSvgImage = svgImage; + svgImage = this.__createElement("use"); + svgImage.setAttribute("width", dw); + svgImage.setAttribute("height", dh); + var imageUrl = image.getAttribute("src"); + svgImage.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", "#" + imageUrl); + //if the image has not been dseen before download and add to defs for reuse + if(!this.__ids[imageUrl]) + { + this.__ids[imageUrl] = imageUrl; + orgSvgImage.setAttribute("id", imageUrl); + this.__defs.appendChild(orgSvgImage); + toDataUrl(image.getAttribute("src"), + function(dataUrl) + { + orgSvgImage.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", dataUrl); + }); + } + } + else + { + svgImage.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", image.getAttribute("src")); + } + svgImage.setAttribute("transform", translateDirective); - svgImage.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", - image.nodeName === "CANVAS" ? image.toDataURL() : image.getAttribute("src")); + parent.appendChild(svgImage); } }; diff --git a/test/example/tiger.jpg b/test/example/tiger.jpg new file mode 100644 index 0000000..6fddf57 Binary files /dev/null and b/test/example/tiger.jpg differ diff --git a/test/unit.spec.js b/test/unit.spec.js index 63dc637..0629f07 100644 --- a/test/unit.spec.js +++ b/test/unit.spec.js @@ -3,17 +3,17 @@ describe('canvas2svg', function() { describe('it can be created', function(){ it("with options", function() { - var ctx = new C2S({width:100, height:200, enableMirroring:true}); + var ctx = new C2S({width:100, height:200, embedImages:true}); expect(ctx instanceof C2S).to.equal(true); expect(ctx.width).to.equal(100); expect(ctx.height).to.equal(200); - expect(ctx.enableMirroring).to.equal(true); + expect(ctx.embedImages).to.equal(true); var ctx2 = new C2S(300,400); expect(ctx2 instanceof C2S).to.equal(true); expect(ctx2.width).to.equal(300); expect(ctx2.height).to.equal(400); - expect(ctx2.enableMirroring).to.equal(false); + expect(ctx2.embedImages).to.equal(false); }); it("with no options and have defaults", function() { @@ -21,28 +21,28 @@ describe('canvas2svg', function() { expect(ctx instanceof C2S).to.equal(true); expect(ctx.width).to.equal(500); expect(ctx.height).to.equal(500); - expect(ctx.enableMirroring).to.equal(false); + expect(ctx.embedImages).to.equal(false); }); it("even if it's called as a function", function() { //notice the lack of new! - var ctx = C2S({width:100, height:200, enableMirroring:true}); + var ctx = C2S({width:100, height:200, embedImages:true}); expect(ctx instanceof C2S).to.equal(true); expect(ctx.width).to.equal(100); expect(ctx.height).to.equal(200); - expect(ctx.enableMirroring).to.equal(true); + expect(ctx.embedImages).to.equal(true); var ctx2 = C2S(300,400); expect(ctx2 instanceof C2S).to.equal(true); expect(ctx2.width).to.equal(300); expect(ctx2.height).to.equal(400); - expect(ctx2.enableMirroring).to.equal(false); + expect(ctx2.embedImages).to.equal(false); var ctx3 = C2S(); expect(ctx3 instanceof C2S).to.equal(true); expect(ctx3.width).to.equal(500); expect(ctx3.height).to.equal(500); - expect(ctx3.enableMirroring).to.equal(false); + expect(ctx3.embedImages).to.equal(false); }); @@ -357,4 +357,20 @@ describe('canvas2svg', function() { expect(svg.querySelector("clipPath > path").getAttribute("d")).to.not.equal(null); }); }); + + describe("supports embedded images", function() { + it("adds image", function(done) { + var ctx = new C2S({embedImages:true}); + var img = new Image; + img.src = "./example/tiger.jpg"; + ctx.drawImage(img, 0, 0); + ctx.waitForComplete(function() + { + var svg = ctx.getSvg(); + expect(svg.querySelector("image").getAttribute("xlink:href")).to.not.equal(null); + done(); + }); + }); + }); + });