@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.
@@ -16937,9 +16937,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
16937
16937
  }
16938
16938
  return svgString;
16939
16939
  }
16940
- const resolvedPackageVersion = "0.5.195";
16940
+ const resolvedPackageVersion = "0.5.197";
16941
16941
  const PACKAGE_VERSION = resolvedPackageVersion;
16942
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.195";
16942
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.197";
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) {
@@ -17019,38 +17024,82 @@ async function downscaleConfigRasterImages(config, maxEdgePx) {
17019
17024
  if (targets.length === 0) return 0;
17020
17025
  const shrinkOne = async (dataUrl) => {
17021
17026
  try {
17022
- const img = await new Promise((resolve, reject) => {
17027
+ const decode = async (src) => new Promise((resolve, reject) => {
17023
17028
  const el = new Image();
17024
17029
  el.onload = () => resolve(el);
17025
17030
  el.onerror = (e) => reject(e);
17026
17031
  el.decoding = "sync";
17027
- el.src = dataUrl;
17032
+ el.src = src;
17028
17033
  });
17034
+ let img;
17035
+ let blobUrl = null;
17036
+ try {
17037
+ img = await decode(dataUrl);
17038
+ } catch {
17039
+ try {
17040
+ const resp = await fetch(dataUrl);
17041
+ const blob = await resp.blob();
17042
+ blobUrl = URL.createObjectURL(blob);
17043
+ img = await decode(blobUrl);
17044
+ } catch (inner) {
17045
+ if (blobUrl) URL.revokeObjectURL(blobUrl);
17046
+ console.warn("[canvas-renderer] shrinkOne: failed to decode oversized data URL on this browser, dropping shrink attempt", inner);
17047
+ return null;
17048
+ }
17049
+ }
17029
17050
  const w = img.naturalWidth, h = img.naturalHeight;
17030
- if (!w || !h) return null;
17051
+ if (!w || !h) {
17052
+ if (blobUrl) URL.revokeObjectURL(blobUrl);
17053
+ return null;
17054
+ }
17031
17055
  const longest = Math.max(w, h);
17032
- if (longest <= maxEdgePx) return null;
17033
- const scale = maxEdgePx / longest;
17034
- const tw = Math.max(1, Math.round(w * scale));
17035
- const th = Math.max(1, Math.round(h * scale));
17036
- const canvas = document.createElement("canvas");
17037
- canvas.width = tw;
17038
- canvas.height = th;
17039
- const ctx = canvas.getContext("2d");
17040
- if (!ctx) return null;
17041
- ctx.fillStyle = "#ffffff";
17042
- ctx.fillRect(0, 0, tw, th);
17043
- ctx.drawImage(img, 0, 0, tw, th);
17044
- return canvas.toDataURL("image/jpeg", 0.85);
17056
+ const tooLargeByEdge = longest > maxEdgePx;
17057
+ const tooLargeByBytes = estimateDataUrlBytes(dataUrl) > maxDataUrlBytes;
17058
+ if (!tooLargeByEdge && !tooLargeByBytes) {
17059
+ if (blobUrl) URL.revokeObjectURL(blobUrl);
17060
+ return null;
17061
+ }
17062
+ let scale = tooLargeByEdge ? maxEdgePx / longest : 1;
17063
+ let quality = 0.85;
17064
+ let best = null;
17065
+ for (let attempt = 0; attempt < 4; attempt++) {
17066
+ const tw = Math.max(1, Math.round(w * scale));
17067
+ const th = Math.max(1, Math.round(h * scale));
17068
+ const canvas = document.createElement("canvas");
17069
+ canvas.width = tw;
17070
+ canvas.height = th;
17071
+ const ctx = canvas.getContext("2d");
17072
+ if (!ctx) {
17073
+ if (blobUrl) URL.revokeObjectURL(blobUrl);
17074
+ return best;
17075
+ }
17076
+ ctx.fillStyle = "#ffffff";
17077
+ ctx.fillRect(0, 0, tw, th);
17078
+ ctx.drawImage(img, 0, 0, tw, th);
17079
+ best = canvas.toDataURL("image/jpeg", quality);
17080
+ const outBytes = estimateDataUrlBytes(best);
17081
+ if (outBytes <= maxDataUrlBytes || attempt === 3) {
17082
+ if (blobUrl) URL.revokeObjectURL(blobUrl);
17083
+ return best;
17084
+ }
17085
+ const byteScale = Math.sqrt(maxDataUrlBytes / Math.max(1, outBytes)) * 0.92;
17086
+ scale = Math.max(0.1, scale * Math.min(0.95, byteScale));
17087
+ quality = Math.max(0.68, quality - 0.06);
17088
+ }
17089
+ if (blobUrl) URL.revokeObjectURL(blobUrl);
17090
+ return best;
17045
17091
  } catch {
17046
17092
  return null;
17047
17093
  }
17048
17094
  };
17049
17095
  let shrunk = 0;
17050
17096
  for (const { node, field } of targets) {
17097
+ const before = String(node[field]);
17051
17098
  const next = await shrinkOne(String(node[field]));
17052
17099
  if (next) {
17053
17100
  node[field] = next;
17101
+ const twin = field === "src" ? "imageUrl" : "src";
17102
+ if (node[twin] === before) node[twin] = next;
17054
17103
  shrunk++;
17055
17104
  }
17056
17105
  }
@@ -17489,7 +17538,7 @@ class PixldocsRenderer {
17489
17538
  const isSafariLike = detectSafariOrIos();
17490
17539
  const shouldForcePerElement = forceMode === true ? true : forceMode === false ? false : hasUserDataImage && isSafariLike;
17491
17540
  const maxEdgeOpt = options.maxImageEdgePx ?? this.config.maxImageEdgePx;
17492
- const effectiveMaxEdge = typeof maxEdgeOpt === "number" ? Math.max(0, maxEdgeOpt | 0) : shouldForcePerElement ? 2048 : 0;
17541
+ const effectiveMaxEdge = typeof maxEdgeOpt === "number" ? Math.max(0, maxEdgeOpt | 0) : hasUserDataImage ? 2048 : 0;
17493
17542
  if (effectiveMaxEdge > 0) {
17494
17543
  try {
17495
17544
  const downscaled = await downscaleConfigRasterImages(cloned, effectiveMaxEdge);
@@ -17566,7 +17615,7 @@ class PixldocsRenderer {
17566
17615
  await this.waitForCanvasScene(container, cloned, i);
17567
17616
  }
17568
17617
  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-DOflxvRE.cjs"));
17618
+ const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-CMSo382K.cjs"));
17570
17619
  const prepared = preparePagesForExport(
17571
17620
  cloned.pages,
17572
17621
  canvasWidth,
@@ -17588,11 +17637,10 @@ class PixldocsRenderer {
17588
17637
  // correct. v0.5.191 shipped with this skip enabled and produced exactly
17589
17638
  // that regression.
17590
17639
  //
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.
17640
+ // The large-photo missing/corrupt image issue is solved by the
17641
+ // `downscaleConfigRasterImages()` pre-pass above, which shrinks and
17642
+ // recompresses oversized `data:image/*` sources before the canvas
17643
+ // mounts. That keeps SVG capture reliable without altering layout.
17596
17644
  //
17597
17645
  // We still honor an explicit `forcePerElementPdf: true` from the host
17598
17646
  // app (advanced opt-in for niche cases), but the auto path no longer
@@ -19764,7 +19812,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
19764
19812
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
19765
19813
  sanitizeSvgTreeForPdf(svgToDraw);
19766
19814
  try {
19767
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-DOflxvRE.cjs"));
19815
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-CMSo382K.cjs"));
19768
19816
  try {
19769
19817
  await logTextMeasurementDiagnostic(svgToDraw);
19770
19818
  } catch {
@@ -20161,4 +20209,4 @@ exports.setAutoShrinkDebug = setAutoShrinkDebug;
20161
20209
  exports.setBundledAssetPrefixes = setBundledAssetPrefixes;
20162
20210
  exports.warmResolvedTemplateForPreview = warmResolvedTemplateForPreview;
20163
20211
  exports.warmTemplateFromForm = warmTemplateFromForm;
20164
- //# sourceMappingURL=index-DsCBhthJ.cjs.map
20212
+ //# sourceMappingURL=index-dXQP21w2.cjs.map