Git Product home page Git Product logo

dom-to-image-more's Introduction

DOM to Image

Version Bundle Size Open Issues GitHub Repo stars Twitter

Breaking Change Notice

The 3.x release branch included some breaking changes in the very infrequently used ability to configure some utility methods used in this internal processing of dom-to-image-more. As browsers have matured, many of the hacks we're accumulated over the years are not needed, or better ways have been found to handle some edge-cases. With the help of folks like @meche-gh, in #99 we're stripping out the following members:

  • .mimes - was the not-very-comprehensive list of mime types used to handle inlining things
  • .parseExtension - was a method to extract the extension from a filename, used to guess mime types
  • .mimeType - was a method to map file extensions to mime types
  • .dataAsUrl - was a method to reassemble a data: URI from a Base64 representation and mime type

The 3.x release branch should also fix more node compatibility and iframe issues.

What is it

dom-to-image-more is a library which can turn arbitrary DOM node, including same origin and blob iframes, into a vector (SVG) or raster (PNG or JPEG) image, written in JavaScript.

This fork of dom-to-image by Anatolii Saienko (tsayen) with some important fixes merged. We are eternally grateful for his starting point.

Anatolii's version was based on domvas by Paul Bakaus and has been completely rewritten, with some bugs fixed and some new features (like web font and image support) added.

Moved to 1904labs organization from my repositories 2019-02-06 as of version 2.7.3

Installation

NPM

npm install dom-to-image-more

Then load

/* in ES 6 */
import domtoimage from 'dom-to-image-more';
/* in ES 5 */
var domtoimage = require('dom-to-image-more');

Usage

All the top level functions accept DOM node and rendering options, and return promises, which are fulfilled with corresponding data URLs.
Get a PNG image base64-encoded data URL and display right away:

var node = document.getElementById('my-node');

domtoimage
    .toPng(node)
    .then(function (dataUrl) {
        var img = new Image();
        img.src = dataUrl;
        document.body.appendChild(img);
    })
    .catch(function (error) {
        console.error('oops, something went wrong!', error);
    });

Get a PNG image blob and download it (using FileSaver, for example):

domtoimage.toBlob(document.getElementById('my-node')).then(function (blob) {
    window.saveAs(blob, 'my-node.png');
});

Save and download a compressed JPEG image:

domtoimage
    .toJpeg(document.getElementById('my-node'), { quality: 0.95 })
    .then(function (dataUrl) {
        var link = document.createElement('a');
        link.download = 'my-image-name.jpeg';
        link.href = dataUrl;
        link.click();
    });

Get an SVG data URL, but filter out all the <i> elements:

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

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

Get the raw pixel data as a Uint8Array with every 4 array elements representing the RGBA data of a pixel:

var node = document.getElementById('my-node');

domtoimage.toPixelData(node).then(function (pixels) {
    for (var y = 0; y < node.scrollHeight; ++y) {
        for (var x = 0; x < node.scrollWidth; ++x) {
            pixelAtXYOffset = 4 * y * node.scrollHeight + 4 * x;
            /* pixelAtXY is a Uint8Array[4] containing RGBA values of the pixel at (x, y) in the range 0..255 */
            pixelAtXY = pixels.slice(pixelAtXYOffset, pixelAtXYOffset + 4);
        }
    }
});

Get a canvas object:

domtoimage.toCanvas(document.getElementById('my-node')).then(function (canvas) {
    console.log('canvas', canvas.width, canvas.height);
});

Adjust cloned nodes before/after children are cloned sample fiddle

const adjustClone = (node, clone, after) => {
    if (!after && clone.id === 'element') {
        clone.style.transform = 'translateY(100px)';
    }
    return clone;
};

const wrapper = document.getElementById('wrapper');
const blob = domtoimage.toBlob(wrapper, { adjustClonedNode: adjustClone });

All the functions under impl are not public API and are exposed only for unit testing.


Rendering options

filter

