@pixldocs/canvas-renderer 0.5.195 → 0.5.197
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-BggTMIGH.js → index-6bqq7X_L.js} +76 -28
- package/dist/{index-BggTMIGH.js.map → index-6bqq7X_L.js.map} +1 -1
- package/dist/{index-DsCBhthJ.cjs → index-dXQP21w2.cjs} +76 -28
- package/dist/{index-DsCBhthJ.cjs.map → index-dXQP21w2.cjs.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-DOflxvRE.cjs → vectorPdfExport-CMSo382K.cjs} +4 -4
- package/dist/{vectorPdfExport-DOflxvRE.cjs.map → vectorPdfExport-CMSo382K.cjs.map} +1 -1
- package/dist/{vectorPdfExport-eV507wxX.js → vectorPdfExport-D4pUlnXw.js} +4 -4
- package/dist/{vectorPdfExport-eV507wxX.js.map → vectorPdfExport-D4pUlnXw.js.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
|
|
@@ -16919,9 +16919,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
|
|
|
16919
16919
|
}
|
|
16920
16920
|
return svgString;
|
|
16921
16921
|
}
|
|
16922
|
-
const resolvedPackageVersion = "0.5.
|
|
16922
|
+
const resolvedPackageVersion = "0.5.197";
|
|
16923
16923
|
const PACKAGE_VERSION = resolvedPackageVersion;
|
|
16924
|
-
const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.
|
|
16924
|
+
const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.197";
|
|
16925
16925
|
const roundParityValue = (value) => {
|
|
16926
16926
|
if (typeof value !== "number") return value;
|
|
16927
16927
|
return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
|
|
@@ -16981,11 +16981,16 @@ function detectSafariOrIos() {
|
|
|
16981
16981
|
return false;
|
|
16982
16982
|
}
|
|
16983
16983
|
}
|
|
16984
|
-
async function downscaleConfigRasterImages(config, maxEdgePx) {
|
|
16984
|
+
async function downscaleConfigRasterImages(config, maxEdgePx, maxDataUrlBytes = 2e6) {
|
|
16985
16985
|
if (!maxEdgePx || maxEdgePx <= 0) return 0;
|
|
16986
16986
|
if (typeof document === "undefined") return 0;
|
|
16987
16987
|
const targets = [];
|
|
16988
16988
|
const isRasterDataUrl = (u) => typeof u === "string" && /^data:image\/(jpeg|jpg|png|webp)[;,]/i.test(u);
|
|
16989
|
+
const estimateDataUrlBytes = (u) => {
|
|
16990
|
+
const comma = u.indexOf(",");
|
|
16991
|
+
const payload = comma >= 0 ? u.slice(comma + 1) : u;
|
|
16992
|
+
return Math.floor(payload.length * 0.75);
|
|
16993
|
+
};
|
|
16989
16994
|
const walk = (nodes) => {
|
|
16990
16995
|
if (!Array.isArray(nodes)) return;
|
|
16991
16996
|
for (const node of nodes) {
|
|
@@ -17001,38 +17006,82 @@ async function downscaleConfigRasterImages(config, maxEdgePx) {
|
|
|
17001
17006
|
if (targets.length === 0) return 0;
|
|
17002
17007
|
const shrinkOne = async (dataUrl) => {
|
|
17003
17008
|
try {
|
|
17004
|
-
const
|
|
17009
|
+
const decode = async (src) => new Promise((resolve, reject) => {
|
|
17005
17010
|
const el = new Image();
|
|
17006
17011
|
el.onload = () => resolve(el);
|
|
17007
17012
|
el.onerror = (e) => reject(e);
|
|
17008
17013
|
el.decoding = "sync";
|
|
17009
|
-
el.src =
|
|
17014
|
+
el.src = src;
|
|
17010
17015
|
});
|
|
17016
|
+
let img;
|
|
17017
|
+
let blobUrl = null;
|
|
17018
|
+
try {
|
|
17019
|
+
img = await decode(dataUrl);
|
|
17020
|
+
} catch {
|
|
17021
|
+
try {
|
|
17022
|
+
const resp = await fetch(dataUrl);
|
|
17023
|
+
const blob = await resp.blob();
|
|
17024
|
+
blobUrl = URL.createObjectURL(blob);
|
|
17025
|
+
img = await decode(blobUrl);
|
|
17026
|
+
} catch (inner) {
|
|
17027
|
+
if (blobUrl) URL.revokeObjectURL(blobUrl);
|
|
17028
|
+
console.warn("[canvas-renderer] shrinkOne: failed to decode oversized data URL on this browser, dropping shrink attempt", inner);
|
|
17029
|
+
return null;
|
|
17030
|
+
}
|
|
17031
|
+
}
|
|
17011
17032
|
const w = img.naturalWidth, h = img.naturalHeight;
|
|
17012
|
-
if (!w || !h)
|
|
17033
|
+
if (!w || !h) {
|
|
17034
|
+
if (blobUrl) URL.revokeObjectURL(blobUrl);
|
|
17035
|
+
return null;
|
|
17036
|
+
}
|
|
17013
17037
|
const longest = Math.max(w, h);
|
|
17014
|
-
|
|
17015
|
-
const
|
|
17016
|
-
|
|
17017
|
-
|
|
17018
|
-
|
|
17019
|
-
|
|
17020
|
-
|
|
17021
|
-
|
|
17022
|
-
|
|
17023
|
-
|
|
17024
|
-
|
|
17025
|
-
|
|
17026
|
-
|
|
17038
|
+
const tooLargeByEdge = longest > maxEdgePx;
|
|
17039
|
+
const tooLargeByBytes = estimateDataUrlBytes(dataUrl) > maxDataUrlBytes;
|
|
17040
|
+
if (!tooLargeByEdge && !tooLargeByBytes) {
|
|
17041
|
+
if (blobUrl) URL.revokeObjectURL(blobUrl);
|
|
17042
|
+
return null;
|
|
17043
|
+
}
|
|
17044
|
+
let scale = tooLargeByEdge ? maxEdgePx / longest : 1;
|
|
17045
|
+
let quality = 0.85;
|
|
17046
|
+
let best = null;
|
|
17047
|
+
for (let attempt = 0; attempt < 4; attempt++) {
|
|
17048
|
+
const tw = Math.max(1, Math.round(w * scale));
|
|
17049
|
+
const th = Math.max(1, Math.round(h * scale));
|
|
17050
|
+
const canvas = document.createElement("canvas");
|
|
17051
|
+
canvas.width = tw;
|
|
17052
|
+
canvas.height = th;
|
|
17053
|
+
const ctx = canvas.getContext("2d");
|
|
17054
|
+
if (!ctx) {
|
|
17055
|
+
if (blobUrl) URL.revokeObjectURL(blobUrl);
|
|
17056
|
+
return best;
|
|
17057
|
+
}
|
|
17058
|
+
ctx.fillStyle = "#ffffff";
|
|
17059
|
+
ctx.fillRect(0, 0, tw, th);
|
|
17060
|
+
ctx.drawImage(img, 0, 0, tw, th);
|
|
17061
|
+
best = canvas.toDataURL("image/jpeg", quality);
|
|
17062
|
+
const outBytes = estimateDataUrlBytes(best);
|
|
17063
|
+
if (outBytes <= maxDataUrlBytes || attempt === 3) {
|
|
17064
|
+
if (blobUrl) URL.revokeObjectURL(blobUrl);
|
|
17065
|
+
return best;
|
|
17066
|
+
}
|
|
17067
|
+
const byteScale = Math.sqrt(maxDataUrlBytes / Math.max(1, outBytes)) * 0.92;
|
|
17068
|
+
scale = Math.max(0.1, scale * Math.min(0.95, byteScale));
|
|
17069
|
+
quality = Math.max(0.68, quality - 0.06);
|
|
17070
|
+
}
|
|
17071
|
+
if (blobUrl) URL.revokeObjectURL(blobUrl);
|
|
17072
|
+
return best;
|
|
17027
17073
|
} catch {
|
|
17028
17074
|
return null;
|
|
17029
17075
|
}
|
|
17030
17076
|
};
|
|
17031
17077
|
let shrunk = 0;
|
|
17032
17078
|
for (const { node, field } of targets) {
|
|
17079
|
+
const before = String(node[field]);
|
|
17033
17080
|
const next = await shrinkOne(String(node[field]));
|
|
17034
17081
|
if (next) {
|
|
17035
17082
|
node[field] = next;
|
|
17083
|
+
const twin = field === "src" ? "imageUrl" : "src";
|
|
17084
|
+
if (node[twin] === before) node[twin] = next;
|
|
17036
17085
|
shrunk++;
|
|
17037
17086
|
}
|
|
17038
17087
|
}
|
|
@@ -17471,7 +17520,7 @@ class PixldocsRenderer {
|
|
|
17471
17520
|
const isSafariLike = detectSafariOrIos();
|
|
17472
17521
|
const shouldForcePerElement = forceMode === true ? true : forceMode === false ? false : hasUserDataImage && isSafariLike;
|
|
17473
17522
|
const maxEdgeOpt = options.maxImageEdgePx ?? this.config.maxImageEdgePx;
|
|
17474
|
-
const effectiveMaxEdge = typeof maxEdgeOpt === "number" ? Math.max(0, maxEdgeOpt | 0) :
|
|
17523
|
+
const effectiveMaxEdge = typeof maxEdgeOpt === "number" ? Math.max(0, maxEdgeOpt | 0) : hasUserDataImage ? 2048 : 0;
|
|
17475
17524
|
if (effectiveMaxEdge > 0) {
|
|
17476
17525
|
try {
|
|
17477
17526
|
const downscaled = await downscaleConfigRasterImages(cloned, effectiveMaxEdge);
|
|
@@ -17548,7 +17597,7 @@ class PixldocsRenderer {
|
|
|
17548
17597
|
await this.waitForCanvasScene(container, cloned, i);
|
|
17549
17598
|
}
|
|
17550
17599
|
console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
|
|
17551
|
-
const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-
|
|
17600
|
+
const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-D4pUlnXw.js");
|
|
17552
17601
|
const prepared = preparePagesForExport(
|
|
17553
17602
|
cloned.pages,
|
|
17554
17603
|
canvasWidth,
|
|
@@ -17570,11 +17619,10 @@ class PixldocsRenderer {
|
|
|
17570
17619
|
// correct. v0.5.191 shipped with this skip enabled and produced exactly
|
|
17571
17620
|
// that regression.
|
|
17572
17621
|
//
|
|
17573
|
-
// The
|
|
17574
|
-
// `downscaleConfigRasterImages()` pre-pass above, which shrinks
|
|
17575
|
-
// `data:image/*`
|
|
17576
|
-
// That
|
|
17577
|
-
// layout pipeline.
|
|
17622
|
+
// The large-photo missing/corrupt image issue is solved by the
|
|
17623
|
+
// `downscaleConfigRasterImages()` pre-pass above, which shrinks and
|
|
17624
|
+
// recompresses oversized `data:image/*` sources before the canvas
|
|
17625
|
+
// mounts. That keeps SVG capture reliable without altering layout.
|
|
17578
17626
|
//
|
|
17579
17627
|
// We still honor an explicit `forcePerElementPdf: true` from the host
|
|
17580
17628
|
// app (advanced opt-in for niche cases), but the auto path no longer
|
|
@@ -19746,7 +19794,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
|
|
|
19746
19794
|
if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
|
|
19747
19795
|
sanitizeSvgTreeForPdf(svgToDraw);
|
|
19748
19796
|
try {
|
|
19749
|
-
const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-
|
|
19797
|
+
const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-D4pUlnXw.js");
|
|
19750
19798
|
try {
|
|
19751
19799
|
await logTextMeasurementDiagnostic(svgToDraw);
|
|
19752
19800
|
} catch {
|
|
@@ -20146,4 +20194,4 @@ export {
|
|
|
20146
20194
|
buildTeaserBlurFlatKeys as y,
|
|
20147
20195
|
collectFontDescriptorsFromConfig as z
|
|
20148
20196
|
};
|
|
20149
|
-
//# sourceMappingURL=index-
|
|
20197
|
+
//# sourceMappingURL=index-6bqq7X_L.js.map
|