@pixldocs/canvas-renderer 0.5.191 → 0.5.193

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.
@@ -16800,6 +16800,23 @@ function stampFabricLineMetricsOnTextSvg(svg, obj) {
16800
16800
  return `<tspan${cleaned} data-pd-line-width="${Number(lineWidth.toFixed(3))}" data-pd-line-start="${Number(lineStart.toFixed(3))}">`;
16801
16801
  });
16802
16802
  }
16803
+ function escapeSvgDataAttr(value) {
16804
+ return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
16805
+ }
16806
+ function hasRenderableRasterCandidate(obj) {
16807
+ if (!obj || typeof obj !== "object") return false;
16808
+ const candidates = [obj._element, obj._originalElement, obj._filteredEl, obj._cacheCanvasEl];
16809
+ for (const candidate of candidates) {
16810
+ if (candidate instanceof HTMLImageElement || candidate instanceof HTMLCanvasElement) return true;
16811
+ }
16812
+ const children = Array.isArray(obj == null ? void 0 : obj._objects) ? obj._objects : [];
16813
+ return children.some((child) => hasRenderableRasterCandidate(child));
16814
+ }
16815
+ function stampPixldocsImageIdOnSvg(svg, id) {
16816
+ if (!id || !/<image\b/i.test(svg) || /data-pixldocs-image-id=/i.test(svg)) return svg;
16817
+ const attr = ` data-pixldocs-image-id="${escapeSvgDataAttr(id)}"`;
16818
+ return svg.replace(/<image\b/i, `<image${attr}`);
16819
+ }
16803
16820
  function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight) {
16804
16821
  const prevVPT = fabricInstance.viewportTransform ? [...fabricInstance.viewportTransform] : void 0;
16805
16822
  const prevSvgVPT = fabricInstance.svgViewportTransformation;
@@ -16816,14 +16833,20 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
16816
16833
  );
16817
16834
  } catch {
16818
16835
  }
16819
- const textSvgPatchRecords = [];
16836
+ const svgPatchRecords = [];
16820
16837
  try {
16821
16838
  const visit = (obj) => {
16822
16839
  if (!obj) return;
16823
- if (isTextboxLike(obj) && typeof obj.toSVG === "function") {
16840
+ const imageId = typeof obj.__docuforgeId === "string" && hasRenderableRasterCandidate(obj) ? obj.__docuforgeId : "";
16841
+ if ((isTextboxLike(obj) || imageId) && typeof obj.toSVG === "function") {
16824
16842
  const originalToSVG = obj.toSVG.bind(obj);
16825
- obj.toSVG = (reviver) => stampFabricLineMetricsOnTextSvg(originalToSVG(reviver), obj);
16826
- textSvgPatchRecords.push({ obj, originalToSVG });
16843
+ obj.toSVG = (reviver) => {
16844
+ let svg = originalToSVG(reviver);
16845
+ if (isTextboxLike(obj)) svg = stampFabricLineMetricsOnTextSvg(svg, obj);
16846
+ if (imageId) svg = stampPixldocsImageIdOnSvg(svg, imageId);
16847
+ return svg;
16848
+ };
16849
+ svgPatchRecords.push({ obj, originalToSVG });
16827
16850
  }
16828
16851
  const children = Array.isArray(obj == null ? void 0 : obj._objects) ? obj._objects : [];
16829
16852
  for (const child of children) visit(child);
@@ -16856,6 +16879,13 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
16856
16879
  evented: false,
16857
16880
  objectCaching: false
16858
16881
  });
16882
+ if (typeof obj.__docuforgeId === "string") {
16883
+ replacement.__docuforgeId = obj.__docuforgeId;
16884
+ if (typeof replacement.toSVG === "function") {
16885
+ const originalReplacementToSVG = replacement.toSVG.bind(replacement);
16886
+ replacement.toSVG = (reviver) => stampPixldocsImageIdOnSvg(originalReplacementToSVG(reviver), obj.__docuforgeId);
16887
+ }
16888
+ }
16859
16889
  const insertIndex = fabricInstance._objects.indexOf(obj);
16860
16890
  const prevExclude = obj.excludeFromExport;
16861
16891
  obj.excludeFromExport = true;
@@ -16888,7 +16918,7 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
16888
16918
  } catch {
16889
16919
  }
16890
16920
  }
