@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.
@@ -4071,6 +4071,11 @@ async function loadImageAsync(element, placeholder, fc, fabricRef, syncLockedRef
4071
4071
  setObjectData(cropGroup, element.id);
4072
4072
  cropGroup.__imageSrc = imageUrl;
4073
4073
  cropGroup.__svgColorMap = nextSvgColorMap;
4074
+ cropGroup.set({
4075
+ flipX: element.flipX ?? false,
4076
+ flipY: element.flipY ?? false
4077
+ });
4078
+ cropGroup.setCoords();
4074
4079
  finalObject = cropGroup;
4075
4080
  } else {
4076
4081
  setObjectData(img, element.id);
@@ -6897,8 +6902,10 @@ function buildTextShadow(element) {
6897
6902
  if (!color || color === "transparent") return null;
6898
6903
  const type = element.textShadowType;
6899
6904
  if (type && type !== "drop") return null;
6905
+ const { perPassAlpha } = resolveShadowStrength(element);
6906
+ const finalColor = applyAlphaMultiplier(String(color), perPassAlpha);
6900
6907
  return new fabric__namespace.Shadow({
6901
- color: String(color),
6908
+ color: finalColor,
6902
6909
  blur: blur || 0,
6903
6910
  offsetX: ox || 0,
6904
6911
  offsetY: oy || 0,
@@ -6906,6 +6913,65 @@ function buildTextShadow(element) {
6906
6913
  nonScaling: false
6907
6914
  });
6908
6915
  }
6916
+ function resolveShadowStrength(element) {
6917
+ const raw = element.textShadowStrength;
6918
+ const s = typeof raw === "number" && Number.isFinite(raw) ? Math.max(0, Math.min(100, raw)) : 25;
6919
+ if (s <= 0) return { perPassAlpha: 0, passes: 0 };
6920
+ if (s <= 25) return { perPassAlpha: s / 25, passes: 1 };
6921
+ const passes = Math.min(4, Math.ceil(s / 25));
6922
+ return { perPassAlpha: 1, passes };
6923
+ }
6924
+ function applyTextShadow(textbox, element) {
6925
+ const shadow = buildTextShadow(element);
6926
+ const { passes } = resolveShadowStrength(element);
6927
+ const obj = textbox;
6928
+ if (!obj.__pdShadowOrigRender) {
6929
+ obj.__pdShadowOrigRender = obj.render.bind(obj);
6930
+ obj.render = function(ctx) {
6931
+ const n = Math.max(1, Number(obj.__pdShadowPasses || 1));
6932
+ for (let i = 0; i < n; i++) obj.__pdShadowOrigRender(ctx);
6933
+ };
6934
+ }
6935
+ obj.__pdShadowPasses = shadow ? Math.max(1, passes) : 1;
6936
+ textbox.set("shadow", shadow ?? null);
6937
+ }
6938
+ function applyAlphaMultiplier(c, mult) {
6939
+ const m = Math.max(0, Math.min(1, mult));
6940
+ const s = String(c).trim();
6941
+ const rgba = s.match(/^rgba?\s*\(([^)]+)\)$/i);
6942
+ if (rgba) {
6943
+ const parts = rgba[1].split(",").map((p) => p.trim());
6944
+ const [r, g, b] = parts;
6945
+ const baseA = parts.length >= 4 ? Math.max(0, Math.min(1, parseFloat(parts[3]) || 0)) : 1;
6946
+ return `rgba(${r}, ${g}, ${b}, ${Math.round(baseA * m * 1e3) / 1e3})`;
6947
+ }
6948
+ const hex8 = s.match(/^#([0-9a-f]{8})$/i);
6949
+ if (hex8) {
6950
+ const h = hex8[1];
6951
+ const r = parseInt(h.slice(0, 2), 16);
6952
+ const g = parseInt(h.slice(2, 4), 16);
6953
+ const b = parseInt(h.slice(4, 6), 16);
6954
+ const baseA = parseInt(h.slice(6, 8), 16) / 255;
6955
+ return `rgba(${r}, ${g}, ${b}, ${Math.round(baseA * m * 1e3) / 1e3})`;
6956
+ }
6957
+ const hex6 = s.match(/^#([0-9a-f]{6})$/i);
6958
+ if (hex6) {
6959
+ const h = hex6[1];
6960
+ const r = parseInt(h.slice(0, 2), 16);
6961
+ const g = parseInt(h.slice(2, 4), 16);
6962
+ const b = parseInt(h.slice(4, 6), 16);
6963
+ return `rgba(${r}, ${g}, ${b}, ${Math.round(m * 1e3) / 1e3})`;
6964
+ }
6965
+ const hex3 = s.match(/^#([0-9a-f]{3})$/i);
6966
+ if (hex3) {
6967
+ const h = hex3[1];
6968
+ const r = parseInt(h[0] + h[0], 16);
6969
+ const g = parseInt(h[1] + h[1], 16);
6970
+ const b = parseInt(h[2] + h[2], 16);
6971
+ return `rgba(${r}, ${g}, ${b}, ${Math.round(m * 1e3) / 1e3})`;
6972
+ }
6973
+ return `rgba(0, 0, 0, ${Math.round(m * 1e3) / 1e3})`;
6974
+ }
6909
6975
  function buildRoundedRectPath2D(ctx, x, y, w, h, rTL, rTR, rBR, rBL) {
6910
6976
  const maxR = Math.min(w, h) / 2;
6911
6977
  const tl = Math.min(Math.max(0, rTL), maxR);
@@ -8470,8 +8536,7 @@ function createText(element) {
8470
8536
  }));
8471
8537
  }
8472
8538
  applyTextBackground(textbox, extractTextBgConfig(element));
8473
- const shadow = buildTextShadow(element);
8474
- if (shadow) textbox.set("shadow", shadow);
8539
+ applyTextShadow(textbox, element);
8475
8540
  return textbox;
8476
8541
  }
8477
8542
  function createLine(element) {
@@ -12278,8 +12343,7 @@ const PageCanvas = react.forwardRef(
12278
12343
  obj.dirty = true;
12279
12344
  try {
12280
12345
  applyTextBackground(obj, extractTextBgConfig(element));
12281
- const shadow = buildTextShadow(element);
12282
- obj.set("shadow", shadow ?? null);
12346
+ applyTextShadow(obj, element);
12283
12347
  try {
12284
12348
  obj._cacheCanvas = null;
12285
12349
  obj._cacheContext = null;
@@ -12307,7 +12371,8 @@ const PageCanvas = react.forwardRef(
12307
12371
  sy: element.textShadowOffsetY ?? 0,
12308
12372
  st: element.textShadowAffectsText !== false,
12309
12373
  sa: element.textShadowAffectsBg !== false,
12310
- sty: element.textShadowType ?? null
12374
+ sty: element.textShadowType ?? null,
12375
+ ss: element.textShadowStrength ?? null
12311
12376
  });
12312
12377
  obj.dirty = true;
12313
12378
  } catch (err) {
@@ -18965,9 +19030,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
18965
19030
  }
18966
19031
  return svgString;
18967
19032
  }
18968
- const resolvedPackageVersion = "0.5.210";
19033
+ const resolvedPackageVersion = "0.5.212";
18969
19034
  const PACKAGE_VERSION = resolvedPackageVersion;
18970
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.210";
19035
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.212";
18971
19036
  const roundParityValue = (value) => {
18972
19037
  if (typeof value !== "number") return value;
18973
19038
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -19027,7 +19092,7 @@ function detectSafariOrIos() {
19027
19092
  return false;
19028
19093
  }
19029
19094
  }
19030
- async function downscaleConfigRasterImages(config, maxEdgePx, maxDataUrlBytes = 2e6) {
19095
+ async function downscaleConfigRasterImages(config, maxEdgePx, maxDataUrlBytes = 2e6, jpegQuality = 0.85) {
19031
19096
  if (!maxEdgePx || maxEdgePx <= 0) return 0;
19032
19097
  if (typeof document === "undefined") return 0;
19033
19098
  const targets = [];
@@ -19091,7 +19156,7 @@ async function downscaleConfigRasterImages(config, maxEdgePx, maxDataUrlBytes =
19091
19156
  return null;
19092
19157
  }
19093
19158
  let scale = tooLargeByEdge ? maxEdgePx / longest : 1;
19094
- let quality = 0.85;
19159
+ let quality = Math.max(0.4, Math.min(1, jpegQuality));
19095
19160
  let best = null;
19096
19161
  for (let attempt = 0; attempt < 4; attempt++) {
19097
19162
  const tw = Math.max(1, Math.round(w * scale));
@@ -19115,7 +19180,7 @@ async function downscaleConfigRasterImages(config, maxEdgePx, maxDataUrlBytes =
19115
19180
  }
19116
19181
  const byteScale = Math.sqrt(maxDataUrlBytes / Math.max(1, outBytes)) * 0.92;
19117
19182
  scale = Math.max(0.1, scale * Math.min(0.95, byteScale));
19118
- quality = Math.max(0.68, quality - 0.06);
19183
+ quality = Math.max(0.4, quality - 0.06);
19119
19184
  }
19120
19185
  if (blobUrl) URL.revokeObjectURL(blobUrl);
19121
19186
  return best;
@@ -19490,7 +19555,9 @@ class PixldocsRenderer {
19490
19555
  title: options == null ? void 0 : options.title,
19491
19556
  textMode: options == null ? void 0 : options.textMode,
19492
19557
  forcePerElementPdf: options == null ? void 0 : options.forcePerElementPdf,
19493
- maxImageEdgePx: options == null ? void 0 : options.maxImageEdgePx
19558
+ maxImageEdgePx: options == null ? void 0 : options.maxImageEdgePx,
19559
+ compressImages: options == null ? void 0 : options.compressImages,
19560
+ compressionQuality: options == null ? void 0 : options.compressionQuality
19494
19561
  });
19495
19562
  }
19496
19563
  /**
@@ -19498,7 +19565,7 @@ class PixldocsRenderer {
19498
19565
  * This is the primary PDF export API — mirrors renderFromForm() but returns a PDF.
19499
19566
  */
19500
19567
  async renderPdfFromForm(options) {
19501
- const { templateId, formSchemaId, sectionState, themeId, watermark, watermarkOptions, prefetched, title, fontBaseUrl, textMode, forcePerElementPdf, maxImageEdgePx } = options;
19568
+ const { templateId, formSchemaId, sectionState, themeId, watermark, watermarkOptions, prefetched, title, fontBaseUrl, textMode, forcePerElementPdf, maxImageEdgePx, compressImages, compressionQuality } = options;
19502
19569
  const resolved = await resolveFromForm({
19503
19570
  templateId,
19504
19571
  formSchemaId,
@@ -19521,7 +19588,9 @@ class PixldocsRenderer {
19521
19588
  watermark: shouldWatermark,
19522
19589
  textMode,
19523
19590
  forcePerElementPdf,
19524
- maxImageEdgePx
19591
+ maxImageEdgePx,
19592
+ compressImages,
19593
+ compressionQuality
19525
19594
  });
19526
19595
  }
19527
19596
  async renderById(templateId, formData, options) {
@@ -19568,13 +19637,20 @@ class PixldocsRenderer {
19568
19637
  const hasUserDataImage = configHasUserDataImage(cloned);
19569
19638
  const isSafariLike = detectSafariOrIos();
19570
19639
  const shouldForcePerElement = forceMode === true ? true : forceMode === false ? false : false;
19640
+ const compressImagesOpt = options.compressImages ?? true;
19571
19641
  const maxEdgeOpt = options.maxImageEdgePx ?? this.config.maxImageEdgePx;
19572
- const effectiveMaxEdge = typeof maxEdgeOpt === "number" ? Math.max(0, maxEdgeOpt | 0) : hasUserDataImage ? 2048 : 0;
19642
+ const effectiveMaxEdge = typeof maxEdgeOpt === "number" ? Math.max(0, maxEdgeOpt | 0) : compressImagesOpt ? hasUserDataImage ? 2048 : 0 : 0;
19643
+ const downscaleQuality = compressImagesOpt ? typeof options.compressionQuality === "number" ? Math.max(0.4, Math.min(1, options.compressionQuality)) : 0.85 : 0.95;
19573
19644
  if (effectiveMaxEdge > 0) {
19574
19645
  try {
19575
- const downscaled = await downscaleConfigRasterImages(cloned, effectiveMaxEdge);
19646
+ const downscaled = await downscaleConfigRasterImages(
19647
+ cloned,
19648
+ effectiveMaxEdge,
19649
+ void 0,
19650
+ downscaleQuality
19651
+ );
19576
19652
  if (downscaled > 0) {
19577
- console.log(`[canvas-renderer][pdf-unified] downscaled ${downscaled} raster image(s) to <=${effectiveMaxEdge}px edge`);
19653
+ console.log(`[canvas-renderer][pdf-unified] downscaled ${downscaled} raster image(s) to <=${effectiveMaxEdge}px edge @ q=${downscaleQuality}`);
19578
19654
  }
19579
19655
  } catch (e) {
19580
19656
  console.warn("[canvas-renderer][pdf-unified] image downscale pass failed (continuing with originals):", e);
@@ -19585,7 +19661,9 @@ class PixldocsRenderer {
19585
19661
  hasUserDataImage,
19586
19662
  isSafariLike,
19587
19663
  shouldForcePerElement,
19588
- effectiveMaxEdge
19664
+ effectiveMaxEdge,
19665
+ compressImages: compressImagesOpt,
19666
+ downscaleQuality
19589
19667
  });
19590
19668
  const stampPrefix = `__pixldocs_pdf_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
19591
19669
  const pageIds = cloned.pages.map((p, i) => {
@@ -19646,7 +19724,7 @@ class PixldocsRenderer {
19646
19724
  await this.waitForCanvasScene(container, cloned, i);
19647
19725
  }
19648
19726
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
19649
- const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-BSK-Z1p4.cjs"));
19727
+ const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-zGrrm-iT.cjs"));
19650
19728
  const prepared = preparePagesForExport(
19651
19729
  cloned.pages,
19652
19730
  canvasWidth,
@@ -19663,7 +19741,9 @@ class PixldocsRenderer {
19663
19741
  // path and use the live Fabric object PDF path instead, preserving live
19664
19742
  // matrices/order so we do not regress into stale config-space layout.
19665
19743
  skipLiveCanvasSvgFastPath: forceMode === true,
19666
- useLiveCanvasObjectPdfPath: shouldForcePerElement && forceMode !== true
19744
+ useLiveCanvasObjectPdfPath: shouldForcePerElement && forceMode !== true,
19745
+ compressImages: options.compressImages ?? true,
19746
+ compressionQuality: options.compressionQuality
19667
19747
  });
19668
19748
  if (!result || typeof result === "undefined") {
19669
19749
  throw new Error("exportMultiPagePdf returned no blob (returnBlob path failed)");
@@ -21830,7 +21910,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
21830
21910
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
21831
21911
  sanitizeSvgTreeForPdf(svgToDraw);
21832
21912
  try {
21833
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-BSK-Z1p4.cjs"));
21913
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-zGrrm-iT.cjs"));
21834
21914
  try {
21835
21915
  await logTextMeasurementDiagnostic(svgToDraw);
21836
21916
  } catch {
@@ -22227,4 +22307,4 @@ exports.setAutoShrinkDebug = setAutoShrinkDebug;
22227
22307
  exports.setBundledAssetPrefixes = setBundledAssetPrefixes;
22228
22308
  exports.warmResolvedTemplateForPreview = warmResolvedTemplateForPreview;
22229
22309
  exports.warmTemplateFromForm = warmTemplateFromForm;
22230
- //# sourceMappingURL=index-54CXM_hR.cjs.map
22310
+ //# sourceMappingURL=index-DB8j0PV0.cjs.map