@zumer/snapdom 1.9.12-dev.1 → 1.9.12-dev.3
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/dist/snapdom.js +603 -185
- package/dist/snapdom.min.js +5 -5
- package/dist/snapdom.min.mjs +4 -4
- package/dist/snapdom.mjs +598 -181
- package/package.json +1 -1
package/dist/snapdom.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
/*
|
|
3
3
|
* snapdom
|
|
4
|
-
* v.1.9.12-dev.
|
|
4
|
+
* v.1.9.12-dev.3
|
|
5
5
|
* Author Juan Martin Muda
|
|
6
6
|
* License MIT
|
|
7
7
|
**/
|
|
@@ -581,17 +581,6 @@ function getStyle(el, pseudo = null) {
|
|
|
581
581
|
}
|
|
582
582
|
return map.get(pseudo);
|
|
583
583
|
}
|
|
584
|
-
function parseContent(content) {
|
|
585
|
-
let clean = content.replace(/^['"]|['"]$/g, "");
|
|
586
|
-
if (clean.startsWith("\\")) {
|
|
587
|
-
try {
|
|
588
|
-
return String.fromCharCode(parseInt(clean.replace("\\", ""), 16));
|
|
589
|
-
} catch {
|
|
590
|
-
return clean;
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
return clean;
|
|
594
|
-
}
|
|
595
584
|
function snapshotComputedStyle(style) {
|
|
596
585
|
const snap = {};
|
|
597
586
|
for (let prop of style) {
|
|
@@ -709,6 +698,7 @@ function getSnapshot(el, preStyle = null, options = {}) {
|
|
|
709
698
|
if (rec && rec.epoch === __epoch) return rec.snapshot;
|
|
710
699
|
const style = preStyle || getComputedStyle(el);
|
|
711
700
|
const snap = snapshotComputedStyleFull(style, options);
|
|
701
|
+
stripHeightForWrappers(el, style, snap);
|
|
712
702
|
snapshotCache.set(el, { epoch: __epoch, snapshot: snap });
|
|
713
703
|
return snap;
|
|
714
704
|
}
|
|
@@ -772,6 +762,49 @@ async function inlineAllStyles(source, clone, sessionOrCtx, opts) {
|
|
|
772
762
|
}
|
|
773
763
|
session.styleMap.set(clone, key);
|
|
774
764
|
}
|
|
765
|
+
function isReplaced(el) {
|
|
766
|
+
return el instanceof HTMLImageElement || el instanceof HTMLCanvasElement || el instanceof HTMLVideoElement || el instanceof HTMLIFrameElement || el instanceof SVGElement || el instanceof HTMLObjectElement || el instanceof HTMLEmbedElement;
|
|
767
|
+
}
|
|
768
|
+
function hasBox(cs) {
|
|
769
|
+
if (cs.backgroundImage && cs.backgroundImage !== "none") return true;
|
|
770
|
+
if (cs.backgroundColor && cs.backgroundColor !== "rgba(0, 0, 0, 0)" && cs.backgroundColor !== "transparent") return true;
|
|
771
|
+
if ((parseFloat(cs.borderTopWidth) || 0) > 0) return true;
|
|
772
|
+
if ((parseFloat(cs.borderBottomWidth) || 0) > 0) return true;
|
|
773
|
+
if ((parseFloat(cs.paddingTop) || 0) > 0) return true;
|
|
774
|
+
if ((parseFloat(cs.paddingBottom) || 0) > 0) return true;
|
|
775
|
+
const ob = cs.overflowBlock || cs.overflowY || "visible";
|
|
776
|
+
return ob !== "visible";
|
|
777
|
+
}
|
|
778
|
+
function isFlexOrGridItem(el) {
|
|
779
|
+
const p = el.parentElement;
|
|
780
|
+
if (!p) return false;
|
|
781
|
+
const pd = getComputedStyle(p).display || "";
|
|
782
|
+
return pd.includes("flex") || pd.includes("grid");
|
|
783
|
+
}
|
|
784
|
+
function hasFlowFast(el, cs) {
|
|
785
|
+
if (el.textContent && /\S/.test(el.textContent)) return true;
|
|
786
|
+
const f = el.firstElementChild, l = el.lastElementChild;
|
|
787
|
+
if (f && f.tagName === "BR" || l && l.tagName === "BR") return true;
|
|
788
|
+
const sh = el.scrollHeight;
|
|
789
|
+
if (sh === 0) return false;
|
|
790
|
+
const pt = parseFloat(cs.paddingTop) || 0;
|
|
791
|
+
const pb = parseFloat(cs.paddingBottom) || 0;
|
|
792
|
+
return sh > pt + pb;
|
|
793
|
+
}
|
|
794
|
+
function stripHeightForWrappers(el, cs, snap) {
|
|
795
|
+
if (el instanceof HTMLElement && el.style && el.style.height) return;
|
|
796
|
+
const disp = cs.display || "";
|
|
797
|
+
if (disp.includes("flex") || disp.includes("grid")) return;
|
|
798
|
+
if (isReplaced(el)) return;
|
|
799
|
+
const pos = cs.position;
|
|
800
|
+
if (pos === "absolute" || pos === "fixed" || pos === "sticky") return;
|
|
801
|
+
if (cs.transform !== "none") return;
|
|
802
|
+
if (hasBox(cs)) return;
|
|
803
|
+
if (isFlexOrGridItem(el)) return;
|
|
804
|
+
if (!hasFlowFast(el, cs)) return;
|
|
805
|
+
delete snap.height;
|
|
806
|
+
delete snap["block-size"];
|
|
807
|
+
}
|
|
775
808
|
|
|
776
809
|
// src/core/clone.js
|
|
777
810
|
function idleCallback(childList, callback, fast) {
|
|
@@ -2056,10 +2089,20 @@ function buildCounterContext(root) {
|
|
|
2056
2089
|
const empty = /* @__PURE__ */ new Map();
|
|
2057
2090
|
build(rootEl, empty, empty);
|
|
2058
2091
|
return {
|
|
2092
|
+
/**
|
|
2093
|
+
* Get top value for counter name at given node.
|
|
2094
|
+
* @param {Element} node
|
|
2095
|
+
* @param {string} name
|
|
2096
|
+
*/
|
|
2059
2097
|
get(node, name) {
|
|
2060
2098
|
const s = nodeCounters.get(node)?.get(name);
|
|
2061
2099
|
return s && s.length ? s[s.length - 1] : 0;
|
|
2062
2100
|
},
|
|
2101
|
+
/**
|
|
2102
|
+
* Get full stack for counter name at given node.
|
|
2103
|
+
* @param {Element} node
|
|
2104
|
+
* @param {string} name
|
|
2105
|
+
*/
|
|
2063
2106
|
getStack(node, name) {
|
|
2064
2107
|
const s = nodeCounters.get(node)?.get(name);
|
|
2065
2108
|
return s ? s.slice() : [];
|
|
@@ -2095,8 +2138,109 @@ function resolveCountersInContent(raw, node, ctx) {
|
|
|
2095
2138
|
|
|
2096
2139
|
// src/modules/pseudo.js
|
|
2097
2140
|
var counterCtx = null;
|
|
2141
|
+
var __siblingCounters = /* @__PURE__ */ new WeakMap();
|
|
2142
|
+
function unquoteDoubleStrings2(s) {
|
|
2143
|
+
return (s || "").replace(/"([^"]*)"/g, "$1");
|
|
2144
|
+
}
|
|
2145
|
+
function collapseCssContent(raw) {
|
|
2146
|
+
if (!raw) return "";
|
|
2147
|
+
const tokens = [];
|
|
2148
|
+
const rx = /"([^"]*)"/g;
|
|
2149
|
+
let m;
|
|
2150
|
+
while (m = rx.exec(raw)) tokens.push(m[1]);
|
|
2151
|
+
if (tokens.length) return tokens.join("");
|
|
2152
|
+
return unquoteDoubleStrings2(raw);
|
|
2153
|
+
}
|
|
2154
|
+
function withSiblingOverrides(node, base) {
|
|
2155
|
+
const parent = node.parentElement;
|
|
2156
|
+
const map = parent ? __siblingCounters.get(parent) : null;
|
|
2157
|
+
if (!map) return base;
|
|
2158
|
+
return {
|
|
2159
|
+
get(n, name) {
|
|
2160
|
+
const v = base.get(n, name);
|
|
2161
|
+
const ov = map.get(name);
|
|
2162
|
+
return typeof ov === "number" ? Math.max(v, ov) : v;
|
|
2163
|
+
},
|
|
2164
|
+
getStack(n, name) {
|
|
2165
|
+
const s = base.getStack(n, name);
|
|
2166
|
+
if (!s.length) return s;
|
|
2167
|
+
const ov = map.get(name);
|
|
2168
|
+
if (typeof ov === "number") {
|
|
2169
|
+
const out = s.slice();
|
|
2170
|
+
out[out.length - 1] = Math.max(out[out.length - 1], ov);
|
|
2171
|
+
return out;
|
|
2172
|
+
}
|
|
2173
|
+
return s;
|
|
2174
|
+
}
|
|
2175
|
+
};
|
|
2176
|
+
}
|
|
2177
|
+
function deriveCounterCtxForPseudo(node, pseudoStyle, baseCtx) {
|
|
2178
|
+
const modStacks = /* @__PURE__ */ new Map();
|
|
2179
|
+
function parseListDecl(value) {
|
|
2180
|
+
const out = [];
|
|
2181
|
+
if (!value || value === "none") return out;
|
|
2182
|
+
for (const part of String(value).split(",")) {
|
|
2183
|
+
const toks = part.trim().split(/\s+/);
|
|
2184
|
+
const name = toks[0];
|
|
2185
|
+
const num = Number.isFinite(Number(toks[1])) ? Number(toks[1]) : void 0;
|
|
2186
|
+
if (name) out.push({ name, num });
|
|
2187
|
+
}
|
|
2188
|
+
return out;
|
|
2189
|
+
}
|
|
2190
|
+
const resets = parseListDecl(pseudoStyle?.counterReset);
|
|
2191
|
+
const incs = parseListDecl(pseudoStyle?.counterIncrement);
|
|
2192
|
+
function getStackDerived(name) {
|
|
2193
|
+
if (modStacks.has(name)) return modStacks.get(name).slice();
|
|
2194
|
+
let stack = baseCtx.getStack(node, name);
|
|
2195
|
+
stack = stack.length ? stack.slice() : [];
|
|
2196
|
+
const r = resets.find((x) => x.name === name);
|
|
2197
|
+
if (r) {
|
|
2198
|
+
const val = Number.isFinite(r.num) ? r.num : 0;
|
|
2199
|
+
stack = stack.length ? [...stack, val] : [val];
|
|
2200
|
+
}
|
|
2201
|
+
const inc = incs.find((x) => x.name === name);
|
|
2202
|
+
if (inc) {
|
|
2203
|
+
const by = Number.isFinite(inc.num) ? inc.num : 1;
|
|
2204
|
+
if (stack.length === 0) stack = [0];
|
|
2205
|
+
stack[stack.length - 1] += by;
|
|
2206
|
+
}
|
|
2207
|
+
modStacks.set(name, stack.slice());
|
|
2208
|
+
return stack;
|
|
2209
|
+
}
|
|
2210
|
+
return {
|
|
2211
|
+
get(_node, name) {
|
|
2212
|
+
const s = getStackDerived(name);
|
|
2213
|
+
return s.length ? s[s.length - 1] : 0;
|
|
2214
|
+
},
|
|
2215
|
+
getStack(_node, name) {
|
|
2216
|
+
return getStackDerived(name);
|
|
2217
|
+
},
|
|
2218
|
+
/** expone increments del pseudo para que el caller pueda propagar a hermanos */
|
|
2219
|
+
__incs: incs
|
|
2220
|
+
};
|
|
2221
|
+
}
|
|
2222
|
+
function resolvePseudoContentAndIncs(node, pseudo, baseCtx) {
|
|
2223
|
+
let ps;
|
|
2224
|
+
try {
|
|
2225
|
+
ps = getComputedStyle(node, pseudo);
|
|
2226
|
+
} catch {
|
|
2227
|
+
}
|
|
2228
|
+
const raw = ps?.content;
|
|
2229
|
+
if (!raw || raw === "none" || raw === "normal") return { text: "", incs: [] };
|
|
2230
|
+
const baseWithSiblings = withSiblingOverrides(node, baseCtx);
|
|
2231
|
+
const derived = deriveCounterCtxForPseudo(node, ps, baseWithSiblings);
|
|
2232
|
+
let resolved = hasCounters(raw) ? resolveCountersInContent(raw, node, derived) : raw;
|
|
2233
|
+
const text = collapseCssContent(resolved);
|
|
2234
|
+
return { text, incs: derived.__incs || [] };
|
|
2235
|
+
}
|
|
2098
2236
|
async function inlinePseudoElements(source, clone, sessionCache, options) {
|
|
2099
2237
|
if (!(source instanceof Element) || !(clone instanceof Element)) return;
|
|
2238
|
+
if (!counterCtx) {
|
|
2239
|
+
try {
|
|
2240
|
+
counterCtx = buildCounterContext(source.ownerDocument || document);
|
|
2241
|
+
} catch {
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2100
2244
|
for (const pseudo of ["::before", "::after", "::first-letter"]) {
|
|
2101
2245
|
try {
|
|
2102
2246
|
const style = getStyle(source, pseudo);
|
|
@@ -2127,12 +2271,8 @@ async function inlinePseudoElements(source, clone, sessionCache, options) {
|
|
|
2127
2271
|
clone.insertBefore(span, restNode);
|
|
2128
2272
|
continue;
|
|
2129
2273
|
}
|
|
2130
|
-
const
|
|
2131
|
-
|
|
2132
|
-
if (hasCounters(content)) {
|
|
2133
|
-
counterCtx = buildCounterContext(source.ownerDocument || document);
|
|
2134
|
-
cleanContent = resolveCountersInContent(cleanContent, source, counterCtx);
|
|
2135
|
-
}
|
|
2274
|
+
const rawContent = style.content;
|
|
2275
|
+
const { text: cleanContent, incs } = resolvePseudoContentAndIncs(source, pseudo, counterCtx);
|
|
2136
2276
|
const bg = style.backgroundImage;
|
|
2137
2277
|
const bgColor = style.backgroundColor;
|
|
2138
2278
|
const fontFamily = style.fontFamily;
|
|
@@ -2146,28 +2286,41 @@ async function inlinePseudoElements(source, clone, sessionCache, options) {
|
|
|
2146
2286
|
const borderWidth = parseFloat(style.borderWidth);
|
|
2147
2287
|
const transform = style.transform;
|
|
2148
2288
|
const isIconFont22 = isIconFont2(fontFamily);
|
|
2149
|
-
const hasExplicitContent =
|
|
2289
|
+
const hasExplicitContent = rawContent !== "none" && cleanContent !== "";
|
|
2150
2290
|
const hasBg = bg && bg !== "none";
|
|
2151
2291
|
const hasBgColor = bgColor && bgColor !== "transparent" && bgColor !== "rgba(0, 0, 0, 0)";
|
|
2152
|
-
const
|
|
2292
|
+
const hasBox2 = display !== "inline" && (width > 0 || height > 0);
|
|
2153
2293
|
const hasBorder = borderStyle && borderStyle !== "none" && borderWidth > 0;
|
|
2154
2294
|
const hasTransform = transform && transform !== "none";
|
|
2155
|
-
const shouldRender = hasExplicitContent || hasBg || hasBgColor ||
|
|
2156
|
-
if (!shouldRender)
|
|
2295
|
+
const shouldRender = hasExplicitContent || hasBg || hasBgColor || hasBox2 || hasBorder || hasTransform;
|
|
2296
|
+
if (!shouldRender) {
|
|
2297
|
+
if (incs && incs.length && source.parentElement) {
|
|
2298
|
+
const map = __siblingCounters.get(source.parentElement) || /* @__PURE__ */ new Map();
|
|
2299
|
+
for (const { name, num } of incs) {
|
|
2300
|
+
if (!name) continue;
|
|
2301
|
+
const baseWithSibs = withSiblingOverrides(source, counterCtx);
|
|
2302
|
+
const derived = deriveCounterCtxForPseudo(source, getComputedStyle(source, pseudo), baseWithSibs);
|
|
2303
|
+
const finalVal = derived.get(source, name);
|
|
2304
|
+
map.set(name, finalVal);
|
|
2305
|
+
}
|
|
2306
|
+
__siblingCounters.set(source.parentElement, map);
|
|
2307
|
+
}
|
|
2308
|
+
continue;
|
|
2309
|
+
}
|
|
2157
2310
|
const pseudoEl = document.createElement("span");
|
|
2158
2311
|
pseudoEl.dataset.snapdomPseudo = pseudo;
|
|
2159
2312
|
pseudoEl.style.verticalAlign = "middle";
|
|
2160
2313
|
const snapshot = snapshotComputedStyle(style);
|
|
2161
2314
|
const key = getStyleKey(snapshot, "span");
|
|
2162
2315
|
sessionCache.styleMap.set(pseudoEl, key);
|
|
2163
|
-
if (isIconFont22 && cleanContent.length === 1) {
|
|
2164
|
-
const { dataUrl, width:
|
|
2316
|
+
if (isIconFont22 && cleanContent && cleanContent.length === 1) {
|
|
2317
|
+
const { dataUrl, width: w, height: h } = await iconToImage(cleanContent, fontFamily, fontWeight, fontSize, color);
|
|
2165
2318
|
const imgEl = document.createElement("img");
|
|
2166
2319
|
imgEl.src = dataUrl;
|
|
2167
|
-
imgEl.style = `height:${fontSize}px;width:${
|
|
2320
|
+
imgEl.style = `height:${fontSize}px;width:${w / h * fontSize}px;object-fit:contain;`;
|
|
2168
2321
|
pseudoEl.appendChild(imgEl);
|
|
2169
2322
|
clone.dataset.snapdomHasIcon = "true";
|
|
2170
|
-
} else if (cleanContent.startsWith("url(")) {
|
|
2323
|
+
} else if (cleanContent && cleanContent.startsWith("url(")) {
|
|
2171
2324
|
const rawUrl = extractURL(cleanContent);
|
|
2172
2325
|
if (rawUrl?.trim()) {
|
|
2173
2326
|
try {
|
|
@@ -2194,7 +2347,18 @@ async function inlinePseudoElements(source, clone, sessionCache, options) {
|
|
|
2194
2347
|
}
|
|
2195
2348
|
if (hasBgColor) pseudoEl.style.backgroundColor = bgColor;
|
|
2196
2349
|
const hasContent2 = pseudoEl.childNodes.length > 0 || pseudoEl.textContent?.trim() !== "";
|
|
2197
|
-
const hasVisibleBox = hasContent2 || hasBg || hasBgColor ||
|
|
2350
|
+
const hasVisibleBox = hasContent2 || hasBg || hasBgColor || hasBox2 || hasBorder || hasTransform;
|
|
2351
|
+
if (incs && incs.length && source.parentElement) {
|
|
2352
|
+
const map = __siblingCounters.get(source.parentElement) || /* @__PURE__ */ new Map();
|
|
2353
|
+
const baseWithSibs = withSiblingOverrides(source, counterCtx);
|
|
2354
|
+
const derived = deriveCounterCtxForPseudo(source, getComputedStyle(source, pseudo), baseWithSibs);
|
|
2355
|
+
for (const { name } of incs) {
|
|
2356
|
+
if (!name) continue;
|
|
2357
|
+
const finalVal = derived.get(source, name);
|
|
2358
|
+
map.set(name, finalVal);
|
|
2359
|
+
}
|
|
2360
|
+
__siblingCounters.set(source.parentElement, map);
|
|
2361
|
+
}
|
|
2198
2362
|
if (!hasVisibleBox) continue;
|
|
2199
2363
|
if (pseudo === "::before") {
|
|
2200
2364
|
clone.insertBefore(pseudoEl, clone.firstChild);
|
|
@@ -2341,12 +2505,13 @@ async function prepareClone(element, options = {}) {
|
|
|
2341
2505
|
sessionCache.styleCache.set(element, computed);
|
|
2342
2506
|
const transform = stripTranslate(computed.transform);
|
|
2343
2507
|
clone.style.margin = "0";
|
|
2344
|
-
clone.style.position = "static";
|
|
2345
2508
|
clone.style.top = "auto";
|
|
2346
2509
|
clone.style.left = "auto";
|
|
2347
2510
|
clone.style.right = "auto";
|
|
2348
2511
|
clone.style.bottom = "auto";
|
|
2349
|
-
clone.style.
|
|
2512
|
+
clone.style.animation = "none";
|
|
2513
|
+
clone.style.transition = "none";
|
|
2514
|
+
clone.style.willChange = "auto";
|
|
2350
2515
|
clone.style.float = "none";
|
|
2351
2516
|
clone.style.clear = "none";
|
|
2352
2517
|
clone.style.transform = transform || "";
|
|
@@ -2742,7 +2907,7 @@ async function captureDOM(element, options) {
|
|
|
2742
2907
|
idle(async () => {
|
|
2743
2908
|
const required = collectUsedFontVariants(element);
|
|
2744
2909
|
const usedCodepoints = collectUsedCodepoints(element);
|
|
2745
|
-
if (isSafari) {
|
|
2910
|
+
if (isSafari()) {
|
|
2746
2911
|
const families = new Set(
|
|
2747
2912
|
Array.from(required).map((k) => String(k).split("__")[0]).filter(Boolean)
|
|
2748
2913
|
);
|
|
@@ -2774,101 +2939,186 @@ async function captureDOM(element, options) {
|
|
|
2774
2939
|
}
|
|
2775
2940
|
await new Promise((resolve) => {
|
|
2776
2941
|
idle(() => {
|
|
2777
|
-
const
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2942
|
+
const csEl = getComputedStyle(element);
|
|
2943
|
+
function parseFilterDropShadows(cs) {
|
|
2944
|
+
const raw = `${cs.filter || ""} ${cs.webkitFilter || ""}`.trim();
|
|
2945
|
+
if (!raw || raw === "none") {
|
|
2946
|
+
return { bleed: { top: 0, right: 0, bottom: 0, left: 0 }, has: false };
|
|
2947
|
+
}
|
|
2948
|
+
const tokens = raw.match(/drop-shadow\((?:[^()]|\([^()]*\))*\)/gi) || [];
|
|
2949
|
+
let t = 0, r = 0, b = 0, l = 0;
|
|
2950
|
+
let found = false;
|
|
2951
|
+
for (const tok of tokens) {
|
|
2952
|
+
found = true;
|
|
2953
|
+
const nums = tok.match(/-?\d+(?:\.\d+)?px/gi)?.map((v) => parseFloat(v)) || [];
|
|
2954
|
+
const [ox = 0, oy = 0, blur = 0] = nums;
|
|
2955
|
+
const extX = Math.abs(ox) + blur;
|
|
2956
|
+
const extY = Math.abs(oy) + blur;
|
|
2957
|
+
r = Math.max(r, extX + Math.max(ox, 0));
|
|
2958
|
+
l = Math.max(l, extX + Math.max(-ox, 0));
|
|
2959
|
+
b = Math.max(b, extY + Math.max(oy, 0));
|
|
2960
|
+
t = Math.max(t, extY + Math.max(-oy, 0));
|
|
2795
2961
|
}
|
|
2962
|
+
return {
|
|
2963
|
+
bleed: { top: Math.ceil(t), right: Math.ceil(r), bottom: Math.ceil(b), left: Math.ceil(l) },
|
|
2964
|
+
has: found
|
|
2965
|
+
};
|
|
2796
2966
|
}
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2967
|
+
function parseBoxShadow(cs) {
|
|
2968
|
+
const v = cs.boxShadow || "";
|
|
2969
|
+
if (!v || v === "none") return { top: 0, right: 0, bottom: 0, left: 0 };
|
|
2970
|
+
const parts = v.split(/\),(?=(?:[^()]*\([^()]*\))*[^()]*$)/).map((s) => s.trim());
|
|
2971
|
+
let t = 0, r = 0, b2 = 0, l = 0;
|
|
2972
|
+
for (const part of parts) {
|
|
2973
|
+
const nums = part.match(/-?\d+(\.\d+)?px/g)?.map((n) => parseFloat(n)) || [];
|
|
2974
|
+
if (nums.length < 2) continue;
|
|
2975
|
+
const [ox2, oy2, blur = 0, spread = 0] = nums;
|
|
2976
|
+
const extX = Math.abs(ox2) + blur + spread;
|
|
2977
|
+
const extY = Math.abs(oy2) + blur + spread;
|
|
2978
|
+
r = Math.max(r, extX + Math.max(ox2, 0));
|
|
2979
|
+
l = Math.max(l, extX + Math.max(-ox2, 0));
|
|
2980
|
+
b2 = Math.max(b2, extY + Math.max(oy2, 0));
|
|
2981
|
+
t = Math.max(t, extY + Math.max(-oy2, 0));
|
|
2982
|
+
}
|
|
2983
|
+
return { top: Math.ceil(t), right: Math.ceil(r), bottom: Math.ceil(b2), left: Math.ceil(l) };
|
|
2984
|
+
}
|
|
2985
|
+
function parseFilterBlur(cs) {
|
|
2986
|
+
const m = (cs.filter || "").match(/blur\(\s*([0-9.]+)px\s*\)/);
|
|
2987
|
+
const b2 = m ? Math.ceil(parseFloat(m[1]) || 0) : 0;
|
|
2988
|
+
return { top: b2, right: b2, bottom: b2, left: b2 };
|
|
2989
|
+
}
|
|
2990
|
+
function parseOutline(cs) {
|
|
2991
|
+
if ((cs.outlineStyle || "none") === "none") return { top: 0, right: 0, bottom: 0, left: 0 };
|
|
2992
|
+
const w2 = Math.ceil(parseFloat(cs.outlineWidth || "0") || 0);
|
|
2993
|
+
return { top: w2, right: w2, bottom: w2, left: w2 };
|
|
2994
|
+
}
|
|
2995
|
+
function bboxWithOriginFull(w2, h2, M, ox2, oy2) {
|
|
2996
|
+
const a2 = M.a, b2 = M.b, c2 = M.c, d2 = M.d, e2 = M.e || 0, f2 = M.f || 0;
|
|
2997
|
+
function pt(x, y) {
|
|
2998
|
+
let X = x - ox2, Y = y - oy2;
|
|
2999
|
+
let X2 = a2 * X + c2 * Y, Y2 = b2 * X + d2 * Y;
|
|
3000
|
+
X2 += ox2 + e2;
|
|
3001
|
+
Y2 += oy2 + f2;
|
|
3002
|
+
return [X2, Y2];
|
|
3003
|
+
}
|
|
3004
|
+
const P = [pt(0, 0), pt(w2, 0), pt(0, h2), pt(w2, h2)];
|
|
3005
|
+
let minX2 = Infinity, minY2 = Infinity, maxX2 = -Infinity, maxY2 = -Infinity;
|
|
3006
|
+
for (const [X, Y] of P) {
|
|
3007
|
+
if (X < minX2) minX2 = X;
|
|
3008
|
+
if (Y < minY2) minY2 = Y;
|
|
3009
|
+
if (X > maxX2) maxX2 = X;
|
|
3010
|
+
if (Y > maxY2) maxY2 = Y;
|
|
3011
|
+
}
|
|
3012
|
+
return { minX: minX2, minY: minY2, maxX: maxX2, maxY: maxY2, width: maxX2 - minX2, height: maxY2 - minY2 };
|
|
3013
|
+
}
|
|
3014
|
+
function hasTFBBox(el) {
|
|
3015
|
+
return hasBBoxAffectingTransform(el);
|
|
3016
|
+
}
|
|
3017
|
+
const rect = element.getBoundingClientRect();
|
|
3018
|
+
const w0 = Math.max(1, Math.ceil(element.offsetWidth || parseFloat(csEl.width) || rect.width || 1));
|
|
3019
|
+
const h0 = Math.max(1, Math.ceil(element.offsetHeight || parseFloat(csEl.height) || rect.height || 1));
|
|
3020
|
+
const coerceNum = (v, def = NaN) => {
|
|
3021
|
+
const n = typeof v === "string" ? parseFloat(v) : v;
|
|
3022
|
+
return Number.isFinite(n) ? n : def;
|
|
3023
|
+
};
|
|
3024
|
+
const optW = coerceNum(options.width);
|
|
3025
|
+
const optH = coerceNum(options.height);
|
|
3026
|
+
let w = w0, h = h0;
|
|
3027
|
+
const hasW = Number.isFinite(optW);
|
|
3028
|
+
const hasH = Number.isFinite(optH);
|
|
3029
|
+
const aspect0 = h0 > 0 ? w0 / h0 : 1;
|
|
3030
|
+
if (hasW && hasH) {
|
|
3031
|
+
w = Math.max(1, Math.ceil(optW));
|
|
3032
|
+
h = Math.max(1, Math.ceil(optH));
|
|
3033
|
+
} else if (hasW) {
|
|
3034
|
+
w = Math.max(1, Math.ceil(optW));
|
|
3035
|
+
h = Math.max(1, Math.ceil(w / (aspect0 || 1)));
|
|
3036
|
+
} else if (hasH) {
|
|
3037
|
+
h = Math.max(1, Math.ceil(optH));
|
|
3038
|
+
w = Math.max(1, Math.ceil(h * (aspect0 || 1)));
|
|
2810
3039
|
} else {
|
|
3040
|
+
w = w0;
|
|
3041
|
+
h = h0;
|
|
3042
|
+
}
|
|
3043
|
+
let minX = 0, minY = 0, maxX = w0, maxY = h0;
|
|
3044
|
+
if (hasTFBBox(element)) {
|
|
3045
|
+
const baseTransform2 = csEl.transform && csEl.transform !== "none" ? csEl.transform : "";
|
|
3046
|
+
const ind2 = readIndividualTransforms(element);
|
|
2811
3047
|
const TOTAL = readTotalTransformMatrix({
|
|
2812
|
-
baseTransform,
|
|
2813
|
-
rotate:
|
|
2814
|
-
scale:
|
|
2815
|
-
translate:
|
|
3048
|
+
baseTransform: baseTransform2,
|
|
3049
|
+
rotate: ind2.rotate || "0deg",
|
|
3050
|
+
scale: ind2.scale,
|
|
3051
|
+
translate: ind2.translate
|
|
2816
3052
|
});
|
|
2817
|
-
const
|
|
2818
|
-
const
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
3053
|
+
const { ox: ox2, oy: oy2 } = parseTransformOriginPx(csEl, w0, h0);
|
|
3054
|
+
const M = TOTAL.is2D ? TOTAL : new DOMMatrix(TOTAL.toString());
|
|
3055
|
+
const bb = bboxWithOriginFull(w0, h0, M, ox2, oy2);
|
|
3056
|
+
minX = bb.minX;
|
|
3057
|
+
minY = bb.minY;
|
|
3058
|
+
maxX = bb.maxX;
|
|
3059
|
+
maxY = bb.maxY;
|
|
3060
|
+
}
|
|
3061
|
+
const bleedShadow = parseBoxShadow(csEl);
|
|
3062
|
+
const bleedBlur = parseFilterBlur(csEl);
|
|
3063
|
+
const bleedOutline = parseOutline(csEl);
|
|
3064
|
+
const drop = parseFilterDropShadows(csEl);
|
|
3065
|
+
const bleed = {
|
|
3066
|
+
top: bleedShadow.top + bleedBlur.top + bleedOutline.top + drop.bleed.top,
|
|
3067
|
+
right: bleedShadow.right + bleedBlur.right + bleedOutline.right + drop.bleed.right,
|
|
3068
|
+
bottom: bleedShadow.bottom + bleedBlur.bottom + bleedOutline.bottom + drop.bleed.bottom,
|
|
3069
|
+
left: bleedShadow.left + bleedBlur.left + bleedOutline.left + drop.bleed.left
|
|
3070
|
+
};
|
|
3071
|
+
minX -= bleed.left;
|
|
3072
|
+
minY -= bleed.top;
|
|
3073
|
+
maxX += bleed.right;
|
|
3074
|
+
maxY += bleed.bottom;
|
|
3075
|
+
const vbW0 = Math.max(1, Math.ceil(maxX - minX));
|
|
3076
|
+
const vbH0 = Math.max(1, Math.ceil(maxY - minY));
|
|
3077
|
+
const outW = Math.max(1, Math.round(vbW0 * (hasW || hasH ? w / w0 : 1)));
|
|
3078
|
+
const outH = Math.max(1, Math.round(vbH0 * (hasH || hasW ? h / h0 : 1)));
|
|
2828
3079
|
const svgNS = "http://www.w3.org/2000/svg";
|
|
3080
|
+
const pad = isSafari() ? 1 : 0;
|
|
2829
3081
|
const fo = document.createElementNS(svgNS, "foreignObject");
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
fo.setAttribute("x", String(
|
|
2833
|
-
fo.setAttribute("y", String(
|
|
3082
|
+
const vbMinX = Math.floor(minX);
|
|
3083
|
+
const vbMinY = Math.floor(minY);
|
|
3084
|
+
fo.setAttribute("x", String(-(vbMinX - pad)));
|
|
3085
|
+
fo.setAttribute("y", String(-(vbMinY - pad)));
|
|
3086
|
+
fo.setAttribute("width", String(Math.ceil(w0 + pad * 2)));
|
|
3087
|
+
fo.setAttribute("height", String(Math.ceil(h0 + pad * 2)));
|
|
2834
3088
|
fo.style.overflow = "visible";
|
|
2835
3089
|
const styleTag = document.createElement("style");
|
|
2836
3090
|
styleTag.textContent = baseCSS + fontsCSS + "svg{overflow:visible;} foreignObject{overflow:visible;}" + classCSS;
|
|
2837
3091
|
fo.appendChild(styleTag);
|
|
2838
3092
|
const container = document.createElement("div");
|
|
2839
3093
|
container.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
|
|
2840
|
-
container.style.width = `${
|
|
2841
|
-
container.style.height = `${
|
|
3094
|
+
container.style.width = `${w0}px`;
|
|
3095
|
+
container.style.height = `${h0}px`;
|
|
2842
3096
|
container.style.overflow = "visible";
|
|
2843
|
-
container.style.transformOrigin = "0 0";
|
|
2844
|
-
const cancels = [];
|
|
2845
|
-
if (cancelTranslateX || cancelTranslateY) {
|
|
2846
|
-
cancels.push(`translate(${cancelTranslateX}px, ${cancelTranslateY}px)`);
|
|
2847
|
-
}
|
|
2848
|
-
if (!hasScale && (hasW || hasH)) {
|
|
2849
|
-
const sxWH = rect.width ? w / rect.width : 1;
|
|
2850
|
-
const syWH = rect.height ? h / rect.height : 1;
|
|
2851
|
-
cancels.push(`scale(${sxWH}, ${syWH})`);
|
|
2852
|
-
} else if (hasScale) {
|
|
2853
|
-
cancels.push(`scale(${scaleOpt})`);
|
|
2854
|
-
}
|
|
2855
|
-
if (cancels.length) container.style.transform = cancels.join(" ");
|
|
2856
3097
|
clone.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
|
|
2857
|
-
clone.style.transformOrigin = "0 0";
|
|
2858
|
-
clone.style.position = "static";
|
|
2859
|
-
clone.style.left = "0";
|
|
2860
|
-
clone.style.top = "0";
|
|
2861
|
-
if (baseTransform) clone.style.transform = baseTransform;
|
|
2862
|
-
clone.style.rotate = ind.rotate || "0deg";
|
|
2863
|
-
clone.style.scale = ind.scale || "1";
|
|
2864
|
-
clone.style.translate = ind.translate || "0 0";
|
|
2865
3098
|
container.appendChild(clone);
|
|
2866
3099
|
fo.appendChild(container);
|
|
2867
3100
|
const serializer = new XMLSerializer();
|
|
2868
3101
|
const foString = serializer.serializeToString(fo);
|
|
2869
|
-
const
|
|
2870
|
-
const
|
|
2871
|
-
const
|
|
3102
|
+
const vbW = vbW0 + pad * 2;
|
|
3103
|
+
const vbH = vbH0 + pad * 2;
|
|
3104
|
+
const wantsSize = hasW || hasH;
|
|
3105
|
+
options.meta = {
|
|
3106
|
+
w0,
|
|
3107
|
+
// ancho natural del elemento
|
|
3108
|
+
h0,
|
|
3109
|
+
// alto natural
|
|
3110
|
+
vbW,
|
|
3111
|
+
// ancho del viewBox
|
|
3112
|
+
vbH,
|
|
3113
|
+
// alto del viewBox
|
|
3114
|
+
targetW: w,
|
|
3115
|
+
// ancho deseado según options.width
|
|
3116
|
+
targetH: h
|
|
3117
|
+
// alto deseado según options.height
|
|
3118
|
+
};
|
|
3119
|
+
const svgOutW = isSafari() && wantsSize ? vbW : outW + pad * 2;
|
|
3120
|
+
const svgOutH = isSafari() && wantsSize ? vbH : outH + pad * 2;
|
|
3121
|
+
const svgHeader = `<svg xmlns="${svgNS}" width="${svgOutW}" height="${svgOutH}" viewBox="0 0 ${vbW} ${vbH}">`;
|
|
2872
3122
|
const svgFooter = "</svg>";
|
|
2873
3123
|
svgString = svgHeader + foString + svgFooter;
|
|
2874
3124
|
dataURL = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgString)}`;
|
|
@@ -2888,26 +3138,6 @@ function matrixFromComputed(el) {
|
|
|
2888
3138
|
return new WebKitCSSMatrix(tr);
|
|
2889
3139
|
}
|
|
2890
3140
|
}
|
|
2891
|
-
function matrix2DNoTranslate(M) {
|
|
2892
|
-
return new DOMMatrix([M.a, M.b, M.c, M.d, 0, 0]);
|
|
2893
|
-
}
|
|
2894
|
-
function bboxFromMatrix(w, h, M2D) {
|
|
2895
|
-
const pts = [
|
|
2896
|
-
{ x: 0, y: 0 },
|
|
2897
|
-
{ x: w, y: 0 },
|
|
2898
|
-
{ x: 0, y: h },
|
|
2899
|
-
{ x: w, y: h }
|
|
2900
|
-
];
|
|
2901
|
-
const t = pts.map((p) => ({
|
|
2902
|
-
x: M2D.a * p.x + M2D.c * p.y,
|
|
2903
|
-
y: M2D.b * p.x + M2D.d * p.y
|
|
2904
|
-
}));
|
|
2905
|
-
const minX = Math.min(...t.map((p) => p.x));
|
|
2906
|
-
const maxX = Math.max(...t.map((p) => p.x));
|
|
2907
|
-
const minY = Math.min(...t.map((p) => p.y));
|
|
2908
|
-
const maxY = Math.max(...t.map((p) => p.y));
|
|
2909
|
-
return { minX, minY, width: maxX - minX, height: maxY - minY };
|
|
2910
|
-
}
|
|
2911
3141
|
function readIndividualTransforms(el) {
|
|
2912
3142
|
const out = { rotate: "0deg", scale: null, translate: null };
|
|
2913
3143
|
const map = typeof el.computedStyleMap === "function" ? el.computedStyleMap() : null;
|
|
@@ -2923,26 +3153,37 @@ function readIndividualTransforms(el) {
|
|
|
2923
3153
|
};
|
|
2924
3154
|
const rot = safeGet("rotate");
|
|
2925
3155
|
if (rot) {
|
|
2926
|
-
|
|
2927
|
-
|
|
3156
|
+
if (rot.angle) {
|
|
3157
|
+
const ang = rot.angle;
|
|
2928
3158
|
out.rotate = ang.unit === "rad" ? ang.value * 180 / Math.PI + "deg" : ang.value + ang.unit;
|
|
3159
|
+
} else if (rot.unit) {
|
|
3160
|
+
out.rotate = rot.unit === "rad" ? rot.value * 180 / Math.PI + "deg" : rot.value + rot.unit;
|
|
2929
3161
|
} else {
|
|
2930
3162
|
out.rotate = String(rot);
|
|
2931
3163
|
}
|
|
3164
|
+
} else {
|
|
3165
|
+
const cs2 = getComputedStyle(el);
|
|
3166
|
+
out.rotate = cs2.rotate && cs2.rotate !== "none" ? cs2.rotate : "0deg";
|
|
2932
3167
|
}
|
|
2933
3168
|
const sc = safeGet("scale");
|
|
2934
|
-
if (sc
|
|
2935
|
-
const sx = sc[0]?.value
|
|
2936
|
-
const sy = sc[1]?.value
|
|
3169
|
+
if (sc) {
|
|
3170
|
+
const sx = "x" in sc && sc.x?.value != null ? sc.x.value : Array.isArray(sc) ? sc[0]?.value : Number(sc) || 1;
|
|
3171
|
+
const sy = "y" in sc && sc.y?.value != null ? sc.y.value : Array.isArray(sc) ? sc[1]?.value : sx;
|
|
2937
3172
|
out.scale = `${sx} ${sy}`;
|
|
3173
|
+
} else {
|
|
3174
|
+
const cs2 = getComputedStyle(el);
|
|
3175
|
+
out.scale = cs2.scale && cs2.scale !== "none" ? cs2.scale : null;
|
|
2938
3176
|
}
|
|
2939
3177
|
const tr = safeGet("translate");
|
|
2940
|
-
if (tr
|
|
2941
|
-
const tx = tr[0]?.value
|
|
2942
|
-
const ty = tr[1]?.value
|
|
2943
|
-
const ux = tr
|
|
2944
|
-
const uy = tr
|
|
3178
|
+
if (tr) {
|
|
3179
|
+
const tx = "x" in tr && "value" in tr.x ? tr.x.value : Array.isArray(tr) ? tr[0]?.value : 0;
|
|
3180
|
+
const ty = "y" in tr && "value" in tr.y ? tr.y.value : Array.isArray(tr) ? tr[1]?.value : 0;
|
|
3181
|
+
const ux = "x" in tr && tr.x?.unit ? tr.x.unit : "px";
|
|
3182
|
+
const uy = "y" in tr && tr.y?.unit ? tr.y.unit : "px";
|
|
2945
3183
|
out.translate = `${tx}${ux} ${ty}${uy}`;
|
|
3184
|
+
} else {
|
|
3185
|
+
const cs2 = getComputedStyle(el);
|
|
3186
|
+
out.translate = cs2.translate && cs2.translate !== "none" ? cs2.translate : null;
|
|
2946
3187
|
}
|
|
2947
3188
|
return out;
|
|
2948
3189
|
}
|
|
@@ -2953,17 +3194,33 @@ function readIndividualTransforms(el) {
|
|
|
2953
3194
|
return out;
|
|
2954
3195
|
}
|
|
2955
3196
|
function hasBBoxAffectingTransform(el) {
|
|
2956
|
-
const
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
return true;
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
const
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
3197
|
+
const cs = getComputedStyle(el);
|
|
3198
|
+
const t = cs.transform || "none";
|
|
3199
|
+
const hasMatrix = t !== "none" && !/^matrix\(\s*1\s*,\s*0\s*,\s*0\s*,\s*1\s*,\s*0\s*,\s*0\s*\)$/i.test(t);
|
|
3200
|
+
if (hasMatrix) return true;
|
|
3201
|
+
const r = cs.rotate && cs.rotate !== "none" && cs.rotate !== "0deg";
|
|
3202
|
+
const s = cs.scale && cs.scale !== "none" && cs.scale !== "1";
|
|
3203
|
+
const tr = cs.translate && cs.translate !== "none" && cs.translate !== "0px 0px";
|
|
3204
|
+
return Boolean(r || s || tr);
|
|
3205
|
+
}
|
|
3206
|
+
function parseTransformOriginPx(cs, w, h) {
|
|
3207
|
+
const raw = (cs.transformOrigin || "0 0").trim().split(/\s+/);
|
|
3208
|
+
const [oxRaw, oyRaw] = [raw[0] || "0", raw[1] || "0"];
|
|
3209
|
+
const toPx = (token, size) => {
|
|
3210
|
+
const t = token.toLowerCase();
|
|
3211
|
+
if (t === "left" || t === "top") return 0;
|
|
3212
|
+
if (t === "center") return size / 2;
|
|
3213
|
+
if (t === "right") return size;
|
|
3214
|
+
if (t === "bottom") return size;
|
|
3215
|
+
if (t.endsWith("px")) return parseFloat(t) || 0;
|
|
3216
|
+
if (t.endsWith("%")) return (parseFloat(t) || 0) * size / 100;
|
|
3217
|
+
if (/^-?\d+(\.\d+)?$/.test(t)) return parseFloat(t) || 0;
|
|
3218
|
+
return 0;
|
|
3219
|
+
};
|
|
3220
|
+
return {
|
|
3221
|
+
ox: toPx(oxRaw, w),
|
|
3222
|
+
oy: toPx(oyRaw, h)
|
|
3223
|
+
};
|
|
2967
3224
|
}
|
|
2968
3225
|
var __measureHost = null;
|
|
2969
3226
|
function getMeasureHost() {
|
|
@@ -3051,40 +3308,198 @@ function createContext(options = {}) {
|
|
|
3051
3308
|
};
|
|
3052
3309
|
}
|
|
3053
3310
|
|
|
3054
|
-
// src/exporters/
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3311
|
+
// src/exporters/toCanvas.js
|
|
3312
|
+
function isSvgDataURL(u) {
|
|
3313
|
+
return typeof u === "string" && /^data:image\/svg\+xml/i.test(u);
|
|
3314
|
+
}
|
|
3315
|
+
function decodeSvgFromDataURL(u) {
|
|
3316
|
+
const i = u.indexOf(",");
|
|
3317
|
+
return i >= 0 ? decodeURIComponent(u.slice(i + 1)) : "";
|
|
3318
|
+
}
|
|
3319
|
+
function encodeSvgToDataURL(svgText) {
|
|
3320
|
+
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgText)}`;
|
|
3321
|
+
}
|
|
3322
|
+
function splitDecls(s) {
|
|
3323
|
+
let parts = [], buf = "", depth = 0;
|
|
3324
|
+
for (let i = 0; i < s.length; i++) {
|
|
3325
|
+
const ch = s[i];
|
|
3326
|
+
if (ch === "(") depth++;
|
|
3327
|
+
if (ch === ")") depth = Math.max(0, depth - 1);
|
|
3328
|
+
if (ch === ";" && depth === 0) {
|
|
3329
|
+
parts.push(buf);
|
|
3330
|
+
buf = "";
|
|
3331
|
+
} else buf += ch;
|
|
3332
|
+
}
|
|
3333
|
+
if (buf.trim()) parts.push(buf);
|
|
3334
|
+
return parts.map((x) => x.trim()).filter(Boolean);
|
|
3335
|
+
}
|
|
3336
|
+
function boxShadowToDropShadow(value) {
|
|
3337
|
+
const layers = [];
|
|
3338
|
+
let buf = "", depth = 0;
|
|
3339
|
+
for (let i = 0; i < value.length; i++) {
|
|
3340
|
+
const ch = value[i];
|
|
3341
|
+
if (ch === "(") depth++;
|
|
3342
|
+
if (ch === ")") depth = Math.max(0, depth - 1);
|
|
3343
|
+
if (ch === "," && depth === 0) {
|
|
3344
|
+
layers.push(buf.trim());
|
|
3345
|
+
buf = "";
|
|
3346
|
+
} else buf += ch;
|
|
3347
|
+
}
|
|
3348
|
+
if (buf.trim()) layers.push(buf.trim());
|
|
3349
|
+
const fns = [];
|
|
3350
|
+
for (const layer of layers) {
|
|
3351
|
+
if (/\binset\b/i.test(layer)) continue;
|
|
3352
|
+
const nums = layer.match(/-?\d+(?:\.\d+)?px/gi) || [];
|
|
3353
|
+
const [ox = "0px", oy = "0px", blur = "0px"] = nums;
|
|
3354
|
+
let color = layer.replace(/-?\d+(?:\.\d+)?px/gi, "").replace(/\binset\b/ig, "").trim().replace(/\s{2,}/g, " ");
|
|
3355
|
+
const hasColor = !!color && color !== ",";
|
|
3356
|
+
fns.push(`drop-shadow(${ox} ${oy} ${blur}${hasColor ? ` ${color}` : ""})`);
|
|
3357
|
+
}
|
|
3358
|
+
return fns.join(" ");
|
|
3359
|
+
}
|
|
3360
|
+
function rewriteDeclList(list) {
|
|
3361
|
+
const decls = splitDecls(list);
|
|
3362
|
+
let filter = null, wfilter = null, box = null;
|
|
3363
|
+
const rest = [];
|
|
3364
|
+
for (const d of decls) {
|
|
3365
|
+
const idx = d.indexOf(":");
|
|
3366
|
+
if (idx < 0) continue;
|
|
3367
|
+
const prop = d.slice(0, idx).trim().toLowerCase();
|
|
3368
|
+
const val = d.slice(idx + 1).trim();
|
|
3369
|
+
if (prop === "box-shadow") box = val;
|
|
3370
|
+
else if (prop === "filter") filter = val;
|
|
3371
|
+
else if (prop === "-webkit-filter") wfilter = val;
|
|
3372
|
+
else rest.push([prop, val]);
|
|
3373
|
+
}
|
|
3374
|
+
if (box) {
|
|
3375
|
+
const ds = boxShadowToDropShadow(box);
|
|
3376
|
+
if (ds) {
|
|
3377
|
+
filter = filter ? `${filter} ${ds}` : ds;
|
|
3378
|
+
wfilter = wfilter ? `${wfilter} ${ds}` : ds;
|
|
3379
|
+
}
|
|
3380
|
+
}
|
|
3381
|
+
const out = [...rest];
|
|
3382
|
+
if (filter) out.push(["filter", filter]);
|
|
3383
|
+
if (wfilter) out.push(["-webkit-filter", wfilter]);
|
|
3384
|
+
return out.map(([k, v]) => `${k}:${v}`).join(";");
|
|
3385
|
+
}
|
|
3386
|
+
function rewriteCssBlock(css) {
|
|
3387
|
+
return css.replace(/([^{}]+)\{([^}]*)\}/g, (_m, sel, body) => `${sel}{${rewriteDeclList(body)}}`);
|
|
3388
|
+
}
|
|
3389
|
+
function rewriteSvgBoxShadowToDropShadow(svgText) {
|
|
3390
|
+
svgText = svgText.replace(
|
|
3391
|
+
/<style[^>]*>([\s\S]*?)<\/style>/gi,
|
|
3392
|
+
(m, css) => m.replace(css, rewriteCssBlock(css))
|
|
3393
|
+
);
|
|
3394
|
+
svgText = svgText.replace(
|
|
3395
|
+
/style=(['"])([\s\S]*?)\1/gi,
|
|
3396
|
+
(m, q, body) => `style=${q}${rewriteDeclList(body)}${q}`
|
|
3397
|
+
);
|
|
3398
|
+
return svgText;
|
|
3399
|
+
}
|
|
3400
|
+
function maybeConvertBoxShadowForSafari(url) {
|
|
3401
|
+
if (!isSafari() || !isSvgDataURL(url)) return url;
|
|
3402
|
+
try {
|
|
3403
|
+
const svg = decodeSvgFromDataURL(url);
|
|
3404
|
+
const fixed = rewriteSvgBoxShadowToDropShadow(svg);
|
|
3405
|
+
return encodeSvgToDataURL(fixed);
|
|
3406
|
+
} catch {
|
|
3407
|
+
return url;
|
|
3062
3408
|
}
|
|
3063
|
-
return img;
|
|
3064
3409
|
}
|
|
3065
|
-
|
|
3066
|
-
// src/exporters/toCanvas.js
|
|
3067
3410
|
async function toCanvas(url, options) {
|
|
3068
|
-
|
|
3411
|
+
let { width: optW, height: optH, scale = 1, dpr = 1, meta = {} } = options;
|
|
3412
|
+
url = maybeConvertBoxShadowForSafari(url);
|
|
3069
3413
|
const img = new Image();
|
|
3070
|
-
img.crossOrigin = "anonymous";
|
|
3071
3414
|
img.loading = "eager";
|
|
3072
3415
|
img.decoding = "sync";
|
|
3416
|
+
img.crossOrigin = "anonymous";
|
|
3073
3417
|
img.src = url;
|
|
3074
3418
|
await img.decode();
|
|
3075
|
-
const
|
|
3076
|
-
const
|
|
3419
|
+
const natW = img.naturalWidth;
|
|
3420
|
+
const natH = img.naturalHeight;
|
|
3421
|
+
const refW = Number.isFinite(meta.w0) ? meta.w0 : natW;
|
|
3422
|
+
const refH = Number.isFinite(meta.h0) ? meta.h0 : natH;
|
|
3423
|
+
let outW, outH;
|
|
3424
|
+
const hasW = Number.isFinite(optW);
|
|
3425
|
+
const hasH = Number.isFinite(optH);
|
|
3426
|
+
if (hasW && hasH) {
|
|
3427
|
+
outW = Math.max(1, optW);
|
|
3428
|
+
outH = Math.max(1, optH);
|
|
3429
|
+
} else if (hasW) {
|
|
3430
|
+
const k = optW / Math.max(1, refW);
|
|
3431
|
+
outW = optW;
|
|
3432
|
+
outH = Math.round(refH * k);
|
|
3433
|
+
} else if (hasH) {
|
|
3434
|
+
const k = optH / Math.max(1, refH);
|
|
3435
|
+
outH = optH;
|
|
3436
|
+
outW = Math.round(refW * k);
|
|
3437
|
+
} else {
|
|
3438
|
+
outW = natW;
|
|
3439
|
+
outH = natH;
|
|
3440
|
+
}
|
|
3441
|
+
outW = Math.round(outW * scale);
|
|
3442
|
+
outH = Math.round(outH * scale);
|
|
3077
3443
|
const canvas = document.createElement("canvas");
|
|
3078
|
-
canvas.width = Math.ceil(
|
|
3079
|
-
canvas.height = Math.ceil(
|
|
3080
|
-
canvas.style.width = `${
|
|
3081
|
-
canvas.style.height = `${
|
|
3444
|
+
canvas.width = Math.ceil(outW * dpr);
|
|
3445
|
+
canvas.height = Math.ceil(outH * dpr);
|
|
3446
|
+
canvas.style.width = `${outW}px`;
|
|
3447
|
+
canvas.style.height = `${outH}px`;
|
|
3082
3448
|
const ctx = canvas.getContext("2d");
|
|
3083
3449
|
if (dpr !== 1) ctx.scale(dpr, dpr);
|
|
3084
|
-
ctx.drawImage(img, 0, 0,
|
|
3450
|
+
ctx.drawImage(img, 0, 0, outW, outH);
|
|
3085
3451
|
return canvas;
|
|
3086
3452
|
}
|
|
3087
3453
|
|
|
3454
|
+
// src/modules/rasterize.js
|
|
3455
|
+
async function rasterize(url, options) {
|
|
3456
|
+
const canvas = await toCanvas(url, options);
|
|
3457
|
+
const finalCanvas = options.backgroundColor ? createBackground(canvas, options.backgroundColor) : canvas;
|
|
3458
|
+
const img = new Image();
|
|
3459
|
+
img.src = finalCanvas.toDataURL(`image/${options.format}`, options.quality);
|
|
3460
|
+
await img.decode();
|
|
3461
|
+
img.style.width = `${finalCanvas.width / options.dpr}px`;
|
|
3462
|
+
img.style.height = `${finalCanvas.height / options.dpr}px`;
|
|
3463
|
+
return img;
|
|
3464
|
+
}
|
|
3465
|
+
|
|
3466
|
+
// src/exporters/toImg.js
|
|
3467
|
+
async function toImg(url, options) {
|
|
3468
|
+
const { scale = 1, width, height, meta = {} } = options;
|
|
3469
|
+
const hasW = Number.isFinite(width);
|
|
3470
|
+
const hasH = Number.isFinite(height);
|
|
3471
|
+
const wantsScale = Number.isFinite(scale) && scale !== 1 || hasW || hasH;
|
|
3472
|
+
if (isSafari() && wantsScale) {
|
|
3473
|
+
const pngUrl = await rasterize(url, { ...options, format: "png", quality: 1, meta });
|
|
3474
|
+
return pngUrl;
|
|
3475
|
+
}
|
|
3476
|
+
const img = new Image();
|
|
3477
|
+
img.decoding = "sync";
|
|
3478
|
+
img.loading = "eager";
|
|
3479
|
+
img.src = url;
|
|
3480
|
+
await img.decode();
|
|
3481
|
+
if (hasW && hasH) {
|
|
3482
|
+
img.style.width = `${width}px`;
|
|
3483
|
+
img.style.height = `${height}px`;
|
|
3484
|
+
} else if (hasW) {
|
|
3485
|
+
const refW = Number.isFinite(meta.w0) ? meta.w0 : img.naturalWidth;
|
|
3486
|
+
const refH = Number.isFinite(meta.h0) ? meta.h0 : img.naturalHeight;
|
|
3487
|
+
const k = width / Math.max(1, refW);
|
|
3488
|
+
img.style.width = `${width}px`;
|
|
3489
|
+
img.style.height = `${Math.round(refH * k)}px`;
|
|
3490
|
+
} else if (hasH) {
|
|
3491
|
+
const refW = Number.isFinite(meta.w0) ? meta.w0 : img.naturalWidth;
|
|
3492
|
+
const refH = Number.isFinite(meta.h0) ? meta.h0 : img.naturalHeight;
|
|
3493
|
+
const k = height / Math.max(1, refH);
|
|
3494
|
+
img.style.height = `${height}px`;
|
|
3495
|
+
img.style.width = `${Math.round(refW * k)}px`;
|
|
3496
|
+
} else if (scale !== 1) {
|
|
3497
|
+
img.style.width = `${Math.round(img.naturalWidth * scale)}px`;
|
|
3498
|
+
img.style.height = `${Math.round(img.naturalHeight * scale)}px`;
|
|
3499
|
+
}
|
|
3500
|
+
return img;
|
|
3501
|
+
}
|
|
3502
|
+
|
|
3088
3503
|
// src/exporters/toBlob.js
|
|
3089
3504
|
async function toBlob(url, options) {
|
|
3090
3505
|
const type = options.type;
|
|
@@ -3103,18 +3518,6 @@ async function toBlob(url, options) {
|
|
|
3103
3518
|
);
|
|
3104
3519
|
}
|
|
3105
3520
|
|
|
3106
|
-
// src/modules/rasterize.js
|
|
3107
|
-
async function rasterize(url, options) {
|
|
3108
|
-
const canvas = await toCanvas(url, options);
|
|
3109
|
-
const finalCanvas = options.backgroundColor ? createBackground(canvas, options.backgroundColor) : canvas;
|
|
3110
|
-
const img = new Image();
|
|
3111
|
-
img.src = finalCanvas.toDataURL(`image/${options.format}`, options.quality);
|
|
3112
|
-
await img.decode();
|
|
3113
|
-
img.style.width = `${finalCanvas.width / options.dpr}px`;
|
|
3114
|
-
img.style.height = `${finalCanvas.height / options.dpr}px`;
|
|
3115
|
-
return img;
|
|
3116
|
-
}
|
|
3117
|
-
|
|
3118
3521
|
// src/exporters/download.js
|
|
3119
3522
|
async function download(url, options) {
|
|
3120
3523
|
options.dpr = 1;
|
|
@@ -3141,7 +3544,8 @@ var INTERNAL_TOKEN = Symbol("snapdom.internal");
|
|
|
3141
3544
|
var _safariWarmup = false;
|
|
3142
3545
|
async function snapdom(element, userOptions) {
|
|
3143
3546
|
if (!element) throw new Error("Element cannot be null or undefined");
|
|
3144
|
-
|
|
3547
|
+
const context = createContext(userOptions);
|
|
3548
|
+
if (isSafari() && (context.embedFonts === true || hasBackgroundOrMask(element))) {
|
|
3145
3549
|
for (let i = 0; i < 3; i++) {
|
|
3146
3550
|
try {
|
|
3147
3551
|
await safariWarmup(element, userOptions);
|
|
@@ -3151,7 +3555,6 @@ async function snapdom(element, userOptions) {
|
|
|
3151
3555
|
}
|
|
3152
3556
|
}
|
|
3153
3557
|
}
|
|
3154
|
-
const context = createContext(userOptions);
|
|
3155
3558
|
if (context.iconFonts && context.iconFonts.length > 0) extendIconFonts(context.iconFonts);
|
|
3156
3559
|
if (!context.snap) {
|
|
3157
3560
|
context.snap = {
|
|
@@ -3231,6 +3634,20 @@ async function safariWarmup(element, baseOptions) {
|
|
|
3231
3634
|
cleanup();
|
|
3232
3635
|
});
|
|
3233
3636
|
}
|
|
3637
|
+
function hasBackgroundOrMask(el) {
|
|
3638
|
+
const walker = document.createTreeWalker(el, NodeFilter.SHOW_ELEMENT);
|
|
3639
|
+
while (walker.nextNode()) {
|
|
3640
|
+
const node = (
|
|
3641
|
+
/** @type {Element} */
|
|
3642
|
+
walker.currentNode
|
|
3643
|
+
);
|
|
3644
|
+
const cs = getComputedStyle(node);
|
|
3645
|
+
const bg = cs.backgroundImage && cs.backgroundImage !== "none";
|
|
3646
|
+
const mask = cs.maskImage && cs.maskImage !== "none" || cs.webkitMaskImage && cs.webkitMaskImage !== "none";
|
|
3647
|
+
if (bg || mask) return true;
|
|
3648
|
+
}
|
|
3649
|
+
return false;
|
|
3650
|
+
}
|
|
3234
3651
|
|
|
3235
3652
|
// src/api/preCache.js
|
|
3236
3653
|
async function preCache(root = document, options = {}) {
|