@pixldocs/canvas-renderer 0.5.217 → 0.5.219

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.
@@ -6901,13 +6901,7 @@ function readShadowStrength(element) {
6901
6901
  return typeof raw === "number" && Number.isFinite(raw) ? Math.max(0, Math.min(100, raw)) : 100;
6902
6902
  }
6903
6903
  function resolveShadowSourceSpread(element) {
6904
- const strength = readShadowStrength(element);
6905
- const blur = Math.max(0, Number(element.textShadowBlur ?? 0) || 0);
6906
- const fontSize = Math.max(1, Number(element.fontSize ?? 16) || 16);
6907
- if (blur <= 2) return 0;
6908
- const blurRamp = Math.min(1, (blur - 2) / 4);
6909
- const t = Math.max(0, (strength - 35) / 65);
6910
- return Math.min(8, blur * 0.16, fontSize * 0.08) * t * t * blurRamp;
6904
+ return 0;
6911
6905
  }
6912
6906
  function resolveShadowAlpha(element) {
6913
6907
  const s = readShadowStrength(element);
@@ -6930,7 +6924,7 @@ function applyTextShadow(textbox, element) {
6930
6924
  }
6931
6925
  obj.__pdShadowAlpha = canonicalShadow ? resolveShadowAlpha(element) : 1;
6932
6926
  obj.__pdShadowBaseColor = canonicalShadow ? String(element.textShadowColor || "") : void 0;
6933
- obj.__pdShadowSourceSpread = canonicalShadow ? resolveShadowSourceSpread(element) : 0;
6927
+ obj.__pdShadowSourceSpread = canonicalShadow ? resolveShadowSourceSpread() : 0;
6934
6928
  textbox.set("shadow", canonicalShadow ?? null);
6935
6929
  }
6936
6930
  function applyAlphaMultiplier(c, mult) {
@@ -7214,8 +7208,6 @@ function applyTextBackground(obj, cfg) {
7214
7208
  }
7215
7209
  }
7216
7210
  const suppressShadowOnText = bg && bg.shadowAffectsText === false;
