@pixldocs/canvas-renderer 0.5.222 → 0.5.225

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/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const index = require("./index-BKJxI43i.cjs");
3
+ const index = require("./index-6nrov1rx.cjs");
4
4
  exports.DEPLOYMENT_VERSION_MARKER = index.DEPLOYMENT_VERSION_MARKER;
5
5
  exports.FONT_FALLBACK_DEVANAGARI = index.FONT_FALLBACK_DEVANAGARI;
6
6
  exports.FONT_FALLBACK_MATH = index.FONT_FALLBACK_MATH;
package/dist/index.d.ts CHANGED
@@ -9,6 +9,7 @@ import { isBundledAssetUrl } from '../../../src/lib/canvasImageLoader';
9
9
  import { isPrivateUrl } from '../../../src/lib/canvasImageLoader';
10
10
  import { jsPDF } from 'jspdf';
11
11
  import { JSX as JSX_2 } from 'react/jsx-runtime';
12
+ import { ReactNode } from 'react';
12
13
  import { resolveBlurElementExactIdsFromFlatFormKeys } from '../../../src/lib/previewBlur';
13
14
  import { ResolveBlurOptions } from '../../../src/lib/previewBlur';
14
15
  import { SectionFormState } from '../../../src/lib/inferFormSchemaFromTemplate';
