I have an HTML5 canvas on which I draw an image from an svg.
HTML
<canvas id="canvas" width="600" height="320"></canvas>
JavaScript
var DOMURL = window.URL || window.webkitURL || window;
var data = '<svg xmlns="http://www.w3.org/2000/svg" width="600" height="320">'+
'<foreignObject width="100%" height="100%">'+
'<style>'+
'foreignObject {'+
'background-color: #000;'+
'color: #fff'+
'border-radius: 10px;'+
'}'+
'h1 {'+
'color: #2acabd;'+
'font: 25px arial;'+
'font-weight: bold;'+
'text-align: center;'+
'}'+
'h2 {'+
'margin: 0;'+
'color: #2acabd;'+
'font: 15px arial;'+
'}'+
'p {'+
'color: #fff;'+
'}'+
'</style>'+
'<div xmlns="http://www.w3.org/1999/xhtml" style="font-size: 40px;">'+
'<h1>Heading</h1>'+
'<div>'+
'<div id="details-wrapper">'+
'<h2>Full Name</h2>'+
'<p>Alan Johnson</p>'+
'<h2>Date of Birth</h2>'+
'<p>7th November 1988</p>'+
'<p><span id="user-id">34329483028493284093284432</span></p>'+
'</div>'+
'</div>'+
'</div>'+
'</foreignObject>'+
'</svg>';
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
img = new Image();
img.setAttribute("crossOrigin", "anonymous");
var svg = new Blob([data], {type: 'image/svg+xml;charset=utf-8'});
var url = DOMURL.createObjectURL(svg);
img.onload = function() {
ctx.drawImage(img, 0, 0);
DOMURL.revokeObjectURL(url);
console.log(canvas.toDataURL());
}
img.src = url;
(JS Fiddle: https://jsfiddle.net/LondonAppDev/qnpcg8th/1/)
When I call canvas.toDataURL()
, I am getting the exception:
(index):98 Uncaught SecurityError: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.
I have seen the many other questions and answers on Stack Overflow relating to this exception. My problem is different, because (as you can see) I am not at any point including any images from another domain in my svg or canvas.
I am guessing the problem is that I am creating an object URL with DOMURL.createObjectURL
.
I am aware that there are some compatibility issues with doing this in different browsers, however this app only needs to run in Chrome. Also, drawing the text directly onto the canvas is not an option, I must do it via an svg.
Any ideas as to how I can get around this issue, and successfully get a PNG of my canvas?
I solved this by converting the svg to a data URL instead of a Blob.
I removed
var url = DOMURL.createObjectURL(svg);
and replacedimg.src = url;
with this:Now it works flawlessly.
This
iswas for security and user privacy reasons.To paint a
<foreignObject>
on a canvas can leaks several informations about users, and until then, chrome team thought they didn't had enough security measures to prevent this so they did taint the canvas.Here is the chromium bug report about this issue.
But as OP found out, they had an implementation bug that forgot to set this restriction on dataURI versions.
I did post this bug report about the fact that they do not taint canvas with dataURIs.
All this lead to the leverage of the former restrictions, so in next versions of chrome, we should be able to paint
<foreignObject>
on canvas without tainting it, even with BlobURIs.But note that Safari still does have the same restriction (without the implementation bug, i.e the dataURI hack doesn't work there), and that IE < Edge do not support
<foreignObject>
element and taints the canvas when any SVG has been painted to it, and that FF does remove all UserAgent and OS styles (which leads to different results than the one you could expect).A real workaround then is to not use this hack to get your elements painted, but draw all your HTML with CanvasAPI drawing methods (just like html2canvas does).