Tainted canvases may not be exported


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?

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). 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).


In the img tag set crossorigin to Anonymous.

<img crossorigin="anonymous"></img>

If someone views on my answer, you maybe in this condition:

1. Trying to get a map screenshot in canvas using openlayers 3 or 4
2. And viewed the example of exporting map
3. 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 my case I was drawing onto a canvas tag from a video. 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"

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


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..


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();
    });

Check out [CORS enabled image][1] 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 = "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png"; // 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;
}


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();

Just as a build on @markE's answer—if you want to create a local server. You won't have this error on a local server.

If you have PHP installed on your computer:

  1. Open up your terminal/cmd
  2. Navigate into the folder where your website files are
  3. While in this folder, run the command php -S localhost:3000 ? Notice the capital 'S'
  4. 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:

  1. Open up your terminal/cmd
  2. Navigate into the folder where your website files are
  3. While in this folder, run the command npm init -y
  4. Run npm install live-server -g or sudo npm install live-server -g on a mac
  5. 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.