@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 CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  /*
3
3
  * snapdom
4
- * v.1.9.12-dev.1
4
+ * v.1.9.12-dev.3
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) {
@@ -757,6 +746,7 @@
757
746
  if (rec && rec.epoch === __epoch) return rec.snapshot;
758
747
  const style = preStyle || getComputedStyle(el);
759
748
  const snap = snapshotComputedStyleFull(style, options);
749
+ stripHeightForWrappers(el, style, snap);
760
750
  snapshotCache.set(el, { epoch: __epoch, snapshot: snap });
761
751
  return snap;
762
752
  }
@@ -823,6 +813,49 @@
823
813
  session.styleMap.set(clone, key);
824
814
  });
825
815
  }
816
+ function isReplaced(el) {
817
+ return el instanceof HTMLImageElement || el instanceof HTMLCanvasElement || el instanceof HTMLVideoElement || el instanceof HTMLIFrameElement || el instanceof SVGElement || el instanceof HTMLObjectElement || el instanceof HTMLEmbedElement;
818
+ }
819
+ function hasBox(cs) {
820
+ if (cs.backgroundImage && cs.backgroundImage !== "none") return true;
821
+ if (cs.backgroundColor && cs.backgroundColor !== "rgba(0, 0, 0, 0)" && cs.backgroundColor !== "transparent") return true;
822
+ if ((parseFloat(cs.borderTopWidth) || 0) > 0) return true;
823
+ if ((parseFloat(cs.borderBottomWidth) || 0) > 0) return true;
824
+ if ((parseFloat(cs.paddingTop) || 0) > 0) return true;
825
+ if ((parseFloat(cs.paddingBottom) || 0) > 0) return true;
826
+ const ob = cs.overflowBlock || cs.overflowY || "visible";
827
+ return ob !== "visible";
828
+ }
829
+ function isFlexOrGridItem(el) {
830
+ const p = el.parentElement;
831
+ if (!p) return false;
832
+ const pd = getComputedStyle(p).display || "";
833
+ return pd.includes("flex") || pd.includes("grid");
834
+ }
835
+ function hasFlowFast(el, cs) {
836
+ if (el.textContent && /\S/.test(el.textContent)) return true;
837
+ const f = el.firstElementChild, l = el.lastElementChild;
838
+ if (f && f.tagName === "BR" || l && l.tagName === "BR") return true;
839
+ const sh = el.scrollHeight;
840
+ if (sh === 0) return false;
841
+ const pt = parseFloat(cs.paddingTop) || 0;
842
+ const pb = parseFloat(cs.paddingBottom) || 0;
843
+ return sh > pt + pb;
844
+ }
845
+ function stripHeightForWrappers(el, cs, snap) {
846
+ if (el instanceof HTMLElement && el.style && el.style.height) return;
847
+ const disp = cs.display || "";
848
+ if (disp.includes("flex") || disp.includes("grid")) return;
849
+ if (isReplaced(el)) return;
850
+ const pos = cs.position;
851
+ if (pos === "absolute" || pos === "fixed" || pos === "sticky") return;
852
+ if (cs.transform !== "none") return;
853
+ if (hasBox(cs)) return;
854
+ if (isFlexOrGridItem(el)) return;
855
+ if (!hasFlowFast(el, cs)) return;
856
+ delete snap.height;
857
+ delete snap["block-size"];
858
+ }
826
859
 
827
860
  // src/core/clone.js
828
861
  function idleCallback(childList, callback, fast) {
@@ -2128,11 +2161,21 @@
2128
2161
  const empty = /* @__PURE__ */ new Map();
2129
2162
  build(rootEl, empty, empty);
