@signiphi/page-assembly 0.2.0-beta.16 → 0.2.0-beta.18
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.d.mts +33 -2
- package/dist/index.d.ts +33 -2
- package/dist/index.js +127 -74
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +128 -75
- package/dist/index.mjs.map +1 -1
- package/dist/styles/index.css +182 -163
- package/package.json +94 -93
- package/src/styles/index.css +164 -109
package/dist/index.d.mts
CHANGED
|
@@ -64,6 +64,13 @@ type TrackedFormField = {
|
|
|
64
64
|
label?: string;
|
|
65
65
|
required?: boolean;
|
|
66
66
|
options?: string[];
|
|
67
|
+
/** RADIO: each option's circle rect, top-origin, on `position.page`. */
|
|
68
|
+
optionPositions?: Array<{
|
|
69
|
+
x: number;
|
|
70
|
+
y: number;
|
|
71
|
+
width: number;
|
|
72
|
+
height: number;
|
|
73
|
+
}>;
|
|
67
74
|
placeholder?: string;
|
|
68
75
|
defaultValue?: string;
|
|
69
76
|
assignedSignerEmail?: string;
|
|
@@ -307,6 +314,17 @@ interface ExtractedField {
|
|
|
307
314
|
options?: string[];
|
|
308
315
|
fieldId?: string;
|
|
309
316
|
signer?: string;
|
|
317
|
+
/**
|
|
318
|
+
* RADIO: each option's circle widget rect on this page, top-origin, in
|
|
319
|
+
* option order — exactly as read, so a re-add can put every circle back
|
|
320
|
+
* where it was. `x/y/width/height` above is only the first circle.
|
|
321
|
+
*/
|
|
322
|
+
optionPositions?: Array<{
|
|
323
|
+
x: number;
|
|
324
|
+
y: number;
|
|
325
|
+
width: number;
|
|
326
|
+
height: number;
|
|
327
|
+
}>;
|
|
310
328
|
}
|
|
311
329
|
/**
|
|
312
330
|
* One field's metadata entry, as stored in the block.
|
|
@@ -398,7 +416,8 @@ interface SigniphiMetadata {
|
|
|
398
416
|
checkboxLabelsBaked?: boolean;
|
|
399
417
|
}
|
|
400
418
|
/**
|
|
401
|
-
* Extract SigniphiMetadata from a PDF document
|
|
419
|
+
* Extract SigniphiMetadata from a PDF document. Delegates to the contract
|
|
420
|
+
* package's reader; null when there is no block or it will not parse.
|
|
402
421
|
*/
|
|
403
422
|
declare function getSigniphiMetadata(pdfDoc: PDFDocument): SigniphiMetadata | null;
|
|
404
423
|
/**
|
|
@@ -408,7 +427,8 @@ declare function getSigniphiMetadata(pdfDoc: PDFDocument): SigniphiMetadata | nu
|
|
|
408
427
|
*/
|
|
409
428
|
declare function extractFieldsFromPdf(pdfBytes: Uint8Array): Promise<ExtractedField[]>;
|
|
410
429
|
/**
|
|
411
|
-
* Set SigniphiMetadata in a PDF document
|
|
430
|
+
* Set SigniphiMetadata in a PDF document. Delegates to the contract package's
|
|
431
|
+
* writer, which stores hex and throws on failure.
|
|
412
432
|
*/
|
|
413
433
|
declare function setSigniphiMetadata(pdfDoc: PDFDocument, metadata: SigniphiMetadata): void;
|
|
414
434
|
|
|
@@ -457,6 +477,17 @@ interface FormFieldForPdf {
|
|
|
457
477
|
multiline?: boolean;
|
|
458
478
|
maxLength?: number;
|
|
459
479
|
assignedSignerEmail?: string;
|
|
480
|
+
/**
|
|
481
|
+
* RADIO: each option's circle widget rect, top-origin, on `position.page`,
|
|
482
|
+
* as `extractFieldsFromPdf` read it. When it covers every option, the circles
|
|
483
|
+
* are re-added exactly there instead of being laid out afresh in `position`.
|
|
484
|
+
*/
|
|
485
|
+
optionPositions?: Array<{
|
|
486
|
+
x: number;
|
|
487
|
+
y: number;
|
|
488
|
+
width: number;
|
|
489
|
+
height: number;
|
|
490
|
+
}>;
|
|
460
491
|
}
|
|
461
492
|
/**
|
|
462
493
|
* Add form fields to a PDF document
|
package/dist/index.d.ts
CHANGED
|
@@ -64,6 +64,13 @@ type TrackedFormField = {
|
|
|
64
64
|
label?: string;
|
|
65
65
|
required?: boolean;
|
|
66
66
|
options?: string[];
|
|
67
|
+
/** RADIO: each option's circle rect, top-origin, on `position.page`. */
|
|
68
|
+
optionPositions?: Array<{
|
|
69
|
+
x: number;
|
|
70
|
+
y: number;
|
|
71
|
+
width: number;
|
|
72
|
+
height: number;
|
|
73
|
+
}>;
|
|
67
74
|
placeholder?: string;
|
|
68
75
|
defaultValue?: string;
|
|
69
76
|
assignedSignerEmail?: string;
|
|
@@ -307,6 +314,17 @@ interface ExtractedField {
|
|
|
307
314
|
options?: string[];
|
|
308
315
|
fieldId?: string;
|
|
309
316
|
signer?: string;
|
|
317
|
+
/**
|
|
318
|
+
* RADIO: each option's circle widget rect on this page, top-origin, in
|
|
319
|
+
* option order — exactly as read, so a re-add can put every circle back
|
|
320
|
+
* where it was. `x/y/width/height` above is only the first circle.
|
|
321
|
+
*/
|
|
322
|
+
optionPositions?: Array<{
|
|
323
|
+
x: number;
|
|
324
|
+
y: number;
|
|
325
|
+
width: number;
|
|
326
|
+
height: number;
|
|
327
|
+
}>;
|
|
310
328
|
}
|
|
311
329
|
/**
|
|
312
330
|
* One field's metadata entry, as stored in the block.
|
|
@@ -398,7 +416,8 @@ interface SigniphiMetadata {
|
|
|
398
416
|
checkboxLabelsBaked?: boolean;
|
|
399
417
|
}
|
|
400
418
|
/**
|
|
401
|
-
* Extract SigniphiMetadata from a PDF document
|
|
419
|
+
* Extract SigniphiMetadata from a PDF document. Delegates to the contract
|
|
420
|
+
* package's reader; null when there is no block or it will not parse.
|
|
402
421
|
*/
|
|
403
422
|
declare function getSigniphiMetadata(pdfDoc: PDFDocument): SigniphiMetadata | null;
|
|
404
423
|
/**
|
|
@@ -408,7 +427,8 @@ declare function getSigniphiMetadata(pdfDoc: PDFDocument): SigniphiMetadata | nu
|
|
|
408
427
|
*/
|
|
409
428
|
declare function extractFieldsFromPdf(pdfBytes: Uint8Array): Promise<ExtractedField[]>;
|
|
410
429
|
/**
|
|
411
|
-
* Set SigniphiMetadata in a PDF document
|
|
430
|
+
* Set SigniphiMetadata in a PDF document. Delegates to the contract package's
|
|
431
|
+
* writer, which stores hex and throws on failure.
|
|
412
432
|
*/
|
|
413
433
|
declare function setSigniphiMetadata(pdfDoc: PDFDocument, metadata: SigniphiMetadata): void;
|
|
414
434
|
|
|
@@ -457,6 +477,17 @@ interface FormFieldForPdf {
|
|
|
457
477
|
multiline?: boolean;
|
|
458
478
|
maxLength?: number;
|
|
459
479
|
assignedSignerEmail?: string;
|
|
480
|
+
/**
|
|
481
|
+
* RADIO: each option's circle widget rect, top-origin, on `position.page`,
|
|
482
|
+
* as `extractFieldsFromPdf` read it. When it covers every option, the circles
|
|
483
|
+
* are re-added exactly there instead of being laid out afresh in `position`.
|
|
484
|
+
*/
|
|
485
|
+
optionPositions?: Array<{
|
|
486
|
+
x: number;
|
|
487
|
+
y: number;
|
|
488
|
+
width: number;
|
|
489
|
+
height: number;
|
|
490
|
+
}>;
|
|
460
491
|
}
|
|
461
492
|
/**
|
|
462
493
|
* Add form fields to a PDF document
|
package/dist/index.js
CHANGED
|
@@ -247,8 +247,9 @@ var SelectContent = React7__namespace.forwardRef(({ className, children, positio
|
|
|
247
247
|
position,
|
|
248
248
|
style: {
|
|
249
249
|
zIndex: 99999,
|
|
250
|
-
backgroundColor: "
|
|
251
|
-
|
|
250
|
+
backgroundColor: "hsl(var(--xpa-popover))",
|
|
251
|
+
color: "hsl(var(--xpa-popover-foreground))",
|
|
252
|
+
border: "1px solid hsl(var(--xpa-border))",
|
|
252
253
|
borderRadius: "0.5rem",
|
|
253
254
|
boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1)",
|
|
254
255
|
marginTop: "4px"
|
|
@@ -342,16 +343,23 @@ var AlertDialogOverlay = React7__namespace.forwardRef(({ className, ...props },
|
|
|
342
343
|
}
|
|
343
344
|
));
|
|
344
345
|
AlertDialogOverlay.displayName = AlertDialogPrimitive__namespace.Overlay.displayName;
|
|
345
|
-
var
|
|
346
|
+
var CENTERED = {
|
|
347
|
+
position: "fixed",
|
|
348
|
+
left: "50%",
|
|
349
|
+
top: "50%",
|
|
350
|
+
transform: "translate(-50%, -50%)"
|
|
351
|
+
};
|
|
352
|
+
var AlertDialogContent = React7__namespace.forwardRef(({ className, style, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(AlertDialogPortal, { container: document.querySelector(".signiphi-page-assembly"), children: [
|
|
346
353
|
/* @__PURE__ */ jsxRuntime.jsx(AlertDialogOverlay, {}),
|
|
347
354
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
348
355
|
AlertDialogPrimitive__namespace.Content,
|
|
349
356
|
{
|
|
350
357
|
ref,
|
|
351
358
|
className: cn(
|
|
352
|
-
"
|
|
359
|
+
"z-[9999] grid w-full max-w-lg gap-4 border bg-background p-6 shadow-lg sm:rounded-lg",
|
|
353
360
|
className
|
|
354
361
|
),
|
|
362
|
+
style: { ...CENTERED, ...style },
|
|
355
363
|
...props
|
|
356
364
|
}
|
|
357
365
|
)
|
|
@@ -467,8 +475,9 @@ var DropdownMenuContent = React7__namespace.forwardRef(({ className, sideOffset
|
|
|
467
475
|
),
|
|
468
476
|
style: {
|
|
469
477
|
zIndex: 99999,
|
|
470
|
-
backgroundColor: "
|
|
471
|
-
|
|
478
|
+
backgroundColor: "hsl(var(--xpa-popover))",
|
|
479
|
+
color: "hsl(var(--xpa-popover-foreground))",
|
|
480
|
+
border: "1px solid hsl(var(--xpa-border))",
|
|
472
481
|
borderRadius: "0.5rem",
|
|
473
482
|
boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1)",
|
|
474
483
|
padding: "6px"
|
|
@@ -1399,6 +1408,46 @@ function downloadPdf(pdfBytes, filename) {
|
|
|
1399
1408
|
a.click();
|
|
1400
1409
|
URL.revokeObjectURL(url);
|
|
1401
1410
|
}
|
|
1411
|
+
var KEY = "SigniphiMetadata";
|
|
1412
|
+
function readMetadata(doc, pdfLib) {
|
|
1413
|
+
let blockPresent = false;
|
|
1414
|
+
try {
|
|
1415
|
+
const infoRef = doc.context.trailerInfo.Info;
|
|
1416
|
+
if (!infoRef) return { metadata: null, blockPresent };
|
|
1417
|
+
const info = doc.context.lookup(infoRef);
|
|
1418
|
+
if (!info || typeof info.get !== "function") return { metadata: null, blockPresent };
|
|
1419
|
+
const value = info.get(pdfLib.PDFName.of(KEY));
|
|
1420
|
+
if (!value) return { metadata: null, blockPresent };
|
|
1421
|
+
blockPresent = true;
|
|
1422
|
+
const json = typeof value.decodeText === "function" ? value.decodeText() : stripDelimiters(value.toString());
|
|
1423
|
+
return { metadata: JSON.parse(json), blockPresent };
|
|
1424
|
+
} catch {
|
|
1425
|
+
return { metadata: null, blockPresent };
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
function writeMetadata(doc, metadata, pdfLib) {
|
|
1429
|
+
const info = doc.context.lookup(doc.context.trailerInfo.Info);
|
|
1430
|
+
if (!info || typeof info.set !== "function") {
|
|
1431
|
+
throw new Error("Cannot write SigniphiMetadata: the PDF has no Info dictionary");
|
|
1432
|
+
}
|
|
1433
|
+
info.set(pdfLib.PDFName.of(KEY), pdfLib.PDFHexString.fromText(JSON.stringify(metadata)));
|
|
1434
|
+
}
|
|
1435
|
+
function stripDelimiters(raw) {
|
|
1436
|
+
const wrapped = raw.startsWith("(") && raw.endsWith(")") || raw.startsWith("<") && raw.endsWith(">");
|
|
1437
|
+
return wrapped ? raw.slice(1, -1) : raw;
|
|
1438
|
+
}
|
|
1439
|
+
function textLabelPlacements(source) {
|
|
1440
|
+
const fields = source.metadata?.fields;
|
|
1441
|
+
if (!fields || !source.pages?.length) return [];
|
|
1442
|
+
const out = [];
|
|
1443
|
+
for (const [name, entry] of Object.entries(fields)) {
|
|
1444
|
+
if (entry.type !== "text_label" || !entry.position) continue;
|
|
1445
|
+
for (const { source: page, combined } of source.pages) {
|
|
1446
|
+
if (page === entry.position.page) out.push({ name, page: combined });
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
return out;
|
|
1450
|
+
}
|
|
1402
1451
|
function toCombinedPage(entry, page) {
|
|
1403
1452
|
if (!entry.position && !entry.radioPositions && !entry.radioOptions) return entry;
|
|
1404
1453
|
const carried = { ...entry };
|
|
@@ -1409,78 +1458,55 @@ function toCombinedPage(entry, page) {
|
|
|
1409
1458
|
}
|
|
1410
1459
|
return carried;
|
|
1411
1460
|
}
|
|
1412
|
-
function
|
|
1461
|
+
function assembleMetadata(sources) {
|
|
1413
1462
|
const merged = { version: "1.0", fields: {}, fieldIdIndex: {} };
|
|
1414
1463
|
let radioLabelsBaked;
|
|
1415
1464
|
let textLabelsBaked;
|
|
1416
1465
|
let checkboxLabelsBaked;
|
|
1466
|
+
let legacyRadioStripped;
|
|
1417
1467
|
for (const source of sources) {
|
|
1418
1468
|
const meta = source.metadata;
|
|
1419
|
-
if (!meta || source.fields.length === 0) continue;
|
|
1469
|
+
if (!meta || source.fields.length === 0 && !source.pages?.length) continue;
|
|
1420
1470
|
if (meta.radioLabelsBaked === false) radioLabelsBaked = false;
|
|
1421
1471
|
else if (meta.radioLabelsBaked === true && radioLabelsBaked === void 0) radioLabelsBaked = true;
|
|
1422
1472
|
if (meta.textLabelsBaked === false) textLabelsBaked = false;
|
|
1423
1473
|
else if (meta.textLabelsBaked === true && textLabelsBaked === void 0) textLabelsBaked = true;
|
|
1424
1474
|
if (meta.checkboxLabelsBaked === false) checkboxLabelsBaked = false;
|
|
1425
1475
|
else if (meta.checkboxLabelsBaked === true && checkboxLabelsBaked === void 0) checkboxLabelsBaked = true;
|
|
1476
|
+
legacyRadioStripped = meta.legacyRadioStripped === true && legacyRadioStripped !== false;
|
|
1426
1477
|
}
|
|
1427
1478
|
for (const source of sources) {
|
|
1428
1479
|
const meta = source.metadata;
|
|
1429
1480
|
if (!meta) continue;
|
|
1430
1481
|
const sourceTextLabelsBaked = meta.textLabelsBaked !== false;
|
|
1431
|
-
for (const { name, page } of source.fields) {
|
|
1482
|
+
for (const { name, page } of [...source.fields, ...textLabelPlacements(source)]) {
|
|
1432
1483
|
const key = `${name}_p${page}`;
|
|
1433
1484
|
const entry = meta.fields?.[name];
|
|
1434
|
-
if (entry)
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
const fieldId = meta.fieldIdIndex?.[name];
|
|
1439
|
-
if (fieldId) merged.fieldIdIndex[key] = fieldId;
|
|
1485
|
+
if (!entry) continue;
|
|
1486
|
+
const carried = toCombinedPage(entry, page);
|
|
1487
|
+
merged.fields[key] = textLabelsBaked === false && sourceTextLabelsBaked ? { ...carried, textLabelBaked: true } : carried;
|
|
1488
|
+
if (entry.fieldId) merged.fieldIdIndex[entry.fieldId] = key;
|
|
1440
1489
|
}
|
|
1441
1490
|
}
|
|
1442
1491
|
if (radioLabelsBaked !== void 0) merged.radioLabelsBaked = radioLabelsBaked;
|
|
1443
1492
|
if (textLabelsBaked !== void 0) merged.textLabelsBaked = textLabelsBaked;
|
|
1444
1493
|
if (checkboxLabelsBaked !== void 0) merged.checkboxLabelsBaked = checkboxLabelsBaked;
|
|
1494
|
+
if (legacyRadioStripped) merged.legacyRadioStripped = true;
|
|
1445
1495
|
const hasEntries = Object.keys(merged.fields).length > 0;
|
|
1446
1496
|
const hasMarkers = radioLabelsBaked !== void 0 || textLabelsBaked !== void 0 || checkboxLabelsBaked !== void 0;
|
|
1447
1497
|
if (!hasEntries && !hasMarkers) return null;
|
|
1448
1498
|
if (Object.keys(merged.fieldIdIndex).length === 0) delete merged.fieldIdIndex;
|
|
1449
1499
|
return merged;
|
|
1450
1500
|
}
|
|
1501
|
+
function mergeSourceMetadata(sources) {
|
|
1502
|
+
return assembleMetadata(sources);
|
|
1503
|
+
}
|
|
1451
1504
|
function getSigniphiMetadata(pdfDoc) {
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
return null;
|
|
1456
|
-
}
|
|
1457
|
-
const infoObj = pdfDoc.context.lookup(infoRef);
|
|
1458
|
-
if (!infoObj || typeof infoObj.get !== "function") {
|
|
1459
|
-
return null;
|
|
1460
|
-
}
|
|
1461
|
-
const infoDict = infoObj;
|
|
1462
|
-
const metadataObj = infoDict.get(pdfLib.PDFName.of("SigniphiMetadata"));
|
|
1463
|
-
if (!metadataObj) {
|
|
1464
|
-
return null;
|
|
1465
|
-
}
|
|
1466
|
-
let jsonStr;
|
|
1467
|
-
if (typeof metadataObj.decodeText === "function") {
|
|
1468
|
-
jsonStr = metadataObj.decodeText();
|
|
1469
|
-
} else {
|
|
1470
|
-
const metadataStr = metadataObj.toString();
|
|
1471
|
-
jsonStr = metadataStr;
|
|
1472
|
-
if (metadataStr.startsWith("(") && metadataStr.endsWith(")")) {
|
|
1473
|
-
jsonStr = metadataStr.slice(1, -1);
|
|
1474
|
-
} else if (metadataStr.startsWith("<") && metadataStr.endsWith(">")) {
|
|
1475
|
-
jsonStr = metadataStr.slice(1, -1);
|
|
1476
|
-
}
|
|
1477
|
-
}
|
|
1478
|
-
const metadata = JSON.parse(jsonStr);
|
|
1479
|
-
return metadata;
|
|
1480
|
-
} catch (error) {
|
|
1481
|
-
console.error("Failed to load Signiphi metadata:", error);
|
|
1482
|
-
return null;
|
|
1505
|
+
const { metadata, blockPresent } = readMetadata(pdfDoc, { PDFName: pdfLib.PDFName });
|
|
1506
|
+
if (blockPresent && !metadata) {
|
|
1507
|
+
console.error("Failed to load Signiphi metadata: the block is present but will not parse");
|
|
1483
1508
|
}
|
|
1509
|
+
return metadata;
|
|
1484
1510
|
}
|
|
1485
1511
|
function getBaseFieldName(encodedName) {
|
|
1486
1512
|
let baseName = encodedName;
|
|
@@ -1539,6 +1565,10 @@ async function extractFieldsFromPdf(pdfBytes) {
|
|
|
1539
1565
|
const metadata = getSigniphiMetadata(pdfDoc);
|
|
1540
1566
|
const form = pdfDoc.getForm();
|
|
1541
1567
|
const pdfFields = form.getFields();
|
|
1568
|
+
const pages = pdfDoc.getPages();
|
|
1569
|
+
const pageHeights = pages.map((p) => p.getHeight());
|
|
1570
|
+
const pageIndexByRef = new Map(pages.map((p, i) => [p.ref, i + 1]));
|
|
1571
|
+
const pageNumberOf = (ref) => ref && pageIndexByRef.get(ref) || 1;
|
|
1542
1572
|
for (const field of pdfFields) {
|
|
1543
1573
|
const fieldName = field.getName();
|
|
1544
1574
|
const widgets = field.acroField.getWidgets();
|
|
@@ -1567,19 +1597,20 @@ async function extractFieldsFromPdf(pdfBytes) {
|
|
|
1567
1597
|
} else {
|
|
1568
1598
|
console.log(`[field-extraction] Unknown field type "${fieldName}" - defaulting required=false`);
|
|
1569
1599
|
}
|
|
1600
|
+
const optionPositionsByPage = /* @__PURE__ */ new Map();
|
|
1601
|
+
if (widgetType === "radio") {
|
|
1602
|
+
for (const w of widgets) {
|
|
1603
|
+
const pageNumber = pageNumberOf(w.P());
|
|
1604
|
+
const r = w.getRectangle();
|
|
1605
|
+
const top = pageHeights[pageNumber - 1] - r.y - r.height;
|
|
1606
|
+
const bucket = optionPositionsByPage.get(pageNumber) ?? [];
|
|
1607
|
+
bucket.push({ x: r.x, y: top, width: r.width, height: r.height });
|
|
1608
|
+
optionPositionsByPage.set(pageNumber, bucket);
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1570
1611
|
for (const widget of widgets) {
|
|
1571
1612
|
const rect = widget.getRectangle();
|
|
1572
|
-
const
|
|
1573
|
-
let pageNumber = 1;
|
|
1574
|
-
if (pageRef) {
|
|
1575
|
-
const pages = pdfDoc.getPages();
|
|
1576
|
-
for (let i = 0; i < pages.length; i++) {
|
|
1577
|
-
if (pages[i].ref === pageRef) {
|
|
1578
|
-
pageNumber = i + 1;
|
|
1579
|
-
break;
|
|
1580
|
-
}
|
|
1581
|
-
}
|
|
1582
|
-
}
|
|
1613
|
+
const pageNumber = pageNumberOf(widget.P());
|
|
1583
1614
|
const pageQualifiedKey = `${baseName}_p${pageNumber}`;
|
|
1584
1615
|
let fieldMetadata = metadata?.fields?.[pageQualifiedKey];
|
|
1585
1616
|
if (!fieldMetadata) {
|
|
@@ -1596,8 +1627,7 @@ async function extractFieldsFromPdf(pdfBytes) {
|
|
|
1596
1627
|
if (fieldMetadata?.options?.length) {
|
|
1597
1628
|
fieldOptions = fieldMetadata.options;
|
|
1598
1629
|
}
|
|
1599
|
-
const
|
|
1600
|
-
const pageHeight = page.getHeight();
|
|
1630
|
+
const pageHeight = pageHeights[pageNumber - 1];
|
|
1601
1631
|
const extractedField = {
|
|
1602
1632
|
name: baseName,
|
|
1603
1633
|
// Use clean base name without __SIGNER__ encoding (signer stored in signer property)
|
|
@@ -1614,6 +1644,9 @@ async function extractFieldsFromPdf(pdfBytes) {
|
|
|
1614
1644
|
signer: signerFromName
|
|
1615
1645
|
// Extract signer from encoded name
|
|
1616
1646
|
};
|
|
1647
|
+
if (widgetType === "radio") {
|
|
1648
|
+
extractedField.optionPositions = optionPositionsByPage.get(pageNumber) ?? [];
|
|
1649
|
+
}
|
|
1617
1650
|
if (fieldMetadata) {
|
|
1618
1651
|
if (fieldMetadata.fieldId) {
|
|
1619
1652
|
extractedField.fieldId = fieldMetadata.fieldId;
|
|
@@ -1659,20 +1692,14 @@ async function extractFieldsFromPdf(pdfBytes) {
|
|
|
1659
1692
|
}
|
|
1660
1693
|
function setSigniphiMetadata(pdfDoc, metadata) {
|
|
1661
1694
|
try {
|
|
1662
|
-
|
|
1663
|
-
const infoObj = pdfDoc.context.lookup(infoRef);
|
|
1664
|
-
if (!infoObj || typeof infoObj.set !== "function") {
|
|
1665
|
-
throw new Error("Info object is not a PDFDict or does not have set method");
|
|
1666
|
-
}
|
|
1667
|
-
const infoDict = infoObj;
|
|
1668
|
-
const metadataString = JSON.stringify(metadata);
|
|
1669
|
-
infoDict.set(pdfLib.PDFName.of("SigniphiMetadata"), pdfLib.PDFHexString.fromText(metadataString));
|
|
1695
|
+
writeMetadata(pdfDoc, metadata, { PDFName: pdfLib.PDFName, PDFHexString: pdfLib.PDFHexString });
|
|
1670
1696
|
} catch (error) {
|
|
1671
1697
|
console.error("Failed to set Signiphi metadata:", error);
|
|
1672
1698
|
throw error;
|
|
1673
1699
|
}
|
|
1674
1700
|
}
|
|
1675
1701
|
var SIGNIPHI_CHECKBOX_LABEL_MARKER = "SiPL_Check";
|
|
1702
|
+
var RADIO_BORDER_WIDTH = 1;
|
|
1676
1703
|
var FormFieldType = /* @__PURE__ */ ((FormFieldType2) => {
|
|
1677
1704
|
FormFieldType2["TEXT"] = "text";
|
|
1678
1705
|
FormFieldType2["SIGNATURE"] = "signature";
|
|
@@ -1969,7 +1996,24 @@ async function addFormFieldsToPdf(pdfBytes, formFields, options = {}) {
|
|
|
1969
1996
|
case "radio" /* RADIO */: {
|
|
1970
1997
|
const encodedName = encodeFieldNameWithSigner(uniqueFieldName, field.assignedSignerEmail);
|
|
1971
1998
|
const radioGroup = form.createRadioGroup(encodedName);
|
|
1972
|
-
if (field.options && field.options.length > 0) {
|
|
1999
|
+
if (field.options && field.options.length > 0 && field.optionPositions?.length === field.options.length) {
|
|
2000
|
+
const optionPositions = field.optionPositions;
|
|
2001
|
+
field.options.forEach((option, index) => {
|
|
2002
|
+
const at = optionPositions[index];
|
|
2003
|
+
radioGroup.addOptionToPage(option, page, {
|
|
2004
|
+
x: at.x + RADIO_BORDER_WIDTH / 2,
|
|
2005
|
+
y: pageHeight - at.y - at.height + RADIO_BORDER_WIDTH / 2,
|
|
2006
|
+
width: at.width - RADIO_BORDER_WIDTH,
|
|
2007
|
+
height: at.height - RADIO_BORDER_WIDTH,
|
|
2008
|
+
borderWidth: RADIO_BORDER_WIDTH,
|
|
2009
|
+
borderColor: pdfLib.rgb(0.5, 0.5, 0.5),
|
|
2010
|
+
backgroundColor: pdfLib.rgb(1, 1, 1)
|
|
2011
|
+
});
|
|
2012
|
+
});
|
|
2013
|
+
if (field.defaultValue && field.options.includes(field.defaultValue)) {
|
|
2014
|
+
radioGroup.select(field.defaultValue);
|
|
2015
|
+
}
|
|
2016
|
+
} else if (field.options && field.options.length > 0) {
|
|
1973
2017
|
const optionHeight = Math.min(20, field.position.height / field.options.length);
|
|
1974
2018
|
const spacing = field.position.height / field.options.length;
|
|
1975
2019
|
field.options.forEach((option, index) => {
|
|
@@ -2309,7 +2353,7 @@ var DraggablePageThumbnail = ({
|
|
|
2309
2353
|
ref,
|
|
2310
2354
|
"data-handler-id": handlerId,
|
|
2311
2355
|
className: cn(
|
|
2312
|
-
"relative group cursor-pointer border-2 rounded-lg overflow-hidden bg-
|
|
2356
|
+
"relative group cursor-pointer border-2 rounded-lg overflow-hidden bg-card transition-all",
|
|
2313
2357
|
isDragging && "opacity-50 scale-95",
|
|
2314
2358
|
isSelected ? "border-primary shadow-md" : "border-gray-200 hover:border-gray-300"
|
|
2315
2359
|
),
|
|
@@ -2337,7 +2381,7 @@ var DraggablePageThumbnail = ({
|
|
|
2337
2381
|
}
|
|
2338
2382
|
},
|
|
2339
2383
|
onContextMenu: handleContextMenu,
|
|
2340
|
-
className: "h-4 w-4 rounded border-
|
|
2384
|
+
className: "h-4 w-4 rounded border-border text-primary focus:ring-primary focus:ring-2 bg-card/90 cursor-pointer"
|
|
2341
2385
|
}
|
|
2342
2386
|
)
|
|
2343
2387
|
}
|
|
@@ -2665,7 +2709,7 @@ var BulkActionBar = ({
|
|
|
2665
2709
|
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Move selected pages to another file" }) })
|
|
2666
2710
|
] }) }),
|
|
2667
2711
|
/* @__PURE__ */ jsxRuntime.jsxs(Select, { onValueChange: onMovePages, children: [
|
|
2668
|
-
/* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { id: "move-select", className: "w-48 bg-
|
|
2712
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { id: "move-select", className: "w-48 bg-card", children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, { placeholder: "Choose file..." }) }),
|
|
2669
2713
|
/* @__PURE__ */ jsxRuntime.jsx(SelectContent, { children: files.map((file) => /* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: file.id, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2670
2714
|
file.type === "pdf" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileText, { className: "h-4 w-4" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Image, { className: "h-4 w-4" }),
|
|
2671
2715
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: file.name })
|
|
@@ -2694,7 +2738,7 @@ var BulkActionBar = ({
|
|
|
2694
2738
|
variant: "outline",
|
|
2695
2739
|
size: "sm",
|
|
2696
2740
|
onClick: onClearSelection,
|
|
2697
|
-
className: "bg-
|
|
2741
|
+
className: "bg-card",
|
|
2698
2742
|
children: "Clear Selection"
|
|
2699
2743
|
}
|
|
2700
2744
|
)
|
|
@@ -3114,12 +3158,19 @@ var PageAssembler = React7.forwardRef(
|
|
|
3114
3158
|
if (sourcePdf && !sourceMetadata) {
|
|
3115
3159
|
console.warn(`[PageAssembler] No readable SigniphiMetadata in "${fileName}"`);
|
|
3116
3160
|
}
|
|
3161
|
+
const pageUsageForFile = filePageUsage.get(fileId) || /* @__PURE__ */ new Map();
|
|
3117
3162
|
const placements = [];
|
|
3118
|
-
|
|
3163
|
+
const pageLandings = [];
|
|
3164
|
+
for (const [srcIndex, pageInstances] of pageUsageForFile) {
|
|
3165
|
+
for (const pageInstance of pageInstances) {
|
|
3166
|
+
const combined = pageIdToNewPageNumber.get(pageInstance.id);
|
|
3167
|
+
if (combined !== void 0) pageLandings.push({ source: srcIndex + 1, combined });
|
|
3168
|
+
}
|
|
3169
|
+
}
|
|
3170
|
+
metadataSources.push({ metadata: sourceMetadata, fields: placements, pages: pageLandings });
|
|
3119
3171
|
console.log(`[PageAssembler] Extracting fields from "${fileName}"`);
|
|
3120
3172
|
const extractedFields = await extractFieldsFromPdf(pdfBytes2);
|
|
3121
3173
|
console.log(`[PageAssembler] Found ${extractedFields.length} fields in "${fileName}"`);
|
|
3122
|
-
const pageUsageForFile = filePageUsage.get(fileId) || /* @__PURE__ */ new Map();
|
|
3123
3174
|
for (const field of extractedFields) {
|
|
3124
3175
|
const originalPage = field.page;
|
|
3125
3176
|
const srcIndex = originalPage - 1;
|
|
@@ -3143,6 +3194,7 @@ var PageAssembler = React7.forwardRef(
|
|
|
3143
3194
|
label: field.label,
|
|
3144
3195
|
required: field.required,
|
|
3145
3196
|
options: field.options,
|
|
3197
|
+
optionPositions: field.optionPositions,
|
|
3146
3198
|
placeholder: field.placeholder,
|
|
3147
3199
|
fieldId: field.fieldId,
|
|
3148
3200
|
assignedSignerEmail: field.signer
|
|
@@ -3247,6 +3299,7 @@ var PageAssembler = React7.forwardRef(
|
|
|
3247
3299
|
label: field.label,
|
|
3248
3300
|
required: field.required,
|
|
3249
3301
|
options: field.options,
|
|
3302
|
+
optionPositions: field.optionPositions,
|
|
3250
3303
|
placeholder: field.placeholder,
|
|
3251
3304
|
assignedSignerEmail: field.assignedSignerEmail
|
|
3252
3305
|
}));
|