@@ -430,6 +431,8 @@ declare interface PixldocsPreviewBaseProps {
430
431
  onReady?: () => void;
431
432
  /** Called when resolution or rendering fails */
432
433
  onError?: (error: Error) => void;
434
+ /** Optional custom UI shown while the live preview is preparing */
435
+ loadingFallback?: ReactNode;
433
436
  /** Allow package previews to skip the blocking font-ready wait used by app preview */
434
437
  skipFontReadyWait?: boolean;
435
438
  /**
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { D, F, o, q, s, P, t, u, v, w, x, y, z, B, C, E, G, H, I, J, K, L, M, b, N, O, Q, R, S, U, V, W, X, Y, Z, _, $, a0, a1, a2, a3, a4, a5 } from "./index-DxL--cfL.js";
1
+ import { D, F, o, q, s, P, t, u, v, w, x, y, z, B, C, E, G, H, I, J, K, L, M, b, N, O, Q, R, S, U, V, W, X, Y, Z, _, $, a0, a1, a2, a3, a4, a5 } from "./index-BpViFQMO.js";
2
2
  export {
3
3
  D as DEPLOYMENT_VERSION_MARKER,
4
4
  F as FONT_FALLBACK_DEVANAGARI,
@@ -3,7 +3,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const jspdf = require("jspdf");
4
4
  const svg2pdf_js = require("svg2pdf.js");
5
5
  const fabric = require("fabric");
6
- const index = require("./index-BKJxI43i.cjs");
6
+ const index = require("./index-6nrov1rx.cjs");
7
7
  const pdfFonts = require("./pdfFonts-BTj2f465.cjs");
8
8
  function _interopNamespaceDefault(e) {
9
9
  const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
@@ -1003,6 +1003,100 @@ function parseInlineSvgStyleDeclarations(styleText) {
1003
1003
  };
1004
1004
  }).filter((x) => !!x);
1005
1005
  }
1006
+ function shortPdfDebug(value, max = 180) {
1007
+ if (!value) return null;
1008
+ const str = String(value).replace(/\s+/g, " ").trim();
1009
+ return str.length > max ? `${str.slice(0, max)}…` : str;
1010
+ }
1011
+ function logSvgGradientClipDiagnostics(stage, svg, context) {
1012
+ try {
1013
+ const gradients = Array.from(svg.querySelectorAll("linearGradient, radialGradient"));
1014
+ const images = Array.from(svg.querySelectorAll("image"));
1015
+ const nestedSvgs = Array.from(svg.querySelectorAll("svg")).filter((node) => node !== svg);
1016
+ const gradientRefs = [];
1017
+ Array.from(svg.querySelectorAll("*")).forEach((el) => {
1018
+ const fill = el.getAttribute("fill") || getInlineStyleValue(el, "fill");
1019
+ const stroke = el.getAttribute("stroke") || getInlineStyleValue(el, "stroke");
1020
+ const fillId = extractGradientIdFromPaint(fill);
1021
+ const strokeId = extractGradientIdFromPaint(stroke);
1022
+ if (!fillId && !strokeId) return;
1023
+ gradientRefs.push({
1024
+ tag: el.tagName,
1025
+ id: el.getAttribute("id"),
1026
+ className: el.getAttribute("class"),
1027
+ fill: shortPdfDebug(fill, 80),
1028
+ stroke: shortPdfDebug(stroke, 80),
1029
+ fillRule: el.getAttribute("fill-rule") || getInlineStyleValue(el, "fill-rule"),
1030
+ transform: shortPdfDebug(el.getAttribute("transform"), 120),
1031
+ dStart: shortPdfDebug(el.getAttribute("d"), 160)
1032
+ });
1033
+ });
1034
+ console.log("[client-pdf-export][gradient-svg-diag]", {
1035
+ stage,
1036
+ ...context,
1037
+ root: {
1038
+ width: svg.getAttribute("width"),
1039
+ height: svg.getAttribute("height"),
1040
+ viewBox: svg.getAttribute("viewBox")
1041
+ },
1042
+ counts: {
1043
+ gradients: gradients.length,
1044
+ gradientRefs: gradientRefs.length,
1045
+ images: images.length,
1046
+ nestedSvgs: nestedSvgs.length,
1047
+ paths: svg.querySelectorAll("path").length,
1048
+ clips: svg.querySelectorAll("clipPath").length,
1049
+ masks: svg.querySelectorAll("mask").length
1050
+ },
1051
+ gradients: gradients.slice(0, 8).map((g) => ({
1052
+ tag: g.tagName,
1053
+ id: g.getAttribute("id"),
1054
+ units: g.getAttribute("gradientUnits"),
1055
+ transform: g.getAttribute("gradientTransform"),
1056
+ x1: g.getAttribute("x1"),
1057
+ y1: g.getAttribute("y1"),
1058
+ x2: g.getAttribute("x2"),
1059
+ y2: g.getAttribute("y2"),
1060
+ stops: Array.from(g.querySelectorAll("stop")).map((s) => ({ offset: s.getAttribute("offset"), color: s.getAttribute("stop-color"), opacity: s.getAttribute("stop-opacity") })).slice(0, 5)
1061
+ })),
1062
+ images: images.slice(0, 8).map((img) => ({
1063
+ id: img.getAttribute("id"),
1064
+ href: shortPdfDebug(getSvgImageHref(img), 220),
1065
+ x: img.getAttribute("x"),
1066
+ y: img.getAttribute("y"),
1067
+ width: img.getAttribute("width"),
1068
+ height: img.getAttribute("height"),
1069
+ transform: shortPdfDebug(img.getAttribute("transform"), 140),
1070
+ clipPath: img.getAttribute("clip-path"),
1071
+ preserveAspectRatio: img.getAttribute("preserveAspectRatio")
1072
+ })),
1073
+ clipPaths: Array.from(svg.querySelectorAll("clipPath")).slice(0, 8).map((clip) => ({
1074
+ id: clip.getAttribute("id"),
1075
+ units: clip.getAttribute("clipPathUnits"),
1076
+ transform: shortPdfDebug(clip.getAttribute("transform"), 140),
1077
+ rects: Array.from(clip.querySelectorAll("rect")).slice(0, 3).map((rect) => ({
1078
+ x: rect.getAttribute("x"),
1079
+ y: rect.getAttribute("y"),
1080
+ width: rect.getAttribute("width"),
1081
+ height: rect.getAttribute("height"),
1082
+ transform: shortPdfDebug(rect.getAttribute("transform"), 140)
1083
+ }))
1084
+ })),
1085
+ nestedSvgs: nestedSvgs.slice(0, 8).map((node) => ({
1086
+ id: node.getAttribute("id"),
1087
+ x: node.getAttribute("x"),
1088
+ y: node.getAttribute("y"),
1089
+ width: node.getAttribute("width"),
1090
+ height: node.getAttribute("height"),
1091
+ viewBox: node.getAttribute("viewBox"),
1092
+ transform: shortPdfDebug(node.getAttribute("transform"), 140)
1093
+ })),
1094
+ gradientRefs: gradientRefs.slice(0, 10)
1095
+ });
1096
+ } catch (err) {
1097
+ console.warn("[client-pdf-export][gradient-svg-diag] failed", { stage, err });
1098
+ }
1099
+ }
1006
1100
  function logGradientBindingDiagnostics(svg) {
1007
1101
  const gradientCount = svg.querySelectorAll("linearGradient, radialGradient").length;
1008
1102
  if (gradientCount === 0) return;
@@ -1366,7 +1460,39 @@ function decodeSvgDataUri(href) {
1366
1460
  }
1367
1461
  }
1368
1462
  }
1369
- function inlineNestedSvgImageDataUris(svgString, domParser = new DOMParser()) {
1463
+ async function readNestedSvgImageMarkup(href) {
1464
+ if (!href) return null;
1465
+ if (href.startsWith("data:image/svg+xml")) return decodeSvgDataUri(href);
1466
+ if (/\.svg(?:[?#]|$)/i.test(href) || href.startsWith("blob:")) {
1467
+ const urls = [];
1468
+ const addUrl = (url) => {
1469
+ if (url && !urls.includes(url)) urls.push(url);
1470
+ };
1471
+ if (href.startsWith("blob:")) {
1472
+ addUrl(href);
1473
+ } else {
1474
+ addUrl(index.getProxiedImageUrl(href));
1475
+ addUrl(href);
1476
+ try {
1477
+ const proxyUrl = new URL(`${index.API_URL}/image-proxy`);
1478
+ proxyUrl.searchParams.set("url", href);
1479
+ addUrl(proxyUrl.toString());
1480
+ } catch {
1481
+ }
1482
+ }
1483
+ for (const url of urls) {
1484
+ try {
1485
+ const res = await fetch(url, { cache: "no-store", ...index.getImageProxyFetchOptions() });
1486
+ if (!res.ok) continue;
1487
+ const text = await res.text();
1488
+ if (/<svg[\s>]/i.test(text)) return text;
1489
+ } catch {
1490
+ }
1491
+ }
1492
+ }
1493
+ return null;
1494
+ }
1495
+ async function inlineNestedSvgImageDataUris(svgString, domParser = new DOMParser()) {
1370
1496
  var _a;
1371
1497
  try {
1372
1498
  const doc = domParser.parseFromString(svgString, "image/svg+xml");
@@ -1374,42 +1500,102 @@ function inlineNestedSvgImageDataUris(svgString, domParser = new DOMParser()) {
1374
1500
  const root = doc.documentElement;
1375
1501
  if (!root || root.tagName.toLowerCase() !== "svg") return svgString;
1376
1502
  let changed = false;
1503
+ let inlined = 0;
1504
+ let failed = 0;
1377
1505
  const images = Array.from(doc.querySelectorAll("image"));
1378
- for (const img of images) {
1506
+ logSvgGradientClipDiagnostics("inline-pass:start", root, { imageCount: images.length });
1507
+ for (const [imageIndex, img] of images.entries()) {
1379
1508
  const href = img.getAttribute("href") || img.getAttributeNS("http://www.w3.org/1999/xlink", "href");
1380
- if (!href || !href.startsWith("data:image/svg+xml")) continue;
1509
+ if (!href) continue;
1381
1510
  try {
1382
- const svgContent = decodeSvgDataUri(href);
1383
- if (!svgContent || !/<svg[\s>]/i.test(svgContent)) continue;
1511
+ const svgContent = await readNestedSvgImageMarkup(href);
1512
+ if (!svgContent || !/<svg[\s>]/i.test(svgContent)) {
1513
+ if (/\.svg(?:[?#]|$)/i.test(href) || href.startsWith("blob:") || href.startsWith("data:image/svg+xml")) {
1514
+ console.warn("[client-pdf-export][gradient-svg-diag] inline source not readable", {
1515
+ imageIndex,
1516
+ href: shortPdfDebug(href, 260),
1517
+ x: img.getAttribute("x"),
1518
+ y: img.getAttribute("y"),
1519
+ width: img.getAttribute("width"),
1520
+ height: img.getAttribute("height"),
1521
+ transform: shortPdfDebug(img.getAttribute("transform"), 160)
1522
+ });
1523
+ }
1524
+ continue;
1525
+ }
1384
1526
  const innerDoc = domParser.parseFromString(svgContent, "image/svg+xml");
1385
1527
  if (innerDoc.querySelector("parsererror")) continue;
1386
1528
  const innerSvg = innerDoc.documentElement;
1387
1529
  if (!innerSvg || innerSvg.tagName.toLowerCase() !== "svg") continue;
1530
+ const sourceId = img.getAttribute("id") || `image-${imageIndex}`;
1531
+ prefixSvgIds(innerSvg, `inline-${sourceId}`);
1388
1532
  const ix = parseFloat(img.getAttribute("x") || "0") || 0;
1389
1533
  const iy = parseFloat(img.getAttribute("y") || "0") || 0;
1390
1534
  const iw = parseFloat(img.getAttribute("width") || "0");
1391
1535
  const ih = parseFloat(img.getAttribute("height") || "0");
1392
- if (!(iw > 0 && ih > 0)) continue;
1393
- const nestedSvg = doc.importNode(innerSvg, true);
1394
- const vb = nestedSvg.getAttribute("viewBox");
1395
- if (!vb) {
1396
- nestedSvg.setAttribute("viewBox", `0 0 ${iw} ${ih}`);
1536
+ if (!(iw > 0 && ih > 0)) {
1537
+ console.warn("[client-pdf-export][gradient-svg-diag] inline skipped: image has invalid frame", {
1538
+ imageIndex,
1539
+ href: shortPdfDebug(href, 260),
1540
+ x: img.getAttribute("x"),
1541
+ y: img.getAttribute("y"),
1542
+ width: img.getAttribute("width"),
1543
+ height: img.getAttribute("height"),
1544
+ innerViewBox: innerSvg.getAttribute("viewBox")
1545
+ });
1546
+ continue;
1397
1547
  }
1398
- nestedSvg.setAttribute("x", "0");
1399
- nestedSvg.setAttribute("y", "0");
1400
- nestedSvg.setAttribute("width", String(iw));
1401
- nestedSvg.setAttribute("height", String(ih));
1402
- nestedSvg.setAttribute(
1403
- "preserveAspectRatio",
1404
- img.getAttribute("preserveAspectRatio") || nestedSvg.getAttribute("preserveAspectRatio") || "xMidYMid meet"
1405
- );
1406
1548
  const g = doc.createElementNS("http://www.w3.org/2000/svg", "g");
1407
1549
  const existingTransform = img.getAttribute("transform") || "";
1550
+ const vb = (innerSvg.getAttribute("viewBox") || "").trim().split(/[\s,]+/).map((n) => Number.parseFloat(n));
1551
+ const [vbX, vbY, vbW, vbH] = vb.length === 4 && vb.every((n) => Number.isFinite(n)) && vb[2] > 0 && vb[3] > 0 ? vb : [0, 0, iw, ih];
1552
+ const par = img.getAttribute("preserveAspectRatio") || innerSvg.getAttribute("preserveAspectRatio") || "xMidYMid meet";
1553
+ const parParts = par.trim().split(/\s+/).filter(Boolean);
1554
+ const align = parParts[0] === "defer" ? parParts[1] || "xMidYMid" : parParts[0] || "xMidYMid";
1555
+ const meetOrSlice = parParts[0] === "defer" ? parParts[2] || "meet" : parParts[1] || "meet";
1556
+ let sx = iw / vbW;
1557
+ let sy = ih / vbH;
1558
+ let ox = 0;
1559
+ let oy = 0;
1560
+ if (align !== "none") {
1561
+ const s = meetOrSlice === "slice" ? Math.max(sx, sy) : Math.min(sx, sy);
1562
+ sx = s;
1563
+ sy = s;
1564
+ const extraW = iw - vbW * s;
1565
+ const extraH = ih - vbH * s;
1566
+ if (align.includes("xMid")) ox = extraW / 2;
1567
+ else if (align.includes("xMax")) ox = extraW;
1568
+ if (align.includes("YMid")) oy = extraH / 2;
1569
+ else if (align.includes("YMax")) oy = extraH;
1570
+ }
1408
1571
  let transform = existingTransform;
1409
- transform += `${transform ? " " : ""}translate(${ix},${iy})`;
1572
+ transform += `${transform ? " " : ""}translate(${ix + ox},${iy + oy}) scale(${sx},${sy}) translate(${-vbX},${-vbY})`;
1410
1573
  if (transform) {
1411
1574
  g.setAttribute("transform", transform);
1412
1575
  }
1576
+ logSvgGradientClipDiagnostics("inline-pass:image-inlined", innerSvg, {
1577
+ imageIndex,
1578
+ sourceId,
1579
+ href: shortPdfDebug(href, 260),
1580
+ imageFrame: { x: ix, y: iy, width: iw, height: ih },
1581
+ innerRoot: {
1582
+ width: innerSvg.getAttribute("width"),
1583
+ height: innerSvg.getAttribute("height"),
1584
+ viewBox: innerSvg.getAttribute("viewBox"),
1585
+ preserveAspectRatio: innerSvg.getAttribute("preserveAspectRatio")
1586
+ },
1587
+ appliedTransform: transform
1588
+ });
1589
+ const imageClipPath = img.getAttribute("clip-path") || getInlineStyleValue(img, "clip-path");
1590
+ const dropImageClipPath = isRedundantImageClipPathForInlineSvg(root, imageClipPath, ix, iy, iw, ih);
1591
+ if (dropImageClipPath) {
1592
+ console.log("[client-pdf-export][gradient-svg-diag] dropped redundant inline SVG image clipPath", {
1593
+ imageIndex,
1594
+ sourceId,
1595
+ clipPath: imageClipPath,
1596
+ imageFrame: { x: ix, y: iy, width: iw, height: ih }
1597
+ });
1598
+ }
1413
1599
  const passthroughAttrs = /* @__PURE__ */ new Set([
1414
1600
  "id",
1415
1601
  "class",
@@ -1423,21 +1609,123 @@ function inlineNestedSvgImageDataUris(svgString, domParser = new DOMParser()) {
1423
1609
  "pointer-events"
1424
1610
  ]);
1425
1611
  Array.from(img.attributes).forEach((attr) => {
1612
+ if (dropImageClipPath && attr.name === "clip-path") return;
1613
+ if (dropImageClipPath && attr.name === "style") {
1614
+ const kept = parseInlineSvgStyleDeclarations(attr.value).filter((decl) => decl.key !== "clip-path").map((decl) => `${decl.key}: ${decl.value}`).join("; ");
1615
+ if (kept) g.setAttribute("style", kept);
1616
+ return;
1617
+ }
1426
1618
  if (passthroughAttrs.has(attr.name)) {
1427
1619
  g.setAttribute(attr.name, attr.value);
1428
1620
  }
1429
1621
  });
1430
- g.appendChild(nestedSvg);
1622
+ for (const child of Array.from(innerSvg.childNodes)) {
1623
+ g.appendChild(doc.importNode(child, true));
1624
+ }
1431
1625
  (_a = img.parentNode) == null ? void 0 : _a.replaceChild(g, img);
1432
1626
  changed = true;
1627
+ inlined++;
1433
1628
  } catch {
1629
+ failed++;
1434
1630
  }
1435
1631
  }
