@zumer/snapdom 1.9.12-dev.4 → 1.9.13

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
@@ -858,6 +858,65 @@
858
858
  delete snap["block-size"];
859
859
  }
860
860
 
861
+ // src/modules/CSSVar.js
862
+ function resolveCSSVars(sourceEl, cloneEl) {
863
+ var _a, _b, _c;
864
+ if (!(sourceEl instanceof Element) || !(cloneEl instanceof Element)) return;
865
+ const styleAttr = (_a = sourceEl.getAttribute) == null ? void 0 : _a.call(sourceEl, "style");
866
+ let hasVar = !!(styleAttr && styleAttr.includes("var("));
867
+ if (!hasVar && ((_b = sourceEl.attributes) == null ? void 0 : _b.length)) {
868
+ const attrs = sourceEl.attributes;
869
+ for (let i = 0; i < attrs.length; i++) {
870
+ const a = attrs[i];
871
+ if (a && typeof a.value === "string" && a.value.includes("var(")) {
872
+ hasVar = true;
873
+ break;
874
+ }
875
+ }
876
+ }
877
+ if (!hasVar) return;
878
+ let cs;
879
+ try {
880
+ cs = getComputedStyle(sourceEl);
881
+ } catch (e) {
882
+ return;
883
+ }
884
+ const author = sourceEl.style;
885
+ if (author && author.length) {
886
+ for (let i = 0; i < author.length; i++) {
887
+ const prop = author[i];
888
+ const val = author.getPropertyValue(prop);
889
+ if (!val || !val.includes("var(")) continue;
890
+ const resolved = cs.getPropertyValue(prop);
891
+ if (resolved) {
892
+ try {
893
+ cloneEl.style.setProperty(prop, resolved.trim(), author.getPropertyPriority(prop));
894
+ } catch (e) {
895
+ }
896
+ }
897
+ }
898
+ }
899
+ if ((_c = sourceEl.attributes) == null ? void 0 : _c.length) {
900
+ const attrs = sourceEl.attributes;
901
+ for (let i = 0; i < attrs.length; i++) {
902
+ const a = attrs[i];
903
+ if (!a || typeof a.value !== "string" || !a.value.includes("var(")) continue;
904
+ const propName = a.name;
905
+ let resolved = "";
906
+ try {
907
+ resolved = cs.getPropertyValue(propName);
908
+ } catch (e) {
909
+ }
910
+ if (resolved) {
911
+ try {
912
+ cloneEl.style.setProperty(propName, resolved.trim());
913
+ } catch (e) {
914
+ }
915
+ }
916
+ }
917
+ }
918
+ }
919
+
861
920
  // src/core/clone.js
862
921
  function idleCallback(childList, callback, fast) {
863
922
  return Promise.all(childList.map((child) => {
@@ -879,7 +938,7 @@
879
938
  function addNotSlottedRightmost(sel) {
880
939
  sel = sel.trim();
881
940
  if (!sel) return sel;
882
- if (/\:not\(\s*\[data-sd-slotted\]\s*\)\s*$/.test(sel)) return sel;
941
+ if (/:not\(\s*\[data-sd-slotted\]\s*\)\s*$/.test(sel)) return sel;
883
942
  return `${sel}:not([data-sd-slotted])`;
884
943
  }
885
944
  function wrapWithScope(selectorList, scopeSelector, excludeSlotted = true) {
@@ -1175,7 +1234,7 @@
1175
1234
  inlineAllStyles(node, clone2, sessionCache, options);
1176
1235
  const placeholder = document.createElement("div");
1177
1236
  placeholder.textContent = node.getAttribute("data-placeholder-text") || "";
1178
- placeholder.style.cssText = `color:#666;font-size:12px;text-align:center;line-height:1.4;padding:0.5em;box-sizing:border-box;`;
1237
+ placeholder.style.cssText = "color:#666;font-size:12px;text-align:center;line-height:1.4;padding:0.5em;box-sizing:border-box;";
1179
1238
  clone2.appendChild(placeholder);
1180
1239
  return clone2;
1181
1240
  }
@@ -1192,6 +1251,7 @@
1192
1251
  let clone;
1193
1252
  try {
1194
1253
  clone = node.cloneNode(false);
1254
+ resolveCSSVars(node, clone);
1195
1255
  sessionCache.nodeMap.set(clone, node);
1196
1256
  if (node.tagName === "IMG") {
1197
1257
  freezeImgSrcset(node, clone);
@@ -1247,7 +1307,7 @@
1247
1307
  } else {
1248
1308
  deepClone(child, sessionCache, options).then((clonedChild) => {
1249
1309
  resolve(clonedChild || null);
1250
- }).catch((e) => {
1310
+ }).catch(() => {
1251
1311
  resolve(null);
1252
1312
  });
1253
1313
  }
@@ -1288,7 +1348,7 @@
1288
1348
  markSlottedSubtree(clonedChild);
1289
1349
  }
1290
1350
  resolve(clonedChild || null);
1291
- }).catch((e) => {
1351
+ }).catch(() => {
1292
1352
  resolve(null);
1293
1353
  });
1294
1354
  };
@@ -1303,7 +1363,7 @@
1303
1363
  if (clonedAssignedNodes.has(child)) return resolve(null);
1304
1364
  deepClone(child, sessionCache, options).then((clonedChild) => {
1305
1365
  resolve(clonedChild || null);
1306
- }).catch((e) => {
1366
+ }).catch(() => {
1307
1367
  resolve(null);
1308
1368
  });
1309
1369
  }