16891
- for (const rec of textSvgPatchRecords) {
16921
+ for (const rec of svgPatchRecords) {
16892
16922
  try {
16893
16923
  rec.obj.toSVG = rec.originalToSVG;
16894
16924
  } catch {
@@ -16907,9 +16937,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
16907
16937
  }
16908
16938
  return svgString;
16909
16939
  }
16910
- const resolvedPackageVersion = "0.5.191";
16940
+ const resolvedPackageVersion = "0.5.193";
16911
16941
  const PACKAGE_VERSION = resolvedPackageVersion;
16912
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.191";
16942
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.193";
16913
16943
  const roundParityValue = (value) => {
16914
16944
  if (typeof value !== "number") return value;
16915
16945
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -17530,13 +17560,13 @@ class PixldocsRenderer {
17530
17560
  );
17531
17561
  });
17532
17562
  await this.waitForCanvasScene(container, cloned, i);
17533
- const expected = this.getExpectedImageCount(cloned, i);
17563
+ const expected = this.getExpectedImageIds(cloned, i);
17534
17564
  await this.waitForCanvasImages(container, expected);
17535
17565
  await this.waitForStableTextMetrics(container, cloned, { clearGlobalCharCache: false });
17536
17566
  await this.waitForCanvasScene(container, cloned, i);
17537
17567
  }
17538
17568
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
17539
- const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-D_2mdcwO.cjs"));
17569
+ const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-BHKMcbVb.cjs"));
17540
17570
  const prepared = preparePagesForExport(
17541
17571
  cloned.pages,
17542
17572
  canvasWidth,
@@ -17547,7 +17577,27 @@ class PixldocsRenderer {
17547
17577
  watermark: !!options.watermark,
17548
17578
  returnBlob: true,
17549
17579
  pdfTextMode: options.textMode ?? (cloned == null ? void 0 : cloned.pdfTextMode) ?? ((_a = cloned.canvas) == null ? void 0 : _a.n) ?? "auto",
17550
- skipLiveCanvasSvgFastPath: shouldForcePerElement
17580
+ // IMPORTANT: We intentionally do NOT skip the live-canvas SVG fast path
17581
+ // here, even when `shouldForcePerElement` is true via auto-detection.
17582
+ //
17583
+ // Reason: skipping the fast path forces the config-space export, which
17584
+ // computes positions from stored values rather than the live Fabric
17585
+ // layout (post auto-shrink, post text-init, post crop-group bake).
17586
+ // That drift causes severe text overlap / mis-positioning in the PDF
17587
+ // while the on-screen preview (which uses the live canvas) still looks
17588
+ // correct. v0.5.191 shipped with this skip enabled and produced exactly
17589
+ // that regression.
17590
+ //
17591
+ // The Safari/iOS "missing photo" issue is solved end-to-end by the
17592
+ // `downscaleConfigRasterImages()` pre-pass above, which shrinks large
17593
+ // `data:image/*` JPEGs to a safe edge length before the canvas mounts.
17594
+ // That alone keeps Safari's SVG capture reliable without altering the
17595
+ // layout pipeline.
17596
+ //
17597
+ // We still honor an explicit `forcePerElementPdf: true` from the host
17598
+ // app (advanced opt-in for niche cases), but the auto path no longer
17599
+ // toggles this flag.
17600
+ skipLiveCanvasSvgFastPath: forceMode === true
17551
17601
  });
