@unlev/exeq 0.5.7 → 0.6.1
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/chunk-OLRWRBLO.mjs +650 -0
- package/dist/chunk-OLRWRBLO.mjs.map +1 -0
- package/dist/embed.css +1 -0
- package/dist/embed.global.js +426 -0
- package/dist/index.d.mts +4 -229
- package/dist/index.d.ts +4 -229
- package/dist/index.js +16 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +41 -612
- package/dist/index.mjs.map +1 -1
- package/dist/pdfFiller-Btf-SnEB.d.mts +247 -0
- package/dist/pdfFiller-Btf-SnEB.d.ts +247 -0
- package/dist/server.d.mts +34 -0
- package/dist/server.d.ts +34 -0
- package/dist/server.js +663 -0
- package/dist/server.js.map +1 -0
- package/dist/server.mjs +61 -0
- package/dist/server.mjs.map +1 -0
- package/package.json +7 -9
|
@@ -0,0 +1,650 @@
|
|
|
1
|
+
// src/types/pdf-builder.ts
|
|
2
|
+
function generateId() {
|
|
3
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
4
|
+
return crypto.randomUUID();
|
|
5
|
+
}
|
|
6
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
7
|
+
const r = Math.random() * 16 | 0;
|
|
8
|
+
const v = c === "x" ? r : r & 3 | 8;
|
|
9
|
+
return v.toString(16);
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
var DEFAULT_SIGNER_ROLES = ["Sender", "Signer 1"];
|
|
13
|
+
var SIGNER_ROLE_COLORS = {
|
|
14
|
+
"Sender": "#6366f1",
|
|
15
|
+
"Signer 1": "#22c55e",
|
|
16
|
+
"Signer 2": "#f59e0b",
|
|
17
|
+
"Signer 3": "#ef4444",
|
|
18
|
+
"Signer 4": "#06b6d4",
|
|
19
|
+
"Signer 5": "#d946ef"
|
|
20
|
+
};
|
|
21
|
+
function getSignerColor(role) {
|
|
22
|
+
return SIGNER_ROLE_COLORS[role] || "#888888";
|
|
23
|
+
}
|
|
24
|
+
function isRedactField(f) {
|
|
25
|
+
return f.type === "blackout" || f.type === "whiteout";
|
|
26
|
+
}
|
|
27
|
+
function isSignatureField(f) {
|
|
28
|
+
return f.type === "signature" || f.type === "initials";
|
|
29
|
+
}
|
|
30
|
+
function isTextLikeField(f) {
|
|
31
|
+
return f.type === "text" || f.type === "dropdown" || f.type === "signed-date";
|
|
32
|
+
}
|
|
33
|
+
function getInputType(subtype) {
|
|
34
|
+
switch (subtype) {
|
|
35
|
+
case "email":
|
|
36
|
+
return "email";
|
|
37
|
+
case "number":
|
|
38
|
+
return "number";
|
|
39
|
+
case "phone":
|
|
40
|
+
return "tel";
|
|
41
|
+
case "date":
|
|
42
|
+
return "date";
|
|
43
|
+
default:
|
|
44
|
+
return "text";
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
var CSS_FONT_MAP = {
|
|
48
|
+
Courier: '"Courier New", Courier, monospace',
|
|
49
|
+
TimesRoman: '"Times New Roman", Times, serif',
|
|
50
|
+
Helvetica: "Helvetica, Arial, sans-serif"
|
|
51
|
+
};
|
|
52
|
+
function getCssFontFamily(fontFamily) {
|
|
53
|
+
return fontFamily ? CSS_FONT_MAP[fontFamily] : void 0;
|
|
54
|
+
}
|
|
55
|
+
function getCssTextStyle(field) {
|
|
56
|
+
const decorations = [];
|
|
57
|
+
if (field.underline) decorations.push("underline");
|
|
58
|
+
if (field.strikethrough) decorations.push("line-through");
|
|
59
|
+
const align = field.align ?? "left";
|
|
60
|
+
return {
|
|
61
|
+
fontFamily: getCssFontFamily(field.fontFamily),
|
|
62
|
+
fontWeight: field.bold ? "bold" : void 0,
|
|
63
|
+
fontStyle: field.italic ? "italic" : void 0,
|
|
64
|
+
textDecorationLine: decorations.length ? decorations.join(" ") : void 0,
|
|
65
|
+
textAlign: field.align,
|
|
66
|
+
// Hug the alignment edge — no padding on the trailing side — so text reaches
|
|
67
|
+
// the container edge (matches the PDF's 2pt offsets).
|
|
68
|
+
paddingLeft: align === "right" ? "0px" : "2px",
|
|
69
|
+
paddingRight: align === "left" ? "0px" : "2px"
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function sortFieldsByPosition(fields) {
|
|
73
|
+
return [...fields].sort((a, b) => {
|
|
74
|
+
if (a.page !== b.page) return a.page - b.page;
|
|
75
|
+
const bandThreshold = 2;
|
|
76
|
+
if (Math.abs(a.y - b.y) > bandThreshold) return a.y - b.y;
|
|
77
|
+
return a.x - b.x;
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
function preserveFieldValues(oldFields, newFields) {
|
|
81
|
+
const valueMap = new Map(oldFields.filter((f) => f.value).map((f) => [f.id, f.value]));
|
|
82
|
+
return newFields.map((f) => valueMap.has(f.id) ? { ...f, value: valueMap.get(f.id) } : f);
|
|
83
|
+
}
|
|
84
|
+
function getFieldValues(fields) {
|
|
85
|
+
const values = {};
|
|
86
|
+
fields.forEach((f) => {
|
|
87
|
+
if (!isRedactField(f)) values[f.label] = f.value || "";
|
|
88
|
+
});
|
|
89
|
+
return values;
|
|
90
|
+
}
|
|
91
|
+
var FONT_FAMILIES = [
|
|
92
|
+
{ value: "Helvetica", label: "Helvetica" },
|
|
93
|
+
{ value: "Courier", label: "Courier New" },
|
|
94
|
+
{ value: "TimesRoman", label: "Times New Roman" }
|
|
95
|
+
];
|
|
96
|
+
var FIELD_DEFAULTS = {
|
|
97
|
+
text: {
|
|
98
|
+
width: 20,
|
|
99
|
+
height: 3,
|
|
100
|
+
fontSize: 12,
|
|
101
|
+
placeholder: "Enter text",
|
|
102
|
+
textSubtype: "freeform"
|
|
103
|
+
},
|
|
104
|
+
dropdown: {
|
|
105
|
+
width: 20,
|
|
106
|
+
height: 3,
|
|
107
|
+
fontSize: 12,
|
|
108
|
+
placeholder: "Select..."
|
|
109
|
+
},
|
|
110
|
+
signature: {
|
|
111
|
+
width: 20,
|
|
112
|
+
height: 6,
|
|
113
|
+
fontSize: 12,
|
|
114
|
+
placeholder: "Sign here"
|
|
115
|
+
},
|
|
116
|
+
"signed-date": {
|
|
117
|
+
width: 15,
|
|
118
|
+
height: 3,
|
|
119
|
+
fontSize: 12,
|
|
120
|
+
placeholder: "Date"
|
|
121
|
+
},
|
|
122
|
+
checkbox: {
|
|
123
|
+
width: 2.5,
|
|
124
|
+
height: 2.5,
|
|
125
|
+
fontSize: 12,
|
|
126
|
+
placeholder: ""
|
|
127
|
+
},
|
|
128
|
+
initials: {
|
|
129
|
+
width: 8,
|
|
130
|
+
height: 5,
|
|
131
|
+
fontSize: 12,
|
|
132
|
+
placeholder: "Initials"
|
|
133
|
+
},
|
|
134
|
+
blackout: {
|
|
135
|
+
width: 20,
|
|
136
|
+
height: 3,
|
|
137
|
+
fontSize: 12,
|
|
138
|
+
placeholder: ""
|
|
139
|
+
},
|
|
140
|
+
whiteout: {
|
|
141
|
+
width: 20,
|
|
142
|
+
height: 3,
|
|
143
|
+
fontSize: 12,
|
|
144
|
+
placeholder: ""
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
var TYPE_LABELS = {
|
|
148
|
+
text: "Text Field",
|
|
149
|
+
dropdown: "Dropdown",
|
|
150
|
+
signature: "Signature",
|
|
151
|
+
"signed-date": "Signed Date",
|
|
152
|
+
checkbox: "Checkbox",
|
|
153
|
+
initials: "Initials",
|
|
154
|
+
blackout: "Blackout",
|
|
155
|
+
whiteout: "Whiteout"
|
|
156
|
+
};
|
|
157
|
+
function uniqueLabel(desired, existingLabels) {
|
|
158
|
+
const lower = desired.toLowerCase();
|
|
159
|
+
if (!existingLabels.some((l) => l.toLowerCase() === lower)) return desired;
|
|
160
|
+
let i = 2;
|
|
161
|
+
while (existingLabels.some((l) => l.toLowerCase() === `${lower} ${i}`)) i++;
|
|
162
|
+
return `${desired} ${i}`;
|
|
163
|
+
}
|
|
164
|
+
function createField(type, assignee, page, x, y, existingFields) {
|
|
165
|
+
const defaults = FIELD_DEFAULTS[type];
|
|
166
|
+
const baseLabel = TYPE_LABELS[type];
|
|
167
|
+
const existingLabels = existingFields?.map((f) => f.label) ?? [];
|
|
168
|
+
return {
|
|
169
|
+
id: generateId(),
|
|
170
|
+
type,
|
|
171
|
+
label: uniqueLabel(baseLabel, existingLabels),
|
|
172
|
+
placeholder: defaults.placeholder || "",
|
|
173
|
+
required: type === "checkbox" || type === "blackout" || type === "whiteout" ? false : true,
|
|
174
|
+
assignee,
|
|
175
|
+
page,
|
|
176
|
+
x,
|
|
177
|
+
y,
|
|
178
|
+
width: defaults.width || 20,
|
|
179
|
+
height: defaults.height || 3,
|
|
180
|
+
fontSize: defaults.fontSize || 12,
|
|
181
|
+
value: "",
|
|
182
|
+
...type === "text" ? { textSubtype: defaults.textSubtype } : {}
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// src/utils/formulaResolver.ts
|
|
187
|
+
function parseDate(value) {
|
|
188
|
+
const s = value.trim();
|
|
189
|
+
if (!s) return null;
|
|
190
|
+
if (/^\d+(\.\d+)?$/.test(s)) {
|
|
191
|
+
const serial = parseFloat(s);
|
|
192
|
+
if (serial > 59) {
|
|
193
|
+
const utc = new Date(Math.round((serial - 25569) * 864e5));
|
|
194
|
+
if (!isNaN(utc.getTime())) {
|
|
195
|
+
return new Date(utc.getUTCFullYear(), utc.getUTCMonth(), utc.getUTCDate());
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
const iso = s.match(/^(\d{4})-(\d{2})-(\d{2})$/);
|
|
200
|
+
if (iso) return new Date(+iso[1], +iso[2] - 1, +iso[3]);
|
|
201
|
+
const d = new Date(s);
|
|
202
|
+
return isNaN(d.getTime()) ? null : d;
|
|
203
|
+
}
|
|
204
|
+
var BUILTIN_TRANSFORMS = {
|
|
205
|
+
// Date transforms (expects a parseable date string)
|
|
206
|
+
month: (v) => {
|
|
207
|
+
const d = parseDate(v);
|
|
208
|
+
return d ? String(d.getMonth() + 1) : "";
|
|
209
|
+
},
|
|
210
|
+
month2: (v) => {
|
|
211
|
+
const d = parseDate(v);
|
|
212
|
+
return d ? String(d.getMonth() + 1).padStart(2, "0") : "";
|
|
213
|
+
},
|
|
214
|
+
monthname: (v) => {
|
|
215
|
+
const d = parseDate(v);
|
|
216
|
+
return d ? d.toLocaleString("en", { month: "long" }) : "";
|
|
217
|
+
},
|
|
218
|
+
monthshort: (v) => {
|
|
219
|
+
const d = parseDate(v);
|
|
220
|
+
return d ? d.toLocaleString("en", { month: "short" }) : "";
|
|
221
|
+
},
|
|
222
|
+
day: (v) => {
|
|
223
|
+
const d = parseDate(v);
|
|
224
|
+
return d ? String(d.getDate()) : "";
|
|
225
|
+
},
|
|
226
|
+
day2: (v) => {
|
|
227
|
+
const d = parseDate(v);
|
|
228
|
+
return d ? String(d.getDate()).padStart(2, "0") : "";
|
|
229
|
+
},
|
|
230
|
+
year: (v) => {
|
|
231
|
+
const d = parseDate(v);
|
|
232
|
+
return d ? String(d.getFullYear()) : "";
|
|
233
|
+
},
|
|
234
|
+
year2: (v) => {
|
|
235
|
+
const d = parseDate(v);
|
|
236
|
+
return d ? String(d.getFullYear()).slice(-2) : "";
|
|
237
|
+
},
|
|
238
|
+
// String transforms
|
|
239
|
+
upper: (v) => v.toUpperCase(),
|
|
240
|
+
lower: (v) => v.toLowerCase(),
|
|
241
|
+
trim: (v) => v.trim(),
|
|
242
|
+
first: (v) => v.split(/\s+/)[0] || "",
|
|
243
|
+
last: (v) => {
|
|
244
|
+
const parts = v.split(/\s+/);
|
|
245
|
+
return parts[parts.length - 1] || "";
|
|
246
|
+
},
|
|
247
|
+
initials: (v) => v.split(/\s+/).map((w) => w[0] || "").join("").toUpperCase(),
|
|
248
|
+
// Name parts. Handles "Last, First [Middle]" (comma form) and "First [Middle]
|
|
249
|
+
// Last" (no comma). firstName → the given name(s); lastName → the surname.
|
|
250
|
+
firstName: (v) => {
|
|
251
|
+
const c = v.indexOf(",");
|
|
252
|
+
if (c >= 0) return v.slice(c + 1).trim();
|
|
253
|
+
const parts = v.trim().split(/\s+/).filter(Boolean);
|
|
254
|
+
return parts.length > 1 ? parts.slice(0, -1).join(" ") : parts[0] || "";
|
|
255
|
+
},
|
|
256
|
+
lastName: (v) => {
|
|
257
|
+
const c = v.indexOf(",");
|
|
258
|
+
if (c >= 0) return v.slice(0, c).trim();
|
|
259
|
+
const parts = v.trim().split(/\s+/).filter(Boolean);
|
|
260
|
+
return parts.length > 1 ? parts[parts.length - 1] : parts[0] || "";
|
|
261
|
+
},
|
|
262
|
+
// Numeric / substring
|
|
263
|
+
last4: (v) => v.slice(-4),
|
|
264
|
+
last2: (v) => v.slice(-2),
|
|
265
|
+
first4: (v) => v.slice(0, 4),
|
|
266
|
+
first2: (v) => v.slice(0, 2),
|
|
267
|
+
digits: (v) => v.replace(/\D/g, ""),
|
|
268
|
+
number: (v) => {
|
|
269
|
+
const n = parseFloat(v);
|
|
270
|
+
return isNaN(n) ? "" : String(n);
|
|
271
|
+
},
|
|
272
|
+
currency: (v) => {
|
|
273
|
+
const n = parseFloat(v.replace(/[^0-9.-]/g, ""));
|
|
274
|
+
return isNaN(n) ? "" : `$${n.toFixed(2)}`;
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
var FORMULA_RE = /\{\{(.+?)\}\}/g;
|
|
278
|
+
var PIPE_RE = /^(.+?)\s*\|\s*(.+)$/;
|
|
279
|
+
function todayIso() {
|
|
280
|
+
const d = /* @__PURE__ */ new Date();
|
|
281
|
+
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
282
|
+
}
|
|
283
|
+
function resolveFormula(formula, fields, customTransforms, values) {
|
|
284
|
+
const transforms = { ...BUILTIN_TRANSFORMS, ...customTransforms };
|
|
285
|
+
return formula.replace(FORMULA_RE, (_match, expr) => {
|
|
286
|
+
const pipeMatch = expr.match(PIPE_RE);
|
|
287
|
+
const sourceLabel = (pipeMatch ? pipeMatch[1] : expr).trim();
|
|
288
|
+
const transformName = pipeMatch ? pipeMatch[2].trim() : null;
|
|
289
|
+
const lowerLabel = sourceLabel.toLowerCase();
|
|
290
|
+
const sourceField = fields.find((f) => f.label.toLowerCase() === lowerLabel) || fields.find((f) => f.id === sourceLabel);
|
|
291
|
+
let rawValue;
|
|
292
|
+
if (sourceField && sourceField.value) {
|
|
293
|
+
rawValue = sourceField.value;
|
|
294
|
+
}
|
|
295
|
+
if (rawValue === void 0 && values) {
|
|
296
|
+
const key = Object.keys(values).find((k) => k.toLowerCase() === lowerLabel);
|
|
297
|
+
if (key !== void 0) rawValue = values[key] || "";
|
|
298
|
+
}
|
|
299
|
+
if (rawValue === void 0 && sourceField) {
|
|
300
|
+
rawValue = sourceField.value || "";
|
|
301
|
+
}
|
|
302
|
+
if (rawValue === void 0 && (lowerLabel === "today" || lowerLabel === "now")) {
|
|
303
|
+
rawValue = todayIso();
|
|
304
|
+
}
|
|
305
|
+
if (rawValue === void 0) return "";
|
|
306
|
+
if (!transformName) return rawValue;
|
|
307
|
+
const fn = transforms[transformName];
|
|
308
|
+
if (!fn) return rawValue;
|
|
309
|
+
try {
|
|
310
|
+
return fn(rawValue);
|
|
311
|
+
} catch {
|
|
312
|
+
return rawValue;
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
function resolveAllFormulas(fields, customTransforms, values) {
|
|
317
|
+
return fields.map((f) => {
|
|
318
|
+
if (!f.formula) return f;
|
|
319
|
+
const computed = resolveFormula(f.formula, fields, customTransforms, values);
|
|
320
|
+
return computed !== f.value ? { ...f, value: computed } : f;
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// src/utils/fitText.ts
|
|
325
|
+
function fitFontSize(o) {
|
|
326
|
+
let size = o.autoShrink ? Math.min(o.fontSize, o.fieldHeightPt * 0.7) : o.fontSize;
|
|
327
|
+
if (o.autoShrink && o.value) {
|
|
328
|
+
const padding = 4;
|
|
329
|
+
while (size > 4) {
|
|
330
|
+
if (o.measure(o.value, size) <= o.fieldWidthPt - padding) break;
|
|
331
|
+
size -= 0.5;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return size;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// src/utils/pdfFiller.ts
|
|
338
|
+
import {
|
|
339
|
+
PDFDocument,
|
|
340
|
+
rgb,
|
|
341
|
+
StandardFonts
|
|
342
|
+
} from "pdf-lib";
|
|
343
|
+
var FONT_VARIANTS = {
|
|
344
|
+
Helvetica: [StandardFonts.Helvetica, StandardFonts.HelveticaBold, StandardFonts.HelveticaOblique, StandardFonts.HelveticaBoldOblique],
|
|
345
|
+
Courier: [StandardFonts.Courier, StandardFonts.CourierBold, StandardFonts.CourierOblique, StandardFonts.CourierBoldOblique],
|
|
346
|
+
TimesRoman: [StandardFonts.TimesRoman, StandardFonts.TimesRomanBold, StandardFonts.TimesRomanItalic, StandardFonts.TimesRomanBoldItalic]
|
|
347
|
+
};
|
|
348
|
+
function resolveStandardFont(family, bold, italic) {
|
|
349
|
+
const variants = FONT_VARIANTS[family] || FONT_VARIANTS.Helvetica;
|
|
350
|
+
const idx = (bold ? 1 : 0) + (italic ? 2 : 0);
|
|
351
|
+
return variants[idx];
|
|
352
|
+
}
|
|
353
|
+
function fontKey(family, bold, italic) {
|
|
354
|
+
return `${family}|${bold ? "b" : ""}|${italic ? "i" : ""}`;
|
|
355
|
+
}
|
|
356
|
+
var US_LETTER = [612, 792];
|
|
357
|
+
var US_LEGAL = [612, 1008];
|
|
358
|
+
var A4 = [595.28, 841.89];
|
|
359
|
+
var DEFAULT_CALIBRATION = {
|
|
360
|
+
xOffset: 0,
|
|
361
|
+
yOffset: 0,
|
|
362
|
+
xScale: 1,
|
|
363
|
+
yScale: 1
|
|
364
|
+
};
|
|
365
|
+
function applyCalibration(fields, calibration, pageSize = US_LETTER) {
|
|
366
|
+
if (calibration.xOffset === 0 && calibration.yOffset === 0 && calibration.xScale === 1 && calibration.yScale === 1) {
|
|
367
|
+
return fields;
|
|
368
|
+
}
|
|
369
|
+
const xPctOff = calibration.xOffset / pageSize[0] * 100;
|
|
370
|
+
const yPctOff = calibration.yOffset / pageSize[1] * 100;
|
|
371
|
+
return fields.map((f) => ({
|
|
372
|
+
...f,
|
|
373
|
+
x: f.x * calibration.xScale + xPctOff,
|
|
374
|
+
y: f.y * calibration.yScale + yPctOff,
|
|
375
|
+
width: f.width * calibration.xScale,
|
|
376
|
+
height: f.height * calibration.yScale
|
|
377
|
+
}));
|
|
378
|
+
}
|
|
379
|
+
function hexToRgb(hex) {
|
|
380
|
+
const r = parseInt(hex.slice(1, 3), 16) / 255;
|
|
381
|
+
const g = parseInt(hex.slice(3, 5), 16) / 255;
|
|
382
|
+
const b = parseInt(hex.slice(5, 7), 16) / 255;
|
|
383
|
+
return rgb(r, g, b);
|
|
384
|
+
}
|
|
385
|
+
async function renderFieldsOnPages(pages, fields, getFont, getSignature) {
|
|
386
|
+
for (const field of fields) {
|
|
387
|
+
const page = pages[field.page];
|
|
388
|
+
if (!page) continue;
|
|
389
|
+
const pageWidth = page.getWidth();
|
|
390
|
+
const pageHeight = page.getHeight();
|
|
391
|
+
const x = field.x / 100 * pageWidth;
|
|
392
|
+
const y = pageHeight - field.y / 100 * pageHeight - field.height / 100 * pageHeight;
|
|
393
|
+
const w = field.width / 100 * pageWidth;
|
|
394
|
+
const h = field.height / 100 * pageHeight;
|
|
395
|
+
if (isRedactField(field)) {
|
|
396
|
+
page.drawRectangle({
|
|
397
|
+
x,
|
|
398
|
+
y,
|
|
399
|
+
width: w,
|
|
400
|
+
height: h,
|
|
401
|
+
color: field.type === "blackout" ? rgb(0, 0, 0) : rgb(1, 1, 1)
|
|
402
|
+
});
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
if (!field.value) continue;
|
|
406
|
+
const inkColor = field.inkColor ? hexToRgb(field.inkColor) : rgb(0, 0, 0);
|
|
407
|
+
if (field.type === "checkbox") {
|
|
408
|
+
if (field.value === "true") {
|
|
409
|
+
const inset = Math.min(w, h) * 0.2;
|
|
410
|
+
const lineWidth = Math.min(w, h) * 0.08;
|
|
411
|
+
page.drawLine({
|
|
412
|
+
start: { x: x + inset, y: y + inset },
|
|
413
|
+
end: { x: x + w - inset, y: y + h - inset },
|
|
414
|
+
thickness: lineWidth,
|
|
415
|
+
color: inkColor
|
|
416
|
+
});
|
|
417
|
+
page.drawLine({
|
|
418
|
+
start: { x: x + w - inset, y: y + inset },
|
|
419
|
+
end: { x: x + inset, y: y + h - inset },
|
|
420
|
+
thickness: lineWidth,
|
|
421
|
+
color: inkColor
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
} else if (isSignatureField(field)) {
|
|
425
|
+
if (field.value.startsWith("data:image/png")) {
|
|
426
|
+
const img = await getSignature(field.value);
|
|
427
|
+
page.drawImage(img, { x, y, width: w, height: h });
|
|
428
|
+
}
|
|
429
|
+
} else {
|
|
430
|
+
const font = await getFont(field.fontFamily || "Helvetica", field.bold, field.italic);
|
|
431
|
+
const spacing = field.letterSpacing || 0;
|
|
432
|
+
const textWidthAtSize = (text, size) => font.widthOfTextAtSize(text, size) + spacing * (text.length - 1);
|
|
433
|
+
const fontSize = fitFontSize({
|
|
434
|
+
value: field.value,
|
|
435
|
+
fontSize: field.fontSize,
|
|
436
|
+
autoShrink: field.autoShrink,
|
|
437
|
+
fieldWidthPt: w,
|
|
438
|
+
fieldHeightPt: h,
|
|
439
|
+
measure: textWidthAtSize
|
|
440
|
+
});
|
|
441
|
+
const fullTextWidth = textWidthAtSize(field.value, fontSize);
|
|
442
|
+
const pad = 2;
|
|
443
|
+
const textX = field.align === "center" ? x + Math.max(pad, (w - fullTextWidth) / 2) : field.align === "right" ? x + Math.max(pad, w - fullTextWidth - pad) : x + pad;
|
|
444
|
+
const baselineY = y + h * 0.3;
|
|
445
|
+
if (spacing > 0) {
|
|
446
|
+
let cx = textX;
|
|
447
|
+
for (const char of field.value) {
|
|
448
|
+
page.drawText(char, {
|
|
449
|
+
x: cx,
|
|
450
|
+
y: baselineY,
|
|
451
|
+
size: fontSize,
|
|
452
|
+
font,
|
|
453
|
+
color: inkColor
|
|
454
|
+
});
|
|
455
|
+
cx += font.widthOfTextAtSize(char, fontSize) + spacing;
|
|
456
|
+
}
|
|
457
|
+
} else {
|
|
458
|
+
page.drawText(field.value, {
|
|
459
|
+
x: textX,
|
|
460
|
+
y: baselineY,
|
|
461
|
+
size: fontSize,
|
|
462
|
+
font,
|
|
463
|
+
color: inkColor
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
if (field.underline || field.strikethrough) {
|
|
467
|
+
const textWidth = textWidthAtSize(field.value, fontSize);
|
|
468
|
+
const lineThickness = Math.max(0.5, fontSize * 0.06);
|
|
469
|
+
if (field.underline) {
|
|
470
|
+
const uy = baselineY - fontSize * 0.12;
|
|
471
|
+
page.drawLine({
|
|
472
|
+
start: { x: textX, y: uy },
|
|
473
|
+
end: { x: textX + textWidth, y: uy },
|
|
474
|
+
thickness: lineThickness,
|
|
475
|
+
color: inkColor
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
if (field.strikethrough) {
|
|
479
|
+
const sy = baselineY + fontSize * 0.3;
|
|
480
|
+
page.drawLine({
|
|
481
|
+
start: { x: textX, y: sy },
|
|
482
|
+
end: { x: textX + textWidth, y: sy },
|
|
483
|
+
thickness: lineThickness,
|
|
484
|
+
color: inkColor
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
async function generateFilledPdf(opts, builderOptions) {
|
|
492
|
+
const builder = await createPdfBuilder({
|
|
493
|
+
pageSize: opts.pageSize,
|
|
494
|
+
metadata: builderOptions?.metadata
|
|
495
|
+
});
|
|
496
|
+
await builder.addRecord(opts);
|
|
497
|
+
return builder.save();
|
|
498
|
+
}
|
|
499
|
+
function applyMetadata(doc, m) {
|
|
500
|
+
if (!m) return;
|
|
501
|
+
if (m.title !== void 0) doc.setTitle(m.title);
|
|
502
|
+
if (m.author !== void 0) doc.setAuthor(m.author);
|
|
503
|
+
if (m.subject !== void 0) doc.setSubject(m.subject);
|
|
504
|
+
if (m.creator !== void 0) doc.setCreator(m.creator);
|
|
505
|
+
if (m.producer !== void 0) doc.setProducer(m.producer);
|
|
506
|
+
if (m.creationDate !== void 0) doc.setCreationDate(m.creationDate);
|
|
507
|
+
if (m.modificationDate !== void 0) doc.setModificationDate(m.modificationDate);
|
|
508
|
+
}
|
|
509
|
+
async function createPdfBuilder(defaults = {}) {
|
|
510
|
+
const doc = await PDFDocument.create();
|
|
511
|
+
const fontCache = /* @__PURE__ */ new Map();
|
|
512
|
+
const embeddedPagesByUrl = /* @__PURE__ */ new Map();
|
|
513
|
+
const signatureCache = /* @__PURE__ */ new Map();
|
|
514
|
+
async function getFont(family, bold, italic) {
|
|
515
|
+
const key = fontKey(family, bold, italic);
|
|
516
|
+
let f = fontCache.get(key);
|
|
517
|
+
if (!f) {
|
|
518
|
+
f = await doc.embedFont(resolveStandardFont(family, bold, italic));
|
|
519
|
+
fontCache.set(key, f);
|
|
520
|
+
}
|
|
521
|
+
return f;
|
|
522
|
+
}
|
|
523
|
+
async function ensureFonts(fields) {
|
|
524
|
+
const keys = /* @__PURE__ */ new Map();
|
|
525
|
+
for (const f of fields) keys.set(fontKey(f.fontFamily || "Helvetica", f.bold, f.italic), f);
|
|
526
|
+
for (const f of keys.values()) await getFont(f.fontFamily || "Helvetica", f.bold, f.italic);
|
|
527
|
+
}
|
|
528
|
+
async function embedSource(source) {
|
|
529
|
+
if (typeof source === "string") {
|
|
530
|
+
let pages = embeddedPagesByUrl.get(source);
|
|
531
|
+
if (!pages) {
|
|
532
|
+
const res = await fetch(source);
|
|
533
|
+
const bytes = await res.arrayBuffer();
|
|
534
|
+
const srcDoc2 = await PDFDocument.load(bytes);
|
|
535
|
+
pages = await Promise.all(
|
|
536
|
+
srcDoc2.getPages().map((p) => doc.embedPage(p))
|
|
537
|
+
);
|
|
538
|
+
embeddedPagesByUrl.set(source, pages);
|
|
539
|
+
}
|
|
540
|
+
return pages;
|
|
541
|
+
}
|
|
542
|
+
const srcDoc = await PDFDocument.load(source);
|
|
543
|
+
return Promise.all(srcDoc.getPages().map((p) => doc.embedPage(p)));
|
|
544
|
+
}
|
|
545
|
+
async function getSignature(dataUrl) {
|
|
546
|
+
let img = signatureCache.get(dataUrl);
|
|
547
|
+
if (!img) {
|
|
548
|
+
const b64 = dataUrl.split(",")[1] ?? "";
|
|
549
|
+
const pngBytes = Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
|
|
550
|
+
img = await doc.embedPng(pngBytes);
|
|
551
|
+
signatureCache.set(dataUrl, img);
|
|
552
|
+
}
|
|
553
|
+
return img;
|
|
554
|
+
}
|
|
555
|
+
return {
|
|
556
|
+
doc,
|
|
557
|
+
async addRecord(opts) {
|
|
558
|
+
const pageSize = opts.pageSize ?? defaults.pageSize;
|
|
559
|
+
let fields = opts.resolveFormulas ? resolveAllFormulas(opts.fields, opts.customTransforms, opts.formulaValues) : opts.fields;
|
|
560
|
+
if (opts.calibration) {
|
|
561
|
+
fields = applyCalibration(
|
|
562
|
+
fields,
|
|
563
|
+
opts.calibration,
|
|
564
|
+
pageSize ?? US_LETTER
|
|
565
|
+
);
|
|
566
|
+
}
|
|
567
|
+
await ensureFonts(fields);
|
|
568
|
+
let sourcePages = [];
|
|
569
|
+
let pageCount;
|
|
570
|
+
if (opts.pdfSource !== null) {
|
|
571
|
+
sourcePages = await embedSource(opts.pdfSource);
|
|
572
|
+
pageCount = sourcePages.length;
|
|
573
|
+
} else {
|
|
574
|
+
pageCount = opts.pageCount ?? 1;
|
|
575
|
+
}
|
|
576
|
+
const newPages = [];
|
|
577
|
+
for (let i = 0; i < pageCount; i++) {
|
|
578
|
+
const size = pageSize ? pageSize : sourcePages[i] ? [sourcePages[i].width, sourcePages[i].height] : US_LETTER;
|
|
579
|
+
const page = doc.addPage(size);
|
|
580
|
+
newPages.push(page);
|
|
581
|
+
if (sourcePages[i]) {
|
|
582
|
+
page.drawPage(sourcePages[i], {
|
|
583
|
+
x: 0,
|
|
584
|
+
y: 0,
|
|
585
|
+
width: size[0],
|
|
586
|
+
height: size[1]
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
await renderFieldsOnPages(newPages, fields, getFont, getSignature);
|
|
591
|
+
},
|
|
592
|
+
async save() {
|
|
593
|
+
applyMetadata(doc, defaults.metadata);
|
|
594
|
+
return doc.save();
|
|
595
|
+
}
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
function downloadPdf(bytes, filename) {
|
|
599
|
+
const blob = new Blob([bytes.slice().buffer], { type: "application/pdf" });
|
|
600
|
+
const url = URL.createObjectURL(blob);
|
|
601
|
+
const a = document.createElement("a");
|
|
602
|
+
a.href = url;
|
|
603
|
+
a.download = filename;
|
|
604
|
+
a.click();
|
|
605
|
+
URL.revokeObjectURL(url);
|
|
606
|
+
}
|
|
607
|
+
async function postPdfToCallback(bytes, callbackUrl, filename) {
|
|
608
|
+
const blob = new Blob([bytes.slice().buffer], { type: "application/pdf" });
|
|
609
|
+
const formData = new FormData();
|
|
610
|
+
formData.append("file", blob, filename);
|
|
611
|
+
await fetch(callbackUrl, {
|
|
612
|
+
method: "POST",
|
|
613
|
+
body: formData
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
export {
|
|
618
|
+
generateId,
|
|
619
|
+
DEFAULT_SIGNER_ROLES,
|
|
620
|
+
SIGNER_ROLE_COLORS,
|
|
621
|
+
getSignerColor,
|
|
622
|
+
isRedactField,
|
|
623
|
+
isSignatureField,
|
|
624
|
+
isTextLikeField,
|
|
625
|
+
getInputType,
|
|
626
|
+
getCssFontFamily,
|
|
627
|
+
getCssTextStyle,
|
|
628
|
+
sortFieldsByPosition,
|
|
629
|
+
preserveFieldValues,
|
|
630
|
+
getFieldValues,
|
|
631
|
+
FONT_FAMILIES,
|
|
632
|
+
FIELD_DEFAULTS,
|
|
633
|
+
uniqueLabel,
|
|
634
|
+
createField,
|
|
635
|
+
parseDate,
|
|
636
|
+
BUILTIN_TRANSFORMS,
|
|
637
|
+
resolveFormula,
|
|
638
|
+
resolveAllFormulas,
|
|
639
|
+
fitFontSize,
|
|
640
|
+
US_LETTER,
|
|
641
|
+
US_LEGAL,
|
|
642
|
+
A4,
|
|
643
|
+
DEFAULT_CALIBRATION,
|
|
644
|
+
applyCalibration,
|
|
645
|
+
generateFilledPdf,
|
|
646
|
+
createPdfBuilder,
|
|
647
|
+
downloadPdf,
|
|
648
|
+
postPdfToCallback
|
|
649
|
+
};
|
|
650
|
+
//# sourceMappingURL=chunk-OLRWRBLO.mjs.map
|