@@ -1368,7 +1428,10 @@
1368
1428
  return __async(this, null, function* () {
1369
1429
  fontFamily = fontFamily.replace(/^['"]+|['"]+$/g, "");
1370
1430
  const dpr = window.devicePixelRatio || 1;
1371
- yield document.fonts.ready;
1431
+ try {
1432
+ yield document.fonts.ready;
1433
+ } catch (e) {
1434
+ }
1372
1435
  const span = document.createElement("span");
1373
1436
  span.textContent = unicodeChar;
1374
1437
  span.style.position = "absolute";
@@ -1386,8 +1449,8 @@
1386
1449
  const height = Math.ceil(rect.height);
1387
1450
  document.body.removeChild(span);
1388
1451
  const canvas = document.createElement("canvas");
1389
- canvas.width = width * dpr;
1390
- canvas.height = height * dpr;
1452
+ canvas.width = Math.max(1, width * dpr);
1453
+ canvas.height = Math.max(1, height * dpr);
1391
1454
  const ctx = canvas.getContext("2d");
1392
1455
  ctx.scale(dpr, dpr);
1393
1456
  ctx.font = fontWeight ? `${fontWeight} ${fontSize}px "${fontFamily}"` : `${fontSize}px "${fontFamily}"`;
@@ -1507,6 +1570,313 @@
1507
1570
  }
1508
1571
  return out;
1509
1572
  }
1573
+ function rewriteRelativeUrls(cssText, baseHref) {
1574
+ if (!cssText) return cssText;
1575
+ return cssText.replace(
1576
+ /url\(\s*(['"]?)([^)'"]+)\1\s*\)/g,
1577
+ (m, q, u) => {
1578
+ const src = (u || "").trim();
1579
+ if (!src || /^data:|^blob:|^https?:|^file:|^about:/i.test(src)) return m;
1580
+ let abs = src;
1581
+ try {
1582
+ abs = new URL(src, baseHref || location.href).href;
1583
+ } catch (e) {
1584
+ }
1585
+ return `url("${abs}")`;
1586
+ }
1587
+ );
1588
+ }
1589
+ var IMPORT_ANY_RE = /@import\s+(?:url\(\s*(['"]?)([^)"']+)\1\s*\)|(['"])([^"']+)\3)([^;]*);/g;
1590
+ var MAX_IMPORT_DEPTH = 4;
1591
+ function inlineImportsAndRewrite(cssText, ownerHref, useProxy) {
1592
+ return __async(this, null, function* () {
1593
+ if (!cssText) return cssText;
1594
+ const visited = /* @__PURE__ */ new Set();
1595
+ function normalizeUrl(u, base) {
1596
+ try {
1597
+ return new URL(u, base || location.href).href;
1598
+ } catch (e) {
1599
+ return u;
1600
+ }
1601
+ }
1602
+ function resolveOnce(text, baseHref, depth = 0) {
1603
+ return __async(this, null, function* () {
1604
+ if (depth > MAX_IMPORT_DEPTH) {
1605
+ console.warn(`[snapDOM] @import depth exceeded (${MAX_IMPORT_DEPTH}) at ${baseHref}`);
1606
+ return text;
1607
+ }
1608
+ let out = "";
1609
+ let last = 0;
1610
+ let m;
1611
+ while (m = IMPORT_ANY_RE.exec(text)) {
1612
+ out += text.slice(last, m.index);
1613
+ last = IMPORT_ANY_RE.lastIndex;
1614
+ const rawUrl = (m[2] || m[4] || "").trim();
1615
+ const absUrl = normalizeUrl(rawUrl, baseHref);
1616
+ if (visited.has(absUrl)) {
1617
+ console.warn("[snapDOM] Skipping circular import");
1618
+ continue;
1619
+ }
1620
+ visited.add(absUrl);
1621
+ let imported = "";
1622
+ try {
1623
+ const r = yield snapFetch(absUrl, { as: "text", useProxy, silent: true });
1624
+ if (r.ok && typeof r.data === "string") imported = r.data;
1625
+ } catch (e) {
1626
+ }
1627
+ if (imported) {
1628
+ imported = rewriteRelativeUrls(imported, absUrl);
1629
+ imported = yield resolveOnce(imported, absUrl, depth + 1);
1630
+ out += `
1631
+ /* inlined: ${absUrl} */
1632
+ ${imported}
1633
+ `;
1634
+ } else {
1635
+ out += m[0];
1636
+ }
1637
+ }
1638
+ out += text.slice(last);
1639
+ return out;
1640
+ });
1641
+ }
1642
+ let rewritten = rewriteRelativeUrls(cssText, ownerHref || location.href);
1643
+ rewritten = yield resolveOnce(rewritten, ownerHref || location.href, 0);
1644
+ return rewritten;
1645
+ });
1646
+ }
1647
+ var URL_RE = /url\((["']?)([^"')]+)\1\)/g;
1648
+ var FACE_RE = /@font-face[^{}]*\{[^}]*\}/g;
1649
+ function parseUnicodeRange(ur) {
1650
+ if (!ur) return [];
1651
+ const ranges = [];
1652
+ const parts = ur.split(",").map((s) => s.trim()).filter(Boolean);
1653
+ for (const p of parts) {
1654
+ const m = p.match(/^U\+([0-9A-Fa-f?]+)(?:-([0-9A-Fa-f?]+))?$/);
1655
+ if (!m) continue;
1656
+ const a = m[1], b = m[2];
1657
+ const expand = (hex) => {
1658
+ if (!hex.includes("?")) return parseInt(hex, 16);
1659
+ const min = parseInt(hex.replace(/\?/g, "0"), 16);
1660
+ const max = parseInt(hex.replace(/\?/g, "F"), 16);
1661
+ return [min, max];
1662
+ };
1663
+ if (b) {
1664
+ const A = expand(a), B = expand(b);
1665
+ const min = Array.isArray(A) ? A[0] : A;
1666
+ const max = Array.isArray(B) ? B[1] : B;
1667
+ ranges.push([Math.min(min, max), Math.max(min, max)]);
1668
+ } else {
1669
+ const X = expand(a);
1670
+ if (Array.isArray(X)) ranges.push([X[0], X[1]]);
1671
+ else ranges.push([X, X]);
1672
+ }
1673
+ }
1674
+ return ranges;
1675
+ }
1676
+ function unicodeIntersects(used, ranges) {
1677
+ if (!ranges.length) return true;
1678
+ if (!used || used.size === 0) return true;
1679
+ for (const cp of used) {
1680
+ for (const [a, b] of ranges) if (cp >= a && cp <= b) return true;
1681
+ }
1682
+ return false;
1683
+ }
1684
+ function extractSrcUrls(srcValue, baseHref) {
1685
+ const urls = [];
1686
+ if (!srcValue) return urls;
1687
+ for (const m of srcValue.matchAll(URL_RE)) {
1688
+ let u = (m[2] || "").trim();
1689
+ if (!u || u.startsWith("data:")) continue;
1690
+ if (!/^https?:/i.test(u)) {
1691
+ try {
1692
+ u = new URL(u, baseHref || location.href).href;
1693
+ } catch (e) {
1694
+ }
1695
+ }
1696
+ urls.push(u);
1697
+ }
1698
+ return urls;
1699
+ }
1700
+ function inlineUrlsInCssBlock(cssBlock, baseHref, useProxy = "") {
1701
+ return __async(this, null, function* () {
1702
+ var _a, _b, _c, _d, _e;
1703
+ let out = cssBlock;
1704
+ for (const m of cssBlock.matchAll(URL_RE)) {
1705
+ const raw = extractURL(m[0]);
1706
+ if (!raw) continue;
1707
+ let abs = raw;
1708
+ if (!abs.startsWith("http") && !abs.startsWith("data:")) {
1709
+ try {
1710
+ abs = new URL(abs, baseHref || location.href).href;
1711
+ } catch (e) {
1712
+ }
1713
+ }
1714
+ if (isIconFont2(abs)) continue;
1715
+ if ((_a = cache.resource) == null ? void 0 : _a.has(abs)) {
1716
+ (_b = cache.font) == null ? void 0 : _b.add(abs);
1717
+ out = out.replace(m[0], `url(${cache.resource.get(abs)})`);
1718
+ continue;
1719
+ }
1720
+ if ((_c = cache.font) == null ? void 0 : _c.has(abs)) continue;
1721
+ try {
1722
+ const r = yield snapFetch(abs, { as: "dataURL", useProxy, silent: true });
1723
+ if (r.ok && typeof r.data === "string") {
1724
+ const b64 = r.data;
1725
+ (_d = cache.resource) == null ? void 0 : _d.set(abs, b64);
1726
+ (_e = cache.font) == null ? void 0 : _e.add(abs);
1727
+ out = out.replace(m[0], `url(${b64})`);
1728
+ }
1729
+ } catch (e) {
1730
+ console.warn("[snapDOM] Failed to fetch font resource:", abs);
1731
+ }
1732
+ }
1733
+ return out;
1734
+ });
1735
+ }
1736
+ function subsetFromRanges(ranges) {
1737
+ if (!ranges.length) return null;
1738
+ const hit = (a, b) => ranges.some(([x, y]) => !(y < a || x > b));
1739
+ const latin = hit(0, 255) || hit(305, 305);
1740
+ const latinExt = hit(256, 591) || hit(7680, 7935);
1741
+ const greek = hit(880, 1023);
1742
+ const cyr = hit(1024, 1279);
1743
+ const viet = hit(7840, 7929) || hit(258, 259) || hit(416, 417) || hit(431, 432);
1744
+ if (viet) return "vietnamese";
1745
+ if (cyr) return "cyrillic";
1746
+ if (greek) return "greek";
1747
+ if (latinExt) return "latin-ext";
1748
+ if (latin) return "latin";
1749
+ return null;
1750
+ }
1751
+ function buildSimpleExcluder(ex = {}) {
1752
+ const famSet = new Set((ex.families || []).map((s) => String(s).toLowerCase()));
1753
+ const domSet = new Set((ex.domains || []).map((s) => String(s).toLowerCase()));
1754
+ const subSet = new Set((ex.subsets || []).map((s) => String(s).toLowerCase()));
1755
+ return (meta, parsedRanges) => {
1756
+ if (famSet.size && famSet.has(meta.family.toLowerCase())) return true;
1757
+ if (domSet.size) {
1758
+ for (const u of meta.srcUrls) {
1759
+ try {
1760
+ if (domSet.has(new URL(u).host.toLowerCase())) return true;
1761
+ } catch (e) {
1762
+ }
1763
+ }
1764
+ }
1765
+ if (subSet.size) {
1766
+ const label = subsetFromRanges(parsedRanges);
1767
+ if (label && subSet.has(label)) return true;
1768
+ }
1769
+ return false;
1770
+ };
1771
+ }
1772
+ function dedupeFontFaces(cssText) {
1773
+ var _a, _b, _c, _d, _e, _f;
1774
+ if (!cssText) return cssText;
1775
+ const FACE_RE_G = /@font-face[^{}]*\{[^}]*\}/gi;
1776
+ const seen = /* @__PURE__ */ new Set();
1777
+ const out = [];
1778
+ for (const block of cssText.match(FACE_RE_G) || []) {
1779
+ const familyRaw = ((_a = block.match(/font-family:\s*([^;]+);/i)) == null ? void 0 : _a[1]) || "";
1780
+ const family = pickPrimaryFamily(familyRaw);
1781
+ const weightSpec = (((_b = block.match(/font-weight:\s*([^;]+);/i)) == null ? void 0 : _b[1]) || "400").trim();
1782
+ const styleSpec = (((_c = block.match(/font-style:\s*([^;]+);/i)) == null ? void 0 : _c[1]) || "normal").trim();
1783
+ const stretchSpec = (((_d = block.match(/font-stretch:\s*([^;]+);/i)) == null ? void 0 : _d[1]) || "100%").trim();
1784
+ const urange = (((_e = block.match(/unicode-range:\s*([^;]+);/i)) == null ? void 0 : _e[1]) || "").trim();
1785
+ const srcRaw = (((_f = block.match(/src\s*:\s*([^;]+);/i)) == null ? void 0 : _f[1]) || "").trim();
1786
+ const urls = extractSrcUrls(srcRaw, location.href);
1787
+ const srcPart = urls.length ? urls.map((u) => String(u).toLowerCase()).sort().join("|") : srcRaw.toLowerCase();
1788
+ const key = [
1789
+ String(family || "").toLowerCase(),
1790
+ weightSpec,
1791
+ styleSpec,
1792
+ stretchSpec,
1793
+ urange.toLowerCase(),
1794
+ srcPart
1795
+ ].join("|");
1796
+ if (!seen.has(key)) {
1797
+ seen.add(key);
1798
+ out.push(block);
1799
+ }
1800
+ }
1801
+ if (out.length === 0) return cssText;
1802
+ let i = 0;
1803
+ return cssText.replace(FACE_RE_G, () => out[i++] || "");
1804
+ }
1805
+ function buildFontsCacheKey(required, exclude, localFonts, useProxy) {
1806
+ const req = Array.from(required || []).sort().join("|");
1807
+ const ex = exclude ? JSON.stringify({
1808
+ families: (exclude.families || []).map((s) => String(s).toLowerCase()).sort(),
1809
+ domains: (exclude.domains || []).map((s) => String(s).toLowerCase()).sort(),
1810
+ subsets: (exclude.subsets || []).map((s) => String(s).toLowerCase()).sort()
1811
+ }) : "";
1812
+ const lf = (localFonts || []).map((f) => `${(f.family || "").toLowerCase()}::${f.weight || "normal"}::${f.style || "normal"}::${f.src || ""}`).sort().join("|");
1813
+ const px = useProxy || "";
1814
+ return `fonts-embed-css::req=${req}::ex=${ex}::lf=${lf}::px=${px}`;
1815
+ }
1816
+ function collectFacesFromSheet(sheet, baseHref, emitFace, ctx) {
1817
+ return __async(this, null, function* () {
1818
+ let rules;
1819
+ try {
1820
+ rules = sheet.cssRules || [];
1821
+ } catch (e) {
1822
+ return;
1823
+ }
1824
+ const normalizeUrl = (u, base) => {
1825
+ try {
1826
+ return new URL(u, base || location.href).href;
1827
+ } catch (e) {
1828
+ return u;
1829
+ }
1830
+ };
1831
+ for (const rule of rules) {
1832
+ if (rule.type === CSSRule.IMPORT_RULE && rule.styleSheet) {
1833
+ const childHref = rule.href ? normalizeUrl(rule.href, baseHref) : baseHref;
1834
+ if (ctx.depth >= MAX_IMPORT_DEPTH) {
1835
+ console.warn(`[snapDOM] CSSOM import depth exceeded (${MAX_IMPORT_DEPTH}) at ${childHref}`);
1836
+ continue;
1837
+ }
1838
+ if (childHref && ctx.visitedSheets.has(childHref)) {
1839
+ console.warn(`[snapDOM] Skipping circular CSSOM import: ${childHref}`);
1840
+ continue;
1841
+ }
1842
+ if (childHref) ctx.visitedSheets.add(childHref);
1843
+ const nextCtx = __spreadProps(__spreadValues({}, ctx), { depth: (ctx.depth || 0) + 1 });
1844
+ yield collectFacesFromSheet(rule.styleSheet, childHref, emitFace, nextCtx);
1845
+ continue;
1846
+ }
1847
+ if (rule.type === CSSRule.FONT_FACE_RULE) {
1848
+ const famRaw = (rule.style.getPropertyValue("font-family") || "").trim();
1849
+ const family = pickPrimaryFamily(famRaw);
1850
+ if (!family || isIconFont2(family)) continue;
1851
+ const weightSpec = (rule.style.getPropertyValue("font-weight") || "400").trim();
1852
+ const styleSpec = (rule.style.getPropertyValue("font-style") || "normal").trim();
1853
+ const stretchSpec = (rule.style.getPropertyValue("font-stretch") || "100%").trim();
1854
+ const srcRaw = (rule.style.getPropertyValue("src") || "").trim();
1855
+ const urange = (rule.style.getPropertyValue("unicode-range") || "").trim();
1856
+ if (!ctx.faceMatchesRequired(family, styleSpec, weightSpec, stretchSpec)) continue;
1857
+ const ranges = parseUnicodeRange(urange);
1858
+ if (!unicodeIntersects(ctx.usedCodepoints, ranges)) continue;
1859
+ const meta = {
1860
+ family,
1861
+ weightSpec,
1862
+ styleSpec,
1863
+ stretchSpec,
1864
+ unicodeRange: urange,
1865
+ srcRaw,
1866
+ srcUrls: extractSrcUrls(srcRaw, baseHref || location.href),
1867
+ href: baseHref || location.href
1868
+ };
1869
+ if (ctx.simpleExcluder && ctx.simpleExcluder(meta, ranges)) continue;
1870
+ if (/url\(/i.test(srcRaw)) {
1871
+ const inlinedSrc = yield inlineUrlsInCssBlock(srcRaw, baseHref || location.href, ctx.useProxy);
1872
+ yield emitFace(`@font-face{font-family:${family};src:${inlinedSrc};font-style:${styleSpec};font-weight:${weightSpec};font-stretch:${stretchSpec};${urange ? `unicode-range:${urange};` : ""}}`);
1873
+ } else {
1874
+ yield emitFace(`@font-face{font-family:${family};src:${srcRaw};font-style:${styleSpec};font-weight:${weightSpec};font-stretch:${stretchSpec};${urange ? `unicode-range:${urange};` : ""}}`);
1875
+ }
1876
+ }
1877
+ }
1878
+ });
1879
+ }
1510
1880
  function embedCustomFonts() {
1511
1881
  return __async(this, arguments, function* ({
1512
1882
  required,
@@ -1526,9 +1896,6 @@
1526
1896
  arr.push({ w: parseInt(w, 10), s, st: parseInt(st, 10) });
1527
1897
  requiredIndex.set(fam, arr);
1528
1898
  }
1529
- const IMPORT_RE = /@import\s+url\(["']?([^"')]+)["']?\)/g;
1530
- const URL_RE = /url\((["']?)([^"')]+)\1\)/g;
1531
- const FACE_RE = /@font-face[^{}]*\{[^}]*\}/g;
1532
1899
  function faceMatchesRequired(fam, styleSpec, weightSpec, stretchSpec) {
1533
1900
  if (!requiredIndex.has(fam)) return false;
1534
1901
  const need = requiredIndex.get(fam);
@@ -1554,192 +1921,23 @@
1554
1921
  const sOk = styleOK(normStyle(r.s));
1555
1922
  const tOk = r.st >= ts.min && r.st <= ts.max;
1556
1923
  const nearWeight = Math.abs(faceSingleW - r.w) <= 300;
1557
- if (nearWeight && sOk && tOk) {
1558
- return true;
1559
- }
1924
+ if (nearWeight && sOk && tOk) return true;
1560
1925
  }
1561
1926
  }
1562
1927
  return false;
1563
1928
  }
1564
- function parseUnicodeRange(ur) {
1565
- if (!ur) return [];
1566
- const ranges = [];
1567
- const parts = ur.split(",").map((s) => s.trim()).filter(Boolean);
1568
- for (const p of parts) {
1569
- const m = p.match(/^U\+([0-9A-Fa-f?]+)(?:-([0-9A-Fa-f?]+))?$/);
1570
- if (!m) continue;
1571
- const a = m[1], b = m[2];
1572
- const expand = (hex) => {
1573
- if (!hex.includes("?")) return parseInt(hex, 16);
1574
- const min = parseInt(hex.replace(/\?/g, "0"), 16);
1575
- const max = parseInt(hex.replace(/\?/g, "F"), 16);
1576
- return [min, max];
1577
- };
1578
- if (b) {
1579
- const A = expand(a), B = expand(b);
1580
- const min = Array.isArray(A) ? A[0] : A;
1581
- const max = Array.isArray(B) ? B[1] : B;
1582
- ranges.push([Math.min(min, max), Math.max(min, max)]);
1583
- } else {
1584
- const X = expand(a);
1585
- if (Array.isArray(X)) ranges.push([X[0], X[1]]);
1586
- else ranges.push([X, X]);
1587
- }
1588
- }
1589
- return ranges;
1590
- }
1591
- function unicodeIntersects(used, ranges) {
1592
- if (!ranges.length) return true;
1593
- if (!used || used.size === 0) return true;
1594
- for (const cp of used) {
1595
- for (const [a, b] of ranges) if (cp >= a && cp <= b) return true;
1596
- }
1597
- return false;
1598
- }
1599
- function extractSrcUrls(srcValue, baseHref) {
1600
- const urls = [];
1601
- if (!srcValue) return urls;
1602
- for (const m of srcValue.matchAll(URL_RE)) {
1603
- let u = (m[2] || "").trim();
1604
- if (!u || u.startsWith("data:")) continue;
1605
- if (!/^https?:/i.test(u)) {
1606
- try {
1607
- u = new URL(u, baseHref || location.href).href;
1608
- } catch (e) {
1609
- }
1610
- }
1611
- urls.push(u);
1612
- }
1613
- return urls;
1614
- }
1615
- function inlineUrlsInCssBlock(cssBlock, baseHref) {
1616
- return __async(this, null, function* () {
1617
- var _a2, _b2, _c2, _d2, _e2;
1618
- let out = cssBlock;
1619
- for (const m of cssBlock.matchAll(URL_RE)) {
1620
- const raw = extractURL(m[0]);
1621
- if (!raw) continue;
1622
- let abs = raw;
1623
- if (!abs.startsWith("http") && !abs.startsWith("data:")) {
1624
- try {
1625
- abs = new URL(abs, baseHref || location.href).href;
1626
- } catch (e) {
1627
- }
1628
- }
1629
- if (isIconFont2(abs)) continue;
1630
- if ((_a2 = cache.resource) == null ? void 0 : _a2.has(abs)) {
1631
- (_b2 = cache.font) == null ? void 0 : _b2.add(abs);
1632
- out = out.replace(m[0], `url(${cache.resource.get(abs)})`);
1633
- continue;
1634
- }
1635
- if ((_c2 = cache.font) == null ? void 0 : _c2.has(abs)) continue;
1636
- try {
1637
- const r = yield snapFetch(abs, { as: "dataURL", useProxy, silent: true });
1638
- if (r.ok && typeof r.data === "string") {
1639
- const b64 = r.data;
1640
- (_d2 = cache.resource) == null ? void 0 : _d2.set(abs, b64);
1641
- (_e2 = cache.font) == null ? void 0 : _e2.add(abs);
1642
- out = out.replace(m[0], `url(${b64})`);
1643
- }
1644
- } catch (e) {
1645
- console.warn("[snapDOM] Failed to fetch font resource:", abs);
1646
- }
1647
- }
1648
- return out;
1649
- });
1650
- }
1651
- function subsetFromRanges(ranges) {
1652
- if (!ranges.length) return null;
1653
- const hit = (a, b) => ranges.some(([x, y]) => !(y < a || x > b));
1654
- const latin = hit(0, 255) || hit(305, 305);
1655
- const latinExt = hit(256, 591) || hit(7680, 7935);
1656
- const greek = hit(880, 1023);
1657
- const cyr = hit(1024, 1279);
1658
- const viet = hit(7840, 7929) || hit(258, 259) || hit(416, 417) || hit(431, 432);
1659
- if (viet) return "vietnamese";
1660
- if (cyr) return "cyrillic";
1661
- if (greek) return "greek";
1662
- if (latinExt) return "latin-ext";
1663
- if (latin) return "latin";
1664
- return null;
1665
- }
1666
- function buildSimpleExcluder(ex = {}) {
1667
- const famSet = new Set((ex.families || []).map((s) => String(s).toLowerCase()));
1668
- const domSet = new Set((ex.domains || []).map((s) => String(s).toLowerCase()));
1669
- const subSet = new Set((ex.subsets || []).map((s) => String(s).toLowerCase()));
1670
- return (meta, parsedRanges) => {
1671
- if (famSet.size && famSet.has(meta.family.toLowerCase())) return true;
1672
- if (domSet.size) {
1673
- for (const u of meta.srcUrls) {
1674
- try {
1675
- if (domSet.has(new URL(u).host.toLowerCase())) return true;
1676
- } catch (e) {
1677
- }
1678
- }
1679
- }
1680
- if (subSet.size) {
1681
- const label = subsetFromRanges(parsedRanges);
1682
- if (label && subSet.has(label)) return true;
1683
- }
1684
- return false;
1685
- };
1686
- }
1687
1929
  const simpleExcluder = buildSimpleExcluder(exclude);
1688
- function dedupeFontFaces(cssText) {
1689
- var _a2, _b2, _c2, _d2, _e2, _f2;
1690
- if (!cssText) return cssText;
1691
- const FACE_RE2 = /@font-face[^{}]*\{[^}]*\}/gi;
1692
- const seen = /* @__PURE__ */ new Set();
1693
- const out = [];
1694
- for (const block of cssText.match(FACE_RE2) || []) {
1695
- const familyRaw = ((_a2 = block.match(/font-family:\s*([^;]+);/i)) == null ? void 0 : _a2[1]) || "";
1696
- const family = pickPrimaryFamily(familyRaw);
1697
- const weightSpec = (((_b2 = block.match(/font-weight:\s*([^;]+);/i)) == null ? void 0 : _b2[1]) || "400").trim();
1698
- const styleSpec = (((_c2 = block.match(/font-style:\s*([^;]+);/i)) == null ? void 0 : _c2[1]) || "normal").trim();
1699
- const stretchSpec = (((_d2 = block.match(/font-stretch:\s*([^;]+);/i)) == null ? void 0 : _d2[1]) || "100%").trim();
1700
- const urange = (((_e2 = block.match(/unicode-range:\s*([^;]+);/i)) == null ? void 0 : _e2[1]) || "").trim();
1701
- const srcRaw = (((_f2 = block.match(/src\s*:\s*([^;]+);/i)) == null ? void 0 : _f2[1]) || "").trim();
1702
- const urls = extractSrcUrls(srcRaw, location.href);
1703
- const srcPart = urls.length ? urls.map((u) => String(u).toLowerCase()).sort().join("|") : srcRaw.toLowerCase();
1704
- const key = [
1705
- String(family || "").toLowerCase(),
1706
- weightSpec,
1707
- styleSpec,
1708
- stretchSpec,
1709
- urange.toLowerCase(),
1710
- srcPart
1711
- ].join("|");
1712
- if (!seen.has(key)) {
1713
- seen.add(key);
1714
- out.push(block);
1715
- }
1716
- }
1717
- if (out.length === 0) return cssText;
1718
- let i = 0;
1719
- return cssText.replace(FACE_RE2, () => out[i++] || "");
1720
- }
1721
- function buildFontsCacheKey() {
1722
- const req = Array.from(required || []).sort().join("|");
1723
- const ex = exclude ? JSON.stringify({
1724
- families: (exclude.families || []).map((s) => String(s).toLowerCase()).sort(),
1725
- domains: (exclude.domains || []).map((s) => String(s).toLowerCase()).sort(),
1726
- subsets: (exclude.subsets || []).map((s) => String(s).toLowerCase()).sort()
1727
- }) : "";
1728
- const lf = (localFonts || []).map((f) => `${(f.family || "").toLowerCase()}::${f.weight || "normal"}::${f.style || "normal"}::${f.src || ""}`).sort().join("|");
1729
- const px = useProxy || "";
1730
- return `fonts-embed-css::req=${req}::ex=${ex}::lf=${lf}::px=${px}`;
1731
- }
1732
- const cacheKey = buildFontsCacheKey();
1930
+ const cacheKey = buildFontsCacheKey(required, exclude, localFonts, useProxy);
1733
1931
  if ((_a = cache.resource) == null ? void 0 : _a.has(cacheKey)) {
1734
- const css = cache.resource.get(cacheKey);
1735
- return css;
1932
+ return cache.resource.get(cacheKey);
1736
1933
  }
1737
1934
  const requiredFamilies = familiesFromRequired(required);
1738
1935
  const importUrls = [];
1936
+ const IMPORT_ANY_RE_LOCAL = IMPORT_ANY_RE;
1739
1937
  for (const styleTag of document.querySelectorAll("style")) {
1740
1938
  const cssText = styleTag.textContent || "";
1741
- for (const m of cssText.matchAll(IMPORT_RE)) {
1742
- const u = m[1];
1939
+ for (const m of cssText.matchAll(IMPORT_ANY_RE_LOCAL)) {
1940
+ const u = (m[2] || m[4] || "").trim();
1743
1941
  if (!u || isIconFont2(u)) continue;
1744
1942
  const hasLink = !!document.querySelector(`link[rel="stylesheet"][href="${u}"]`);
1745
1943
  if (!hasLink) importUrls.push(u);
@@ -1786,65 +1984,53 @@
1786
1984
  cssText = res.data;
1787
1985
  if (isIconFont2(link.href)) continue;
1788
1986
  }
1987
+ cssText = yield inlineImportsAndRewrite(cssText, link.href, useProxy);
1789
1988
  let facesOut = "";
1790
1989
  for (const face of cssText.match(FACE_RE) || []) {
1791
1990
  const famRaw = (((_b = face.match(/font-family:\s*([^;]+);/i)) == null ? void 0 : _b[1]) || "").trim();
1792
1991
  const family = pickPrimaryFamily(famRaw);
1793
- if (!family || isIconFont2(family)) {
1794
- continue;
1795
- }
1992
+ if (!family || isIconFont2(family)) continue;
1796
1993
  const weightSpec = (((_c = face.match(/font-weight:\s*([^;]+);/i)) == null ? void 0 : _c[1]) || "400").trim();
1797
1994
  const styleSpec = (((_d = face.match(/font-style:\s*([^;]+);/i)) == null ? void 0 : _d[1]) || "normal").trim();
1798
1995
  const stretchSpec = (((_e = face.match(/font-stretch:\s*([^;]+);/i)) == null ? void 0 : _e[1]) || "100%").trim();
1799
1996
  const urange = (((_f = face.match(/unicode-range:\s*([^;]+);/i)) == null ? void 0 : _f[1]) || "").trim();
1800
1997
  const srcRaw = (((_g = face.match(/src\s*:\s*([^;]+);/i)) == null ? void 0 : _g[1]) || "").trim();
1801
1998
  const srcUrls = extractSrcUrls(srcRaw, link.href);
1802
- if (!faceMatchesRequired(family, styleSpec, weightSpec, stretchSpec)) {
1803
- continue;
1804
- }
1999
+ if (!faceMatchesRequired(family, styleSpec, weightSpec, stretchSpec)) continue;
1805
2000
  const ranges = parseUnicodeRange(urange);
1806
- if (!unicodeIntersects(usedCodepoints, ranges)) {
1807
- continue;
1808
- }
2001
+ if (!unicodeIntersects(usedCodepoints, ranges)) continue;
1809
2002
  const meta = { family, weightSpec, styleSpec, stretchSpec, unicodeRange: urange, srcRaw, srcUrls, href: link.href };
1810
- if (exclude && simpleExcluder(meta, ranges)) {
1811
- continue;
1812
- }
1813
- const newFace = /url\(/i.test(srcRaw) ? yield inlineUrlsInCssBlock(face, link.href) : face;
1814
- facesOut += newFace + "";
2003
+ if (exclude && simpleExcluder(meta, ranges)) continue;
2004
+ const newFace = /url\(/i.test(srcRaw) ? yield inlineUrlsInCssBlock(face, link.href, useProxy) : face;
2005
+ facesOut += newFace;
1815
2006
  }
1816
- if (facesOut.trim()) finalCSS += facesOut + "";
2007
+ if (facesOut.trim()) finalCSS += facesOut;
1817
2008
  } catch (e) {
1818
- console.warn("[snapdom] Failed to process stylesheet:", link.href);
2009
+ console.warn("[snapDOM] Failed to process stylesheet:", link.href);
1819
2010
  }
1820
2011
  }
2012
+ const ctx = {
2013
+ requiredIndex,
2014
+ usedCodepoints,
2015
+ faceMatchesRequired,
2016
+ simpleExcluder: exclude ? buildSimpleExcluder(exclude) : null,
2017
+ useProxy,
2018
+ visitedSheets: /* @__PURE__ */ new Set(),
2019
+ depth: 0
2020
+ };
1821
2021
  for (const sheet of document.styleSheets) {
2022
+ if (sheet.href && linkNodes.some((l) => l.href === sheet.href)) continue;
1822
2023
  try {
1823
- if (sheet.href && linkNodes.some((l) => l.href === sheet.href)) continue;
1824
- const rules = sheet.cssRules || [];
1825
- for (const rule of rules) {
1826
- if (rule.type !== CSSRule.FONT_FACE_RULE) continue;
1827
- const famRaw = (rule.style.getPropertyValue("font-family") || "").trim();
1828
- const family = pickPrimaryFamily(famRaw);
1829
- if (!family || isIconFont2(family)) continue;
1830
- const weightSpec = (rule.style.getPropertyValue("font-weight") || "400").trim();
1831
- const styleSpec = (rule.style.getPropertyValue("font-style") || "normal").trim();
1832
- const stretchSpec = (rule.style.getPropertyValue("font-stretch") || "100%").trim();
1833
- const srcRaw = (rule.style.getPropertyValue("src") || "").trim();
1834
- const urange = (rule.style.getPropertyValue("unicode-range") || "").trim();
1835
- const srcUrls = extractSrcUrls(srcRaw, sheet.href || location.href);
1836
- if (!faceMatchesRequired(family, styleSpec, weightSpec, stretchSpec)) continue;
1837
- const ranges = parseUnicodeRange(urange);
1838
- if (!unicodeIntersects(usedCodepoints, ranges)) continue;
1839
- const meta = { family, weightSpec, styleSpec, stretchSpec, unicodeRange: urange, srcRaw, srcUrls, href: sheet.href || location.href };
1840
- if (exclude && simpleExcluder(meta, ranges)) continue;
1841
- if (/url\(/i.test(srcRaw)) {
1842
- const inlinedSrc = yield inlineUrlsInCssBlock(srcRaw, sheet.href || location.href);
1843
- finalCSS += `@font-face{font-family:${family};src:${inlinedSrc};font-style:${styleSpec};font-weight:${weightSpec};font-stretch:${stretchSpec};${urange ? `unicode-range:${urange};` : ""}}`;
1844
- } else {
1845
- finalCSS += `@font-face{font-family:${family};src:${srcRaw};font-style:${styleSpec};font-weight:${weightSpec};font-stretch:${stretchSpec};${urange ? `unicode-range:${urange};` : ""}}`;
1846
- }
1847
- }
2024
+ const rootHref = sheet.href || location.href;
2025
+ if (rootHref) ctx.visitedSheets.add(rootHref);
2026
+ yield collectFacesFromSheet(
2027
+ sheet,
2028
+ rootHref,
2029
+ (faceCss) => __async(null, null, function* () {
2030
+ finalCSS += faceCss;
2031
+ }),
2032
+ ctx
2033
+ );
1848
2034
  } catch (e) {
1849
2035
  }
1850
2036
  }
@@ -1873,7 +2059,7 @@
1873
2059
  continue;
1874
2060
  }
1875
2061
  } catch (e) {
1876
- console.warn("[snapdom] Failed to fetch dynamic font src:", f._snapdomSrc);
2062
+ console.warn("[snapDOM] Failed to fetch dynamic font src:", f._snapdomSrc);
1877
2063
  continue;
1878
2064
  }
1879
2065
  }
@@ -1908,7 +2094,7 @@
1908
2094
  continue;
1909
2095
  }
1910
2096
  } catch (e) {
1911
- console.warn("[snapdom] Failed to fetch localFonts src:", src);
2097
+ console.warn("[snapDOM] Failed to fetch localFonts src:", src);
1912
2098
  continue;
1913
2099
  }
1914
2100
  }
@@ -1999,7 +2185,7 @@
1999
2185
  if (fams.length === 0) return;
2000
2186
  const warmupOnce = () => {
2001
2187
  const container = document.createElement("div");
2002
- container.style.cssText = `position: absolute !important;left: -9999px !important;top: 0 !important;opacity: 0 !important;pointer-events: none !important;contain: layout size style;`;
2188
+ container.style.cssText = "position:absolute!important;left:-9999px!important;top:0!important;opacity:0!important;pointer-events:none!important;contain:layout size style;";
2003
2189
  for (const fam of fams) {
2004
2190
  const span = document.createElement("span");
2005
2191
  span.textContent = "AaBbGg1234\xC1\xC9\xCD\xD3\xDA\xE7\xF1\u2014\u221E";
@@ -2360,9 +2546,6 @@
2360
2546
  const fontSize = parseInt(style.fontSize) || 32;
2361
2547
  const fontWeight = parseInt(style.fontWeight) || false;
2362
2548
  const color = style.color || "#000";
2363
- const display = style.display;
2364
- const width = parseFloat(style.width);
2365
- const height = parseFloat(style.height);
2366
2549
  const borderStyle = style.borderStyle;
2367
2550
  const borderWidth = parseFloat(style.borderWidth);
2368
2551
  const transform = style.transform;
@@ -2370,14 +2553,13 @@
2370
2553
  const hasExplicitContent = rawContent !== "none" && cleanContent !== "";
2371
2554
  const hasBg = bg && bg !== "none";
2372
2555
  const hasBgColor = bgColor && bgColor !== "transparent" && bgColor !== "rgba(0, 0, 0, 0)";
2373
- const hasBox2 = display !== "inline" && (width > 0 || height > 0);
2374
2556
  const hasBorder = borderStyle && borderStyle !== "none" && borderWidth > 0;
2375
2557
  const hasTransform = transform && transform !== "none";
2376
2558
  const shouldRender = hasExplicitContent || hasBg || hasBgColor || hasBorder || hasTransform;
2377
2559
  if (!shouldRender) {
2378
2560
  if (incs && incs.length && source.parentElement) {
2379
2561
  const map = __siblingCounters.get(source.parentElement) || /* @__PURE__ */ new Map();
2380
- for (const { name, num } of incs) {
2562
+ for (const { name } of incs) {
2381
2563
  if (!name) continue;
2382
2564
  const baseWithSibs = withSiblingOverrides(source, counterCtx);
2383
2565
  const derived = deriveCounterCtxForPseudo(source, getComputedStyle(source, pseudo), baseWithSibs);
@@ -3079,19 +3261,50 @@
3079
3261
  }
3080
3262
 
3081
3263
  // src/core/capture.js
3264
+ function stripRootShadows(originalEl, cloneRoot) {
3265
+ if (!originalEl || !cloneRoot || !cloneRoot.style) return;
3266
+ const cs = getComputedStyle(originalEl);
3267
+ try {
3268
+ cloneRoot.style.boxShadow = "none";
3269
+ } catch (e) {
3270
+ }
3271
+ try {
3272
+ cloneRoot.style.textShadow = "none";
3273
+ } catch (e) {
3274
+ }
3275
+ try {
3276
+ cloneRoot.style.outline = "none";
3277
+ } catch (e) {
3278
+ }
3279
+ const f = cs.filter || "";
3280
+ const cleaned = f.replace(/\bblur\([^()]*\)\s*/gi, "").replace(/\bdrop-shadow\([^()]*\)\s*/gi, "").trim().replace(/\s+/g, " ");
3281
+ try {
3282
+ cloneRoot.style.filter = cleaned.length ? cleaned : "none";
3283
+ } catch (e) {
3284
+ }
3285
+ }
3082
3286
  function captureDOM(element, options) {
3083
3287
  return __async(this, null, function* () {
3084
3288
  if (!element) throw new Error("Element cannot be null or undefined");
3085
3289
  applyCachePolicy(options.cache);
3086
3290
  const fast = options.fast;
3291
+ const straighten = !!options.straighten;
3292
+ const noShadows = !!options.noShadows;
3087
3293
  let clone, classCSS, styleCache;
3088
3294
  let fontsCSS = "";
3089
3295
  let baseCSS = "";
3090
3296
  let dataURL;
3091
3297
  let svgString;
3298
+ let rootTransform2D = null;
3092
3299
  const undoClamp = lineClamp(element);
3093
3300
  try {
3094
3301
  ({ clone, classCSS, styleCache } = yield prepareClone(element, options));
3302
+ if (straighten && clone) {
3303
+ rootTransform2D = normalizeRootTransforms(element, clone);
3304
+ }
3305
+ if (noShadows && clone) {
3306
+ stripRootShadows(element, clone);
3307
+ }
3095
3308
  } finally {
3096
3309
  undoClamp();
3097
3310
  }
@@ -3116,7 +3329,7 @@
3116
3329
  const families = new Set(
3117
3330
  Array.from(required).map((k) => String(k).split("__")[0]).filter(Boolean)
3118
3331
  );
3119
- yield ensureFontsReady(families, 2);
3332
+ yield ensureFontsReady(families, 1);
3120
3333
  }
3121
3334
  fontsCSS = yield embedCustomFonts({
3122
3335
  required,
@@ -3165,61 +3378,7 @@
3165
3378
  b = Math.max(b, extY + Math.max(oy, 0));
3166
3379
  t = Math.max(t, extY + Math.max(-oy, 0));
3167
3380
  }
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));
3189
- }
3190
- return { top: Math.ceil(t), right: Math.ceil(r), bottom: Math.ceil(b2), left: Math.ceil(l) };
3191
- }
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);
3381
+ return { bleed: { top: Math.ceil(t), right: Math.ceil(r), bottom: Math.ceil(b), left: Math.ceil(l) }, has: found };
3223
3382
  }
3224
3383
  const rect = element.getBoundingClientRect();
3225
3384
  const w0 = Math.max(1, Math.ceil(element.offsetWidth || parseFloat(csEl.width) || rect.width || 1));
@@ -3248,28 +3407,38 @@
3248
3407
  h = h0;
3249
3408
  }
3250
3409
  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);
3254
- const TOTAL = readTotalTransformMatrix({
3255
- baseTransform: baseTransform2,
3256
- rotate: ind2.rotate || "0deg",
3257
- scale: ind2.scale,
3258
- translate: ind2.translate
3259
- });
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;
3410
+ if (straighten && rootTransform2D && Number.isFinite(rootTransform2D.a)) {
3411
+ const M2 = { a: rootTransform2D.a, b: rootTransform2D.b || 0, c: rootTransform2D.c || 0, d: rootTransform2D.d || 1, e: 0, f: 0 };
3412
+ const bb2 = bboxWithOriginFull(w0, h0, M2, 0, 0);
3413
+ minX = bb2.minX;
3414
+ minY = bb2.minY;
3415
+ maxX = bb2.maxX;
3416
+ maxY = bb2.maxY;
3417
+ } else {
3418
+ const useTFBBox = !straighten && hasTFBBox(element);
3419
+ if (useTFBBox) {
3420
+ const baseTransform2 = csEl.transform && csEl.transform !== "none" ? csEl.transform : "";
3421
+ const ind2 = readIndividualTransforms(element);
3422
+ const TOTAL = readTotalTransformMatrix({
3423
+ baseTransform: baseTransform2,
3424
+ rotate: ind2.rotate || "0deg",
3425
+ scale: ind2.scale,
3426
+ translate: ind2.translate
3427
+ });
3428
+ const { ox: ox2, oy: oy2 } = parseTransformOriginPx(csEl, w0, h0);
3429
+ const M = TOTAL.is2D ? TOTAL : new DOMMatrix(TOTAL.toString());
3430
+ const bb = bboxWithOriginFull(w0, h0, M, ox2, oy2);
3431
+ minX = bb.minX;
3432
+ minY = bb.minY;
3433
+ maxX = bb.maxX;
3434
+ maxY = bb.maxY;
3435
+ }
3267
3436
  }
3268
3437
  const bleedShadow = parseBoxShadow(csEl);
3269
3438
  const bleedBlur = parseFilterBlur(csEl);
3270
3439
  const bleedOutline = parseOutline(csEl);
3271
3440
  const drop = parseFilterDropShadows(csEl);
3272
- const bleed = {
3441
+ const bleed = noShadows ? { top: 0, right: 0, bottom: 0, left: 0 } : {
3273
3442
  top: bleedShadow.top + bleedBlur.top + bleedOutline.top + drop.bleed.top,
3274
3443
  right: bleedShadow.right + bleedBlur.right + bleedOutline.right + drop.bleed.right,
3275
3444
  bottom: bleedShadow.bottom + bleedBlur.bottom + bleedOutline.bottom + drop.bleed.bottom,
@@ -3284,7 +3453,9 @@
3284
3453
  const outW = Math.max(1, Math.round(vbW0 * (hasW || hasH ? w / w0 : 1)));
3285
3454
  const outH = Math.max(1, Math.round(vbH0 * (hasH || hasW ? h / h0 : 1)));
3286
3455
  const svgNS = "http://www.w3.org/2000/svg";
3287
- const pad = isSafari() ? 1 : 0;
3456
+ const basePad = isSafari() ? 1 : 0;
3457
+ const extraPad = straighten ? 1 : 0;
3458
+ const pad = basePad + extraPad;
3288
3459
  const fo = document.createElementNS(svgNS, "foreignObject");
3289
3460
  const vbMinX = Math.floor(minX);
3290
3461
  const vbMinY = Math.floor(minY);
@@ -3309,20 +3480,7 @@
3309
3480
  const vbW = vbW0 + pad * 2;
3310
3481
  const vbH = vbH0 + pad * 2;
3311
3482
  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
- };
3483
+ options.meta = { w0, h0, vbW, vbH, targetW: w, targetH: h };
3326
3484
  const svgOutW = isSafari() && wantsSize ? vbW : outW + pad * 2;
3327
3485
  const svgOutH = isSafari() && wantsSize ? vbH : outH + pad * 2;
3328
3486
  const svgHeader = `<svg xmlns="${svgNS}" width="${svgOutW}" height="${svgOutH}" viewBox="0 0 ${vbW} ${vbH}">`;
@@ -3337,6 +3495,116 @@
3337
3495
  return dataURL;
3338
3496
  });
3339
3497
  }
3498
+ function normalizeRootTransforms(originalEl, cloneRoot) {
3499
+ if (!originalEl || !cloneRoot || !cloneRoot.style) return null;
3500
+ const cs = getComputedStyle(originalEl);
3501
+ try {
3502
+ cloneRoot.style.transformOrigin = "0 0";
3503
+ } catch (e) {
3504
+ }
3505
+ try {
3506
+ if ("translate" in cloneRoot.style) cloneRoot.style.translate = "none";
3507
+ if ("rotate" in cloneRoot.style) cloneRoot.style.rotate = "none";
3508
+ } catch (e) {
3509
+ }
3510
+ const tr = cs.transform || "none";
3511
+ if (!tr || tr === "none") {
3512
+ try {
3513
+ const M = matrixFromComputed(originalEl);
3514
+ if (M.a === 1 && M.b === 0 && M.c === 0 && M.d === 1) {
3515
+ cloneRoot.style.transform = "none";
3516
+ return { a: 1, b: 0, c: 0, d: 1 };
3517
+ }
3518
+ } catch (e) {
3519
+ }
3520
+ }
3521
+ const m2d = tr.match(/^matrix\(\s*([^)]+)\)$/i);
3522
+ if (m2d) {
3523
+ const nums = m2d[1].split(",").map((v) => parseFloat(v.trim()));
3524
+ if (nums.length === 6 && nums.every(Number.isFinite)) {
3525
+ const [a, b, c, d] = nums;
3526
+ const scaleX = Math.sqrt(a * a + b * b) || 0;
3527
+ let a1 = 0, b1 = 0, shear = 0, c2 = 0, d2 = 0, scaleY = 0;
3528
+ if (scaleX > 0) {
3529
+ a1 = a / scaleX;
3530
+ b1 = b / scaleX;
3531
+ shear = a1 * c + b1 * d;
3532
+ c2 = c - a1 * shear;
3533
+ d2 = d - b1 * shear;
3534
+ scaleY = Math.sqrt(c2 * c2 + d2 * d2) || 0;
3535
+ if (scaleY > 0) shear = shear / scaleY;
3536
+ else shear = 0;
3537
+ }
3538
+ const aP = scaleX;
3539
+ const bP = 0;
3540
+ const cP = shear * scaleY;
3541
+ const dP = scaleY;
3542
+ try {
3543
+ cloneRoot.style.transform = `matrix(${aP}, ${bP}, ${cP}, ${dP}, 0, 0)`;
3544
+ } catch (e) {
3545
+ }
3546
+ return { a: aP, b: bP, c: cP, d: dP };
3547
+ }
3548
+ }
3549
+ try {
3550
+ const legacy = String(tr).trim();
3551
+ cloneRoot.style.transform = legacy + " translate(0px, 0px) rotate(0deg)";
3552
+ return null;
3553
+ } catch (e) {
3554
+ return null;
3555
+ }
3556
+ }
3557
+ function parseBoxShadow(cs) {
3558
+ var _a;
3559
+ const v = cs.boxShadow || "";
3560
+ if (!v || v === "none") return { top: 0, right: 0, bottom: 0, left: 0 };
3561
+ const parts = v.split(/\),(?=(?:[^()]*\([^()]*\))*[^()]*$)/).map((s) => s.trim());
3562
+ let t = 0, r = 0, b2 = 0, l = 0;
3563
+ for (const part of parts) {
3564
+ const nums = ((_a = part.match(/-?\d+(\.\d+)?px/g)) == null ? void 0 : _a.map((n) => parseFloat(n))) || [];
3565
+ if (nums.length < 2) continue;
3566
+ const [ox2, oy2, blur = 0, spread = 0] = nums;
3567
+ const extX = Math.abs(ox2) + blur + spread;
3568
+ const extY = Math.abs(oy2) + blur + spread;
3569
+ r = Math.max(r, extX + Math.max(ox2, 0));
3570
+ l = Math.max(l, extX + Math.max(-ox2, 0));
3571
+ b2 = Math.max(b2, extY + Math.max(oy2, 0));
3572
+ t = Math.max(t, extY + Math.max(-oy2, 0));
3573
+ }
3574
+ return { top: Math.ceil(t), right: Math.ceil(r), bottom: Math.ceil(b2), left: Math.ceil(l) };
3575
+ }
3576
+ function parseFilterBlur(cs) {
3577
+ const m = (cs.filter || "").match(/blur\(\s*([0-9.]+)px\s*\)/);
3578
+ const b2 = m ? Math.ceil(parseFloat(m[1]) || 0) : 0;
3579
+ return { top: b2, right: b2, bottom: b2, left: b2 };
3580
+ }
3581
+ function parseOutline(cs) {
3582
+ if ((cs.outlineStyle || "none") === "none") return { top: 0, right: 0, bottom: 0, left: 0 };
3583
+ const w2 = Math.ceil(parseFloat(cs.outlineWidth || "0") || 0);
3584
+ return { top: w2, right: w2, bottom: w2, left: w2 };
3585
+ }
3586
+ function bboxWithOriginFull(w2, h2, M, ox2, oy2) {
3587
+ const a2 = M.a, b2 = M.b, c2 = M.c, d2 = M.d, e2 = M.e || 0, f2 = M.f || 0;
3588
+ function pt(x, y) {
3589
+ let X = x - ox2, Y = y - oy2;
3590
+ let X2 = a2 * X + c2 * Y, Y2 = b2 * X + d2 * Y;
3591
+ X2 += ox2 + e2;
3592
+ Y2 += oy2 + f2;
3593
+ return [X2, Y2];
3594
+ }
3595
+ const P = [pt(0, 0), pt(w2, 0), pt(0, h2), pt(w2, h2)];
3596
+ let minX2 = Infinity, minY2 = Infinity, maxX2 = -Infinity, maxY2 = -Infinity;
3597
+ for (const [X, Y] of P) {
3598
+ if (X < minX2) minX2 = X;
3599
+ if (Y < minY2) minY2 = Y;
3600
+ if (X > maxX2) maxX2 = X;
3601
+ if (Y > maxY2) maxY2 = Y;
3602
+ }
3603
+ return { minX: minX2, minY: minY2, maxX: maxX2, maxY: maxY2, width: maxX2 - minX2, height: maxY2 - minY2 };
3604
+ }
3605
+ function hasTFBBox(el) {
3606
+ return hasBBoxAffectingTransform(el);
3607
+ }
3340
3608
  function matrixFromComputed(el) {
3341
3609
  const tr = getComputedStyle(el).transform;
3342
3610
  if (!tr || tr === "none") return new DOMMatrix();
@@ -3478,7 +3746,7 @@
3478
3746
  return "soft";
3479
3747
  }
3480
3748
  function createContext(options = {}) {
3481
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r;
3749
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t;
3482
3750
  const resolvedFormat = (_a = options.format) != null ? _a : "png";
3483
3751
  const cachePolicy = normalizeCachePolicy(options.cache);
3484
3752
  return {
@@ -3491,20 +3759,20 @@
3491
3759
  excludeMode: (_f = options.excludeMode) != null ? _f : "hide",
3492
3760
  filter: (_g = options.filter) != null ? _g : null,
3493
3761
  filterMode: (_h = options.filterMode) != null ? _h : "hide",
3762
+ // Placeholders
3494
3763
  placeholders: options.placeholders !== false,
3495
3764
  // default true
3496
- // Fuentes
3765
+ // Fonts
3497
3766
  embedFonts: (_i = options.embedFonts) != null ? _i : false,
3498
3767
  iconFonts: Array.isArray(options.iconFonts) ? options.iconFonts : options.iconFonts ? [options.iconFonts] : [],
3499
3768
  localFonts: Array.isArray(options.localFonts) ? options.localFonts : [],
3500
3769
  excludeFonts: (_j = options.excludeFonts) != null ? _j : void 0,
3501
3770
  fallbackURL: (_k = options.fallbackURL) != null ? _k : void 0,
3502
- /** Cache policy (disabled|full|auto|soft). Default: "soft" */
3503
3771
  /** @type {CachePolicy} */
3504
3772
  cache: cachePolicy,
3505
- // Red
3773
+ // Network
3506
3774
  useProxy: typeof options.useProxy === "string" ? options.useProxy : "",
3507
- // Salida
3775
+ // Output
3508
3776
  width: (_l = options.width) != null ? _l : null,
3509
3777
  height: (_m = options.height) != null ? _m : null,
3510
3778
  format: resolvedFormat,
@@ -3512,8 +3780,11 @@
3512
3780
  quality: (_o = options.quality) != null ? _o : 0.92,
3513
3781
  dpr: (_p = options.dpr) != null ? _p : window.devicePixelRatio || 1,
3514
3782
  backgroundColor: (_q = options.backgroundColor) != null ? _q : ["jpg", "jpeg", "webp"].includes(resolvedFormat) ? "#ffffff" : null,
3515
- filename: (_r = options.filename) != null ? _r : "snapDOM"
3516
- // Plugins (reservado para futura activación)
3783
+ filename: (_r = options.filename) != null ? _r : "snapDOM",
3784
+ // NEW flags (user-friendly)
3785
+ straighten: (_s = options.straighten) != null ? _s : false,
3786
+ noShadows: (_t = options.noShadows) != null ? _t : false
3787
+ // Plugins (reservado)
3517
3788
  // plugins: normalizePlugins(...),
3518
3789
  };
3519
3790
  }
@@ -3708,9 +3979,20 @@
3708
3979
  const k = height / Math.max(1, refH);
3709
3980
  img.style.height = `${height}px`;
3710
3981
  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`;
3982
+ } else {
3983
+ const cssW = Math.round(img.naturalWidth * scale);
3984
+ const cssH = Math.round(img.naturalHeight * scale);
3985
+ img.style.width = `${cssW}px`;
3986
+ img.style.height = `${cssH}px`;
3987
+ if (typeof url === "string" && url.startsWith("data:image/svg+xml")) {
3988
+ try {
3989
+ const decoded = decodeURIComponent(url.split(",")[1]);
3990
+ const patched = decoded.replace(/width="[^"]*"/, `width="${cssW}"`).replace(/height="[^"]*"/, `height="${cssH}"`);
3991
+ url = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(patched)}`;
3992
+ img.src = url;
3993
+ } catch (e) {
3994
+ }
3995
+ }
3714
3996
  }
