Hi I have the following problem.
I'm generating a SVG image (https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Drawing_DOM_objects_into_a_canvas).
The image is generated correctly and looks ok.
Now I need to get the data URI, but everytime I try to get that from canvas.toDataURL() this message appears Uncaught DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.(…)
I've created some sample code to illustrate the situation.
</!DOCTYPE html>
<html>
<head>
<title>SVG to PNG</title>
</head>
<body>
<canvas id="canvas" style="border:2px solid black;" width="200" height="200">
</canvas>
<script type="text/javascript">
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var data = '<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">' +
'<foreignObject width="100%" height="100%">' +
'<div xmlns="http://www.w3.org/1999/xhtml" style="font-size:40px">' +
'<em>I</em> like ' +
'<span style="color:white; text-shadow:0 0 2px blue;">' +
'beer</span>' +
'</div>' +
'</foreignObject>' +
'</svg>';
var domURL = window.URL || window.webkitURL || window;
var img = new Image();
var svg = new Blob([data], {type: 'image/svg+xml'});
var url = domURL.createObjectURL(svg);
img.onload = function () {
ctx.drawImage(img, 0, 0);
domURL.revokeObjectURL(url);
var dataURL = canvas.toDataURL();
console.log(dataURL);
};
img.src = url;
</script>
</body>
</html>
This code generates the following image
This is correct, but then when these lines are executed
var dataURL = canvas.toDataURL();
console.log(dataURL);
It throws the error. I'm doing this inside the image onload method to allow the image finish drawing to the canvas. If I try to get the dataURL from outside the onload method, the canvas hasn't finished loading, so it would give me an empty image.
I've been searching a lot but I couldn't find the answer yet. This is something related to CORS. I've installed the chrome plugin CORS and added the img, the thing is that this is just an example, and a CORS based solution would not be useful, because I'm working on an application that's running over a crippled chromium browser (I mean the browser just shows the web app, you can't do anything there except interact with the app).
To notice you can obtain the data URI, with
inspect --> network --> select the image and open in sources panel and there it is just copy image as data uri.
I get the base64 image that way, and if I use some decoder like this (http://base64online.org/decode/) it's showing the image correctly.
So definitely the problem is that I'm getting the data URI before the canvas is drawn.
Any ideas?
Thanks in advance!
Regards
Mauro
This has nothing to do with CORS. You are not doing any request to an external resource.
The issue is that drawing an svg (moreover when it contains html elements) is a really sensitive action for browsers.
That's why there are a lot of limitations on this technique (you can't load external resources, scripts are ignored, you can't style the content from external CSS, all default browser and OS styles are disabled to avoid fingerprinting etc.).
I don't work for safari nor chrome, so I can't tell for sure, but I think they aren't able to provide enough security on one of these points (safari realised it in v9, and it's a known issue in chrome too).
So what they do, is that they taint the canvas, locking any of its export methods. (Note that IE < Edge did also taint the canvas whenever any svg had been drawn to the canvas for similar security reasons).
If what you want is to rasterize DOM elements, then you should parse the DOM and all its applied styles, and reproduce it with canvas drawings methods.
Doing so, you can bypass most security issues, and no UA will taint the canvas. Some library out there do exactly this, the most famous being html2canvas.
If what you want is to draw this image, then you can rewrite it without using a foreignObject (svg text has more options than canvas one), then use a library like canvg to render it on your canvas (because otherwise IE will taint the canvas as said previously).
Note : chrome's issue can be workedaround, but I'm not sure it's a good idea, it will still not work in Safari, nor in IE < Edge, and chrome may just have forgotten to block it too and will in next releases, anyway, here is how :
var canvas = document.getElementById('c');
var ctx = canvas.getContext('2d');
var data = '<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">' +
'<foreignObject width="100%" height="100%">' +
'<div xmlns="http://www.w3.org/1999/xhtml" style="font-size:40px">' +
'<em>I</em> like ' +
'<span style="color:white; text-shadow:0 0 2px blue;">' +
'beer</span>' +
'</div>' +
'</foreignObject>' +
'</svg>';
var img = new Image();
// instead of a blobURL, if we use a dataURL, chrome seems happy...
var url = 'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(data);
img.onload = function() {
c.width = this.width;
c.height = this.height;
ctx.drawImage(img, 0, 0);
var dataURL = canvas.toDataURL();
console.log(dataURL);
};
img.src = url;
<canvas id="c"></canvas>
Related
I can't seem to have Chrome browser show the true image size via <img>
and also by drawImage via canvas context. Explorer shows both of these
correctly.
In Chrome, the image shown is not by the dimensions of the original image
but a scaled down one. The browser when rendered from a Web server seems to
have something to do with it. Curiously enough, when open the browser on
the html file locally:
E.g. file:///C:/xampp/htdocs/Website_TEST_active/test1.html, the image dimensions are correct.
Attached is a stripped down HTML and Javascript code. Appreciate any insights.
Thanks
Sean
<!DOCTYPE html>
<html>
<body>
<p>Image to use:</p>
<img id="scream" onload="loadImage()" src="pics/cover.jpg" alt="Test">
<p>Canvas:</p>
<canvas id="myCanvas" width="854" height="480" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.
</canvas>
<script>
window.onload = function() {
// Not used
}
/*
* Upon image load, draw image on canvas
*/
function loadImage(){
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var img = document.getElementById("scream");
ctx.drawImage(img, 0,0, img.naturalWidth, img.naturalHeight);
console.log("Original Image W=" + img.naturalWidth +
" H=" + img.naturalHeight);
}
</script>
</body>
</html>
I found something that might be right for what you're asking.
Here's the link- https://superuser.com/a/364875
Open Developer tools (Ctrl+Shift+I or use the Settings Icon at the
top-right of your browser window => Tools => Developer tools) and on
the relevant page, switch to the Network tab and reload the page.
In the Size column you'll see the size of everything loaded
(Documents, Stylesheets, Images, Scripts, ...). You can enable a
filter to help you find out what you need - at the bottom-center of
Developer tools frame.
In a client-side standalone JS application, I'm trying to make it so I can call toDataURL() on a canvas on which I've drawn some images specified by a URL. Ie I can input into a textbox the url to any image (hosted on, say, imgur) that I want to draw on the canvas, click a "draw" button and it will draw on the canvas. The end user should be able to save their final render as a single image, for this I'm using toDataURL().
Anyway, until they actually fix that annoying "operation is insecure" error (gee, you're going to tell the end user what they can and can't do with their own data?) I followed a workaround that said to add the image to the DOM and set its crossOrigin property to "Anonmyous" and then draw it to the canvas.
Here's a full working simplified version of my code (but in reality there will be many more features):
<!DOCTYPE html5>
<html>
<head>
<style>
#canvas {border:10px solid green;background-color:black;}
#imgbox {border:2px solid black;}
</style>
</head>
<body>
<canvas id="canvas" width=336 height=336></canvas>
<br><br>
<input size=60 id="imgbox">
<input type="submit" value="Draw" onclick=draw()>
<script>
function draw() {
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var img = new Image();
img.src = document.getElementById("imgbox").value;
img.crossOrigin = "Anonymous";
context.drawImage(img, 40, 40);
}
</script>
</body>
</html>
Without the img.crossOrigin = "Anonymous"; line, I could input http://i.imgur.com/c2wRzfD.jpg into the textbox and click draw and it would work. However as soon as I added that line, the whole thing broke and it won't even be drawn to the canvas at all.
What do I need to change to fix this? I really need to be able to implement the functionality for the end user to save their final image and it's extremely annoying that the people who wrote the html5 spec purposely introduced this bug.
You must set the CORS request before the src - just swap the lines into:
img.crossOrigin = "Anonymous";
img.src = document.getElementById("imgbox").value;
You will also need to add an onload handler to the image as loading is asynchronous:
img.onload = function() {
context.drawImage(this, 40, 40);
// call next step in your code here, f.ex: nextStep();
};
img.crossOrigin = "Anonymous";
img.src = document.getElementById("imgbox").value;
When the server requires authorization to access the images the value should be:
img.crossOrigin = "Use-Credentials";
Otherwise the browser will give up after receiving HTTP 401.
If your image disappears after setting cross origin to anonymous it means your server doesn't allow cross origin. If you're using amazon s3 to serve your images, you need to enable public access to your bucket, and then add cross origin policy (from templates). After that adding cross origin "anonymous" should work.
I have some HTML 5 content with Javascript which generates a set of rectangles and texts with links.
That code doesn't work on the IE8 because it doesn't support HTML 5.
I want the browser to display an image instead of a text, if the page is shown in an IE8 or any other browser that doesn't support HTML5.
I tried this code but it didn't gave me the expected output.
function draw(){
canvas = document.getElementById("myCanvas");
// check if supported
if(canvas.getContext){
ctx=canvas.getContext("2d");
var ctz=canvas.getContext("2d");
ctz.fillStyle="#FF0000";
ctz.fillRect(0,0,150,75);
//..Rest of the code to add html5 content
}else{
var img = document.createElement("img");
img.src = 'http://www.newyorker.com/online/blogs/photobooth/NASAEarth-01.jpg';
img.width = 276;
img.height = 110;
img.alt = '';
document.body.appendChild(img);
}
}
draw();
What could be the error here?
By the definition of the canvas element, its content acts as fallback content: it is ignored by browsers that implement the element, but it will naturally be processed and displayed by older browsers that simply ignore the <canvas ...> and </canvas> tags. Example:
<canvas id=myCanvas>
<img src=http://www.newyorker.com/online/blogs/photobooth/NASAEarth-01.jpg
alt="" width=276 height=110>
</canvas>
I want to use CSS to style an element on a webpage and then to use that element as a static png. Is it possible to draw html node on eg. canvas and save such image with transparency to a file?
I want to find a way to take already existing HTML with CSS and render it to PNG file keeping transparency.
Saving HTML elements to images requires a Complicated Answer!
First, for very good security reasons, browsers do not to allow same-page imaging of HTML elements. Imagine for example a malicious script that takes the HTML input form containing your banking username+password and converts it to an image and sends the image to a thief—not good!
Therefore, IE simply block same page HTML element imaging--period.
Chrome and Firefox might still have a feature (bug?!) that allows you to turn HTML elements into images like this:
1. Embed the HTML element into an SVG element using "foreignObject".
2. Draw the SVG element into a Canvas element.
3. Use canvas.toDataURL(‘image/png’) to get an encoded string representing the png image.
Since it looks like you are in control of styling the HTML, you might have a full solution by using a “headless” HTML generator like PhantomJs.org (phantomjs.org). You can use Phantomjs to generate and style the HTML element. PhantomJs then has a rasterizer you can use to convert that element into an image. Plus, If you can serve up a web page that contains only the styled HTML you need, then this 1 line of PhantomJs code will get you a png of that page:
phantomjs rasterize.js http://myHtmlOnMyPage.html myHtmlImage.png
The Chrome/Firefox code is found here: https://developer.mozilla.org/en-US/docs/HTML/Canvas/Drawing_DOM_objects_into_a_canvas
And looks like this:
<!DOCTYPE html>
<html>
<body>
<p><canvas id="canvas" style="border:2px solid black;" width="200" height="200"></canvas>
<script>
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var data = "<svg xmlns='http://www.w3.org/2000/svg' width='200' height='200'>" +
"<foreignObject width='100%' height='100%'>" +
"<div xmlns='http://www.w3.org/1999/xhtml' style='font-size:40px'>" +
"<em>I</em> like <span style='color:white; text-shadow:0 0 2px blue;'>cheese</span>" +
"</div>" +
"</foreignObject>" +
"</svg>";
var DOMURL = self.URL || self.webkitURL || self;
var img = new Image();
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);
};
img.src = url;
</script>
</body>
</html>
Just found an easy way via Safari.
Right click on element, Inspect Element, then in the Inspector right click on the element node you want to export and pick Capture Screenshot. This to me resolves transparency. Safari Version 13.1 (14609.1.20.111.8)
Yes it is possible to save a HTML element as image (PNG file). See
Capture HTML Canvas as gif/jpg/png/pdf?
how to save canvas as png image?
HTML5 Save canvas to PNG
Drawing PNG to a canvas element -- not showing transparency
http://asserttrue.blogspot.de/2011/12/in-yesterdays-post-i-showed-how-to.html
I currently have a canvas html5 element setup to receive an uploaded image, resize it, and pass its data uri to a form value which is then submitted to the server. This works well on computers but has run into a problem when I tested it on my iPhone through Safari.
With the new IOS 6.0.1 update when uploading an image I can choose 'Take Photo or Video', however two issues arise when doing so from an iPhone (i do not have an iPad handy to test :( )...
1) The image is either appears squished or "unfinished" as in the top 1/3 of the image is all that remains
I removed my context.scale() function and these issues did not
appear. Is there some processing limitation for scaling on iphone
devices? A simple context.scale(0.5, 0.5) does not seem to work.
2) The image is rotated (this problem persists when context.scale() is removed).
I've read that image may preserve some of its orientation data, but how would one remove that?
These issues seem isolated to only when I 'Take Photo' or use an image that my iPhone took. When using images downloaded from my computer onto my iPhone there seems to be no problem for either issues 1) or 2). Also when using pictures taken by my iPhone, sent to my computer, and then uploaded from the computer there seems to be no problems as well.
For issue 1) since the same picture is used by my computer (no problems) and iPhone (problem) I venture to guess it is a iPhone limitation for context.scale(), unless when sending an image by email my iPhone compresses it somehow and removes the orientation data which is also not a problem when used by my computer. As you can see I'm quite confused! Thanks for any help!
my code for canvas resizing...
reader.onload = function(e) {
preview.html('<img id="scream" src="' + e.target.result + '" ' + (preview.css('max-height') != 'none' ? 'style="max-height: ' + preview.css('max-height') + ';"' : '') + ' />')
element.addClass('fileupload-exists').removeClass('fileupload-new')
var canvas = document.createElement("canvas"),
context = canvas.getContext("2d"),
image = new Image();
image.onload = function () {
var img = this,
width = img.width,
height = img.height;
canvas.width = 300;
canvas.height = 300;
context.scale(0.25, 0.25);
context.drawImage(img, 0, 0);
var base64 = canvas.toDataURL();
//console.log(base64);
//$('form_6').val(base64);
document.body.appendChild(canvas);
};
image.src = e.target.result;
}