@unlev/exeq 0.3.0 → 0.3.2

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/dist/index.js CHANGED
@@ -30,7 +30,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/lib/index.ts
31
31
  var lib_exports = {};
32
32
  __export(lib_exports, {
33
+ A4: () => A4,
33
34
  BUILTIN_TRANSFORMS: () => BUILTIN_TRANSFORMS,
35
+ DEFAULT_CALIBRATION: () => DEFAULT_CALIBRATION,
34
36
  DEFAULT_SIGNER_ROLES: () => DEFAULT_SIGNER_ROLES,
35
37
  DesignerView: () => DesignerView,
36
38
  FIELD_DEFAULTS: () => FIELD_DEFAULTS,
@@ -42,7 +44,11 @@ __export(lib_exports, {
42
44
  SignatureCanvas: () => SignatureCanvas,
43
45
  SignerRoleSelector: () => SignerRoleSelector,
44
46
  SignerView: () => SignerView,
47
+ US_LEGAL: () => US_LEGAL,
48
+ US_LETTER: () => US_LETTER,
49
+ applyCalibration: () => applyCalibration,
45
50
  createField: () => createField,
51
+ createPdfBuilder: () => createPdfBuilder,
46
52
  downloadPdf: () => downloadPdf,
47
53
  generateFilledPdf: () => generateFilledPdf,
48
54
  generateId: () => generateId,
@@ -222,6 +228,7 @@ function PdfViewer({
222
228
  onPageClick,
223
229
  onDropField,
224
230
  onGroupMove,
231
+ onMoveStart,
225
232
  onMoveEnd,
226
233
  mode,
227
234
  currentSigner,
@@ -331,6 +338,7 @@ function PdfViewer({
331
338
  onMove: onFieldMove,
332
339
  onResize: onFieldResize,
333
340
  onGroupMove: selectedFieldIds.size > 1 && selectedFieldIds.has(field.id) ? onGroupMove : void 0,
341
+ onMoveStart,
334
342
  onMoveEnd,
335
343
  otherFields: mode === "designer" ? pageFields.filter((f) => f.id !== field.id && !selectedFieldIds.has(f.id)) : void 0,
336
344
  setGuides: mode === "designer" ? setGuides : void 0,
@@ -420,6 +428,7 @@ function FieldOverlayItem({
420
428
  onMove,
421
429
  onResize,
422
430
  onGroupMove,
431
+ onMoveStart,
423
432
  onMoveEnd,
424
433
  otherFields,
425
434
  setGuides,
@@ -449,6 +458,7 @@ function FieldOverlayItem({
449
458
  onSelect(e);
450
459
  }
451
460
  if (field.locked) return;
461
+ onMoveStart?.();
452
462
  const pageEl = overlayRef.current?.closest(".pdf-page");
453
463
  if (!pageEl) return;
454
464
  didDragRef.current = false;
@@ -497,11 +507,12 @@ function FieldOverlayItem({
497
507
  };
498
508
  window.addEventListener("mousemove", handleMouseMove);
499
509
  window.addEventListener("mouseup", handleMouseUp);
500
- }, [field, mode, onMove, onSelect, isMultiSelected, onGroupMove, onMoveEnd, selectedIds, otherFields, setGuides, pageIndex]);
510
+ }, [field, mode, onMove, onSelect, isMultiSelected, onGroupMove, onMoveStart, onMoveEnd, selectedIds, otherFields, setGuides, pageIndex]);
501
511
  const handleResizeMouseDown = (0, import_react.useCallback)((e) => {
502
512
  if (mode !== "designer" || !onResize || field.locked) return;
503
513
  e.preventDefault();
504
514
  e.stopPropagation();
515
+ onMoveStart?.();
505
516
  const pageEl = overlayRef.current?.closest(".pdf-page");
506
517
  if (!pageEl) return;
507
518
  resizeStartRef.current = {
@@ -527,7 +538,7 @@ function FieldOverlayItem({
527
538
  };
528
539
  window.addEventListener("mousemove", handleMouseMove);
529
540
  window.addEventListener("mouseup", handleMouseUp);
530
- }, [field, mode, onResize, onMoveEnd]);
541
+ }, [field, mode, onResize, onMoveStart, onMoveEnd]);
531
542
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
532
543
  "div",
533
544
  {
@@ -666,6 +677,13 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete, prefillCon
666
677
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "number", min: "0", max: "9999", value: field.maxLength || 0, onChange: (e) => onUpdate(field.id, { maxLength: Number(e.target.value) }) })
667
678
  ] })
668
679
  ] }),
680
+ isTextField && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-field", children: [
681
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "panel-checkbox-label", children: [
682
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "checkbox", checked: field.autoShrink || false, onChange: (e) => onUpdate(field.id, { autoShrink: e.target.checked }) }),
683
+ "Auto-shrink to fit"
684
+ ] }),
685
+ field.autoShrink && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "panel-hint", children: "Font size reduces to fit text within the field width" })
686
+ ] }),
669
687
  (field.type === "text" || field.type === "dropdown") && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-field", children: [
