Reputation: 3265
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?
Upvotes: 329
Views: 454461
Reputation: 31
adding crossorigin
in image src string and then setting crossorigin attribute to anonymous worked for me.
const image = new Image(width, length);
image.src = `${src}?crossorigin`;
image.setAttribute('crossOrigin', 'anonymous');
Upvotes: 0
Reputation: 4152
I was working on react js and initially I tried the upvoted answer here. but, it didn't worked.
var img=new Image();
img.crossOrigin="anonymous"
Then I though to configure cors policy in server. but, before just tried one more thing.
const video = document.createElement("video")
video.crossOrigin = "anonymous"
video.src = url
So, adding crossOrigin
both
1> to root src i.e video in my case &
2> to image src i.e preview of video in timeline (taking screenshot of video and converting it to base64)
solved the error
Upvotes: 1
Reputation: 1189
Another case for me was to update the library itself, I was using very old alpha version. The crossorigin
wasn't any effect. After the update I just used the following settings and it worked:
useCORS : true,
allowTaint : true
Upvotes: 0
Reputation: 1103
You may be in this condition:
1. Trying to get a map screenshot in canvas using openlayers (version >= 3)
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 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:
Upvotes: 44
Reputation: 105035
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).
Upvotes: 309
Reputation: 96
Here is how I solved this issue. The issue is because you cannot have an external image URL inside of your canvas. You must fetch the image and create a local copy of it. Then you can add the image and its url.
fetch("https://i.imgur.com/fHyEMsl.jpg")
.then(result => result.blob())
.then(blob => {
const url = URL.createObjectURL(blob);
// set image url
});
Upvotes: 3
Reputation: 1195
tl;dr
This issue made me crazy and solved it by loading image with crossOrigin="anonymous" before rendering canvas.
For those who uses React + canvg + Amazon S3 and want to export svg as png via canvas, this could be useful.
First, create a React hook to detect preloading cross-origin images:
// useLoadCrossOriginImage.tsx
import { useReducer, useMemo } from 'react'
export function useLoadCrossOriginImage(imageUrls: string[]) {
const [count, increase] = useReducer((count) => count + 1, 0)
const render = () =>
imageUrls.map((url) => (
<img
src={url}
crossOrigin="anonymous"
onLoad={increase}
className="hidden"
/>
))
const loaded = useMemo(() => count === imageUrls.length, [count, imageUrls])
return {
render,
loaded,
}
}
Then, render svg lazily after loading images:
// ImagePreview.tsx
import { useLoadCrossOriginImage } from './useLoadCrossOriginImage'
// This is usually state from parent component
const imageUrls = [
'https://s3-ap-northeast-1.amazonaws.com/bucket/xxxxxxx.png',
'https://s3-ap-northeast-1.amazonaws.com/bucket/yyyyyyy.png',
]
export const ImagePreview = () => {
const { loaded, render } = useLoadCrossOriginImage(imageUrls)
return (
<div className="border border-slate-300" onClick={onClick}>
{render()}
{loaded && (
<svg xmlns="http://www.w3.org/2000/svg">
{imageUrls.map((imageUrl) => (
<image key={el.id} href={imageUrl} />
))}
</svg>
)}
<canvas className="hidden" />
</div>
)
}
Finally, you can convert the canvas element into png:
const canvas = document.querySelector('canvas')!
const ctx = canvas.getContext('2d')!
const svg = document.querySelector('svg')!
const v = Canvg.fromString(ctx, svg.outerHTML, { anonymousCrossOrigin: true })
Finally, the S3 cors policy should be like this:
{
"CORSRules": [
{
"ID": "s3-cors-policy",
"AllowedHeaders": ["*"],
"AllowedMethods": ["GET", "HEAD"],
"AllowedOrigins": ["*"],
"ExposeHeaders": []
}
]
}
Please leave "MaxAgeSeconds"
empty.
Upvotes: 4
Reputation: 518
For anyone who still encountering the same issue from S3 even after applying the server cross-origin settings, it probably a browser caching issue. So you need to make sure to disable the caching and test again, you can do that from the browser dev-tools -> network tab -> click on disable cash option -> try again:
Upvotes: 2
Reputation: 2622
In the img tag set crossorigin to Anonymous.
<img crossorigin="anonymous" />
Upvotes: 214
Reputation: 2085
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):
php -S localhost:3000
If you have Node.js installed on your computer:
npm init -y
npm install live-server -g
or sudo npm install live-server -g
on a maclive-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.
Upvotes: 6
Reputation: 945
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>
crossorigin="anonymous"
Upvotes: 32
Reputation: 470
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();
});
Upvotes: 6
Reputation: 4501
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.
Upvotes: 2
Reputation: 95
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;
}
Upvotes: 6
Reputation: 43
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();
Upvotes: 3
Reputation: 16587
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
Upvotes: 47
Reputation: 1268
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();
});
Upvotes: 15
Reputation: 3453
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..
Upvotes: 12