1632
+ if (inlined > 0 || failed > 0) {
1633
+ console.log("[client-pdf-export] nested SVG image inline pass", { inlined, failed, imageCount: images.length, changed });
1634
+ logSvgGradientClipDiagnostics("inline-pass:done", doc.documentElement, { inlined, failed, imageCount: images.length, changed });
1635
+ }
1436
1636
  return changed ? new XMLSerializer().serializeToString(doc.documentElement) : svgString;
1437
1637
  } catch {
1438
1638
  return svgString;
1439
1639
  }
1440
1640
  }
1641
+ function parseSvgLength(value, fallback) {
1642
+ if (!value) return fallback;
1643
+ const trimmed = value.trim();
1644
+ if (!trimmed || trimmed.endsWith("%")) return fallback;
1645
+ const parsed = Number.parseFloat(trimmed);
1646
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
1647
+ }
1648
+ function isIdentitySvgMatrix(value) {
1649
+ var _a;
1650
+ if (!value) return true;
1651
+ const nums = ((_a = value.match(/-?\d*\.?\d+(?:e[-+]?\d+)?/gi)) == null ? void 0 : _a.map(Number)) ?? [];
1652
+ return nums.length === 6 && Math.abs(nums[0] - 1) < 1e-3 && Math.abs(nums[1]) < 1e-3 && Math.abs(nums[2]) < 1e-3 && Math.abs(nums[3] - 1) < 1e-3 && Math.abs(nums[4]) < 1e-3 && Math.abs(nums[5]) < 1e-3;
1653
+ }
1654
+ function isRedundantImageClipPathForInlineSvg(root, clipPathRef, ix, iy, iw, ih) {
1655
+ const clipId = extractGradientIdFromPaint(clipPathRef);
1656
+ if (!clipId || !(iw > 0 && ih > 0)) return false;
1657
+ let clip = null;
1658
+ for (const el of root.querySelectorAll("[id]")) {
1659
+ if (el.getAttribute("id") === clipId) {
1660
+ clip = el;
1661
+ break;
1662
+ }
1663
+ }
1664
+ if (!clip || clip.tagName.toLowerCase() !== "clippath") return false;
1665
+ const units = (clip.getAttribute("clipPathUnits") || "userSpaceOnUse").toLowerCase();
1666
+ const rects = Array.from(clip.children).filter((el) => el.tagName.toLowerCase() === "rect");
1667
+ if (rects.length !== 1 || rects[0].parentElement !== clip) return false;
1668
+ const rect = rects[0];
1669
+ if (!isIdentitySvgMatrix(rect.getAttribute("transform"))) return false;
1670
+ const rx = Number.parseFloat(rect.getAttribute("x") || "0") || 0;
1671
+ const ry = Number.parseFloat(rect.getAttribute("y") || "0") || 0;
1672
+ const rw = Number.parseFloat(rect.getAttribute("width") || "0");
1673
+ const rh = Number.parseFloat(rect.getAttribute("height") || "0");
1674
+ if (!(rw > 0 && rh > 0)) return false;
1675
+ const near = (a, b) => Math.abs(a - b) <= Math.max(1, Math.max(Math.abs(a), Math.abs(b)) * 0.01);
1676
+ if (units === "objectboundingbox") return near(rx, 0) && near(ry, 0) && near(rw, 1) && near(rh, 1);
1677
+ return near(rx, ix) && near(ry, iy) && near(rw, iw) && near(rh, ih);
1678
+ }
1679
+ function flattenNestedSvgViewports(rootSvg) {
1680
+ const nestedSvgs = Array.from(rootSvg.querySelectorAll("svg")).filter((svg) => svg !== rootSvg).reverse();
1681
+ if (nestedSvgs.length === 0) return;
1682
+ let flattened = 0;
1683
+ for (const nestedSvg of nestedSvgs) {
1684
+ const parent = nestedSvg.parentNode;
1685
+ const doc = nestedSvg.ownerDocument;
1686
+ if (!parent || !doc) continue;
1687
+ const vb = (nestedSvg.getAttribute("viewBox") || "").trim().split(/[\s,]+/).map((n) => Number.parseFloat(n));
1688
+ const hasViewBox = vb.length === 4 && vb.every((n) => Number.isFinite(n)) && vb[2] > 0 && vb[3] > 0;
1689
+ const [vbX, vbY, vbW, vbH] = hasViewBox ? vb : [0, 0, 0, 0];
1690
+ const width = parseSvgLength(nestedSvg.getAttribute("width"), hasViewBox ? vbW : 0);
1691
+ const height = parseSvgLength(nestedSvg.getAttribute("height"), hasViewBox ? vbH : 0);
1692
+ if (!(width > 0 && height > 0)) continue;
1693
+ const x = Number.parseFloat(nestedSvg.getAttribute("x") || "0") || 0;
1694
+ const y = Number.parseFloat(nestedSvg.getAttribute("y") || "0") || 0;
1695
+ const par = nestedSvg.getAttribute("preserveAspectRatio") || "xMidYMid meet";
1696
+ const parParts = par.trim().split(/\s+/).filter(Boolean);
1697
+ const align = parParts[0] === "defer" ? parParts[1] || "xMidYMid" : parParts[0] || "xMidYMid";
1698
+ const meetOrSlice = parParts[0] === "defer" ? parParts[2] || "meet" : parParts[1] || "meet";
1699
+ let sx = hasViewBox ? width / vbW : 1;
1700
+ let sy = hasViewBox ? height / vbH : 1;
1701
+ let ox = 0;
1702
+ let oy = 0;
1703
+ if (hasViewBox && align !== "none") {
1704
+ const s = meetOrSlice === "slice" ? Math.max(sx, sy) : Math.min(sx, sy);
1705
+ sx = s;
1706
+ sy = s;
1707
+ const extraW = width - vbW * s;
1708
+ const extraH = height - vbH * s;
1709
+ if (align.includes("xMid")) ox = extraW / 2;
1710
+ else if (align.includes("xMax")) ox = extraW;
1711
+ if (align.includes("YMid")) oy = extraH / 2;
1712
+ else if (align.includes("YMax")) oy = extraH;
1713
+ }
1714
+ const g = doc.createElementNS("http://www.w3.org/2000/svg", "g");
1715
+ const existingTransform = nestedSvg.getAttribute("transform") || "";
1716
+ const viewBoxTransform = hasViewBox ? `translate(${x + ox},${y + oy}) scale(${sx},${sy}) translate(${-vbX},${-vbY})` : `translate(${x},${y})`;
1717
+ g.setAttribute("transform", `${existingTransform}${existingTransform ? " " : ""}${viewBoxTransform}`);
1718
+ for (const attr of Array.from(nestedSvg.attributes)) {
1719
+ if (["id", "class", "style", "opacity", "display", "visibility", "clip-path", "mask", "filter", "pointer-events"].includes(attr.name)) {
1720
+ g.setAttribute(attr.name, attr.value);
1721
+ }
1722
+ }
1723
+ for (const child of Array.from(nestedSvg.childNodes)) g.appendChild(child);
1724
+ parent.replaceChild(g, nestedSvg);
1725
+ flattened++;
1726
+ }
1727
+ if (flattened > 0) console.log("[client-pdf-export] flattened nested SVG viewport(s)", { flattened });
1728
+ }
1441
1729
  function hasInvalidSvgNumericToken(value) {
1442
1730
  return typeof value === "string" && /\b(?:NaN|-?Infinity|undefined|null)\b/.test(value);
1443
1731
  }
