Heya, so I'm running into a weird bug/edgecase. Check out the following code:
var i = new Image();
i.src = 'http://ia.media-imdb.com/images/M/MV5BMTIxOTAxNTc4NF5BMl5BanBnXkFtZTcwOTg1NzQyMQ##._V1._SX97_SY140_.jpg';
console.log(i.width);
This works fine in chrome (as it's referring to a real image), but fails in FF. Thought it may have to do with the 'at' signs or the double extension (eg. '._V1._SX97_SY140_.jpg'), but don't really know.
Thanks for any help.
keep in mind, that the image is loaded asynchronously. You need to assign a event handler for the load event of the image and get the width there:
var i = new Image();
i.onload = function() {
console.log(this.width);
}
i.src = 'http://ia.media-imdb.com/images/M/MV5BMTIxOTAxNTc4NF5BMl5BanBnXkFtZTcwOTg1NzQyMQ##._V1._SX97_SY140_.jpg';
A 403 response header means Forbidden (wiki), that you are not allowed to access the resource.
imdb.com might be doing this to prevent hotlinking of their images in other sites.
Related
Task
I am currently trying to create a web extension for Firefox.
It should be able to read images' pixels.
For that purpose, I am rendering the image on an invsible canvas, and then I want to read it.
Example Code
function getImdata(reference) {
var canvas=document.createElement("canvas");
canvas.width=reference.naturalWidth;
canvas.height=reference.naturalHeight;
var context=canvas.getContext("2d");
context.drawImage(reference,0,0);
return context.getImageData(0,0,reference.naturalWidth,reference.naturalHeight); //Here I actually get the error...
}
Problem
However, I am getting a "Security Error" if I use "getImageData()".
Question
So I need a workaround, but couldn't find anything myself.
How can I read images' pixels without getImageData() ?
EDIT
Apparently it has something to do with CORS : HTML5 Canvas getImageData and Same Origin Policy
Thanks in advance!
There is. Since you're running from an extension your extension will have privileged access to cross-origin sources but only if loaded via fetch() and XMLHttpRequest() from a content script (or background script) - excerpt from that link:
Content scripts get the same cross-domain privileges as the rest of
the extension: so if the extension has requested cross-domain access
for a domain using the permissions key in manifest.json, then its
content scripts get access that domain as well.
This is accomplished by exposing more privileged XHR and fetch
instances in the content script [...]
Please note that these calls when called from a content script will not set origin and referer headers which sometimes can cause problems if the cross-origin site expects these to be set - for those cases you will need to use the non-privileged content.XMLHttpRequest or content.fetch() which will bring you back to square one.
The permissions in the manifest file (or if set permissions dynamically) must also allow access to these cross-origin sites.
This means however that you will have to "reload" the image source separately via these calls. You can do this the following way by first obtaining the original URL to the image you want to load, say, from a content script:
// example loading all images in current tab
let images = document.querySelectorAll("img");
for(let image of images) loadAsBitmap(image.src); // some sub-call using the url
Then load that source via the content script's fetch():
fetch(src).then(resp => { // load from original source
return resp.blob(); // obtain a blob
}).then(blob => { // convert blob, see below
// ...
};
When the blob is obtained you can convert it to an Object-URL and set that as source for an image and be able to go around the cross-origin restriction we otherwise face. In the content script, next steps would be:
let url = URL.createObjectURL(blob); // attach Object-URL to blob
let img = new Image(); // create image element *
img.onload = () => { // attach handler
let c = document.createElement("canvas"); // create canvas
let ctx = c.getContext("2d"); // get context
c.width = img.width; // canvas size = image
c.height = img.height;
ctx.drawImage(img, 0, 0); // draw in image
URL.revokeObjectURL(url); // remove reference.
let imageData =
ctx.getImageData(0,0,c.width,c.height); // get image data
// .. callback to a function that handles the image data
};
img.src = url; // start loading blob
I'm a newbie in HTML5+JS, I want to develop an hybrid app using ocrad.js.
The code given below, downloaded from github page is perfectly working for me(Chrome 32.0.1).
<html>
<head>
</head>
<body>
<script src="../ocrad.js"></script>
<script>
function OCRImage(image){
var canvas = document.createElement('canvas')
canvas.width = image.naturalWidth;
canvas.height = image.naturalHeight;
canvas.getContext('2d').drawImage(image, 0, 0)
return OCRAD(canvas)
}
function OCRPath(url, callback){
var image = new Image()
image.src = url;
image.onload = function(){ callback(OCRImage(image)) }
}
function OCRFile(file, callback){
var reader = new FileReader();
reader.onload = function(){ OCRPath(reader.result, callback); }
reader.readAsDataURL(file)
}
</script>
<input type="file" onchange="OCRFile(this.files[0], function(text){alert(text)})">
</body>
</html>
When I called OCRAD() API in my code its giving Uncaught SecurityError: Failed to execute 'getImageData' on 'CanvasRenderingContext2D': the canvas has been tainted by cross-origin data.
My CODE
<html>
<head>
<script src="../ocrad.js"></script>
<body>
<canvas id="cancan" width="800", height="500">Test image</canvas>
<script type="text/javascript">
function imageLoaded(ev) {
element = document.getElementById("cancan");
c = element.getContext("2d");
im = ev.target;
width = element.width;
height = element.height;
c.drawImage(im, 0, 0);
var data1=OCRAD(c);
console.log(data1);
}
im = new Image();
im.src = "message.png";
im.onload = imageLoaded;
</script>
</body>
</html>
I have seen similar Stackoverflow Q&A here but it didn't help me to solve the issue. Please answer if any one had any comment on this issue who have worked with Ocrad.js.
OR
Is there anyother way to pass my image file (here message.png in second code example) as an argument to OCRFile() function in first code example ? (Simply I want to pass an image stored in an local file URL to OCRAD() Call to return text. )
Thanks in advance.... :)
It is a cross-origin issue which is a security mechanism in browsers.
You will either need to:
Move image to same origin as the page (origin = domain, port and protocol)
Request CORS usage from the other origin if you can't move the image
Use a proxy page to load the image (see one in action here - note: I do not know this site so use only for testing with non-critical data).
A request can be made like this (assuming im contains the image you want to OCR treat):
function imageLoaded(ev) {
element = document.getElementById("cancan");
c = element.getContext("2d");
width = element.width;
height = element.height;
c.drawImage(this, 0, 0); // 'this' = current image loaded
var data1 = OCRAD(c);
console.log(data1);
}
var im = new Image();
im.onload = imageLoaded; // set onload before src
im.crossOrigin = 'anonymous'; // request CORS usage before setting src
im.src = "message.png";
If a request will work is entirely up to the server which may deny the request (which is default behavior in most cases).
In that case only moving the image or setting up a proxy page to load the external image will allow usage of it. Note that file:// or local files are considered different origins.
A proxy page is in essence a page you pass the image url to as an argument. The page will then, on server side, load the image and pass the data back to your first (requesting) page. This way you can "stream" the image through your own server removing CORS restrictions but at the expense of increased traffic on your own server. Some server may also block this approach by denying external access (ie. by referrer or IP etc.)
See Cross-Origin Resource Sharing for more details.
Precursor: I know there have been a few questions already asked about this topic, but none of them seem to offer a straight JavaScript only solution.
So I ran into this error where I am trying to get the pixel data from a canvas using something like context.getImageData(), my exact code can been seen here or below:
<canvas id="imageCanvas" width="600" height="800"></canvas>
// Load Image
var canvas = document.getElementById('imageCanvas');
var image = new Image();
image.src = 'http://emoticons.pw/emoticons/emoticon-faces-002-medium.png';
image.onload = function() {
width=image.width;
height=image.height;
var context = canvas.getContext('2d');
context.fillStyle="#024359"; // canvas background color
context.fillRect(0, 0, width, height);
context.drawImage(this,0,0);
imageData = context.getImageData(0,0,width, height); // PROBLEM HERE
context.putImageData(imageData, 0, 0);
}
I get the following errors in Chrome:
Unable to get image data from canvas because the canvas has been
tainted by cross-origin data.
and then a security error as well. I don't want to make server changes or start chrome with a weird instruction thing. I feel like there has to be something I can do in JavaScript.
Using local images only is not a problem, but when trying that, I got the same error!
I am trying to do this without a server, if I put this on my "default" godaddy web server, are all my problems solved? I heard rumors dropbox could also simulate a server close enough?
You can't use file:// if you're using that (Chrome allow you to override this but I won't recommend it).
For local testing use a light-weight server such as Mongoose which allow you use http://localhost as a domain to test your local pages. This way you avoid problems with CORS.
If you need to host images on a different domain you need to make sure they support cross-origin usage.
DropBox and ImgUrl (I recommend the latter for just images) both support CORS usage, but you need to request such usage by adding the crossOrigin property to your image before setting the source (or include it in the HTML tag if you're using that):
var img = new Image;
img.onload = myLoader;
img.crossOrigin = ''; ///=anonymous
img.src = 'http://imgur.com/....';
or in HTML:
<img src="..." alt="" crossOrigin="anonymous" />
Make sure you put the img.setAttribute('crossOrigin', ''); before you set the source of your image object. just like this:
var img = document.createElement("img");
//fix crossorigin here...
img.setAttribute('crossOrigin', '');
//after that put your source
img.src = imageSrcURL;
document.body.appendChild(img);
I want to perform a javascript xhr request for a png file from a C# webserver which I wrote.
Here is the code I use
var imgUrl = "http://localhost:8085/AnImage.png?" + now;
var request = new XMLHttpRequest();
request.open('GET', imgUrl, false);
request.send(); // this is in a try/catch
On the server-side I send back the file and add a Content-Disposition header.
I obtain the following response
I made sure that Content-Disposition was attached in the headers after the Content-Type (the screenshot is from Firebug, which appends in alphabetical order).
The results is that no dialog box is triggered, am I missing something in the response?
edit:
I want to perform everything in javascript for several reasons.
First: I don't want to show the image and I want to keep everything behind the curtain.
Second: when requesting the image I want the Content-Disposition to be added only on particular requests. Such requests are marked with a "Warning" header with value "AttachmentRequest"
request.setRequestHeader("Warning","AttachmentRequest");
I don't think Content-Disposition triggers any file save dialog when the request is via XHR. The use of XHR suggests you're going to handle the result in code.
If you want the user to be prompted to save the image to a file, I've used this technique successfully:
window.open("http://localhost:8085/AnImage.png?" + now);
It has the downside that it flashes a blank open window briefly until the header arrives, then the new window closes and the "save file" dialog box appears.
Using an iframe may prevent the window flashing:
var f = document.createElement('iframe');
f.style.position = "absolute";
f.style.left = "-10000px";
f.src = "http://localhost:8085/AnImage.png?" + now;
document.body.appendChild(f);
Separately, I wonder what effect (if any) Content-Disposition has on the handling of an img element:
var img = document.createElement('img');
img.style.position = "absolute";
img.style.left = "-10000px";
img.src = "http://localhost:8085/AnImage.png?" + now;
document.body.appendChild(img);
I haven't tried that, but the browser might respect the header. You'd need to be sure to test on all of the browsers you want to support.
I would like to try and set up a progress bar for several images while their loading and haven't been able to get it to work. I have several images that are preloaded like:
var Image1 = new Image();
var Image2 = new Image();
var Image3 = new Image();
Image1.onload = moveProgressBar();
Image2.onload = moveProgressBar();
Image3.onload = moveProgressBar();
Image1.src = url;
Image2.src = url;
Image3.src = url;
Something weird is happening since they're immediately running the moveProgressBar() function even though the images aren't entirely loaded yet. This happens even when approaching the page with no cache and with cache. Am I missing something? Any help would be appreciated.
The js onload event on images is a big messy mess. Different browsers handle it differently, and different versions (mostly IE) handle it differently. Plus, most browsers have iffy bugs.
Here is a short checklist:
set your onload BEFORE you set your src. (Wait for it, it will get quirkier...)
Handle onerror as well.
Handle images that already was cached. Sometimes they will not fire onload, while other times they will fire and do so imediately.
Use a generic belt-and-suspenders setTimeout()-function that polls to see if the image has gotten a width or height in the DOM. This is done differently in different browsers/versions, which means you'll have to use feature detection, starting with naturalWidth, then move on to getAttribute(width) before image.width.
And then you will still probably have some bugs... but you will only know if you test all browser/versions you intend to support.
For reference, the error was that Noah was calling each function and assigning the result to the onload. It should have been:
Image1.onload = moveProgressBar; // note the lack of ()