Skip to content

Commit 562f2be

Browse files
committed
Remove canvg and add embedded image support in print
1 parent b1ee861 commit 562f2be

File tree

3 files changed

+64
-44
lines changed

3 files changed

+64
-44
lines changed

package-lock.json

Lines changed: 6 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,6 @@
9696
},
9797
"devDependencies": {
9898
"@vitejs/plugin-vue": "^5.2.3",
99-
"canvg": "^4.0.3",
10099
"cypress": "^14.0.3",
101100
"jspdf": "^3.0.1",
102101
"remove-attr": "^0.0.13",
@@ -106,4 +105,4 @@
106105
"vitest": "^3.1.1",
107106
"vue": "^3.5.14"
108107
}
109-
}
108+
}

src/dom-to-png.js

Lines changed: 57 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,34 +9,46 @@ import { XMLNS } from "./lib";
99
*/
1010
async function inlineAllImages(clone) {
1111
const imgEls = clone.querySelectorAll('img');
12-
const promises = [];
13-
imgEls.forEach(imgEl => {
14-
if (imgEl.src && !imgEl.src.startsWith('data:')) {
15-
promises.push(
16-
new Promise(resolve => {
17-
const image = new window.Image();
18-
image.crossOrigin = 'anonymous'; // Try CORS
19-
image.onload = function () {
20-
try {
21-
const canvas = document.createElement('canvas');
22-
canvas.width = image.naturalWidth;
23-
canvas.height = image.naturalHeight;
24-
canvas.getContext('2d').drawImage(image, 0, 0);
25-
imgEl.src = canvas.toDataURL();
26-
} catch (e) {
27-
// it's tainted
28-
}
29-
resolve();
30-
};
31-
image.onerror = function () { resolve(); };
32-
image.src = imgEl.src;
33-
})
34-
);
35-
}
12+
const promises = Array.from(imgEls).map(imgEl => {
13+
return new Promise(resolve => {
14+
if (!imgEl.src || imgEl.src.startsWith('data:')) return resolve();
15+
16+
if (imgEl.complete && imgEl.naturalWidth !== 0) {
17+
try {
18+
const canvas = document.createElement('canvas');
19+
canvas.width = imgEl.naturalWidth;
20+
canvas.height = imgEl.naturalHeight;
21+
canvas.getContext('2d').drawImage(imgEl, 0, 0);
22+
imgEl.src = canvas.toDataURL();
23+
} catch (e) {
24+
// tainted
25+
}
26+
return resolve();
27+
}
28+
29+
const image = new window.Image();
30+
image.crossOrigin = 'anonymous';
31+
image.onload = function () {
32+
try {
33+
const canvas = document.createElement('canvas');
34+
canvas.width = image.naturalWidth;
35+
canvas.height = image.naturalHeight;
36+
canvas.getContext('2d').drawImage(image, 0, 0);
37+
imgEl.src = canvas.toDataURL();
38+
} catch (e) {
39+
// tainted
40+
}
41+
resolve();
42+
};
43+
image.onerror = function () { resolve(); };
44+
image.src = imgEl.src;
45+
});
3646
});
47+
3748
await Promise.all(promises);
3849
}
3950

51+
4052
/**
4153
* Removes all elements in the given DOM subtree that have the data-dom-to-png-ignore attribute.
4254
* @param {HTMLElement} root - The root of the cloned DOM tree.
@@ -384,6 +396,23 @@ function walkAllAndApply(cloneNode, liveNode) {
384396
}
385397
}
386398

399+
function forceInlineImageStyles(clone, original) {
400+
const originalImgs = Array.from(original.querySelectorAll('img'));
401+
clone.querySelectorAll('img').forEach(img => {
402+
const src = img.getAttribute('src');
403+
const match = originalImgs.find(oimg => oimg.getAttribute('src') === src);
404+
if (match) {
405+
const computedStyle = window.getComputedStyle(match);
406+
let styleString = '';
407+
for (let j = 0; j < computedStyle.length; j++) {
408+
const property = computedStyle[j];
409+
styleString += `${property}:${computedStyle.getPropertyValue(property)};`;
410+
}
411+
img.setAttribute('style', styleString);
412+
}
413+
});
414+
}
415+
387416
/**
388417
* Converts a DOM element (including HTML, SVG, and canvas) into a high-resolution PNG data URL.
389418
*
@@ -446,6 +475,9 @@ async function domToPng({ container, scale = 2 }) {
446475
inlineForeignObjectHTMLComputedStyles(cloneSvg, liveSvg);
447476
applyAllSvgComputedStylesInline(cloneSvg);
448477
setFontFamilyOnAllSvgTextElements(cloneSvg, containerFontFamily);
478+
forceInlineImageStyles(cloneSvg, liveSvg);
479+
480+
await inlineAllImages(cloneSvg);
449481

450482
const bbox = liveSvg.getBoundingClientRect();
451483
const svgWidth = bbox.width;
@@ -464,6 +496,7 @@ async function domToPng({ container, scale = 2 }) {
464496
}
465497

466498
applyAllComputedStylesDeep(clone, container, containerFontFamily);
499+
forceInlineImageStyles(clone, container);
467500
removeIgnoredElements(clone);
468501
await inlineAllImages(clone);
469502

0 commit comments

Comments
 (0)