@zumer/snapdom 1.0.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/snapdom.js +69 -34
- package/dist/snapdom.min.js +3 -3
- package/dist/snapdom.min.mjs +3 -3
- package/dist/snapdom.mjs +65 -34
- package/package.json +11 -8
- package/types/snapdom.d.ts +56 -0
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# snapDOM
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
|
-
<a href="http://zumerlab.github.io/
|
|
4
|
+
<a href="http://zumerlab.github.io/snapdom">
|
|
5
5
|
<img src="https://raw.githubusercontent.com/zumerlab/snapdom/main/docs/assets/hero.png" width="80%">
|
|
6
6
|
</a>
|
|
7
7
|
</p>
|
|
@@ -64,7 +64,7 @@ import { snapdom } from './snapdom.mjs';
|
|
|
64
64
|
|
|
65
65
|
## Basic usage
|
|
66
66
|
|
|
67
|
-
### Reusable
|
|
67
|
+
### Reusable capture
|
|
68
68
|
|
|
69
69
|
```js
|
|
70
70
|
const el = document.querySelector('#target');
|
package/dist/snapdom.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
/*
|
|
3
3
|
* snapdom
|
|
4
|
-
* v.1.
|
|
4
|
+
* v.1.2.1
|
|
5
5
|
* Author Juan Martin Muda
|
|
6
6
|
* License MIT
|
|
7
7
|
**/
|
|
@@ -300,8 +300,6 @@
|
|
|
300
300
|
return img;
|
|
301
301
|
}
|
|
302
302
|
if (node.nodeType === Node.TEXT_NODE) {
|
|
303
|
-
const trimmed = node.textContent.trim();
|
|
304
|
-
if (!trimmed) return null;
|
|
305
303
|
if (node.parentElement?.shadowRoot) {
|
|
306
304
|
const tag = node.parentElement.tagName.toLowerCase();
|
|
307
305
|
if (customElements.get(tag)) return null;
|
|
@@ -330,17 +328,25 @@
|
|
|
330
328
|
// src/modules/fonts.js
|
|
331
329
|
async function iconToImage(unicodeChar, fontFamily, fontWeight, fontSize = 32, color = "#000") {
|
|
332
330
|
fontFamily = fontFamily.replace(/^['"]+|['"]+$/g, "");
|
|
333
|
-
|
|
334
|
-
|
|
331
|
+
const dpr = window.devicePixelRatio || 1;
|
|
332
|
+
const tempCanvas = document.createElement("canvas");
|
|
333
|
+
const tempCtx = tempCanvas.getContext("2d");
|
|
334
|
+
tempCtx.font = fontWeight ? `${fontWeight} ${fontSize}px "${fontFamily}"` : `${fontSize}px "${fontFamily}"`;
|
|
335
|
+
const metrics = tempCtx.measureText(unicodeChar);
|
|
336
|
+
const ascent = metrics.actualBoundingBoxAscent || fontSize * 0.8;
|
|
337
|
+
const descent = metrics.actualBoundingBoxDescent || fontSize * 0.2;
|
|
338
|
+
const height = ascent + descent;
|
|
339
|
+
const width = metrics.width;
|
|
335
340
|
const canvas = document.createElement("canvas");
|
|
336
|
-
canvas.width =
|
|
337
|
-
canvas.height =
|
|
341
|
+
canvas.width = Math.ceil(width * dpr);
|
|
342
|
+
canvas.height = Math.ceil(height * dpr);
|
|
338
343
|
const ctx = canvas.getContext("2d");
|
|
339
|
-
ctx.
|
|
340
|
-
ctx.
|
|
341
|
-
ctx.
|
|
344
|
+
ctx.scale(dpr, dpr);
|
|
345
|
+
ctx.font = tempCtx.font;
|
|
346
|
+
ctx.textAlign = "left";
|
|
347
|
+
ctx.textBaseline = "alphabetic";
|
|
342
348
|
ctx.fillStyle = color;
|
|
343
|
-
ctx.fillText(unicodeChar,
|
|
349
|
+
ctx.fillText(unicodeChar, 0, ascent);
|
|
344
350
|
return canvas.toDataURL();
|
|
345
351
|
}
|
|
346
352
|
async function embedCustomFonts({ ignoreIconFonts = true, preCached = false }) {
|
|
@@ -351,7 +357,6 @@
|
|
|
351
357
|
const res = await fetch(link.href);
|
|
352
358
|
const cssText = await res.text();
|
|
353
359
|
if (ignoreIconFonts && (isIconFont(link.href) || isIconFont(cssText))) {
|
|
354
|
-
console.log("\u23ED\uFE0F Ignorando icon font CSS:", link.href);
|
|
355
360
|
continue;
|
|
356
361
|
}
|
|
357
362
|
const urlRegex = /url\(([^)]+)\)/g;
|
|
@@ -362,7 +367,6 @@
|
|
|
362
367
|
url = new URL(url, link.href).href;
|
|
363
368
|
}
|
|
364
369
|
if (ignoreIconFonts && isIconFont(url)) {
|
|
365
|
-
console.log("\u23ED\uFE0F Ignorando icon font URL:", url);
|
|
366
370
|
return null;
|
|
367
371
|
}
|
|
368
372
|
if (resourceCache.has(url)) {
|
|
@@ -379,7 +383,7 @@
|
|
|
379
383
|
resourceCache.set(url, b64);
|
|
380
384
|
return { original: match[0], inlined: `url(${b64})` };
|
|
381
385
|
} catch (err) {
|
|
382
|
-
console.warn("\u274C
|
|
386
|
+
console.warn("\u274C Failed to fetch font:", url);
|
|
383
387
|
return null;
|
|
384
388
|
}
|
|
385
389
|
})
|
|
@@ -392,7 +396,7 @@
|
|
|
392
396
|
}
|
|
393
397
|
finalCSS += cssFinal + "\n";
|
|
394
398
|
} catch (e) {
|
|
395
|
-
console.warn("\u274C
|
|
399
|
+
console.warn("\u274C Failed to fetch CSS:", link.href);
|
|
396
400
|
}
|
|
397
401
|
}
|
|
398
402
|
if (finalCSS && preCached) {
|
|
@@ -405,7 +409,7 @@
|
|
|
405
409
|
}
|
|
406
410
|
|
|
407
411
|
// src/modules/pseudo.js
|
|
408
|
-
async function inlinePseudoElements(source, clone, styleMap, styleCache, compress) {
|
|
412
|
+
async function inlinePseudoElements(source, clone, styleMap, styleCache, compress, embedFonts = false) {
|
|
409
413
|
if (!(source instanceof Element) || !(clone instanceof Element)) return;
|
|
410
414
|
for (const pseudo of ["::before", "::after"]) {
|
|
411
415
|
try {
|
|
@@ -427,7 +431,7 @@
|
|
|
427
431
|
styleMap.set(pseudoEl, key);
|
|
428
432
|
const isIconFont2 = fontFamily && /font.*awesome|material|bootstrap|glyphicons|ionicons|remixicon|simple-line-icons|octicons|feather|typicons|weathericons/i.test(fontFamily);
|
|
429
433
|
let cleanContent = parseContent(content);
|
|
430
|
-
if (isIconFont2 && cleanContent.length === 1) {
|
|
434
|
+
if (!embedFonts && isIconFont2 && cleanContent.length === 1) {
|
|
431
435
|
const imgEl = document.createElement("img");
|
|
432
436
|
imgEl.src = await iconToImage(cleanContent, fontFamily, fontWeight, fontSize, color);
|
|
433
437
|
imgEl.style = "display:block;width:100%;height:100%;object-fit:contain;";
|
|
@@ -463,15 +467,15 @@
|
|
|
463
467
|
(child) => !child.dataset.snapdomPseudo
|
|
464
468
|
);
|
|
465
469
|
for (let i = 0; i < Math.min(sChildren.length, cChildren.length); i++) {
|
|
466
|
-
await inlinePseudoElements(sChildren[i], cChildren[i], styleMap, styleCache, compress);
|
|
470
|
+
await inlinePseudoElements(sChildren[i], cChildren[i], styleMap, styleCache, compress, embedFonts);
|
|
467
471
|
}
|
|
468
472
|
}
|
|
469
473
|
|
|
470
474
|
// src/core/prepare.js
|
|
471
|
-
async function prepareClone(element, compress = false) {
|
|
475
|
+
async function prepareClone(element, compress = false, embedFonts = false) {
|
|
472
476
|
const styleMap = /* @__PURE__ */ new Map();
|
|
473
477
|
const styleCache = /* @__PURE__ */ new WeakMap();
|
|
474
|
-
const nodeMap = /* @__PURE__ */ new
|
|
478
|
+
const nodeMap = /* @__PURE__ */ new Map();
|
|
475
479
|
let clone;
|
|
476
480
|
try {
|
|
477
481
|
clone = deepClone(element, styleMap, styleCache, nodeMap, compress);
|
|
@@ -480,19 +484,46 @@
|
|
|
480
484
|
throw e;
|
|
481
485
|
}
|
|
482
486
|
try {
|
|
483
|
-
await inlinePseudoElements(element, clone, styleMap, styleCache, compress);
|
|
487
|
+
await inlinePseudoElements(element, clone, styleMap, styleCache, compress, embedFonts);
|
|
484
488
|
} catch (e) {
|
|
485
489
|
console.warn("inlinePseudoElements failed:", e);
|
|
486
490
|
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
const
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
491
|
+
let classCSS = "";
|
|
492
|
+
if (compress) {
|
|
493
|
+
const keyToClass = generateCSSClasses(styleMap);
|
|
494
|
+
classCSS = Array.from(keyToClass.entries()).map(([key, className]) => `.${className}{${key}}`).join("");
|
|
495
|
+
for (const [node, key] of styleMap.entries()) {
|
|
496
|
+
if (node.tagName === "STYLE") continue;
|
|
497
|
+
const className = keyToClass.get(key);
|
|
498
|
+
if (className) node.classList.add(className);
|
|
499
|
+
const bgImage = node.style?.backgroundImage;
|
|
500
|
+
node.removeAttribute("style");
|
|
501
|
+
if (bgImage && bgImage !== "none") node.style.backgroundImage = bgImage;
|
|
502
|
+
}
|
|
503
|
+
} else {
|
|
504
|
+
for (const [node, key] of styleMap.entries()) {
|
|
505
|
+
if (node.tagName === "STYLE") continue;
|
|
506
|
+
node.setAttribute("style", key.replace(/;/g, "; "));
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
for (const [cloneNode, originalNode] of nodeMap.entries()) {
|
|
510
|
+
const scrollX = originalNode.scrollLeft;
|
|
511
|
+
const scrollY = originalNode.scrollTop;
|
|
512
|
+
const hasScroll = scrollX || scrollY;
|
|
513
|
+
if (hasScroll && cloneNode instanceof HTMLElement) {
|
|
514
|
+
cloneNode.style.overflow = "hidden";
|
|
515
|
+
cloneNode.style.scrollbarWidth = "none";
|
|
516
|
+
cloneNode.style.msOverflowStyle = "none";
|
|
517
|
+
const inner = document.createElement("div");
|
|
518
|
+
inner.style.transform = `translate(${-scrollX}px, ${-scrollY}px)`;
|
|
519
|
+
inner.style.willChange = "transform";
|
|
520
|
+
inner.style.display = "inline-block";
|
|
521
|
+
inner.style.width = "100%";
|
|
522
|
+
while (cloneNode.firstChild) {
|
|
523
|
+
inner.appendChild(cloneNode.firstChild);
|
|
524
|
+
}
|
|
525
|
+
cloneNode.appendChild(inner);
|
|
526
|
+
}
|
|
496
527
|
}
|
|
497
528
|
return { clone, classCSS, styleCache };
|
|
498
529
|
}
|
|
@@ -563,7 +594,7 @@
|
|
|
563
594
|
let baseCSS = "";
|
|
564
595
|
let dataURL;
|
|
565
596
|
let svgString;
|
|
566
|
-
({ clone, classCSS, styleCache } = await prepareClone(element, compress));
|
|
597
|
+
({ clone, classCSS, styleCache } = await prepareClone(element, compress, embedFonts));
|
|
567
598
|
await new Promise((resolve) => {
|
|
568
599
|
idle(async () => {
|
|
569
600
|
await inlineImages(clone);
|
|
@@ -579,7 +610,7 @@
|
|
|
579
610
|
if (embedFonts) {
|
|
580
611
|
await new Promise((resolve) => {
|
|
581
612
|
idle(async () => {
|
|
582
|
-
fontsCSS = await embedCustomFonts({ ignoreIconFonts:
|
|
613
|
+
fontsCSS = await embedCustomFonts({ ignoreIconFonts: !embedFonts });
|
|
583
614
|
resolve();
|
|
584
615
|
}, { fast });
|
|
585
616
|
});
|
|
@@ -726,7 +757,7 @@
|
|
|
726
757
|
toImg: () => toImg(url, { dpr, scale }),
|
|
727
758
|
toCanvas: () => toCanvas(url, { dpr, scale }),
|
|
728
759
|
toBlob: () => toBlob(url),
|
|
729
|
-
toPng: () => toRasterImg(url, { dpr, scale }, "png"),
|
|
760
|
+
toPng: (options2) => toRasterImg(url, { dpr, scale, ...options2 }, "png"),
|
|
730
761
|
toJpg: (options2) => toRasterImg(url, { dpr, scale, ...options2 }, "jpeg"),
|
|
731
762
|
toWebp: (options2) => toRasterImg(url, { dpr, scale, ...options2 }, "webp"),
|
|
732
763
|
download: ({ format = "png", filename = "capture", backgroundColor } = {}) => download(url, { dpr, scale, backgroundColor, format, filename })
|
|
@@ -789,8 +820,12 @@
|
|
|
789
820
|
}
|
|
790
821
|
}
|
|
791
822
|
if (embedFonts) {
|
|
792
|
-
await embedCustomFonts({ ignoreIconFonts:
|
|
823
|
+
await embedCustomFonts({ ignoreIconFonts: !embedFonts, preCached: true });
|
|
793
824
|
}
|
|
794
825
|
await Promise.all(promises);
|
|
795
826
|
}
|
|
827
|
+
|
|
828
|
+
// src/index.browser.js
|
|
829
|
+
window.snapdom = snapdom;
|
|
830
|
+
window.preCache = preCache;
|
|
796
831
|
})();
|
package/dist/snapdom.min.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
(()=>{var
|
|
2
|
-
`;return o}function
|
|
3
|
-
`}catch{console.warn("\u274C
|
|
1
|
+
(()=>{var b=new Map,S=new Map,I=new Map,T=new Map,k=new Map,R=new WeakMap;var ie=["div","span","p","a","img","ul","li","button","input","select","textarea","label","section","article","header","footer","nav","main","aside","h1","h2","h3","h4","h5","h6","svg","path","circle","rect","line","g","table","thead","tbody","tr","td","th"];function z(){for(let e of ie)_(e)}function _(e){if(T.has(e))return T.get(e);if(new Set(["script","style","meta","link","noscript","template"]).has(e)){let i={};return T.set(e,i),i}let o=document.getElementById("snapdom-sandbox");o||(o=document.createElement("div"),o.id="snapdom-sandbox",o.style.position="absolute",o.style.left="-9999px",o.style.top="-9999px",o.style.width="0",o.style.height="0",o.style.overflow="hidden",document.body.appendChild(o));let n=document.createElement(e);n.style.all="initial",o.appendChild(n);let a=getComputedStyle(n),r={};for(let i of a)r[i]=a.getPropertyValue(i);return o.removeChild(n),T.set(e,r),r}function U(e,t,o=!1){let n=[],a=_(t);for(let[r,i]of Object.entries(e))if(!o)i&&n.push(`${r}:${i}`);else{let c=a[r];i&&i!==c&&n.push(`${r}:${i}`)}return n.sort().join(";")}function H(e){let t=new Set;return e.nodeType!==Node.ELEMENT_NODE&&e.nodeType!==Node.DOCUMENT_FRAGMENT_NODE?[]:(e.tagName&&t.add(e.tagName.toLowerCase()),typeof e.querySelectorAll=="function"&&e.querySelectorAll("*").forEach(o=>t.add(o.tagName.toLowerCase())),Array.from(t))}function Y(e){let t=new Map;for(let n of e){let a=T.get(n);if(!a)continue;let r=Object.entries(a).map(([i,c])=>`${i}:${c};`).sort().join("");t.has(r)||t.set(r,[]),t.get(r).push(n)}let o="";for(let[n,a]of t.entries())o+=`${a.join(",")} { ${n} }
|
|
2
|
+
`;return o}function K(e){let t=new Set(e.values()),o=new Map,n=1;for(let a of t)o.set(a,`c${n++}`);return o}function N(e,{fast:t=!1}={}){if(t)return e();"requestIdleCallback"in window?requestIdleCallback(e,{timeout:50}):setTimeout(e,1)}function A(e,t=null){if(!(e instanceof Element))return window.getComputedStyle(e,t);let o=R.get(e);if(o||(o=new Map,R.set(e,o)),!o.has(t)){let n=window.getComputedStyle(e,t);o.set(t,n)}return o.get(t)}function J(e){let t=e.replace(/^['"]|['"]$/g,"");if(t.startsWith("\\"))try{return String.fromCharCode(parseInt(t.replace("\\",""),16))}catch{return t}return t}function X(e){let t=e.match(/url\(["']?([^"')]+)["']?\)/);return t?t[1]:null}function B(e){return[/font\s*awesome/i,/material\s*icons/i,/ionicons/i,/glyphicons/i,/feather/i,/bootstrap\s*icons/i,/remix\s*icons/i,/heroicons/i].some(o=>o.test(e))}function x(e,t=3e3){return b.has(e)?Promise.resolve(b.get(e)):new Promise((o,n)=>{let a=setTimeout(()=>{n(new Error("Image load timed out"))},t),r=new Image;r.crossOrigin="anonymous",r.onload=async()=>{clearTimeout(a);try{await r.decode();let i=document.createElement("canvas");i.width=r.width,i.height=r.height,i.getContext("2d").drawImage(r,0,0);try{let s=i.toDataURL("image/png");b.set(e,s),o(s)}catch{n(new Error("CORS restrictions prevented image capture"))}}catch(i){n(i)}},r.onerror=i=>{clearTimeout(a),n(new Error("Failed to load image: "+(i.message||"Unknown error")))},r.src=e})}function M(e){let t={};for(let o of e)t[o]=e.getPropertyValue(o);return t}function F(){return/^((?!chrome|android).)*safari/i.test(navigator.userAgent)}function O(e,t,o,n,a){if(e.tagName==="STYLE")return;n.has(e)||n.set(e,A(e));let r=n.get(e),i=M(r),c=e.tagName?.toLowerCase()||"div",s=U(i,c,a);o.set(t,s)}function D(e,t,o,n,a){if(e.nodeType===Node.ELEMENT_NODE&&e.getAttribute("data-capture")==="exclude"){let c=document.createElement("div"),s=e.getBoundingClientRect();return c.style.cssText=`display: inline-block; width: ${s.width}px; height: ${s.height}px; visibility: hidden;`,c}if(e.tagName==="IFRAME"){let c=document.createElement("div");return c.textContent="",c.style.cssText=`width: ${e.offsetWidth}px; height: ${e.offsetHeight}px; background: repeating-linear-gradient(45deg, #ddd, #ddd 5px, #f9f9f9 5px, #f9f9f9 10px);display: flex;align-items: center;justify-content: center;font-size: 12px;color: #555; border: 1px solid #aaa;`,c}if(e.nodeType===Node.ELEMENT_NODE&&e.getAttribute("data-capture")==="placeholder"){let c=e.cloneNode(!1);n.set(c,e),O(e,c,t,o,a);let s=document.createElement("div");return s.textContent=e.getAttribute("data-placeholder-text")||"",s.style.cssText="color: #666;font-size: 12px;text-align: center;line-height: 1.4;padding: 0.5em;box-sizing: border-box;",c.appendChild(s),c}if(e.tagName==="CANVAS"){let c=e.toDataURL(),s=document.createElement("img");return s.src=c,s.width=e.width,s.height=e.height,s.style.display="inline-block",s.style.width=`${e.width}px`,s.style.height=`${e.height}px`,s}if(e.nodeType===Node.TEXT_NODE){if(e.parentElement?.shadowRoot){let c=e.parentElement.tagName.toLowerCase();if(customElements.get(c))return null}return e.cloneNode(!0)}if(e.nodeType!==Node.ELEMENT_NODE)return e.cloneNode(!0);let r=e.cloneNode(!1);n.set(r,e),O(e,r,t,o,a);let i=document.createDocumentFragment();if(e.childNodes.forEach(c=>{let s=D(c,t,o,n,a);s&&i.appendChild(s)}),r.appendChild(i),e.shadowRoot){let c=Array.from(e.shadowRoot.children).filter(l=>l.tagName!=="STYLE").map(l=>D(l,t,o,n)).filter(Boolean),s=document.createDocumentFragment();c.forEach(l=>s.appendChild(l)),r.appendChild(s)}return r}async function G(e,t,o,n=32,a="#000"){t=t.replace(/^['"]+|['"]+$/g,"");let r=window.devicePixelRatio||1,c=document.createElement("canvas").getContext("2d");c.font=o?`${o} ${n}px "${t}"`:`${n}px "${t}"`;let s=c.measureText(e),l=s.actualBoundingBoxAscent||n*.8,d=s.actualBoundingBoxDescent||n*.2,m=l+d,h=s.width,p=document.createElement("canvas");p.width=Math.ceil(h*r),p.height=Math.ceil(m*r);let f=p.getContext("2d");return f.scale(r,r),f.font=c.font,f.textAlign="left",f.textBaseline="alphabetic",f.fillStyle=a,f.fillText(e,0,l),p.toDataURL()}async function j({ignoreIconFonts:e=!0,preCached:t=!1}){let o=Array.from(document.querySelectorAll('link[rel="stylesheet"]')).filter(a=>a.href),n="";for(let a of o)try{let i=await(await fetch(a.href)).text();if(e&&(B(a.href)||B(i)))continue;let c=/url\(([^)]+)\)/g,s=await Promise.all(Array.from(i.matchAll(c)).map(async d=>{let m=d[1].replace(/["']/g,"");if(m.startsWith("http")||(m=new URL(m,a.href).href),e&&B(m))return null;if(I.has(m))return{original:d[0],inlined:`url(${I.get(m)})`};try{let p=await(await fetch(m)).blob(),f=await new Promise(u=>{let w=new FileReader;w.onload=()=>u(w.result),w.readAsDataURL(p)});return I.set(m,f),{original:d[0],inlined:`url(${f})`}}catch{return console.warn("\u274C Failed to fetch font:",m),null}})),l=i;for(let d of s)d&&(l=l.replace(d.original,d.inlined));n+=l+`
|
|
3
|
+
`}catch{console.warn("\u274C Failed to fetch CSS:",a.href)}if(n&&t){let a=document.createElement("style");a.setAttribute("data-snapdom","embedFonts"),a.textContent=n,document.head.appendChild(a)}return n}async function W(e,t,o,n,a,r=!1){if(!(e instanceof Element)||!(t instanceof Element))return;for(let s of["::before","::after"])try{let l=A(e,s);if(!l)continue;let d=l.getPropertyValue("content"),m=l.getPropertyValue("background-image"),h=d&&d!=="none"&&d!=='""'&&d!=="''",p=m&&m.startsWith("url(");if(h||p){let f=l.getPropertyValue("font-family"),u=parseInt(l.getPropertyValue("font-size"))||32,w=parseInt(l.getPropertyValue("font-weight"))||!1,L=l.getPropertyValue("color")||"#000",y=document.createElement("span");y.dataset.snapdomPseudo=s;let E=M(l),P=U(E,"span",a);o.set(y,P);let q=f&&/font.*awesome|material|bootstrap|glyphicons|ionicons|remixicon|simple-line-icons|octicons|feather|typicons|weathericons/i.test(f),C=J(d);if(!r&&q&&C.length===1){let v=document.createElement("img");v.src=await G(C,f,w,u,L),v.style="display:block;width:100%;height:100%;object-fit:contain;",y.appendChild(v)}else if(C.startsWith("url(")){let v=C.match(/url\(["']?([^"')]+)["']?\)/);if(v?.[1])try{let $=document.createElement("img"),se=await x(v[1]);$.src=se,$.style="display:block;width:100%;height:100%;object-fit:contain;",y.appendChild($)}catch($){console.error(`[snapdom] Error in pseudo ${s} for`,e,$)}}else C&&C!=="none"&&(y.textContent=C);s==="::before"?t.insertBefore(y,t.firstChild):t.appendChild(y)}}catch(l){console.warn(`[snapdom] Failed to capture ${s} for`,e,l)}let i=Array.from(e.children),c=Array.from(t.children).filter(s=>!s.dataset.snapdomPseudo);for(let s=0;s<Math.min(i.length,c.length);s++)await W(i[s],c[s],o,n,a,r)}async function Q(e,t=!1,o=!1){let n=new Map,a=new WeakMap,r=new Map,i;try{i=D(e,n,a,r,t)}catch(s){throw console.warn("deepClone failed:",s),s}try{await W(e,i,n,a,t,o)}catch(s){console.warn("inlinePseudoElements failed:",s)}let c="";if(t){let s=K(n);c=Array.from(s.entries()).map(([l,d])=>`.${d}{${l}}`).join("");for(let[l,d]of n.entries()){if(l.tagName==="STYLE")continue;let m=s.get(d);m&&l.classList.add(m);let h=l.style?.backgroundImage;l.removeAttribute("style"),h&&h!=="none"&&(l.style.backgroundImage=h)}}else for(let[s,l]of n.entries())s.tagName!=="STYLE"&&s.setAttribute("style",l.replace(/;/g,"; "));for(let[s,l]of r.entries()){let d=l.scrollLeft,m=l.scrollTop;if((d||m)&&s instanceof HTMLElement){s.style.overflow="hidden",s.style.scrollbarWidth="none",s.style.msOverflowStyle="none";let p=document.createElement("div");for(p.style.transform=`translate(${-d}px, ${-m}px)`,p.style.willChange="transform",p.style.display="inline-block",p.style.width="100%";s.firstChild;)p.appendChild(s.firstChild);s.appendChild(p)}}return{clone:i,classCSS:c,styleCache:a}}async function Z(e){let t=Array.from(e.querySelectorAll("img")),o=async n=>{let a=n.src;try{let r=await x(a);n.src=r,n.width||(n.width=n.naturalWidth||100),n.height||(n.height=n.naturalHeight||100)}catch{let r=document.createElement("div");r.style=`width: ${n.width||100}px; height: ${n.height||100}px; background: #ccc; display: inline-block; text-align: center; line-height: ${n.height||100}px; color: #666; font-size: 12px;`,r.innerText="img",n.replaceWith(r)}};for(let n=0;n<t.length;n+=4){let a=t.slice(n,n+4).map(o);await Promise.allSettled(a)}}async function ee(e,t,o){let n=[[e,t]];for(;n.length;){let[a,r]=n.shift(),i=o.get(a)||A(a);o.has(a)||o.set(a,i);let c=i.getPropertyValue("background-image");if(c&&c.includes("url(")){let d=c.match(/url\(["']?([^"')]+)["']?\)/);if(d?.[1])try{let m=d[1],h;S.has(m)?h=S.get(m):(h=await x(m),S.set(m,h)),r.style.backgroundImage=`url(${h})`}catch{r.style.backgroundImage="none"}}let s=Array.from(a.children),l=Array.from(r.children);for(let d=0;d<Math.min(s.length,l.length);d++)n.push([s[d],l[d]])}}async function te(e,t={}){if(!e)throw new Error("Element cannot be null or undefined");let{compress:o=!0,embedFonts:n=!1,fast:a=!0,scale:r=1}=t,i,c,s,l="",d="",m,h;if({clone:i,classCSS:c,styleCache:s}=await Q(e,o,n),await new Promise(f=>{N(async()=>{await Z(i),f()},{fast:a})}),await new Promise(f=>{N(async()=>{await ee(e,i,s),f()},{fast:a})}),n&&await new Promise(f=>{N(async()=>{l=await j({ignoreIconFonts:!n}),f()},{fast:a})}),o){let f=H(i).sort(),u=f.join(",");k.has(u)?d=k.get(u):await new Promise(w=>{N(()=>{d=Y(f),k.set(u,d),w()},{fast:a})})}await new Promise(f=>{N(()=>{let u=e.getBoundingClientRect(),w=Math.ceil(u.width),L=Math.ceil(u.height);i.setAttribute("xmlns","http://www.w3.org/1999/xhtml"),i.style.transformOrigin="top left",r!==1&&F()&&(i.style.scale=`${r}`);let y="http://www.w3.org/2000/svg",E=document.createElementNS(y,"foreignObject");E.setAttribute("width","100%"),E.setAttribute("height","100%");let P=document.createElement("style");P.textContent=d+l+"svg{overflow:visible;}"+c,E.appendChild(P),E.appendChild(i);let C=new XMLSerializer().serializeToString(E);h=`<svg xmlns="${y}" width="${w}" height="${L}" viewBox="0 0 ${w} ${L}">`+C+"</svg>",m=`data:image/svg+xml;charset=utf-8,${encodeURIComponent(h)}`,f()},{fast:a})});let p=document.getElementById("snapdom-sandbox");return p&&p.style.position==="absolute"&&p.remove(),m}async function ce(e,{dpr:t=1,scale:o=1}){let n=new Image;return n.src=e,await n.decode(),F?(n.width=n.width*o,n.height=n.height*o):(n.width=n.width/o,n.height=n.height/o),n}async function ne(e,{dpr:t=1,scale:o=1}){let n=new Image;n.src=e,await n.decode();let a=document.createElement("canvas"),r=n.width*o*t,i=n.height*o*t;a.width=Math.ceil(r),a.height=Math.ceil(i);let c=a.getContext("2d");return c.scale(o*t,o*t),c.drawImage(n,0,0),a}async function oe(e){let t=decodeURIComponent(e.split(",")[1]);return new Blob([t],{type:"image/svg+xml"})}async function ae(e,{dpr:t=1,scale:o=1},n){let a=await ne(e,{dpr:t,scale:o});if(!n)return a;let r=document.createElement("canvas");r.width=a.width,r.height=a.height;let i=r.getContext("2d");return i.fillStyle=n,i.fillRect(0,0,r.width,r.height),i.drawImage(a,0,0),r}async function V(e,{dpr:t=1,scale:o=1,backgroundColor:n="#fff",quality:a},r="png"){let i=await ae(e,{dpr:t,scale:o},n),c=new Image;return c.src=i.toDataURL(`image/${r}`,a),await c.decode(),c.style.width=`${i.width/t}px`,c.style.height=`${i.height/t}px`,c}async function le(e,{dpr:t=1,scale:o=1,backgroundColor:n="#fff",format:a="png",filename:r="capture"}={}){if(a==="svg"){let h=await oe(e),p=URL.createObjectURL(h),f=document.createElement("a");f.href=p,f.download=`${r}.svg`,f.click(),URL.revokeObjectURL(p);return}let i=["jpg","jpeg","webp"].includes(a)?"#fff":void 0,s=await ae(e,{dpr:t,scale:o},n??i),l={jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",webp:"image/webp"}[a]||"image/png",d=s.toDataURL(l),m=document.createElement("a");m.href=d,m.download=`${r}.${a}`,m.click()}async function g(e,t={}){if(t={scale:1,...t},!e)throw new Error("Element cannot be null or undefined");return await g.capture(e,t)}g.capture=async(e,t={})=>{let o=await te(e,t),n=window.devicePixelRatio||1,a=t.scale||1;return{url:o,options:t,toRaw:()=>o,toImg:()=>ce(o,{dpr:n,scale:a}),toCanvas:()=>ne(o,{dpr:n,scale:a}),toBlob:()=>oe(o),toPng:r=>V(o,{dpr:n,scale:a,...r},"png"),toJpg:r=>V(o,{dpr:n,scale:a,...r},"jpeg"),toWebp:r=>V(o,{dpr:n,scale:a,...r},"webp"),download:({format:r="png",filename:i="capture",backgroundColor:c}={})=>le(o,{dpr:n,scale:a,backgroundColor:c,format:r,filename:i})}};g.toRaw=async(e,t)=>(await g.capture(e,t)).toRaw();g.toImg=async(e,t)=>(await g.capture(e,t)).toImg();g.toCanvas=async(e,t)=>(await g.capture(e,t)).toCanvas();g.toBlob=async(e,t)=>(await g.capture(e,t)).toBlob();g.toPng=async(e,t)=>(await g.capture(e,t)).toPng(t);g.toJpg=async(e,t)=>(await g.capture(e,t)).toJpg(t);g.toWebp=async(e,t)=>(await g.capture(e,t)).toWebp(t);g.download=async(e,t={})=>{let{format:o="png",filename:n="capture",backgroundColor:a,...r}=t;return await(await g.capture(e,r)).download({format:o,filename:n,backgroundColor:a})};async function re(e=document,t={}){let{embedFonts:o=!0,reset:n=!1,preWarm:a=!0}=t;if(n){b.clear(),S.clear(),I.clear(),k.clear(),R.clear();return}await document.fonts.ready,z();let r=[],i=[];e?.querySelectorAll&&(r=Array.from(e.querySelectorAll("img[src]")),i=Array.from(e.querySelectorAll("*")));let c=[];for(let s of r){let l=s.src;b.has(l)||c.push(x(l).then(d=>b.set(l,d)).catch(()=>{}))}for(let s of i){let l=getComputedStyle(s).backgroundImage,d=X(l);d&&!S.has(d)&&c.push(x(d).then(m=>S.set(d,m)).catch(()=>{}))}o&&await j({ignoreIconFonts:!o,preCached:!0}),await Promise.all(c)}window.snapdom=g;window.preCache=re;})();
|
package/dist/snapdom.min.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
2
|
-
`;return o}function
|
|
3
|
-
`}catch{console.warn("\u274C
|
|
1
|
+
var b=new Map,S=new Map,I=new Map,T=new Map,k=new Map,R=new WeakMap;var se=["div","span","p","a","img","ul","li","button","input","select","textarea","label","section","article","header","footer","nav","main","aside","h1","h2","h3","h4","h5","h6","svg","path","circle","rect","line","g","table","thead","tbody","tr","td","th"];function z(){for(let e of se)_(e)}function _(e){if(T.has(e))return T.get(e);if(new Set(["script","style","meta","link","noscript","template"]).has(e)){let i={};return T.set(e,i),i}let o=document.getElementById("snapdom-sandbox");o||(o=document.createElement("div"),o.id="snapdom-sandbox",o.style.position="absolute",o.style.left="-9999px",o.style.top="-9999px",o.style.width="0",o.style.height="0",o.style.overflow="hidden",document.body.appendChild(o));let n=document.createElement(e);n.style.all="initial",o.appendChild(n);let a=getComputedStyle(n),r={};for(let i of a)r[i]=a.getPropertyValue(i);return o.removeChild(n),T.set(e,r),r}function U(e,t,o=!1){let n=[],a=_(t);for(let[r,i]of Object.entries(e))if(!o)i&&n.push(`${r}:${i}`);else{let c=a[r];i&&i!==c&&n.push(`${r}:${i}`)}return n.sort().join(";")}function H(e){let t=new Set;return e.nodeType!==Node.ELEMENT_NODE&&e.nodeType!==Node.DOCUMENT_FRAGMENT_NODE?[]:(e.tagName&&t.add(e.tagName.toLowerCase()),typeof e.querySelectorAll=="function"&&e.querySelectorAll("*").forEach(o=>t.add(o.tagName.toLowerCase())),Array.from(t))}function Y(e){let t=new Map;for(let n of e){let a=T.get(n);if(!a)continue;let r=Object.entries(a).map(([i,c])=>`${i}:${c};`).sort().join("");t.has(r)||t.set(r,[]),t.get(r).push(n)}let o="";for(let[n,a]of t.entries())o+=`${a.join(",")} { ${n} }
|
|
2
|
+
`;return o}function K(e){let t=new Set(e.values()),o=new Map,n=1;for(let a of t)o.set(a,`c${n++}`);return o}function N(e,{fast:t=!1}={}){if(t)return e();"requestIdleCallback"in window?requestIdleCallback(e,{timeout:50}):setTimeout(e,1)}function A(e,t=null){if(!(e instanceof Element))return window.getComputedStyle(e,t);let o=R.get(e);if(o||(o=new Map,R.set(e,o)),!o.has(t)){let n=window.getComputedStyle(e,t);o.set(t,n)}return o.get(t)}function J(e){let t=e.replace(/^['"]|['"]$/g,"");if(t.startsWith("\\"))try{return String.fromCharCode(parseInt(t.replace("\\",""),16))}catch{return t}return t}function X(e){let t=e.match(/url\(["']?([^"')]+)["']?\)/);return t?t[1]:null}function B(e){return[/font\s*awesome/i,/material\s*icons/i,/ionicons/i,/glyphicons/i,/feather/i,/bootstrap\s*icons/i,/remix\s*icons/i,/heroicons/i].some(o=>o.test(e))}function x(e,t=3e3){return b.has(e)?Promise.resolve(b.get(e)):new Promise((o,n)=>{let a=setTimeout(()=>{n(new Error("Image load timed out"))},t),r=new Image;r.crossOrigin="anonymous",r.onload=async()=>{clearTimeout(a);try{await r.decode();let i=document.createElement("canvas");i.width=r.width,i.height=r.height,i.getContext("2d").drawImage(r,0,0);try{let s=i.toDataURL("image/png");b.set(e,s),o(s)}catch{n(new Error("CORS restrictions prevented image capture"))}}catch(i){n(i)}},r.onerror=i=>{clearTimeout(a),n(new Error("Failed to load image: "+(i.message||"Unknown error")))},r.src=e})}function M(e){let t={};for(let o of e)t[o]=e.getPropertyValue(o);return t}function F(){return/^((?!chrome|android).)*safari/i.test(navigator.userAgent)}function O(e,t,o,n,a){if(e.tagName==="STYLE")return;n.has(e)||n.set(e,A(e));let r=n.get(e),i=M(r),c=e.tagName?.toLowerCase()||"div",s=U(i,c,a);o.set(t,s)}function D(e,t,o,n,a){if(e.nodeType===Node.ELEMENT_NODE&&e.getAttribute("data-capture")==="exclude"){let c=document.createElement("div"),s=e.getBoundingClientRect();return c.style.cssText=`display: inline-block; width: ${s.width}px; height: ${s.height}px; visibility: hidden;`,c}if(e.tagName==="IFRAME"){let c=document.createElement("div");return c.textContent="",c.style.cssText=`width: ${e.offsetWidth}px; height: ${e.offsetHeight}px; background: repeating-linear-gradient(45deg, #ddd, #ddd 5px, #f9f9f9 5px, #f9f9f9 10px);display: flex;align-items: center;justify-content: center;font-size: 12px;color: #555; border: 1px solid #aaa;`,c}if(e.nodeType===Node.ELEMENT_NODE&&e.getAttribute("data-capture")==="placeholder"){let c=e.cloneNode(!1);n.set(c,e),O(e,c,t,o,a);let s=document.createElement("div");return s.textContent=e.getAttribute("data-placeholder-text")||"",s.style.cssText="color: #666;font-size: 12px;text-align: center;line-height: 1.4;padding: 0.5em;box-sizing: border-box;",c.appendChild(s),c}if(e.tagName==="CANVAS"){let c=e.toDataURL(),s=document.createElement("img");return s.src=c,s.width=e.width,s.height=e.height,s.style.display="inline-block",s.style.width=`${e.width}px`,s.style.height=`${e.height}px`,s}if(e.nodeType===Node.TEXT_NODE){if(e.parentElement?.shadowRoot){let c=e.parentElement.tagName.toLowerCase();if(customElements.get(c))return null}return e.cloneNode(!0)}if(e.nodeType!==Node.ELEMENT_NODE)return e.cloneNode(!0);let r=e.cloneNode(!1);n.set(r,e),O(e,r,t,o,a);let i=document.createDocumentFragment();if(e.childNodes.forEach(c=>{let s=D(c,t,o,n,a);s&&i.appendChild(s)}),r.appendChild(i),e.shadowRoot){let c=Array.from(e.shadowRoot.children).filter(l=>l.tagName!=="STYLE").map(l=>D(l,t,o,n)).filter(Boolean),s=document.createDocumentFragment();c.forEach(l=>s.appendChild(l)),r.appendChild(s)}return r}async function G(e,t,o,n=32,a="#000"){t=t.replace(/^['"]+|['"]+$/g,"");let r=window.devicePixelRatio||1,c=document.createElement("canvas").getContext("2d");c.font=o?`${o} ${n}px "${t}"`:`${n}px "${t}"`;let s=c.measureText(e),l=s.actualBoundingBoxAscent||n*.8,d=s.actualBoundingBoxDescent||n*.2,f=l+d,h=s.width,p=document.createElement("canvas");p.width=Math.ceil(h*r),p.height=Math.ceil(f*r);let m=p.getContext("2d");return m.scale(r,r),m.font=c.font,m.textAlign="left",m.textBaseline="alphabetic",m.fillStyle=a,m.fillText(e,0,l),p.toDataURL()}async function j({ignoreIconFonts:e=!0,preCached:t=!1}){let o=Array.from(document.querySelectorAll('link[rel="stylesheet"]')).filter(a=>a.href),n="";for(let a of o)try{let i=await(await fetch(a.href)).text();if(e&&(B(a.href)||B(i)))continue;let c=/url\(([^)]+)\)/g,s=await Promise.all(Array.from(i.matchAll(c)).map(async d=>{let f=d[1].replace(/["']/g,"");if(f.startsWith("http")||(f=new URL(f,a.href).href),e&&B(f))return null;if(I.has(f))return{original:d[0],inlined:`url(${I.get(f)})`};try{let p=await(await fetch(f)).blob(),m=await new Promise(u=>{let w=new FileReader;w.onload=()=>u(w.result),w.readAsDataURL(p)});return I.set(f,m),{original:d[0],inlined:`url(${m})`}}catch{return console.warn("\u274C Failed to fetch font:",f),null}})),l=i;for(let d of s)d&&(l=l.replace(d.original,d.inlined));n+=l+`
|
|
3
|
+
`}catch{console.warn("\u274C Failed to fetch CSS:",a.href)}if(n&&t){let a=document.createElement("style");a.setAttribute("data-snapdom","embedFonts"),a.textContent=n,document.head.appendChild(a)}return n}async function W(e,t,o,n,a,r=!1){if(!(e instanceof Element)||!(t instanceof Element))return;for(let s of["::before","::after"])try{let l=A(e,s);if(!l)continue;let d=l.getPropertyValue("content"),f=l.getPropertyValue("background-image"),h=d&&d!=="none"&&d!=='""'&&d!=="''",p=f&&f.startsWith("url(");if(h||p){let m=l.getPropertyValue("font-family"),u=parseInt(l.getPropertyValue("font-size"))||32,w=parseInt(l.getPropertyValue("font-weight"))||!1,L=l.getPropertyValue("color")||"#000",y=document.createElement("span");y.dataset.snapdomPseudo=s;let E=M(l),P=U(E,"span",a);o.set(y,P);let q=m&&/font.*awesome|material|bootstrap|glyphicons|ionicons|remixicon|simple-line-icons|octicons|feather|typicons|weathericons/i.test(m),C=J(d);if(!r&&q&&C.length===1){let v=document.createElement("img");v.src=await G(C,m,w,u,L),v.style="display:block;width:100%;height:100%;object-fit:contain;",y.appendChild(v)}else if(C.startsWith("url(")){let v=C.match(/url\(["']?([^"')]+)["']?\)/);if(v?.[1])try{let $=document.createElement("img"),re=await x(v[1]);$.src=re,$.style="display:block;width:100%;height:100%;object-fit:contain;",y.appendChild($)}catch($){console.error(`[snapdom] Error in pseudo ${s} for`,e,$)}}else C&&C!=="none"&&(y.textContent=C);s==="::before"?t.insertBefore(y,t.firstChild):t.appendChild(y)}}catch(l){console.warn(`[snapdom] Failed to capture ${s} for`,e,l)}let i=Array.from(e.children),c=Array.from(t.children).filter(s=>!s.dataset.snapdomPseudo);for(let s=0;s<Math.min(i.length,c.length);s++)await W(i[s],c[s],o,n,a,r)}async function Q(e,t=!1,o=!1){let n=new Map,a=new WeakMap,r=new Map,i;try{i=D(e,n,a,r,t)}catch(s){throw console.warn("deepClone failed:",s),s}try{await W(e,i,n,a,t,o)}catch(s){console.warn("inlinePseudoElements failed:",s)}let c="";if(t){let s=K(n);c=Array.from(s.entries()).map(([l,d])=>`.${d}{${l}}`).join("");for(let[l,d]of n.entries()){if(l.tagName==="STYLE")continue;let f=s.get(d);f&&l.classList.add(f);let h=l.style?.backgroundImage;l.removeAttribute("style"),h&&h!=="none"&&(l.style.backgroundImage=h)}}else for(let[s,l]of n.entries())s.tagName!=="STYLE"&&s.setAttribute("style",l.replace(/;/g,"; "));for(let[s,l]of r.entries()){let d=l.scrollLeft,f=l.scrollTop;if((d||f)&&s instanceof HTMLElement){s.style.overflow="hidden",s.style.scrollbarWidth="none",s.style.msOverflowStyle="none";let p=document.createElement("div");for(p.style.transform=`translate(${-d}px, ${-f}px)`,p.style.willChange="transform",p.style.display="inline-block",p.style.width="100%";s.firstChild;)p.appendChild(s.firstChild);s.appendChild(p)}}return{clone:i,classCSS:c,styleCache:a}}async function Z(e){let t=Array.from(e.querySelectorAll("img")),o=async n=>{let a=n.src;try{let r=await x(a);n.src=r,n.width||(n.width=n.naturalWidth||100),n.height||(n.height=n.naturalHeight||100)}catch{let r=document.createElement("div");r.style=`width: ${n.width||100}px; height: ${n.height||100}px; background: #ccc; display: inline-block; text-align: center; line-height: ${n.height||100}px; color: #666; font-size: 12px;`,r.innerText="img",n.replaceWith(r)}};for(let n=0;n<t.length;n+=4){let a=t.slice(n,n+4).map(o);await Promise.allSettled(a)}}async function ee(e,t,o){let n=[[e,t]];for(;n.length;){let[a,r]=n.shift(),i=o.get(a)||A(a);o.has(a)||o.set(a,i);let c=i.getPropertyValue("background-image");if(c&&c.includes("url(")){let d=c.match(/url\(["']?([^"')]+)["']?\)/);if(d?.[1])try{let f=d[1],h;S.has(f)?h=S.get(f):(h=await x(f),S.set(f,h)),r.style.backgroundImage=`url(${h})`}catch{r.style.backgroundImage="none"}}let s=Array.from(a.children),l=Array.from(r.children);for(let d=0;d<Math.min(s.length,l.length);d++)n.push([s[d],l[d]])}}async function te(e,t={}){if(!e)throw new Error("Element cannot be null or undefined");let{compress:o=!0,embedFonts:n=!1,fast:a=!0,scale:r=1}=t,i,c,s,l="",d="",f,h;if({clone:i,classCSS:c,styleCache:s}=await Q(e,o,n),await new Promise(m=>{N(async()=>{await Z(i),m()},{fast:a})}),await new Promise(m=>{N(async()=>{await ee(e,i,s),m()},{fast:a})}),n&&await new Promise(m=>{N(async()=>{l=await j({ignoreIconFonts:!n}),m()},{fast:a})}),o){let m=H(i).sort(),u=m.join(",");k.has(u)?d=k.get(u):await new Promise(w=>{N(()=>{d=Y(m),k.set(u,d),w()},{fast:a})})}await new Promise(m=>{N(()=>{let u=e.getBoundingClientRect(),w=Math.ceil(u.width),L=Math.ceil(u.height);i.setAttribute("xmlns","http://www.w3.org/1999/xhtml"),i.style.transformOrigin="top left",r!==1&&F()&&(i.style.scale=`${r}`);let y="http://www.w3.org/2000/svg",E=document.createElementNS(y,"foreignObject");E.setAttribute("width","100%"),E.setAttribute("height","100%");let P=document.createElement("style");P.textContent=d+l+"svg{overflow:visible;}"+c,E.appendChild(P),E.appendChild(i);let C=new XMLSerializer().serializeToString(E);h=`<svg xmlns="${y}" width="${w}" height="${L}" viewBox="0 0 ${w} ${L}">`+C+"</svg>",f=`data:image/svg+xml;charset=utf-8,${encodeURIComponent(h)}`,m()},{fast:a})});let p=document.getElementById("snapdom-sandbox");return p&&p.style.position==="absolute"&&p.remove(),f}async function ie(e,{dpr:t=1,scale:o=1}){let n=new Image;return n.src=e,await n.decode(),F?(n.width=n.width*o,n.height=n.height*o):(n.width=n.width/o,n.height=n.height/o),n}async function ne(e,{dpr:t=1,scale:o=1}){let n=new Image;n.src=e,await n.decode();let a=document.createElement("canvas"),r=n.width*o*t,i=n.height*o*t;a.width=Math.ceil(r),a.height=Math.ceil(i);let c=a.getContext("2d");return c.scale(o*t,o*t),c.drawImage(n,0,0),a}async function oe(e){let t=decodeURIComponent(e.split(",")[1]);return new Blob([t],{type:"image/svg+xml"})}async function ae(e,{dpr:t=1,scale:o=1},n){let a=await ne(e,{dpr:t,scale:o});if(!n)return a;let r=document.createElement("canvas");r.width=a.width,r.height=a.height;let i=r.getContext("2d");return i.fillStyle=n,i.fillRect(0,0,r.width,r.height),i.drawImage(a,0,0),r}async function V(e,{dpr:t=1,scale:o=1,backgroundColor:n="#fff",quality:a},r="png"){let i=await ae(e,{dpr:t,scale:o},n),c=new Image;return c.src=i.toDataURL(`image/${r}`,a),await c.decode(),c.style.width=`${i.width/t}px`,c.style.height=`${i.height/t}px`,c}async function ce(e,{dpr:t=1,scale:o=1,backgroundColor:n="#fff",format:a="png",filename:r="capture"}={}){if(a==="svg"){let h=await oe(e),p=URL.createObjectURL(h),m=document.createElement("a");m.href=p,m.download=`${r}.svg`,m.click(),URL.revokeObjectURL(p);return}let i=["jpg","jpeg","webp"].includes(a)?"#fff":void 0,s=await ae(e,{dpr:t,scale:o},n??i),l={jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",webp:"image/webp"}[a]||"image/png",d=s.toDataURL(l),f=document.createElement("a");f.href=d,f.download=`${r}.${a}`,f.click()}async function g(e,t={}){if(t={scale:1,...t},!e)throw new Error("Element cannot be null or undefined");return await g.capture(e,t)}g.capture=async(e,t={})=>{let o=await te(e,t),n=window.devicePixelRatio||1,a=t.scale||1;return{url:o,options:t,toRaw:()=>o,toImg:()=>ie(o,{dpr:n,scale:a}),toCanvas:()=>ne(o,{dpr:n,scale:a}),toBlob:()=>oe(o),toPng:r=>V(o,{dpr:n,scale:a,...r},"png"),toJpg:r=>V(o,{dpr:n,scale:a,...r},"jpeg"),toWebp:r=>V(o,{dpr:n,scale:a,...r},"webp"),download:({format:r="png",filename:i="capture",backgroundColor:c}={})=>ce(o,{dpr:n,scale:a,backgroundColor:c,format:r,filename:i})}};g.toRaw=async(e,t)=>(await g.capture(e,t)).toRaw();g.toImg=async(e,t)=>(await g.capture(e,t)).toImg();g.toCanvas=async(e,t)=>(await g.capture(e,t)).toCanvas();g.toBlob=async(e,t)=>(await g.capture(e,t)).toBlob();g.toPng=async(e,t)=>(await g.capture(e,t)).toPng(t);g.toJpg=async(e,t)=>(await g.capture(e,t)).toJpg(t);g.toWebp=async(e,t)=>(await g.capture(e,t)).toWebp(t);g.download=async(e,t={})=>{let{format:o="png",filename:n="capture",backgroundColor:a,...r}=t;return await(await g.capture(e,r)).download({format:o,filename:n,backgroundColor:a})};async function le(e=document,t={}){let{embedFonts:o=!0,reset:n=!1,preWarm:a=!0}=t;if(n){b.clear(),S.clear(),I.clear(),k.clear(),R.clear();return}await document.fonts.ready,z();let r=[],i=[];e?.querySelectorAll&&(r=Array.from(e.querySelectorAll("img[src]")),i=Array.from(e.querySelectorAll("*")));let c=[];for(let s of r){let l=s.src;b.has(l)||c.push(x(l).then(d=>b.set(l,d)).catch(()=>{}))}for(let s of i){let l=getComputedStyle(s).backgroundImage,d=X(l);d&&!S.has(d)&&c.push(x(d).then(f=>S.set(d,f)).catch(()=>{}))}o&&await j({ignoreIconFonts:!o,preCached:!0}),await Promise.all(c)}export{le as preCache,g as snapdom};
|
package/dist/snapdom.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
/*
|
|
3
3
|
* snapdom
|
|
4
|
-
* v.1.
|
|
4
|
+
* v.1.2.1
|
|
5
5
|
* Author Juan Martin Muda
|
|
6
6
|
* License MIT
|
|
7
7
|
**/
|
|
@@ -300,8 +300,6 @@ function deepClone(node, styleMap, styleCache, nodeMap, compress) {
|
|
|
300
300
|
return img;
|
|
301
301
|
}
|
|
302
302
|
if (node.nodeType === Node.TEXT_NODE) {
|
|
303
|
-
const trimmed = node.textContent.trim();
|
|
304
|
-
if (!trimmed) return null;
|
|
305
303
|
if (node.parentElement?.shadowRoot) {
|
|
306
304
|
const tag = node.parentElement.tagName.toLowerCase();
|
|
307
305
|
if (customElements.get(tag)) return null;
|
|
@@ -330,17 +328,25 @@ function deepClone(node, styleMap, styleCache, nodeMap, compress) {
|
|
|
330
328
|
// src/modules/fonts.js
|
|
331
329
|
async function iconToImage(unicodeChar, fontFamily, fontWeight, fontSize = 32, color = "#000") {
|
|
332
330
|
fontFamily = fontFamily.replace(/^['"]+|['"]+$/g, "");
|
|
333
|
-
|
|
334
|
-
|
|
331
|
+
const dpr = window.devicePixelRatio || 1;
|
|
332
|
+
const tempCanvas = document.createElement("canvas");
|
|
333
|
+
const tempCtx = tempCanvas.getContext("2d");
|
|
334
|
+
tempCtx.font = fontWeight ? `${fontWeight} ${fontSize}px "${fontFamily}"` : `${fontSize}px "${fontFamily}"`;
|
|
335
|
+
const metrics = tempCtx.measureText(unicodeChar);
|
|
336
|
+
const ascent = metrics.actualBoundingBoxAscent || fontSize * 0.8;
|
|
337
|
+
const descent = metrics.actualBoundingBoxDescent || fontSize * 0.2;
|
|
338
|
+
const height = ascent + descent;
|
|
339
|
+
const width = metrics.width;
|
|
335
340
|
const canvas = document.createElement("canvas");
|
|
336
|
-
canvas.width =
|
|
337
|
-
canvas.height =
|
|
341
|
+
canvas.width = Math.ceil(width * dpr);
|
|
342
|
+
canvas.height = Math.ceil(height * dpr);
|
|
338
343
|
const ctx = canvas.getContext("2d");
|
|
339
|
-
ctx.
|
|
340
|
-
ctx.
|
|
341
|
-
ctx.
|
|
344
|
+
ctx.scale(dpr, dpr);
|
|
345
|
+
ctx.font = tempCtx.font;
|
|
346
|
+
ctx.textAlign = "left";
|
|
347
|
+
ctx.textBaseline = "alphabetic";
|
|
342
348
|
ctx.fillStyle = color;
|
|
343
|
-
ctx.fillText(unicodeChar,
|
|
349
|
+
ctx.fillText(unicodeChar, 0, ascent);
|
|
344
350
|
return canvas.toDataURL();
|
|
345
351
|
}
|
|
346
352
|
async function embedCustomFonts({ ignoreIconFonts = true, preCached = false }) {
|
|
@@ -351,7 +357,6 @@ async function embedCustomFonts({ ignoreIconFonts = true, preCached = false }) {
|
|
|
351
357
|
const res = await fetch(link.href);
|
|
352
358
|
const cssText = await res.text();
|
|
353
359
|
if (ignoreIconFonts && (isIconFont(link.href) || isIconFont(cssText))) {
|
|
354
|
-
console.log("\u23ED\uFE0F Ignorando icon font CSS:", link.href);
|
|
355
360
|
continue;
|
|
356
361
|
}
|
|
357
362
|
const urlRegex = /url\(([^)]+)\)/g;
|
|
@@ -362,7 +367,6 @@ async function embedCustomFonts({ ignoreIconFonts = true, preCached = false }) {
|
|
|
362
367
|
url = new URL(url, link.href).href;
|
|
363
368
|
}
|
|
364
369
|
if (ignoreIconFonts && isIconFont(url)) {
|
|
365
|
-
console.log("\u23ED\uFE0F Ignorando icon font URL:", url);
|
|
366
370
|
return null;
|
|
367
371
|
}
|
|
368
372
|
if (resourceCache.has(url)) {
|
|
@@ -379,7 +383,7 @@ async function embedCustomFonts({ ignoreIconFonts = true, preCached = false }) {
|
|
|
379
383
|
resourceCache.set(url, b64);
|
|
380
384
|
return { original: match[0], inlined: `url(${b64})` };
|
|
381
385
|
} catch (err) {
|
|
382
|
-
console.warn("\u274C
|
|
386
|
+
console.warn("\u274C Failed to fetch font:", url);
|
|
383
387
|
return null;
|
|
384
388
|
}
|
|
385
389
|
})
|
|
@@ -392,7 +396,7 @@ async function embedCustomFonts({ ignoreIconFonts = true, preCached = false }) {
|
|
|
392
396
|
}
|
|
393
397
|
finalCSS += cssFinal + "\n";
|
|
394
398
|
} catch (e) {
|
|
395
|
-
console.warn("\u274C
|
|
399
|
+
console.warn("\u274C Failed to fetch CSS:", link.href);
|
|
396
400
|
}
|
|
397
401
|
}
|
|
398
402
|
if (finalCSS && preCached) {
|
|
@@ -405,7 +409,7 @@ async function embedCustomFonts({ ignoreIconFonts = true, preCached = false }) {
|
|
|
405
409
|
}
|
|
406
410
|
|
|
407
411
|
// src/modules/pseudo.js
|
|
408
|
-
async function inlinePseudoElements(source, clone, styleMap, styleCache, compress) {
|
|
412
|
+
async function inlinePseudoElements(source, clone, styleMap, styleCache, compress, embedFonts = false) {
|
|
409
413
|
if (!(source instanceof Element) || !(clone instanceof Element)) return;
|
|
410
414
|
for (const pseudo of ["::before", "::after"]) {
|
|
411
415
|
try {
|
|
@@ -427,7 +431,7 @@ async function inlinePseudoElements(source, clone, styleMap, styleCache, compres
|
|
|
427
431
|
styleMap.set(pseudoEl, key);
|
|
428
432
|
const isIconFont2 = fontFamily && /font.*awesome|material|bootstrap|glyphicons|ionicons|remixicon|simple-line-icons|octicons|feather|typicons|weathericons/i.test(fontFamily);
|
|
429
433
|
let cleanContent = parseContent(content);
|
|
430
|
-
if (isIconFont2 && cleanContent.length === 1) {
|
|
434
|
+
if (!embedFonts && isIconFont2 && cleanContent.length === 1) {
|
|
431
435
|
const imgEl = document.createElement("img");
|
|
432
436
|
imgEl.src = await iconToImage(cleanContent, fontFamily, fontWeight, fontSize, color);
|
|
433
437
|
imgEl.style = "display:block;width:100%;height:100%;object-fit:contain;";
|
|
@@ -463,15 +467,15 @@ async function inlinePseudoElements(source, clone, styleMap, styleCache, compres
|
|
|
463
467
|
(child) => !child.dataset.snapdomPseudo
|
|
464
468
|
);
|
|
465
469
|
for (let i = 0; i < Math.min(sChildren.length, cChildren.length); i++) {
|
|
466
|
-
await inlinePseudoElements(sChildren[i], cChildren[i], styleMap, styleCache, compress);
|
|
470
|
+
await inlinePseudoElements(sChildren[i], cChildren[i], styleMap, styleCache, compress, embedFonts);
|
|
467
471
|
}
|
|
468
472
|
}
|
|
469
473
|
|
|
470
474
|
// src/core/prepare.js
|
|
471
|
-
async function prepareClone(element, compress = false) {
|
|
475
|
+
async function prepareClone(element, compress = false, embedFonts = false) {
|
|
472
476
|
const styleMap = /* @__PURE__ */ new Map();
|
|
473
477
|
const styleCache = /* @__PURE__ */ new WeakMap();
|
|
474
|
-
const nodeMap = /* @__PURE__ */ new
|
|
478
|
+
const nodeMap = /* @__PURE__ */ new Map();
|
|
475
479
|
let clone;
|
|
476
480
|
try {
|
|
477
481
|
clone = deepClone(element, styleMap, styleCache, nodeMap, compress);
|
|
@@ -480,19 +484,46 @@ async function prepareClone(element, compress = false) {
|
|
|
480
484
|
throw e;
|
|
481
485
|
}
|
|
482
486
|
try {
|
|
483
|
-
await inlinePseudoElements(element, clone, styleMap, styleCache, compress);
|
|
487
|
+
await inlinePseudoElements(element, clone, styleMap, styleCache, compress, embedFonts);
|
|
484
488
|
} catch (e) {
|
|
485
489
|
console.warn("inlinePseudoElements failed:", e);
|
|
486
490
|
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
const
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
491
|
+
let classCSS = "";
|
|
492
|
+
if (compress) {
|
|
493
|
+
const keyToClass = generateCSSClasses(styleMap);
|
|
494
|
+
classCSS = Array.from(keyToClass.entries()).map(([key, className]) => `.${className}{${key}}`).join("");
|
|
495
|
+
for (const [node, key] of styleMap.entries()) {
|
|
496
|
+
if (node.tagName === "STYLE") continue;
|
|
497
|
+
const className = keyToClass.get(key);
|
|
498
|
+
if (className) node.classList.add(className);
|
|
499
|
+
const bgImage = node.style?.backgroundImage;
|
|
500
|
+
node.removeAttribute("style");
|
|
501
|
+
if (bgImage && bgImage !== "none") node.style.backgroundImage = bgImage;
|
|
502
|
+
}
|
|
503
|
+
} else {
|
|
504
|
+
for (const [node, key] of styleMap.entries()) {
|
|
505
|
+
if (node.tagName === "STYLE") continue;
|
|
506
|
+
node.setAttribute("style", key.replace(/;/g, "; "));
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
for (const [cloneNode, originalNode] of nodeMap.entries()) {
|
|
510
|
+
const scrollX = originalNode.scrollLeft;
|
|
511
|
+
const scrollY = originalNode.scrollTop;
|
|
512
|
+
const hasScroll = scrollX || scrollY;
|
|
513
|
+
if (hasScroll && cloneNode instanceof HTMLElement) {
|
|
514
|
+
cloneNode.style.overflow = "hidden";
|
|
515
|
+
cloneNode.style.scrollbarWidth = "none";
|
|
516
|
+
cloneNode.style.msOverflowStyle = "none";
|
|
517
|
+
const inner = document.createElement("div");
|
|
518
|
+
inner.style.transform = `translate(${-scrollX}px, ${-scrollY}px)`;
|
|
519
|
+
inner.style.willChange = "transform";
|
|
520
|
+
inner.style.display = "inline-block";
|
|
521
|
+
inner.style.width = "100%";
|
|
522
|
+
while (cloneNode.firstChild) {
|
|
523
|
+
inner.appendChild(cloneNode.firstChild);
|
|
524
|
+
}
|
|
525
|
+
cloneNode.appendChild(inner);
|
|
526
|
+
}
|
|
496
527
|
}
|
|
497
528
|
return { clone, classCSS, styleCache };
|
|
498
529
|
}
|
|
@@ -563,7 +594,7 @@ async function captureDOM(element, options = {}) {
|
|
|
563
594
|
let baseCSS = "";
|
|
564
595
|
let dataURL;
|
|
565
596
|
let svgString;
|
|
566
|
-
({ clone, classCSS, styleCache } = await prepareClone(element, compress));
|
|
597
|
+
({ clone, classCSS, styleCache } = await prepareClone(element, compress, embedFonts));
|
|
567
598
|
await new Promise((resolve) => {
|
|
568
599
|
idle(async () => {
|
|
569
600
|
await inlineImages(clone);
|
|
@@ -579,7 +610,7 @@ async function captureDOM(element, options = {}) {
|
|
|
579
610
|
if (embedFonts) {
|
|
580
611
|
await new Promise((resolve) => {
|
|
581
612
|
idle(async () => {
|
|
582
|
-
fontsCSS = await embedCustomFonts({ ignoreIconFonts:
|
|
613
|
+
fontsCSS = await embedCustomFonts({ ignoreIconFonts: !embedFonts });
|
|
583
614
|
resolve();
|
|
584
615
|
}, { fast });
|
|
585
616
|
});
|
|
@@ -726,7 +757,7 @@ snapdom.capture = async (el, options = {}) => {
|
|
|
726
757
|
toImg: () => toImg(url, { dpr, scale }),
|
|
727
758
|
toCanvas: () => toCanvas(url, { dpr, scale }),
|
|
728
759
|
toBlob: () => toBlob(url),
|
|
729
|
-
toPng: () => toRasterImg(url, { dpr, scale }, "png"),
|
|
760
|
+
toPng: (options2) => toRasterImg(url, { dpr, scale, ...options2 }, "png"),
|
|
730
761
|
toJpg: (options2) => toRasterImg(url, { dpr, scale, ...options2 }, "jpeg"),
|
|
731
762
|
toWebp: (options2) => toRasterImg(url, { dpr, scale, ...options2 }, "webp"),
|
|
732
763
|
download: ({ format = "png", filename = "capture", backgroundColor } = {}) => download(url, { dpr, scale, backgroundColor, format, filename })
|
|
@@ -789,7 +820,7 @@ async function preCache(root = document, options = {}) {
|
|
|
789
820
|
}
|
|
790
821
|
}
|
|
791
822
|
if (embedFonts) {
|
|
792
|
-
await embedCustomFonts({ ignoreIconFonts:
|
|
823
|
+
await embedCustomFonts({ ignoreIconFonts: !embedFonts, preCached: true });
|
|
793
824
|
}
|
|
794
825
|
await Promise.all(promises);
|
|
795
826
|
}
|
package/package.json
CHANGED
|
@@ -1,22 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zumer/snapdom",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "snapDOM captures DOM elements as images with exceptional speed and minimal overhead. Designed to avoid bottlenecks and long tasks.",
|
|
5
5
|
"main": "dist/snapdom.js",
|
|
6
6
|
"module": "dist/snapdom.mjs",
|
|
7
|
-
"exports": {
|
|
8
|
-
".": {
|
|
9
|
-
"import": "./dist/snapdom.mjs",
|
|
10
|
-
"require": "./dist/snapdom.js"
|
|
11
|
-
}
|
|
12
|
-
},
|
|
13
7
|
"scripts": {
|
|
14
8
|
"test": "npx vitest run --browser.headless --reporter=verbose",
|
|
15
9
|
"test:benchmark": "npx vitest bench --browser.headless --watch=false",
|
|
16
|
-
"compile-browser": "npx @zumerbox/build -n snapdom -j src/index.js",
|
|
10
|
+
"compile-browser": "npx @zumerbox/build -n snapdom -j src/index.browser.js",
|
|
17
11
|
"compile-esm": "npx @zumerbox/build -n snapdom -j src/index.js -p neutral -x",
|
|
18
12
|
"compile": "npm run compile-browser && npm run compile-esm",
|
|
19
13
|
"build": "npm run compile && npm pack",
|
|
14
|
+
"bump:dry": "npx @zumerbox/bump -d",
|
|
20
15
|
"bump": "npx @zumerbox/bump && npx @zumerbox/changelog",
|
|
21
16
|
"prebuild": "git add CHANGELOG.md && git commit -m \"Bumped version\" && git push --follow-tags"
|
|
22
17
|
},
|
|
@@ -24,11 +19,19 @@
|
|
|
24
19
|
"type": "git",
|
|
25
20
|
"url": "git+https://github.com/zumerlab/snapdom.git"
|
|
26
21
|
},
|
|
22
|
+
"types": "types/snapdom.d.ts",
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"import": "./dist/snapdom.mjs",
|
|
26
|
+
"require": "./dist/snapdom.js"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
27
29
|
"files": [
|
|
28
30
|
"dist/snapdom.js",
|
|
29
31
|
"dist/snapdom.min.js",
|
|
30
32
|
"dist/snapdom.min.mjs",
|
|
31
33
|
"dist/snapdom.mjs",
|
|
34
|
+
"types/snapdom.d.ts",
|
|
32
35
|
"README.md"
|
|
33
36
|
],
|
|
34
37
|
"keywords": [
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
declare module "@zumer/snapdom" {
|
|
2
|
+
export interface SnapOptions {
|
|
3
|
+
compress?: boolean;
|
|
4
|
+
embedFonts?: boolean;
|
|
5
|
+
fast?: boolean;
|
|
6
|
+
scale?: number;
|
|
7
|
+
backgroundColor?: string;
|
|
8
|
+
format?: "png" | "jpeg" | "jpg" | "webp" | "svg";
|
|
9
|
+
filename?: string;
|
|
10
|
+
dpr?: number;
|
|
11
|
+
quality?: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface SnapResult {
|
|
15
|
+
url: string;
|
|
16
|
+
options: SnapOptions;
|
|
17
|
+
toRaw(): string;
|
|
18
|
+
toImg(): Promise<HTMLImageElement>;
|
|
19
|
+
toCanvas(): Promise<HTMLCanvasElement>;
|
|
20
|
+
toBlob(): Promise<Blob>;
|
|
21
|
+
toPng(options?: SnapOptions): Promise<HTMLImageElement>;
|
|
22
|
+
toJpg(options?: SnapOptions): Promise<HTMLImageElement>;
|
|
23
|
+
toWebp(options?: SnapOptions): Promise<HTMLImageElement>;
|
|
24
|
+
download(options?: SnapOptions): Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Captura un elemento del DOM como imagen SVG + métodos de exportación.
|
|
29
|
+
*/
|
|
30
|
+
export function snapdom(
|
|
31
|
+
element: HTMLElement,
|
|
32
|
+
options?: SnapOptions
|
|
33
|
+
): Promise<SnapResult>;
|
|
34
|
+
|
|
35
|
+
export namespace snapdom {
|
|
36
|
+
function capture(element: HTMLElement, options?: SnapOptions): Promise<SnapResult>;
|
|
37
|
+
function toRaw(element: HTMLElement, options?: SnapOptions): Promise<string>;
|
|
38
|
+
function toImg(element: HTMLElement, options?: SnapOptions): Promise<HTMLImageElement>;
|
|
39
|
+
function toCanvas(element: HTMLElement, options?: SnapOptions): Promise<HTMLCanvasElement>;
|
|
40
|
+
function toBlob(element: HTMLElement, options?: SnapOptions): Promise<Blob>;
|
|
41
|
+
function toPng(element: HTMLElement, options?: SnapOptions): Promise<HTMLImageElement>;
|
|
42
|
+
function toJpg(element: HTMLElement, options?: SnapOptions): Promise<HTMLImageElement>;
|
|
43
|
+
function toWebp(element: HTMLElement, options?: SnapOptions): Promise<HTMLImageElement>;
|
|
44
|
+
function download(element: HTMLElement, options?: SnapOptions): Promise<void>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Precarga imágenes, fondos y fuentes para acelerar capturas posteriores.
|
|
49
|
+
*/
|
|
50
|
+
export function preCache(
|
|
51
|
+
root?: Document | HTMLElement,
|
|
52
|
+
options?: {
|
|
53
|
+
embedFonts?: boolean;
|
|
54
|
+
}
|
|
55
|
+
): Promise<void>;
|
|
56
|
+
}
|