Tainted canvases may not be exported
JavascriptHtml5 CanvasJavascript Problem Overview
I want to save my canvas to a img
. I have this function:
function save() {
document.getElementById("canvasimg").style.border = "2px solid";
var dataURL = canvas.toDataURL();
document.getElementById("canvasimg").src = dataURL;
document.getElementById("canvasimg").style.display = "inline";
}
It gives me error:
> Uncaught SecurityError: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.
What should I do?
Javascript Solutions
Solution 1 - Javascript
For security reasons, your local drive is declared to be "other-domain" and will taint the canvas.
(That's because your most sensitive info is likely on your local drive!).
While testing try these workarounds:
-
Put all page related files (.html, .jpg, .js, .css, etc) on your desktop (not in sub-folders).
-
Post your images to a site that supports cross-domain sharing (like dropbox.com or GitHub). Be sure you put your images in dropbox's public folder and also set the cross origin flag when downloading the image (
var img=new Image(); img.crossOrigin="anonymous"
...) -
Install a webserver on your development computer (IIS and PHP web servers both have free editions that work nicely on a local computer).
Solution 2 - Javascript
In the img tag set crossorigin to Anonymous.
<img crossorigin="anonymous" />
Solution 3 - Javascript
If someone views on my answer, you maybe in this condition:
*1. Trying to get a map screenshot in canvas using openlayers (version >= 3)
2. And viewed the example of exporting map
- Using ol.source.XYZ to render map layer*
Bingo!
Using ol.source.XYZ.crossOrigin = 'Anonymous' to solve your confuse. Or like following code:
var baseLayer = new ol.layer.Tile({
name: 'basic',
source: new ol.source.XYZ({
url: options.baseMap.basic,
crossOrigin: "Anonymous"
})
});
In OpenLayers6, something is changed with ES6. However, the code is similar.
import { XYZ } from 'ol/source'
import { Tile as TileLayer } from 'ol/layer'
const baseLayer = new TileLayer({
name : 'basic',
source: new XYZ({
url: 'example.tile.com/x/y/z', // your tile url
crossOrigin: 'Anonymous',
// remove this function config if the tile's src is nothing to decorate. It's usually to debug the src
tileLoadFunction: function(tile, src) {
tile.getImage().src = src
}
})
})
What's more, don't forget to set the access-control-allow-origin: *
or access-control-allow-origin: [your whitelist origins]
in the response header if the tiles are requested in your own server.
Like this:
More details, and this one
Solution 4 - Javascript
If you're using ctx.drawImage()
function, you can do the following:
var img = loadImage('../yourimage.png', callback);
function loadImage(src, callback) {
var img = new Image();
img.onload = callback;
img.setAttribute('crossorigin', 'anonymous'); // works for me
img.src = src;
return img;
}
And in your callback you can now use ctx.drawImage
and export it using toDataURL
Solution 5 - Javascript
In my case I was drawing onto a canvas tag from a video with something like canvas.drawImage(video, 0, 0)
. To address the tainted canvas error I had to do two things:
<video id="video_source" crossorigin="anonymous">
<source src="http://crossdomain.example.com/myfile.mp4">
</video>
- Ensure Access-Control-Allow-Origin header is set in the video source response (proper setup of crossdomain.example.com)
- Set the video tag to have
crossorigin="anonymous"
Solution 6 - Javascript
I resolved the problem using useCORS: true
option
html2canvas(document.getElementsByClassName("droppable-area")[0], { useCORS:true}).then(function (canvas){
var imgBase64 = canvas.toDataURL();
// console.log("imgBase64:", imgBase64);
var imgURL = "data:image/" + imgBase64;
var triggerDownload = $("<a>").attr("href", imgURL).attr("download", "layout_"+new Date().getTime()+".jpeg").appendTo("body");
triggerDownload[0].click();
triggerDownload.remove();
});
Solution 7 - Javascript
Seems like you are using an image from a URL that has not set correct Access-Control-Allow-Origin header and hence the issue.. You can fetch that image from your server and get it from your server to avoid CORS issues..
Solution 8 - Javascript
Check out CORS enabled image from MDN. Basically you must have a server hosting images with the appropriate Access-Control-Allow-Origin header.
<IfModule mod_setenvif.c>
<IfModule mod_headers.c>
<FilesMatch "\.(cur|gif|ico|jpe?g|png|svgz?|webp)$">
SetEnvIf Origin ":" IS_CORS
Header set Access-Control-Allow-Origin "*" env=IS_CORS
</FilesMatch>
</IfModule>
</IfModule>
You will be able to save those images to DOM Storage as if they were served from your domain otherwise you will run into security issue.
var img = new Image,
canvas = document.createElement("canvas"),
ctx = canvas.getContext("2d"),
src = "http://example.com/image"; // insert image url here
img.crossOrigin = "Anonymous";
img.onload = function() {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage( img, 0, 0 );
localStorage.setItem( "savedImageData", canvas.toDataURL("image/png") );
}
img.src = src;
// make sure the load event fires for cached images too
if ( img.complete || img.complete === undefined ) {
img.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
img.src = src;
}
Solution 9 - Javascript
Just as a build on @markE's answer. You can serve your website via a local server. You won't have this error on a local server.
If you have PHP installed on your computer (some older MacOS versions has it preinstalled):
- Open up your terminal/cmd
- Navigate into the folder where your website files are
- While in this folder, run the command
php -S localhost:3000
- Open up your browser and in the URL bar go to localhost:3000. Your website should be running there.
or
If you have Node.js installed on your computer:
- Open up your terminal/cmd
- Navigate into the folder where your website files are
- While in this folder, run the command
npm init -y
- Run
npm install live-server -g
orsudo npm install live-server -g
on a mac - Run
live-server
and it should automatically open up a new tab in the browser with your website open.
Note: remember to have an index.html file in the root of your folder or else you might have some issues.
Solution 10 - Javascript
This one can work smoothly in laravel.
First of all, you need to convert tainted canvas to blob. after that, you can upload a blob to serve and save it as an image. Return image URL in ajax call.
Here is an ajax call to upload canvas blob.
$("#downloadCollage").click(function(){
canvas.toBlob(function(blob){
var formDataToUpload = new FormData();
formDataToUpload.append("_token", "{{ csrf_token() }}");
formDataToUpload.append("image", blob);
$.ajax({
url:"{{ route('selfie_collage_upload') }}",
data: formDataToUpload,
type:"POST",
contentType:false,
processData:false,
cache:false,
dataType:"json",
error:function(err){
console.error(err);
},
success:function(data){
window.location.href= data.url;
},
complete:function(){
}
});
},'image/png');
link.click();
});
Solution 11 - Javascript
I also solved this error by adding useCORS : true,
in my code like -
html2canvas($("#chart-section")[0], {
useCORS : true,
allowTaint : true,
scale : 0.98,
dpi : 500,
width: 1400, height: 900
}).then();
Solution 12 - Javascript
In my case I was testing it from my desktop, having CORS error even after saving image locally to sub-folder.
Solution:
Moved the folder to local server WAMP in my case. Worked perfect from local server.
Note: Works only when you have saved image locally.