17552
17602
  if (!result || typeof result === "undefined") {
17553
17603
  throw new Error("exportMultiPagePdf returned no blob (returnBlob path failed)");
@@ -17588,17 +17638,21 @@ class PixldocsRenderer {
17588
17638
  walk(page.children);
17589
17639
  return ids;
17590
17640
  }
17591
- waitForCanvasImages(container, expectedImageCount, maxWaitMs, pollMs = 120) {
17641
+ waitForCanvasImages(container, expectedImages, maxWaitMs, pollMs = 120) {
17592
17642
  const timeout = Math.max(500, maxWaitMs ?? this.config.assetWaitTimeoutMs ?? 15e3);
17593
17643
  const earlyExitMs = Math.max(250, this.config.assetWaitEarlyExitMs ?? 1500);
17594
17644
  const debug = !!this.config.debug;
17595
- return new Promise((resolve) => {
17645
+ const expectedImageIds = Array.isArray(expectedImages) ? [...new Set(expectedImages)] : [];
17646
+ const expectedImageCount = Array.isArray(expectedImages) ? expectedImageIds.length : expectedImages;
17647
+ const strictById = expectedImageIds.length > 0;
17648
+ return new Promise((resolve, reject) => {
17596
17649
  const start = Date.now();
17597
17650
  let stableFrames = 0;
17598
17651
  let lastSummary = "";
17599
- let lastActual = -1;
17652
+ let lastProgressKey = "";
17600
17653
  let lastProgressAt = Date.now();
17601
17654
  const isRenderableImage = (value) => value instanceof HTMLImageElement && value.complete && value.naturalWidth > 0 && value.naturalHeight > 0;
17655
+ const isRenderableCanvas = (value) => value instanceof HTMLCanvasElement && value.width > 0 && value.height > 0;
17602
17656
  const collectRenderableImages = (obj, seen) => {
17603
17657
  if (!obj || typeof obj !== "object") return;
17604
17658
  const candidates = [obj._element, obj._originalElement, obj._filteredEl, obj._cacheCanvasEl];
@@ -17615,6 +17669,25 @@ class PixldocsRenderer {
17615
17669
  }
17616
17670
  return true;
17617
17671
  };
17672
+ const collectRenderableImageIds = (obj, readyIds) => {
17673
+ if (!obj || typeof obj !== "object") return { loaded: false, pending: false };
17674
+ let loaded = false;
17675
+ let pending = false;
17676
+ const candidates = [obj._element, obj._originalElement, obj._filteredEl, obj._cacheCanvasEl];
17677
+ for (const candidate of candidates) {
17678
+ if (isRenderableImage(candidate) || isRenderableCanvas(candidate)) loaded = true;
17679
+ else if (candidate instanceof HTMLImageElement) pending = true;
17680
+ }
17681
+ const nested = Array.isArray(obj._objects) ? obj._objects : Array.isArray(obj.objects) ? obj.objects : [];
17682
+ for (const child of nested) {
17683
+ const childState = collectRenderableImageIds(child, readyIds);
17684
+ loaded = loaded || childState.loaded;
17685
+ pending = pending || childState.pending;
17686
+ }
17687
+ const id = typeof obj.__docuforgeId === "string" ? obj.__docuforgeId : "";
17688
+ if (id && loaded && !pending) readyIds.add(id);
17689
+ return { loaded, pending };
17690
+ };
17618
17691
  const getFabricCanvas = () => {
17619
17692
  const registry2 = window.__fabricCanvasRegistry;
17620
17693
  if (registry2 instanceof Map) {
@@ -17654,17 +17727,21 @@ class PixldocsRenderer {
17654
17727
  const fabricObjects = fabricCanvas && typeof fabricCanvas.getObjects === "function" ? fabricCanvas.getObjects() : [];
17655
17728
  const renderableImages = /* @__PURE__ */ new Set();
17656
17729
  const fabricReady = fabricObjects.every((obj) => collectRenderableImages(obj, renderableImages) !== false);
17730
+ const renderableImageIds = /* @__PURE__ */ new Set();
17731
+ for (const obj of fabricObjects) collectRenderableImageIds(obj, renderableImageIds);
17732
+ const missingImageIds = strictById ? expectedImageIds.filter((id) => !renderableImageIds.has(id)) : [];
17657
17733
  const actualImageCount = Math.max(domImages.length, renderableImages.size);
17658
17734
  const canvasReady = !!fabricCanvas && !!(fabricCanvas.lowerCanvasEl || fabricCanvas.upperCanvasEl);
17659
- const hasExpectedAssets = expectedImageCount === 0 ? true : actualImageCount >= expectedImageCount;
17735
+ const hasExpectedAssets = strictById ? missingImageIds.length === 0 : expectedImageCount === 0 ? true : actualImageCount >= expectedImageCount;
17660
17736
  const ready = allDomLoaded && fabricReady && hasExpectedAssets;
17661
- const summary = `expected=${expectedImageCount} actual=${actualImageCount} dom=${domImages.length} fabricReady=${fabricReady} domReady=${allDomLoaded} canvasReady=${canvasReady}`;
17737
+ const summary = `expected=${expectedImageCount} actual=${actualImageCount} fabricIds=${renderableImageIds.size} dom=${domImages.length} fabricReady=${fabricReady} domReady=${allDomLoaded} canvasReady=${canvasReady}${missingImageIds.length ? ` missing=${missingImageIds.join(",")}` : ""}`;
17662
17738
  if (summary !== lastSummary) {
17663
17739
  lastSummary = summary;
17664
17740
  if (debug) console.log(`[canvas-renderer][asset-wait] ${summary}`);
17665
17741
  }
17666
- if (actualImageCount !== lastActual) {
17667
- lastActual = actualImageCount;
17742
+ const progressKey = strictById ? expectedImageIds.filter((id) => renderableImageIds.has(id)).join("|") : String(actualImageCount);
17743
+ if (progressKey !== lastProgressKey) {
17744
+ lastProgressKey = progressKey;
17668
17745
  lastProgressAt = Date.now();
17669
17746
  }
17670
17747
  if (ready) {
@@ -17679,7 +17756,7 @@ class PixldocsRenderer {
17679
17756
  }
17680
17757
  const sceneSettled = fabricReady && allDomLoaded && canvasReady && fabricObjects.length > 0;
17681
17758
  const idleFor = Date.now() - lastProgressAt;
17682
- if (sceneSettled && actualImageCount < expectedImageCount && idleFor >= earlyExitMs) {
17759
+ if (!strictById && sceneSettled && actualImageCount < expectedImageCount && idleFor >= earlyExitMs) {
17683
17760
  if (debug) {
17684
17761
  console.log(
17685
17762
  `[canvas-renderer][asset-wait] early-exit after ${elapsed}ms (idle=${idleFor}ms, ${summary})`
@@ -17703,6 +17780,10 @@ class PixldocsRenderer {
17703
17780
  console.warn(`[canvas-renderer][asset-wait-timeout] elapsed=${elapsed}ms ${summary}`);
17704
17781
  console.warn("[canvas-renderer][asset-wait-timeout][dom-images]", domImageDebug);
17705
17782
  console.warn("[canvas-renderer][asset-wait-timeout][fabric-images]", fabricImageDebug);
17783
+ if (strictById && missingImageIds.length > 0) {
17784
+ reject(new Error(`[canvas-renderer][asset-wait-timeout] Missing expected image(s): ${missingImageIds.join(", ")}`));
17785
+ return;
17786
+ }
17706
17787
  settle();
17707
17788
  return;
17708
17789
  }
@@ -17903,8 +17984,8 @@ class PixldocsRenderer {
17903
17984
  const onReadyOnce = () => {
17904
17985
  this.waitForCanvasScene(container, renderConfig, pageIndex).then(async () => {
17905
17986
  const fabricInstance = this.getFabricCanvasFromContainer(container);
17906
- const expectedImageCount = this.getExpectedImageCount(renderConfig, pageIndex);
17907
- await this.waitForCanvasImages(container, expectedImageCount);
17987
+ const expectedImageIds = this.getExpectedImageIds(renderConfig, pageIndex);
17988
+ await this.waitForCanvasImages(container, expectedImageIds);
17908
17989
  await this.waitForStableTextMetrics(container, renderConfig);
17909
17990
  await this.waitForCanvasScene(container, renderConfig, pageIndex);
17910
17991
  if (!fabricInstance) return settle();
@@ -17930,8 +18011,8 @@ class PixldocsRenderer {
17930
18011
  this.waitForCanvasScene(container, renderConfig, pageIndex).then(async () => {
17931
18012
  try {
17932
18013
  const fabricInstance = this.getFabricCanvasFromContainer(container);
17933
- const expectedImageCount = this.getExpectedImageCount(renderConfig, pageIndex);
17934
- await this.waitForCanvasImages(container, expectedImageCount);
18014
+ const expectedImageIds = this.getExpectedImageIds(renderConfig, pageIndex);
18015
+ await this.waitForCanvasImages(container, expectedImageIds);
17935
18016
  await this.waitForStableTextMetrics(container, renderConfig);
17936
18017
  await this.waitForCanvasScene(container, renderConfig, pageIndex);
17937
18018
  firstMountSettled = true;
@@ -18042,8 +18123,8 @@ class PixldocsRenderer {
18042
18123
  this.waitForCanvasScene(container, renderConfig, pageIndex).then(async () => {
18043
18124
  var _a, _b;
18044
18125
  try {
18045
- const expectedImageCount = this.getExpectedImageCount(renderConfig, pageIndex);
18046
- await this.waitForCanvasImages(container, expectedImageCount);
18126
+ const expectedImageIds = this.getExpectedImageIds(renderConfig, pageIndex);
18127
+ await this.waitForCanvasImages(container, expectedImageIds);
18047
18128
  await this.waitForStableTextMetrics(container, renderConfig);
18048
18129
  await this.waitForCanvasScene(container, renderConfig, pageIndex);
18049
18130
  const fabricInstance = this.getFabricCanvasFromContainer(container);
@@ -19682,7 +19763,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
19682
19763
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
19683
19764
  sanitizeSvgTreeForPdf(svgToDraw);
19684
19765
  try {
19685
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-D_2mdcwO.cjs"));
19766
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-BHKMcbVb.cjs"));
19686
19767
  try {
19687
19768
  await logTextMeasurementDiagnostic(svgToDraw);
19688
19769
  } catch {
@@ -20079,4 +20160,4 @@ exports.setAutoShrinkDebug = setAutoShrinkDebug;
20079
20160
  exports.setBundledAssetPrefixes = setBundledAssetPrefixes;
20080
20161
  exports.warmResolvedTemplateForPreview = warmResolvedTemplateForPreview;
20081
20162
  exports.warmTemplateFromForm = warmTemplateFromForm;
20082
- //# sourceMappingURL=index-BJR7zaam.cjs.map
20163
+ //# sourceMappingURL=index-uwKwubUL.cjs.map