Dom-to-image: Cross Origin Image Requests

Created on 3 Apr 2017  ·  8Comments  ·  Source: tsayen/dom-to-image

Use case:

The current image loading implementation fails when it tries to fetch image resources where the server does not have CORS enabled. However it is possible to load a CORS image, in an image tag, copy the content to a canvas and get the data URL for use using the code in the link below.

CORS Enabled Image

For example I am trying to take a screen capture of a Leaflet map using Stamen Tiles, the problem is that the Stamen Tiles server does not always have the "Access-Control-Allow-Origin: *" header depending on how the image is requested causing errors.

I was able to get this to partially work by changing the getAndEncode method to use makeImage with crossOrigin="anonymous" then drawing to a canvas and reading the data out. It was just some prototype hacks to see if this was possible and it appears it is.

Most helpful comment

For anyone who finds this issue via Google, I found a way to get this to work for my situation, but it was a caching issue that caused the missing CORS header, so if your server doesn't have that header this wont fix things for you.

https://github.com/tsayen/dom-to-image/pull/129

All 8 comments

hey @cnatis ,
I am also facing the same issue,
could you elaborate more on the hack . Because setting the crossOrigin attribute in the image object of makeImage doesnt seem to help me.
The Access-Control-Allow-Origin: * has to be there for any XMLHttpRequest fetching the image.For servers that do not set this, the
No 'Access-Control-Allow-Origin' header is present on the requested resource
error is being encountered

Hi @sarath-sasikumar

The hack starts at the function getAndEncode in newUtil, what I did was changed getAndEncode to request the images via makeImage instead of XHR. In getAndEncode once you have the image in memory, you can then draw it on a canvas and return the dataURL so that getAndEncode still returns an encoded URL.

The makeImage method was modified to add the "crossOrigin" property.

This works conceptually because the image is loaded via the image tag with crossOrigin set which fixes the CORS issues with the request. Once we have the image we can render it to a canvas and read out the data; we can read from the canvas because our image tag will not taint the canvas due to the crossOrigin property.

Hi @cnatis. I'm also seeing the same issue. Would it be possible to see a gist of your getAndEncode function? Thanks a lot.

@jamesburke-examtime

I threw away the code I was playing with, but here is a gist with the changes I made (I didn't test this)

    function makeImage(uri) {
        return new Promise(function (resolve, reject) {
            var image = new Image();
            image.onload = function () {
                resolve(image);
            };
            image.crossOrigin = 'Anonymous';
            image.onerror = reject;
            image.src = uri;
        });
    }

    function getAndEncode(url) {
        return makeImage(url)
            .then(img => {
                let canvas = document.createElement('canvas');
                canvas.width = img.width;
                canvas.height = img.height;

                // Copy the image contents to the canvas
                var ctx = canvas.getContext("2d");
                ctx.drawImage(img, 0, 0);
                return canvas.toDataURL();
            });
    }

I just want to note as well this uses the following technology -> https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image
This does not replace the CORS header, it is still required. For some reason the server I was working with was only adding the header on certain requests, in my case using the image tag made sure the response had the CORS header which allowed me to use the image on my canvas without tainting it.

@cnatis Thanks a lot for that! Very helpful and informative too.

Unfortunately, it didn't work for me as I was still seeing images being loaded without the CORS header. As it turns out, I think my browser was caching versions of the image loaded without the crossorigin attribute previously, so the cached version didn't include the Access-Control-Allow-Origin header. Adding the crossorigin="anonymous" attribute to the img and then clearing the cache fixed my problem. :)

Hi. Unfortunately CORS issues are the main reason for issues being opened on this lib :smile:
I would happily add image.crossOrigin = 'Anonymous'; to every image just to prevent some of the bugs, but I'm afraid I don't know how to test that. The best (and frankly speaking, the only acceptable) way would be to add a regression that fails without this property set and passes with it. I'll have to think about that.

@tsayen Yes unfortunately after thinking about this, I realized it's actually an issue with the server I was working with. Ill close this now as I don't feel relying on undocumented observed behaviour (crossOrigin property) for a fix is not a good solution.

Thank you for the work you have done!

For anyone who finds this issue via Google, I found a way to get this to work for my situation, but it was a caching issue that caused the missing CORS header, so if your server doesn't have that header this wont fix things for you.

https://github.com/tsayen/dom-to-image/pull/129

Was this page helpful?
0 / 5 - 0 ratings

Related issues

unit57 picture unit57  ·  6Comments

shahebaz444 picture shahebaz444  ·  5Comments

distroyq picture distroyq  ·  6Comments

DanielZambranoC picture DanielZambranoC  ·  5Comments

yangqin007 picture yangqin007  ·  3Comments