@zumer/snapdom 1.9.12-dev.2 → 1.9.12-dev.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  /*
3
3
  * snapdom
4
- * v.1.9.12-dev.2
4
+ * v.1.9.12-dev.4
5
5
  * Author Juan Martin Muda
6
6
  * License MIT
7
7
  **/
@@ -628,17 +628,6 @@
628
628
  }
629
629
  return map.get(pseudo);
630
630
  }
631
- function parseContent(content) {
632
- let clean = content.replace(/^['"]|['"]$/g, "");
633
- if (clean.startsWith("\\")) {
634
- try {
635
- return String.fromCharCode(parseInt(clean.replace("\\", ""), 16));
636
- } catch (e) {
637
- return clean;
638
- }
639
- }
640
- return clean;
641
- }
642
631
  function snapshotComputedStyle(style) {
643
632
  const snap = {};
644
633
  for (let prop of style) {
@@ -675,8 +664,9 @@
675
664
  function isSafari() {
676
665
  const ua = typeof navigator !== "undefined" && navigator.userAgent ? navigator.userAgent : "";
677
666
  const isSafariUA = /^((?!chrome|android).)*safari/i.test(ua);
667
+ const isUIWebView = /AppleWebKit/i.test(ua) && /Mobile/i.test(ua) && !/Safari/i.test(ua);
678
668
  const isWeChatUA = /(MicroMessenger|wxwork|WeCom|WindowsWechat|MacWechat)/i.test(ua);
679
- return isSafariUA || isWeChatUA;
669
+ return isSafariUA || isUIWebView || isWeChatUA;
680
670
  }
681
671
 
682
672
  // src/modules/styles.js
@@ -2172,11 +2162,21 @@
2172
2162
  const empty = /* @__PURE__ */ new Map();
2173
2163
  build(rootEl, empty, empty);
2174
2164
  return {
2165
+ /**
2166
+ * Get top value for counter name at given node.
2167
+ * @param {Element} node
2168
+ * @param {string} name
2169
+ */
2175
2170
  get(node, name) {
2176
2171
  var _a;
2177
2172
  const s = (_a = nodeCounters.get(node)) == null ? void 0 : _a.get(name);
2178
2173
  return s && s.length ? s[s.length - 1] : 0;
2179
2174
  },
2175
+ /**
2176
+ * Get full stack for counter name at given node.
2177
+ * @param {Element} node
2178
+ * @param {string} name
2179
+ */
2180
2180
  getStack(node, name) {
2181
2181
  var _a;
2182
2182
  const s = (_a = nodeCounters.get(node)) == null ? void 0 : _a.get(name);
@@ -2214,10 +2214,111 @@
2214
2214
 
2215
2215
  // src/modules/pseudo.js
2216
2216
  var counterCtx = null;
2217
+ var __siblingCounters = /* @__PURE__ */ new WeakMap();
2218
+ function unquoteDoubleStrings2(s) {
2219
+ return (s || "").replace(/"([^"]*)"/g, "$1");
2220
+ }
2221
+ function collapseCssContent(raw) {
2222
+ if (!raw) return "";
2223
+ const tokens = [];
2224
+ const rx = /"([^"]*)"/g;
2225
+ let m;
2226
+ while (m = rx.exec(raw)) tokens.push(m[1]);
2227
+ if (tokens.length) return tokens.join("");
2228
+ return unquoteDoubleStrings2(raw);
2229
+ }
2230
+ function withSiblingOverrides(node, base) {
2231
+ const parent = node.parentElement;
2232
+ const map = parent ? __siblingCounters.get(parent) : null;
2233
+ if (!map) return base;
2234
+ return {
2235
+ get(n, name) {
2236
+ const v = base.get(n, name);
2237
+ const ov = map.get(name);
2238
+ return typeof ov === "number" ? Math.max(v, ov) : v;
2239
+ },
2240
+ getStack(n, name) {
2241
+ const s = base.getStack(n, name);
2242
+ if (!s.length) return s;
2243
+ const ov = map.get(name);
2244
+ if (typeof ov === "number") {
2245
+ const out = s.slice();
2246
+ out[out.length - 1] = Math.max(out[out.length - 1], ov);
2247
+ return out;
2248
+ }
2249
+ return s;
2250
+ }
2251
+ };
2252
+ }
2253
+ function deriveCounterCtxForPseudo(node, pseudoStyle, baseCtx) {
2254
+ const modStacks = /* @__PURE__ */ new Map();
2255
+ function parseListDecl(value) {
2256
+ const out = [];
2257
+ if (!value || value === "none") return out;
2258
+ for (const part of String(value).split(",")) {
2259
+ const toks = part.trim().split(/\s+/);
2260
+ const name = toks[0];
2261
+ const num = Number.isFinite(Number(toks[1])) ? Number(toks[1]) : void 0;
2262
+ if (name) out.push({ name, num });
2263
+ }
2264
+ return out;
2265
+ }
2266
+ const resets = parseListDecl(pseudoStyle == null ? void 0 : pseudoStyle.counterReset);
2267
+ const incs = parseListDecl(pseudoStyle == null ? void 0 : pseudoStyle.counterIncrement);
2268
+ function getStackDerived(name) {
2269
+ if (modStacks.has(name)) return modStacks.get(name).slice();
2270
+ let stack = baseCtx.getStack(node, name);
2271
+ stack = stack.length ? stack.slice() : [];
2272
+ const r = resets.find((x) => x.name === name);
2273
+ if (r) {
2274
+ const val = Number.isFinite(r.num) ? r.num : 0;
2275
+ stack = stack.length ? [...stack, val] : [val];
2276
+ }
2277
+ const inc = incs.find((x) => x.name === name);
2278
+ if (inc) {
2279
+ const by = Number.isFinite(inc.num) ? inc.num : 1;
2280
+ if (stack.length === 0) stack = [0];
2281
+ stack[stack.length - 1] += by;
2282
+ }
2283
+ modStacks.set(name, stack.slice());
2284
+ return stack;
2285
+ }
2286
+ return {
2287
+ get(_node, name) {
2288
+ const s = getStackDerived(name);
2289
+ return s.length ? s[s.length - 1] : 0;
2290
+ },
2291
+ getStack(_node, name) {
2292
+ return getStackDerived(name);
2293
+ },
2294
+ /** expone increments del pseudo para que el caller pueda propagar a hermanos */
2295
+ __incs: incs
2296
+ };
2297
+ }
2298
+ function resolvePseudoContentAndIncs(node, pseudo, baseCtx) {
2299
+ let ps;
2300
+ try {
2301
+ ps = getComputedStyle(node, pseudo);
2302
+ } catch (e) {
2303
+ }
2304
+ const raw = ps == null ? void 0 : ps.content;
2305
+ if (!raw || raw === "none" || raw === "normal") return { text: "", incs: [] };
2306
+ const baseWithSiblings = withSiblingOverrides(node, baseCtx);
2307
+ const derived = deriveCounterCtxForPseudo(node, ps, baseWithSiblings);
2308
+ let resolved = hasCounters(raw) ? resolveCountersInContent(raw, node, derived) : raw;
2309
+ const text = collapseCssContent(resolved);
2310
+ return { text, incs: derived.__incs || [] };
2311
+ }
2217
2312
  function inlinePseudoElements(source, clone, sessionCache, options) {
2218
2313
  return __async(this, null, function* () {
2219
2314
  var _a;
2220
2315
  if (!(source instanceof Element) || !(clone instanceof Element)) return;
2316
+ if (!counterCtx) {
2317
+ try {
2318
+ counterCtx = buildCounterContext(source.ownerDocument || document);
2319
+ } catch (e) {
2320
+ }
2321
+ }
2221
2322
  for (const pseudo of ["::before", "::after", "::first-letter"]) {
2222
2323
  try {
2223
2324
  const style = getStyle(source, pseudo);
@@ -2251,12 +2352,8 @@
2251
2352
  clone.insertBefore(span, restNode);
2252
2353
  continue;
2253
2354
  }
2254
- const content = style.content;
2255
- let cleanContent = parseContent(content);
2256
- if (hasCounters(content)) {
2257
- counterCtx = buildCounterContext(source.ownerDocument || document);
2258
- cleanContent = resolveCountersInContent(cleanContent, source, counterCtx);
2259
- }
2355
+ const rawContent = style.content;
2356
+ const { text: cleanContent, incs } = resolvePseudoContentAndIncs(source, pseudo, counterCtx);
2260
2357
  const bg = style.backgroundImage;
2261
2358
  const bgColor = style.backgroundColor;
2262
2359
  const fontFamily = style.fontFamily;
@@ -2270,28 +2367,42 @@
2270
2367
  const borderWidth = parseFloat(style.borderWidth);
2271
2368
  const transform = style.transform;
2272
2369
  const isIconFont22 = isIconFont2(fontFamily);
2273
- const hasExplicitContent = content !== "none" && cleanContent !== "";
2370
+ const hasExplicitContent = rawContent !== "none" && cleanContent !== "";
2274
2371
  const hasBg = bg && bg !== "none";
2275
2372
  const hasBgColor = bgColor && bgColor !== "transparent" && bgColor !== "rgba(0, 0, 0, 0)";
2276
2373
  const hasBox2 = display !== "inline" && (width > 0 || height > 0);
2277
2374
  const hasBorder = borderStyle && borderStyle !== "none" && borderWidth > 0;
2278
2375
  const hasTransform = transform && transform !== "none";
2279
- const shouldRender = hasExplicitContent || hasBg || hasBgColor || hasBox2 || hasBorder || hasTransform;
2280
- if (!shouldRender) continue;
2376
+ const shouldRender = hasExplicitContent || hasBg || hasBgColor || hasBorder || hasTransform;
2377
+ if (!shouldRender) {
2378
+ if (incs && incs.length && source.parentElement) {
2379
+ const map = __siblingCounters.get(source.parentElement) || /* @__PURE__ */ new Map();
2380
+ for (const { name, num } of incs) {
2381
+ if (!name) continue;
2382
+ const baseWithSibs = withSiblingOverrides(source, counterCtx);
2383
+ const derived = deriveCounterCtxForPseudo(source, getComputedStyle(source, pseudo), baseWithSibs);
2384
+ const finalVal = derived.get(source, name);
2385
+ map.set(name, finalVal);
2386
+ }
2387
+ __siblingCounters.set(source.parentElement, map);
2388
+ }
2389
+ continue;
2390
+ }
2281
2391
  const pseudoEl = document.createElement("span");
2282
2392
  pseudoEl.dataset.snapdomPseudo = pseudo;
2283
2393
  pseudoEl.style.verticalAlign = "middle";
2394
+ pseudoEl.style.pointerEvents = "none";
2284
2395
  const snapshot = snapshotComputedStyle(style);
2285
2396
  const key = getStyleKey(snapshot, "span");
2286
2397
  sessionCache.styleMap.set(pseudoEl, key);
2287
- if (isIconFont22 && cleanContent.length === 1) {
2288
- const { dataUrl, width: width2, height: height2 } = yield iconToImage(cleanContent, fontFamily, fontWeight, fontSize, color);
2398
+ if (isIconFont22 && cleanContent && cleanContent.length === 1) {
2399
+ const { dataUrl, width: w, height: h } = yield iconToImage(cleanContent, fontFamily, fontWeight, fontSize, color);
2289
2400
  const imgEl = document.createElement("img");
2290
2401
  imgEl.src = dataUrl;
2291
- imgEl.style = `height:${fontSize}px;width:${width2 / height2 * fontSize}px;object-fit:contain;`;
2402
+ imgEl.style = `height:${fontSize}px;width:${w / h * fontSize}px;object-fit:contain;`;
2292
2403
  pseudoEl.appendChild(imgEl);
2293
2404
  clone.dataset.snapdomHasIcon = "true";
2294
- } else if (cleanContent.startsWith("url(")) {
2405
+ } else if (cleanContent && cleanContent.startsWith("url(")) {
2295
2406
  const rawUrl = extractURL(cleanContent);
2296
2407
  if (rawUrl == null ? void 0 : rawUrl.trim()) {
2297
2408
  try {
@@ -2307,6 +2418,10 @@
2307
2418
  } else if (!isIconFont22 && hasExplicitContent) {
2308
2419
  pseudoEl.textContent = cleanContent;
2309
2420
  }
2421
+ pseudoEl.style.background = "none";
2422
+ if ("mask" in pseudoEl.style) {
2423
+ pseudoEl.style.mask = "none";
2424
+ }
2310
2425
  if (hasBg) {
2311
2426
  try {
2312
2427
  const bgSplits = splitBackgroundImage(bg);
@@ -2318,7 +2433,18 @@
2318
2433
  }
2319
2434
  if (hasBgColor) pseudoEl.style.backgroundColor = bgColor;
2320
2435
  const hasContent2 = pseudoEl.childNodes.length > 0 || ((_a = pseudoEl.textContent) == null ? void 0 : _a.trim()) !== "";
2321
- const hasVisibleBox = hasContent2 || hasBg || hasBgColor || hasBox2 || hasBorder || hasTransform;
2436
+ const hasVisibleBox = hasContent2 || hasBg || hasBgColor || hasBorder || hasTransform;
2437
+ if (incs && incs.length && source.parentElement) {
2438
+ const map = __siblingCounters.get(source.parentElement) || /* @__PURE__ */ new Map();
2439
+ const baseWithSibs = withSiblingOverrides(source, counterCtx);
2440
+ const derived = deriveCounterCtxForPseudo(source, getComputedStyle(source, pseudo), baseWithSibs);
2441
+ for (const { name } of incs) {
2442
+ if (!name) continue;
2443
+ const finalVal = derived.get(source, name);
2444
+ map.set(name, finalVal);
2445
+ }
2446
+ __siblingCounters.set(source.parentElement, map);
2447
+ }
2322
2448
  if (!hasVisibleBox) continue;
2323
2449
  if (pseudo === "::before") {
2324
2450
  clone.insertBefore(pseudoEl, clone.firstChild);
@@ -2387,6 +2513,103 @@
2387
2513
  });
2388
2514
  }
2389
2515
 
2516
+ // src/modules/changeCSS.js
2517
+ function freezeSticky(originalRoot, cloneRoot) {
2518
+ var _a;
2519
+ if (!originalRoot || !cloneRoot) return;
2520
+ const scrollTop = originalRoot.scrollTop || 0;
2521
+ if (!scrollTop) return;
2522
+ if (getComputedStyle(cloneRoot).position === "static") {
2523
+ cloneRoot.style.position = "relative";
2524
+ }
2525
+ const rootRect = originalRoot.getBoundingClientRect();
2526
+ const viewportH = originalRoot.clientHeight;
2527
+ const PH_ATTR = "data-snap-ph";
2528
+ const walker = document.createTreeWalker(originalRoot, NodeFilter.SHOW_ELEMENT);
2529
+ while (walker.nextNode()) {
2530
+ const el = (
2531
+ /** @type {HTMLElement} */
2532
+ walker.currentNode
2533
+ );
2534
+ const cs = getComputedStyle(el);
2535
+ const pos = cs.position;
2536
+ if (pos !== "sticky" && pos !== "-webkit-sticky") continue;
2537
+ const topInit = _toPx(cs.top);
2538
+ const bottomInit = _toPx(cs.bottom);
2539
+ if (topInit == null && bottomInit == null) continue;
2540
+ const path = _pathOf(el, originalRoot);
2541
+ const cloneEl = _findByPathIgnoringPlaceholders(cloneRoot, path, PH_ATTR);
2542
+ if (!cloneEl) continue;
2543
+ const elRect = el.getBoundingClientRect();
2544
+ const widthPx = elRect.width;
2545
+ const heightPx = elRect.height;
2546
+ const leftPx = elRect.left - rootRect.left;
2547
+ if (!(widthPx > 0 && heightPx > 0)) continue;
2548
+ if (!Number.isFinite(leftPx)) continue;
2549
+ const topAbsPx = topInit != null ? topInit + scrollTop : scrollTop + (viewportH - heightPx - /** bottomInit non-null */
2550
+ bottomInit);
2551
+ if (!Number.isFinite(topAbsPx)) continue;
2552
+ const zParsed = Number.parseInt(cs.zIndex, 10);
2553
+ const hasZ = Number.isFinite(zParsed);
2554
+ const overlayZ = hasZ ? Math.max(zParsed, 1) + 1 : 2;
2555
+ const placeholderZ = hasZ ? zParsed - 1 : 0;
2556
+ const ph = cloneEl.cloneNode(false);
2557
+ ph.setAttribute(PH_ATTR, "1");
2558
+ ph.style.position = "sticky";
2559
+ ph.style.left = `${leftPx}px`;
2560
+ ph.style.top = `${topAbsPx}px`;
2561
+ ph.style.width = `${widthPx}px`;
2562
+ ph.style.height = `${heightPx}px`;
2563
+ ph.style.visibility = "hidden";
2564
+ ph.style.zIndex = String(placeholderZ);
2565
+ ph.style.overflow = "hidden";
2566
+ ph.style.background = "transparent";
2567
+ ph.style.boxShadow = "none";
2568
+ ph.style.filter = "none";
2569
+ (_a = cloneEl.parentElement) == null ? void 0 : _a.insertBefore(ph, cloneEl);
2570
+ cloneEl.style.position = "absolute";
2571
+ cloneEl.style.left = `${leftPx}px`;
2572
+ cloneEl.style.top = `${topAbsPx}px`;
2573
+ cloneEl.style.bottom = "auto";
2574
+ cloneEl.style.zIndex = String(overlayZ);
2575
+ cloneEl.style.pointerEvents = "none";
2576
+ }
2577
+ }
2578
+ function _toPx(v) {
2579
+ if (!v || v === "auto") return null;
2580
+ const n = Number.parseFloat(v);
2581
+ return Number.isFinite(n) ? n : null;
2582
+ }
2583
+ function _pathOf(el, root) {
2584
+ const path = [];
2585
+ for (let cur = el; cur && cur !== root; ) {
2586
+ const p = cur.parentElement;
2587
+ if (!p) break;
2588
+ path.push(Array.prototype.indexOf.call(p.children, cur));
2589
+ cur = p;
2590
+ }
2591
+ return path.reverse();
2592
+ }
2593
+ function _findByPathIgnoringPlaceholders(root, path, phAttr) {
2594
+ let cur = root;
2595
+ for (let i = 0; i < path.length; i++) {
2596
+ const kids = _childrenWithoutPlaceholders(cur, phAttr);
2597
+ cur = /** @type {HTMLElement|undefined} */
2598
+ kids[path[i]];
2599
+ if (!cur) return null;
2600
+ }
2601
+ return cur instanceof HTMLElement ? cur : null;
2602
+ }
2603
+ function _childrenWithoutPlaceholders(el, phAttr) {
2604
+ const out = [];
2605
+ const ch = el.children;
2606
+ for (let i = 0; i < ch.length; i++) {
2607
+ const c = ch[i];
2608
+ if (!c.hasAttribute(phAttr)) out.push(c);
2609
+ }
2610
+ return out;
2611
+ }
2612
+
2390
2613
  // src/core/prepare.js
2391
2614
  function prepareClone(_0) {
2392
2615
  return __async(this, arguments, function* (element, options = {}) {
@@ -2463,17 +2686,20 @@
2463
2686
  cloneNode.appendChild(inner);
2464
2687
  }
2465
2688
  }
2689
+ const contentRoot = clone instanceof HTMLElement && clone.firstElementChild instanceof HTMLElement ? clone.firstElementChild : clone;
2690
+ freezeSticky(element, contentRoot);
2466
2691
  if (element === sessionCache.nodeMap.get(clone)) {
2467
2692
  const computed = sessionCache.styleCache.get(element) || window.getComputedStyle(element);
2468
2693
  sessionCache.styleCache.set(element, computed);
2469
2694
  const transform = stripTranslate(computed.transform);
2470
2695
  clone.style.margin = "0";
2471
- clone.style.position = "static";
2472
2696
  clone.style.top = "auto";
2473
2697
  clone.style.left = "auto";
2474
2698
  clone.style.right = "auto";
2475
2699
  clone.style.bottom = "auto";
2476
- clone.style.zIndex = "auto";
2700
+ clone.style.animation = "none";
2701
+ clone.style.transition = "none";
2702
+ clone.style.willChange = "auto";
2477
2703
  clone.style.float = "none";
2478
2704
  clone.style.clear = "none";
2479
2705
  clone.style.transform = transform || "";
@@ -2886,7 +3112,7 @@
2886
3112
  idle(() => __async(null, null, function* () {
2887
3113
  const required = collectUsedFontVariants(element);
2888
3114
  const usedCodepoints = collectUsedCodepoints(element);
2889
- if (isSafari) {
3115
+ if (isSafari()) {
2890
3116
  const families = new Set(
2891
3117
  Array.from(required).map((k) => String(k).split("__")[0]).filter(Boolean)
2892
3118
  );
@@ -2918,102 +3144,188 @@
2918
3144
  }
2919
3145
  yield new Promise((resolve) => {
2920
3146
  idle(() => {
2921
- var _a, _b, _c, _d, _e;
2922
- const rect = element.getBoundingClientRect();
2923
- let w = Number(rect.width) || 0;
2924
- let h = Number(rect.height) || 0;
2925
- const hasW = Number.isFinite(options.width);
2926
- const hasH = Number.isFinite(options.height);
2927
- const scaleOpt = (_a = options.scale) != null ? _a : 1;
2928
- const hasScale = typeof scaleOpt === "number" && scaleOpt !== 1;
2929
- if (!hasScale) {
2930
- const aspect = h > 0 ? w / h : 1;
2931
- if (hasW && hasH) {
2932
- w = Number(options.width);
2933
- h = Number(options.height);
2934
- } else if (hasW) {
2935
- w = Number(options.width);
2936
- h = w / (aspect || 1);
2937
- } else if (hasH) {
2938
- h = Number(options.height);
2939
- w = h * (aspect || 1);
3147
+ const csEl = getComputedStyle(element);
3148
+ function parseFilterDropShadows(cs) {
3149
+ var _a;
3150
+ const raw = `${cs.filter || ""} ${cs.webkitFilter || ""}`.trim();
3151
+ if (!raw || raw === "none") {
3152
+ return { bleed: { top: 0, right: 0, bottom: 0, left: 0 }, has: false };
3153
+ }
3154
+ const tokens = raw.match(/drop-shadow\((?:[^()]|\([^()]*\))*\)/gi) || [];
3155
+ let t = 0, r = 0, b = 0, l = 0;
3156
+ let found = false;
3157
+ for (const tok of tokens) {
3158
+ found = true;
3159
+ const nums = ((_a = tok.match(/-?\d+(?:\.\d+)?px/gi)) == null ? void 0 : _a.map((v) => parseFloat(v))) || [];
3160
+ const [ox = 0, oy = 0, blur = 0] = nums;
3161
+ const extX = Math.abs(ox) + blur;
3162
+ const extY = Math.abs(oy) + blur;
3163
+ r = Math.max(r, extX + Math.max(ox, 0));
3164
+ l = Math.max(l, extX + Math.max(-ox, 0));
3165
+ b = Math.max(b, extY + Math.max(oy, 0));
3166
+ t = Math.max(t, extY + Math.max(-oy, 0));
3167
+ }
3168
+ return {
3169
+ bleed: { top: Math.ceil(t), right: Math.ceil(r), bottom: Math.ceil(b), left: Math.ceil(l) },
3170
+ has: found
3171
+ };
3172
+ }
3173
+ function parseBoxShadow(cs) {
3174
+ var _a;
3175
+ const v = cs.boxShadow || "";
3176
+ if (!v || v === "none") return { top: 0, right: 0, bottom: 0, left: 0 };
3177
+ const parts = v.split(/\),(?=(?:[^()]*\([^()]*\))*[^()]*$)/).map((s) => s.trim());
3178
+ let t = 0, r = 0, b2 = 0, l = 0;
3179
+ for (const part of parts) {
3180
+ const nums = ((_a = part.match(/-?\d+(\.\d+)?px/g)) == null ? void 0 : _a.map((n) => parseFloat(n))) || [];
3181
+ if (nums.length < 2) continue;
3182
+ const [ox2, oy2, blur = 0, spread = 0] = nums;
3183
+ const extX = Math.abs(ox2) + blur + spread;
3184
+ const extY = Math.abs(oy2) + blur + spread;
3185
+ r = Math.max(r, extX + Math.max(ox2, 0));
3186
+ l = Math.max(l, extX + Math.max(-ox2, 0));
3187
+ b2 = Math.max(b2, extY + Math.max(oy2, 0));
3188
+ t = Math.max(t, extY + Math.max(-oy2, 0));
2940
3189
  }
3190
+ return { top: Math.ceil(t), right: Math.ceil(r), bottom: Math.ceil(b2), left: Math.ceil(l) };
2941
3191
  }
2942
- w = Math.max(1, Math.ceil(Number.isFinite(w) ? w : 1));
2943
- h = Math.max(1, Math.ceil(Number.isFinite(h) ? h : 1));
2944
- const csEl = getComputedStyle(element);
2945
- const baseTransform = csEl.transform && csEl.transform !== "none" ? csEl.transform : "";
2946
- const ind = readIndividualTransforms(element);
2947
- let outW, outH, tx, ty, cancelTranslateX = 0, cancelTranslateY = 0;
2948
- if (!hasBBoxAffectingTransform(element)) {
2949
- const r = element.getBoundingClientRect();
2950
- const vp = inflateRect(r, { top: 0, right: 0, bottom: 0, left: 0 });
2951
- outW = Math.max(1, vp.width);
2952
- outH = Math.max(1, vp.height);
2953
- tx = -(vp.x - Math.floor(r.left));
2954
- ty = -(vp.y - Math.floor(r.top));
3192
+ function parseFilterBlur(cs) {
3193
+ const m = (cs.filter || "").match(/blur\(\s*([0-9.]+)px\s*\)/);
3194
+ const b2 = m ? Math.ceil(parseFloat(m[1]) || 0) : 0;
3195
+ return { top: b2, right: b2, bottom: b2, left: b2 };
3196
+ }
3197
+ function parseOutline(cs) {
3198
+ if ((cs.outlineStyle || "none") === "none") return { top: 0, right: 0, bottom: 0, left: 0 };
3199
+ const w2 = Math.ceil(parseFloat(cs.outlineWidth || "0") || 0);
3200
+ return { top: w2, right: w2, bottom: w2, left: w2 };
3201
+ }
3202
+ function bboxWithOriginFull(w2, h2, M, ox2, oy2) {
3203
+ const a2 = M.a, b2 = M.b, c2 = M.c, d2 = M.d, e2 = M.e || 0, f2 = M.f || 0;
3204
+ function pt(x, y) {
3205
+ let X = x - ox2, Y = y - oy2;
3206
+ let X2 = a2 * X + c2 * Y, Y2 = b2 * X + d2 * Y;
3207
+ X2 += ox2 + e2;
3208
+ Y2 += oy2 + f2;
3209
+ return [X2, Y2];
3210
+ }
3211
+ const P = [pt(0, 0), pt(w2, 0), pt(0, h2), pt(w2, h2)];
3212
+ let minX2 = Infinity, minY2 = Infinity, maxX2 = -Infinity, maxY2 = -Infinity;
3213
+ for (const [X, Y] of P) {
3214
+ if (X < minX2) minX2 = X;
3215
+ if (Y < minY2) minY2 = Y;
3216
+ if (X > maxX2) maxX2 = X;
3217
+ if (Y > maxY2) maxY2 = Y;
3218
+ }
3219
+ return { minX: minX2, minY: minY2, maxX: maxX2, maxY: maxY2, width: maxX2 - minX2, height: maxY2 - minY2 };
3220
+ }
3221
+ function hasTFBBox(el) {
3222
+ return hasBBoxAffectingTransform(el);
3223
+ }
3224
+ const rect = element.getBoundingClientRect();
3225
+ const w0 = Math.max(1, Math.ceil(element.offsetWidth || parseFloat(csEl.width) || rect.width || 1));
3226
+ const h0 = Math.max(1, Math.ceil(element.offsetHeight || parseFloat(csEl.height) || rect.height || 1));
3227
+ const coerceNum = (v, def = NaN) => {
3228
+ const n = typeof v === "string" ? parseFloat(v) : v;
3229
+ return Number.isFinite(n) ? n : def;
3230
+ };
3231
+ const optW = coerceNum(options.width);
3232
+ const optH = coerceNum(options.height);
3233
+ let w = w0, h = h0;
3234
+ const hasW = Number.isFinite(optW);
3235
+ const hasH = Number.isFinite(optH);
3236
+ const aspect0 = h0 > 0 ? w0 / h0 : 1;
3237
+ if (hasW && hasH) {
3238
+ w = Math.max(1, Math.ceil(optW));
3239
+ h = Math.max(1, Math.ceil(optH));
3240
+ } else if (hasW) {
3241
+ w = Math.max(1, Math.ceil(optW));
3242
+ h = Math.max(1, Math.ceil(w / (aspect0 || 1)));
3243
+ } else if (hasH) {
3244
+ h = Math.max(1, Math.ceil(optH));
3245
+ w = Math.max(1, Math.ceil(h * (aspect0 || 1)));
2955
3246
  } else {
3247
+ w = w0;
3248
+ h = h0;
3249
+ }
3250
+ let minX = 0, minY = 0, maxX = w0, maxY = h0;
3251
+ if (hasTFBBox(element)) {
3252
+ const baseTransform2 = csEl.transform && csEl.transform !== "none" ? csEl.transform : "";
3253
+ const ind2 = readIndividualTransforms(element);
2956
3254
  const TOTAL = readTotalTransformMatrix({
2957
- baseTransform,
2958
- rotate: ind.rotate || "0deg",
2959
- scale: ind.scale,
2960
- translate: ind.translate
3255
+ baseTransform: baseTransform2,
3256
+ rotate: ind2.rotate || "0deg",
3257
+ scale: ind2.scale,
3258
+ translate: ind2.translate
2961
3259
  });
2962
- const e = (_c = (_b = TOTAL.e) != null ? _b : TOTAL.m41) != null ? _c : 0;
2963
- const f = (_e = (_d = TOTAL.f) != null ? _d : TOTAL.m42) != null ? _e : 0;
2964
- cancelTranslateX = -e;
2965
- cancelTranslateY = -f;
2966
- const M2D = TOTAL.is2D ? matrix2DNoTranslate(TOTAL) : matrix2DNoTranslate(new DOMMatrix(TOTAL.toString()));
2967
- const bb = bboxFromMatrix(w, h, M2D);
2968
- outW = Math.max(1, Math.ceil(bb.width));
2969
- outH = Math.max(1, Math.ceil(bb.height));
2970
- tx = -bb.minX;
2971
- ty = -bb.minY;
3260
+ const { ox: ox2, oy: oy2 } = parseTransformOriginPx(csEl, w0, h0);
3261
+ const M = TOTAL.is2D ? TOTAL : new DOMMatrix(TOTAL.toString());
3262
+ const bb = bboxWithOriginFull(w0, h0, M, ox2, oy2);
3263
+ minX = bb.minX;
3264
+ minY = bb.minY;
3265
+ maxX = bb.maxX;
3266
+ maxY = bb.maxY;
2972
3267
  }
3268
+ const bleedShadow = parseBoxShadow(csEl);
3269
+ const bleedBlur = parseFilterBlur(csEl);
3270
+ const bleedOutline = parseOutline(csEl);
3271
+ const drop = parseFilterDropShadows(csEl);
3272
+ const bleed = {
3273
+ top: bleedShadow.top + bleedBlur.top + bleedOutline.top + drop.bleed.top,
3274
+ right: bleedShadow.right + bleedBlur.right + bleedOutline.right + drop.bleed.right,
3275
+ bottom: bleedShadow.bottom + bleedBlur.bottom + bleedOutline.bottom + drop.bleed.bottom,
3276
+ left: bleedShadow.left + bleedBlur.left + bleedOutline.left + drop.bleed.left
3277
+ };
3278
+ minX -= bleed.left;
3279
+ minY -= bleed.top;
3280
+ maxX += bleed.right;
3281
+ maxY += bleed.bottom;
3282
+ const vbW0 = Math.max(1, Math.ceil(maxX - minX));
3283
+ const vbH0 = Math.max(1, Math.ceil(maxY - minY));
3284
+ const outW = Math.max(1, Math.round(vbW0 * (hasW || hasH ? w / w0 : 1)));
3285
+ const outH = Math.max(1, Math.round(vbH0 * (hasH || hasW ? h / h0 : 1)));
2973
3286
  const svgNS = "http://www.w3.org/2000/svg";
3287
+ const pad = isSafari() ? 1 : 0;
2974
3288
  const fo = document.createElementNS(svgNS, "foreignObject");
2975
- fo.setAttribute("width", "100%");
2976
- fo.setAttribute("height", "100%");
2977
- fo.setAttribute("x", String(tx));
2978
- fo.setAttribute("y", String(ty));
3289
+ const vbMinX = Math.floor(minX);
3290
+ const vbMinY = Math.floor(minY);
3291
+ fo.setAttribute("x", String(-(vbMinX - pad)));
3292
+ fo.setAttribute("y", String(-(vbMinY - pad)));
3293
+ fo.setAttribute("width", String(Math.ceil(w0 + pad * 2)));
3294
+ fo.setAttribute("height", String(Math.ceil(h0 + pad * 2)));
2979
3295
  fo.style.overflow = "visible";
2980
3296
  const styleTag = document.createElement("style");
2981
3297
  styleTag.textContent = baseCSS + fontsCSS + "svg{overflow:visible;} foreignObject{overflow:visible;}" + classCSS;
2982
3298
  fo.appendChild(styleTag);
2983
3299
  const container = document.createElement("div");
2984
3300
  container.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
2985
- container.style.width = `${w}px`;
2986
- container.style.height = `${h}px`;
3301
+ container.style.width = `${w0}px`;
3302
+ container.style.height = `${h0}px`;
2987
3303
  container.style.overflow = "visible";
2988
- container.style.transformOrigin = "0 0";
2989
- const cancels = [];
2990
- if (cancelTranslateX || cancelTranslateY) {
2991
- cancels.push(`translate(${cancelTranslateX}px, ${cancelTranslateY}px)`);
2992
- }
2993
- if (!hasScale && (hasW || hasH)) {
2994
- const sxWH = rect.width ? w / rect.width : 1;
2995
- const syWH = rect.height ? h / rect.height : 1;
2996
- cancels.push(`scale(${sxWH}, ${syWH})`);
2997
- } else if (hasScale) {
2998
- cancels.push(`scale(${scaleOpt})`);
2999
- }
3000
- if (cancels.length) container.style.transform = cancels.join(" ");
3001
3304
  clone.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
3002
- clone.style.transformOrigin = "0 0";
3003
- clone.style.position = "static";
3004
- clone.style.left = "0";
3005
- clone.style.top = "0";
3006
- if (baseTransform) clone.style.transform = baseTransform;
3007
- clone.style.rotate = ind.rotate || "0deg";
3008
- clone.style.scale = ind.scale || "1";
3009
- clone.style.translate = ind.translate || "0 0";
3010
3305
  container.appendChild(clone);
3011
3306
  fo.appendChild(container);
3012
3307
  const serializer = new XMLSerializer();
3013
3308
  const foString = serializer.serializeToString(fo);
3014
- const finalW = !hasScale && (hasW || hasH) ? w : outW;
3015
- const finalH = !hasScale && (hasH || hasW) ? h : outH;
3016
- const svgHeader = `<svg xmlns="${svgNS}" width="${finalW}" height="${finalH}" viewBox="0 0 ${outW} ${outH}">`;
3309
+ const vbW = vbW0 + pad * 2;
3310
+ const vbH = vbH0 + pad * 2;
3311
+ const wantsSize = hasW || hasH;
3312
+ options.meta = {
3313
+ w0,
3314
+ // ancho natural del elemento
3315
+ h0,
3316
+ // alto natural
3317
+ vbW,
3318
+ // ancho del viewBox
3319
+ vbH,
3320
+ // alto del viewBox
3321
+ targetW: w,
3322
+ // ancho deseado según options.width
3323
+ targetH: h
3324
+ // alto deseado según options.height
3325
+ };
3326
+ const svgOutW = isSafari() && wantsSize ? vbW : outW + pad * 2;
3327
+ const svgOutH = isSafari() && wantsSize ? vbH : outH + pad * 2;
3328
+ const svgHeader = `<svg xmlns="${svgNS}" width="${svgOutW}" height="${svgOutH}" viewBox="0 0 ${vbW} ${vbH}">`;
3017
3329
  const svgFooter = "</svg>";
3018
3330
  svgString = svgHeader + foString + svgFooter;
3019
3331
  dataURL = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgString)}`;
@@ -3034,28 +3346,8 @@
3034
3346
  return new WebKitCSSMatrix(tr);
3035
3347
  }
3036
3348
  }
3037
- function matrix2DNoTranslate(M) {
3038
- return new DOMMatrix([M.a, M.b, M.c, M.d, 0, 0]);
3039
- }
3040
- function bboxFromMatrix(w, h, M2D) {
3041
- const pts = [
3042
- { x: 0, y: 0 },
3043
- { x: w, y: 0 },
3044
- { x: 0, y: h },
3045
- { x: w, y: h }
3046
- ];
3047
- const t = pts.map((p) => ({
3048
- x: M2D.a * p.x + M2D.c * p.y,
3049
- y: M2D.b * p.x + M2D.d * p.y
3050
- }));
3051
- const minX = Math.min(...t.map((p) => p.x));
3052
- const maxX = Math.max(...t.map((p) => p.x));
3053
- const minY = Math.min(...t.map((p) => p.y));
3054
- const maxY = Math.max(...t.map((p) => p.y));
3055
- return { minX, minY, width: maxX - minX, height: maxY - minY };
3056
- }
3057
3349
  function readIndividualTransforms(el) {
3058
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
3350
+ var _a, _b, _c, _d, _e, _f, _g, _h;
3059
3351
  const out = { rotate: "0deg", scale: null, translate: null };
3060
3352
  const map = typeof el.computedStyleMap === "function" ? el.computedStyleMap() : null;
3061
3353
  if (map) {
@@ -3070,26 +3362,37 @@
3070
3362
  };
3071
3363
  const rot = safeGet("rotate");
3072
3364
  if (rot) {
3073
- const ang = (_a = rot.angle) != null ? _a : rot;
3074
- if (ang && typeof ang === "object" && "unit" in ang) {
3365
+ if (rot.angle) {
3366
+ const ang = rot.angle;
3075
3367
  out.rotate = ang.unit === "rad" ? ang.value * 180 / Math.PI + "deg" : ang.value + ang.unit;
3368
+ } else if (rot.unit) {
3369
+ out.rotate = rot.unit === "rad" ? rot.value * 180 / Math.PI + "deg" : rot.value + rot.unit;
3076
3370
  } else {
3077
3371
  out.rotate = String(rot);
3078
3372
  }
3373
+ } else {
3374
+ const cs2 = getComputedStyle(el);
3375
+ out.rotate = cs2.rotate && cs2.rotate !== "none" ? cs2.rotate : "0deg";
3079
3376
  }
3080
3377
  const sc = safeGet("scale");
3081
- if (sc && sc.length) {
3082
- const sx = (_c = (_b = sc[0]) == null ? void 0 : _b.value) != null ? _c : 1;
3083
- const sy = (_e = (_d = sc[1]) == null ? void 0 : _d.value) != null ? _e : sx;
3378
+ if (sc) {
3379
+ const sx = "x" in sc && ((_a = sc.x) == null ? void 0 : _a.value) != null ? sc.x.value : Array.isArray(sc) ? (_b = sc[0]) == null ? void 0 : _b.value : Number(sc) || 1;
3380
+ const sy = "y" in sc && ((_c = sc.y) == null ? void 0 : _c.value) != null ? sc.y.value : Array.isArray(sc) ? (_d = sc[1]) == null ? void 0 : _d.value : sx;
3084
3381
  out.scale = `${sx} ${sy}`;
3382
+ } else {
3383
+ const cs2 = getComputedStyle(el);
3384
+ out.scale = cs2.scale && cs2.scale !== "none" ? cs2.scale : null;
3085
3385
  }
3086
3386
  const tr = safeGet("translate");
3087
- if (tr && tr.length) {
3088
- const tx = (_g = (_f = tr[0]) == null ? void 0 : _f.value) != null ? _g : 0;
3089
- const ty = (_i = (_h = tr[1]) == null ? void 0 : _h.value) != null ? _i : 0;
3090
- const ux = (_k = (_j = tr[0]) == null ? void 0 : _j.unit) != null ? _k : "px";
3091
- const uy = (_m = (_l = tr[1]) == null ? void 0 : _l.unit) != null ? _m : "px";
3387
+ if (tr) {
3388
+ const tx = "x" in tr && "value" in tr.x ? tr.x.value : Array.isArray(tr) ? (_e = tr[0]) == null ? void 0 : _e.value : 0;
3389
+ const ty = "y" in tr && "value" in tr.y ? tr.y.value : Array.isArray(tr) ? (_f = tr[1]) == null ? void 0 : _f.value : 0;
3390
+ const ux = "x" in tr && ((_g = tr.x) == null ? void 0 : _g.unit) ? tr.x.unit : "px";
3391
+ const uy = "y" in tr && ((_h = tr.y) == null ? void 0 : _h.unit) ? tr.y.unit : "px";
3092
3392
  out.translate = `${tx}${ux} ${ty}${uy}`;
3393
+ } else {
3394
+ const cs2 = getComputedStyle(el);
3395
+ out.translate = cs2.translate && cs2.translate !== "none" ? cs2.translate : null;
3093
3396
  }
3094
3397
  return out;
3095
3398
  }
@@ -3100,17 +3403,33 @@
3100
3403
  return out;
3101
3404
  }
3102
3405
  function hasBBoxAffectingTransform(el) {
3103
- const t = getComputedStyle(el).transform || "none";
3104
- if (t === "none" || t === "matrix(1, 0, 0, 1, 0, 0)") return false;
3105
- if (/^matrix\(\s*1\s*,\s*0\s*,\s*0\s*,\s*1\s*,/i.test(t)) return false;
3106
- return true;
3107
- }
3108
- function inflateRect(r, p) {
3109
- const x = Math.floor(r.left - p.left);
3110
- const y = Math.floor(r.top - p.top);
3111
- const w = Math.ceil(r.width + p.left + p.right);
3112
- const h = Math.ceil(r.height + p.top + p.bottom);
3113
- return { x, y, width: w, height: h };
3406
+ const cs = getComputedStyle(el);
3407
+ const t = cs.transform || "none";
3408
+ 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);
3409
+ if (hasMatrix) return true;
3410
+ const r = cs.rotate && cs.rotate !== "none" && cs.rotate !== "0deg";
3411
+ const s = cs.scale && cs.scale !== "none" && cs.scale !== "1";
3412
+ const tr = cs.translate && cs.translate !== "none" && cs.translate !== "0px 0px";
3413
+ return Boolean(r || s || tr);
3414
+ }
3415
+ function parseTransformOriginPx(cs, w, h) {
3416
+ const raw = (cs.transformOrigin || "0 0").trim().split(/\s+/);
3417
+ const [oxRaw, oyRaw] = [raw[0] || "0", raw[1] || "0"];
3418
+ const toPx = (token, size) => {
3419
+ const t = token.toLowerCase();
3420
+ if (t === "left" || t === "top") return 0;
3421
+ if (t === "center") return size / 2;
3422
+ if (t === "right") return size;
3423
+ if (t === "bottom") return size;
3424
+ if (t.endsWith("px")) return parseFloat(t) || 0;
3425
+ if (t.endsWith("%")) return (parseFloat(t) || 0) * size / 100;
3426
+ if (/^-?\d+(\.\d+)?$/.test(t)) return parseFloat(t) || 0;
3427
+ return 0;
3428
+ };
3429
+ return {
3430
+ ox: toPx(oxRaw, w),
3431
+ oy: toPx(oyRaw, h)
3432
+ };
3114
3433
  }
3115
3434
  var __measureHost = null;
3116
3435
  function getMeasureHost() {
@@ -3199,41 +3518,201 @@
3199
3518
  };
3200
3519
  }
3201
3520
 
3202
- // src/exporters/toImg.js
3203
- function toImg(url, options) {
3521
+ // src/exporters/toCanvas.js
3522
+ function isSvgDataURL(u) {
3523
+ return typeof u === "string" && /^data:image\/svg\+xml/i.test(u);
3524
+ }
3525
+ function decodeSvgFromDataURL(u) {
3526
+ const i = u.indexOf(",");
3527
+ return i >= 0 ? decodeURIComponent(u.slice(i + 1)) : "";
3528
+ }
3529
+ function encodeSvgToDataURL(svgText) {
3530
+ return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgText)}`;
3531
+ }
3532
+ function splitDecls(s) {
3533
+ let parts = [], buf = "", depth = 0;
3534
+ for (let i = 0; i < s.length; i++) {
3535
+ const ch = s[i];
3536
+ if (ch === "(") depth++;
3537
+ if (ch === ")") depth = Math.max(0, depth - 1);
3538
+ if (ch === ";" && depth === 0) {
3539
+ parts.push(buf);
3540
+ buf = "";
3541
+ } else buf += ch;
3542
+ }
3543
+ if (buf.trim()) parts.push(buf);
3544
+ return parts.map((x) => x.trim()).filter(Boolean);
3545
+ }
3546
+ function boxShadowToDropShadow(value) {
3547
+ const layers = [];
3548
+ let buf = "", depth = 0;
3549
+ for (let i = 0; i < value.length; i++) {
3550
+ const ch = value[i];
3551
+ if (ch === "(") depth++;
3552
+ if (ch === ")") depth = Math.max(0, depth - 1);
3553
+ if (ch === "," && depth === 0) {
3554
+ layers.push(buf.trim());
3555
+ buf = "";
3556
+ } else buf += ch;
3557
+ }
3558
+ if (buf.trim()) layers.push(buf.trim());
3559
+ const fns = [];
3560
+ for (const layer of layers) {
3561
+ if (/\binset\b/i.test(layer)) continue;
3562
+ const nums = layer.match(/-?\d+(?:\.\d+)?px/gi) || [];
3563
+ const [ox = "0px", oy = "0px", blur = "0px"] = nums;
3564
+ let color = layer.replace(/-?\d+(?:\.\d+)?px/gi, "").replace(/\binset\b/ig, "").trim().replace(/\s{2,}/g, " ");
3565
+ const hasColor = !!color && color !== ",";
3566
+ fns.push(`drop-shadow(${ox} ${oy} ${blur}${hasColor ? ` ${color}` : ""})`);
3567
+ }
3568
+ return fns.join(" ");
3569
+ }
3570
+ function rewriteDeclList(list) {
3571
+ const decls = splitDecls(list);
3572
+ let filter = null, wfilter = null, box = null;
3573
+ const rest = [];
3574
+ for (const d of decls) {
3575
+ const idx = d.indexOf(":");
3576
+ if (idx < 0) continue;
3577
+ const prop = d.slice(0, idx).trim().toLowerCase();
3578
+ const val = d.slice(idx + 1).trim();
3579
+ if (prop === "box-shadow") box = val;
3580
+ else if (prop === "filter") filter = val;
3581
+ else if (prop === "-webkit-filter") wfilter = val;
3582
+ else rest.push([prop, val]);
3583
+ }
3584
+ if (box) {
3585
+ const ds = boxShadowToDropShadow(box);
3586
+ if (ds) {
3587
+ filter = filter ? `${filter} ${ds}` : ds;
3588
+ wfilter = wfilter ? `${wfilter} ${ds}` : ds;
3589
+ }
3590
+ }
3591
+ const out = [...rest];
3592
+ if (filter) out.push(["filter", filter]);
3593
+ if (wfilter) out.push(["-webkit-filter", wfilter]);
3594
+ return out.map(([k, v]) => `${k}:${v}`).join(";");
3595
+ }
3596
+ function rewriteCssBlock(css) {
3597
+ return css.replace(/([^{}]+)\{([^}]*)\}/g, (_m, sel, body) => `${sel}{${rewriteDeclList(body)}}`);
3598
+ }
3599
+ function rewriteSvgBoxShadowToDropShadow(svgText) {
3600
+ svgText = svgText.replace(
3601
+ /<style[^>]*>([\s\S]*?)<\/style>/gi,
3602
+ (m, css) => m.replace(css, rewriteCssBlock(css))
3603
+ );
3604
+ svgText = svgText.replace(
3605
+ /style=(['"])([\s\S]*?)\1/gi,
3606
+ (m, q, body) => `style=${q}${rewriteDeclList(body)}${q}`
3607
+ );
3608
+ return svgText;
3609
+ }
3610
+ function maybeConvertBoxShadowForSafari(url) {
3611
+ if (!isSafari() || !isSvgDataURL(url)) return url;
3612
+ try {
3613
+ const svg = decodeSvgFromDataURL(url);
3614
+ const fixed = rewriteSvgBoxShadowToDropShadow(svg);
3615
+ return encodeSvgToDataURL(fixed);
3616
+ } catch (e) {
3617
+ return url;
3618
+ }
3619
+ }
3620
+ function toCanvas(url, options) {
3204
3621
  return __async(this, null, function* () {
3622
+ let { width: optW, height: optH, scale = 1, dpr = 1, meta = {} } = options;
3623
+ url = maybeConvertBoxShadowForSafari(url);
3205
3624
  const img = new Image();
3625
+ img.loading = "eager";
3626
+ img.decoding = "sync";
3627
+ img.crossOrigin = "anonymous";
3206
3628
  img.src = url;
3207
3629
  yield img.decode();
3208
- if (options.scale !== 1) {
3209
- img.style.width = `${img.naturalWidth * options.scale}px`;
3210
- img.style.height = `${img.naturalHeight * options.scale}px`;
3630
+ const natW = img.naturalWidth;
3631
+ const natH = img.naturalHeight;
3632
+ const refW = Number.isFinite(meta.w0) ? meta.w0 : natW;
3633
+ const refH = Number.isFinite(meta.h0) ? meta.h0 : natH;
3634
+ let outW, outH;
3635
+ const hasW = Number.isFinite(optW);
3636
+ const hasH = Number.isFinite(optH);
3637
+ if (hasW && hasH) {
3638
+ outW = Math.max(1, optW);
3639
+ outH = Math.max(1, optH);
3640
+ } else if (hasW) {
3641
+ const k = optW / Math.max(1, refW);
3642
+ outW = optW;
3643
+ outH = Math.round(refH * k);
3644
+ } else if (hasH) {
3645
+ const k = optH / Math.max(1, refH);
3646
+ outH = optH;
3647
+ outW = Math.round(refW * k);
3648
+ } else {
3649
+ outW = natW;
3650
+ outH = natH;
3211
3651
  }
3652
+ outW = Math.round(outW * scale);
3653
+ outH = Math.round(outH * scale);
3654
+ const canvas = document.createElement("canvas");
3655
+ canvas.width = Math.ceil(outW * dpr);
3656
+ canvas.height = Math.ceil(outH * dpr);
3657
+ canvas.style.width = `${outW}px`;
3658
+ canvas.style.height = `${outH}px`;
3659
+ const ctx = canvas.getContext("2d");
3660
+ if (dpr !== 1) ctx.scale(dpr, dpr);
3661
+ ctx.drawImage(img, 0, 0, outW, outH);
3662
+ return canvas;
3663
+ });
3664
+ }
3665
+
3666
+ // src/modules/rasterize.js
3667
+ function rasterize(url, options) {
3668
+ return __async(this, null, function* () {
3669
+ const canvas = yield toCanvas(url, options);
3670
+ const finalCanvas = options.backgroundColor ? createBackground(canvas, options.backgroundColor) : canvas;
3671
+ const img = new Image();
3672
+ img.src = finalCanvas.toDataURL(`image/${options.format}`, options.quality);
3673
+ yield img.decode();
3674
+ img.style.width = `${finalCanvas.width / options.dpr}px`;
3675
+ img.style.height = `${finalCanvas.height / options.dpr}px`;
3212
3676
  return img;
3213
3677
  });
3214
3678
  }
3215
3679
 
3216
- // src/exporters/toCanvas.js
3217
- function toCanvas(url, options) {
3680
+ // src/exporters/toImg.js
3681
+ function toImg(url, options) {
3218
3682
  return __async(this, null, function* () {
3219
- const { scale, dpr } = options;
3683
+ const { scale = 1, width, height, meta = {} } = options;
3684
+ const hasW = Number.isFinite(width);
3685
+ const hasH = Number.isFinite(height);
3686
+ const wantsScale = Number.isFinite(scale) && scale !== 1 || hasW || hasH;
3687
+ if (isSafari() && wantsScale) {
3688
+ const pngUrl = yield rasterize(url, __spreadProps(__spreadValues({}, options), { format: "png", quality: 1, meta }));
3689
+ return pngUrl;
3690
+ }
3220
3691
  const img = new Image();
3221
- img.crossOrigin = "anonymous";
3222
- img.loading = "eager";
3223
3692
  img.decoding = "sync";
3693
+ img.loading = "eager";
3224
3694
  img.src = url;
3225
3695
  yield img.decode();
3226
- const width = img.naturalWidth * scale;
3227
- const height = img.naturalHeight * scale;
3228
- const canvas = document.createElement("canvas");
3229
- canvas.width = Math.ceil(width * dpr);
3230
- canvas.height = Math.ceil(height * dpr);
3231
- canvas.style.width = `${width}px`;
3232
- canvas.style.height = `${height}px`;
3233
- const ctx = canvas.getContext("2d");
3234
- if (dpr !== 1) ctx.scale(dpr, dpr);
3235
- ctx.drawImage(img, 0, 0, width, height);
3236
- return canvas;
3696
+ if (hasW && hasH) {
3697
+ img.style.width = `${width}px`;
3698
+ img.style.height = `${height}px`;
3699
+ } else if (hasW) {
3700
+ const refW = Number.isFinite(meta.w0) ? meta.w0 : img.naturalWidth;
3701
+ const refH = Number.isFinite(meta.h0) ? meta.h0 : img.naturalHeight;
3702
+ const k = width / Math.max(1, refW);
3703
+ img.style.width = `${width}px`;
3704
+ img.style.height = `${Math.round(refH * k)}px`;
3705
+ } else if (hasH) {
3706
+ const refW = Number.isFinite(meta.w0) ? meta.w0 : img.naturalWidth;
3707
+ const refH = Number.isFinite(meta.h0) ? meta.h0 : img.naturalHeight;
3708
+ const k = height / Math.max(1, refH);
3709
+ img.style.height = `${height}px`;
3710
+ img.style.width = `${Math.round(refW * k)}px`;
3711
+ } else if (scale !== 1) {
3712
+ img.style.width = `${Math.round(img.naturalWidth * scale)}px`;
3713
+ img.style.height = `${Math.round(img.naturalHeight * scale)}px`;
3714
+ }
3715
+ return img;
3237
3716
  });
3238
3717
  }
3239
3718
 
@@ -3257,20 +3736,6 @@
3257
3736
  });
3258
3737
  }
3259
3738
 
3260
- // src/modules/rasterize.js
3261
- function rasterize(url, options) {
3262
- return __async(this, null, function* () {
3263
- const canvas = yield toCanvas(url, options);
3264
- const finalCanvas = options.backgroundColor ? createBackground(canvas, options.backgroundColor) : canvas;
3265
- const img = new Image();
3266
- img.src = finalCanvas.toDataURL(`image/${options.format}`, options.quality);
3267
- yield img.decode();
3268
- img.style.width = `${finalCanvas.width / options.dpr}px`;
3269
- img.style.height = `${finalCanvas.height / options.dpr}px`;
3270
- return img;
3271
- });
3272
- }
3273
-
3274
3739
  // src/exporters/download.js
3275
3740
  function download(url, options) {
3276
3741
  return __async(this, null, function* () {
@@ -3300,7 +3765,8 @@
3300
3765
  function snapdom(element, userOptions) {
3301
3766
  return __async(this, null, function* () {
3302
3767
  if (!element) throw new Error("Element cannot be null or undefined");
3303
- if (isSafari()) {
3768
+ const context = createContext(userOptions);
3769
+ if (isSafari() && (context.embedFonts === true || hasBackgroundOrMask(element))) {
3304
3770
  for (let i = 0; i < 3; i++) {
3305
3771
  try {
3306
3772
  yield safariWarmup(element, userOptions);
@@ -3310,7 +3776,6 @@
3310
3776
  }
3311
3777
  }
3312
3778
  }
3313
- const context = createContext(userOptions);
3314
3779
  if (context.iconFonts && context.iconFonts.length > 0) extendIconFonts(context.iconFonts);
3315
3780
  if (!context.snap) {
3316
3781
  context.snap = {
@@ -3392,6 +3857,20 @@
3392
3857
  });
3393
3858
  });
3394
3859
  }
3860
+ function hasBackgroundOrMask(el) {
3861
+ const walker = document.createTreeWalker(el, NodeFilter.SHOW_ELEMENT);
3862
+ while (walker.nextNode()) {
3863
+ const node = (
3864
+ /** @type {Element} */
3865
+ walker.currentNode
3866
+ );
3867
+ const cs = getComputedStyle(node);
3868
+ const bg = cs.backgroundImage && cs.backgroundImage !== "none";
3869
+ const mask = cs.maskImage && cs.maskImage !== "none" || cs.webkitMaskImage && cs.webkitMaskImage !== "none";
3870
+ if (bg || mask) return true;
3871
+ }
3872
+ return false;
3873
+ }
3395
3874
 
3396
3875
  // src/api/preCache.js
3397
3876
  function preCache() {