From f6e16b62948a8fab984c660d640bd939ff6f2fc7 Mon Sep 17 00:00:00 2001 From: Joshua Walsh Date: Wed, 22 Mar 2023 22:41:51 +1100 Subject: [PATCH 1/3] Workaround for Firefox bug (fixes #123) This is a workaround for Firefox bug webcompat/web-bugs#119834. Waiting one extra frame here ensures that the image is fully rendered before we read the data from it. --- src/dom-to-image-more.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/dom-to-image-more.js b/src/dom-to-image-more.js index f56b16b5..85be38b7 100644 --- a/src/dom-to-image-more.js +++ b/src/dom-to-image-more.js @@ -738,7 +738,11 @@ image.crossOrigin = 'use-credentials'; } image.onload = function () { - resolve(image); + // In order to work around a Firefox bug (webcompat/web-bugs#119834) we + // need to wait one extra frame before it's safe to read the image data. + window.requestAnimationFrame(function() { + resolve(image); + }); }; image.onerror = reject; image.src = uri; From cfac73683fc588df36e9d365da214024dd22266d Mon Sep 17 00:00:00 2001 From: Joshua Walsh Date: Thu, 23 Mar 2023 18:07:31 +1100 Subject: [PATCH 2/3] Ensure Node compatibility --- src/dom-to-image-more.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/dom-to-image-more.js b/src/dom-to-image-more.js index 85be38b7..33c4f9f3 100644 --- a/src/dom-to-image-more.js +++ b/src/dom-to-image-more.js @@ -738,6 +738,13 @@ image.crossOrigin = 'use-credentials'; } image.onload = function () { + if(typeof window === "undefined" || !window.requestAnimationFrame) { + // NodeJS doesn't have a requestAnimationFrame function and may not have a window object. + // NodeJS also presumably doesn't exhibit this Firefox bug though, so in this case we can proceed immediately. + resolve(image); + return; + } + // In order to work around a Firefox bug (webcompat/web-bugs#119834) we // need to wait one extra frame before it's safe to read the image data. window.requestAnimationFrame(function() { From e2dd9f165fd810f99ccede5f4c58bbab7dbd1c77 Mon Sep 17 00:00:00 2001 From: Marc Brooks Date: Thu, 23 Mar 2023 13:40:24 -0500 Subject: [PATCH 3/3] Simplify deferred image loading Fixes #123 Also removed the obsolete mozImageSmoothingEnabled Fixes #119 Bumped prettier to 2.8.6 --- README.md | 3 ++- dist/dom-to-image-more.min.js | 4 +-- dist/dom-to-image-more.min.js.map | 2 +- package-lock.json | 42 +++++++++++++++---------------- package.json | 9 ++++--- spec/dom-to-image-more.spec.js | 2 -- src/dom-to-image-more.js | 19 ++++++-------- 7 files changed, 39 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 6f8b1556..02ccc491 100644 --- a/README.md +++ b/README.md @@ -280,7 +280,8 @@ for you, following steps are taken: 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), @meche-gh (many major updates) +David DOLCIMASCOLO (packaging), Zee (ZM) @zm-cttae (many major updates), Joshua Walsh @JoshuaWalsh (Firefox issues) + ## License diff --git a/dist/dom-to-image-more.min.js b/dist/dom-to-image-more.min.js index a3535380..38288749 100644 --- a/dist/dom-to-image-more.min.js +++ b/dist/dom-to-image-more.min.js @@ -1,3 +1,3 @@ -/*! dom-to-image-more 14-03-2023 */ -!function(u){"use strict";const f=function(){let e=0;return{escape:function(e){return e.replace(/([.*+?^${}()|[]\/\\])/g,"\\$1")},isDataUrl:function(e){return-1!==e.search(/^(data:)/)},canvasToBlob:function(t){if(t.toBlob)return new Promise(function(e){t.toBlob(e)});return function(r){return new Promise(function(e){var t=s(r.toDataURL().split(",")[1]),n=t.length,o=new Uint8Array(n);for(let e=0;et.style.removeProperty(e)),["left","right","top","bottom"].forEach(e=>{t.style.getPropertyValue(e)&&t.style.setProperty(e,"0px")})))}e(c,s)}function t(){const l=f.uid();function t(r){const i=h(c,r),u=i.getPropertyValue("content");if(""!==u&&"none"!==u){const t=s.getAttribute("class")||"",n=(s.setAttribute("class",t+" "+l),document.createElement("style"));function e(){const e=`.${l}:`+r,t=(i.cssText?n:o)();return document.createTextNode(e+`{${t}}`);function n(){return`${i.cssText} content: ${u};`}function o(){const e=f.asArray(i).map(t).join("; ");return e+";";function t(e){const t=i.getPropertyValue(e),n=i.getPropertyPriority(e)?" !important":"";return e+": "+t+n}}}n.appendChild(e()),s.appendChild(n)}}[":before",":after"].forEach(function(e){t(e)})}function n(){f.isHTMLTextAreaElement(c)&&(s.innerHTML=c.value),f.isHTMLInputElement(c)&&s.setAttribute("value",c.value)}function o(){f.isSVGElement(s)&&(s.setAttribute("xmlns","http://www.w3.org/2000/svg"),f.isSVGRectElement(s))&&["width","height"].forEach(function(e){const t=s.getAttribute(e);t&&s.style.setProperty(e,t)})}}}(e,r,null,t)}).then(p).then(g).then(function(t){r.bgcolor&&(t.style.backgroundColor=r.bgcolor);r.width&&(t.style.width=r.width+"px");r.height&&(t.style.height=r.height+"px");r.style&&Object.keys(r.style).forEach(function(e){t.style[e]=r.style[e]});let e=null;"function"==typeof r.onclone&&(e=r.onclone(t));return Promise.resolve(e).then(function(){return t})}).then(function(e){let n=r.width||f.width(e),o=r.height||f.height(e);return Promise.resolve(e).then(function(e){return e.setAttribute("xmlns","http://www.w3.org/1999/xhtml"),(new XMLSerializer).serializeToString(e)}).then(f.escapeXhtml).then(function(e){var t=(f.isDimensionMissing(n)?' width="100%"':` width="${n}"`)+(f.isDimensionMissing(o)?' height="100%"':` height="${o}"`);return`${e}`}).then(function(e){return"data:image/svg+xml;charset=utf-8,"+e})}).then(function(e){for(;0{E=null,w={}},2e4)}(),e})}function i(r,i){return m(r,i=i||{}).then(f.makeImage).then(function(e){var t="number"!=typeof i.scale?1:i.scale,n=function(e,t){let n=i.width||f.width(e),o=i.height||f.height(e);f.isDimensionMissing(n)&&(n=f.isDimensionMissing(o)?300:2*o);f.isDimensionMissing(o)&&(o=n/2);e=document.createElement("canvas");e.width=n*t,e.height=o*t,i.bgcolor&&((t=e.getContext("2d")).fillStyle=i.bgcolor,t.fillRect(0,0,e.width,e.height));return e}(r,t),o=n.getContext("2d");return o.mozImageSmoothingEnabled=!1,o.msImageSmoothingEnabled=!1,o.imageSmoothingEnabled=!1,e&&(o.scale(t,t),o.drawImage(e,0,0)),n})}let d=null;function p(n){return e.resolveAll().then(function(e){var t;return""!==e&&(t=document.createElement("style"),n.appendChild(t),t.appendChild(document.createTextNode(e))),n})}function g(e){return n.inlineAll(e).then(function(){return e})}function y(e,t,i,u,n){const l=a.impl.options.copyDefaultStyles?function(t,e){var e=function(e){var t=[];do{if(e.nodeType===c){var n=e.tagName;if(t.push(n),v.includes(n))break}}while(e=e.parentNode,e);return t}(e),n=function(e){return("relaxed"!==t.styleCaching?e:e.filter((e,t,n)=>0===t||t===n.length-1)).join(">")}(e);if(w[n])return w[n];var o=function(){if(d)return d.contentWindow;var e=document.characterSet||"UTF-8",t=document.doctype,t=t?(`":"";return(d=document.createElement("iframe")).id="domtoimage-sandbox-"+f.uid(),d.style.visibility="hidden",d.style.position="fixed",document.body.appendChild(d),function(e,t,n,o){try{return e.contentWindow.document.write(t+`${o} `),e.contentWindow}catch(e){}var r=document.createElement("meta");r.setAttribute("charset",n);try{var i=document.implementation.createHTMLDocument(o),u=(i.head.appendChild(r),t+i.documentElement.outerHTML);return e.setAttribute("srcdoc",u),e.contentWindow}catch(e){}return e.contentDocument.head.appendChild(r),e.contentDocument.title=o,e.contentWindow}(d,t,e,"domtoimage-sandbox");function n(e){var t;return e?((t=document.createElement("div")).innerText=e,t.innerHTML):""}}(),e=function(e,t){let n=e.body;do{var o=t.pop(),o=e.createElement(o);n.appendChild(o),n=o}while(0t.style.removeProperty(e)),["left","right","top","bottom"].forEach(e=>{t.style.getPropertyValue(e)&&t.style.setProperty(e,"0px")})))}e(c,l)}function t(){const s=f.uid();function t(r){const i=h(c,r),u=i.getPropertyValue("content");if(""!==u&&"none"!==u){const t=l.getAttribute("class")||"",n=(l.setAttribute("class",t+" "+s),document.createElement("style"));function e(){const e=`.${s}:`+r,t=(i.cssText?n:o)();return document.createTextNode(e+`{${t}}`);function n(){return`${i.cssText} content: ${u};`}function o(){const e=f.asArray(i).map(t).join("; ");return e+";";function t(e){const t=i.getPropertyValue(e),n=i.getPropertyPriority(e)?" !important":"";return e+": "+t+n}}}n.appendChild(e()),l.appendChild(n)}}[":before",":after"].forEach(function(e){t(e)})}function n(){f.isHTMLTextAreaElement(c)&&(l.innerHTML=c.value),f.isHTMLInputElement(c)&&l.setAttribute("value",c.value)}function o(){f.isSVGElement(l)&&(l.setAttribute("xmlns","http://www.w3.org/2000/svg"),f.isSVGRectElement(l))&&["width","height"].forEach(function(e){const t=l.getAttribute(e);t&&l.style.setProperty(e,t)})}}}(e,r,null,t)}).then(p).then(g).then(function(t){r.bgcolor&&(t.style.backgroundColor=r.bgcolor);r.width&&(t.style.width=r.width+"px");r.height&&(t.style.height=r.height+"px");r.style&&Object.keys(r.style).forEach(function(e){t.style[e]=r.style[e]});let e=null;"function"==typeof r.onclone&&(e=r.onclone(t));return Promise.resolve(e).then(function(){return t})}).then(function(e){let n=r.width||f.width(e),o=r.height||f.height(e);return Promise.resolve(e).then(function(e){return e.setAttribute("xmlns","http://www.w3.org/1999/xhtml"),(new XMLSerializer).serializeToString(e)}).then(f.escapeXhtml).then(function(e){var t=(f.isDimensionMissing(n)?' width="100%"':` width="${n}"`)+(f.isDimensionMissing(o)?' height="100%"':` height="${o}"`);return`${e}`}).then(function(e){return"data:image/svg+xml;charset=utf-8,"+e})}).then(function(e){for(;0{w=null,E={}},2e4)}(),e})}function i(r,i){return m(r,i=i||{}).then(f.makeImage).then(function(e){var t="number"!=typeof i.scale?1:i.scale,n=function(e,t){let n=i.width||f.width(e),o=i.height||f.height(e);f.isDimensionMissing(n)&&(n=f.isDimensionMissing(o)?300:2*o);f.isDimensionMissing(o)&&(o=n/2);e=document.createElement("canvas");e.width=n*t,e.height=o*t,i.bgcolor&&((t=e.getContext("2d")).fillStyle=i.bgcolor,t.fillRect(0,0,e.width,e.height));return e}(r,t),o=n.getContext("2d");return o.msImageSmoothingEnabled=!1,o.imageSmoothingEnabled=!1,e&&(o.scale(t,t),o.drawImage(e,0,0)),n})}let d=null;function p(n){return e.resolveAll().then(function(e){var t;return""!==e&&(t=document.createElement("style"),n.appendChild(t),t.appendChild(document.createTextNode(e))),n})}function g(e){return n.inlineAll(e).then(function(){return e})}function y(e,t,i,u,n){const s=a.impl.options.copyDefaultStyles?function(t,e){var e=function(e){var t=[];do{if(e.nodeType===c){var n=e.tagName;if(t.push(n),v.includes(n))break}}while(e=e.parentNode,e);return t}(e),n=function(e){return("relaxed"!==t.styleCaching?e:e.filter((e,t,n)=>0===t||t===n.length-1)).join(">")}(e);if(E[n])return E[n];var o=function(){if(d)return d.contentWindow;var e=document.characterSet||"UTF-8",t=document.doctype,t=t?(`":"";return(d=document.createElement("iframe")).id="domtoimage-sandbox-"+f.uid(),d.style.visibility="hidden",d.style.position="fixed",document.body.appendChild(d),function(e,t,n,o){try{return e.contentWindow.document.write(t+`${o} `),e.contentWindow}catch(e){}var r=document.createElement("meta");r.setAttribute("charset",n);try{var i=document.implementation.createHTMLDocument(o),u=(i.head.appendChild(r),t+i.documentElement.outerHTML);return e.setAttribute("srcdoc",u),e.contentWindow}catch(e){}return e.contentDocument.head.appendChild(r),e.contentDocument.title=o,e.contentWindow}(d,t,e,"domtoimage-sandbox");function n(e){var t;return e?((t=document.createElement("div")).innerText=e,t.innerHTML):""}}(),e=function(e,t){let n=e.body;do{var o=t.pop(),o=e.createElement(o);n.appendChild(o),n=o}while(0=16.0.0" @@ -184,9 +184,9 @@ } }, "node_modules/@types/node": { - "version": "18.15.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.3.tgz", - "integrity": "sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==", + "version": "18.15.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.5.tgz", + "integrity": "sha512-Ark2WDjjZO7GmvsyFFf81MXuGTA/d6oP38anyxWOL6EREyBKAxKoFHwBhaZxCfLRLpO8JgVXwqOwSwa7jRcjew==", "dev": true }, "node_modules/abbrev": { @@ -1706,9 +1706,9 @@ } }, "node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, "node_modules/grapheme-splitter": { @@ -2354,9 +2354,9 @@ } }, "node_modules/js-sdsl": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", - "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz", + "integrity": "sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==", "dev": true, "funding": { "type": "opencollective", @@ -3444,9 +3444,9 @@ } }, "node_modules/prettier": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz", - "integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==", + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.6.tgz", + "integrity": "sha512-mtuzdiBbHwPEgl7NxWlqOkithPyp4VN93V7VeHVWBF+ad3I5avc0RVDT4oImXQy9H/AqxA2NSQH8pSxHW6FYbQ==", "dev": true, "bin": { "prettier": "bin-prettier.js" diff --git a/package.json b/package.json index bd281287..99a92292 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dom-to-image-more", - "version": "3.1.0", + "version": "3.1.1", "description": "Generates an image from a DOM node using HTML5 canvas and SVG", "main": "dist/dom-to-image-more.min.js", "devDependencies": { @@ -20,7 +20,7 @@ "karma-mocha": "^2.0.1", "karma-mocha-reporter": "^2.2.5", "mocha": "^10.2.0", - "prettier": "^2.8.4", + "prettier": "^2.8.6", "semver": "^7.3.8" }, "scripts": { @@ -60,8 +60,9 @@ "Phani Rithvij @phanirithvij", "David DOLCIMASCOLO @ddolcimascolo", "Nikita Staroseltsev @Nikitozz13", - "@meche-gh", - "Andoni Zubimendi @AndoniZubimendi" + "Zee (ZM) @zm-cttae", + "Andoni Zubimendi @AndoniZubimendi", + "Joshua Walsh @JoshuaWalsh" ], "license": "MIT", "bugs": { diff --git a/spec/dom-to-image-more.spec.js b/spec/dom-to-image-more.spec.js index 594ed2be..d57e620f 100644 --- a/spec/dom-to-image-more.spec.js +++ b/spec/dom-to-image-more.spec.js @@ -571,7 +571,6 @@ canvas.height = image.naturalHeight; canvas.width = image.naturalWidth; var ctx = canvas.getContext('2d'); - ctx.mozImageSmoothingEnabled = false; ctx.msImageSmoothingEnabled = false; ctx.imageSmoothingEnabled = false; ctx.drawImage(image, 0, 0); @@ -636,7 +635,6 @@ c.height = dimensions.height || node.offsetHeight.toString(); c.width = dimensions.width || node.offsetWidth.toString(); const ctx = c.getContext('2d'); - ctx.mozImageSmoothingEnabled = false; ctx.msImageSmoothingEnabled = false; ctx.imageSmoothingEnabled = false; ctx.drawImage(image, 0, 0); diff --git a/src/dom-to-image-more.js b/src/dom-to-image-more.js index 33c4f9f3..05cecf56 100644 --- a/src/dom-to-image-more.js +++ b/src/dom-to-image-more.js @@ -280,7 +280,6 @@ const scale = typeof options.scale !== 'number' ? 1 : options.scale; const canvas = newCanvas(domNode, scale); const ctx = canvas.getContext('2d'); - ctx.mozImageSmoothingEnabled = false; ctx.msImageSmoothingEnabled = false; ctx.imageSmoothingEnabled = false; if (image) { @@ -738,18 +737,16 @@ image.crossOrigin = 'use-credentials'; } image.onload = function () { - if(typeof window === "undefined" || !window.requestAnimationFrame) { - // NodeJS doesn't have a requestAnimationFrame function and may not have a window object. - // NodeJS also presumably doesn't exhibit this Firefox bug though, so in this case we can proceed immediately. + if (window && window.requestAnimationFrame) { + // In order to work around a Firefox bug (webcompat/web-bugs#119834) we + // need to wait one extra frame before it's safe to read the image data. + window.requestAnimationFrame(function () { + resolve(image); + }); + } else { + // If we don't have a window or requestAnimationFrame function proceed immediately. resolve(image); - return; } - - // In order to work around a Firefox bug (webcompat/web-bugs#119834) we - // need to wait one extra frame before it's safe to read the image data. - window.requestAnimationFrame(function() { - resolve(image); - }); }; image.onerror = reject; image.src = uri;