fabric.js - toDataURL shows blank page when there is image on canvas - javascript

I'm using fabric.js for my canvas application, toDataURL method works properly except when there is a image on canvas. When i add an image to canvas and call toDataURL it shows me a blank page.
//When i call it from chrome console
canvas.toDataURL();
//It returns a working data url with no problem.
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPAAAAGkCAYAAAAPPajHAAAgAElEQ…fpmwgogX1TrjoqP0FACewngtZh+iYCSmDflKuOyk8Q+H+CKCqUW0spTgAAAABJRU5ErkJggg=="
//When i execute same code in a .js file it returns a data url which shows a blank image.
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPAAAAGkCAYAAAAPPajHAAAKC0lEQ…sBAmEBAw6XJzoBA/YDBMICBhwuT3QCBuwHCIQFDDhcnugEHt/IAaW9dzALAAAAAElFTkSuQmCC"
It's interesting that it's working on chrome dev console but not works in .js file even if it's same code. I noticed that working data url finishes with '==' but other one not. However i don't know what this means.

You didn't give much to analyze but I'll go from there on my gut feeling.
The image you are using is probably violating Cross-Origin Resource Sharing (CORS) requirements. When this happens the canvas will return a blank image when you try to get the pixel data either by using canvas.toDataURL() or context.getImageData().
It basically happens when the image is not located on the same domain as the page or is loaded from the file:// protocol.
You can try to add the following to the image object before setting the source:
image.crossOrigin = 'anonymous';
image.src = '...';
From tag:
<img crossOrigin='anonymous' src='...' alt='' />
This will request permission from the server to use the image cross-origin. If the server allows you should be fine.
If not you will either have to copy the image to your own server so it loads from the same domain as your page, or create a proxy page that loads the image externally and serves it to your page from the proxy page (it sounds complicated but really isn't).
If the image does not show up at all on the canvas you are probably not using load callback which you need since image loading is asynchronous. If so just add:
image.onload = function() {
/// now you can draw the image to canvas
}
image.crossOrigin = 'anonymous';
image.src = '...';

The problem is solved. The point i missed is, i was calling toDataURL function before canvas is loaded, that's why it was showing me a blank page.
canvas.loadFromJSON(json,function(){
var dataURL = canvas.toDataURL();
});
This solved my problem when i gave toDataURL() function as a callback to loadFromJSON function.
But after a while i had a different issue about CORS when i tried to upload my images from s3 bucket and i solved this problem as upward solution.

I was facing the same issues when I was trying to generate images from the canvas using Fabricsjs and generate PDF from images using JSPDF so below is my case I also spend hours on this may this can help someone to save time.
Load the canvas from JSON that i.e
canvas.loadFromJSON(json, canvas.renderAll.bind(canvas), function(obj, object) {
//fabric.log(obj, object);
});
Canvas was drawing images all fine then I was generating the images from that canvas it was a case of multiple images in a single canvas and I was generating PDF based on each page of the canvas.
Instead of this
canvas.toDataURL('image/png', 0.1)
I used this and it starts returning me the propper images dataurl
var imgData = document.getElementById('canvas_0').toDataURL();
Below are snippets
$("#pdfSelector .canvas-container").each(function(index, value){ // canvas selector
html2canvas($("#canvas_"+index), { // html2canvas used to get images
onrendered: function(canvas) { // on successfully render of images
//var imgData = canvas.toDataURL('image/png', 0.1);
var imgData = document.getElementById('canvas_0').toDataURL();
const imgProps= doc.getImageProperties(imgData);
const pdfWidth = doc.internal.pageSize.getWidth();
const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width;
doc.addImage(imgData, 'PNG', 0, 0, pdfWidth, pdfHeight, 'page_'+index, 'FAST');
}
});
});

Related

use app image data on not respectable websites

I use canvas.getImageData to get the colour of one pixel in a HTML/js canvas.
If it isn't an respectable website it is load on, it won't do it, because of cross-origin data.
But I can't make the website respectable, because I want to open it as HTML document on my device, to be able, to open it offline.
I load images into the canvas, but when you don't have internet, it will load the image saved in localStorage (as dataURL).
Thanks for your attention
I'm happy about every answer which might help
There are two ways to load cross-origin images. First is using fetch and blob:
let image_url = await(await fetch(url, mode='cors')).blob();
let image = new Image;
image.url = image_url;
The other option is to set crossOrigin=anynymous on the image element:
downloadedImg = new Image;
downloadedImg.crossOrigin = "Anonymous";
downloadedImg.addEventListener("load", imageReceived, false);
downloadedImg.src = imageURL;
See https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image

Get pixel data back from programmatically generated image

I generate array of pixels in client side JavaScript code and convert it to a blob. Then I pass URL of the blob as image.src and revoke it at image.onload callback. I don't keep any references to the data, generated by the previous steps, so this data may be freed by GC.
There are many images generated this way, and it works fine. But sometimes user may want to save the generated image by clicking on a Save button near the image. I don't want to generate image again, because generation is slow, and the image is already generated and is visible on screen. So I want to get my pixels back from the image. I tried to create canvas again, draw image on it and then call toBlob, but browser treats this image as cross origin and throws an exception: "Failed to execute 'toBlob' on 'HTMLCanvasElement': tainted canvases may not be exported". Similar errors I get with canvas.toDataUrl and canvasContext.getImageData.
Is there a workaround for this problem?
I also tried to create canvases instead of images, but when I create the second canvas, the content of the first one clears.
Added
This error occurs only in the Chrome and other WebKit browsers. Firefox and MS Edge work fine. And when I commented out line of code that revoked blob url, this error disappeared - I can draw the image on the canvas and get its pixel data without CORS issues. But it is pointless to do so, because I already have blob that is not deleted.
But my page may generate many images - it depends on its user and is unlimited. Size of images is also unlimited - it may be useful to generate even 4096x4096 images. So I want to reduce memory consumption of my page as much as possible. And all these images should be downloadable. Generation of most images uses previously generated images, so to regenerate last image in a chain, I must to regenerate all images.
So I need a workaround only for Chrome browser.
Added 2
I tried to reproduce this problem in JS Fiddle, but couldn't. However locally my code doesn't work - I developed my app locally and I haven't tried running it on server. Create test.html file on your computer and open it in browser (locally, without server):
<html>
<body>
<pre id="log"></pre>
</body>
<script>
var log = document.getElementById("log");
var canvas = document.createElement("canvas");
canvas.width = canvas.height = 256;
var ctx = canvas.getContext("2d");
canvas.toBlob(function(blob) {
var img = new Image();
var blobUrl = URL.createObjectURL(blob);
img.src = blobUrl;
img.onload = function()
{
URL.revokeObjectURL(blobUrl);
ctx.drawImage(img, 0, 0);
try { canvas.toBlob(function(blob) { log.textContent += 'success\n'; }); }
catch(e) {log.textContent += e.message + '\n';}
};
});
</script>
</html>
It will print Failed to execute 'toBlob' on 'HTMLCanvasElement': Tainted canvases may not be exported..
So, I think, my workaround is to detect that page is run on combination of WebKit browser and file:/// protocol. And I can to defer revoking blob URL until page unload only for this combination.
That indeed sounds like a bug in chrome's implementation, you may want to report it.
What seems to happen is that chrome only checks if the image has been loaded through a clean origin when it is drawn for the first time on the canvas.
Since at this time, you already did revoke the blobURI, it can't map this URI to a clean origin (file:// protocol is quite strict on chrome).
But there seems to be a simple workaround:
By drawing this image on a [the reviver]* canvas before revoking the blobURI, chrome will mark the image as clean, and will remember it.
*[edit] Actually it seems it needs to be the same canvas...
So you can simply create your export canvas before-hand, and draw each images on it (to save memory you can even set it to 1x1px when you don't use it for the export) before you do revoke the image's src:
// somewhere accessible to the export logic
var reviver = document.createElement('canvas').getContext('2d');
// in your canvas to img logic
canvas.toBlob(function(blob){
var img = new Image();
img.onload = function(){
reviver.canvas.width = reviver.canvas.height = 1; // save memory
reviver.drawImage(this, 0,0); // mark our image as origin clean
URL.revokeObjectURL(this.src);
}
img.src = URL.createObjectURL(blob);
probablySaveInAnArray(img);
};
// and when you want to save your images to disk
function reviveBlob(img){
reviver.canvas.width = img.width;
reviver.canvas.height = img.height;
reviver.drawImage(img,0,0);
reviver.canvas.toBlob(...
}
But note that this method will create a new Blob, not retrieve the previous one, which has probably been Collected by the GarbageCollector at this time. But it is unfortunately your only way since you did revoke the blobURI...

Retrieving image data URi from HTTP response

The Problem: Imagine I have a td element structured as such:
<td td="" colspan="2">
<br>
<img src="ImageServlet" alt="random image" border="0" id="simpleRandomImg">
</td>
of course, with other HTML around it. The image contained within the servlet doesn't have a useful src indicated in the HTML (clearly) - however, when I open up the Network tab in Chrome, I can preview the loaded image itself and copy the image as a data URI - which gives me a stable URL like:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAAAyCAIAAACWMwO2AAAhQklEQVR42u2dB1Sb5/nonTRt2pPkJGmSNm180zhNTkabuPGKMeAwxBaIJYEYWmhLSEIggRhaaAIaIEASEggQew+zzPLEdjyy07T/9t9129t129u9z7mP9LnfXxZCBuLeS/71c77zIRQCGj89z+993+d7vS/GH7GxsXH+wGAwCf5ITExMSkpKTk5OSUlJTU1N80d6etcetcetc...
so, clearly there exists a stable reference to the loaded image. And clearly my browser 'knows' what that reference is, because I can retrieve the link to it as a data URI - but there's no reference to that data URI in the actual HTTP response. This probably seems a lot less mystical to someone who understands JavaScript, but that someone is not myself - so could someone explain what's going on here, and if there is some way to gather the data image URI from the HTTP response?
Attemped Solutions:
Did a little digging around in the HTTP response and located this bit of JavaScript which, apparently, handles the changing of images:
function changeImage() {
// makes a new image load
var obj=document.getElementById('simpleRandomImg');
if (obj != null) {
// append a unique index to force browser to reload
obj.src='ImageServlet?'+(cnt++);
}
However, nothing I see there gives any indication as to the actual URI location of the image. As before, if I open the Google Chrome network tab and attempt to retrieve the data image URI from the individual response, it works and gives me a valid URI - so, clearly the browser is receiving it. How can I access it?
e: to be clear, I do not control the website in question, so I can't 'fix' it by just changing the internal javascript - I'm viewing the site and am interested in whether or not it's possible to retrieve the loaded images short of screenshotting the page itself.
Something like this should work. Canvas API has a function called toDataURI
function getDataUri(url, callback) {
var image = new Image();
image.onload = function () {
var canvas = document.createElement('canvas');
canvas.width = this.naturalWidth; // or 'width' if you want a special/scaled size
canvas.height = this.naturalHeight; // or 'height' if you want a special/scaled size
canvas.getContext('2d').drawImage(this, 0, 0);
// Get raw image data
callback(canvas.toDataURL('image/jpg').replace(/^data:image\/(png|jpg);base64,/, ''));
// ... or get as Data URI
callback(canvas.toDataURL('image/jpg'));
};
image.src = url;
}
// Usage
// beware of server cors settings..
getDataUri('image url here', function(dataUri) {
console.log(dataUri)
// Do whatever you'd like with the Data URI!
});
You can set the src of the image to your URI by using setAttribute, like so:
obj.setAttribute('src', uriString)

save css computed background image as a variable or object for later use

Is it possible to get the image data from a computed style and save it in a variable or object for later use ?
For example in HTML web page I load the background from an image with CSS:
document.body.style.background = "url(...path/image.php) #FFF";
Now I need to save the image which was loaded in document.body.style.background
because i will use it later. I would like to save the image in localStorage as DATA URI and access it even after i restart the browser.
I cannot use AJAX or send request to server. I have to copy/save somehow from webpage which was loaded. But I'm not sure if this is possible and if yes , how
I cannot use the image URL because the url generates random images everytime, it something like http://url.php ... and not a standard image url
Maybe using HTML5 Canvas is possible to copy from document.body ?
the webpage is populated with other child elements
Create a canvas > object - load your generated image into the canvas, then use canvas.toDataURL to grab the base64 representation of the image. You can then store that base64 string as a variable and reload it whenever you want.
Detailed example in this SO answer: How to convert image into base64 string using javascript
There is no way to take a real screen-shot of any element through canvas. (at least not in WebAPI, in some browsers, extensions are allowed to do so).
You will have to create a new Image object, set it's src to the one of the background-image, and then only be able to draw it on the canvas.
But if the server answers with a no-cache, then the Image will just load an other image than the one set as background.
So you will have to make things in the other way :
First load the image through an Image Object.
Then draw this image to a canvas and get its dataURI version by calling canvas.toDataURL().
Finally, set your element's background to this dataURI you just extracted.
// first load your image from the server once, into an Image object
var img = new Image();
// wait for the image has loaded
img.onload = function(){
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
canvas.width = this.width;
canvas.height = this.height;
ctx.drawImage(this, 0,0);
// since I'm receiving photos, jpeg is better
var dataURL = canvas.toDataURL('image/jpeg');
// if we could use local storage in snippets...
//localStorage.setItem('background-image', dataURL);
// since we can't
saved = dataURL;
document.body.style.backgroundImage = 'url('+dataURL+')';
}
// I'm loading it from an external server
img.crossOrigin = 'anonymous';
img.src = 'http://lorempixel.com/200/200/?'+Math.random();
// since we can't use local storage in sand-boxed iframe
// we will use a global in this snippet
var saved;
button.onclick = function(){
// if we could use localStorage
// var saved = localStorage.getItem('background-image');
document.getElementById('result').style.backgroundImage = 'url('+saved+')';
};
div{width: 200px; height: 200px; border: 1px solid; background-color: white;}
body{width: 100vw; height: 100vh; margin: 0;}
<button id="button">set the div's background to the saved one</button>
<div id="result"></div>
Note that localStorage is limited so depending on your graphics, you may want to use the image/jpeg compression type of the toDataURL() method.
Also, you'll be limited by cross-origin policies, if the php page is not on the same domain, make sure it is correctly configured to accept cross-origin requests and set you Image object's crossOrigin property to "anonymous".
Ps : You could also avoid the canvas part by using an XMLHttpRequest and a FileReader(), wich will involve different cross-origin restrictions.

Read image from Url and save it in base64 in html / phonegap

I'm trying to read a URL (the resource of which is an image) and encode this image in base64 to save it in a database.
I've looked around Google and Stackoverflow and a lot of people say that it is impossible to save a base64 formatted image that you read from a URL.(Are they wrong?)
My steps are as follows:
I parse an XML file where there is a URL for the image. I'm trying to save this image in a base64 format in a DB.
Can anybody help me?
I can't readily put together an example of this, but it should be possible, assuming the image is coming from the same domain you are running the code on, or you can control the cross origin header for the image (or else you'll get a Cross-origin error).
Assuming this is the case, you can. Here is a JSFiddle where I encode the logo of JSFiddle: http://fiddle.jshell.net/5S6BY/ (since that logo is on the same domain as where the code is running, it works).
The trick is to draw it to a canvas, then convert that canvas to a base64.
Here is the code:
var url = "http://fiddle.jshell.net/img/logo.png";
var canvas = document.createElement('canvas');
document.body.appendChild(canvas);
var ctx = canvas.getContext('2d');
var image = new Image();
image.crossOrigin = 'anonymous';
image.addEventListener('load', function() {
ctx.drawImage(image, 0, 0, image.width, image.height);
document.body.innerHTML = canvas.toDataURL();
});
image.src = url;
It's pretty straight forward. Load the image, draw the image to the canvas, then call canvas.toDataUrl() to get the base64 encoded version, which you can use to do whatever you want.

Categories