@pixldocs/canvas-renderer 0.5.194 → 0.5.196
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 +15 -17
- package/dist/{index-pshD8xMc.cjs → index-DQnFS9GV.cjs} +47 -27
- package/dist/{index-pshD8xMc.cjs.map → index-DQnFS9GV.cjs.map} +1 -1
- package/dist/{index-C1BrMJjh.js → index-KqyAyv91.js} +47 -27
- package/dist/{index-C1BrMJjh.js.map → index-KqyAyv91.js.map} +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +5 -6
- package/dist/index.js +1 -1
- package/dist/{vectorPdfExport-CuD_Ax0x.js → vectorPdfExport-BBQHeFKb.js} +4 -4
- package/dist/{vectorPdfExport-CuD_Ax0x.js.map → vectorPdfExport-BBQHeFKb.js.map} +1 -1
- package/dist/{vectorPdfExport-D5Xagjna.cjs → vectorPdfExport-BGlG4zN9.cjs} +4 -4
- package/dist/{vectorPdfExport-D5Xagjna.cjs.map → vectorPdfExport-BGlG4zN9.cjs.map} +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -275,17 +275,16 @@ const renderer = new PixldocsRenderer({
|
|
|
275
275
|
pixelRatio?: number, // default: 2
|
|
276
276
|
assetWaitTimeoutMs?: number, // default: 15000; use ~30000 for multi-photo PDF exports
|
|
277
277
|
assetWaitEarlyExitMs?: number, // default: 1500; ignored for strict PDF image-ID waits
|
|
278
|
-
maxImageEdgePx?: number, //
|
|
278
|
+
maxImageEdgePx?: number, // auto: 2048 for user-uploaded PDF photos; 0 disables
|
|
279
279
|
debug?: boolean, // logs image wait/SVG completeness diagnostics
|
|
280
280
|
});
|
|
281
281
|
```
|
|
282
282
|
|
|
283
283
|
For BioMaker-style PDFs with user-uploaded profile/gallery photos, use the live
|
|
284
|
-
SVG path
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
silently exporting an incomplete PDF.
|
|
284
|
+
SVG path and **do not** set `forcePerElementPdf: true`. Large `data:image/*`
|
|
285
|
+
photos are normalized before the hidden Fabric canvas mounts (default cap:
|
|
286
|
+
2048px edge and ~2MB encoded data URL) so svg2pdf does not corrupt/drop
|
|
287
|
+
multi-megapixel uploads.
|
|
289
288
|
|
|
290
289
|
#### `renderer.renderFromForm(options)`
|
|
291
290
|
|
|
@@ -638,13 +637,12 @@ v0.5.191 fixes this with two new options:
|
|
|
638
637
|
```ts
|
|
639
638
|
const renderer = new PixldocsRenderer({
|
|
640
639
|
supabaseUrl, supabaseAnonKey,
|
|
641
|
-
//
|
|
642
|
-
//
|
|
643
|
-
forcePerElementPdf:
|
|
644
|
-
//
|
|
645
|
-
//
|
|
646
|
-
//
|
|
647
|
-
maxImageEdgePx: 2048, // 0 to disable
|
|
640
|
+
// Keep the live SVG path. Use true only as a manual escape hatch because
|
|
641
|
+
// the per-element/config-space path can drift from live Fabric layout.
|
|
642
|
+
forcePerElementPdf: 'auto', // true | false | 'auto' (default)
|
|
643
|
+
// Normalize data:image/* photos before mounting into Fabric. The default
|
|
644
|
+
// auto value is 2048 and also recompresses oversized encoded data URLs.
|
|
645
|
+
maxImageEdgePx: 2048, // default auto; 0 disables
|
|
648
646
|
});
|
|
649
647
|
|
|
650
648
|
const pdf = await renderer.renderPdfFromForm({
|
|
@@ -655,10 +653,10 @@ const pdf = await renderer.renderPdfFromForm({
|
|
|
655
653
|
});
|
|
656
654
|
```
|
|
657
655
|
|
|
658
|
-
**Defaults:** `forcePerElementPdf: 'auto'`
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
656
|
+
**Defaults:** `forcePerElementPdf: 'auto'` keeps the live SVG path. Any resolved
|
|
657
|
+
config containing `data:image/(jpeg|png|webp)` sources is normalized before PDF
|
|
658
|
+
mounting so large camera/gallery uploads behave consistently across browsers
|
|
659
|
+
and server renderers.
|
|
662
660
|
|
|
663
661
|
Hosts that previously monkey-patched `captureFabricCanvasSvgForPdf` to
|
|
664
662
|
throw (the unofficial BioMaker workaround) can delete that patch after
|
|
@@ -16937,9 +16937,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
|
|
|
16937
16937
|
}
|
|
16938
16938
|
return svgString;
|
|
16939
16939
|
}
|
|
16940
|
-
const resolvedPackageVersion = "0.5.
|
|
16940
|
+
const resolvedPackageVersion = "0.5.196";
|
|
16941
16941
|
const PACKAGE_VERSION = resolvedPackageVersion;
|
|
16942
|
-
const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.
|
|
16942
|
+
const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.196";
|
|
16943
16943
|
const roundParityValue = (value) => {
|
|
16944
16944
|
if (typeof value !== "number") return value;
|
|
16945
16945
|
return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
|
|
@@ -16999,11 +16999,16 @@ function detectSafariOrIos() {
|
|
|
16999
16999
|
return false;
|
|
17000
17000
|
}
|
|
17001
17001
|
}
|
|
17002
|
-
async function downscaleConfigRasterImages(config, maxEdgePx) {
|
|
17002
|
+
async function downscaleConfigRasterImages(config, maxEdgePx, maxDataUrlBytes = 2e6) {
|
|
17003
17003
|
if (!maxEdgePx || maxEdgePx <= 0) return 0;
|
|
17004
17004
|
if (typeof document === "undefined") return 0;
|
|
17005
17005
|
const targets = [];
|
|
17006
17006
|
const isRasterDataUrl = (u) => typeof u === "string" && /^data:image\/(jpeg|jpg|png|webp)[;,]/i.test(u);
|
|
17007
|
+
const estimateDataUrlBytes = (u) => {
|
|
17008
|
+
const comma = u.indexOf(",");
|
|
17009
|
+
const payload = comma >= 0 ? u.slice(comma + 1) : u;
|
|
17010
|
+
return Math.floor(payload.length * 0.75);
|
|
17011
|
+
};
|
|
17007
17012
|
const walk = (nodes) => {
|
|
17008
17013
|
if (!Array.isArray(nodes)) return;
|
|
17009
17014
|
for (const node of nodes) {
|
|
@@ -17029,28 +17034,43 @@ async function downscaleConfigRasterImages(config, maxEdgePx) {
|
|
|
17029
17034
|
const w = img.naturalWidth, h = img.naturalHeight;
|
|
17030
17035
|
if (!w || !h) return null;
|
|
17031
17036
|
const longest = Math.max(w, h);
|
|
17032
|
-
|
|
17033
|
-
const
|
|
17034
|
-
|
|
17035
|
-
|
|
17036
|
-
|
|
17037
|
-
|
|
17038
|
-
|
|
17039
|
-
|
|
17040
|
-
|
|
17041
|
-
|
|
17042
|
-
|
|
17043
|
-
|
|
17044
|
-
|
|
17037
|
+
const tooLargeByEdge = longest > maxEdgePx;
|
|
17038
|
+
const tooLargeByBytes = estimateDataUrlBytes(dataUrl) > maxDataUrlBytes;
|
|
17039
|
+
if (!tooLargeByEdge && !tooLargeByBytes) return null;
|
|
17040
|
+
let scale = tooLargeByEdge ? maxEdgePx / longest : 1;
|
|
17041
|
+
let quality = 0.85;
|
|
17042
|
+
let best = null;
|
|
17043
|
+
for (let attempt = 0; attempt < 4; attempt++) {
|
|
17044
|
+
const tw = Math.max(1, Math.round(w * scale));
|
|
17045
|
+
const th = Math.max(1, Math.round(h * scale));
|
|
17046
|
+
const canvas = document.createElement("canvas");
|
|
17047
|
+
canvas.width = tw;
|
|
17048
|
+
canvas.height = th;
|
|
17049
|
+
const ctx = canvas.getContext("2d");
|
|
17050
|
+
if (!ctx) return best;
|
|
17051
|
+
ctx.fillStyle = "#ffffff";
|
|
17052
|
+
ctx.fillRect(0, 0, tw, th);
|
|
17053
|
+
ctx.drawImage(img, 0, 0, tw, th);
|
|
17054
|
+
best = canvas.toDataURL("image/jpeg", quality);
|
|
17055
|
+
const outBytes = estimateDataUrlBytes(best);
|
|
17056
|
+
if (outBytes <= maxDataUrlBytes || attempt === 3) return best;
|
|
17057
|
+
const byteScale = Math.sqrt(maxDataUrlBytes / Math.max(1, outBytes)) * 0.92;
|
|
17058
|
+
scale = Math.max(0.1, scale * Math.min(0.95, byteScale));
|
|
17059
|
+
quality = Math.max(0.68, quality - 0.06);
|
|
17060
|
+
}
|
|
17061
|
+
return best;
|
|
17045
17062
|
} catch {
|
|
17046
17063
|
return null;
|
|
17047
17064
|
}
|
|
17048
17065
|
};
|
|
17049
17066
|
let shrunk = 0;
|
|
17050
17067
|
for (const { node, field } of targets) {
|
|
17068
|
+
const before = String(node[field]);
|
|
17051
17069
|
const next = await shrinkOne(String(node[field]));
|
|
17052
17070
|
if (next) {
|
|
17053
17071
|
node[field] = next;
|
|
17072
|
+
const twin = field === "src" ? "imageUrl" : "src";
|
|
17073
|
+
if (node[twin] === before) node[twin] = next;
|
|
17054
17074
|
shrunk++;
|
|
17055
17075
|
}
|
|
17056
17076
|
}
|
|
@@ -17489,7 +17509,7 @@ class PixldocsRenderer {
|
|
|
17489
17509
|
const isSafariLike = detectSafariOrIos();
|
|
17490
17510
|
const shouldForcePerElement = forceMode === true ? true : forceMode === false ? false : hasUserDataImage && isSafariLike;
|
|
17491
17511
|
const maxEdgeOpt = options.maxImageEdgePx ?? this.config.maxImageEdgePx;
|
|
17492
|
-
const effectiveMaxEdge = typeof maxEdgeOpt === "number" ? Math.max(0, maxEdgeOpt | 0) :
|
|
17512
|
+
const effectiveMaxEdge = typeof maxEdgeOpt === "number" ? Math.max(0, maxEdgeOpt | 0) : hasUserDataImage ? 2048 : 0;
|
|
17493
17513
|
if (effectiveMaxEdge > 0) {
|
|
17494
17514
|
try {
|
|
17495
17515
|
const downscaled = await downscaleConfigRasterImages(cloned, effectiveMaxEdge);
|
|
@@ -17566,7 +17586,7 @@ class PixldocsRenderer {
|
|
|
17566
17586
|
await this.waitForCanvasScene(container, cloned, i);
|
|
17567
17587
|
}
|
|
17568
17588
|
console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
|
|
17569
|
-
const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-
|
|
17589
|
+
const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-BGlG4zN9.cjs"));
|
|
17570
17590
|
const prepared = preparePagesForExport(
|
|
17571
17591
|
cloned.pages,
|
|
17572
17592
|
canvasWidth,
|
|
@@ -17588,11 +17608,10 @@ class PixldocsRenderer {
|
|
|
17588
17608
|
// correct. v0.5.191 shipped with this skip enabled and produced exactly
|
|
17589
17609
|
// that regression.
|
|
17590
17610
|
//
|
|
17591
|
-
// The
|
|
17592
|
-
// `downscaleConfigRasterImages()` pre-pass above, which shrinks
|
|
17593
|
-
// `data:image/*`
|
|
17594
|
-
// That
|
|
17595
|
-
// layout pipeline.
|
|
17611
|
+
// The large-photo missing/corrupt image issue is solved by the
|
|
17612
|
+
// `downscaleConfigRasterImages()` pre-pass above, which shrinks and
|
|
17613
|
+
// recompresses oversized `data:image/*` sources before the canvas
|
|
17614
|
+
// mounts. That keeps SVG capture reliable without altering layout.
|
|
17596
17615
|
//
|
|
17597
17616
|
// We still honor an explicit `forcePerElementPdf: true` from the host
|
|
17598
17617
|
// app (advanced opt-in for niche cases), but the auto path no longer
|
|
@@ -17781,8 +17800,9 @@ class PixldocsRenderer {
|
|
|
17781
17800
|
console.warn("[canvas-renderer][asset-wait-timeout][dom-images]", domImageDebug);
|
|
17782
17801
|
console.warn("[canvas-renderer][asset-wait-timeout][fabric-images]", fabricImageDebug);
|
|
17783
17802
|
if (strictById && missingImageIds.length > 0) {
|
|
17784
|
-
|
|
17785
|
-
|
|
17803
|
+
console.warn(
|
|
17804
|
+
`[canvas-renderer][asset-wait-timeout] Missing expected image(s) — continuing with partial set: ${missingImageIds.join(", ")}`
|
|
17805
|
+
);
|
|
17786
17806
|
}
|
|
17787
17807
|
settle();
|
|
17788
17808
|
return;
|
|
@@ -19763,7 +19783,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
|
|
|
19763
19783
|
if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
|
|
19764
19784
|
sanitizeSvgTreeForPdf(svgToDraw);
|
|
19765
19785
|
try {
|
|
19766
|
-
const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-
|
|
19786
|
+
const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-BGlG4zN9.cjs"));
|
|
19767
19787
|
try {
|
|
19768
19788
|
await logTextMeasurementDiagnostic(svgToDraw);
|
|
19769
19789
|
} catch {
|
|
@@ -20160,4 +20180,4 @@ exports.setAutoShrinkDebug = setAutoShrinkDebug;
|
|
|
20160
20180
|
exports.setBundledAssetPrefixes = setBundledAssetPrefixes;
|
|
20161
20181
|
exports.warmResolvedTemplateForPreview = warmResolvedTemplateForPreview;
|
|
20162
20182
|
exports.warmTemplateFromForm = warmTemplateFromForm;
|
|
20163
|
-
//# sourceMappingURL=index-
|
|
20183
|
+
//# sourceMappingURL=index-DQnFS9GV.cjs.map
|