tsayen / dom-to-image

Generates an image from a DOM node using HTML5 canvas
Other
10.28k stars 1.68k forks source link

Event Error when using toPng #243

Open ra-hulk opened 6 years ago

ra-hulk commented 6 years ago

Use case:

I have been using this script to capture JS Google Maps object (loaded in a div element) and saving it as a base64 image. But suddenly since yesterday (15/08) I have been getting this strange error, which I have not been able to solve. Please see screenshot attached below.

Error screenshot

I have debugged the problem to an extent and it seems like LOC 461 breaks when, for some reason, a deserialised SVG string is passed instead of a base64 string. As a result, screenshot is not generated. Any one had this problem ?

Browsers

lcechinel commented 6 years ago

Exactly the same here, using with Angular Google Maps

NadeemShadan commented 6 years ago

yup same issue here, other functions like toJpeg and toSvg are working though, also losing some layers of map while converting to jpeg. As of now converting to svg and from svg to base64 is the solution i use

rinocovelli commented 6 years ago

I'm getting the same problem using toJpeg, now can i solved the trouble using toSvg like @NadeemShadan says?

Any suggestion about this

ra-hulk commented 6 years ago

I was able to implement what @NadeemShadan suggested and it worked! Thanks heaps!

rinocovelli commented 6 years ago

@ra-hulk Can you give me more details about the solution?

NadeemShadan commented 6 years ago

@rinocovelli, from what you said i believe you are using toJpeg and it is not working, for me toJpeg was working though. So what i did to convert my dom to image was to use the toSvg function as mentioned in dom-to-image(this project).

function filter (node) {
    return (node.tagName !== 'i');
}

domtoimage.toSvg(document.getElementById('my-node'), {filter: filter})
    .then(function (dataUrl) {
        /* do something */
    });

(you need not use the filter) the dataUrl variable will return an encoded svg of the dom.

rinocovelli commented 6 years ago

Dear friends, I solved updating makeImage method in this way:

I changed image.src = uri with image.src = encodeURI(uri);

mrmikemeaney commented 5 years ago

I have the same issue, solution from @rinocovelli did not work, on its own:

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

The other suggestion of converting to SVG first did not work either, toSvg is throwing same error for me.

For me it also seems to be related to images in the DOM. What worked (sometimes) was the encodeURI change above, along with filtering images out:

domtoimage.toPng(node, { filter: node => { return node.tagName !== 'IMG'; }})
   .then(dataUrl => { .... });

On some pages I still get the same error as the first post.

But see what the above changes deliver on this page - not pleasant, the %0A comes from the encodeURI hack:

screenshot

Here's how it looks without encodeURI hack: screenshot

stsglnt commented 5 years ago

Faced the same bug with img tag. The issue is related to image rendering. If I run .toPng only when images have been rendered it works fine. https://stackoverflow.com/questions/14578356/how-to-detect-when-an-image-has-finished-rendering-in-the-browser-i-e-painted

tete1030 commented 4 years ago

@mrmikemeaney the %0A problem is caused by escaping already conducted before encodeURI. Instead of using

image.src = encodeURI(uri);

a better way of doing this is to image

The %0A is because util.escapeXhtml encodes \n to %0A, and encodeURI encodes %0A again.

The reason of using encodeURIComponent on the data segment of the URI is that encodeURI would not encode # character. However, because # had already been encoded by util.escapeXhtml, you wouldn't notice it.

So the simple solution is to remove the use of original util.escapeXhtml and just encode URI at the final stage.

ptrk8 commented 4 years ago

@mrmikemeaney the %0A problem is caused by escaping already conducted before encodeURI. Instead of using

image.src = encodeURI(uri);

a better way of doing this is to image

The %0A is because util.escapeXhtml encodes \n to %0A, and encodeURI encodes %0A again.

The reason of using encodeURIComponent on the data segment of the URI is that encodeURI would not encode # character. However, because # had already been encoded by util.escapeXhtml, you wouldn't notice it.

So the simple solution is to remove the use of original util.escapeXhtml and just encode URI at the final stage.

@tete1030 Excellent explanation.

The TLDR is comment out .then(util.escapeXhtml) from makeSvgDataUri as shown below.

function makeSvgDataUri(node, width, height) {
        return Promise.resolve(node)
            .then(function (node) {
                node.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml');
                return new XMLSerializer().serializeToString(node);
            })
            // .then(util.escapeXhtml)
            .then(function (xhtml) {
                return '<foreignObject x="0" y="0" width="100%" height="100%">' + xhtml + '</foreignObject>';
            })
            .then(function (foreignObject) {
                return '<svg xmlns="http://www.w3.org/2000/svg" width="' + width + '" height="' + height + '">' +
                    foreignObject + '</svg>';
            })
            .then(function (svg) {
                return 'data:image/svg+xml;charset=utf-8,' + svg;
            });
    }

Then edit the makeImage method in the following way.

function makeImage(uri) {
            return new Promise(function (resolve, reject) {
                var image = new Image();
                image.onload = function () {
                    resolve(image);
                };
                image.onerror = reject;
                // image.src = uri;
                image.src = encodeURI(uri);
            });
        }
kalandher commented 2 years ago

Exactly the same here, using with Angular Google Maps

How did you solved it? i'm facing same issue using angular-google-maps

michalbie commented 7 months ago

Happens to me only when using some maps