@signiphi/page-assembly 0.2.0-beta.13 → 0.2.0-beta.15
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 +83 -14
- package/dist/index.d.ts +83 -14
- package/dist/index.js +102 -53
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +103 -54
- package/dist/index.mjs.map +1 -1
- package/dist/styles/index.css +6 -0
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -14,7 +14,7 @@ import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';
|
|
|
14
14
|
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
|
|
15
15
|
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
|
|
16
16
|
import * as pdfjsLib from 'pdfjs-dist';
|
|
17
|
-
import { PDFDocument, PDFTextField, PDFCheckBox, PDFDropdown, PDFRadioGroup, StandardFonts, rgb,
|
|
17
|
+
import { PDFDocument, PDFName, PDFTextField, PDFCheckBox, PDFDropdown, PDFRadioGroup, PDFHexString, StandardFonts, rgb, PDFString, PDFOperator, PDFOperatorNames, PDFDict, PDFArray, PDFInvalidObject, PDFObjectParser, PDFRawStream, PDFObjectStreamParser, PDFRef, PDFBool, PDFNumber } from 'pdf-lib';
|
|
18
18
|
|
|
19
19
|
// src/components/PageAssembler.tsx
|
|
20
20
|
function cn(...inputs) {
|
|
@@ -1372,6 +1372,55 @@ function downloadPdf(pdfBytes, filename) {
|
|
|
1372
1372
|
a.click();
|
|
1373
1373
|
URL.revokeObjectURL(url);
|
|
1374
1374
|
}
|
|
1375
|
+
function toCombinedPage(entry, page) {
|
|
1376
|
+
if (!entry.position && !entry.radioPositions && !entry.radioOptions) return entry;
|
|
1377
|
+
const carried = { ...entry };
|
|
1378
|
+
if (entry.position) carried.position = { ...entry.position, page };
|
|
1379
|
+
if (entry.radioPositions) carried.radioPositions = entry.radioPositions.map((p) => ({ ...p, page }));
|
|
1380
|
+
if (entry.radioOptions) {
|
|
1381
|
+
carried.radioOptions = entry.radioOptions.map((o) => ({ ...o, position: { ...o.position, page } }));
|
|
1382
|
+
}
|
|
1383
|
+
return carried;
|
|
1384
|
+
}
|
|
1385
|
+
function mergeSourceMetadata(sources) {
|
|
1386
|
+
const merged = { version: "1.0", fields: {}, fieldIdIndex: {} };
|
|
1387
|
+
let radioLabelsBaked;
|
|
1388
|
+
let textLabelsBaked;
|
|
1389
|
+
let checkboxLabelsBaked;
|
|
1390
|
+
for (const source of sources) {
|
|
1391
|
+
const meta = source.metadata;
|
|
1392
|
+
if (!meta || source.fields.length === 0) continue;
|
|
1393
|
+
if (meta.radioLabelsBaked === false) radioLabelsBaked = false;
|
|
1394
|
+
else if (meta.radioLabelsBaked === true && radioLabelsBaked === void 0) radioLabelsBaked = true;
|
|
1395
|
+
if (meta.textLabelsBaked === false) textLabelsBaked = false;
|
|
1396
|
+
else if (meta.textLabelsBaked === true && textLabelsBaked === void 0) textLabelsBaked = true;
|
|
1397
|
+
if (meta.checkboxLabelsBaked === false) checkboxLabelsBaked = false;
|
|
1398
|
+
else if (meta.checkboxLabelsBaked === true && checkboxLabelsBaked === void 0) checkboxLabelsBaked = true;
|
|
1399
|
+
}
|
|
1400
|
+
for (const source of sources) {
|
|
1401
|
+
const meta = source.metadata;
|
|
1402
|
+
if (!meta) continue;
|
|
1403
|
+
const sourceTextLabelsBaked = meta.textLabelsBaked !== false;
|
|
1404
|
+
for (const { name, page } of source.fields) {
|
|
1405
|
+
const key = `${name}_p${page}`;
|
|
1406
|
+
const entry = meta.fields?.[name];
|
|
1407
|
+
if (entry) {
|
|
1408
|
+
const carried = toCombinedPage(entry, page);
|
|
1409
|
+
merged.fields[key] = textLabelsBaked === false && sourceTextLabelsBaked ? { ...carried, textLabelBaked: true } : carried;
|
|
1410
|
+
}
|
|
1411
|
+
const fieldId = meta.fieldIdIndex?.[name];
|
|
1412
|
+
if (fieldId) merged.fieldIdIndex[key] = fieldId;
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
if (radioLabelsBaked !== void 0) merged.radioLabelsBaked = radioLabelsBaked;
|
|
1416
|
+
if (textLabelsBaked !== void 0) merged.textLabelsBaked = textLabelsBaked;
|
|
1417
|
+
if (checkboxLabelsBaked !== void 0) merged.checkboxLabelsBaked = checkboxLabelsBaked;
|
|
1418
|
+
const hasEntries = Object.keys(merged.fields).length > 0;
|
|
1419
|
+
const hasMarkers = radioLabelsBaked !== void 0 || textLabelsBaked !== void 0 || checkboxLabelsBaked !== void 0;
|
|
1420
|
+
if (!hasEntries && !hasMarkers) return null;
|
|
1421
|
+
if (Object.keys(merged.fieldIdIndex).length === 0) delete merged.fieldIdIndex;
|
|
1422
|
+
return merged;
|
|
1423
|
+
}
|
|
1375
1424
|
function getSigniphiMetadata(pdfDoc) {
|
|
1376
1425
|
try {
|
|
1377
1426
|
const infoRef = pdfDoc.context.trailerInfo.Info;
|
|
@@ -1387,12 +1436,17 @@ function getSigniphiMetadata(pdfDoc) {
|
|
|
1387
1436
|
if (!metadataObj) {
|
|
1388
1437
|
return null;
|
|
1389
1438
|
}
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
jsonStr = metadataStr
|
|
1439
|
+
let jsonStr;
|
|
1440
|
+
if (typeof metadataObj.decodeText === "function") {
|
|
1441
|
+
jsonStr = metadataObj.decodeText();
|
|
1442
|
+
} else {
|
|
1443
|
+
const metadataStr = metadataObj.toString();
|
|
1444
|
+
jsonStr = metadataStr;
|
|
1445
|
+
if (metadataStr.startsWith("(") && metadataStr.endsWith(")")) {
|
|
1446
|
+
jsonStr = metadataStr.slice(1, -1);
|
|
1447
|
+
} else if (metadataStr.startsWith("<") && metadataStr.endsWith(">")) {
|
|
1448
|
+
jsonStr = metadataStr.slice(1, -1);
|
|
1449
|
+
}
|
|
1396
1450
|
}
|
|
1397
1451
|
const metadata = JSON.parse(jsonStr);
|
|
1398
1452
|
return metadata;
|
|
@@ -1585,12 +1639,13 @@ function setSigniphiMetadata(pdfDoc, metadata) {
|
|
|
1585
1639
|
}
|
|
1586
1640
|
const infoDict = infoObj;
|
|
1587
1641
|
const metadataString = JSON.stringify(metadata);
|
|
1588
|
-
infoDict.set(PDFName.of("SigniphiMetadata"),
|
|
1642
|
+
infoDict.set(PDFName.of("SigniphiMetadata"), PDFHexString.fromText(metadataString));
|
|
1589
1643
|
} catch (error) {
|
|
1590
1644
|
console.error("Failed to set Signiphi metadata:", error);
|
|
1591
1645
|
throw error;
|
|
1592
1646
|
}
|
|
1593
1647
|
}
|
|
1648
|
+
var SIGNIPHI_CHECKBOX_LABEL_MARKER = "SiPL_Check";
|
|
1594
1649
|
var FormFieldType = /* @__PURE__ */ ((FormFieldType2) => {
|
|
1595
1650
|
FormFieldType2["TEXT"] = "text";
|
|
1596
1651
|
FormFieldType2["SIGNATURE"] = "signature";
|
|
@@ -1676,6 +1731,7 @@ async function addFormFieldsToPdf(pdfBytes, formFields, options = {}) {
|
|
|
1676
1731
|
}
|
|
1677
1732
|
}
|
|
1678
1733
|
const font = await pdfDoc.embedFont(StandardFonts.Helvetica);
|
|
1734
|
+
const bakedCheckboxCaptions = /* @__PURE__ */ new Map();
|
|
1679
1735
|
const fieldNamePages = /* @__PURE__ */ new Map();
|
|
1680
1736
|
for (const field of formFields) {
|
|
1681
1737
|
const baseName = field.name;
|
|
@@ -1772,10 +1828,15 @@ async function addFormFieldsToPdf(pdfBytes, formFields, options = {}) {
|
|
|
1772
1828
|
} catch {
|
|
1773
1829
|
}
|
|
1774
1830
|
if (drawLabels) {
|
|
1775
|
-
const fontSize = Math.min(
|
|
1831
|
+
const fontSize = Math.max(1, Math.min(10, field.position.height * 0.4));
|
|
1776
1832
|
const labelX = pdfX + field.position.width + 5;
|
|
1777
1833
|
const labelY = pdfY + (field.position.height - fontSize) / 2;
|
|
1834
|
+
page.pushOperators(
|
|
1835
|
+
PDFOperator.of(PDFOperatorNames.BeginMarkedContent, [PDFName.of(SIGNIPHI_CHECKBOX_LABEL_MARKER)])
|
|
1836
|
+
);
|
|
1778
1837
|
page.drawText(field.label, { x: labelX, y: labelY, size: fontSize, font, color: rgb(0, 0, 0) });
|
|
1838
|
+
page.pushOperators(PDFOperator.of(PDFOperatorNames.EndMarkedContent));
|
|
1839
|
+
bakedCheckboxCaptions.set(encodedName, field);
|
|
1779
1840
|
}
|
|
1780
1841
|
}
|
|
1781
1842
|
break;
|
|
@@ -1927,8 +1988,28 @@ async function addFormFieldsToPdf(pdfBytes, formFields, options = {}) {
|
|
|
1927
1988
|
console.error(`Error adding field ${field.name}:`, error);
|
|
1928
1989
|
}
|
|
1929
1990
|
}
|
|
1991
|
+
recordBakedCheckboxCaptions(pdfDoc, bakedCheckboxCaptions);
|
|
1930
1992
|
return await pdfDoc.save();
|
|
1931
1993
|
}
|
|
1994
|
+
function recordBakedCheckboxCaptions(pdfDoc, captions) {
|
|
1995
|
+
if (captions.size === 0) return;
|
|
1996
|
+
try {
|
|
1997
|
+
const metadata = getSigniphiMetadata(pdfDoc);
|
|
1998
|
+
if (!metadata) return;
|
|
1999
|
+
metadata.fields ?? (metadata.fields = {});
|
|
2000
|
+
for (const [name, field] of captions) {
|
|
2001
|
+
metadata.fields[name] = {
|
|
2002
|
+
signer: field.assignedSignerEmail,
|
|
2003
|
+
label: field.label,
|
|
2004
|
+
...metadata.fields[name],
|
|
2005
|
+
checkboxCaptionBaked: true
|
|
2006
|
+
};
|
|
2007
|
+
}
|
|
2008
|
+
setSigniphiMetadata(pdfDoc, metadata);
|
|
2009
|
+
} catch (error) {
|
|
2010
|
+
console.warn("Failed to record baked checkbox captions:", error);
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
1932
2013
|
function mapFieldPositionsAfterAssembly(fields, pageMapping) {
|
|
1933
2014
|
return fields.map((field) => {
|
|
1934
2015
|
const mappingKey = String(field.position.page - 1);
|
|
@@ -2898,7 +2979,7 @@ var PageAssembler = forwardRef(
|
|
|
2898
2979
|
setIsLoading(true);
|
|
2899
2980
|
setProcessingStatus("Assembling PDF...");
|
|
2900
2981
|
try {
|
|
2901
|
-
const { PDFDocument: PDFDocument5, PDFName: PDFName4
|
|
2982
|
+
const { PDFDocument: PDFDocument5, PDFName: PDFName4 } = await import('pdf-lib');
|
|
2902
2983
|
const loadedPdfs = /* @__PURE__ */ new Map();
|
|
2903
2984
|
const fileMap = /* @__PURE__ */ new Map();
|
|
2904
2985
|
const pdfFiles = state.files.filter((f) => f.type === "pdf" && f.originalBytes?.length > 0);
|
|
@@ -2977,11 +3058,7 @@ var PageAssembler = forwardRef(
|
|
|
2977
3058
|
}
|
|
2978
3059
|
console.log("[PageAssembler] Starting multi-PDF assembly with field preservation");
|
|
2979
3060
|
const allExtractedFields = [];
|
|
2980
|
-
const
|
|
2981
|
-
version: "1.0",
|
|
2982
|
-
fields: {},
|
|
2983
|
-
fieldIdIndex: {}
|
|
2984
|
-
};
|
|
3061
|
+
const metadataSources = [];
|
|
2985
3062
|
const filePageUsage = /* @__PURE__ */ new Map();
|
|
2986
3063
|
for (const file of state.files) {
|
|
2987
3064
|
for (const page of file.pages) {
|
|
@@ -3006,28 +3083,12 @@ var PageAssembler = forwardRef(
|
|
|
3006
3083
|
}
|
|
3007
3084
|
console.log("[PageAssembler] Page mapping:", Object.fromEntries(pageIdToNewPageNumber));
|
|
3008
3085
|
const extractAndMapFields = async (fileId, fileName, pdfBytes2, sourcePdf) => {
|
|
3009
|
-
|
|
3010
|
-
if (sourcePdf) {
|
|
3011
|
-
|
|
3012
|
-
const sourceInfoRef = sourcePdf.context.trailerInfo.Info;
|
|
3013
|
-
if (sourceInfoRef) {
|
|
3014
|
-
const sourceInfoDict = sourcePdf.context.lookup(sourceInfoRef);
|
|
3015
|
-
if (sourceInfoDict && typeof sourceInfoDict.get === "function") {
|
|
3016
|
-
const metadataObj = sourceInfoDict.get(PDFName4.of("SigniphiMetadata"));
|
|
3017
|
-
if (metadataObj) {
|
|
3018
|
-
const metadataStr = metadataObj.toString();
|
|
3019
|
-
let jsonStr = metadataStr;
|
|
3020
|
-
if (metadataStr.startsWith("(") && metadataStr.endsWith(")")) {
|
|
3021
|
-
jsonStr = metadataStr.slice(1, -1);
|
|
3022
|
-
}
|
|
3023
|
-
sourceMetadata = JSON.parse(jsonStr);
|
|
3024
|
-
}
|
|
3025
|
-
}
|
|
3026
|
-
}
|
|
3027
|
-
} catch (metadataError) {
|
|
3028
|
-
console.warn(`Could not read metadata from PDF ${fileName}:`, metadataError);
|
|
3029
|
-
}
|
|
3086
|
+
const sourceMetadata = sourcePdf ? getSigniphiMetadata(sourcePdf) : null;
|
|
3087
|
+
if (sourcePdf && !sourceMetadata) {
|
|
3088
|
+
console.warn(`[PageAssembler] No readable SigniphiMetadata in "${fileName}"`);
|
|
3030
3089
|
}
|
|
3090
|
+
const placements = [];
|
|
3091
|
+
metadataSources.push({ metadata: sourceMetadata, fields: placements });
|
|
3031
3092
|
console.log(`[PageAssembler] Extracting fields from "${fileName}"`);
|
|
3032
3093
|
const extractedFields = await extractFieldsFromPdf(pdfBytes2);
|
|
3033
3094
|
console.log(`[PageAssembler] Found ${extractedFields.length} fields in "${fileName}"`);
|
|
@@ -3061,13 +3122,7 @@ var PageAssembler = forwardRef(
|
|
|
3061
3122
|
};
|
|
3062
3123
|
allExtractedFields.push(trackedField);
|
|
3063
3124
|
console.log(`[PageAssembler] Mapped field "${field.name}" from page ${originalPage} to page ${newPageNumber}`);
|
|
3064
|
-
|
|
3065
|
-
if (sourceMetadata?.fields?.[field.name]) {
|
|
3066
|
-
mergedMetadata.fields[metadataKey] = sourceMetadata.fields[field.name];
|
|
3067
|
-
}
|
|
3068
|
-
if (sourceMetadata?.fieldIdIndex?.[field.name]) {
|
|
3069
|
-
mergedMetadata.fieldIdIndex[metadataKey] = sourceMetadata.fieldIdIndex[field.name];
|
|
3070
|
-
}
|
|
3125
|
+
placements.push({ name: field.name, page: newPageNumber });
|
|
3071
3126
|
}
|
|
3072
3127
|
}
|
|
3073
3128
|
}
|
|
@@ -3145,21 +3200,15 @@ var PageAssembler = forwardRef(
|
|
|
3145
3200
|
}
|
|
3146
3201
|
}
|
|
3147
3202
|
}
|
|
3148
|
-
|
|
3203
|
+
const mergedMetadata = mergeSourceMetadata(metadataSources);
|
|
3204
|
+
if (mergedMetadata) {
|
|
3149
3205
|
try {
|
|
3150
|
-
|
|
3151
|
-
if (newInfoRef) {
|
|
3152
|
-
const newInfoDict = combinedDoc.context.lookup(newInfoRef);
|
|
3153
|
-
if (newInfoDict && typeof newInfoDict.set === "function") {
|
|
3154
|
-
newInfoDict.set(
|
|
3155
|
-
PDFName4.of("SigniphiMetadata"),
|
|
3156
|
-
PDFString4.of(JSON.stringify(mergedMetadata))
|
|
3157
|
-
);
|
|
3158
|
-
}
|
|
3159
|
-
}
|
|
3206
|
+
setSigniphiMetadata(combinedDoc, mergedMetadata);
|
|
3160
3207
|
} catch (metadataError) {
|
|
3161
3208
|
console.warn("Could not write merged metadata:", metadataError);
|
|
3162
3209
|
}
|
|
3210
|
+
} else {
|
|
3211
|
+
console.warn("[PageAssembler] No source metadata survived the merge; the assembled document will read as unprepared downstream");
|
|
3163
3212
|
}
|
|
3164
3213
|
let pdfBytes = await combinedDoc.save();
|
|
3165
3214
|
if (allExtractedFields.length > 0) {
|