2130
2163
  return {
2164
+ /**
2165
+ * Get top value for counter name at given node.
2166
+ * @param {Element} node
2167
+ * @param {string} name
2168
+ */
2131
2169
  get(node, name) {
2132
2170
  var _a;
2133
2171
  const s = (_a = nodeCounters.get(node)) == null ? void 0 : _a.get(name);
2134
2172
  return s && s.length ? s[s.length - 1] : 0;
2135
2173
  },
2174
+ /**
2175
+ * Get full stack for counter name at given node.
2176
+ * @param {Element} node
2177
+ * @param {string} name
2178
+ */
2136
2179
  getStack(node, name) {
2137
2180
  var _a;
2138
2181
  const s = (_a = nodeCounters.get(node)) == null ? void 0 : _a.get(name);
@@ -2170,10 +2213,111 @@
2170
2213
 
2171
2214
  // src/modules/pseudo.js
2172
2215
  var counterCtx = null;
2216
+ var __siblingCounters = /* @__PURE__ */ new WeakMap();
2217
+ function unquoteDoubleStrings2(s) {
2218
+ return (s || "").replace(/"([^"]*)"/g, "$1");
2219
+ }
2220
+ function collapseCssContent(raw) {
2221
+ if (!raw) return "";
2222
+ const tokens = [];
2223
+ const rx = /"([^"]*)"/g;
2224
+ let m;
2225
+ while (m = rx.exec(raw)) tokens.push(m[1]);
2226
+ if (tokens.length) return tokens.join("");
2227
+ return unquoteDoubleStrings2(raw);
2228
+ }
2229
+ function withSiblingOverrides(node, base) {
2230
+ const parent = node.parentElement;
2231
+ const map = parent ? __siblingCounters.get(parent) : null;
2232
+ if (!map) return base;
2233
+ return {
2234
+ get(n, name) {
2235
+ const v = base.get(n, name);
2236
+ const ov = map.get(name);
2237
+ return typeof ov === "number" ? Math.max(v, ov) : v;
2238
+ },
2239
+ getStack(n, name) {
2240
+ const s = base.getStack(n, name);
2241
+ if (!s.length) return s;
2242
+ const ov = map.get(name);
2243
+ if (typeof ov === "number") {
2244
+ const out = s.slice();
2245
+ out[out.length - 1] = Math.max(out[out.length - 1], ov);
2246
+ return out;
2247
+ }
2248
+ return s;
2249
+ }
2250
+ };
2251
+ }
2252
+ function deriveCounterCtxForPseudo(node, pseudoStyle, baseCtx) {
2253
+ const modStacks = /* @__PURE__ */ new Map();
2254
+ function parseListDecl(value) {
2255
+ const out = [];
2256
+ if (!value || value === "none") return out;
2257
+ for (const part of String(value).split(",")) {
2258
+ const toks = part.trim().split(/\s+/);
2259
+ const name = toks[0];
2260
+ const num = Number.isFinite(Number(toks[1])) ? Number(toks[1]) : void 0;
2261
+ if (name) out.push({ name, num });
2262
+ }
2263
+ return out;
2264
+ }
2265
+ const resets = parseListDecl(pseudoStyle == null ? void 0 : pseudoStyle.counterReset);
2266
+ const incs = parseListDecl(pseudoStyle == null ? void 0 : pseudoStyle.counterIncrement);
2267
+ function getStackDerived(name) {
2268
+ if (modStacks.has(name)) return modStacks.get(name).slice();
2269
+ let stack = baseCtx.getStack(node, name);
2270
+ stack = stack.length ? stack.slice() : [];
2271
+ const r = resets.find((x) => x.name === name);
2272
+ if (r) {
2273
+ const val = Number.isFinite(r.num) ? r.num : 0;
2274
+ stack = stack.length ? [...stack, val] : [val];
2275
+ }
2276
+ const inc = incs.find((x) => x.name === name);
2277
+ if (inc) {
2278
+ const by = Number.isFinite(inc.num) ? inc.num : 1;
2279
+ if (stack.length === 0) stack = [0];
2280
+ stack[stack.length - 1] += by;
2281
+ }
2282
+ modStacks.set(name, stack.slice());
2283
+ return stack;
2284
+ }
2285
+ return {
2286
+ get(_node, name) {
2287
+ const s = getStackDerived(name);
2288
+ return s.length ? s[s.length - 1] : 0;
2289
+ },
2290
+ getStack(_node, name) {
2291
+ return getStackDerived(name);
2292
+ },
2293
+ /** expone increments del pseudo para que el caller pueda propagar a hermanos */
2294
+ __incs: incs
2295
+ };
2296
+ }
2297
+ function resolvePseudoContentAndIncs(node, pseudo, baseCtx) {
2298
+ let ps;
2299
+ try {
2300
+ ps = getComputedStyle(node, pseudo);
2301
+ } catch (e) {
2302
+ }
2303
+ const raw = ps == null ? void 0 : ps.content;
2304
+ if (!raw || raw === "none" || raw === "normal") return { text: "", incs: [] };
2305
+ const baseWithSiblings = withSiblingOverrides(node, baseCtx);
2306
+ const derived = deriveCounterCtxForPseudo(node, ps, baseWithSiblings);
2307
+ let resolved = hasCounters(raw) ? resolveCountersInContent(raw, node, derived) : raw;
2308
+ const text = collapseCssContent(resolved);
2309
+ return { text, incs: derived.__incs || [] };
2310
+ }
2173
2311
  function inlinePseudoElements(source, clone, sessionCache, options) {
2174
2312
  return __async(this, null, function* () {
2175
2313
  var _a;
2176
2314
  if (!(source instanceof Element) || !(clone instanceof Element)) return;
2315
+ if (!counterCtx) {
2316
+ try {
2317
+ counterCtx = buildCounterContext(source.ownerDocument || document);
2318
+ } catch (e) {
2319
+ }
2320
+ }
2177
2321
  for (const pseudo of ["::before", "::after", "::first-letter"]) {
2178
2322
  try {
2179
2323
  const style = getStyle(source, pseudo);
@@ -2207,12 +2351,8 @@
2207
2351
  clone.insertBefore(span, restNode);
2208
2352
  continue;
2209
2353
  }
2210
- const content = style.content;
2211
- let cleanContent = parseContent(content);
2212
- if (hasCounters(content)) {
2213
- counterCtx = buildCounterContext(source.ownerDocument || document);
2214
- cleanContent = resolveCountersInContent(cleanContent, source, counterCtx);
2215
- }
2354
+ const rawContent = style.content;
2355
+ const { text: cleanContent, incs } = resolvePseudoContentAndIncs(source, pseudo, counterCtx);
2216
2356
  const bg = style.backgroundImage;
2217
2357
  const bgColor = style.backgroundColor;
2218
2358
  const fontFamily = style.fontFamily;
@@ -2226,28 +2366,41 @@
2226
2366
  const borderWidth = parseFloat(style.borderWidth);
2227
2367
  const transform = style.transform;
2228
2368
  const isIconFont22 = isIconFont2(fontFamily);
2229
- const hasExplicitContent = content !== "none" && cleanContent !== "";
2369
+ const hasExplicitContent = rawContent !== "none" && cleanContent !== "";
2230
2370
  const hasBg = bg && bg !== "none";
2231
2371
  const hasBgColor = bgColor && bgColor !== "transparent" && bgColor !== "rgba(0, 0, 0, 0)";
2232
- const hasBox = display !== "inline" && (width > 0 || height > 0);
2372
+ const hasBox2 = display !== "inline" && (width > 0 || height > 0);
2233
2373
  const hasBorder = borderStyle && borderStyle !== "none" && borderWidth > 0;
2234
2374
  const hasTransform = transform && transform !== "none";
2235
- const shouldRender = hasExplicitContent || hasBg || hasBgColor || hasBox || hasBorder || hasTransform;
2236
- if (!shouldRender) continue;
2375
+ const shouldRender = hasExplicitContent || hasBg || hasBgColor || hasBox2 || hasBorder || hasTransform;
2376
+ if (!shouldRender) {
2377
+ if (incs && incs.length && source.parentElement) {
2378
+ const map = __siblingCounters.get(source.parentElement) || /* @__PURE__ */ new Map();
2379
+ for (const { name, num } of incs) {
2380
+ if (!name) continue;
2381
+ const baseWithSibs = withSiblingOverrides(source, counterCtx);
2382
+ const derived = deriveCounterCtxForPseudo(source, getComputedStyle(source, pseudo), baseWithSibs);
2383
+ const finalVal = derived.get(source, name);
2384
+ map.set(name, finalVal);
2385
+ }
2386
+ __siblingCounters.set(source.parentElement, map);
2387
+ }
2388
+ continue;
2389
+ }
2237
2390
  const pseudoEl = document.createElement("span");
2238
2391
  pseudoEl.dataset.snapdomPseudo = pseudo;
2239
2392
  pseudoEl.style.verticalAlign = "middle";
2240
2393
  const snapshot = snapshotComputedStyle(style);
2241
2394
  const key = getStyleKey(snapshot, "span");
2242
2395
  sessionCache.styleMap.set(pseudoEl, key);
2243
- if (isIconFont22 && cleanContent.length === 1) {
2244
- const { dataUrl, width: width2, height: height2 } = yield iconToImage(cleanContent, fontFamily, fontWeight, fontSize, color);
2396
+ if (isIconFont22 && cleanContent && cleanContent.length === 1) {
2397
+ const { dataUrl, width: w, height: h } = yield iconToImage(cleanContent, fontFamily, fontWeight, fontSize, color);
2245
2398
  const imgEl = document.createElement("img");
2246
2399
  imgEl.src = dataUrl;
2247
- imgEl.style = `height:${fontSize}px;width:${width2 / height2 * fontSize}px;object-fit:contain;`;
2400
+ imgEl.style = `height:${fontSize}px;width:${w / h * fontSize}px;object-fit:contain;`;
2248
2401
  pseudoEl.appendChild(imgEl);
2249
2402
  clone.dataset.snapdomHasIcon = "true";
2250
- } else if (cleanContent.startsWith("url(")) {
2403
+ } else if (cleanContent && cleanContent.startsWith("url(")) {
2251
2404
  const rawUrl = extractURL(cleanContent);
2252
2405
  if (rawUrl == null ? void 0 : rawUrl.trim()) {
2253
2406
  try {
@@ -2274,7 +2427,18 @@
2274
2427
  }
2275
2428
  if (hasBgColor) pseudoEl.style.backgroundColor = bgColor;
2276
2429
  const hasContent2 = pseudoEl.childNodes.length > 0 || ((_a = pseudoEl.textContent) == null ? void 0 : _a.trim()) !== "";
2277
- const hasVisibleBox = hasContent2 || hasBg || hasBgColor || hasBox || hasBorder || hasTransform;
2430
+ const hasVisibleBox = hasContent2 || hasBg || hasBgColor || hasBox2 || hasBorder || hasTransform;
2431
+ if (incs && incs.length && source.parentElement) {
2432
+ const map = __siblingCounters.get(source.parentElement) || /* @__PURE__ */ new Map();
2433
+ const baseWithSibs = withSiblingOverrides(source, counterCtx);
2434
+ const derived = deriveCounterCtxForPseudo(source, getComputedStyle(source, pseudo), baseWithSibs);
2435
+ for (const { name } of incs) {
2436
+ if (!name) continue;
2437
+ const finalVal = derived.get(source, name);
2438
+ map.set(name, finalVal);
2439
+ }
2440
+ __siblingCounters.set(source.parentElement, map);
2441
+ }
2278
2442
  if (!hasVisibleBox) continue;
2279
2443
  if (pseudo === "::before") {
2280
2444
  clone.insertBefore(pseudoEl, clone.firstChild);
@@ -2424,12 +2588,13 @@
2424
2588
  sessionCache.styleCache.set(element, computed);
2425
2589
  const transform = stripTranslate(computed.transform);
2426
2590
  clone.style.margin = "0";
2427
- clone.style.position = "static";
2428
2591
  clone.style.top = "auto";
2429
2592
  clone.style.left = "auto";
2430
2593
  clone.style.right = "auto";
2431
2594
  clone.style.bottom = "auto";
2432
- clone.style.zIndex = "auto";
2595
+ clone.style.animation = "none";
2596
+ clone.style.transition = "none";
2597
+ clone.style.willChange = "auto";
2433
2598
  clone.style.float = "none";
2434
2599
  clone.style.clear = "none";
2435
2600
  clone.style.transform = transform || "";
@@ -2842,7 +3007,7 @@
2842
3007
  idle(() => __async(null, null, function* () {
2843
3008
  const required = collectUsedFontVariants(element);
2844
3009
  const usedCodepoints = collectUsedCodepoints(element);
2845
- if (isSafari) {
3010
+ if (isSafari()) {
2846
3011
  const families = new Set(
2847
3012
  Array.from(required).map((k) => String(k).split("__")[0]).filter(Boolean)
2848
3013
  );
@@ -2874,102 +3039,188 @@
2874
3039
  }
2875
3040
  yield new Promise((resolve) => {
2876
3041
  idle(() => {
2877
- var _a, _b, _c, _d, _e;
2878
- const rect = element.getBoundingClientRect();
2879
- let w = Number(rect.width) || 0;
2880
- let h = Number(rect.height) || 0;
2881
- const hasW = Number.isFinite(options.width);
2882
- const hasH = Number.isFinite(options.height);
2883
- const scaleOpt = (_a = options.scale) != null ? _a : 1;
2884
- const hasScale = typeof scaleOpt === "number" && scaleOpt !== 1;
2885
- if (!hasScale) {
2886
- const aspect = h > 0 ? w / h : 1;
2887
- if (hasW && hasH) {
2888
- w = Number(options.width);
2889
- h = Number(options.height);
2890
- } else if (hasW) {
2891
- w = Number(options.width);
2892
- h = w / (aspect || 1);
2893
- } else if (hasH) {
2894
- h = Number(options.height);
2895
- w = h * (aspect || 1);
3042
+ const csEl = getComputedStyle(element);
3043
+ function parseFilterDropShadows(cs) {
3044
+ var _a;
3045
+ const raw = `${cs.filter || ""} ${cs.webkitFilter || ""}`.trim();
3046
+ if (!raw || raw === "none") {
3047
+ return { bleed: { top: 0, right: 0, bottom: 0, left: 0 }, has: false };
3048
+ }
3049
+ const tokens = raw.match(/drop-shadow\((?:[^()]|\([^()]*\))*\)/gi) || [];
3050
+ let t = 0, r = 0, b = 0, l = 0;
3051
+ let found = false;
3052
+ for (const tok of tokens) {
3053
+ found = true;
3054
+ const nums = ((_a = tok.match(/-?\d+(?:\.\d+)?px/gi)) == null ? void 0 : _a.map((v) => parseFloat(v))) || [];
3055
+ const [ox = 0, oy = 0, blur = 0] = nums;
3056
+ const extX = Math.abs(ox) + blur;
3057
+ const extY = Math.abs(oy) + blur;
3058
+ r = Math.max(r, extX + Math.max(ox, 0));
3059
+ l = Math.max(l, extX + Math.max(-ox, 0));
3060
+ b = Math.max(b, extY + Math.max(oy, 0));
3061
+ t = Math.max(t, extY + Math.max(-oy, 0));
2896
3062
  }
3063
+ return {
3064
+ bleed: { top: Math.ceil(t), right: Math.ceil(r), bottom: Math.ceil(b), left: Math.ceil(l) },
3065
+ has: found
3066
+ };
2897
3067
  }
2898
- w = Math.max(1, Math.ceil(Number.isFinite(w) ? w : 1));
2899
- h = Math.max(1, Math.ceil(Number.isFinite(h) ? h : 1));
2900
- const csEl = getComputedStyle(element);
2901
- const baseTransform = csEl.transform && csEl.transform !== "none" ? csEl.transform : "";
2902
- const ind = readIndividualTransforms(element);
2903
- let outW, outH, tx, ty, cancelTranslateX = 0, cancelTranslateY = 0;
2904
- if (!hasBBoxAffectingTransform(element)) {
2905
- const r = element.getBoundingClientRect();
2906
- const vp = inflateRect(r, { top: 0, right: 0, bottom: 0, left: 0 });
2907
- outW = Math.max(1, vp.width);
2908
- outH = Math.max(1, vp.height);
2909
- tx = -(vp.x - Math.floor(r.left));
2910
- ty = -(vp.y - Math.floor(r.top));
3068
+ function parseBoxShadow(cs) {
3069
+ var _a;
3070
+ const v = cs.boxShadow || "";
3071
+ if (!v || v === "none") return { top: 0, right: 0, bottom: 0, left: 0 };
3072
+ const parts = v.split(/\),(?=(?:[^()]*\([^()]*\))*[^()]*$)/).map((s) => s.trim());
3073
+ let t = 0, r = 0, b2 = 0, l = 0;
3074
+ for (const part of parts) {
3075
+ const nums = ((_a = part.match(/-?\d+(\.\d+)?px/g)) == null ? void 0 : _a.map((n) => parseFloat(n))) || [];
3076
+ if (nums.length < 2) continue;
3077
+ const [ox2, oy2, blur = 0, spread = 0] = nums;
3078
+ const extX = Math.abs(ox2) + blur + spread;
3079
+ const extY = Math.abs(oy2) + blur + spread;
3080
+ r = Math.max(r, extX + Math.max(ox2, 0));
3081
+ l = Math.max(l, extX + Math.max(-ox2, 0));
3082
+ b2 = Math.max(b2, extY + Math.max(oy2, 0));
3083
+ t = Math.max(t, extY + Math.max(-oy2, 0));
3084
+ }
3085
+ return { top: Math.ceil(t), right: Math.ceil(r), bottom: Math.ceil(b2), left: Math.ceil(l) };
3086
+ }
3087
+ function parseFilterBlur(cs) {
3088
+ const m = (cs.filter || "").match(/blur\(\s*([0-9.]+)px\s*\)/);
3089
+ const b2 = m ? Math.ceil(parseFloat(m[1]) || 0) : 0;
3090
+ return { top: b2, right: b2, bottom: b2, left: b2 };
3091
+ }
3092
+ function parseOutline(cs) {
3093
+ if ((cs.outlineStyle || "none") === "none") return { top: 0, right: 0, bottom: 0, left: 0 };
3094
+ const w2 = Math.ceil(parseFloat(cs.outlineWidth || "0") || 0);
3095
+ return { top: w2, right: w2, bottom: w2, left: w2 };
3096
+ }
3097
+ function bboxWithOriginFull(w2, h2, M, ox2, oy2) {
3098
+ const a2 = M.a, b2 = M.b, c2 = M.c, d2 = M.d, e2 = M.e || 0, f2 = M.f || 0;
3099
+ function pt(x, y) {
3100
+ let X = x - ox2, Y = y - oy2;
3101
+ let X2 = a2 * X + c2 * Y, Y2 = b2 * X + d2 * Y;
3102
+ X2 += ox2 + e2;
3103
+ Y2 += oy2 + f2;
3104
+ return [X2, Y2];
3105
+ }
3106
+ const P = [pt(0, 0), pt(w2, 0), pt(0, h2), pt(w2, h2)];
3107
+ let minX2 = Infinity, minY2 = Infinity, maxX2 = -Infinity, maxY2 = -Infinity;
3108
+ for (const [X, Y] of P) {
3109
+ if (X < minX2) minX2 = X;
3110
+ if (Y < minY2) minY2 = Y;
3111
+ if (X > maxX2) maxX2 = X;
3112
+ if (Y > maxY2) maxY2 = Y;
3113
+ }
3114
+ return { minX: minX2, minY: minY2, maxX: maxX2, maxY: maxY2, width: maxX2 - minX2, height: maxY2 - minY2 };
3115
+ }
3116
+ function hasTFBBox(el) {
3117
+ return hasBBoxAffectingTransform(el);
3118
+ }
3119
+ const rect = element.getBoundingClientRect();
3120
+ const w0 = Math.max(1, Math.ceil(element.offsetWidth || parseFloat(csEl.width) || rect.width || 1));
3121
+ const h0 = Math.max(1, Math.ceil(element.offsetHeight || parseFloat(csEl.height) || rect.height || 1));
3122
+ const coerceNum = (v, def = NaN) => {
3123
+ const n = typeof v === "string" ? parseFloat(v) : v;
3124
+ return Number.isFinite(n) ? n : def;
3125
+ };
3126
+ const optW = coerceNum(options.width);
3127
+ const optH = coerceNum(options.height);
3128
+ let w = w0, h = h0;
3129
+ const hasW = Number.isFinite(optW);
3130
+ const hasH = Number.isFinite(optH);
3131
+ const aspect0 = h0 > 0 ? w0 / h0 : 1;
3132
+ if (hasW && hasH) {
3133
+ w = Math.max(1, Math.ceil(optW));
3134
+ h = Math.max(1, Math.ceil(optH));
3135
+ } else if (hasW) {
3136
+ w = Math.max(1, Math.ceil(optW));
3137
+ h = Math.max(1, Math.ceil(w / (aspect0 || 1)));
3138
+ } else if (hasH) {
3139
+ h = Math.max(1, Math.ceil(optH));
3140
+ w = Math.max(1, Math.ceil(h * (aspect0 || 1)));
2911
3141
  } else {
3142
+ w = w0;
3143
+ h = h0;
3144
+ }
3145
+ let minX = 0, minY = 0, maxX = w0, maxY = h0;
3146
+ if (hasTFBBox(element)) {
3147
+ const baseTransform2 = csEl.transform && csEl.transform !== "none" ? csEl.transform : "";
3148
+ const ind2 = readIndividualTransforms(element);
2912
3149
  const TOTAL = readTotalTransformMatrix({
2913
- baseTransform,
2914
- rotate: ind.rotate || "0deg",
2915
- scale: ind.scale,
2916
- translate: ind.translate
3150
+ baseTransform: baseTransform2,
3151
+ rotate: ind2.rotate || "0deg",
3152
+ scale: ind2.scale,
3153
+ translate: ind2.translate
2917
3154
  });
2918
- const e = (_c = (_b = TOTAL.e) != null ? _b : TOTAL.m41) != null ? _c : 0;
2919
- const f = (_e = (_d = TOTAL.f) != null ? _d : TOTAL.m42) != null ? _e : 0;
2920
- cancelTranslateX = -e;
2921
- cancelTranslateY = -f;
2922
- const M2D = TOTAL.is2D ? matrix2DNoTranslate(TOTAL) : matrix2DNoTranslate(new DOMMatrix(TOTAL.toString()));
2923
- const bb = bboxFromMatrix(w, h, M2D);
2924
- outW = Math.max(1, Math.ceil(bb.width));
2925
- outH = Math.max(1, Math.ceil(bb.height));
2926
- tx = -bb.minX;
2927
- ty = -bb.minY;
3155
+ const { ox: ox2, oy: oy2 } = parseTransformOriginPx(csEl, w0, h0);
3156
+ const M = TOTAL.is2D ? TOTAL : new DOMMatrix(TOTAL.toString());
3157
+ const bb = bboxWithOriginFull(w0, h0, M, ox2, oy2);
3158
+ minX = bb.minX;
3159
+ minY = bb.minY;
3160
+ maxX = bb.maxX;
3161
+ maxY = bb.maxY;
2928
3162
  }
3163
+ const bleedShadow = parseBoxShadow(csEl);
3164
+ const bleedBlur = parseFilterBlur(csEl);
3165
+ const bleedOutline = parseOutline(csEl);
3166
+ const drop = parseFilterDropShadows(csEl);
3167
+ const bleed = {
3168
+ top: bleedShadow.top + bleedBlur.top + bleedOutline.top + drop.bleed.top,
3169
+ right: bleedShadow.right + bleedBlur.right + bleedOutline.right + drop.bleed.right,
3170
+ bottom: bleedShadow.bottom + bleedBlur.bottom + bleedOutline.bottom + drop.bleed.bottom,
3171
+ left: bleedShadow.left + bleedBlur.left + bleedOutline.left + drop.bleed.left
3172
+ };
3173
+ minX -= bleed.left;
3174
+ minY -= bleed.top;
3175
+ maxX += bleed.right;
3176
+ maxY += bleed.bottom;
3177
+ const vbW0 = Math.max(1, Math.ceil(maxX - minX));
3178
+ const vbH0 = Math.max(1, Math.ceil(maxY - minY));
3179
+ const outW = Math.max(1, Math.round(vbW0 * (hasW || hasH ? w / w0 : 1)));
3180
+ const outH = Math.max(1, Math.round(vbH0 * (hasH || hasW ? h / h0 : 1)));
2929
3181
  const svgNS = "http://www.w3.org/2000/svg";
3182
+ const pad = isSafari() ? 1 : 0;
2930
3183
  const fo = document.createElementNS(svgNS, "foreignObject");
2931
- fo.setAttribute("width", "100%");
2932
- fo.setAttribute("height", "100%");
2933
- fo.setAttribute("x", String(tx));
2934
- fo.setAttribute("y", String(ty));
3184
+ const vbMinX = Math.floor(minX);
3185
+ const vbMinY = Math.floor(minY);
3186
+ fo.setAttribute("x", String(-(vbMinX - pad)));
3187
+ fo.setAttribute("y", String(-(vbMinY - pad)));
3188
+ fo.setAttribute("width", String(Math.ceil(w0 + pad * 2)));
3189
+ fo.setAttribute("height", String(Math.ceil(h0 + pad * 2)));
2935
3190
  fo.style.overflow = "visible";
2936
3191
  const styleTag = document.createElement("style");
2937
3192
  styleTag.textContent = baseCSS + fontsCSS + "svg{overflow:visible;} foreignObject{overflow:visible;}" + classCSS;
2938
3193
  fo.appendChild(styleTag);
2939
3194
  const container = document.createElement("div");
2940
3195
  container.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
2941
- container.style.width = `${w}px`;
2942
- container.style.height = `${h}px`;
3196
+ container.style.width = `${w0}px`;
3197
+ container.style.height = `${h0}px`;
2943
3198
  container.style.overflow = "visible";
2944
- container.style.transformOrigin = "0 0";
2945
- const cancels = [];
2946
- if (cancelTranslateX || cancelTranslateY) {
2947
- cancels.push(`translate(${cancelTranslateX}px, ${cancelTranslateY}px)`);
2948
- }
2949
- if (!hasScale && (hasW || hasH)) {
2950
- const sxWH = rect.width ? w / rect.width : 1;
2951
- const syWH = rect.height ? h / rect.height : 1;
2952
- cancels.push(`scale(${sxWH}, ${syWH})`);
2953
- } else if (hasScale) {
2954
- cancels.push(`scale(${scaleOpt})`);
2955
- }
2956
- if (cancels.length) container.style.transform = cancels.join(" ");
2957
3199
  clone.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
2958
- clone.style.transformOrigin = "0 0";
2959
- clone.style.position = "static";
2960
- clone.style.left = "0";
2961
- clone.style.top = "0";
2962
- if (baseTransform) clone.style.transform = baseTransform;
2963
- clone.style.rotate = ind.rotate || "0deg";
2964
- clone.style.scale = ind.scale || "1";
2965
- clone.style.translate = ind.translate || "0 0";
2966
3200
  container.appendChild(clone);
2967
3201
  fo.appendChild(container);
2968
3202
  const serializer = new XMLSerializer();
2969
3203
  const foString = serializer.serializeToString(fo);
2970
- const finalW = !hasScale && (hasW || hasH) ? w : outW;
2971
- const finalH = !hasScale && (hasH || hasW) ? h : outH;
2972
- const svgHeader = `<svg xmlns="${svgNS}" width="${finalW}" height="${finalH}" viewBox="0 0 ${outW} ${outH}">`;
3204
+ const vbW = vbW0 + pad * 2;
3205
+ const vbH = vbH0 + pad * 2;
3206
+ const wantsSize = hasW || hasH;
3207
+ options.meta = {
3208
+ w0,
3209
+ // ancho natural del elemento
3210
+ h0,
3211
+ // alto natural
3212
+ vbW,
3213
+ // ancho del viewBox
3214
+ vbH,
3215
+ // alto del viewBox
3216
+ targetW: w,
3217
+ // ancho deseado según options.width
3218
+ targetH: h
3219
+ // alto deseado según options.height
3220
+ };
3221
+ const svgOutW = isSafari() && wantsSize ? vbW : outW + pad * 2;
3222
+ const svgOutH = isSafari() && wantsSize ? vbH : outH + pad * 2;
3223
+ const svgHeader = `<svg xmlns="${svgNS}" width="${svgOutW}" height="${svgOutH}" viewBox="0 0 ${vbW} ${vbH}">`;
2973
3224
  const svgFooter = "</svg>";
2974
3225
  svgString = svgHeader + foString + svgFooter;
2975
3226
  dataURL = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgString)}`;
@@ -2990,28 +3241,8 @@
2990
3241
  return new WebKitCSSMatrix(tr);
2991
3242
  }
2992
3243
  }
2993
- function matrix2DNoTranslate(M) {
2994
- return new DOMMatrix([M.a, M.b, M.c, M.d, 0, 0]);
2995
- }
2996
- function bboxFromMatrix(w, h, M2D) {
2997
- const pts = [
2998
- { x: 0, y: 0 },
2999
- { x: w, y: 0 },
3000
- { x: 0, y: h },
3001
- { x: w, y: h }
3002
- ];
3003
- const t = pts.map((p) => ({
3004
- x: M2D.a * p.x + M2D.c * p.y,
3005
- y: M2D.b * p.x + M2D.d * p.y
3006
- }));
3007
- const minX = Math.min(...t.map((p) => p.x));
3008
- const maxX = Math.max(...t.map((p) => p.x));
3009
- const minY = Math.min(...t.map((p) => p.y));
3010
- const maxY = Math.max(...t.map((p) => p.y));
3011
- return { minX, minY, width: maxX - minX, height: maxY - minY };
3012
- }
3013
3244
  function readIndividualTransforms(el) {
3014
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
3245
+ var _a, _b, _c, _d, _e, _f, _g, _h;
3015
3246
  const out = { rotate: "0deg", scale: null, translate: null };
3016
3247
  const map = typeof el.computedStyleMap === "function" ? el.computedStyleMap() : null;
3017
3248
  if (map) {
@@ -3026,26 +3257,37 @@
3026
3257
  };
3027
3258
  const rot = safeGet("rotate");
3028
3259
  if (rot) {
3029
- const ang = (_a = rot.angle) != null ? _a : rot;
3030
- if (ang && typeof ang === "object" && "unit" in ang) {
3260
+ if (rot.angle) {
3261
+ const ang = rot.angle;
3031
3262
  out.rotate = ang.unit === "rad" ? ang.value * 180 / Math.PI + "deg" : ang.value + ang.unit;
3263
+ } else if (rot.unit) {
3264
+ out.rotate = rot.unit === "rad" ? rot.value * 180 / Math.PI + "deg" : rot.value + rot.unit;
3032
3265
  } else {
3033
3266
  out.rotate = String(rot);
3034
3267
  }
3268
+ } else {
3269
+ const cs2 = getComputedStyle(el);
3270
+ out.rotate = cs2.rotate && cs2.rotate !== "none" ? cs2.rotate : "0deg";
3035
3271
  }
3036
3272
  const sc = safeGet("scale");
3037
- if (sc && sc.length) {
3038
- const sx = (_c = (_b = sc[0]) == null ? void 0 : _b.value) != null ? _c : 1;
3039
- const sy = (_e = (_d = sc[1]) == null ? void 0 : _d.value) != null ? _e : sx;
3273
+ if (sc) {
3274
+ 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;
3275
+ 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;
3040
3276
  out.scale = `${sx} ${sy}`;
3277
+ } else {
3278
+ const cs2 = getComputedStyle(el);
3279
+ out.scale = cs2.scale && cs2.scale !== "none" ? cs2.scale : null;
3041
3280
  }
3042
3281
  const tr = safeGet("translate");
3043
- if (tr && tr.length) {
3044
- const tx = (_g = (_f = tr[0]) == null ? void 0 : _f.value) != null ? _g : 0;
3045
- const ty = (_i = (_h = tr[1]) == null ? void 0 : _h.value) != null ? _i : 0;
3046
- const ux = (_k = (_j = tr[0]) == null ? void 0 : _j.unit) != null ? _k : "px";
3047
- const uy = (_m = (_l = tr[1]) == null ? void 0 : _l.unit) != null ? _m : "px";
3282
+ if (tr) {
3283
+ const tx = "x" in tr && "value" in tr.x ? tr.x.value : Array.isArray(tr) ? (_e = tr[0]) == null ? void 0 : _e.value : 0;
3284
+ const ty = "y" in tr && "value" in tr.y ? tr.y.value : Array.isArray(tr) ? (_f = tr[1]) == null ? void 0 : _f.value : 0;
3285
+ const ux = "x" in tr && ((_g = tr.x) == null ? void 0 : _g.unit) ? tr.x.unit : "px";
3286
+ const uy = "y" in tr && ((_h = tr.y) == null ? void 0 : _h.unit) ? tr.y.unit : "px";
3048
3287
  out.translate = `${tx}${ux} ${ty}${uy}`;
3288
+ } else {
3289
+ const cs2 = getComputedStyle(el);
3290
+ out.translate = cs2.translate && cs2.translate !== "none" ? cs2.translate : null;
3049
3291
  }
3050
3292
  return out;
3051
3293
  }
@@ -3056,17 +3298,33 @@
3056
3298
  return out;
3057
3299
  }
3058
3300
  function hasBBoxAffectingTransform(el) {
3059
- const t = getComputedStyle(el).transform || "none";
3060
- if (t === "none" || t === "matrix(1, 0, 0, 1, 0, 0)") return false;
3061
- if (/^matrix\(\s*1\s*,\s*0\s*,\s*0\s*,\s*1\s*,/i.test(t)) return false;
3062
- return true;
3063
- }
3064
- function inflateRect(r, p) {
3065
- const x = Math.floor(r.left - p.left);
3066
- const y = Math.floor(r.top - p.top);
3067
- const w = Math.ceil(r.width + p.left + p.right);
3068
- const h = Math.ceil(r.height + p.top + p.bottom);
3069
- return { x, y, width: w, height: h };
3301
+ const cs = getComputedStyle(el);
3302
+ const t = cs.transform || "none";
3303
+ 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);
3304
+ if (hasMatrix) return true;
3305
+ const r = cs.rotate && cs.rotate !== "none" && cs.rotate !== "0deg";
3306
+ const s = cs.scale && cs.scale !== "none" && cs.scale !== "1";
3307
+ const tr = cs.translate && cs.translate !== "none" && cs.translate !== "0px 0px";
3308
+ return Boolean(r || s || tr);
3309
+ }
3310
+ function parseTransformOriginPx(cs, w, h) {
3311
+ const raw = (cs.transformOrigin || "0 0").trim().split(/\s+/);
3312
+ const [oxRaw, oyRaw] = [raw[0] || "0", raw[1] || "0"];
3313
+ const toPx = (token, size) => {
3314
+ const t = token.toLowerCase();
3315
+ if (t === "left" || t === "top") return 0;
3316
+ if (t === "center") return size / 2;
3317
+ if (t === "right") return size;
3318
+ if (t === "bottom") return size;
3319
+ if (t.endsWith("px")) return parseFloat(t) || 0;
3320
+ if (t.endsWith("%")) return (parseFloat(t) || 0) * size / 100;
3321
+ if (/^-?\d+(\.\d+)?$/.test(t)) return parseFloat(t) || 0;
3322
+ return 0;
3323
+ };
3324
+ return {
3325
+ ox: toPx(oxRaw, w),
3326
+ oy: toPx(oyRaw, h)
3327
+ };
3070
3328
  }
3071
3329
  var __measureHost = null;
3072
3330
  function getMeasureHost() {
@@ -3155,41 +3413,201 @@
3155
3413
  };
3156
3414
  }
3157
3415
 
3158
- // src/exporters/toImg.js
3159
- function toImg(url, options) {
3416
+ // src/exporters/toCanvas.js
3417
+ function isSvgDataURL(u) {
3418
+ return typeof u === "string" && /^data:image\/svg\+xml/i.test(u);
3419
+ }
3420
+ function decodeSvgFromDataURL(u) {
3421
+ const i = u.indexOf(",");
3422
+ return i >= 0 ? decodeURIComponent(u.slice(i + 1)) : "";
3423
+ }
3424
+ function encodeSvgToDataURL(svgText) {
3425
+ return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgText)}`;
3426
+ }
3427
+ function splitDecls(s) {
3428
+ let parts = [], buf = "", depth = 0;
3429
+ for (let i = 0; i < s.length; i++) {
3430
+ const ch = s[i];
3431
+ if (ch === "(") depth++;
3432
+ if (ch === ")") depth = Math.max(0, depth - 1);
3433
+ if (ch === ";" && depth === 0) {
3434
+ parts.push(buf);
3435
+ buf = "";
3436
+ } else buf += ch;
3437
+ }
3438
+ if (buf.trim()) parts.push(buf);
3439
+ return parts.map((x) => x.trim()).filter(Boolean);
3440
+ }
3441
+ function boxShadowToDropShadow(value) {
3442
+ const layers = [];
3443
+ let buf = "", depth = 0;
3444
+ for (let i = 0; i < value.length; i++) {
3445
+ const ch = value[i];
3446
+ if (ch === "(") depth++;
3447
+ if (ch === ")") depth = Math.max(0, depth - 1);
3448
+ if (ch === "," && depth === 0) {
3449
+ layers.push(buf.trim());
3450
+ buf = "";
3451
+ } else buf += ch;
3452
+ }
3453
+ if (buf.trim()) layers.push(buf.trim());
3454
+ const fns = [];
3455
+ for (const layer of layers) {
3456
+ if (/\binset\b/i.test(layer)) continue;
3457
+ const nums = layer.match(/-?\d+(?:\.\d+)?px/gi) || [];
3458
+ const [ox = "0px", oy = "0px", blur = "0px"] = nums;
3459
+ let color = layer.replace(/-?\d+(?:\.\d+)?px/gi, "").replace(/\binset\b/ig, "").trim().replace(/\s{2,}/g, " ");
3460
+ const hasColor = !!color && color !== ",";
3461
+ fns.push(`drop-shadow(${ox} ${oy} ${blur}${hasColor ? ` ${color}` : ""})`);
3462
+ }
3463
+ return fns.join(" ");
3464
+ }
3465
+ function rewriteDeclList(list) {
3466
+ const decls = splitDecls(list);
3467
+ let filter = null, wfilter = null, box = null;
3468
+ const rest = [];
3469
+ for (const d of decls) {
3470
+ const idx = d.indexOf(":");
3471
+ if (idx < 0) continue;
3472
+ const prop = d.slice(0, idx).trim().toLowerCase();
3473
+ const val = d.slice(idx + 1).trim();
3474
+ if (prop === "box-shadow") box = val;
3475
+ else if (prop === "filter") filter = val;
3476
+ else if (prop === "-webkit-filter") wfilter = val;
3477
+ else rest.push([prop, val]);
3478
+ }
3479
+ if (box) {
3480
+ const ds = boxShadowToDropShadow(box);
3481
+ if (ds) {
3482
+ filter = filter ? `${filter} ${ds}` : ds;
3483
+ wfilter = wfilter ? `${wfilter} ${ds}` : ds;
3484
+ }
3485
+ }
3486
+ const out = [...rest];
3487
+ if (filter) out.push(["filter", filter]);
3488
+ if (wfilter) out.push(["-webkit-filter", wfilter]);
3489
+ return out.map(([k, v]) => `${k}:${v}`).join(";");
3490
+ }
3491
+ function rewriteCssBlock(css) {
3492
+ return css.replace(/([^{}]+)\{([^}]*)\}/g, (_m, sel, body) => `${sel}{${rewriteDeclList(body)}}`);
3493
+ }
3494
+ function rewriteSvgBoxShadowToDropShadow(svgText) {
3495
+ svgText = svgText.replace(
3496
+ /<style[^>]*>([\s\S]*?)<\/style>/gi,
3497
+ (m, css) => m.replace(css, rewriteCssBlock(css))
3498
+ );
3499
+ svgText = svgText.replace(
3500
+ /style=(['"])([\s\S]*?)\1/gi,
3501
+ (m, q, body) => `style=${q}${rewriteDeclList(body)}${q}`
3502
+ );
3503
+ return svgText;
3504
+ }
3505
+ function maybeConvertBoxShadowForSafari(url) {
3506
+ if (!isSafari() || !isSvgDataURL(url)) return url;
3507
+ try {
3508
+ const svg = decodeSvgFromDataURL(url);
3509
+ const fixed = rewriteSvgBoxShadowToDropShadow(svg);
3510
+ return encodeSvgToDataURL(fixed);
3511
+ } catch (e) {
3512
+ return url;
3513
+ }
3514
+ }
3515
+ function toCanvas(url, options) {
3160
3516
  return __async(this, null, function* () {
3517
+ let { width: optW, height: optH, scale = 1, dpr = 1, meta = {} } = options;
3518
+ url = maybeConvertBoxShadowForSafari(url);
3161
3519
  const img = new Image();
3520
+ img.loading = "eager";
3521
+ img.decoding = "sync";
3522
+ img.crossOrigin = "anonymous";
3162
3523
  img.src = url;
3163
3524
  yield img.decode();
3164
- if (options.scale !== 1) {
3165
- img.style.width = `${img.naturalWidth * options.scale}px`;
3166
- img.style.height = `${img.naturalHeight * options.scale}px`;
3525
+ const natW = img.naturalWidth;
3526
+ const natH = img.naturalHeight;
3527
+ const refW = Number.isFinite(meta.w0) ? meta.w0 : natW;
3528
+ const refH = Number.isFinite(meta.h0) ? meta.h0 : natH;
3529
+ let outW, outH;
3530
+ const hasW = Number.isFinite(optW);
3531
+ const hasH = Number.isFinite(optH);
3532
+ if (hasW && hasH) {
3533
+ outW = Math.max(1, optW);
3534
+ outH = Math.max(1, optH);
3535
+ } else if (hasW) {
3536
+ const k = optW / Math.max(1, refW);
3537
+ outW = optW;
3538
+ outH = Math.round(refH * k);
3539
+ } else if (hasH) {
3540
+ const k = optH / Math.max(1, refH);
3541
+ outH = optH;
3542
+ outW = Math.round(refW * k);
3543
+ } else {
3544
+ outW = natW;
3545
+ outH = natH;
3167
3546
  }
3547
+ outW = Math.round(outW * scale);
3548
+ outH = Math.round(outH * scale);
3549
+ const canvas = document.createElement("canvas");
3550
+ canvas.width = Math.ceil(outW * dpr);
3551
+ canvas.height = Math.ceil(outH * dpr);
3552
+ canvas.style.width = `${outW}px`;
3553
+ canvas.style.height = `${outH}px`;
3554
+ const ctx = canvas.getContext("2d");
3555
+ if (dpr !== 1) ctx.scale(dpr, dpr);
3556
+ ctx.drawImage(img, 0, 0, outW, outH);
3557
+ return canvas;
3558
+ });
3559
+ }
3560
+
3561
+ // src/modules/rasterize.js
3562
+ function rasterize(url, options) {
3563
+ return __async(this, null, function* () {
3564
+ const canvas = yield toCanvas(url, options);
3565
+ const finalCanvas = options.backgroundColor ? createBackground(canvas, options.backgroundColor) : canvas;
3566
+ const img = new Image();
3567
+ img.src = finalCanvas.toDataURL(`image/${options.format}`, options.quality);
3568
+ yield img.decode();
3569
+ img.style.width = `${finalCanvas.width / options.dpr}px`;
3570
+ img.style.height = `${finalCanvas.height / options.dpr}px`;
3168
3571
  return img;
3169
3572
  });
3170
3573
  }
3171
3574
 
3172
- // src/exporters/toCanvas.js
3173
- function toCanvas(url, options) {
3575
+ // src/exporters/toImg.js
3576
+ function toImg(url, options) {
3174
3577
  return __async(this, null, function* () {
3175
- const { scale, dpr } = options;
3578
+ const { scale = 1, width, height, meta = {} } = options;
3579
+ const hasW = Number.isFinite(width);
3580
+ const hasH = Number.isFinite(height);
3581
+ const wantsScale = Number.isFinite(scale) && scale !== 1 || hasW || hasH;
3582
+ if (isSafari() && wantsScale) {
3583
+ const pngUrl = yield rasterize(url, __spreadProps(__spreadValues({}, options), { format: "png", quality: 1, meta }));
3584
+ return pngUrl;
3585
+ }
3176
3586
  const img = new Image();
3177
- img.crossOrigin = "anonymous";
3178
- img.loading = "eager";
3179
3587
  img.decoding = "sync";
3588
+ img.loading = "eager";
3180
3589
  img.src = url;
3181
3590
  yield img.decode();
3182
- const width = img.naturalWidth * scale;
3183
- const height = img.naturalHeight * scale;
3184
- const canvas = document.createElement("canvas");
3185
- canvas.width = Math.ceil(width * dpr);
3186
- canvas.height = Math.ceil(height * dpr);
3187
- canvas.style.width = `${width}px`;
3188
- canvas.style.height = `${height}px`;
3189
- const ctx = canvas.getContext("2d");
3190
- if (dpr !== 1) ctx.scale(dpr, dpr);
3191
- ctx.drawImage(img, 0, 0, width, height);
3192
- return canvas;
3591
+ if (hasW && hasH) {
3592
+ img.style.width = `${width}px`;
3593
+ img.style.height = `${height}px`;
3594
+ } else if (hasW) {
3595
+ const refW = Number.isFinite(meta.w0) ? meta.w0 : img.naturalWidth;
3596
+ const refH = Number.isFinite(meta.h0) ? meta.h0 : img.naturalHeight;
3597
+ const k = width / Math.max(1, refW);
3598
+ img.style.width = `${width}px`;
3599
+ img.style.height = `${Math.round(refH * k)}px`;
3600
+ } else if (hasH) {
3601
+ const refW = Number.isFinite(meta.w0) ? meta.w0 : img.naturalWidth;
3602
+ const refH = Number.isFinite(meta.h0) ? meta.h0 : img.naturalHeight;
3603
+ const k = height / Math.max(1, refH);
3604
+ img.style.height = `${height}px`;
3605
+ img.style.width = `${Math.round(refW * k)}px`;
3606
+ } else if (scale !== 1) {
3607
+ img.style.width = `${Math.round(img.naturalWidth * scale)}px`;
3608
+ img.style.height = `${Math.round(img.naturalHeight * scale)}px`;
3609
+ }
3610
+ return img;
3193
3611
  });
3194
3612
  }
3195
3613
 
@@ -3213,20 +3631,6 @@
3213
3631
  });
3214
3632
  }
3215
3633
 
3216
- // src/modules/rasterize.js
3217
- function rasterize(url, options) {
3218
- return __async(this, null, function* () {
3219
- const canvas = yield toCanvas(url, options);
3220
- const finalCanvas = options.backgroundColor ? createBackground(canvas, options.backgroundColor) : canvas;
3221
- const img = new Image();
3222
- img.src = finalCanvas.toDataURL(`image/${options.format}`, options.quality);
3223
- yield img.decode();
3224
- img.style.width = `${finalCanvas.width / options.dpr}px`;
3225
- img.style.height = `${finalCanvas.height / options.dpr}px`;
3226
- return img;
3227
- });
3228
- }
3229
-
3230
3634
  // src/exporters/download.js
3231
3635
  function download(url, options) {
3232
3636
  return __async(this, null, function* () {
@@ -3256,7 +3660,8 @@
3256
3660
  function snapdom(element, userOptions) {
3257
3661
  return __async(this, null, function* () {
3258
3662
  if (!element) throw new Error("Element cannot be null or undefined");
3259
- if (isSafari()) {
3663
+ const context = createContext(userOptions);
3664
+ if (isSafari() && (context.embedFonts === true || hasBackgroundOrMask(element))) {
3260
3665
  for (let i = 0; i < 3; i++) {
3261
3666
  try {
3262
3667
  yield safariWarmup(element, userOptions);
@@ -3266,7 +3671,6 @@
3266
3671
  }
3267
3672
  }
3268
3673
  }
3269
- const context = createContext(userOptions);
3270
3674
  if (context.iconFonts && context.iconFonts.length > 0) extendIconFonts(context.iconFonts);
3271
3675
  if (!context.snap) {
3272
3676
  context.snap = {
@@ -3348,6 +3752,20 @@
3348
3752
  });
3349
3753
  });
3350
3754
  }
3755
+ function hasBackgroundOrMask(el) {
3756
+ const walker = document.createTreeWalker(el, NodeFilter.SHOW_ELEMENT);
3757
+ while (walker.nextNode()) {
3758
+ const node = (
3759
+ /** @type {Element} */
3760
+ walker.currentNode
3761
+ );
3762
+ const cs = getComputedStyle(node);
3763
+ const bg = cs.backgroundImage && cs.backgroundImage !== "none";
3764
+ const mask = cs.maskImage && cs.maskImage !== "none" || cs.webkitMaskImage && cs.webkitMaskImage !== "none";
3765
+ if (bg || mask) return true;
3766
+ }
3767
+ return false;
3768
+ }
3351
3769
 
3352
3770
  // src/api/preCache.js
3353
3771
  function preCache() {