@pixldocs/canvas-renderer 0.5.192 → 0.5.194
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/README.md +11 -0
- package/dist/{index-CqgOWYwR.js → index-C1BrMJjh.js} +86 -25
- package/dist/{index-CqgOWYwR.js.map → index-C1BrMJjh.js.map} +1 -1
- package/dist/{index-UIDDdWwb.cjs → index-pshD8xMc.cjs} +86 -25
- package/dist/{index-UIDDdWwb.cjs.map → index-pshD8xMc.cjs.map} +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +7 -15
- package/dist/index.js +1 -1
- package/dist/{vectorPdfExport-DuVMxQSS.js → vectorPdfExport-CuD_Ax0x.js} +65 -6
- package/dist/vectorPdfExport-CuD_Ax0x.js.map +1 -0
- package/dist/{vectorPdfExport-O8vRHBlU.cjs → vectorPdfExport-D5Xagjna.cjs} +65 -6
- package/dist/vectorPdfExport-D5Xagjna.cjs.map +1 -0
- package/package.json +1 -1
- package/dist/vectorPdfExport-DuVMxQSS.js.map +0 -1
- package/dist/vectorPdfExport-O8vRHBlU.cjs.map +0 -1
package/README.md
CHANGED
|
@@ -273,9 +273,20 @@ const renderer = new PixldocsRenderer({
|
|
|
273
273
|
supabaseAnonKey: string,
|
|
274
274
|
imageProxyUrl?: string, // CORS proxy for external images
|
|
275
275
|
pixelRatio?: number, // default: 2
|
|
276
|
+
assetWaitTimeoutMs?: number, // default: 15000; use ~30000 for multi-photo PDF exports
|
|
277
|
+
assetWaitEarlyExitMs?: number, // default: 1500; ignored for strict PDF image-ID waits
|
|
278
|
+
maxImageEdgePx?: number, // recommended: 2048 for user-uploaded PDF photos
|
|
279
|
+
debug?: boolean, // logs image wait/SVG completeness diagnostics
|
|
276
280
|
});
|
|
277
281
|
```
|
|
278
282
|
|
|
283
|
+
For BioMaker-style PDFs with user-uploaded profile/gallery photos, use the live
|
|
284
|
+
SVG path with `maxImageEdgePx: 2048` and **do not** set
|
|
285
|
+
`forcePerElementPdf: true`. Since `0.5.193`, PDF export waits for every expected
|
|
286
|
+
bound image ID, stamps those IDs into the captured SVG, preloads/decodes them
|
|
287
|
+
before `svg2pdf`, and throws if any expected photo is missing instead of
|
|
288
|
+
silently exporting an incomplete PDF.
|
|
289
|
+
|
|
279
290
|
#### `renderer.renderFromForm(options)`
|
|
280
291
|
|
|
281
292
|
Renders all pages from a V2 sectionState payload (same format as the server `/render-from-form` API).
|
|
@@ -16782,6 +16782,23 @@ function stampFabricLineMetricsOnTextSvg(svg, obj) {
|
|
|
16782
16782
|
return `<tspan${cleaned} data-pd-line-width="${Number(lineWidth.toFixed(3))}" data-pd-line-start="${Number(lineStart.toFixed(3))}">`;
|
|
16783
16783
|
});
|
|
16784
16784
|
}
|
|
16785
|
+
function escapeSvgDataAttr(value) {
|
|
16786
|
+
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
16787
|
+
}
|
|
16788
|
+
function hasRenderableRasterCandidate(obj) {
|
|
16789
|
+
if (!obj || typeof obj !== "object") return false;
|
|
16790
|
+
const candidates = [obj._element, obj._originalElement, obj._filteredEl, obj._cacheCanvasEl];
|
|
16791
|
+
for (const candidate of candidates) {
|
|
16792
|
+
if (candidate instanceof HTMLImageElement || candidate instanceof HTMLCanvasElement) return true;
|
|
16793
|
+
}
|
|
16794
|
+
const children = Array.isArray(obj == null ? void 0 : obj._objects) ? obj._objects : [];
|
|
16795
|
+
return children.some((child) => hasRenderableRasterCandidate(child));
|
|
16796
|
+
}
|
|
16797
|
+
function stampPixldocsImageIdOnSvg(svg, id) {
|
|
16798
|
+
if (!id || !/<image\b/i.test(svg) || /data-pixldocs-image-id=/i.test(svg)) return svg;
|
|
16799
|
+
const attr = ` data-pixldocs-image-id="${escapeSvgDataAttr(id)}"`;
|
|
16800
|
+
return svg.replace(/<image\b/i, `<image${attr}`);
|
|
16801
|
+
}
|
|
16785
16802
|
function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight) {
|
|
16786
16803
|
const prevVPT = fabricInstance.viewportTransform ? [...fabricInstance.viewportTransform] : void 0;
|
|
16787
16804
|
const prevSvgVPT = fabricInstance.svgViewportTransformation;
|
|
@@ -16798,14 +16815,20 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
|
|
|
16798
16815
|
);
|
|
16799
16816
|
} catch {
|
|
16800
16817
|
}
|
|
16801
|
-
const
|
|
16818
|
+
const svgPatchRecords = [];
|
|
16802
16819
|
try {
|
|
16803
16820
|
const visit = (obj) => {
|
|
16804
16821
|
if (!obj) return;
|
|
16805
|
-
|
|
16822
|
+
const imageId = typeof obj.__docuforgeId === "string" && hasRenderableRasterCandidate(obj) ? obj.__docuforgeId : "";
|
|
16823
|
+
if ((isTextboxLike(obj) || imageId) && typeof obj.toSVG === "function") {
|
|
16806
16824
|
const originalToSVG = obj.toSVG.bind(obj);
|
|
16807
|
-
obj.toSVG = (reviver) =>
|
|
16808
|
-
|
|
16825
|
+
obj.toSVG = (reviver) => {
|
|
16826
|
+
let svg = originalToSVG(reviver);
|
|
16827
|
+
if (isTextboxLike(obj)) svg = stampFabricLineMetricsOnTextSvg(svg, obj);
|
|
16828
|
+
if (imageId) svg = stampPixldocsImageIdOnSvg(svg, imageId);
|
|
16829
|
+
return svg;
|
|
16830
|
+
};
|
|
16831
|
+
svgPatchRecords.push({ obj, originalToSVG });
|
|
16809
16832
|
}
|
|
16810
16833
|
const children = Array.isArray(obj == null ? void 0 : obj._objects) ? obj._objects : [];
|
|
16811
16834
|
for (const child of children) visit(child);
|
|
@@ -16838,6 +16861,13 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
|
|
|
16838
16861
|
evented: false,
|
|
16839
16862
|
objectCaching: false
|
|
16840
16863
|
});
|
|
16864
|
+
if (typeof obj.__docuforgeId === "string") {
|
|
16865
|
+
replacement.__docuforgeId = obj.__docuforgeId;
|
|
16866
|
+
if (typeof replacement.toSVG === "function") {
|
|
16867
|
+
const originalReplacementToSVG = replacement.toSVG.bind(replacement);
|
|
16868
|
+
replacement.toSVG = (reviver) => stampPixldocsImageIdOnSvg(originalReplacementToSVG(reviver), obj.__docuforgeId);
|
|
16869
|
+
}
|
|
16870
|
+
}
|
|
16841
16871
|
const insertIndex = fabricInstance._objects.indexOf(obj);
|
|
16842
16872
|
const prevExclude = obj.excludeFromExport;
|
|
16843
16873
|
obj.excludeFromExport = true;
|
|
@@ -16870,7 +16900,7 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
|
|
|
16870
16900
|
} catch {
|
|
16871
16901
|
}
|
|
16872
16902
|
}
|
|
16873
|
-
for (const rec of
|
|
16903
|
+
for (const rec of svgPatchRecords) {
|
|
16874
16904
|
try {
|
|
16875
16905
|
rec.obj.toSVG = rec.originalToSVG;
|
|
16876
16906
|
} catch {
|
|
@@ -16889,9 +16919,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
|
|
|
16889
16919
|
}
|
|
16890
16920
|
return svgString;
|
|
16891
16921
|
}
|
|
16892
|
-
const resolvedPackageVersion = "0.5.
|
|
16922
|
+
const resolvedPackageVersion = "0.5.194";
|
|
16893
16923
|
const PACKAGE_VERSION = resolvedPackageVersion;
|
|
16894
|
-
const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.
|
|
16924
|
+
const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.194";
|
|
16895
16925
|
const roundParityValue = (value) => {
|
|
16896
16926
|
if (typeof value !== "number") return value;
|
|
16897
16927
|
return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
|
|
@@ -17512,13 +17542,13 @@ class PixldocsRenderer {
|
|
|
17512
17542
|
);
|
|
17513
17543
|
});
|
|
17514
17544
|
await this.waitForCanvasScene(container, cloned, i);
|
|
17515
|
-
const expected = this.
|
|
17545
|
+
const expected = this.getExpectedImageIds(cloned, i);
|
|
17516
17546
|
await this.waitForCanvasImages(container, expected);
|
|
17517
17547
|
await this.waitForStableTextMetrics(container, cloned, { clearGlobalCharCache: false });
|
|
17518
17548
|
await this.waitForCanvasScene(container, cloned, i);
|
|
17519
17549
|
}
|
|
17520
17550
|
console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
|
|
17521
|
-
const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-
|
|
17551
|
+
const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-CuD_Ax0x.js");
|
|
17522
17552
|
const prepared = preparePagesForExport(
|
|
17523
17553
|
cloned.pages,
|
|
17524
17554
|
canvasWidth,
|
|
@@ -17590,17 +17620,21 @@ class PixldocsRenderer {
|
|
|
17590
17620
|
walk(page.children);
|
|
17591
17621
|
return ids;
|
|
17592
17622
|
}
|
|
17593
|
-
waitForCanvasImages(container,
|
|
17623
|
+
waitForCanvasImages(container, expectedImages, maxWaitMs, pollMs = 120) {
|
|
17594
17624
|
const timeout = Math.max(500, maxWaitMs ?? this.config.assetWaitTimeoutMs ?? 15e3);
|
|
17595
17625
|
const earlyExitMs = Math.max(250, this.config.assetWaitEarlyExitMs ?? 1500);
|
|
17596
17626
|
const debug = !!this.config.debug;
|
|
17597
|
-
|
|
17627
|
+
const expectedImageIds = Array.isArray(expectedImages) ? [...new Set(expectedImages)] : [];
|
|
17628
|
+
const expectedImageCount = Array.isArray(expectedImages) ? expectedImageIds.length : expectedImages;
|
|
17629
|
+
const strictById = expectedImageIds.length > 0;
|
|
17630
|
+
return new Promise((resolve, reject) => {
|
|
17598
17631
|
const start = Date.now();
|
|
17599
17632
|
let stableFrames = 0;
|
|
17600
17633
|
let lastSummary = "";
|
|
17601
|
-
let
|
|
17634
|
+
let lastProgressKey = "";
|
|
17602
17635
|
let lastProgressAt = Date.now();
|
|
17603
17636
|
const isRenderableImage = (value) => value instanceof HTMLImageElement && value.complete && value.naturalWidth > 0 && value.naturalHeight > 0;
|
|
17637
|
+
const isRenderableCanvas = (value) => value instanceof HTMLCanvasElement && value.width > 0 && value.height > 0;
|
|
17604
17638
|
const collectRenderableImages = (obj, seen) => {
|
|
17605
17639
|
if (!obj || typeof obj !== "object") return;
|
|
17606
17640
|
const candidates = [obj._element, obj._originalElement, obj._filteredEl, obj._cacheCanvasEl];
|
|
@@ -17617,6 +17651,25 @@ class PixldocsRenderer {
|
|
|
17617
17651
|
}
|
|
17618
17652
|
return true;
|
|
17619
17653
|
};
|
|
17654
|
+
const collectRenderableImageIds = (obj, readyIds) => {
|
|
17655
|
+
if (!obj || typeof obj !== "object") return { loaded: false, pending: false };
|
|
17656
|
+
let loaded = false;
|
|
17657
|
+
let pending = false;
|
|
17658
|
+
const candidates = [obj._element, obj._originalElement, obj._filteredEl, obj._cacheCanvasEl];
|
|
17659
|
+
for (const candidate of candidates) {
|
|
17660
|
+
if (isRenderableImage(candidate) || isRenderableCanvas(candidate)) loaded = true;
|
|
17661
|
+
else if (candidate instanceof HTMLImageElement) pending = true;
|
|
17662
|
+
}
|
|
17663
|
+
const nested = Array.isArray(obj._objects) ? obj._objects : Array.isArray(obj.objects) ? obj.objects : [];
|
|
17664
|
+
for (const child of nested) {
|
|
17665
|
+
const childState = collectRenderableImageIds(child, readyIds);
|
|
17666
|
+
loaded = loaded || childState.loaded;
|
|
17667
|
+
pending = pending || childState.pending;
|
|
17668
|
+
}
|
|
17669
|
+
const id = typeof obj.__docuforgeId === "string" ? obj.__docuforgeId : "";
|
|
17670
|
+
if (id && loaded && !pending) readyIds.add(id);
|
|
17671
|
+
return { loaded, pending };
|
|
17672
|
+
};
|
|
17620
17673
|
const getFabricCanvas = () => {
|
|
17621
17674
|
const registry2 = window.__fabricCanvasRegistry;
|
|
17622
17675
|
if (registry2 instanceof Map) {
|
|
@@ -17656,17 +17709,21 @@ class PixldocsRenderer {
|
|
|
17656
17709
|
const fabricObjects = fabricCanvas && typeof fabricCanvas.getObjects === "function" ? fabricCanvas.getObjects() : [];
|
|
17657
17710
|
const renderableImages = /* @__PURE__ */ new Set();
|
|
17658
17711
|
const fabricReady = fabricObjects.every((obj) => collectRenderableImages(obj, renderableImages) !== false);
|
|
17712
|
+
const renderableImageIds = /* @__PURE__ */ new Set();
|
|
17713
|
+
for (const obj of fabricObjects) collectRenderableImageIds(obj, renderableImageIds);
|
|
17714
|
+
const missingImageIds = strictById ? expectedImageIds.filter((id) => !renderableImageIds.has(id)) : [];
|
|
17659
17715
|
const actualImageCount = Math.max(domImages.length, renderableImages.size);
|
|
17660
17716
|
const canvasReady = !!fabricCanvas && !!(fabricCanvas.lowerCanvasEl || fabricCanvas.upperCanvasEl);
|
|
17661
|
-
const hasExpectedAssets = expectedImageCount === 0 ? true : actualImageCount >= expectedImageCount;
|
|
17717
|
+
const hasExpectedAssets = strictById ? missingImageIds.length === 0 : expectedImageCount === 0 ? true : actualImageCount >= expectedImageCount;
|
|
17662
17718
|
const ready = allDomLoaded && fabricReady && hasExpectedAssets;
|
|
17663
|
-
const summary = `expected=${expectedImageCount} actual=${actualImageCount} dom=${domImages.length} fabricReady=${fabricReady} domReady=${allDomLoaded} canvasReady=${canvasReady}`;
|
|
17719
|
+
const summary = `expected=${expectedImageCount} actual=${actualImageCount} fabricIds=${renderableImageIds.size} dom=${domImages.length} fabricReady=${fabricReady} domReady=${allDomLoaded} canvasReady=${canvasReady}${missingImageIds.length ? ` missing=${missingImageIds.join(",")}` : ""}`;
|
|
17664
17720
|
if (summary !== lastSummary) {
|
|
17665
17721
|
lastSummary = summary;
|
|
17666
17722
|
if (debug) console.log(`[canvas-renderer][asset-wait] ${summary}`);
|
|
17667
17723
|
}
|
|
17668
|
-
|
|
17669
|
-
|
|
17724
|
+
const progressKey = strictById ? expectedImageIds.filter((id) => renderableImageIds.has(id)).join("|") : String(actualImageCount);
|
|
17725
|
+
if (progressKey !== lastProgressKey) {
|
|
17726
|
+
lastProgressKey = progressKey;
|
|
17670
17727
|
lastProgressAt = Date.now();
|
|
17671
17728
|
}
|
|
17672
17729
|
if (ready) {
|
|
@@ -17681,7 +17738,7 @@ class PixldocsRenderer {
|
|
|
17681
17738
|
}
|
|
17682
17739
|
const sceneSettled = fabricReady && allDomLoaded && canvasReady && fabricObjects.length > 0;
|
|
17683
17740
|
const idleFor = Date.now() - lastProgressAt;
|
|
17684
|
-
if (sceneSettled && actualImageCount < expectedImageCount && idleFor >= earlyExitMs) {
|
|
17741
|
+
if (!strictById && sceneSettled && actualImageCount < expectedImageCount && idleFor >= earlyExitMs) {
|
|
17685
17742
|
if (debug) {
|
|
17686
17743
|
console.log(
|
|
17687
17744
|
`[canvas-renderer][asset-wait] early-exit after ${elapsed}ms (idle=${idleFor}ms, ${summary})`
|
|
@@ -17705,6 +17762,10 @@ class PixldocsRenderer {
|
|
|
17705
17762
|
console.warn(`[canvas-renderer][asset-wait-timeout] elapsed=${elapsed}ms ${summary}`);
|
|
17706
17763
|
console.warn("[canvas-renderer][asset-wait-timeout][dom-images]", domImageDebug);
|
|
17707
17764
|
console.warn("[canvas-renderer][asset-wait-timeout][fabric-images]", fabricImageDebug);
|
|
17765
|
+
if (strictById && missingImageIds.length > 0) {
|
|
17766
|
+
reject(new Error(`[canvas-renderer][asset-wait-timeout] Missing expected image(s): ${missingImageIds.join(", ")}`));
|
|
17767
|
+
return;
|
|
17768
|
+
}
|
|
17708
17769
|
settle();
|
|
17709
17770
|
return;
|
|
17710
17771
|
}
|
|
@@ -17905,8 +17966,8 @@ class PixldocsRenderer {
|
|
|
17905
17966
|
const onReadyOnce = () => {
|
|
17906
17967
|
this.waitForCanvasScene(container, renderConfig, pageIndex).then(async () => {
|
|
17907
17968
|
const fabricInstance = this.getFabricCanvasFromContainer(container);
|
|
17908
|
-
const
|
|
17909
|
-
await this.waitForCanvasImages(container,
|
|
17969
|
+
const expectedImageIds = this.getExpectedImageIds(renderConfig, pageIndex);
|
|
17970
|
+
await this.waitForCanvasImages(container, expectedImageIds);
|
|
17910
17971
|
await this.waitForStableTextMetrics(container, renderConfig);
|
|
17911
17972
|
await this.waitForCanvasScene(container, renderConfig, pageIndex);
|
|
17912
17973
|
if (!fabricInstance) return settle();
|
|
@@ -17932,8 +17993,8 @@ class PixldocsRenderer {
|
|
|
17932
17993
|
this.waitForCanvasScene(container, renderConfig, pageIndex).then(async () => {
|
|
17933
17994
|
try {
|
|
17934
17995
|
const fabricInstance = this.getFabricCanvasFromContainer(container);
|
|
17935
|
-
const
|
|
17936
|
-
await this.waitForCanvasImages(container,
|
|
17996
|
+
const expectedImageIds = this.getExpectedImageIds(renderConfig, pageIndex);
|
|
17997
|
+
await this.waitForCanvasImages(container, expectedImageIds);
|
|
17937
17998
|
await this.waitForStableTextMetrics(container, renderConfig);
|
|
17938
17999
|
await this.waitForCanvasScene(container, renderConfig, pageIndex);
|
|
17939
18000
|
firstMountSettled = true;
|
|
@@ -18044,8 +18105,8 @@ class PixldocsRenderer {
|
|
|
18044
18105
|
this.waitForCanvasScene(container, renderConfig, pageIndex).then(async () => {
|
|
18045
18106
|
var _a, _b;
|
|
18046
18107
|
try {
|
|
18047
|
-
const
|
|
18048
|
-
await this.waitForCanvasImages(container,
|
|
18108
|
+
const expectedImageIds = this.getExpectedImageIds(renderConfig, pageIndex);
|
|
18109
|
+
await this.waitForCanvasImages(container, expectedImageIds);
|
|
18049
18110
|
await this.waitForStableTextMetrics(container, renderConfig);
|
|
18050
18111
|
await this.waitForCanvasScene(container, renderConfig, pageIndex);
|
|
18051
18112
|
const fabricInstance = this.getFabricCanvasFromContainer(container);
|
|
@@ -19684,7 +19745,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
|
|
|
19684
19745
|
if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
|
|
19685
19746
|
sanitizeSvgTreeForPdf(svgToDraw);
|
|
19686
19747
|
try {
|
|
19687
|
-
const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-
|
|
19748
|
+
const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-CuD_Ax0x.js");
|
|
19688
19749
|
try {
|
|
19689
19750
|
await logTextMeasurementDiagnostic(svgToDraw);
|
|
19690
19751
|
} catch {
|
|
@@ -20084,4 +20145,4 @@ export {
|
|
|
20084
20145
|
buildTeaserBlurFlatKeys as y,
|
|
20085
20146
|
collectFontDescriptorsFromConfig as z
|
|
20086
20147
|
};
|
|
20087
|
-
//# sourceMappingURL=index-
|
|
20148
|
+
//# sourceMappingURL=index-C1BrMJjh.js.map
|