3715
3997
  return img;
3716
3998
  });
@@ -3772,7 +4054,7 @@
3772
4054
  yield safariWarmup(element, userOptions);
3773
4055
  console.log("Iteraci\xF3n n\xFAmero:", i);
3774
4056
  _safariWarmup = false;
3775
- } catch (error) {
4057
+ } catch (e) {
3776
4058
  }
3777
4059
  }
3778
4060
  }
@@ -3780,7 +4062,7 @@
3780
4062
  if (!context.snap) {
3781
4063
  context.snap = {
3782
4064
  toPng: (el, opts) => snapdom.toPng(el, opts),
3783
- toImg: (el, opts) => snapdom.toImg(el, opts)
4065
+ toSvg: (el, opts) => snapdom.toSvg(el, opts)
3784
4066
  };
3785
4067
  }
3786
4068
  return snapdom.capture(element, context, INTERNAL_TOKEN);
@@ -3803,6 +4085,7 @@
3803
4085
  url,
3804
4086
  toRaw: () => url,
3805
4087
  toImg: (opts) => toImg(url, ensureContext(opts)),
4088
+ toSvg: (opts) => toImg(url, ensureContext(opts)),
3806
4089
  toCanvas: (opts) => toCanvas(url, ensureContext(opts)),
3807
4090
  toBlob: (opts) => toBlob(url, ensureContext(opts)),
3808
4091
  toPng: withFormat("png"),
@@ -3813,6 +4096,7 @@
3813
4096
  });
3814
4097
  snapdom.toRaw = (el, options) => snapdom(el, options).then((result) => result.toRaw());
3815
4098
  snapdom.toImg = (el, options) => snapdom(el, options).then((result) => result.toImg());
4099
+ snapdom.toSvg = (el, options) => snapdom(el, options).then((result) => result.toSvg());
3816
4100
  snapdom.toCanvas = (el, options) => snapdom(el, options).then((result) => result.toCanvas());
3817
4101
  snapdom.toBlob = (el, options) => snapdom(el, options).then((result) => result.toBlob());
3818
4102
  snapdom.toPng = (el, options) => snapdom(el, __spreadProps(__spreadValues({}, options), { format: "png" })).then((result) => result.toPng());
@@ -3954,7 +4238,7 @@
3954
4238
  const families = new Set(
3955
4239
  Array.from(required).map((k) => String(k).split("__")[0]).filter(Boolean)
3956
4240
  );
3957
- yield ensureFontsReady(families, 2);
4241
+ yield ensureFontsReady(families, 3);
3958
4242
  }
3959
4243
  yield embedCustomFonts({
3960
4244
  required,