@@ -2319,11 +2607,12 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
2319
2607
  var _a;
2320
2608
  try {
2321
2609
  const parser = new DOMParser();
2322
- const processedSvg = inlineNestedSvgImageDataUris(rawSvg, parser);
2610
+ const processedSvg = await inlineNestedSvgImageDataUris(rawSvg, parser);
2323
2611
  const doc = parser.parseFromString(processedSvg, "image/svg+xml");
2324
2612
  if (doc.querySelector("parsererror")) return null;
2325
2613
  const svg = doc.documentElement;
2326
2614
  if (!svg || svg.tagName.toLowerCase() !== "svg") return null;
2615
+ logSvgGradientClipDiagnostics("prepare:parsed-after-inline", svg, { pageKey, pageWidth, pageHeight, processedLength: processedSvg.length });
2327
2616
  svg.setAttribute("xmlns", "http://www.w3.org/2000/svg");
2328
2617
  if (/xlink:href/i.test(processedSvg) && !svg.getAttribute("xmlns:xlink")) {
2329
2618
  svg.setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink");
@@ -2333,14 +2622,18 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
2333
2622
  svg.setAttribute("viewBox", `0 0 ${pageWidth} ${pageHeight}`);
2334
2623
  sanitizeSvgTreeForPdf(svg);
2335
2624
  normalizeSvgViewBoxOrigin(svg);
2625
+ flattenNestedSvgViewports(svg);
2626
+ logSvgGradientClipDiagnostics("prepare:after-flatten-nested-viewports", svg, { pageKey, pageWidth, pageHeight });
2336
2627
  disambiguateNestedSvgIds(svg);
2337
2628
  expandSvgUseElements(svg, pageKey);
2338
2629
  let svgToDraw = normalizeSvgExplicitColors(svg);
2630
+ logSvgGradientClipDiagnostics("prepare:after-explicit-colors", svgToDraw, { pageKey, pageWidth, pageHeight });
2339
2631
  inlineComputedStyles(svgToDraw);
2340
2632
  sanitizeSvgTreeForPdf(svgToDraw);
2341
2633
  normalizeSvgGradientStopOffsets(svgToDraw);
2342
2634
  expandSvgGradientHrefs(svgToDraw);
2343
2635
  prefixSvgIds(svgToDraw, pageKey);
2636
+ logSvgGradientClipDiagnostics("prepare:after-gradient-normalize-prefix", svgToDraw, { pageKey, pageWidth, pageHeight });
2344
2637
  bakeGroupOpacityIntoChildren(svgToDraw);
2345
2638
  stripSuspiciousFullPageOverlayNodes(svgToDraw);
2346
2639
  if (options == null ? void 0 : options.stripPageBackground) {
@@ -2362,8 +2655,10 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
2362
2655
  const rewritten = rewriteSvgFontsForJsPDFWithSourceMeta(new XMLSerializer().serializeToString(svgToDraw));
2363
2656
  const rewrittenDoc = parser.parseFromString(rewritten, "image/svg+xml");
2364
2657
  if (!rewrittenDoc.querySelector("parsererror") && ((_a = rewrittenDoc.documentElement) == null ? void 0 : _a.tagName.toLowerCase()) === "svg") {
2658
+ logSvgGradientClipDiagnostics("prepare:final-before-svg2pdf", rewrittenDoc.documentElement, { pageKey, pageWidth, pageHeight, rewrittenLength: rewritten.length });
2365
2659
  return rewrittenDoc.documentElement;
2366
2660
  }
2661
+ logSvgGradientClipDiagnostics("prepare:final-before-svg2pdf", svgToDraw, { pageKey, pageWidth, pageHeight });
2367
2662
  return svgToDraw;
2368
2663
  } catch {
2369
2664
  return null;
@@ -2787,7 +3082,7 @@ function resolveSvgGradientRefsToSolid(svg, elementId) {
2787
3082
  function disambiguateNestedSvgIds(rootSvg) {
2788
3083
  const nestedSvgs = Array.from(rootSvg.querySelectorAll("svg"));
2789
3084
  const toProcess = nestedSvgs.filter((s) => s !== rootSvg);
2790
- if (toProcess.length < 2) return;
3085
+ if (toProcess.length === 0) return;
2791
3086
  toProcess.forEach((nested, idx) => {
2792
3087
  prefixSvgIds(nested, `n${idx}`);
2793
3088
  });
@@ -2902,6 +3197,8 @@ function normalizeSvgViewBoxOrigin(svg) {
2902
3197
  if (parts.length !== 4) return;
2903
3198
  const [vx, vy, vw, vh] = parts;
2904
3199
  if (vw <= 0 || vh <= 0) return;
3200
+ svg.setAttribute("width", String(vw));
3201
+ svg.setAttribute("height", String(vh));
2905
3202
  if (Math.abs(vx) < 1e-3 && Math.abs(vy) < 1e-3) return;
2906
3203
  const doc = svg.ownerDocument;
2907
3204
  if (!doc) return;
@@ -2974,7 +3271,7 @@ async function fetchSvgAsElement(imageUrl, colorMap) {
2974
3271
  async function getRecoloredSvgDataUrl(imageUrl, colorMap) {
2975
3272
  if (!colorMap || Object.keys(colorMap).length === 0) return null;
2976
3273
  try {
2977
- const { getNormalizedSvgUrl } = await Promise.resolve().then(() => require("./index-BKJxI43i.cjs")).then((n) => n.canvasImageLoader);
3274
+ const { getNormalizedSvgUrl } = await Promise.resolve().then(() => require("./index-6nrov1rx.cjs")).then((n) => n.canvasImageLoader);
2978
3275
  return await getNormalizedSvgUrl(imageUrl, colorMap);
2979
3276
  } catch {
2980
3277
  return null;
@@ -3783,7 +4080,7 @@ async function fetchImageAsBase64(imageUrl, opts = {}) {
3783
4080
  }
3784
4081
  let fetchUrl = imageUrl;
3785
4082
  if (imageUrl.startsWith("http://") || imageUrl.startsWith("https://")) {
3786
- const { isPrivateUrl } = await Promise.resolve().then(() => require("./index-BKJxI43i.cjs")).then((n) => n.canvasImageLoader);
4083
+ const { isPrivateUrl } = await Promise.resolve().then(() => require("./index-6nrov1rx.cjs")).then((n) => n.canvasImageLoader);
3787
4084
  if (isPrivateUrl(imageUrl)) return null;
3788
4085
  const proxyUrl = new URL(`${index.API_URL}/image-proxy`);
3789
4086
  proxyUrl.searchParams.set("url", imageUrl);
@@ -5288,9 +5585,8 @@ async function exportFabricCanvasToVectorPdf(_fabricCanvas, options) {
5288
5585
  const orientation = width > height ? "landscape" : "portrait";
5289
5586
  const pdf = new jspdf.jsPDF({
5290
5587
  orientation,
5291
- unit: "px",
5588
+ unit: "pt",
5292
5589
  format: [width, height],
5293
- hotfixes: ["px_scaling"],
5294
5590
  compress: true
5295
5591
  });
5296
5592
  if (title) {
@@ -5467,9 +5763,8 @@ async function __exportMultiPagePdfInner(pages, options) {
5467
5763
  const orientation = firstPage.width > firstPage.height ? "landscape" : "portrait";
5468
5764
  const pdf = new jspdf.jsPDF({
5469
5765
  orientation,
5470
- unit: "px",
5766
+ unit: "pt",
5471
5767
  format: [firstPage.width, firstPage.height],
5472
- hotfixes: ["px_scaling"],
5473
5768
  // Always keep PDF object/stream compression on. The UI's image-compression
5474
5769
  // switch controls raster resampling/JPEG quality, not lossless PDF packing;
5475
5770
  // disabling this made "uncompressed image" PDFs balloon with no quality gain.
@@ -5882,4 +6177,4 @@ exports.exportMultiPagePdf = exportMultiPagePdf;
5882
6177
  exports.logTextMeasurementDiagnostic = logTextMeasurementDiagnostic;
5883
6178
  exports.preparePagesForExport = preparePagesForExport;
5884
6179
  exports.rewriteSvgFontsForJsPDFWithSourceMeta = rewriteSvgFontsForJsPDFWithSourceMeta;
5885
- //# sourceMappingURL=vectorPdfExport-CDMnc_eW.cjs.map
6180
+ //# sourceMappingURL=vectorPdfExport-CVeK--lR.cjs.map