7217
- const dropShadowSpread = Math.max(0, Number(this.__pdShadowSourceSpread) || 0);
7218
- const dropShadowActive = dropShadowSpread > 0 && !!this.shadow && !suppressShadowOnText && !blockShadowActive && !lineShadowActive;
7219
7211
  if (suppressShadowOnText) {
7220
7212
  ctx.save();
7221
7213
  ctx.shadowColor = "transparent";
@@ -7224,44 +7216,6 @@ function applyTextBackground(obj, cfg) {
7224
7216
  ctx.shadowOffsetY = 0;
7225
7217
  originalRender(ctx);
7226
7218
  ctx.restore();
7227
- } else if (dropShadowActive) {
7228
- const self = this;
7229
- const shadow = self.shadow;
7230
- const shadowColor = String((shadow == null ? void 0 : shadow.color) || this.__pdShadowBaseColor || "rgba(0,0,0,1)");
7231
- const shadowBlur = Math.max(0, Number((shadow == null ? void 0 : shadow.blur) ?? 0) || 0);
7232
- const shadowOffsetX = Number((shadow == null ? void 0 : shadow.offsetX) ?? 0) || 0;
7233
- const shadowOffsetY = Number((shadow == null ? void 0 : shadow.offsetY) ?? 0) || 0;
7234
- const origFill = self.fill;
7235
- const origStroke = self.stroke;
7236
- const origStrokeWidth = self.strokeWidth;
7237
- const origStyles = self.styles;
7238
- const origShadow = self.shadow;
7239
- try {
7240
- ctx.save();
7241
- self.shadow = null;
7242
- self.fill = shadowColor;
7243
- self.styles = {};
7244
- self.stroke = shadowColor;
7245
- self.strokeWidth = dropShadowSpread;
7246
- ctx.translate(shadowOffsetX, shadowOffsetY);
7247
- if (shadowBlur > 0) ctx.filter = `blur(${shadowBlur / 2}px)`;
7248
- ctx.lineJoin = "round";
7249
- ctx.lineCap = "round";
7250
- originalRender(ctx);
7251
- ctx.restore();
7252
- self.fill = origFill;
7253
- self.stroke = origStroke;
7254
- self.strokeWidth = origStrokeWidth;
7255
- self.styles = origStyles;
7256
- self.shadow = null;
7257
- originalRender(ctx);
7258
- } finally {
7259
- self.fill = origFill;
7260
- self.stroke = origStroke;
7261
- self.strokeWidth = origStrokeWidth;
7262
- self.styles = origStyles;
7263
- self.shadow = origShadow;
7264
- }
7265
7219
  } else {
7266
7220
  originalRender(ctx);
7267
7221
  }
@@ -7343,7 +7297,7 @@ function applyTextBackground(obj, cfg) {
7343
7297
  const oy = Number(shadow.offsetY ?? 0) || 0;
7344
7298
  const blur = Math.max(0, Number(shadow.blur ?? 0));
7345
7299
  const shadowColor = String(shadow.color);
7346
- const sourceSpread = Math.max(0, Number(this.__pdShadowSourceSpread) || 0);
7300
+ const sourceSpread = 0;
7347
7301
  const pad = Math.max(16, Math.ceil((blur + sourceSpread) * 4) + Math.ceil(Math.max(Math.abs(ox), Math.abs(oy))) + 8);
7348
7302
  const shadowBounds = unionBounds([
7349
7303
  ...rects,
@@ -17394,7 +17348,14 @@ function buildRepeatablePagesInputForApply(schema, sectionState) {
17394
17348
  const minEntries = page.minEntries != null ? Math.max(0, page.minEntries) : 1;
17395
17349
  let entryCount;
17396
17350
  if (Array.isArray(entries)) {
17397
- entryCount = minEntries === 0 ? entries.length : Math.max(1, entries.length);
17351
+ const realEntries = entries.filter((entry) => {
17352
+ if (!isRecord(entry)) return false;
17353
+ return Object.keys(entry).some(
17354
+ (k) => k !== ENTRY_ID_KEY && k !== ENTRY_NAME_KEY
17355
+ );
17356
+ });
17357
+ const count = minEntries === 0 ? realEntries.length : Math.max(1, realEntries.length);
17358
+ entryCount = count;
17398
17359
  } else {
17399
17360
  entryCount = minEntries === 0 ? 0 : minEntries;
17400
17361
  }
@@ -17405,6 +17366,25 @@ function buildRepeatablePagesInputForApply(schema, sectionState) {
17405
17366
  };
17406
17367
  });
17407
17368
  }
17369
+ function sanitizeSectionStateAgainstSchema(state, schema) {
17370
+ if (!state) return {};
17371
+ if (!schema) return { ...state };
17372
+ const known = /* @__PURE__ */ new Set();
17373
+ for (const s of schema.sections ?? []) known.add(s.id);
17374
+ for (const p of schema.repeatablePages ?? []) known.add(p.id);
17375
+ const out = {};
17376
+ for (const [key, value] of Object.entries(state)) {
17377
+ if (known.has(key)) {
17378
+ out[key] = value;
17379
+ continue;
17380
+ }
17381
+ const compositeMatch = /^(.+?)_(\d+)_(.+)$/.exec(key);
17382
+ if (compositeMatch && known.has(compositeMatch[1]) && known.has(compositeMatch[3])) {
17383
+ out[key] = value;
17384
+ }
17385
+ }
17386
+ return out;
17387
+ }
17408
17388
  async function fetchRow(supabaseUrl, anonKey, table, id) {
17409
17389
  const url = `${supabaseUrl}/rest/v1/${table}?id=eq.${id}&select=*`;
17410
17390
  const res = await fetch(url, {
@@ -17637,7 +17617,8 @@ async function resolveFromForm(options) {
17637
17617
  let mergedSectionState = { ...sectionState };
17638
17618
  const templateDefaultData = templateRow.default_data;
17639
17619
  if (templateDefaultData && isDefaultDataV2(templateDefaultData)) {
17640
- const defaults = templateDefaultData.sectionState;
17620
+ const rawDefaults = templateDefaultData.sectionState;
17621
+ const defaults = sanitizeSectionStateAgainstSchema(rawDefaults, formSchema);
17641
17622
  for (const key of Object.keys(defaults)) {
17642
17623
  if (!(key in mergedSectionState)) {
17643
17624
  mergedSectionState[key] = defaults[key];
@@ -17646,6 +17627,7 @@ async function resolveFromForm(options) {
17646
17627
  }
17647
17628
  mergedSectionState = mergeRepeatableEntryMeta(mergedSectionState, templateDefaultMetaSectionState, inferredSections);
17648
17629
  mergedSectionState = mergeRepeatableEntryMeta(mergedSectionState, defaultFormMetaSectionState, inferredSections);
17630
+ mergedSectionState = sanitizeSectionStateAgainstSchema(mergedSectionState, formSchema);
17649
17631
  const flatFormData = flattenSectionStateToFormData(mergedSectionState, inferredSections);
17650
17632
  const dynamicFields = templateConfig.dynamicFields || [];
17651
17633
  const mappings = [];
@@ -19085,9 +19067,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
19085
19067
  }
19086
19068
  return svgString;
19087
19069
  }
19088
- const resolvedPackageVersion = "0.5.217";
19070
+ const resolvedPackageVersion = "0.5.219";
19089
19071
  const PACKAGE_VERSION = resolvedPackageVersion;
19090
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.217";
19072
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.219";
19091
19073
  const roundParityValue = (value) => {
19092
19074
  if (typeof value !== "number") return value;
19093
19075
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -19648,6 +19630,58 @@ class PixldocsRenderer {
19648
19630
  compressionQuality
19649
19631
  });
19650
19632
  }
19633
+ /**
19634
+ * Render the template AND deliver the resulting PDF to the user — currently
19635
+ * via email (SendGrid). The PDF is rendered server-side on EC2, uploaded to
19636
+ * S3 (returned as `pdfUrl` for download fallback), and emailed with the PDF
19637
+ * attached.
19638
+ *
19639
+ * Every delivery is logged in the `delivery_log` table for audit + admin
19640
+ * resend. The same `pdfUrl` is returned to the caller so the host app can
19641
+ * also offer a "Download" button on the success screen.
19642
+ *
19643
+ * ```ts
19644
+ * const result = await renderer.renderAndDeliver({
19645
+ * templateId, formSchemaId, sectionState,
19646
+ * project: 'biomaker',
19647
+ * recipient: { email: 'user@example.com', name: 'Anya' },
19648
+ * });
19649
+ * // → { jobId, pdfUrl, deliveries: [{ channel: 'email', status: 'sent' }] }
19650
+ * ```
19651
+ *
19652
+ * Requires `RendererConfig.deliveryServerUrl` to be set.
19653
+ */
19654
+ async renderAndDeliver(options) {
19655
+ var _a;
19656
+ const base = (_a = this.config.deliveryServerUrl) == null ? void 0 : _a.replace(/\/+$/, "");
19657
+ if (!base) {
19658
+ throw new Error("renderAndDeliver: RendererConfig.deliveryServerUrl is required");
19659
+ }
19660
+ const { templateId, formSchemaId, sectionState, themeId, watermark, project, recipient, channels, scale } = options;
19661
+ if (!project) throw new Error('renderAndDeliver: project is required (e.g. "biomaker", "pixldocs")');
19662
+ if (!(recipient == null ? void 0 : recipient.email)) throw new Error("renderAndDeliver: recipient.email is required");
19663
+ const body = {
19664
+ template_id: templateId,
19665
+ form_schema_id: formSchemaId,
19666
+ data: { version: 2, sectionState },
19667
+ project,
19668
+ recipient,
19669
+ channels: channels || ["email"]
19670
+ };
19671
+ if (themeId) body.theme = themeId;
19672
+ if (typeof watermark === "boolean") body.watermark = watermark;
19673
+ if (typeof scale === "number") body.scale = scale;
19674
+ const res = await fetch(`${base}/v1/render-and-deliver`, {
19675
+ method: "POST",
19676
+ headers: { "Content-Type": "application/json" },
19677
+ body: JSON.stringify(body)
19678
+ });
19679
+ if (!res.ok) {
19680
+ const errText = await res.text().catch(() => "");
19681
+ throw new Error(`renderAndDeliver failed: ${res.status} ${errText.slice(0, 300)}`);
19682
+ }
19683
+ return await res.json();
19684
+ }
19651
19685
  async renderById(templateId, formData, options) {
19652
19686
  const resolved = await resolveTemplateData({
19653
19687
  templateId,
@@ -19779,7 +19813,7 @@ class PixldocsRenderer {
19779
19813
  await this.waitForCanvasScene(container, cloned, i);
19780
19814
  }
19781
19815
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
19782
- const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-DJR9lwsV.js");
19816
+ const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-Dejgf_PM.js");
19783
19817
  const prepared = preparePagesForExport(
19784
19818
  cloned.pages,
19785
19819
  canvasWidth,
@@ -21721,7 +21755,7 @@ async function rasterizeShadowMarkers(svg) {
21721
21755
  const by = parseFloat(marker.getAttribute("data-by") || "0");
21722
21756
  const bw = parseFloat(marker.getAttribute("data-bw") || "0");
21723
21757
  const bh = parseFloat(marker.getAttribute("data-bh") || "0");
21724
- const spread = parseFloat(marker.getAttribute("data-spread") || "0");
21758
+ const spread = 0;
21725
21759
  const alphaRaw = parseFloat(marker.getAttribute("data-alpha") || "1");
21726
21760
  const shadowAlpha = Number.isFinite(alphaRaw) ? Math.max(0, Math.min(1, alphaRaw)) : 1;
21727
21761
  if (!Number.isFinite(bw) || !Number.isFinite(bh) || bw <= 0 || bh <= 0) {
@@ -21969,7 +22003,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
21969
22003
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
21970
22004
  sanitizeSvgTreeForPdf(svgToDraw);
21971
22005
  try {
21972
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-DJR9lwsV.js");
22006
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-Dejgf_PM.js");
21973
22007
  try {
21974
22008
  await logTextMeasurementDiagnostic(svgToDraw);
21975
22009
  } catch {
@@ -22369,4 +22403,4 @@ export {
22369
22403
  buildTeaserBlurFlatKeys as y,
22370
22404
  collectFontDescriptorsFromConfig as z
22371
22405
  };
22372
- //# sourceMappingURL=index-Ci5YA_Ps.js.map
22406
+ //# sourceMappingURL=index-DbUPI6zs.js.map