@zumer/snapdom 1.9.12-dev.2 → 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 +556 -182
- package/dist/snapdom.min.js +5 -5
- package/dist/snapdom.min.mjs +5 -5
- package/dist/snapdom.mjs +551 -178
- 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) {
|
|
@@ -2100,10 +2089,20 @@ function buildCounterContext(root) {
|
|
|
2100
2089
|
const empty = /* @__PURE__ */ new Map();
|
|
2101
2090
|
build(rootEl, empty, empty);
|
|
2102
2091
|
return {
|
|
2092
|
+
/**
|
|
2093
|
+
* Get top value for counter name at given node.
|
|
2094
|
+
* @param {Element} node
|
|
2095
|
+
* @param {string} name
|
|
2096
|
+
*/
|
|
2103
2097
|
get(node, name) {
|
|
2104
2098
|
const s = nodeCounters.get(node)?.get(name);
|
|
2105
2099
|
return s && s.length ? s[s.length - 1] : 0;
|
|
2106
2100
|
},
|
|
2101
|
+
/**
|
|
2102
|
+
* Get full stack for counter name at given node.
|
|
2103
|
+
* @param {Element} node
|
|
2104
|
+
* @param {string} name
|
|
2105
|
+
*/
|
|
2107
2106
|
getStack(node, name) {
|
|
2108
2107
|
const s = nodeCounters.get(node)?.get(name);
|
|
2109
2108
|
return s ? s.slice() : [];
|
|
@@ -2139,8 +2138,109 @@ function resolveCountersInContent(raw, node, ctx) {
|
|
|
2139
2138
|
|
|
2140
2139
|
// src/modules/pseudo.js
|
|
2141
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
|
+
}
|
|
2142
2236
|
async function inlinePseudoElements(source, clone, sessionCache, options) {
|
|
2143
2237
|
if (!(source instanceof Element) || !(clone instanceof Element)) return;
|
|
2238
|
+
if (!counterCtx) {
|
|
2239
|
+
try {
|
|
2240
|
+
counterCtx = buildCounterContext(source.ownerDocument || document);
|
|
2241
|
+
} catch {
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2144
2244
|
for (const pseudo of ["::before", "::after", "::first-letter"]) {
|
|
2145
2245
|
try {
|
|
2146
2246
|
const style = getStyle(source, pseudo);
|
|
@@ -2171,12 +2271,8 @@ async function inlinePseudoElements(source, clone, sessionCache, options) {
|
|
|
2171
2271
|
clone.insertBefore(span, restNode);
|
|
2172
2272
|
continue;
|
|
2173
2273
|
}
|
|
2174
|
-
const
|
|
2175
|
-
|
|
2176
|
-
if (hasCounters(content)) {
|
|
2177
|
-
counterCtx = buildCounterContext(source.ownerDocument || document);
|
|
2178
|
-
cleanContent = resolveCountersInContent(cleanContent, source, counterCtx);
|
|
2179
|
-
}
|
|
2274
|
+
const rawContent = style.content;
|
|
2275
|
+
const { text: cleanContent, incs } = resolvePseudoContentAndIncs(source, pseudo, counterCtx);
|
|
2180
2276
|
const bg = style.backgroundImage;
|
|
2181
2277
|
const bgColor = style.backgroundColor;
|
|
2182
2278
|
const fontFamily = style.fontFamily;
|
|
@@ -2190,28 +2286,41 @@ async function inlinePseudoElements(source, clone, sessionCache, options) {
|
|
|
2190
2286
|
const borderWidth = parseFloat(style.borderWidth);
|
|
2191
2287
|
const transform = style.transform;
|
|
2192
2288
|
const isIconFont22 = isIconFont2(fontFamily);
|
|
2193
|
-
const hasExplicitContent =
|
|
2289
|
+
const hasExplicitContent = rawContent !== "none" && cleanContent !== "";
|
|
2194
2290
|
const hasBg = bg && bg !== "none";
|
|
2195
2291
|
const hasBgColor = bgColor && bgColor !== "transparent" && bgColor !== "rgba(0, 0, 0, 0)";
|
|
2196
2292
|
const hasBox2 = display !== "inline" && (width > 0 || height > 0);
|
|
2197
2293
|
const hasBorder = borderStyle && borderStyle !== "none" && borderWidth > 0;
|
|
2198
2294
|
const hasTransform = transform && transform !== "none";
|
|
2199
2295
|
const shouldRender = hasExplicitContent || hasBg || hasBgColor || hasBox2 || hasBorder || hasTransform;
|
|
2200
|
-
if (!shouldRender)
|
|
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
|
+
}
|
|
2201
2310
|
const pseudoEl = document.createElement("span");
|
|
2202
2311
|
pseudoEl.dataset.snapdomPseudo = pseudo;
|
|
2203
2312
|
pseudoEl.style.verticalAlign = "middle";
|
|
2204
2313
|
const snapshot = snapshotComputedStyle(style);
|
|
2205
2314
|
const key = getStyleKey(snapshot, "span");
|
|
2206
2315
|
sessionCache.styleMap.set(pseudoEl, key);
|
|
2207
|
-
if (isIconFont22 && cleanContent.length === 1) {
|
|
2208
|
-
const { dataUrl, width:
|
|
2316
|
+
if (isIconFont22 && cleanContent && cleanContent.length === 1) {
|
|
2317
|
+
const { dataUrl, width: w, height: h } = await iconToImage(cleanContent, fontFamily, fontWeight, fontSize, color);
|
|
2209
2318
|
const imgEl = document.createElement("img");
|
|
2210
2319
|
imgEl.src = dataUrl;
|
|
2211
|
-
imgEl.style = `height:${fontSize}px;width:${
|
|
2320
|
+
imgEl.style = `height:${fontSize}px;width:${w / h * fontSize}px;object-fit:contain;`;
|
|
2212
2321
|
pseudoEl.appendChild(imgEl);
|
|
2213
2322
|
clone.dataset.snapdomHasIcon = "true";
|
|
2214
|
-
} else if (cleanContent.startsWith("url(")) {
|
|
2323
|
+
} else if (cleanContent && cleanContent.startsWith("url(")) {
|
|
2215
2324
|
const rawUrl = extractURL(cleanContent);
|
|
2216
2325
|
if (rawUrl?.trim()) {
|
|
2217
2326
|
try {
|
|
@@ -2239,6 +2348,17 @@ async function inlinePseudoElements(source, clone, sessionCache, options) {
|
|
|
2239
2348
|
if (hasBgColor) pseudoEl.style.backgroundColor = bgColor;
|
|
2240
2349
|
const hasContent2 = pseudoEl.childNodes.length > 0 || pseudoEl.textContent?.trim() !== "";
|
|
2241
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
|
+
}
|
|
2242
2362
|
if (!hasVisibleBox) continue;
|
|
2243
2363
|
if (pseudo === "::before") {
|
|
2244
2364
|
clone.insertBefore(pseudoEl, clone.firstChild);
|
|
@@ -2385,12 +2505,13 @@ async function prepareClone(element, options = {}) {
|
|
|
2385
2505
|
sessionCache.styleCache.set(element, computed);
|
|
2386
2506
|
const transform = stripTranslate(computed.transform);
|
|
2387
2507
|
clone.style.margin = "0";
|
|
2388
|
-
clone.style.position = "static";
|
|
2389
2508
|
clone.style.top = "auto";
|
|
2390
2509
|
clone.style.left = "auto";
|
|
2391
2510
|
clone.style.right = "auto";
|
|
2392
2511
|
clone.style.bottom = "auto";
|
|
2393
|
-
clone.style.
|
|
2512
|
+
clone.style.animation = "none";
|
|
2513
|
+
clone.style.transition = "none";
|
|
2514
|
+
clone.style.willChange = "auto";
|
|
2394
2515
|
clone.style.float = "none";
|
|
2395
2516
|
clone.style.clear = "none";
|
|
2396
2517
|
clone.style.transform = transform || "";
|
|
@@ -2786,7 +2907,7 @@ async function captureDOM(element, options) {
|
|
|
2786
2907
|
idle(async () => {
|
|
2787
2908
|
const required = collectUsedFontVariants(element);
|
|
2788
2909
|
const usedCodepoints = collectUsedCodepoints(element);
|
|
2789
|
-
if (isSafari) {
|
|
2910
|
+
if (isSafari()) {
|
|
2790
2911
|
const families = new Set(
|
|
2791
2912
|
Array.from(required).map((k) => String(k).split("__")[0]).filter(Boolean)
|
|
2792
2913
|
);
|
|
@@ -2818,101 +2939,186 @@ async function captureDOM(element, options) {
|
|
|
2818
2939
|
}
|
|
2819
2940
|
await new Promise((resolve) => {
|
|
2820
2941
|
idle(() => {
|
|
2821
|
-
const
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
const scaleOpt = options.scale ?? 1;
|
|
2827
|
-
const hasScale = typeof scaleOpt === "number" && scaleOpt !== 1;
|
|
2828
|
-
if (!hasScale) {
|
|
2829
|
-
const aspect = h > 0 ? w / h : 1;
|
|
2830
|
-
if (hasW && hasH) {
|
|
2831
|
-
w = Number(options.width);
|
|
2832
|
-
h = Number(options.height);
|
|
2833
|
-
} else if (hasW) {
|
|
2834
|
-
w = Number(options.width);
|
|
2835
|
-
h = w / (aspect || 1);
|
|
2836
|
-
} else if (hasH) {
|
|
2837
|
-
h = Number(options.height);
|
|
2838
|
-
w = h * (aspect || 1);
|
|
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 };
|
|
2839
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));
|
|
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
|
+
};
|
|
2840
2966
|
}
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
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)));
|
|
2854
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);
|
|
2855
3047
|
const TOTAL = readTotalTransformMatrix({
|
|
2856
|
-
baseTransform,
|
|
2857
|
-
rotate:
|
|
2858
|
-
scale:
|
|
2859
|
-
translate:
|
|
3048
|
+
baseTransform: baseTransform2,
|
|
3049
|
+
rotate: ind2.rotate || "0deg",
|
|
3050
|
+
scale: ind2.scale,
|
|
3051
|
+
translate: ind2.translate
|
|
2860
3052
|
});
|
|
2861
|
-
const
|
|
2862
|
-
const
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
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)));
|
|
2872
3079
|
const svgNS = "http://www.w3.org/2000/svg";
|
|
3080
|
+
const pad = isSafari() ? 1 : 0;
|
|
2873
3081
|
const fo = document.createElementNS(svgNS, "foreignObject");
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
fo.setAttribute("x", String(
|
|
2877
|
-
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)));
|
|
2878
3088
|
fo.style.overflow = "visible";
|
|
2879
3089
|
const styleTag = document.createElement("style");
|
|
2880
3090
|
styleTag.textContent = baseCSS + fontsCSS + "svg{overflow:visible;} foreignObject{overflow:visible;}" + classCSS;
|
|
2881
3091
|
fo.appendChild(styleTag);
|
|
2882
3092
|
const container = document.createElement("div");
|
|
2883
3093
|
container.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
|
|
2884
|
-
container.style.width = `${
|
|
2885
|
-
container.style.height = `${
|
|
3094
|
+
container.style.width = `${w0}px`;
|
|
3095
|
+
container.style.height = `${h0}px`;
|
|
2886
3096
|
container.style.overflow = "visible";
|
|
2887
|
-
container.style.transformOrigin = "0 0";
|
|
2888
|
-
const cancels = [];
|
|
2889
|
-
if (cancelTranslateX || cancelTranslateY) {
|
|
2890
|
-
cancels.push(`translate(${cancelTranslateX}px, ${cancelTranslateY}px)`);
|
|
2891
|
-
}
|
|
2892
|
-
if (!hasScale && (hasW || hasH)) {
|
|
2893
|
-
const sxWH = rect.width ? w / rect.width : 1;
|
|
2894
|
-
const syWH = rect.height ? h / rect.height : 1;
|
|
2895
|
-
cancels.push(`scale(${sxWH}, ${syWH})`);
|
|
2896
|
-
} else if (hasScale) {
|
|
2897
|
-
cancels.push(`scale(${scaleOpt})`);
|
|
2898
|
-
}
|
|
2899
|
-
if (cancels.length) container.style.transform = cancels.join(" ");
|
|
2900
3097
|
clone.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
|
|
2901
|
-
clone.style.transformOrigin = "0 0";
|
|
2902
|
-
clone.style.position = "static";
|
|
2903
|
-
clone.style.left = "0";
|
|
2904
|
-
clone.style.top = "0";
|
|
2905
|
-
if (baseTransform) clone.style.transform = baseTransform;
|
|
2906
|
-
clone.style.rotate = ind.rotate || "0deg";
|
|
2907
|
-
clone.style.scale = ind.scale || "1";
|
|
2908
|
-
clone.style.translate = ind.translate || "0 0";
|
|
2909
3098
|
container.appendChild(clone);
|
|
2910
3099
|
fo.appendChild(container);
|
|
2911
3100
|
const serializer = new XMLSerializer();
|
|
2912
3101
|
const foString = serializer.serializeToString(fo);
|
|
2913
|
-
const
|
|
2914
|
-
const
|
|
2915
|
-
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}">`;
|
|
2916
3122
|
const svgFooter = "</svg>";
|
|
2917
3123
|
svgString = svgHeader + foString + svgFooter;
|
|
2918
3124
|
dataURL = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgString)}`;
|
|
@@ -2932,26 +3138,6 @@ function matrixFromComputed(el) {
|
|
|
2932
3138
|
return new WebKitCSSMatrix(tr);
|
|
2933
3139
|
}
|
|
2934
3140
|
}
|
|
2935
|
-
function matrix2DNoTranslate(M) {
|
|
2936
|
-
return new DOMMatrix([M.a, M.b, M.c, M.d, 0, 0]);
|
|
2937
|
-
}
|
|
2938
|
-
function bboxFromMatrix(w, h, M2D) {
|
|
2939
|
-
const pts = [
|
|
2940
|
-
{ x: 0, y: 0 },
|
|
2941
|
-
{ x: w, y: 0 },
|
|
2942
|
-
{ x: 0, y: h },
|
|
2943
|
-
{ x: w, y: h }
|
|
2944
|
-
];
|
|
2945
|
-
const t = pts.map((p) => ({
|
|
2946
|
-
x: M2D.a * p.x + M2D.c * p.y,
|
|
2947
|
-
y: M2D.b * p.x + M2D.d * p.y
|
|
2948
|
-
}));
|
|
2949
|
-
const minX = Math.min(...t.map((p) => p.x));
|
|
2950
|
-
const maxX = Math.max(...t.map((p) => p.x));
|
|
2951
|
-
const minY = Math.min(...t.map((p) => p.y));
|
|
2952
|
-
const maxY = Math.max(...t.map((p) => p.y));
|
|
2953
|
-
return { minX, minY, width: maxX - minX, height: maxY - minY };
|
|
2954
|
-
}
|
|
2955
3141
|
function readIndividualTransforms(el) {
|
|
2956
3142
|
const out = { rotate: "0deg", scale: null, translate: null };
|
|
2957
3143
|
const map = typeof el.computedStyleMap === "function" ? el.computedStyleMap() : null;
|
|
@@ -2967,26 +3153,37 @@ function readIndividualTransforms(el) {
|
|
|
2967
3153
|
};
|
|
2968
3154
|
const rot = safeGet("rotate");
|
|
2969
3155
|
if (rot) {
|
|
2970
|
-
|
|
2971
|
-
|
|
3156
|
+
if (rot.angle) {
|
|
3157
|
+
const ang = rot.angle;
|
|
2972
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;
|
|
2973
3161
|
} else {
|
|
2974
3162
|
out.rotate = String(rot);
|
|
2975
3163
|
}
|
|
3164
|
+
} else {
|
|
3165
|
+
const cs2 = getComputedStyle(el);
|
|
3166
|
+
out.rotate = cs2.rotate && cs2.rotate !== "none" ? cs2.rotate : "0deg";
|
|
2976
3167
|
}
|
|
2977
3168
|
const sc = safeGet("scale");
|
|
2978
|
-
if (sc
|
|
2979
|
-
const sx = sc[0]?.value
|
|
2980
|
-
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;
|
|
2981
3172
|
out.scale = `${sx} ${sy}`;
|
|
3173
|
+
} else {
|
|
3174
|
+
const cs2 = getComputedStyle(el);
|
|
3175
|
+
out.scale = cs2.scale && cs2.scale !== "none" ? cs2.scale : null;
|
|
2982
3176
|
}
|
|
2983
3177
|
const tr = safeGet("translate");
|
|
2984
|
-
if (tr
|
|
2985
|
-
const tx = tr[0]?.value
|
|
2986
|
-
const ty = tr[1]?.value
|
|
2987
|
-
const ux = tr
|
|
2988
|
-
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";
|
|
2989
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;
|
|
2990
3187
|
}
|
|
2991
3188
|
return out;
|
|
2992
3189
|
}
|
|
@@ -2997,17 +3194,33 @@ function readIndividualTransforms(el) {
|
|
|
2997
3194
|
return out;
|
|
2998
3195
|
}
|
|
2999
3196
|
function hasBBoxAffectingTransform(el) {
|
|
3000
|
-
const
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
return true;
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
const
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
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
|
+
};
|
|
3011
3224
|
}
|
|
3012
3225
|
var __measureHost = null;
|
|
3013
3226
|
function getMeasureHost() {
|
|
@@ -3095,40 +3308,198 @@ function createContext(options = {}) {
|
|
|
3095
3308
|
};
|
|
3096
3309
|
}
|
|
3097
3310
|
|
|
3098
|
-
// src/exporters/
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
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;
|
|
3106
3408
|
}
|
|
3107
|
-
return img;
|
|
3108
3409
|
}
|
|
3109
|
-
|
|
3110
|
-
// src/exporters/toCanvas.js
|
|
3111
3410
|
async function toCanvas(url, options) {
|
|
3112
|
-
|
|
3411
|
+
let { width: optW, height: optH, scale = 1, dpr = 1, meta = {} } = options;
|
|
3412
|
+
url = maybeConvertBoxShadowForSafari(url);
|
|
3113
3413
|
const img = new Image();
|
|
3114
|
-
img.crossOrigin = "anonymous";
|
|
3115
3414
|
img.loading = "eager";
|
|
3116
3415
|
img.decoding = "sync";
|
|
3416
|
+
img.crossOrigin = "anonymous";
|
|
3117
3417
|
img.src = url;
|
|
3118
3418
|
await img.decode();
|
|
3119
|
-
const
|
|
3120
|
-
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);
|
|
3121
3443
|
const canvas = document.createElement("canvas");
|
|
3122
|
-
canvas.width = Math.ceil(
|
|
3123
|
-
canvas.height = Math.ceil(
|
|
3124
|
-
canvas.style.width = `${
|
|
3125
|
-
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`;
|
|
3126
3448
|
const ctx = canvas.getContext("2d");
|
|
3127
3449
|
if (dpr !== 1) ctx.scale(dpr, dpr);
|
|
3128
|
-
ctx.drawImage(img, 0, 0,
|
|
3450
|
+
ctx.drawImage(img, 0, 0, outW, outH);
|
|
3129
3451
|
return canvas;
|
|
3130
3452
|
}
|
|
3131
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
|
+
|
|
3132
3503
|
// src/exporters/toBlob.js
|
|
3133
3504
|
async function toBlob(url, options) {
|
|
3134
3505
|
const type = options.type;
|
|
@@ -3147,18 +3518,6 @@ async function toBlob(url, options) {
|
|
|
3147
3518
|
);
|
|
3148
3519
|
}
|
|
3149
3520
|
|
|
3150
|
-
// src/modules/rasterize.js
|
|
3151
|
-
async function rasterize(url, options) {
|
|
3152
|
-
const canvas = await toCanvas(url, options);
|
|
3153
|
-
const finalCanvas = options.backgroundColor ? createBackground(canvas, options.backgroundColor) : canvas;
|
|
3154
|
-
const img = new Image();
|
|
3155
|
-
img.src = finalCanvas.toDataURL(`image/${options.format}`, options.quality);
|
|
3156
|
-
await img.decode();
|
|
3157
|
-
img.style.width = `${finalCanvas.width / options.dpr}px`;
|
|
3158
|
-
img.style.height = `${finalCanvas.height / options.dpr}px`;
|
|
3159
|
-
return img;
|
|
3160
|
-
}
|
|
3161
|
-
|
|
3162
3521
|
// src/exporters/download.js
|
|
3163
3522
|
async function download(url, options) {
|
|
3164
3523
|
options.dpr = 1;
|
|
@@ -3185,7 +3544,8 @@ var INTERNAL_TOKEN = Symbol("snapdom.internal");
|
|
|
3185
3544
|
var _safariWarmup = false;
|
|
3186
3545
|
async function snapdom(element, userOptions) {
|
|
3187
3546
|
if (!element) throw new Error("Element cannot be null or undefined");
|
|
3188
|
-
|
|
3547
|
+
const context = createContext(userOptions);
|
|
3548
|
+
if (isSafari() && (context.embedFonts === true || hasBackgroundOrMask(element))) {
|
|
3189
3549
|
for (let i = 0; i < 3; i++) {
|
|
3190
3550
|
try {
|
|
3191
3551
|
await safariWarmup(element, userOptions);
|
|
@@ -3195,7 +3555,6 @@ async function snapdom(element, userOptions) {
|
|
|
3195
3555
|
}
|
|
3196
3556
|
}
|
|
3197
3557
|
}
|
|
3198
|
-
const context = createContext(userOptions);
|
|
3199
3558
|
if (context.iconFonts && context.iconFonts.length > 0) extendIconFonts(context.iconFonts);
|
|
3200
3559
|
if (!context.snap) {
|
|
3201
3560
|
context.snap = {
|
|
@@ -3275,6 +3634,20 @@ async function safariWarmup(element, baseOptions) {
|
|
|
3275
3634
|
cleanup();
|
|
3276
3635
|
});
|
|
3277
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
|
+
}
|
|
3278
3651
|
|
|
3279
3652
|
// src/api/preCache.js
|
|
3280
3653
|
async function preCache(root = document, options = {}) {
|