@pixldocs/canvas-renderer 0.5.222 → 0.5.223

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.
@@ -19123,9 +19123,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
19123
19123
  }
19124
19124
  return svgString;
19125
19125
  }
19126
- const resolvedPackageVersion = "0.5.222";
19126
+ const resolvedPackageVersion = "0.5.223";
19127
19127
  const PACKAGE_VERSION = resolvedPackageVersion;
19128
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.222";
19128
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.223";
19129
19129
  const roundParityValue = (value) => {
19130
19130
  if (typeof value !== "number") return value;
19131
19131
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -19869,7 +19869,7 @@ class PixldocsRenderer {
19869
19869
  await this.waitForCanvasScene(container, cloned, i);
19870
19870
  }
19871
19871
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
19872
- const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-CDMnc_eW.cjs"));
19872
+ const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-C-l9Vtku.cjs"));
19873
19873
  const prepared = preparePagesForExport(
19874
19874
  cloned.pages,
19875
19875
  canvasWidth,
@@ -21265,6 +21265,8 @@ function normalizeSvgViewBoxOrigin(svg) {
21265
21265
  if (parts.length !== 4) return;
21266
21266
  const [vx, vy, vw, vh] = parts;
21267
21267
  if (vw <= 0 || vh <= 0) return;
21268
+ svg.setAttribute("width", String(vw));
21269
+ svg.setAttribute("height", String(vh));
21268
21270
  if (Math.abs(vx) < 1e-3 && Math.abs(vy) < 1e-3) return;
21269
21271
  const doc = svg.ownerDocument;
21270
21272
  if (!doc) return;
@@ -21351,7 +21353,20 @@ function decodeSvgDataUri(href) {
21351
21353
  }
21352
21354
  }
21353
21355
  }
21354
- function inlineNestedSvgImageDataUris(svgString, domParser = new DOMParser()) {
21356
+ async function readNestedSvgImageMarkup(href) {
21357
+ if (!href) return null;
21358
+ if (href.startsWith("data:image/svg+xml")) return decodeSvgDataUri(href);
21359
+ if (!/\.svg(?:[?#]|$)/i.test(href) && !href.startsWith("blob:")) return null;
21360
+ try {
21361
+ const res = await fetch(href, { cache: "no-store" });
21362
+ if (!res.ok) return null;
21363
+ const text = await res.text();
21364
+ return /<svg[\s>]/i.test(text) ? text : null;
21365
+ } catch {
21366
+ return null;
21367
+ }
21368
+ }
21369
+ async function inlineNestedSvgImageDataUris(svgString, domParser = new DOMParser()) {
21355
21370
  var _a;
21356
21371
  try {
21357
21372
  const doc = domParser.parseFromString(svgString, "image/svg+xml");
@@ -21359,47 +21374,161 @@ function inlineNestedSvgImageDataUris(svgString, domParser = new DOMParser()) {
21359
21374
  const root = doc.documentElement;
21360
21375
  if (!root || root.tagName.toLowerCase() !== "svg") return svgString;
21361
21376
  let changed = false;
21362
- for (const img of Array.from(doc.querySelectorAll("image"))) {
21377
+ let inlined = 0;
21378
+ const images = Array.from(doc.querySelectorAll("image"));
21379
+ for (const [imageIndex, img] of images.entries()) {
21363
21380
  const href = img.getAttribute("href") || img.getAttributeNS("http://www.w3.org/1999/xlink", "href");
21364
- if (!href || !href.startsWith("data:image/svg+xml")) continue;
21381
+ if (!href) continue;
21365
21382
  try {
21366
- const svgContent = decodeSvgDataUri(href);
21383
+ const svgContent = await readNestedSvgImageMarkup(href);
21367
21384
  if (!svgContent || !/<svg[\s>]/i.test(svgContent)) continue;
21368
21385
  const innerDoc = domParser.parseFromString(svgContent, "image/svg+xml");
21369
21386
  if (innerDoc.querySelector("parsererror")) continue;
21370
21387
  const innerSvg = innerDoc.documentElement;
21371
21388
  if (!innerSvg || innerSvg.tagName.toLowerCase() !== "svg") continue;
21389
+ const sourceId = img.getAttribute("id") || `image-${imageIndex}`;
21390
+ prefixSvgIds(innerSvg, `inline-${sourceId}`);
21372
21391
  const ix = parseFloat(img.getAttribute("x") || "0") || 0;
21373
21392
  const iy = parseFloat(img.getAttribute("y") || "0") || 0;
21374
21393
  const iw = parseFloat(img.getAttribute("width") || "0");
21375
21394
  const ih = parseFloat(img.getAttribute("height") || "0");
21376
21395
  if (!(iw > 0 && ih > 0)) continue;
21377
- const nestedSvg = doc.importNode(innerSvg, true);
21378
- if (!nestedSvg.getAttribute("viewBox")) nestedSvg.setAttribute("viewBox", `0 0 ${iw} ${ih}`);
21379
- nestedSvg.setAttribute("x", "0");
21380
- nestedSvg.setAttribute("y", "0");
21381
- nestedSvg.setAttribute("width", String(iw));
21382
- nestedSvg.setAttribute("height", String(ih));
21383
- nestedSvg.setAttribute("preserveAspectRatio", img.getAttribute("preserveAspectRatio") || nestedSvg.getAttribute("preserveAspectRatio") || "xMidYMid meet");
21384
21396
  const g = doc.createElementNS("http://www.w3.org/2000/svg", "g");
21385
21397
  const existingTransform = img.getAttribute("transform") || "";
21386
- g.setAttribute("transform", `${existingTransform}${existingTransform ? " " : ""}translate(${ix},${iy})`);
21398
+ const vb = (innerSvg.getAttribute("viewBox") || "").trim().split(/[\s,]+/).map((n) => Number.parseFloat(n));
21399
+ 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];
21400
+ const par = img.getAttribute("preserveAspectRatio") || innerSvg.getAttribute("preserveAspectRatio") || "xMidYMid meet";
21401
+ const parParts = par.trim().split(/\s+/).filter(Boolean);
21402
+ const align = parParts[0] === "defer" ? parParts[1] || "xMidYMid" : parParts[0] || "xMidYMid";
21403
+ const meetOrSlice = parParts[0] === "defer" ? parParts[2] || "meet" : parParts[1] || "meet";
21404
+ let sx = iw / vbW;
21405
+ let sy = ih / vbH;
21406
+ let ox = 0;
21407
+ let oy = 0;
21408
+ if (align !== "none") {
21409
+ const s = meetOrSlice === "slice" ? Math.max(sx, sy) : Math.min(sx, sy);
21410
+ sx = s;
21411
+ sy = s;
21412
+ const extraW = iw - vbW * s;
21413
+ const extraH = ih - vbH * s;
21414
+ if (align.includes("xMid")) ox = extraW / 2;
21415
+ else if (align.includes("xMax")) ox = extraW;
21416
+ if (align.includes("YMid")) oy = extraH / 2;
21417
+ else if (align.includes("YMax")) oy = extraH;
21418
+ }
21419
+ g.setAttribute("transform", `${existingTransform}${existingTransform ? " " : ""}translate(${ix + ox},${iy + oy}) scale(${sx},${sy}) translate(${-vbX},${-vbY})`);
21420
+ const imageClipPath = img.getAttribute("clip-path") || getInlineStyleValue(img, "clip-path");
21421
+ const dropImageClipPath = isRedundantImageClipPathForInlineSvg(root, imageClipPath, ix, iy, iw, ih);
21422
+ if (dropImageClipPath) console.log("[canvas-renderer][pdf] dropped redundant inline SVG image clipPath", { imageIndex, sourceId, clipPath: imageClipPath });
21387
21423
  for (const attr of Array.from(img.attributes)) {
21424
+ if (dropImageClipPath && attr.name === "clip-path") continue;
21425
+ if (dropImageClipPath && attr.name === "style") {
21426
+ const kept = parseInlineSvgStyleDeclarations(attr.value).filter((decl) => decl.key !== "clip-path").map((decl) => `${decl.key}: ${decl.value}`).join("; ");
21427
+ if (kept) g.setAttribute("style", kept);
21428
+ continue;
21429
+ }
21388
21430
  if (["id", "class", "style", "opacity", "display", "visibility", "clip-path", "mask", "filter", "pointer-events"].includes(attr.name)) {
21389
21431
  g.setAttribute(attr.name, attr.value);
21390
21432
  }
21391
21433
  }
21392
- g.appendChild(nestedSvg);
21434
+ for (const child of Array.from(innerSvg.childNodes)) g.appendChild(doc.importNode(child, true));
21393
21435
  (_a = img.parentNode) == null ? void 0 : _a.replaceChild(g, img);
21394
21436
  changed = true;
21437
+ inlined++;
21395
21438
  } catch {
21396
21439
  }
21397
21440
  }
21441
+ if (inlined > 0) console.log(`[canvas-renderer][pdf] inlined ${inlined} nested SVG image(s)`);
21398
21442
  return changed ? new XMLSerializer().serializeToString(doc.documentElement) : svgString;
21399
21443
  } catch {
21400
21444
  return svgString;
21401
21445
  }
21402
21446
  }
21447
+ function parseSvgLength(value, fallback) {
21448
+ if (!value) return fallback;
21449
+ const trimmed = value.trim();
21450
+ if (!trimmed || trimmed.endsWith("%")) return fallback;
21451
+ const parsed = Number.parseFloat(trimmed);
21452
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
21453
+ }
21454
+ function isIdentitySvgMatrix(value) {
21455
+ var _a;
21456
+ if (!value) return true;
21457
+ const nums = ((_a = value.match(/-?\d*\.?\d+(?:e[-+]?\d+)?/gi)) == null ? void 0 : _a.map(Number)) ?? [];
21458
+ 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;
21459
+ }
21460
+ function isRedundantImageClipPathForInlineSvg(root, clipPathRef, ix, iy, iw, ih) {
21461
+ const clipId = extractGradientIdFromPaint(clipPathRef);
21462
+ if (!clipId || !(iw > 0 && ih > 0)) return false;
21463
+ let clip = null;
21464
+ for (const el of root.querySelectorAll("[id]")) {
21465
+ if (el.getAttribute("id") === clipId) {
21466
+ clip = el;
21467
+ break;
21468
+ }
21469
+ }
21470
+ if (!clip || clip.tagName.toLowerCase() !== "clippath") return false;
21471
+ const units = (clip.getAttribute("clipPathUnits") || "userSpaceOnUse").toLowerCase();
21472
+ const rects = Array.from(clip.children).filter((el) => el.tagName.toLowerCase() === "rect");
21473
+ if (rects.length !== 1 || rects[0].parentElement !== clip) return false;
21474
+ const rect = rects[0];
21475
+ if (!isIdentitySvgMatrix(rect.getAttribute("transform"))) return false;
21476
+ const rx = Number.parseFloat(rect.getAttribute("x") || "0") || 0;
21477
+ const ry = Number.parseFloat(rect.getAttribute("y") || "0") || 0;
21478
+ const rw = Number.parseFloat(rect.getAttribute("width") || "0");
21479
+ const rh = Number.parseFloat(rect.getAttribute("height") || "0");
21480
+ if (!(rw > 0 && rh > 0)) return false;
21481
+ const near = (a, b) => Math.abs(a - b) <= Math.max(1, Math.max(Math.abs(a), Math.abs(b)) * 0.01);
21482
+ if (units === "objectboundingbox") return near(rx, 0) && near(ry, 0) && near(rw, 1) && near(rh, 1);
21483
+ return near(rx, ix) && near(ry, iy) && near(rw, iw) && near(rh, ih);
21484
+ }
21485
+ function flattenNestedSvgViewports(rootSvg) {
21486
+ const nestedSvgs = Array.from(rootSvg.querySelectorAll("svg")).filter((svg) => svg !== rootSvg).reverse();
21487
+ let flattened = 0;
21488
+ for (const nestedSvg of nestedSvgs) {
21489
+ const parent = nestedSvg.parentNode;
21490
+ const doc = nestedSvg.ownerDocument;
21491
+ if (!parent || !doc) continue;
21492
+ const vb = (nestedSvg.getAttribute("viewBox") || "").trim().split(/[\s,]+/).map((n) => Number.parseFloat(n));
21493
+ const hasViewBox = vb.length === 4 && vb.every((n) => Number.isFinite(n)) && vb[2] > 0 && vb[3] > 0;
21494
+ const [vbX, vbY, vbW, vbH] = hasViewBox ? vb : [0, 0, 0, 0];
21495
+ const width = parseSvgLength(nestedSvg.getAttribute("width"), hasViewBox ? vbW : 0);
21496
+ const height = parseSvgLength(nestedSvg.getAttribute("height"), hasViewBox ? vbH : 0);
21497
+ if (!(width > 0 && height > 0)) continue;
21498
+ const x = Number.parseFloat(nestedSvg.getAttribute("x") || "0") || 0;
21499
+ const y = Number.parseFloat(nestedSvg.getAttribute("y") || "0") || 0;
21500
+ const par = nestedSvg.getAttribute("preserveAspectRatio") || "xMidYMid meet";
21501
+ const parParts = par.trim().split(/\s+/).filter(Boolean);
21502
+ const align = parParts[0] === "defer" ? parParts[1] || "xMidYMid" : parParts[0] || "xMidYMid";
21503
+ const meetOrSlice = parParts[0] === "defer" ? parParts[2] || "meet" : parParts[1] || "meet";
21504
+ let sx = hasViewBox ? width / vbW : 1;
21505
+ let sy = hasViewBox ? height / vbH : 1;
21506
+ let ox = 0;
21507
+ let oy = 0;
21508
+ if (hasViewBox && align !== "none") {
21509
+ const s = meetOrSlice === "slice" ? Math.max(sx, sy) : Math.min(sx, sy);
21510
+ sx = s;
21511
+ sy = s;
21512
+ const extraW = width - vbW * s;
21513
+ const extraH = height - vbH * s;
21514
+ if (align.includes("xMid")) ox = extraW / 2;
21515
+ else if (align.includes("xMax")) ox = extraW;
21516
+ if (align.includes("YMid")) oy = extraH / 2;
21517
+ else if (align.includes("YMax")) oy = extraH;
21518
+ }
21519
+ const g = doc.createElementNS("http://www.w3.org/2000/svg", "g");
21520
+ const existingTransform = nestedSvg.getAttribute("transform") || "";
21521
+ const viewBoxTransform = hasViewBox ? `translate(${x + ox},${y + oy}) scale(${sx},${sy}) translate(${-vbX},${-vbY})` : `translate(${x},${y})`;
21522
+ g.setAttribute("transform", `${existingTransform}${existingTransform ? " " : ""}${viewBoxTransform}`);
21523
+ for (const attr of Array.from(nestedSvg.attributes)) {
21524
+ if (["id", "class", "style", "opacity", "display", "visibility", "clip-path", "mask", "filter", "pointer-events"].includes(attr.name)) g.setAttribute(attr.name, attr.value);
21525
+ }
21526
+ for (const child of Array.from(nestedSvg.childNodes)) g.appendChild(child);
21527
+ parent.replaceChild(g, nestedSvg);
21528
+ flattened++;
21529
+ }
21530
+ if (flattened > 0) console.log("[canvas-renderer][pdf] flattened nested SVG viewport(s)", { flattened });
21531
+ }
21403
21532
  function inlineComputedStyles(svg) {
21404
21533
  if (typeof document === "undefined") return;
21405
21534
  const wrap = document.createElement("div");
@@ -21440,7 +21569,7 @@ function inlineComputedStyles(svg) {
21440
21569
  function disambiguateNestedSvgIds(rootSvg) {
21441
21570
  const nestedSvgs = Array.from(rootSvg.querySelectorAll("svg"));
21442
21571
  const toProcess = nestedSvgs.filter((s) => s !== rootSvg);
21443
- if (toProcess.length < 2) return;
21572
+ if (toProcess.length === 0) return;
21444
21573
  toProcess.forEach((nested, idx) => {
21445
21574
  prefixSvgIds(nested, `n${idx}`);
21446
21575
  });
@@ -22032,7 +22161,7 @@ function restoreSourceFontsForShadowRaster(markup) {
22032
22161
  async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey, options) {
22033
22162
  try {
22034
22163
  const parser = new DOMParser();
22035
- const processedSvg = inlineNestedSvgImageDataUris(rawSvg, parser);
22164
+ const processedSvg = await inlineNestedSvgImageDataUris(rawSvg, parser);
22036
22165
  const doc = parser.parseFromString(processedSvg, "image/svg+xml");
22037
22166
  if (doc.querySelector("parsererror")) return null;
22038
22167
  const svg = doc.documentElement;
@@ -22046,6 +22175,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
22046
22175
  svg.setAttribute("viewBox", `0 0 ${pageWidth} ${pageHeight}`);
22047
22176
  sanitizeSvgTreeForPdf(svg);
22048
22177
  normalizeSvgViewBoxOrigin(svg);
22178
+ flattenNestedSvgViewports(svg);
22049
22179
  disambiguateNestedSvgIds(svg);
22050
22180
  expandSvgUseElements(svg);
22051
22181
  const svgToDraw = normalizeSvgExplicitColors(svg);
@@ -22059,7 +22189,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
22059
22189
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
22060
22190
  sanitizeSvgTreeForPdf(svgToDraw);
22061
22191
  try {
22062
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-CDMnc_eW.cjs"));
22192
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-C-l9Vtku.cjs"));
22063
22193
  try {
22064
22194
  await logTextMeasurementDiagnostic(svgToDraw);
22065
22195
  } catch {
@@ -22159,9 +22289,8 @@ async function assemblePdfFromSvgs(svgResults, options = {}) {
22159
22289
  }
22160
22290
  const pdf = new jspdf.jsPDF({
22161
22291
  orientation,
22162
- unit: "px",
22292
+ unit: "pt",
22163
22293
  format: [firstPage.width, firstPage.height],
22164
- hotfixes: ["px_scaling"],
22165
22294
  compress: true
22166
22295
  });
22167
22296
  if (title) pdf.setProperties({ title, creator: "Pixldocs" });
@@ -22456,4 +22585,4 @@ exports.setAutoShrinkDebug = setAutoShrinkDebug;
22456
22585
  exports.setBundledAssetPrefixes = setBundledAssetPrefixes;
22457
22586
  exports.warmResolvedTemplateForPreview = warmResolvedTemplateForPreview;
22458
22587
  exports.warmTemplateFromForm = warmTemplateFromForm;
22459
- //# sourceMappingURL=index-BKJxI43i.cjs.map
22588
+ //# sourceMappingURL=index-xWTAswf0.cjs.map