A function taking DOM node as argument. Should return true if passed node should be included in the output (excluding node means excluding it's children as well). Not called on the root node.

bgcolor

A string value for the background color, any valid CSS color value.

height, width

Height and width in pixels to be applied to node before rendering.

style

An object whose properties to be copied to node's style before rendering. You might want to check this reference for JavaScript names of CSS properties.

quality

A number between 0 and 1 indicating image quality (e.g. 0.92 => 92%) of the JPEG image. Defaults to 1.0 (100%)

cacheBust

Set to true to append the current time as a query string to URL requests to enable cache busting. Defaults to false

imagePlaceholder

A data URL for a placeholder image that will be used when fetching an image fails. Defaults to undefined and will throw an error on failed images

copyDefaultStyles

Set to true to enable the copying of the default styles of elements. This will make the process faster. Try disabling it if seeing extra padding and using resetting / normalizing in CSS. Defaults to true.

useCredentialFeatures

Allows optionally setting the useCredentials option if the resource matches a pattern in the useCredentialFilters array.

Alternative Solutions to CORS Policy Issue

Are you facing a CORS policy issue in your app? Don't worry, there are alternative solutions to this problem that you can explore. Here are some options to consider:

  1. Use the option.corsImg support by passing images With this option, you can setup a proxy service that will process the requests in a safe CORS context.

  2. Use third-party services like allOrigins. With this service, you can fetch the source code or an image in base64 format from any website. However, this method can be a bit slow.

  3. Set up your own API service. Compared to third-party services like allOrigins, this method can be faster, but you'll need to convert the image URL to base64 format. You can use the "image-to-base64" package for this purpose.

  4. Utilize server-side functions features of frameworks like Next.js. This is the easiest and most convenient method, where you can directly fetch a URL source within server-side functions and convert it to base64 format if needed.

By exploring these alternative solutions, you can overcome the CORS policy issue in your app and ensure that your images are accessible to everyone.

Browsers

It's tested on latest Chrome and Firefox (49 and 45 respectively at the time of writing), with Chrome performing significantly better on big DOM trees, possibly due to it's more performant SVG support, and the fact that it supports CSSStyleDeclaration.cssText property.

Internet Explorer is not (and will not be) supported, as it does not support SVG <foreignObject> tag

Safari is not supported, as it uses a stricter security model on <foreignObject> tag. Suggested workaround is to use toSvg and render on the server.`

Dependencies

Source

Only standard lib is currently used, but make sure your browser supports:

Tests

As of this v3 branch chain, the testing jig is taking advantage of the onclone hook to insert the clone-output into the testing page. This should make it a tiny bit easier to track down where exactly the inlining of CSS styles against the DOM nodes is wrong.

Most importantly, tests only depend on:

  • ocrad.js, for the parts when you can't compare images (due to the browser rendering differences) and just have to test whether the text is rendered

How it works

There might some day exist (or maybe already exists?) a simple and standard way of exporting parts of the HTML to image (and then this script can only serve as an evidence of all the hoops I had to jump through in order to get such obvious thing done) but I haven't found one so far.

This library uses a feature of SVG that allows having arbitrary HTML content inside of the <foreignObject> tag. So, in order to render that DOM node for you, following steps are taken:

  1. Clone the original DOM node recursively

  2. Compute the style for the node and each sub-node and copy it to corresponding clone

    • and don't forget to recreate pseudo-elements, as they are not cloned in any way, of course
  3. Embed web fonts

    • find all the @font-face declarations that might represent web fonts

    • parse file URLs, download corresponding files

    • base64-encode and inline content as data: URLs

    • concatenate all the processed CSS rules and put them into one <style> element, then attach it to the clone

  4. Embed images

    • embed image URLs in <img> elements

    • inline images used in background CSS property, in a fashion similar to fonts

  5. Serialize the cloned node to XML

  6. Wrap XML into the <foreignObject> tag, then into the SVG, then make it a data URL

  7. Optionally, to get PNG content or raw pixel data as a Uint8Array, create an Image element with the SVG as a source, and render it on an off-screen canvas, that you have also created, then read the content from the canvas

  8. Done!

Using Typescript

  1. Use original dom-to-image type definition npm install @types/dom-to-image --save-dev

  2. Create dom-to-image-more type definition (dom-to-image-more.d.ts)

    declare module 'dom-to-image-more' {
     import domToImage = require('dom-to-image-more');
     export = domToImage;
    }

Things to watch out for

  • if the DOM node you want to render includes a <canvas> element with something drawn on it, it should be handled fine, unless the canvas is tainted - in this case rendering will rather not succeed.

  • at the time of writing, Firefox has a problem with some external stylesheets (see issue #13). In such case, the error will be caught and logged.

Authors

Marc Brooks, Anatolii Saienko (original dom-to-image), Paul Bakaus (original idea), Aidas Klimas (fixes), Edgardo Di Gesto (fixes), ๆจŠๅ†ฌ Fan Dong (fixes), Shrijan Tripathi (docs), SNDST00M (optimize), Joseph White (performance CSS), Phani Rithvij (test), David DOLCIMASCOLO (packaging), Zee (ZM) @zm-cttae (many major updates), Joshua Walsh @JoshuaWalsh (Firefox issues), Emre Coban @emrecoban (documentation), Nate Stuyvesant @nstuyvesant (fixes), King Wang @eachmawzw (CORS image proxy), TMM Schmit @tmmschmit (useCredentialsFilters)

License

MIT

dom-to-image-more's People

Contributors

aidask avatar andonizubimendi avatar andreasg123 avatar andystanton avatar annoyingtechnology avatar cnatis avatar denis-sokolov avatar dependabot[bot] avatar emrecoban avatar fredzeng avatar hakimio avatar idisposable avatar joswhite avatar kufii avatar mfyuce avatar mrharel avatar muhammad-usman-anwar avatar nadavspi avatar nstuyvesant avatar pbakaus avatar qw-in avatar shrijan00003 avatar stebogit avatar tclausing avatar tmmschmit avatar tsayen avatar ulikoehler avatar willemmulder avatar yoshiwalsh avatar zm-cttae avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dom-to-image-more's Issues

Custom fonts problem on iOS15

Use case: description, code

I'm rendering some text with custom fonts and in the render only Helvetica or Times New Roman are displayed (replacing my custom font). This problem occurs only on iOS 15 but not with the original dom-to-image version, only on the -more.

@charset "UTF-8";

@font-face {
  font-family: 'Lato';
  src: url('../fonts/lato-regular.eot');
  src: url('../fonts/lato-regular.eot?#iefix') format('embedded-opentype'),
    url('../fonts/lato-regular.woff2') format('woff2'),
    url('../fonts/lato-regular.woff') format('woff'),
    url('../fonts/lato-regular.ttf') format('truetype'),
    url('../fonts/lato-regular.svg#Lato') format('svg');
  font-style: normal;
  font-weight: 400;
}

I know iOS15 changed some fonts aspects, any way to fix this?

Browsers

iOS Crome
iOS Firefox
iOS Safari

background image not rendered

i try with a div that has background image๏ผŒbut not rendered.

The relevant line in the code

return inliner.inlineAll(background)
.then(function(inlined) {
backgroundNode.style.setProperty(
'background',
inlined,
background
);
})
.then(function() {
return backgroundNode;
});

i change line 861 to backgroundNode.style.getPropertyPriority('background') and it works

Absolute positioned elements don't render properly

Use case

When cloning a node that has absolute positioning, domtoimage produces an image whose contents are shifted by the position of the node. This results in a significant portion of the image being missing. See this jsfiddle.

Expected behavior

The image produced by domtoimage should be the same, whether the node has absolute positioning applied or not.

Actual behavior

The image produced is shifted, with most of the content missing.

Library version

2.8.0

Browsers

  • Chrome 90
  • Firefox 88

Possible solution

I fixed this in my own copy of dom-to-image-more.js by adding the following code to the end of the copyProperties function, which starts on line 289:

// Remove positioning of root elements, which stops them from being captured correctly
if (root) {
    ['inset-block', 'inset-block-start', 'inset-block-end'].forEach((prop) => target.removeProperty(prop));
    ['left', 'right', 'top', 'bottom'].forEach((prop) => {
        if (target.getPropertyValue(prop)) {
            target.setProperty(prop, '0px');
        }
    });
}

This sets any properties like top and left to 0 if they had been set on the cloned node. It also removes inset-block properties, which is a newer CSS property that has the same effect.

I'd be happy to create a pull request for this issue, if anyone would be willing to review it

TypeError after upgrading to 2.9.1

First of all: Thanks for this fantastic project!

I am using dom-to-image-more in combination with pdfmake. Since upgrading to version 2.9.1 I am receiving a TypeError in browser console:

ERROR Error: Uncaught (in promise): TypeError: c.getComputedStyle is not a function TypeError: c.getComputedStyle is not a function at P (dom-to-image-more.min.js:3) at e (dom-to-image-more.min.js:3) at ZoneDelegate.invoke (zone.js:372) at Object.onInvoke (core.js:28672) at ZoneDelegate.invoke (zone.js:371) at Zone.run (zone.js:134) at zone.js:1276 at ZoneDelegate.invokeTask (zone.js:406) at Object.onInvokeTask (core.js:28659) at ZoneDelegate.invokeTask (zone.js:405) at resolvePromise (zone.js:1213) at resolvePromise (zone.js:1167) at zone.js:1279 at ZoneDelegate.invokeTask (zone.js:406) at Object.onInvokeTask (core.js:28659) at ZoneDelegate.invokeTask (zone.js:405) at Zone.runTask (zone.js:178) at drainMicroTaskQueue (zone.js:582) at invokeTask (zone.js:491) at ZoneTask.invoke (zone.js:476)

Downgrading to version 2.8.0 solves the problem. A fix would be appreciated.

Incomplete copying of contents

Use case: description, code

See this URL: http://www.nossebromediaproduktion.se/test/domtopng-test/domtopng-test.html

Expected behavior

Clicking the button I expect it to copy the exact displayed contents of the wrapper DIV element above the button to a PNG image below the button.

Actual behavior (stack traces, console logs etc)

Copies the SVG content that is not in ::before or ::after CSS rules to the PNG image below the button. This example uses a multitude of ::before and ::after to add extra graphical content, so it may be an edge case.

Library version

2.8.0

Browsers

  • Chrome 76
  • Firefox 69

bower package not found

tried to install with bower install dom-to-image-more but was unsuccessful.

bower error: ENOTFOUND Package dom-to-image-more not found.

Font issue on Chrome Android

For some reason, on chrome mobile, when an image is generated, there are some differences and looks like it is related to the font-family or kerning... not sure what is happening. Everything works fine on browser.

DOM:
WhatsApp Image 2021-02-15 at 22 33 16

Generated image:
WhatsApp Image 2021-02-15 at 21 45 05 (1)

Any ideas on what is happening and why the generated image is not matching the DOM on chrome for mobile?

Inlined data URLs being encoded without a mimetype.

Use case: description, code

https://jsfiddle.net/z9042mw8/1/

The getAndEncode() function is well able to fetch base64 URL with correct mimetype. However, the mimetype is manually removed from the result during the inlining and poorly restored later.

Proposed fix:

diff --git a/src/dom-to-image-more.js b/src/dom-to-image-more.js
index 044adea9..84913046 100644
--- a/src/dom-to-image-more.js
+++ b/src/dom-to-image-more.js
@@ -446,8 +446,6 @@
         return {
             escape: escapeRegEx,
             parseExtension: parseExtension,
-            mimeType: mimeType,
-            dataAsUrl: dataAsUrl,
             isDataUrl: isDataUrl,
             canvasToBlob: canvasToBlob,
             resolveUrl: resolveUrl,
@@ -538,11 +536,6 @@
             }
         }
 
-        function mimeType(url) {
-            const extension = parseExtension(url).toLowerCase();
-            return mimes()[extension] || '';
-        }
-
         function isDataUrl(url) {
             return url.search(/^(data:)/) !== -1;
         }
@@ -666,8 +659,7 @@
 
                         const encoder = new FileReader();
                         encoder.onloadend = function () {
-                            const content = encoder.result.split(/,/)[1];
-                            resolve(content);
+                            resolve(encoder.result);
                         };
                         encoder.readAsDataURL(request.response);
                     }
@@ -689,10 +681,6 @@
             return cacheEntry.promise;
         }
 
-        function dataAsUrl(content, type) {
-            return `data:${type};base64,${content}`;
-        }
-
         function escapeRegEx(string) {
             return string.replace(/([.*+?^${}()|\[\]\/\\])/g, '\\$1');
         }
@@ -772,9 +760,6 @@
                     return baseUrl ? util.resolveUrl(urlValue, baseUrl) : urlValue;
                 })
                 .then(get || util.getAndEncode)
-                .then(function (data) {
-                    return util.dataAsUrl(data, util.mimeType(url));
-                })
                 .then(function (dataUrl) {
                     return string.replace(urlAsRegex(url), `\$1${dataUrl}\$3`);
                 });
@@ -891,9 +876,6 @@
 
                 return Promise.resolve(element.src)
                     .then(get || util.getAndEncode)
-                    .then(function (data) {
-                        return util.dataAsUrl(data, util.mimeType(element.src));
-                    })
                     .then(function (dataUrl) {
                         return new Promise(function (resolve) {
                             element.onload = resolve;

Expected behavior

Expected the image to work in the JSFiddle

Actual behavior (stack traces, console logs etc)

The image breaks and the alt text for the mathematical formula appears.

Library version

Confirmed in 2.13.1 and also dom-to-image.

Browsers

  • Chrome 49+
  • Firefox 45+

TypeScript type definitions do not exist

Use case: description, code

I want to use this library from within my Kotlin/JS project, but it does not contain any type definitions that could be used by the Dukat tool.

Expected behavior

High quality TypeScript type definitions exist and can be used to import this library into Kotlin/JS projects

Actual behavior (stack traces, console logs etc)

TypeScript type definitions are missing and thus interoperability is very limited/non existent.

Library version

2.9.5

Browsers

  • Chrome 49+
  • Firefox 45+

Add `onclone` size optimization for inline styles

Use case: description, code

In an ideal world we want to optimize SVG output by removing styles that can be inherited.

This mainly has an impact at >>1K nodes where the output is in the 1MB scale of magnitude.

There's not much of a story here at the moment, but the library did previously lean on unset until the #90 regression:

function copyUserComputedStyle(sourceElement, sourceComputedStyles, targetElement, root) {
const targetStyle = targetElement.style;
const inlineStyles = sourceElement.style;
for (let style of sourceComputedStyles) {
const value = sourceComputedStyles.getPropertyValue(style);
const inlineValue = inlineStyles.getPropertyValue(style);
inlineStyles.setProperty(style, root ? 'initial' : 'unset');
const initialValue = sourceComputedStyles.getPropertyValue(style);
if (initialValue !== value) {
targetStyle.setProperty(style, value);
} else {
targetStyle.removeProperty(style);
}
inlineStyles.setProperty(style, inlineValue);
}
}

There's two strategies I tried to research:

  • The curveball method: reimplement CSSOM in the "cloning" stage using document.styleSheets
    • Multiple PITAs and footguns involved (specificity, source order and the size of this library)
    • Inheritance is done with a while loop on .parentNode, and a hardcoded list of inheritable CSS props
  • The easier, "just as good" way: a post-process op in toSvg
    • Append the output to our iframe sandbox
    • use a TreeWalker to iterate over the nodes
    • see if removing an inline value changes the computed style

Some starter code, but this code snippet:

  1. doesn't restore CSS props in place, making it indeterministic and liable to break styling
  2. doesn't go in ascending order up the DOM tree, thus removing inherited properties that are necessary
const treeWalker = document.createTreeWalker(document.querySelector('foreignObject > *'), NodeFilter.SHOW_ELEMENT)
const elementList = [];
let currentNode = treeWalker.currentNode;

while (currentNode) {
    elementList.push(currentNode);
    currentNode = treeWalker.nextNode();
}

elementList.forEach(function(element) {
    const inlineStyles = element.style;
    const computedStyles = getComputedStyle(element);
    util.asArray(inlineStyles).forEach(function(name) {
        if (inlineStyles.cssText.includes(name + ': ' + value + ';')) {
            const value = inlineStyles.getPropertyValue(name);
            inlineStyles.removeProperty(name);
            if (value !== computedStyles.getPropertyValue(name)) {
                inlineStyles[name] = value;
            }
        }
    });
});

Not rendering all the images

Use case: description, code

I'm trying to take several images and compose them together into one image for download. I'm also adding some metadata text on top of each image.

I have a prototype working at:
https://csb-te5qjm.vercel.app/

It works partly, but I'm getting CORS issue on some images (from Pinterest, Wikipedia). Just wondering how best to fix this. The cachebust option doesn't work for me. Should I try a proxy server to fix this issue?

Below is my code for the event component(i'm using react).

import domtoimage from "dom-to-image"

const downloadImage = () => {
  domtoimage
    .toJpeg(document.getElementById("event-container"), { cacheBust: true })
    .then(function (dataUrl) {
      var link = document.createElement("a")
      link.download = "my-image-name.jpeg"
      link.href = dataUrl
      link.click()
    })
}

Expected behavior

Actual behavior (stack traces, console logs etc)

I'm getting this error in the console:
cannot fetch resource:
I'm pretty sure it's a CORS issue and that images from Pinterest or Wikipedia are not being rendered. Images from Twitter however is OK and gets fetched.

download (1)

Library version

2.11.1

Browsers

  • [ x] Chrome 49+
  • Firefox 45+

Bug introduced in 2.9.x: c.getComputedStyle is not a function

Trying to use dom-to-image with angular 12.x


import domtoimage from 'dom-to-image-more';
export class MyComponent {
    @ViewChild('leafletMap') leafletMap: ElementRef;
    
    downloadMap = (): void => {
        //error happens here
        domtoimage.toPng(this.leafletMap.nativeElement).then((img: any) => {
            console.log(img);
        });
    };
}
/* of course this is simplified */

c.getComputedStyle is not a function is thrown. Strangely enough, this exact same code works with 2.8.0
stackoverflow answer & stackblitz

Library version

2.9.x

Browsers

all

Optimize requests of webfonts

Downloaded the latest, and it looks like tsayen#37 from dom-to-image is not fixed (it's not fixed in dom-to-image either). There is an open PR in the original repo though, tsayen#336

I tried it with the latest of dom-to-image-more and the below looks like the requests that occur

image

Nodes whose content does not fit are rendered larger

Use case

When cloning a node whose content is larger than the node, domtoimage produces an image whose size is larger than the node's size. See this jsfiddle. In the fiddle, the blue border around the image shows that the domtoimage result is 4 pixels taller than the actual height of the node. This extra space doesn't end up displaying content and seems more like a nuisance than an expected behavior.

Side-note: In this particular example, the SVG child is the same size as the node itself, but the node's content is still larger because the SVG is rendered inline (workarounds include: changing its display to block or font-size to 0px).

Expected behavior

A node should not be rendered larger than its actual size.

Actual behavior

The node renders larger than its actual size if the content inside of it is larger.

Library version

2.8.0

Browsers

  • Chrome 91
  • Firefox 89

Possible solution

The behavior occurs because util.width and util.height use scrollWidth and scrollHeight to calculate the size of the element (see line 630). This is helpful if we want to capture overflowing content in the screenshot, as shown in this jsfiddle. I don't think this is the intended behavior in most cases though. We could consider changing the code to use clientWidth and clientHeight instead.

If this overflow behavior is still necessary, I hope we can add an option for users like me to turn it off. Or even better, have it off by default.

Another possible solution is allowing the user to specify the height and width of the node via the options, but unfortunately it does not work for me since the node I am trying to screenshot has a border - see this jsfiddle. We'd need to change applyOptions so it takes this border into account so the resultant image isn't skewed.

Release Request: Add onclone feature as a new release

Hey first of all: Great work!

I want to ask you if you could release a new version to npm/yarn with your changes from the last months.
Escpecially the onclone feature is something I need for my project.

Thank you in advance! :)

font rendering not working due to .hasOwnProperty("cssRules") returning false

Use case: description, code

I was trying out the latest code from master to get font-awesome rendering fixed. This still didn't work.

Expected behavior

Font awesome fonts are rendered.

Actual behavior (stack traces, console logs etc)

When I execute this in the console:

document.styleSheets[1].hasOwnProperty("cssRules")

I saw that it returned false while it's actually a valid styleSheet object with a cssRules property:

Screenshot 2019-11-07 20 48 04

The relevant line in the code:

if (sheet.hasOwnProperty("cssRules")) {

does this check and that's the reason why no stylesheets are actually embedded.

I believe this check can be omitted anyway because of the try catch that follows?

Library version

Master @ commit d3ee842

Browsers

  • Chrome 78.0.3904.70 (Official Build) (64-bit)
  • Firefox 70.0.1 (64-bit)

Render to image without having to append to DOM Tree

Is this possible?

I have been using this but it is limited to having to ensure the element is already in the DOM Tree.

Mu use case, is that there are times i create the element programmatically. Thing is, I have to attach this to the DOM tree in order for the toSvg() or toPng() to work.

I wonder if it is possible to do the same thing without having to add the element to the DOM tree?

    var el = document.createElement('div');
    el.setAttribute('style', 'white-space: nowrap; position: relative; z-index: -99; width: 100px; height:200px; display: inline-block; font: ' 12px Arial');
    el.setAttribute('id', 'test');
    el.innerHTML = 'sample text';

    $('#where_to_insert).append(el);

    domtoimage.toSvg(el).then(function(dataUrl) {
console.log(dataUrl);
        $(el).remove();
    });

This works because of $('#where_to_insert).append(el);, but if i do not add it to the DOM Tree. nothing gets rendered.

Thoughts?

Cannot take screenshot of more complex webpages

Use case:

I'm trying to take a png screenshot using dom-to-image-more for more complex web pages (6000+ HTML nodes), using Firefox version 100.

As an example, see https://jsfiddle.net/joseph_white3/gj7v21yz/.

Expected behavior

Screenshot should be taken successfully. Webpages of this size are quite common, for example Google Sheets has 5000-6000 HTML nodes, according to the output of document.getElementsByTagName('*').length.

Actual behavior

In Chrome version 101, the code works fine. In Firefox 100 though, domtoimage.toPng throws an error in the makeImage function line 555:

image

Note: JSFiddle is letting the page crash instead of logging the error. I had to copy the HTML to a separate webpage to inspect the error.

Presumably this occurs because the data uri is a string 34277656 characters long (32.7 MB), which exceeds the max data URI length (see MDN, which states that Firefox 97+ has a max data URI length of 32 MB). As a result, the image.onerror function in makeImage gets called with the error shown above.

Library version

Latest (v. 2.9.5)

Browsers

  • [] Chrome 49+
  • Firefox 45+

Window breaks Angular Universal

Access to window breaks Angular Universal because is undefined.

Can you solve it and avoid to access window, or returning a shim?

Thanks!

`toSvg` regression for explicit CSS values equal to `inherit`

Use case: description, code

jsfiddle

<div id='dom-node'>
<span><a href='#dom-node'>dom-to-image-more/issues/90</a></span>
</div>

<div id='result'>
<img src='data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="232" height="132"><foreignObject x="0" y="0" width="100%" height="100%"><div id="dom-node" xmlns="http://www.w3.org/1999/xhtml" style="background-color: rgb(211, 211, 211); block-size: 100px; display: block; height: 100px; inline-size: 200px; padding-block: 16px; padding: 16px; padding-inline: 16px; text-align: center; width: 200px;">%0A<span><a href="%23dom-node" style="cursor: pointer; text-decoration-line: underline; text-decoration-thickness: initial;">dom-to-image-more/issues/90</a></span>%0A</div></foreignObject></svg>'></img>
</div>
#dom-node {
  color: #000000;
  height: 100px;
  width: 200px;
  background-color: lightgrey;
  text-align: center;
  padding: 1em;
}

a[href="#dom-node"] {
  color: #000000;
}

#result {
  margin-top: 10px;
}

Expected behavior

Related issues & PRs: #37 #71

It was taken as true that unset had no gotchas. I tried using toSvg on .Box in https://github.com/pulls. copyUserComputedStyle is an over-eager optimisation and has value collisions.

In this case, link color overrides that match inherit (as in .Box .Link--primary) are stripped away prematurely.

Here's an explanation of the copyUserComputedStyle regression:

  1. Initial value from user-agent origin = -webkit-link/-moz-hyperlinktext
  2. Stylesheet from author origin sets it to an explicit color value, which happens to be the computed value of inherit
  3. copyUserComputedStyle assigns unset inline which is computed as inherit
  4. Because the computed value of unset and and the explicit color value match, that value is not added to the inline style
  5. ๐Ÿ‘‰ The output SVG's inline style has no explicit color style. So instead of 2, we're literally back to square 1. ๐Ÿ˜ƒ

Actual behavior (stack traces, console logs etc)

When you open up the #result image src from the jsfiddle demo, it appears purple.

Library version

Regression confirmed in https://github.com/1904labs/dom-to-image-more/releases/tag/v2.12.0 and present since https://github.com/1904labs/dom-to-image-more/releases/tag/v2.9.1.

Browsers

  • Chrome 49+
  • Firefox 45+

Tables where cell padding has been set to 0 lead to cropped/non-matching image

Use case: description, code

jsfiddle

In production, I have a large table and noticed that the bottom and left edges were cut off when saving to Png. Using fiddle I isolated the problem to setting the padding on table cells to 0.

In the fiddle the bottom edge is cut off and also the image is wider than the dom table so I guess there may be various small differences when padding is 0 in this case.

Seems related to #77 where the user also fixed the issue by adding padding. Tables naturally have 1px cell padding so this might not have been noticed in the general case.

image

Expected behavior

Image should be full table.

Actual behavior (stack traces, console logs etc)

Image doesn't match. Edges are cropped etc.

Library version

2.13.0

Browsers

  • Chrome 108

-webkit-background-clip: text error

gives me an error any one cane help ?

.nav-link:hover,
.nav-link:focus{
background: linear-gradient(to top, #ffe838, #fd57bf);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}

Load remote images CORS

HI,
I have an issue with Angular 8+

Access to XMLHttpRequest at 'http://example.com/images/example_image.png' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Is there a way to add a header to requests maybe?
Or what can I do with that?

Browsers

  • Chrome 49+
  • Firefox 45+

Wrong custom fonts in rendered result

How to allow library to use fonts?

Use case: description, code

Use custom fonts in html, especially google fonts, loaded via link. Results after applying dom-to-image-more to div will produce wrong fonts.

Expected behavior

Fonts should be the same as in the original html.

Library version

2.8.0

Browsers

Chrome Version 76.0.3809.132 (Official Build) (64-bit)

Cannot get dynamic elements created by JavaScript

Use case: description, code

My DOM to save as:

<div id="mywrap" class="wrapper">
    <div class="layer1>My images / texts</div>
    <div class="layer2>My images / texts</div>
</div>
All layer is absolute with z-index as 1, 2, 3 => layer1, layer2, layer3.
My JavaScript code as...
let layer3 = document.createElement('div');
layer3.style.width = '100px';
layer3.style.height = '100px';
document.getElementById('mywrap').appendChild(layer3);
Layer3 is not show in created image.

Expected behavior

The result must be included layer3

Actual behavior (stack traces, console logs etc)

Library version

05-07-2022

Browsers

  • Chrome 100+
  • Firefox 100+

Comparison between 2 repositories

Use case:

I was using the dom-to-image-more, and although it was suprisingly faster than the regular lib it was not properly rendering fonts on SVG. I made a comparisson between 2 libs:

** New Edition **
I've run the minified version and it breaks the svg, but the "ugly" run just fine:
repository with just these 2 files to download

dom-to-image-more
dom-to-image-retina-fix

more

function newCanvas(domNode, scale) {
            var canvas = document.createElement('canvas');
            canvas.width = (options.width || util.width(domNode)) * scale;
            canvas.height = (options.height || util.height(domNode)) * scale;

retina-fix

function newCanvas(domNode) {
            var canvas = document.createElement('canvas');
            canvas.width = options.width || util.width(domNode) * 2;
            canvas.height = options.height || util.height(domNode) * 2;
            canvas.style.width = util.width(domNode);
            canvas.style.height = util.height(domNode);

=======================

more
look at the $

function parseExtension(url) {
            var match = /\.([^\.\/]*?)(\?|$)/g.exec(url);
            if (match) return match[1];
            else return '';
        }

retina-fix
look at the $

function parseExtension(url) {
            var match = /\.([^\.\/]*?)$/g.exec(url);
            if (match) return match[1];
            else return '';
        }

=======================

more
look at the reject resolve

function newImage(element) {
            return {
                inline: inline
            };

            function inline(get) {
                if (util.isDataUrl(element.src)) return Promise.resolve();

                return Promise.resolve(element.src)
                    .then(get || util.getAndEncode)
                    .then(function(data) {
                        return util.dataAsUrl(data, util.mimeType(element.src));
                    })
                    .then(function(dataUrl) {
                        return new Promise(function(resolve, reject) {
                            element.onload = resolve;
                            // for any image with invalid src(such as <img src />), just ignore it
                            element.onerror = resolve;
                            element.src = dataUrl;
                        });
                    });
            }
        }

retina-fix
look at the reject resolve

function newImage(element) {
            return {
                inline: inline
            };

            function inline(get) {
                if (util.isDataUrl(element.src)) return Promise.resolve();

                return Promise.resolve(element.src)
                    .then(get || util.getAndEncode)
                    .then(function(data) {
                        return util.dataAsUrl(data, util.mimeType(element.src));
                    })
                    .then(function(dataUrl) {
                        return new Promise(function(resolve, reject) {
                            element.onload = resolve;
                            // for any image with invalid src(such as <img src />), just ignore it
                            element.onerror = reject;
                            element.src = dataUrl;
                        });
                    });
            }
        }

Line break when the image is generated

Hi folks, I have the following DOM being rendered:
Screen Shot 2021-12-29 at 11 25 14

And when I "convert" it to an image, the subtitle is broken:
image (44)

I have no idea what is happening here.

Isn't text node supported?

Use case

jsfiddle

Render a text node.

I know wrapping it with a span can make it render, but what I'm working on is a developer tool, so I want to be able to take screenshot of any DOM node including text node.

Expected behavior

Render correctly.

Actual behavior

Fail to render.

On chrome it gives:

Failed to execute 'appendChild' on 'Node': This node type does not support this method.

The source location is:

node.appendChild(styleNode);

Seems it does not take into account the case of text node and tries to add a style element into it.

Is it difficult to make it support text node?

Library version

2.9.5

Browsers

  • Chrome 49+
  • Firefox 45+

Black image when capturing an iframe of Google Street View

Use case: description, code

Thanks for the help.

domtoimage.toJpeg(
		document.getElementById('street_view'), 
		{ 'quality':0.95 }
	).then(function (dataUrl) {
		
		var img = new Image();
		img.src = dataUrl;
		document.body.appendChild(img);
	})
	.catch(function (error) {
		console.error("oops, something went wrong!", error);
	});
<iframe 
id="street_view" 
width="100%"
height="1024px"
style="border:0"
loading="lazy"
allowfullscreen
src="https://www.google.com/maps/embed/v1/streetview?key=REDACTED
&location=47.2740517,-2.2116541
&heading=10
&pitch=10
&fov=70">
</iframe>

Expected behavior

An image containing the screenshot of the iframe

Actual behavior (stack traces, console logs etc)

Image of the correct size, but entirely black.
No exception are thrown, no errors.

The only error is js?client=google-maps-embed&paint_origin=&libraries=geometry,search&v=3.exp&language=en_US&callback=onApiLoad:258 The deviceorientation events are blocked by permissions policy.

Library version

  • master built a few minutes ago
  • dom-to-image-more 21-05-2019

Browsers

  • Chrome 49+ (90.0.4430.212 )

Firefox : duplicate border-left property to right border

Actually, when a html element have a border-left css property, this property is duplicated to the border right of this element. And put border-right property on an element not showing on the picture generated.

Only on Firefox. Work fine on Chrome !!

Best regards

  • Firefox 68, Linux OS

NPM package should not contain bower_components

Use case: description, code

Package usage via NPM

Expected behavior

Installed package does not contain bower_components

Actual behavior (stack traces, console logs etc)

Package contains bower_components

Library version

Latest (2.10.1)

Access to XMLHttpRequest has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Use case:

I need to download a bookshelf where all the images come from Google Books.

I created the fiddle below, with only 1 image, to show the problem I'm experiencing.

jsfiddle

Expected behavior

Here is an image of my DOM and just below the result, after clicking on Screenshot:

DOM

Actual behavior

Access to XMLHttpRequest at 'https://books.google.com/books/content?id=rBdiDwAAQBAJ&printsec=frontcover&img=1&zoom=1&edge=curl&source=gbs_api'
from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

IMAGE

Library version

  • "dom-to-image-more": "^2.9.5"

Browsers

  • Chrome 49+
  • Firefox 45+
  • Brave 1.34.81

I'm using Vue and my question is: does this have a solution? How to add an 'Access-Control-Allow-Origin' header in the resource?

Box shadow not fully included in result image png. (image cropped)

Use case: calculating size of element with box shadow

Even using the "style" option or "wrapper" trick, i cannot have the full rendering of my element including its box shadow
jsfiddle

Expected behavior

The resulting image should include the box shadow area.

Actual behavior (stack traces, console logs etc)

Library version

Browsers

  • Chrome 103+

ownerWindow is not defined

Use case: description, code

Hi!
I'm trying to install the package on my node.js app, but I'm not sure of how to do. I copied the content of /src/dom-to-image-more.js into my JS script (in the "public" folder), but whenever I try to use the default example:

var node = document.getElementById("h1");

domtoimage
  .toPng(node)
  .then(function (dataUrl) {
    console.log(dataUrl)
  })
  .catch(function (error) {
    console.error("oops, something went wrong!", error);
  });

I got an error saying ownerWindow is not defined.

I tried to use npm install and put import domtoimage from "dom-to-image-more"; in my JS file but then I get an error when trying to use it (<script src="/js/dom-to-img.js" type='module'></script>):

Uncaught TypeError: Failed to resolve module specifier "dom-to-image-more". Relative references must start with either "/", "./", or "../".

I'm pretty new to this, so, not sure what's going on...

Expected behavior

?

Actual behavior (stack traces, console logs etc)

dom-to-img.js:475 Uncaught ReferenceError: ownerWindow is not defined
at Object.getWindow (dom-to-img.js:475:79)
at toSvg (dom-to-img.js:68:50)
at draw (dom-to-img.js:205:16)
at Object.toPng (dom-to-img.js:139:16)
at :4:4

Library version

2.13.1

Browsers

  • Chrome 49+
  • Firefox 45+

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.