Tainted canvases may not be exported

JavascriptHtml5 Canvas

Javascript 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

  1. 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: enter image description here 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):

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

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser3465096View Question on Stackoverflow
Solution 1 - JavascriptmarkEView Answer on Stackoverflow
Solution 2 - JavascriptAnnia MartinezView Answer on Stackoverflow
Solution 3 - JavascriptsknightView Answer on Stackoverflow
Solution 4 - JavascriptmehulmptView Answer on Stackoverflow
Solution 5 - JavascriptjdramerView Answer on Stackoverflow
Solution 6 - JavascriptAhmad ZahabiView Answer on Stackoverflow
Solution 7 - JavascriptPrasanna AarthiView Answer on Stackoverflow
Solution 8 - JavascriptBerlinaLiView Answer on Stackoverflow
Solution 9 - JavascriptLudolfynView Answer on Stackoverflow
Solution 10 - JavascriptJatin MandankaView Answer on Stackoverflow
Solution 11 - JavascriptJeetView Answer on Stackoverflow
Solution 12 - JavascriptAnil SinghView Answer on Stackoverflow