@pixldocs/canvas-renderer 0.5.210 → 0.5.212

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.
@@ -4053,6 +4053,11 @@ async function loadImageAsync(element, placeholder, fc, fabricRef, syncLockedRef
4053
4053
  setObjectData(cropGroup, element.id);
4054
4054
  cropGroup.__imageSrc = imageUrl;
4055
4055
  cropGroup.__svgColorMap = nextSvgColorMap;
4056
+ cropGroup.set({
4057
+ flipX: element.flipX ?? false,
4058
+ flipY: element.flipY ?? false
4059
+ });
4060
+ cropGroup.setCoords();
4056
4061
  finalObject = cropGroup;
4057
4062
  } else {
4058
4063
  setObjectData(img, element.id);
@@ -6879,8 +6884,10 @@ function buildTextShadow(element) {
6879
6884
  if (!color || color === "transparent") return null;
6880
6885
  const type = element.textShadowType;
6881
6886
  if (type && type !== "drop") return null;
6887
+ const { perPassAlpha } = resolveShadowStrength(element);
6888
+ const finalColor = applyAlphaMultiplier(String(color), perPassAlpha);
6882
6889
  return new fabric.Shadow({
6883
- color: String(color),
6890
+ color: finalColor,
6884
6891
  blur: blur || 0,
6885
6892
  offsetX: ox || 0,
6886
6893
  offsetY: oy || 0,
@@ -6888,6 +6895,65 @@ function buildTextShadow(element) {
6888
6895
  nonScaling: false
6889
6896
  });
6890
6897
  }
6898
+ function resolveShadowStrength(element) {
6899
+ const raw = element.textShadowStrength;
6900
+ const s = typeof raw === "number" && Number.isFinite(raw) ? Math.max(0, Math.min(100, raw)) : 25;
6901
+ if (s <= 0) return { perPassAlpha: 0, passes: 0 };
6902
+ if (s <= 25) return { perPassAlpha: s / 25, passes: 1 };
6903
+ const passes = Math.min(4, Math.ceil(s / 25));
6904
+ return { perPassAlpha: 1, passes };
6905
+ }
6906
+ function applyTextShadow(textbox, element) {
6907
+ const shadow = buildTextShadow(element);
6908
+ const { passes } = resolveShadowStrength(element);
6909
+ const obj = textbox;
6910
+ if (!obj.__pdShadowOrigRender) {
6911
+ obj.__pdShadowOrigRender = obj.render.bind(obj);
6912
+ obj.render = function(ctx) {
6913
+ const n = Math.max(1, Number(obj.__pdShadowPasses || 1));
6914
+ for (let i = 0; i < n; i++) obj.__pdShadowOrigRender(ctx);
6915
+ };
6916
+ }
6917
+ obj.__pdShadowPasses = shadow ? Math.max(1, passes) : 1;
6918
+ textbox.set("shadow", shadow ?? null);
6919
+ }
6920
+ function applyAlphaMultiplier(c, mult) {
6921
+ const m = Math.max(0, Math.min(1, mult));
6922
+ const s = String(c).trim();
6923
+ const rgba = s.match(/^rgba?\s*\(([^)]+)\)$/i);
6924
+ if (rgba) {
6925
+ const parts = rgba[1].split(",").map((p) => p.trim());
6926
+ const [r, g, b] = parts;
6927
+ const baseA = parts.length >= 4 ? Math.max(0, Math.min(1, parseFloat(parts[3]) || 0)) : 1;
6928
+ return `rgba(${r}, ${g}, ${b}, ${Math.round(baseA * m * 1e3) / 1e3})`;
6929
+ }
6930
+ const hex8 = s.match(/^#([0-9a-f]{8})$/i);
6931
+ if (hex8) {
6932
+ const h = hex8[1];
6933
+ const r = parseInt(h.slice(0, 2), 16);
6934
+ const g = parseInt(h.slice(2, 4), 16);
6935
+ const b = parseInt(h.slice(4, 6), 16);
6936
+ const baseA = parseInt(h.slice(6, 8), 16) / 255;
6937
+ return `rgba(${r}, ${g}, ${b}, ${Math.round(baseA * m * 1e3) / 1e3})`;
6938
+ }
6939
+ const hex6 = s.match(/^#([0-9a-f]{6})$/i);
6940
+ if (hex6) {
6941
+ const h = hex6[1];
6942
+ const r = parseInt(h.slice(0, 2), 16);
6943
+ const g = parseInt(h.slice(2, 4), 16);
6944
+ const b = parseInt(h.slice(4, 6), 16);
6945
+ return `rgba(${r}, ${g}, ${b}, ${Math.round(m * 1e3) / 1e3})`;
6946
+ }
6947
+ const hex3 = s.match(/^#([0-9a-f]{3})$/i);
6948
+ if (hex3) {
6949
+ const h = hex3[1];
6950
+ const r = parseInt(h[0] + h[0], 16);
6951
+ const g = parseInt(h[1] + h[1], 16);
6952
+ const b = parseInt(h[2] + h[2], 16);
6953
+ return `rgba(${r}, ${g}, ${b}, ${Math.round(m * 1e3) / 1e3})`;
6954
+ }
6955
+ return `rgba(0, 0, 0, ${Math.round(m * 1e3) / 1e3})`;
6956
+ }
6891
6957
  function buildRoundedRectPath2D(ctx, x, y, w, h, rTL, rTR, rBR, rBL) {
6892
6958
  const maxR = Math.min(w, h) / 2;
6893
6959
  const tl = Math.min(Math.max(0, rTL), maxR);
@@ -8452,8 +8518,7 @@ function createText(element) {
8452
8518
  }));
8453
8519
  }
8454
8520
  applyTextBackground(textbox, extractTextBgConfig(element));
8455
- const shadow = buildTextShadow(element);
8456
- if (shadow) textbox.set("shadow", shadow);
8521
+ applyTextShadow(textbox, element);
8457
8522
  return textbox;
8458
8523
  }
8459
8524
  function createLine(element) {
@@ -12260,8 +12325,7 @@ const PageCanvas = forwardRef(
12260
12325
  obj.dirty = true;
12261
12326
  try {
12262
12327
  applyTextBackground(obj, extractTextBgConfig(element));
12263
- const shadow = buildTextShadow(element);
12264
- obj.set("shadow", shadow ?? null);
12328
+ applyTextShadow(obj, element);
12265
12329
  try {
12266
12330
  obj._cacheCanvas = null;
12267
12331
  obj._cacheContext = null;
@@ -12289,7 +12353,8 @@ const PageCanvas = forwardRef(
12289
12353
  sy: element.textShadowOffsetY ?? 0,
12290
12354
  st: element.textShadowAffectsText !== false,
12291
12355
  sa: element.textShadowAffectsBg !== false,
12292
- sty: element.textShadowType ?? null
12356
+ sty: element.textShadowType ?? null,
12357
+ ss: element.textShadowStrength ?? null
12293
12358
  });
12294
12359
  obj.dirty = true;
12295
12360
  } catch (err) {
@@ -18947,9 +19012,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
18947
19012
  }
18948
19013
  return svgString;
18949
19014
  }
18950
- const resolvedPackageVersion = "0.5.210";
19015
+ const resolvedPackageVersion = "0.5.212";
18951
19016
  const PACKAGE_VERSION = resolvedPackageVersion;
18952
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.210";
19017
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.212";
18953
19018
  const roundParityValue = (value) => {
18954
19019
  if (typeof value !== "number") return value;
18955
19020
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -19009,7 +19074,7 @@ function detectSafariOrIos() {
19009
19074
  return false;
19010
19075
  }
19011
19076
  }
19012
- async function downscaleConfigRasterImages(config, maxEdgePx, maxDataUrlBytes = 2e6) {
19077
+ async function downscaleConfigRasterImages(config, maxEdgePx, maxDataUrlBytes = 2e6, jpegQuality = 0.85) {
19013
19078
  if (!maxEdgePx || maxEdgePx <= 0) return 0;
19014
19079
  if (typeof document === "undefined") return 0;
19015
19080
  const targets = [];
@@ -19073,7 +19138,7 @@ async function downscaleConfigRasterImages(config, maxEdgePx, maxDataUrlBytes =
19073
19138
  return null;
19074
19139
  }
19075
19140
  let scale = tooLargeByEdge ? maxEdgePx / longest : 1;
19076
- let quality = 0.85;
19141
+ let quality = Math.max(0.4, Math.min(1, jpegQuality));
19077
19142
  let best = null;
19078
19143
  for (let attempt = 0; attempt < 4; attempt++) {
19079
19144
  const tw = Math.max(1, Math.round(w * scale));
@@ -19097,7 +19162,7 @@ async function downscaleConfigRasterImages(config, maxEdgePx, maxDataUrlBytes =
19097
19162
  }
19098
19163
  const byteScale = Math.sqrt(maxDataUrlBytes / Math.max(1, outBytes)) * 0.92;
19099
19164
  scale = Math.max(0.1, scale * Math.min(0.95, byteScale));
19100
- quality = Math.max(0.68, quality - 0.06);
19165
+ quality = Math.max(0.4, quality - 0.06);
19101
19166
  }
19102
19167
  if (blobUrl) URL.revokeObjectURL(blobUrl);
19103
19168
  return best;
@@ -19472,7 +19537,9 @@ class PixldocsRenderer {
19472
19537
  title: options == null ? void 0 : options.title,
19473
19538
  textMode: options == null ? void 0 : options.textMode,
19474
19539
  forcePerElementPdf: options == null ? void 0 : options.forcePerElementPdf,
19475
- maxImageEdgePx: options == null ? void 0 : options.maxImageEdgePx
19540
+ maxImageEdgePx: options == null ? void 0 : options.maxImageEdgePx,
19541
+ compressImages: options == null ? void 0 : options.compressImages,
19542
+ compressionQuality: options == null ? void 0 : options.compressionQuality
19476
19543
  });
19477
19544
  }
19478
19545
  /**
@@ -19480,7 +19547,7 @@ class PixldocsRenderer {
19480
19547
  * This is the primary PDF export API — mirrors renderFromForm() but returns a PDF.
19481
19548
  */
19482
19549
  async renderPdfFromForm(options) {
19483
- const { templateId, formSchemaId, sectionState, themeId, watermark, watermarkOptions, prefetched, title, fontBaseUrl, textMode, forcePerElementPdf, maxImageEdgePx } = options;
19550
+ const { templateId, formSchemaId, sectionState, themeId, watermark, watermarkOptions, prefetched, title, fontBaseUrl, textMode, forcePerElementPdf, maxImageEdgePx, compressImages, compressionQuality } = options;
19484
19551
  const resolved = await resolveFromForm({
19485
19552
  templateId,
19486
19553
  formSchemaId,
@@ -19503,7 +19570,9 @@ class PixldocsRenderer {
19503
19570
  watermark: shouldWatermark,
19504
19571
  textMode,
19505
19572
  forcePerElementPdf,
19506
- maxImageEdgePx
19573
+ maxImageEdgePx,
19574
+ compressImages,
19575
+ compressionQuality
19507
19576
  });
19508
19577
  }
19509
19578
  async renderById(templateId, formData, options) {
@@ -19550,13 +19619,20 @@ class PixldocsRenderer {
19550
19619
  const hasUserDataImage = configHasUserDataImage(cloned);
19551
19620
  const isSafariLike = detectSafariOrIos();
19552
19621
  const shouldForcePerElement = forceMode === true ? true : forceMode === false ? false : false;
19622
+ const compressImagesOpt = options.compressImages ?? true;
19553
19623
  const maxEdgeOpt = options.maxImageEdgePx ?? this.config.maxImageEdgePx;
19554
- const effectiveMaxEdge = typeof maxEdgeOpt === "number" ? Math.max(0, maxEdgeOpt | 0) : hasUserDataImage ? 2048 : 0;
19624
+ const effectiveMaxEdge = typeof maxEdgeOpt === "number" ? Math.max(0, maxEdgeOpt | 0) : compressImagesOpt ? hasUserDataImage ? 2048 : 0 : 0;
19625
+ const downscaleQuality = compressImagesOpt ? typeof options.compressionQuality === "number" ? Math.max(0.4, Math.min(1, options.compressionQuality)) : 0.85 : 0.95;
19555
19626
  if (effectiveMaxEdge > 0) {
19556
19627
  try {
19557
- const downscaled = await downscaleConfigRasterImages(cloned, effectiveMaxEdge);
19628
+ const downscaled = await downscaleConfigRasterImages(
19629
+ cloned,
19630
+ effectiveMaxEdge,
19631
+ void 0,
19632
+ downscaleQuality
19633
+ );
19558
19634
  if (downscaled > 0) {
19559
- console.log(`[canvas-renderer][pdf-unified] downscaled ${downscaled} raster image(s) to <=${effectiveMaxEdge}px edge`);
19635
+ console.log(`[canvas-renderer][pdf-unified] downscaled ${downscaled} raster image(s) to <=${effectiveMaxEdge}px edge @ q=${downscaleQuality}`);
19560
19636
  }
19561
19637
  } catch (e) {
19562
19638
  console.warn("[canvas-renderer][pdf-unified] image downscale pass failed (continuing with originals):", e);
@@ -19567,7 +19643,9 @@ class PixldocsRenderer {
19567
19643
  hasUserDataImage,
19568
19644
  isSafariLike,
19569
19645
  shouldForcePerElement,
19570
- effectiveMaxEdge
19646
+ effectiveMaxEdge,
19647
+ compressImages: compressImagesOpt,
19648
+ downscaleQuality
19571
19649
  });
19572
19650
  const stampPrefix = `__pixldocs_pdf_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
19573
19651
  const pageIds = cloned.pages.map((p, i) => {
@@ -19628,7 +19706,7 @@ class PixldocsRenderer {
19628
19706
  await this.waitForCanvasScene(container, cloned, i);
19629
19707
  }
19630
19708
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
19631
- const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-Ch6GbQqO.js");
19709
+ const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-CN-N0DE8.js");
19632
19710
  const prepared = preparePagesForExport(
19633
19711
  cloned.pages,
19634
19712
  canvasWidth,
@@ -19645,7 +19723,9 @@ class PixldocsRenderer {
19645
19723
  // path and use the live Fabric object PDF path instead, preserving live
19646
19724
  // matrices/order so we do not regress into stale config-space layout.
19647
19725
  skipLiveCanvasSvgFastPath: forceMode === true,
19648
- useLiveCanvasObjectPdfPath: shouldForcePerElement && forceMode !== true
19726
+ useLiveCanvasObjectPdfPath: shouldForcePerElement && forceMode !== true,
19727
+ compressImages: options.compressImages ?? true,
19728
+ compressionQuality: options.compressionQuality
19649
19729
  });
19650
19730
  if (!result || typeof result === "undefined") {
19651
19731
  throw new Error("exportMultiPagePdf returned no blob (returnBlob path failed)");
@@ -21812,7 +21892,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
21812
21892
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
21813
21893
  sanitizeSvgTreeForPdf(svgToDraw);
21814
21894
  try {
21815
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-Ch6GbQqO.js");
21895
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-CN-N0DE8.js");
21816
21896
  try {
21817
21897
  await logTextMeasurementDiagnostic(svgToDraw);
21818
21898
  } catch {
@@ -22212,4 +22292,4 @@ export {
22212
22292
  buildTeaserBlurFlatKeys as y,
22213
22293
  collectFontDescriptorsFromConfig as z
22214
22294
  };
22215
- //# sourceMappingURL=index-DwYUddYW.js.map
22295
+ //# sourceMappingURL=index-B1QDVK5k.js.map