670
688
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { children: field.type === "dropdown" ? "Options" : "Predefined Options" }),
671
689
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-options-list", children: [
@@ -1088,13 +1106,13 @@ function useHistory(initialState, maxHistory = 50) {
1088
1106
  present: initialState,
1089
1107
  future: []
1090
1108
  });
1091
- const skipRef = (0, import_react4.useRef)(false);
1109
+ const batchRef = (0, import_react4.useRef)(false);
1110
+ const batchStartRef = (0, import_react4.useRef)(null);
1092
1111
  const set = (0, import_react4.useCallback)((updater) => {
1093
1112
  setState((prev) => {
1094
1113
  const newPresent = typeof updater === "function" ? updater(prev.present) : updater;
1095
1114
  if (newPresent === prev.present) return prev;
1096
- if (skipRef.current) {
1097
- skipRef.current = false;
1115
+ if (batchRef.current) {
1098
1116
  return { ...prev, present: newPresent };
1099
1117
  }
1100
1118
  return {
@@ -1105,15 +1123,31 @@ function useHistory(initialState, maxHistory = 50) {
1105
1123
  });
1106
1124
  }, [maxHistory]);
1107
1125
  const setWithoutHistory = (0, import_react4.useCallback)((updater) => {
1108
- skipRef.current = true;
1109
- set(updater);
1110
- }, [set]);
1111
- const snapshot = (0, import_react4.useCallback)(() => {
1112
- setState((prev) => ({
1113
- past: [...prev.past.slice(-maxHistory), prev.present],
1114
- present: prev.present,
1115
- future: []
1116
- }));
1126
+ setState((prev) => {
1127
+ const newPresent = typeof updater === "function" ? updater(prev.present) : updater;
1128
+ if (newPresent === prev.present) return prev;
1129
+ return { ...prev, present: newPresent };
1130
+ });
1131
+ }, []);
1132
+ const beginBatch = (0, import_react4.useCallback)(() => {
1133
+ batchRef.current = true;
1134
+ setState((prev) => {
1135
+ batchStartRef.current = prev.present;
1136
+ return prev;
1137
+ });
1138
+ }, []);
1139
+ const commitBatch = (0, import_react4.useCallback)(() => {
1140
+ batchRef.current = false;
1141
+ setState((prev) => {
1142
+ const startState = batchStartRef.current;
1143
+ batchStartRef.current = null;
1144
+ if (!startState || startState === prev.present) return prev;
1145
+ return {
1146
+ past: [...prev.past.slice(-maxHistory), startState],
1147
+ present: prev.present,
1148
+ future: []
1149
+ };
1150
+ });
1117
1151
  }, [maxHistory]);
1118
1152
  const undo = (0, import_react4.useCallback)(() => {
1119
1153
  setState((prev) => {
@@ -1141,7 +1175,8 @@ function useHistory(initialState, maxHistory = 50) {
1141
1175
  state: state.present,
1142
1176
  set,
1143
1177
  setWithoutHistory,
1144
- snapshot,
1178
+ beginBatch,
1179
+ commitBatch,
1145
1180
  undo,
1146
1181
  redo,
1147
1182
  canUndo: state.past.length > 0,
@@ -1180,7 +1215,7 @@ function DesignerView({
1180
1215
  ] }) });
1181
1216
  }
1182
1217
  const [pages, setPages] = (0, import_react5.useState)([]);
1183
- const { state: fields, set: setFields, setWithoutHistory: setFieldsSilent, snapshot: snapshotFields, undo: undoFields, redo: redoFields, canUndo, canRedo } = useHistory(initialTemplate?.fields ?? []);
1218
+ const { state: fields, set: setFields, setWithoutHistory: setFieldsSilent, beginBatch, commitBatch, undo: undoFields, redo: redoFields, canUndo, canRedo } = useHistory(initialTemplate?.fields ?? []);
1184
1219
  const [selectedFieldIds, setSelectedFieldIds] = (0, import_react5.useState)(/* @__PURE__ */ new Set());
1185
1220
  const [signerRoles, setSignerRoles] = (0, import_react5.useState)(initialTemplate?.signerRoles ?? [...DEFAULT_SIGNER_ROLES]);
1186
1221
  const [activeRole, setActiveRole] = (0, import_react5.useState)("Sender");
@@ -1765,7 +1800,8 @@ function DesignerView({
1765
1800
  onFieldMove: handleFieldMove,
1766
1801
  onFieldResize: handleFieldResize,
1767
1802
  onGroupMove: handleGroupMove,
1768
- onMoveEnd: snapshotFields,
1803
+ onMoveStart: beginBatch,
1804
+ onMoveEnd: commitBatch,
1769
1805
  onPageClick: handlePageClick,
1770
1806
  onDropField: handleDropOnPage,
1771
1807
  onMarqueeSelect: (ids) => setSelectedFieldIds(new Set(ids)),
@@ -2017,33 +2053,132 @@ var import_react7 = require("react");
2017
2053
 
2018
2054
  // src/utils/pdfFiller.ts
2019
2055
  var import_pdf_lib = require("pdf-lib");
2056
+
2057
+ // src/utils/formulaResolver.ts
2058
+ var BUILTIN_TRANSFORMS = {
2059
+ // Date transforms (expects a parseable date string)
2060
+ month: (v) => {
2061
+ const d = new Date(v);
2062
+ return isNaN(d.getTime()) ? "" : String(d.getMonth() + 1);
2063
+ },
2064
+ month2: (v) => {
2065
+ const d = new Date(v);
2066
+ return isNaN(d.getTime()) ? "" : String(d.getMonth() + 1).padStart(2, "0");
2067
+ },
2068
+ monthname: (v) => {
2069
+ const d = new Date(v);
2070
+ return isNaN(d.getTime()) ? "" : d.toLocaleString("en", { month: "long" });
2071
+ },
2072
+ monthshort: (v) => {
2073
+ const d = new Date(v);
2074
+ return isNaN(d.getTime()) ? "" : d.toLocaleString("en", { month: "short" });
2075
+ },
2076
+ day: (v) => {
2077
+ const d = new Date(v);
2078
+ return isNaN(d.getTime()) ? "" : String(d.getDate());
2079
+ },
2080
+ day2: (v) => {
2081
+ const d = new Date(v);
2082
+ return isNaN(d.getTime()) ? "" : String(d.getDate()).padStart(2, "0");
2083
+ },
2084
+ year: (v) => {
2085
+ const d = new Date(v);
2086
+ return isNaN(d.getTime()) ? "" : String(d.getFullYear());
2087
+ },
2088
+ year2: (v) => {
2089
+ const d = new Date(v);
2090
+ return isNaN(d.getTime()) ? "" : String(d.getFullYear()).slice(-2);
2091
+ },
2092
+ // String transforms
2093
+ upper: (v) => v.toUpperCase(),
2094
+ lower: (v) => v.toLowerCase(),
2095
+ trim: (v) => v.trim(),
2096
+ first: (v) => v.split(/\s+/)[0] || "",
2097
+ last: (v) => {
2098
+ const parts = v.split(/\s+/);
2099
+ return parts[parts.length - 1] || "";
2100
+ },
2101
+ initials: (v) => v.split(/\s+/).map((w) => w[0] || "").join("").toUpperCase(),
2102
+ // Numeric / substring
2103
+ last4: (v) => v.slice(-4),
2104
+ last2: (v) => v.slice(-2),
2105
+ first4: (v) => v.slice(0, 4),
2106
+ first2: (v) => v.slice(0, 2),
2107
+ digits: (v) => v.replace(/\D/g, ""),
2108
+ number: (v) => {
2109
+ const n = parseFloat(v);
2110
+ return isNaN(n) ? "" : String(n);
2111
+ },
2112
+ currency: (v) => {
2113
+ const n = parseFloat(v.replace(/[^0-9.-]/g, ""));
2114
+ return isNaN(n) ? "" : `$${n.toFixed(2)}`;
2115
+ }
2116
+ };
2117
+ var FORMULA_RE = /\{\{(.+?)\}\}/g;
2118
+ var PIPE_RE = /^(.+?)\s*\|\s*(.+)$/;
2119
+ function resolveFormula(formula, fields, customTransforms) {
2120
+ const transforms = { ...BUILTIN_TRANSFORMS, ...customTransforms };
2121
+ return formula.replace(FORMULA_RE, (_match, expr) => {
2122
+ const pipeMatch = expr.match(PIPE_RE);
2123
+ const sourceLabel = (pipeMatch ? pipeMatch[1] : expr).trim();
2124
+ const transformName = pipeMatch ? pipeMatch[2].trim() : null;
2125
+ const sourceField = fields.find((f) => f.label.toLowerCase() === sourceLabel.toLowerCase()) || fields.find((f) => f.id === sourceLabel);
2126
+ if (!sourceField) return "";
2127
+ const rawValue = sourceField.value || "";
2128
+ if (!transformName) return rawValue;
2129
+ const fn = transforms[transformName];
2130
+ if (!fn) return rawValue;
2131
+ try {
2132
+ return fn(rawValue);
2133
+ } catch {
2134
+ return rawValue;
2135
+ }
2136
+ });
2137
+ }
2138
+ function resolveAllFormulas(fields, customTransforms) {
2139
+ return fields.map((f) => {
2140
+ if (!f.formula) return f;
2141
+ const computed = resolveFormula(f.formula, fields, customTransforms);
2142
+ return computed !== f.value ? { ...f, value: computed } : f;
2143
+ });
2144
+ }
2145
+
2146
+ // src/utils/pdfFiller.ts
2020
2147
  var FONT_MAP = {
2021
- "Helvetica": import_pdf_lib.StandardFonts.Helvetica,
2022
- "Courier": import_pdf_lib.StandardFonts.Courier,
2023
- "TimesRoman": import_pdf_lib.StandardFonts.TimesRoman
2148
+ Helvetica: import_pdf_lib.StandardFonts.Helvetica,
2149
+ Courier: import_pdf_lib.StandardFonts.Courier,
2150
+ TimesRoman: import_pdf_lib.StandardFonts.TimesRoman
2024
2151
  };
2025
- async function generateFilledPdf(pdfSource, fields) {
2026
- let pdfBytes;
2027
- if (typeof pdfSource === "string") {
2028
- const res = await fetch(pdfSource);
2029
- pdfBytes = await res.arrayBuffer();
2030
- } else {
2031
- pdfBytes = pdfSource;
2032
- }
2033
- const pdfDoc = await import_pdf_lib.PDFDocument.load(pdfBytes);
2034
- const fontCache = /* @__PURE__ */ new Map();
2035
- const usedFontKeys = new Set(fields.map((f) => f.fontFamily || "Helvetica"));
2036
- for (const key of usedFontKeys) {
2037
- const stdFont = FONT_MAP[key] || import_pdf_lib.StandardFonts.Helvetica;
2038
- fontCache.set(key, await pdfDoc.embedFont(stdFont));
2039
- }
2040
- const pages = pdfDoc.getPages();
2041
- function hexToRgb(hex) {
2042
- const r = parseInt(hex.slice(1, 3), 16) / 255;
2043
- const g = parseInt(hex.slice(3, 5), 16) / 255;
2044
- const b = parseInt(hex.slice(5, 7), 16) / 255;
2045
- return (0, import_pdf_lib.rgb)(r, g, b);
2152
+ var US_LETTER = [612, 792];
2153
+ var US_LEGAL = [612, 1008];
2154
+ var A4 = [595.28, 841.89];
2155
+ var DEFAULT_CALIBRATION = {
2156
+ xOffset: 0,
2157
+ yOffset: 0,
2158
+ xScale: 1,
2159
+ yScale: 1
2160
+ };
2161
+ function applyCalibration(fields, calibration, pageSize = US_LETTER) {
2162
+ if (calibration.xOffset === 0 && calibration.yOffset === 0 && calibration.xScale === 1 && calibration.yScale === 1) {
2163
+ return fields;
2046
2164
  }
2165
+ const xPctOff = calibration.xOffset / pageSize[0] * 100;
2166
+ const yPctOff = calibration.yOffset / pageSize[1] * 100;
2167
+ return fields.map((f) => ({
2168
+ ...f,
2169
+ x: f.x * calibration.xScale + xPctOff,
2170
+ y: f.y * calibration.yScale + yPctOff,
2171
+ width: f.width * calibration.xScale,
2172
+ height: f.height * calibration.yScale
2173
+ }));
2174
+ }
2175
+ function hexToRgb(hex) {
2176
+ const r = parseInt(hex.slice(1, 3), 16) / 255;
2177
+ const g = parseInt(hex.slice(3, 5), 16) / 255;
2178
+ const b = parseInt(hex.slice(5, 7), 16) / 255;
2179
+ return (0, import_pdf_lib.rgb)(r, g, b);
2180
+ }
2181
+ async function renderFieldsOnPages(pages, fields, getFont, getSignature) {
2047
2182
  for (const field of fields) {
2048
2183
  const page = pages[field.page];
2049
2184
  if (!page) continue;
@@ -2084,37 +2219,32 @@ async function generateFilledPdf(pdfSource, fields) {
2084
2219
  }
2085
2220
  } else if (field.type === "signature" || field.type === "initials") {
2086
2221
  if (field.value.startsWith("data:image/png")) {
2087
- const pngBytes = Uint8Array.from(
2088
- atob(field.value.split(",")[1]),
2089
- (c) => c.charCodeAt(0)
2090
- );
2091
- const pngImage = await pdfDoc.embedPng(pngBytes);
2092
- page.drawImage(pngImage, {
2093
- x,
2094
- y,
2095
- width: w,
2096
- height: h
2097
- });
2222
+ const img = await getSignature(field.value);
2223
+ page.drawImage(img, { x, y, width: w, height: h });
2098
2224
  }
2099
2225
  } else {
2100
- const font = fontCache.get(field.fontFamily || "Helvetica");
2226
+ const font = await getFont(field.fontFamily || "Helvetica");
2101
2227
  const spacing = field.letterSpacing || 0;
2102
- const textWidthAtSize = (text, size) => {
2103
- const baseWidth = font.widthOfTextAtSize(text, size);
2104
- return baseWidth + spacing * (text.length - 1);
2105
- };
2106
- const maxFontSize = Math.min(field.fontSize, h * 0.7);
2107
- let fontSize = maxFontSize;
2108
- const padding = 4;
2109
- while (fontSize > 4) {
2110
- if (textWidthAtSize(field.value, fontSize) <= w - padding) break;
2111
- fontSize -= 0.5;
2228
+ const textWidthAtSize = (text, size) => font.widthOfTextAtSize(text, size) + spacing * (text.length - 1);
2229
+ let fontSize = Math.min(field.fontSize, h * 0.7);
2230
+ if (field.autoShrink) {
2231
+ const padding = 4;
2232
+ while (fontSize > 4) {
2233
+ if (textWidthAtSize(field.value, fontSize) <= w - padding) break;
2234
+ fontSize -= 0.5;
2235
+ }
2112
2236
  }
2113
2237
  if (spacing > 0) {
2114
2238
  let cx = x + 2;
2115
2239
  const cy = y + h * 0.3;
2116
2240
  for (const char of field.value) {
2117
- page.drawText(char, { x: cx, y: cy, size: fontSize, font, color: inkColor });
2241
+ page.drawText(char, {
2242
+ x: cx,
2243
+ y: cy,
2244
+ size: fontSize,
2245
+ font,
2246
+ color: inkColor
2247
+ });
2118
2248
  cx += font.widthOfTextAtSize(char, fontSize) + spacing;
2119
2249
  }
2120
2250
  } else {
@@ -2128,7 +2258,98 @@ async function generateFilledPdf(pdfSource, fields) {
2128
2258
  }
2129
2259
  }
2130
2260
  }
2131
- return pdfDoc.save();
2261
+ }
2262
+ async function generateFilledPdf(opts) {
2263
+ const builder = await createPdfBuilder({ pageSize: opts.pageSize });
2264
+ await builder.addRecord(opts);
2265
+ return builder.save();
2266
+ }
2267
+ async function createPdfBuilder(defaults = {}) {
2268
+ const doc = await import_pdf_lib.PDFDocument.create();
2269
+ const fontCache = /* @__PURE__ */ new Map();
2270
+ const embeddedPagesByUrl = /* @__PURE__ */ new Map();
2271
+ const signatureCache = /* @__PURE__ */ new Map();
2272
+ async function getFont(family) {
2273
+ let f = fontCache.get(family);
2274
+ if (!f) {
2275
+ const stdName = FONT_MAP[family] || import_pdf_lib.StandardFonts.Helvetica;
2276
+ f = await doc.embedFont(stdName);
2277
+ fontCache.set(family, f);
2278
+ }
2279
+ return f;
2280
+ }
2281
+ async function ensureFonts(fields) {
2282
+ const keys = new Set(fields.map((f) => f.fontFamily || "Helvetica"));
2283
+ for (const k of keys) await getFont(k);
2284
+ }
2285
+ async function embedSource(source) {
2286
+ if (typeof source === "string") {
2287
+ let pages = embeddedPagesByUrl.get(source);
2288
+ if (!pages) {
2289
+ const res = await fetch(source);
2290
+ const bytes = await res.arrayBuffer();
2291
+ const srcDoc2 = await import_pdf_lib.PDFDocument.load(bytes);
2292
+ pages = await Promise.all(
2293
+ srcDoc2.getPages().map((p) => doc.embedPage(p))
2294
+ );
2295
+ embeddedPagesByUrl.set(source, pages);
2296
+ }
2297
+ return pages;
2298
+ }
2299
+ const srcDoc = await import_pdf_lib.PDFDocument.load(source);
2300
+ return Promise.all(srcDoc.getPages().map((p) => doc.embedPage(p)));
2301
+ }
2302
+ async function getSignature(dataUrl) {
2303
+ let img = signatureCache.get(dataUrl);
2304
+ if (!img) {
2305
+ const b64 = dataUrl.split(",")[1] ?? "";
2306
+ const pngBytes = Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
2307
+ img = await doc.embedPng(pngBytes);
2308
+ signatureCache.set(dataUrl, img);
2309
+ }
2310
+ return img;
2311
+ }
2312
+ return {
2313
+ doc,
2314
+ async addRecord(opts) {
2315
+ const pageSize = opts.pageSize ?? defaults.pageSize;
2316
+ let fields = opts.resolveFormulas ? resolveAllFormulas(opts.fields, opts.customTransforms) : opts.fields;
2317
+ if (opts.calibration) {
2318
+ fields = applyCalibration(
2319
+ fields,
2320
+ opts.calibration,
2321
+ pageSize ?? US_LETTER
2322
+ );
2323
+ }
2324
+ await ensureFonts(fields);
2325
+ let sourcePages = [];
2326
+ let pageCount;
2327
+ if (opts.pdfSource !== null) {
2328
+ sourcePages = await embedSource(opts.pdfSource);
2329
+ pageCount = sourcePages.length;
2330
+ } else {
2331
+ pageCount = opts.pageCount ?? 1;
2332
+ }
2333
+ const newPages = [];
2334
+ for (let i = 0; i < pageCount; i++) {
2335
+ const size = pageSize ? pageSize : sourcePages[i] ? [sourcePages[i].width, sourcePages[i].height] : US_LETTER;
2336
+ const page = doc.addPage(size);
2337
+ newPages.push(page);
2338
+ if (sourcePages[i]) {
2339
+ page.drawPage(sourcePages[i], {
2340
+ x: 0,
2341
+ y: 0,
2342
+ width: size[0],
2343
+ height: size[1]
2344
+ });
2345
+ }
2346
+ }
2347
+ await renderFieldsOnPages(newPages, fields, getFont, getSignature);
2348
+ },
2349
+ async save() {
2350
+ return doc.save();
2351
+ }
2352
+ };
2132
2353
  }
2133
2354
  function downloadPdf(bytes, filename) {
2134
2355
  const blob = new Blob([bytes.slice().buffer], { type: "application/pdf" });
@@ -2240,95 +2461,6 @@ function FieldNavigator({
2240
2461
  ] });
2241
2462
  }
2242
2463
 
2243
- // src/utils/formulaResolver.ts
2244
- var BUILTIN_TRANSFORMS = {
2245
- // Date transforms (expects a parseable date string)
2246
- month: (v) => {
2247
- const d = new Date(v);
2248
- return isNaN(d.getTime()) ? "" : String(d.getMonth() + 1);
2249
- },
2250
- month2: (v) => {
2251
- const d = new Date(v);
2252
- return isNaN(d.getTime()) ? "" : String(d.getMonth() + 1).padStart(2, "0");
2253
- },
2254
- monthname: (v) => {
2255
- const d = new Date(v);
2256
- return isNaN(d.getTime()) ? "" : d.toLocaleString("en", { month: "long" });
2257
- },
2258
- monthshort: (v) => {
2259
- const d = new Date(v);
2260
- return isNaN(d.getTime()) ? "" : d.toLocaleString("en", { month: "short" });
2261
- },
2262
- day: (v) => {
2263
- const d = new Date(v);
2264
- return isNaN(d.getTime()) ? "" : String(d.getDate());
2265
- },
2266
- day2: (v) => {
2267
- const d = new Date(v);
2268
- return isNaN(d.getTime()) ? "" : String(d.getDate()).padStart(2, "0");
2269
- },
2270
- year: (v) => {
2271
- const d = new Date(v);
2272
- return isNaN(d.getTime()) ? "" : String(d.getFullYear());
2273
- },
2274
- year2: (v) => {
2275
- const d = new Date(v);
2276
- return isNaN(d.getTime()) ? "" : String(d.getFullYear()).slice(-2);
2277
- },
2278
- // String transforms
2279
- upper: (v) => v.toUpperCase(),
2280
- lower: (v) => v.toLowerCase(),
2281
- trim: (v) => v.trim(),
2282
- first: (v) => v.split(/\s+/)[0] || "",
2283
- last: (v) => {
2284
- const parts = v.split(/\s+/);
2285
- return parts[parts.length - 1] || "";
2286
- },
2287
- initials: (v) => v.split(/\s+/).map((w) => w[0] || "").join("").toUpperCase(),
2288
- // Numeric / substring
2289
- last4: (v) => v.slice(-4),
2290
- last2: (v) => v.slice(-2),
2291
- first4: (v) => v.slice(0, 4),
2292
- first2: (v) => v.slice(0, 2),
2293
- digits: (v) => v.replace(/\D/g, ""),
2294
- number: (v) => {
2295
- const n = parseFloat(v);
2296
- return isNaN(n) ? "" : String(n);
2297
- },
2298
- currency: (v) => {
2299
- const n = parseFloat(v.replace(/[^0-9.-]/g, ""));
2300
- return isNaN(n) ? "" : `$${n.toFixed(2)}`;
2301
- }
2302
- };
2303
- var FORMULA_RE = /\{\{(.+?)\}\}/g;
2304
- var PIPE_RE = /^(.+?)\s*\|\s*(.+)$/;
2305
- function resolveFormula(formula, fields, customTransforms) {
2306
- const transforms = { ...BUILTIN_TRANSFORMS, ...customTransforms };
2307
- return formula.replace(FORMULA_RE, (_match, expr) => {
2308
- const pipeMatch = expr.match(PIPE_RE);
2309
- const sourceLabel = (pipeMatch ? pipeMatch[1] : expr).trim();
2310
- const transformName = pipeMatch ? pipeMatch[2].trim() : null;
2311
- const sourceField = fields.find((f) => f.label.toLowerCase() === sourceLabel.toLowerCase()) || fields.find((f) => f.id === sourceLabel);
2312
- if (!sourceField) return "";
2313
- const rawValue = sourceField.value || "";
2314
- if (!transformName) return rawValue;
2315
- const fn = transforms[transformName];
2316
- if (!fn) return rawValue;
2317
- try {
2318
- return fn(rawValue);
2319
- } catch {
2320
- return rawValue;
2321
- }
2322
- });
2323
- }
2324
- function resolveAllFormulas(fields, customTransforms) {
2325
- return fields.map((f) => {
2326
- if (!f.formula) return f;
2327
- const computed = resolveFormula(f.formula, fields, customTransforms);
2328
- return computed !== f.value ? { ...f, value: computed } : f;
2329
- });
2330
- }
2331
-
2332
2464
  // src/components/pdf-builder/SignerView.tsx
2333
2465
  var import_jsx_runtime6 = require("react/jsx-runtime");
2334
2466
  function SignerView({
@@ -2546,7 +2678,7 @@ function SignerView({
2546
2678
  let pdfBytes;
2547
2679
  if (includeAuditTrail) {
2548
2680
  const { PDFDocument: PDFDocument2, StandardFonts: StandardFonts2, rgb: rgb2 } = await import("pdf-lib");
2549
- const basePdf = await generateFilledPdf(pdfSource, finalFields);
2681
+ const basePdf = await generateFilledPdf({ pdfSource, fields: finalFields });
2550
2682
  const pdfDoc = await PDFDocument2.load(basePdf);
2551
2683
  const font = await pdfDoc.embedFont(StandardFonts2.Helvetica);
2552
2684
  const boldFont = await pdfDoc.embedFont(StandardFonts2.HelveticaBold);
@@ -2563,7 +2695,7 @@ function SignerView({
2563
2695
  }
2564
2696
  pdfBytes = await pdfDoc.save();
2565
2697
  } else {
2566
- pdfBytes = await generateFilledPdf(pdfSource, finalFields);
2698
+ pdfBytes = await generateFilledPdf({ pdfSource, fields: finalFields });
2567
2699
  }
2568
2700
  const blob = new Blob([pdfBytes.slice().buffer], { type: "application/pdf" });
2569
2701
  if (onExport && exportFormat) {
@@ -2886,7 +3018,9 @@ function SignerRoleSelector({
2886
3018
  }
2887
3019
  // Annotate the CommonJS export names for ESM import in node:
2888
3020
  0 && (module.exports = {
3021
+ A4,
2889
3022
  BUILTIN_TRANSFORMS,
3023
+ DEFAULT_CALIBRATION,
2890
3024
  DEFAULT_SIGNER_ROLES,
2891
3025
  DesignerView,
2892
3026
  FIELD_DEFAULTS,
@@ -2898,7 +3032,11 @@ function SignerRoleSelector({
2898
3032
  SignatureCanvas,
2899
3033
  SignerRoleSelector,
2900
3034
  SignerView,
3035
+ US_LEGAL,
3036
+ US_LETTER,
3037
+ applyCalibration,
2901
3038
  createField,
3039
+ createPdfBuilder,
2902
3040
  downloadPdf,
2903
3041
  generateFilledPdf,
2904
3042
  generateId,