@signiphi/page-assembly 0.2.0-beta.3 → 0.2.0-beta.5

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
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
3
  var React7 = require('react');
6
4
  var reactDnd = require('react-dnd');
7
5
  var reactDndHtml5Backend = require('react-dnd-html5-backend');
@@ -722,6 +720,573 @@ function downloadPdf(pdfBytes, filename) {
722
720
  a.click();
723
721
  URL.revokeObjectURL(url);
724
722
  }
723
+ function getSigniphiMetadata(pdfDoc) {
724
+ try {
725
+ const infoRef = pdfDoc.context.trailerInfo.Info;
726
+ if (!infoRef) {
727
+ return null;
728
+ }
729
+ const infoObj = pdfDoc.context.lookup(infoRef);
730
+ if (!infoObj || typeof infoObj.get !== "function") {
731
+ return null;
732
+ }
733
+ const infoDict = infoObj;
734
+ const metadataObj = infoDict.get(pdfLib.PDFName.of("SigniphiMetadata"));
735
+ if (!metadataObj) {
736
+ return null;
737
+ }
738
+ const metadataStr = metadataObj.toString();
739
+ let jsonStr = metadataStr;
740
+ if (metadataStr.startsWith("(") && metadataStr.endsWith(")")) {
741
+ jsonStr = metadataStr.slice(1, -1);
742
+ } else if (metadataStr.startsWith("<") && metadataStr.endsWith(">")) {
743
+ jsonStr = metadataStr.slice(1, -1);
744
+ }
745
+ const metadata = JSON.parse(jsonStr);
746
+ return metadata;
747
+ } catch (error) {
748
+ console.error("Failed to load Signiphi metadata:", error);
749
+ return null;
750
+ }
751
+ }
752
+ function getBaseFieldName(encodedName) {
753
+ let baseName = encodedName;
754
+ if (baseName.includes("__SIGNER__")) {
755
+ baseName = baseName.split("__SIGNER__")[0];
756
+ }
757
+ if (baseName.includes("__LABEL__")) {
758
+ baseName = baseName.split("__LABEL__")[0];
759
+ }
760
+ if (baseName.includes("__PLACEHOLDER__")) {
761
+ baseName = baseName.split("__PLACEHOLDER__")[0];
762
+ }
763
+ return baseName;
764
+ }
765
+ function getSignerFromFieldName(encodedName) {
766
+ if (encodedName.includes("__SIGNER__")) {
767
+ const parts = encodedName.split("__SIGNER__");
768
+ if (parts[1]) {
769
+ return parts[1].split("__")[0] || parts[1];
770
+ }
771
+ }
772
+ return void 0;
773
+ }
774
+ function determineFieldType(fieldName, widgetType) {
775
+ const baseName = getBaseFieldName(fieldName);
776
+ const nameLower = baseName.toLowerCase();
777
+ if (nameLower.includes("_signature") || nameLower.includes("signature")) {
778
+ return "signature";
779
+ }
780
+ if (nameLower.includes("_initials") || nameLower.includes("initials")) {
781
+ return "initials";
782
+ }
783
+ if (nameLower.includes("_date") || nameLower.includes("date")) {
784
+ return "date";
785
+ }
786
+ if (widgetType === "checkbox" || nameLower.includes("checkbox")) {
787
+ return "checkbox";
788
+ }
789
+ if (widgetType === "radio" || nameLower.includes("radio")) {
790
+ return "radio";
791
+ }
792
+ if (widgetType === "dropdown" || widgetType === "select" || nameLower.includes("dropdown")) {
793
+ return "dropdown";
794
+ }
795
+ return "text";
796
+ }
797
+ async function extractFieldsFromPdf(pdfBytes) {
798
+ const pdfDoc = await pdfLib.PDFDocument.load(pdfBytes);
799
+ const fields = [];
800
+ const metadata = getSigniphiMetadata(pdfDoc);
801
+ const form = pdfDoc.getForm();
802
+ const pdfFields = form.getFields();
803
+ for (const field of pdfFields) {
804
+ const fieldName = field.getName();
805
+ const widgets = field.acroField.getWidgets();
806
+ const baseName = getBaseFieldName(fieldName);
807
+ const signerFromName = getSignerFromFieldName(fieldName);
808
+ let required = false;
809
+ let options;
810
+ let widgetType;
811
+ if (field instanceof pdfLib.PDFTextField) {
812
+ required = field.isRequired();
813
+ console.log(`[field-extraction] PDFTextField "${fieldName}" isRequired=${required}`);
814
+ } else if (field instanceof pdfLib.PDFCheckBox) {
815
+ widgetType = "checkbox";
816
+ required = field.isRequired();
817
+ console.log(`[field-extraction] PDFCheckBox "${fieldName}" isRequired=${required}`);
818
+ } else if (field instanceof pdfLib.PDFDropdown) {
819
+ widgetType = "dropdown";
820
+ required = field.isRequired();
821
+ options = field.getOptions();
822
+ console.log(`[field-extraction] PDFDropdown "${fieldName}" isRequired=${required}`);
823
+ } else if (field instanceof pdfLib.PDFRadioGroup) {
824
+ widgetType = "radio";
825
+ required = field.isRequired();
826
+ options = field.getOptions();
827
+ console.log(`[field-extraction] PDFRadioGroup "${fieldName}" isRequired=${required}`);
828
+ } else {
829
+ console.log(`[field-extraction] Unknown field type "${fieldName}" - defaulting required=false`);
830
+ }
831
+ for (const widget of widgets) {
832
+ const rect = widget.getRectangle();
833
+ const pageRef = widget.P();
834
+ let pageNumber = 1;
835
+ if (pageRef) {
836
+ const pages = pdfDoc.getPages();
837
+ for (let i = 0; i < pages.length; i++) {
838
+ if (pages[i].ref === pageRef) {
839
+ pageNumber = i + 1;
840
+ break;
841
+ }
842
+ }
843
+ }
844
+ const pageQualifiedKey = `${baseName}_p${pageNumber}`;
845
+ let fieldMetadata = metadata?.fields?.[pageQualifiedKey];
846
+ if (!fieldMetadata) {
847
+ fieldMetadata = metadata?.fields?.[fieldName];
848
+ }
849
+ if (!fieldMetadata && baseName !== fieldName) {
850
+ fieldMetadata = metadata?.fields?.[baseName];
851
+ }
852
+ let fieldRequired = required;
853
+ let fieldOptions = options;
854
+ if (fieldMetadata?.required !== void 0) {
855
+ fieldRequired = fieldMetadata.required;
856
+ }
857
+ if (fieldMetadata?.options?.length) {
858
+ fieldOptions = fieldMetadata.options;
859
+ }
860
+ const page = pdfDoc.getPage(pageNumber - 1);
861
+ const pageHeight = page.getHeight();
862
+ const extractedField = {
863
+ name: baseName,
864
+ // Use clean base name without __SIGNER__ encoding (signer stored in signer property)
865
+ type: determineFieldType(fieldName, widgetType),
866
+ // Uses base name internally
867
+ page: pageNumber,
868
+ x: rect.x,
869
+ y: pageHeight - rect.y - rect.height,
870
+ // Convert from PDF coords (bottom-left) to top-left
871
+ width: rect.width,
872
+ height: rect.height,
873
+ required: fieldRequired,
874
+ options: fieldOptions,
875
+ signer: signerFromName
876
+ // Extract signer from encoded name
877
+ };
878
+ if (fieldMetadata) {
879
+ if (fieldMetadata.fieldId) {
880
+ extractedField.fieldId = fieldMetadata.fieldId;
881
+ }
882
+ if (fieldMetadata.label) {
883
+ extractedField.label = fieldMetadata.label;
884
+ }
885
+ if (!extractedField.signer && fieldMetadata.signer) {
886
+ extractedField.signer = fieldMetadata.signer;
887
+ }
888
+ if (fieldMetadata.placeholder) {
889
+ extractedField.placeholder = fieldMetadata.placeholder;
890
+ }
891
+ }
892
+ fields.push(extractedField);
893
+ }
894
+ }
895
+ const deduplicatedFields = fields.reduce((acc, field) => {
896
+ const newBaseName = getBaseFieldName(field.name);
897
+ const existingIndex = acc.findIndex((f) => {
898
+ const existingBaseName = getBaseFieldName(f.name);
899
+ return existingBaseName === newBaseName && f.page === field.page;
900
+ });
901
+ if (existingIndex === -1) {
902
+ acc.push(field);
903
+ } else {
904
+ const existing = acc[existingIndex];
905
+ acc[existingIndex] = {
906
+ ...existing,
907
+ // Prefer non-empty values from either field
908
+ label: existing.label || field.label,
909
+ placeholder: existing.placeholder || field.placeholder,
910
+ required: existing.required || field.required,
911
+ options: existing.options?.length ? existing.options : field.options,
912
+ fieldId: existing.fieldId || field.fieldId,
913
+ // Prefer signer from the field that has it
914
+ signer: existing.signer || field.signer
915
+ };
916
+ }
917
+ return acc;
918
+ }, []);
919
+ return deduplicatedFields;
920
+ }
921
+ function setSigniphiMetadata(pdfDoc, metadata) {
922
+ try {
923
+ const infoRef = pdfDoc.context.trailerInfo.Info;
924
+ const infoObj = pdfDoc.context.lookup(infoRef);
925
+ if (!infoObj || typeof infoObj.set !== "function") {
926
+ throw new Error("Info object is not a PDFDict or does not have set method");
927
+ }
928
+ const infoDict = infoObj;
929
+ const metadataString = JSON.stringify(metadata);
930
+ infoDict.set(pdfLib.PDFName.of("SigniphiMetadata"), pdfLib.PDFString.of(metadataString));
931
+ } catch (error) {
932
+ console.error("Failed to set Signiphi metadata:", error);
933
+ throw error;
934
+ }
935
+ }
936
+ var FormFieldType = /* @__PURE__ */ ((FormFieldType2) => {
937
+ FormFieldType2["TEXT"] = "text";
938
+ FormFieldType2["SIGNATURE"] = "signature";
939
+ FormFieldType2["INITIALS"] = "initials";
940
+ FormFieldType2["DATE"] = "date";
941
+ FormFieldType2["CHECKBOX"] = "checkbox";
942
+ FormFieldType2["RADIO"] = "radio";
943
+ FormFieldType2["DROPDOWN"] = "dropdown";
944
+ FormFieldType2["TEXT_LABEL"] = "text_label";
945
+ return FormFieldType2;
946
+ })(FormFieldType || {});
947
+ function parseEncodedFieldName(encodedName) {
948
+ let baseName = encodedName;
949
+ let label;
950
+ let signer;
951
+ if (baseName.includes("__SIGNER__")) {
952
+ const parts = baseName.split("__SIGNER__");
953
+ baseName = parts[0];
954
+ signer = parts[1];
955
+ }
956
+ if (baseName.includes("__LABEL__")) {
957
+ const parts = baseName.split("__LABEL__");
958
+ baseName = parts[0];
959
+ label = parts[1];
960
+ }
961
+ if (baseName.includes("__PLACEHOLDER__")) {
962
+ const parts = baseName.split("__PLACEHOLDER__");
963
+ baseName = parts[0];
964
+ }
965
+ return { baseName, label, signer };
966
+ }
967
+ function encodeFieldNameWithSigner(fieldName, signerEmail) {
968
+ const { baseName, signer: existingSigner } = parseEncodedFieldName(fieldName);
969
+ const finalSigner = existingSigner || signerEmail;
970
+ if (!finalSigner) return baseName;
971
+ return `${baseName}__SIGNER__${finalSigner}`;
972
+ }
973
+ function encodeFieldNameWithLabelAndSigner(fieldName, label, signerEmail) {
974
+ const { baseName, label: existingLabel, signer: existingSigner } = parseEncodedFieldName(fieldName);
975
+ const finalLabel = existingLabel || label;
976
+ const finalSigner = existingSigner || signerEmail;
977
+ let name = baseName;
978
+ if (finalLabel && finalLabel.trim()) {
979
+ name = `${name}__LABEL__${finalLabel.trim()}`;
980
+ }
981
+ if (finalSigner) {
982
+ name = `${name}__SIGNER__${finalSigner}`;
983
+ }
984
+ return name;
985
+ }
986
+ async function addFormFieldsToPdf(pdfBytes, formFields, options = {}) {
987
+ const { removeExistingFields = false, drawLabels = false } = options;
988
+ const pdfDoc = await pdfLib.PDFDocument.load(pdfBytes);
989
+ const form = pdfDoc.getForm();
990
+ const pages = pdfDoc.getPages();
991
+ if (removeExistingFields) {
992
+ const existingFields = form.getFields();
993
+ for (const field of existingFields) {
994
+ try {
995
+ const acroField = field.acroField;
996
+ if (acroField && typeof acroField.getWidgets === "function") {
997
+ let widgets = acroField.getWidgets();
998
+ let safetyCounter = 0;
999
+ const maxIterations = widgets.length + 5;
1000
+ while (widgets.length > 0 && safetyCounter < maxIterations) {
1001
+ try {
1002
+ acroField.removeWidget(widgets.length - 1);
1003
+ widgets = acroField.getWidgets();
1004
+ } catch {
1005
+ break;
1006
+ }
1007
+ safetyCounter++;
1008
+ }
1009
+ }
1010
+ form.removeField(field);
1011
+ } catch (error) {
1012
+ console.warn(`Failed to remove field ${field.getName()}:`, error);
1013
+ try {
1014
+ form.removeField(field);
1015
+ } catch {
1016
+ }
1017
+ }
1018
+ }
1019
+ }
1020
+ const font = await pdfDoc.embedFont(pdfLib.StandardFonts.Helvetica);
1021
+ const fieldNamePages = /* @__PURE__ */ new Map();
1022
+ for (const field of formFields) {
1023
+ const baseName = field.name;
1024
+ if (!fieldNamePages.has(baseName)) {
1025
+ fieldNamePages.set(baseName, /* @__PURE__ */ new Set());
1026
+ }
1027
+ fieldNamePages.get(baseName).add(field.position.page);
1028
+ }
1029
+ const conflictingNames = /* @__PURE__ */ new Set();
1030
+ for (const [name, pages2] of fieldNamePages) {
1031
+ if (pages2.size > 1) {
1032
+ conflictingNames.add(name);
1033
+ }
1034
+ }
1035
+ for (const field of formFields) {
1036
+ const pageIndex = field.position.page - 1;
1037
+ if (pageIndex < 0 || pageIndex >= pages.length) {
1038
+ console.warn(`Field ${field.name} references page ${field.position.page} but PDF only has ${pages.length} pages`);
1039
+ continue;
1040
+ }
1041
+ const page = pages[pageIndex];
1042
+ const { height: pageHeight } = page.getSize();
1043
+ const pdfX = field.position.x;
1044
+ const pdfY = pageHeight - field.position.y - field.position.height;
1045
+ const uniqueFieldName = conflictingNames.has(field.name) ? `${field.name}_p${field.position.page}` : field.name;
1046
+ try {
1047
+ const fieldType = typeof field.type === "string" ? field.type : field.type;
1048
+ switch (fieldType) {
1049
+ case "text" /* TEXT */: {
1050
+ const encodedName = encodeFieldNameWithSigner(uniqueFieldName, field.assignedSignerEmail);
1051
+ const textField = form.createTextField(encodedName);
1052
+ textField.addToPage(page, {
1053
+ x: pdfX,
1054
+ y: pdfY,
1055
+ width: field.position.width,
1056
+ height: field.position.height,
1057
+ borderColor: pdfLib.rgb(0.5, 0.5, 0.5),
1058
+ backgroundColor: pdfLib.rgb(1, 1, 1)
1059
+ });
1060
+ if (field.label && field.label.trim()) {
1061
+ textField.acroField.dict.set(pdfLib.PDFName.of("TU"), pdfLib.PDFString.of(field.label));
1062
+ if (drawLabels) {
1063
+ const fontSize = Math.min(10, field.position.height * 0.4);
1064
+ const labelY = pdfY + field.position.height + 5;
1065
+ page.drawText(field.label, { x: pdfX, y: labelY, size: fontSize, font, color: pdfLib.rgb(0, 0, 0) });
1066
+ }
1067
+ }
1068
+ if (field.defaultValue && field.defaultValue.trim()) {
1069
+ textField.setText(field.defaultValue);
1070
+ }
1071
+ if (field.fontSize && field.fontSize >= 8 && field.fontSize <= 72) {
1072
+ try {
1073
+ textField.setFontSize(field.fontSize);
1074
+ } catch {
1075
+ }
1076
+ }
1077
+ if (field.multiline) {
1078
+ try {
1079
+ textField.enableMultiline();
1080
+ } catch {
1081
+ }
1082
+ }
1083
+ if (field.maxLength && field.maxLength > 0) {
1084
+ try {
1085
+ textField.setMaxLength(field.maxLength);
1086
+ } catch {
1087
+ }
1088
+ }
1089
+ if (field.required) {
1090
+ textField.enableRequired();
1091
+ }
1092
+ break;
1093
+ }
1094
+ case "checkbox" /* CHECKBOX */: {
1095
+ const encodedName = encodeFieldNameWithSigner(uniqueFieldName, field.assignedSignerEmail);
1096
+ const checkBox = form.createCheckBox(encodedName);
1097
+ checkBox.addToPage(page, {
1098
+ x: pdfX,
1099
+ y: pdfY,
1100
+ width: field.position.width,
1101
+ height: field.position.height,
1102
+ borderColor: pdfLib.rgb(0.5, 0.5, 0.5),
1103
+ backgroundColor: pdfLib.rgb(1, 1, 1)
1104
+ });
1105
+ if (field.defaultValue === "true" || field.defaultValue === "checked") {
1106
+ checkBox.check();
1107
+ }
1108
+ if (field.required) {
1109
+ checkBox.enableRequired();
1110
+ }
1111
+ if (field.label && field.label.trim()) {
1112
+ try {
1113
+ checkBox.acroField.dict.set(pdfLib.PDFName.of("TU"), pdfLib.PDFString.of(field.label));
1114
+ } catch {
1115
+ }
1116
+ if (drawLabels) {
1117
+ const fontSize = Math.min(12, field.position.height * 0.6);
1118
+ const labelX = pdfX + field.position.width + 5;
1119
+ const labelY = pdfY + (field.position.height - fontSize) / 2;
1120
+ page.drawText(field.label, { x: labelX, y: labelY, size: fontSize, font, color: pdfLib.rgb(0, 0, 0) });
1121
+ }
1122
+ }
1123
+ break;
1124
+ }
1125
+ case "signature" /* SIGNATURE */: {
1126
+ const baseWithSuffix = field.name.endsWith("_signature") ? field.name : `${field.name}_signature`;
1127
+ const finalName = conflictingNames.has(field.name) ? `${baseWithSuffix}_p${field.position.page}` : baseWithSuffix;
1128
+ const encodedName = encodeFieldNameWithLabelAndSigner(finalName, field.label, field.assignedSignerEmail);
1129
+ const sigField = form.createTextField(encodedName);
1130
+ sigField.addToPage(page, {
1131
+ x: pdfX,
1132
+ y: pdfY,
1133
+ width: field.position.width,
1134
+ height: field.position.height,
1135
+ borderColor: pdfLib.rgb(0, 0, 1),
1136
+ backgroundColor: pdfLib.rgb(0.9, 0.9, 1)
1137
+ });
1138
+ if (field.label && field.label.trim()) {
1139
+ sigField.acroField.dict.set(pdfLib.PDFName.of("TU"), pdfLib.PDFString.of(field.label));
1140
+ }
1141
+ sigField.enableReadOnly();
1142
+ if (field.required) {
1143
+ sigField.enableRequired();
1144
+ }
1145
+ break;
1146
+ }
1147
+ case "initials" /* INITIALS */: {
1148
+ const baseWithSuffix = field.name.endsWith("_initials") ? field.name : `${field.name}_initials`;
1149
+ const finalName = conflictingNames.has(field.name) ? `${baseWithSuffix}_p${field.position.page}` : baseWithSuffix;
1150
+ const encodedName = encodeFieldNameWithLabelAndSigner(finalName, field.label, field.assignedSignerEmail);
1151
+ const initField = form.createTextField(encodedName);
1152
+ initField.addToPage(page, {
1153
+ x: pdfX,
1154
+ y: pdfY,
1155
+ width: field.position.width,
1156
+ height: field.position.height,
1157
+ borderColor: pdfLib.rgb(0.5, 0, 0.5),
1158
+ backgroundColor: pdfLib.rgb(1, 0.95, 1)
1159
+ });
1160
+ if (field.label && field.label.trim()) {
1161
+ initField.acroField.dict.set(pdfLib.PDFName.of("TU"), pdfLib.PDFString.of(field.label));
1162
+ }
1163
+ initField.enableReadOnly();
1164
+ if (field.required) {
1165
+ initField.enableRequired();
1166
+ }
1167
+ break;
1168
+ }
1169
+ case "date" /* DATE */: {
1170
+ const baseWithSuffix = field.name.endsWith("_date") ? field.name : `${field.name}_date`;
1171
+ const finalName = conflictingNames.has(field.name) ? `${baseWithSuffix}_p${field.position.page}` : baseWithSuffix;
1172
+ const encodedName = encodeFieldNameWithLabelAndSigner(finalName, field.label, field.assignedSignerEmail);
1173
+ const dateField = form.createTextField(encodedName);
1174
+ dateField.addToPage(page, {
1175
+ x: pdfX,
1176
+ y: pdfY,
1177
+ width: field.position.width,
1178
+ height: field.position.height,
1179
+ borderColor: pdfLib.rgb(0, 0.5, 0),
1180
+ backgroundColor: pdfLib.rgb(0.95, 1, 0.95)
1181
+ });
1182
+ if (field.label && field.label.trim()) {
1183
+ dateField.acroField.dict.set(pdfLib.PDFName.of("TU"), pdfLib.PDFString.of(field.label));
1184
+ }
1185
+ if (field.required) {
1186
+ dateField.enableRequired();
1187
+ }
1188
+ break;
1189
+ }
1190
+ case "dropdown" /* DROPDOWN */: {
1191
+ const encodedName = encodeFieldNameWithSigner(uniqueFieldName, field.assignedSignerEmail);
1192
+ const dropdown = form.createDropdown(encodedName);
1193
+ dropdown.addToPage(page, {
1194
+ x: pdfX,
1195
+ y: pdfY,
1196
+ width: field.position.width,
1197
+ height: field.position.height,
1198
+ borderColor: pdfLib.rgb(0.5, 0.5, 0.5),
1199
+ backgroundColor: pdfLib.rgb(1, 1, 1)
1200
+ });
1201
+ if (field.options && field.options.length > 0) {
1202
+ dropdown.addOptions(field.options);
1203
+ if (field.defaultValue && field.options.includes(field.defaultValue)) {
1204
+ dropdown.select(field.defaultValue);
1205
+ }
1206
+ }
1207
+ if (field.label && field.label.trim()) {
1208
+ try {
1209
+ dropdown.acroField.dict.set(pdfLib.PDFName.of("TU"), pdfLib.PDFString.of(field.label));
1210
+ } catch {
1211
+ }
1212
+ if (drawLabels) {
1213
+ const fontSize = Math.min(10, field.position.height * 0.4);
1214
+ const labelY = pdfY + field.position.height + 5;
1215
+ page.drawText(field.label, { x: pdfX, y: labelY, size: fontSize, font, color: pdfLib.rgb(0, 0, 0) });
1216
+ }
1217
+ }
1218
+ if (field.required) {
1219
+ dropdown.enableRequired();
1220
+ }
1221
+ break;
1222
+ }
1223
+ case "radio" /* RADIO */: {
1224
+ const encodedName = encodeFieldNameWithSigner(uniqueFieldName, field.assignedSignerEmail);
1225
+ const radioGroup = form.createRadioGroup(encodedName);
1226
+ if (field.options && field.options.length > 0) {
1227
+ const optionHeight = Math.min(20, field.position.height / field.options.length);
1228
+ const spacing = field.position.height / field.options.length;
1229
+ field.options.forEach((option, index) => {
1230
+ const optionY = pdfY + field.position.height - (index + 1) * spacing + (spacing - optionHeight) / 2;
1231
+ radioGroup.addOptionToPage(option, page, {
1232
+ x: pdfX,
1233
+ y: optionY,
1234
+ width: optionHeight,
1235
+ height: optionHeight,
1236
+ borderColor: pdfLib.rgb(0.5, 0.5, 0.5),
1237
+ backgroundColor: pdfLib.rgb(1, 1, 1)
1238
+ });
1239
+ if (drawLabels) {
1240
+ const labelX = pdfX + optionHeight + 5;
1241
+ const labelY = optionY + optionHeight / 4;
1242
+ page.drawText(option, { x: labelX, y: labelY, size: 10, font, color: pdfLib.rgb(0, 0, 0) });
1243
+ }
1244
+ });
1245
+ if (field.defaultValue && field.options.includes(field.defaultValue)) {
1246
+ radioGroup.select(field.defaultValue);
1247
+ }
1248
+ }
1249
+ if (field.label && field.label.trim()) {
1250
+ try {
1251
+ radioGroup.acroField.dict.set(pdfLib.PDFName.of("TU"), pdfLib.PDFString.of(field.label));
1252
+ } catch {
1253
+ }
1254
+ if (drawLabels) {
1255
+ const fontSize = 10;
1256
+ const labelY = pdfY + field.position.height + 5;
1257
+ page.drawText(field.label, { x: pdfX, y: labelY, size: fontSize, font, color: pdfLib.rgb(0, 0, 0) });
1258
+ }
1259
+ }
1260
+ if (field.required) {
1261
+ radioGroup.enableRequired();
1262
+ }
1263
+ break;
1264
+ }
1265
+ default:
1266
+ console.warn(`Unknown field type: ${fieldType} for field ${field.name}`);
1267
+ }
1268
+ } catch (error) {
1269
+ console.error(`Error adding field ${field.name}:`, error);
1270
+ }
1271
+ }
1272
+ return await pdfDoc.save();
1273
+ }
1274
+ function mapFieldPositionsAfterAssembly(fields, pageMapping) {
1275
+ return fields.map((field) => {
1276
+ const mappingKey = String(field.position.page - 1);
1277
+ const newPageNumber = pageMapping.get(mappingKey);
1278
+ if (newPageNumber !== void 0) {
1279
+ return {
1280
+ ...field,
1281
+ position: { ...field.position, page: newPageNumber }
1282
+ };
1283
+ }
1284
+ return field;
1285
+ }).filter((field) => {
1286
+ const mappingKey = String(field.position.page - 1);
1287
+ return pageMapping.has(mappingKey) || pageMapping.size === 0;
1288
+ });
1289
+ }
725
1290
  var isMultiSelectModifier = (event) => {
726
1291
  return event.ctrlKey || event.metaKey;
727
1292
  };
@@ -1354,6 +1919,7 @@ var PageAssembler = React7.forwardRef(
1354
1919
  const processedInitialPdfRef = React7.useRef(null);
1355
1920
  const isDraggingPage = React7.useRef(false);
1356
1921
  const onChangeRef = React7.useRef(onChange);
1922
+ const originalBytesCache = React7.useRef(/* @__PURE__ */ new Map());
1357
1923
  const summary = React7.useMemo(() => ({
1358
1924
  totalFiles: state.files.length,
1359
1925
  totalPages: state.files.reduce((sum, file) => sum + file.pages.length, 0)
@@ -1423,6 +1989,7 @@ var PageAssembler = React7.forwardRef(
1423
1989
  originalFileId: fileId
1424
1990
  }))
1425
1991
  };
1992
+ originalBytesCache.current.set(fileId, originalBytes);
1426
1993
  dispatch({ type: "ADD_FILE", payload: file });
1427
1994
  } catch (error) {
1428
1995
  console.error("Error processing initial PDF:", error);
@@ -1475,6 +2042,7 @@ var PageAssembler = React7.forwardRef(
1475
2042
  const arrayBuffer = await file.arrayBuffer();
1476
2043
  const pdfBytes = new Uint8Array(arrayBuffer);
1477
2044
  fileInfo.originalBytes = new Uint8Array(pdfBytes);
2045
+ originalBytesCache.current.set(fileInfo.id, fileInfo.originalBytes);
1478
2046
  const defensiveCopy = new Uint8Array(pdfBytes);
1479
2047
  const pages = await pdfToImages(defensiveCopy, { hideFormFields: true });
1480
2048
  fileInfo.pages = pages.map((page, index) => ({
@@ -1491,6 +2059,7 @@ var PageAssembler = React7.forwardRef(
1491
2059
  const arrayBuffer = await file.arrayBuffer();
1492
2060
  const imageBytes = new Uint8Array(arrayBuffer);
1493
2061
  fileInfo.originalBytes = new Uint8Array(imageBytes);
2062
+ originalBytesCache.current.set(fileInfo.id, fileInfo.originalBytes);
1494
2063
  const thumbUrl = URL.createObjectURL(file);
1495
2064
  const img = new Image();
1496
2065
  const dimensions = await new Promise((resolve) => {
@@ -1618,21 +2187,205 @@ var PageAssembler = React7.forwardRef(
1618
2187
  setIsLoading(true);
1619
2188
  setProcessingStatus("Assembling PDF...");
1620
2189
  try {
1621
- const { PDFDocument: PDFDocument2 } = await import('pdf-lib');
1622
- const combinedDoc = await PDFDocument2.create();
2190
+ const { PDFDocument: PDFDocument4, PDFName: PDFName3, PDFString: PDFString3 } = await import('pdf-lib');
1623
2191
  const loadedPdfs = /* @__PURE__ */ new Map();
1624
2192
  const fileMap = /* @__PURE__ */ new Map();
2193
+ const pdfFiles = state.files.filter((f) => f.type === "pdf" && f.originalBytes?.length > 0);
2194
+ const imageFiles = state.files.filter((f) => f.type === "image");
2195
+ const isSinglePdfScenario = pdfFiles.length === 1 && imageFiles.length === 0;
1625
2196
  for (const file of state.files) {
1626
2197
  fileMap.set(file.id, file);
1627
2198
  if (file.type === "pdf" && file.originalBytes && file.originalBytes.length > 0) {
1628
2199
  try {
1629
- const sourcePdf = await PDFDocument2.load(file.originalBytes);
2200
+ const sourcePdf = await PDFDocument4.load(file.originalBytes);
1630
2201
  loadedPdfs.set(file.id, sourcePdf);
1631
2202
  } catch (error) {
1632
2203
  console.error(`Error loading PDF for file ${file.id}:`, error);
1633
2204
  }
1634
2205
  }
1635
2206
  }
2207
+ const referencedFileIds = /* @__PURE__ */ new Set();
2208
+ for (const file of state.files) {
2209
+ for (const page of file.pages) {
2210
+ const originalFileId = page.originalFileId || file.id;
2211
+ if (!loadedPdfs.has(originalFileId)) {
2212
+ referencedFileIds.add(originalFileId);
2213
+ }
2214
+ }
2215
+ }
2216
+ for (const fileId of referencedFileIds) {
2217
+ const cachedBytes = originalBytesCache.current.get(fileId);
2218
+ if (cachedBytes && cachedBytes.length > 0) {
2219
+ try {
2220
+ console.log(`[PageAssembler] Loading PDF from cache for deleted file ${fileId}`);
2221
+ const sourcePdf = await PDFDocument4.load(cachedBytes);
2222
+ loadedPdfs.set(fileId, sourcePdf);
2223
+ } catch (error) {
2224
+ console.error(`Error loading cached PDF for file ${fileId}:`, error);
2225
+ }
2226
+ }
2227
+ }
2228
+ console.log("[PageAssembler] Checking single PDF scenario:", {
2229
+ isSinglePdfScenario,
2230
+ pdfFilesCount: pdfFiles.length,
2231
+ imageFilesCount: imageFiles.length
2232
+ });
2233
+ if (isSinglePdfScenario && pdfFiles[0]) {
2234
+ const sourceFile = pdfFiles[0];
2235
+ const sourcePdf = loadedPdfs.get(sourceFile.id);
2236
+ console.log("[PageAssembler] Single PDF check:", {
2237
+ sourcePdfLoaded: !!sourcePdf,
2238
+ pagesCount: sourceFile.pages.length,
2239
+ originalBytesLength: sourceFile.originalBytes?.length
2240
+ });
2241
+ if (sourcePdf) {
2242
+ const desiredPageIndices = sourceFile.pages.map((p) => p.srcIndex);
2243
+ const originalPageCount = sourcePdf.getPageCount();
2244
+ const allPagesFromSameFile = sourceFile.pages.every(
2245
+ (p) => (p.originalFileId || sourceFile.id) === sourceFile.id
2246
+ );
2247
+ const isOriginalOrder = desiredPageIndices.length === originalPageCount && desiredPageIndices.every((idx, i) => idx === i);
2248
+ console.log("[PageAssembler] Original bytes check:", {
2249
+ allPagesFromSameFile,
2250
+ isOriginalOrder,
2251
+ desiredPageIndices,
2252
+ originalPageCount
2253
+ });
2254
+ if (allPagesFromSameFile && isOriginalOrder) {
2255
+ console.log("[PageAssembler] \u2705 Returning ORIGINAL bytes (form fields preserved)");
2256
+ const arrayBuffer2 = sourceFile.originalBytes.buffer.slice(
2257
+ sourceFile.originalBytes.byteOffset,
2258
+ sourceFile.originalBytes.byteOffset + sourceFile.originalBytes.byteLength
2259
+ );
2260
+ const blob2 = new Blob([arrayBuffer2], { type: "application/pdf" });
2261
+ return blob2;
2262
+ } else {
2263
+ console.log("[PageAssembler] \u26A0\uFE0F Pages modified, will use copyPages (form fields may be lost)");
2264
+ }
2265
+ }
2266
+ }
2267
+ console.log("[PageAssembler] Starting multi-PDF assembly with field preservation");
2268
+ const allExtractedFields = [];
2269
+ const mergedMetadata = {
2270
+ version: "1.0",
2271
+ fields: {},
2272
+ fieldIdIndex: {}
2273
+ };
2274
+ const filePageUsage = /* @__PURE__ */ new Map();
2275
+ for (const file of state.files) {
2276
+ for (const page of file.pages) {
2277
+ const originalFileId = page.originalFileId || file.id;
2278
+ if (!filePageUsage.has(originalFileId)) {
2279
+ filePageUsage.set(originalFileId, /* @__PURE__ */ new Map());
2280
+ }
2281
+ const pageUsage = filePageUsage.get(originalFileId);
2282
+ if (!pageUsage.has(page.srcIndex)) {
2283
+ pageUsage.set(page.srcIndex, []);
2284
+ }
2285
+ pageUsage.get(page.srcIndex).push(page);
2286
+ }
2287
+ }
2288
+ let combinedPageIndex = 0;
2289
+ const pageIdToNewPageNumber = /* @__PURE__ */ new Map();
2290
+ for (const file of state.files) {
2291
+ for (const page of file.pages) {
2292
+ combinedPageIndex++;
2293
+ pageIdToNewPageNumber.set(page.id, combinedPageIndex);
2294
+ }
2295
+ }
2296
+ console.log("[PageAssembler] Page mapping:", Object.fromEntries(pageIdToNewPageNumber));
2297
+ const extractAndMapFields = async (fileId, fileName, pdfBytes2, sourcePdf) => {
2298
+ let sourceMetadata = null;
2299
+ if (sourcePdf) {
2300
+ try {
2301
+ const sourceInfoRef = sourcePdf.context.trailerInfo.Info;
2302
+ if (sourceInfoRef) {
2303
+ const sourceInfoDict = sourcePdf.context.lookup(sourceInfoRef);
2304
+ if (sourceInfoDict && typeof sourceInfoDict.get === "function") {
2305
+ const metadataObj = sourceInfoDict.get(PDFName3.of("SigniphiMetadata"));
2306
+ if (metadataObj) {
2307
+ const metadataStr = metadataObj.toString();
2308
+ let jsonStr = metadataStr;
2309
+ if (metadataStr.startsWith("(") && metadataStr.endsWith(")")) {
2310
+ jsonStr = metadataStr.slice(1, -1);
2311
+ }
2312
+ sourceMetadata = JSON.parse(jsonStr);
2313
+ }
2314
+ }
2315
+ }
2316
+ } catch (metadataError) {
2317
+ console.warn(`Could not read metadata from PDF ${fileName}:`, metadataError);
2318
+ }
2319
+ }
2320
+ console.log(`[PageAssembler] Extracting fields from "${fileName}"`);
2321
+ const extractedFields = await extractFieldsFromPdf(pdfBytes2);
2322
+ console.log(`[PageAssembler] Found ${extractedFields.length} fields in "${fileName}"`);
2323
+ const pageUsageForFile = filePageUsage.get(fileId) || /* @__PURE__ */ new Map();
2324
+ for (const field of extractedFields) {
2325
+ const originalPage = field.page;
2326
+ const srcIndex = originalPage - 1;
2327
+ const pageInstances = pageUsageForFile.get(srcIndex) || [];
2328
+ for (const pageInstance of pageInstances) {
2329
+ const newPageNumber = pageIdToNewPageNumber.get(pageInstance.id);
2330
+ if (newPageNumber !== void 0) {
2331
+ const trackedField = {
2332
+ name: field.name,
2333
+ type: field.type,
2334
+ position: {
2335
+ x: field.x,
2336
+ y: field.y,
2337
+ width: field.width,
2338
+ height: field.height,
2339
+ page: newPageNumber
2340
+ // Updated to new page number
2341
+ },
2342
+ pageId: pageInstance.id,
2343
+ originalFileId: fileId,
2344
+ label: field.label,
2345
+ required: field.required,
2346
+ options: field.options,
2347
+ placeholder: field.placeholder,
2348
+ fieldId: field.fieldId,
2349
+ assignedSignerEmail: field.signer
2350
+ };
2351
+ allExtractedFields.push(trackedField);
2352
+ console.log(`[PageAssembler] Mapped field "${field.name}" from page ${originalPage} to page ${newPageNumber}`);
2353
+ const metadataKey = `${field.name}_p${newPageNumber}`;
2354
+ if (sourceMetadata?.fields?.[field.name]) {
2355
+ mergedMetadata.fields[metadataKey] = sourceMetadata.fields[field.name];
2356
+ }
2357
+ if (sourceMetadata?.fieldIdIndex?.[field.name]) {
2358
+ mergedMetadata.fieldIdIndex[metadataKey] = sourceMetadata.fieldIdIndex[field.name];
2359
+ }
2360
+ }
2361
+ }
2362
+ }
2363
+ };
2364
+ const processedFileIds = /* @__PURE__ */ new Set();
2365
+ for (const file of state.files) {
2366
+ if (file.type !== "pdf" || !file.originalBytes || file.originalBytes.length === 0) continue;
2367
+ try {
2368
+ await extractAndMapFields(file.id, file.name, file.originalBytes, loadedPdfs.get(file.id));
2369
+ processedFileIds.add(file.id);
2370
+ } catch (extractError) {
2371
+ console.error(`Error extracting fields from ${file.name}:`, extractError);
2372
+ }
2373
+ }
2374
+ for (const fileId of referencedFileIds) {
2375
+ if (processedFileIds.has(fileId)) continue;
2376
+ const cachedBytes = originalBytesCache.current.get(fileId);
2377
+ if (cachedBytes && cachedBytes.length > 0) {
2378
+ try {
2379
+ console.log(`[PageAssembler] Extracting fields from cached file ${fileId}`);
2380
+ await extractAndMapFields(fileId, `Cached file ${fileId}`, cachedBytes, loadedPdfs.get(fileId));
2381
+ } catch (extractError) {
2382
+ console.error(`Error extracting fields from cached file ${fileId}:`, extractError);
2383
+ }
2384
+ }
2385
+ }
2386
+ console.log(`[PageAssembler] Total extracted fields: ${allExtractedFields.length}`);
2387
+ console.log(`[PageAssembler] Field names:`, allExtractedFields.map((f) => `${f.name} (page ${f.position.page})`));
2388
+ const combinedDoc = await PDFDocument4.create();
1636
2389
  for (const file of state.files) {
1637
2390
  if (file.pages.length === 0) continue;
1638
2391
  for (const page of file.pages) {
@@ -1652,7 +2405,17 @@ var PageAssembler = React7.forwardRef(
1652
2405
  if (page.srcIndex >= 0 && page.srcIndex < sourcePageCount) {
1653
2406
  try {
1654
2407
  const copiedPages = await combinedDoc.copyPages(sourcePdf, [page.srcIndex]);
1655
- copiedPages.forEach((copiedPage) => combinedDoc.addPage(copiedPage));
2408
+ copiedPages.forEach((copiedPage) => {
2409
+ try {
2410
+ const annotsRef = copiedPage.node.get(PDFName3.of("Annots"));
2411
+ if (annotsRef) {
2412
+ copiedPage.node.delete(PDFName3.of("Annots"));
2413
+ }
2414
+ } catch (annotError) {
2415
+ console.warn("Could not remove annotations from copied page:", annotError);
2416
+ }
2417
+ combinedDoc.addPage(copiedPage);
2418
+ });
1656
2419
  } catch (copyError) {
1657
2420
  console.error(`Error copying page ${page.srcIndex}:`, copyError);
1658
2421
  throw new Error(`Failed to copy page: ${copyError instanceof Error ? copyError.message : String(copyError)}`);
@@ -1661,7 +2424,7 @@ var PageAssembler = React7.forwardRef(
1661
2424
  } else if (sourceFile.type === "image" && sourceFile.originalFile) {
1662
2425
  try {
1663
2426
  const imagePdfBytes = await imageToPdf(sourceFile.originalFile);
1664
- const imagePdf = await PDFDocument2.load(imagePdfBytes);
2427
+ const imagePdf = await PDFDocument4.load(imagePdfBytes);
1665
2428
  const copiedPages = await combinedDoc.copyPages(imagePdf, [0]);
1666
2429
  copiedPages.forEach((copiedPage) => combinedDoc.addPage(copiedPage));
1667
2430
  } catch (imageError) {
@@ -1671,7 +2434,46 @@ var PageAssembler = React7.forwardRef(
1671
2434
  }
1672
2435
  }
1673
2436
  }
1674
- const pdfBytes = await combinedDoc.save();
2437
+ if (Object.keys(mergedMetadata.fields).length > 0) {
2438
+ try {
2439
+ const newInfoRef = combinedDoc.context.trailerInfo.Info;
2440
+ if (newInfoRef) {
2441
+ const newInfoDict = combinedDoc.context.lookup(newInfoRef);
2442
+ if (newInfoDict && typeof newInfoDict.set === "function") {
2443
+ newInfoDict.set(
2444
+ PDFName3.of("SigniphiMetadata"),
2445
+ PDFString3.of(JSON.stringify(mergedMetadata))
2446
+ );
2447
+ }
2448
+ }
2449
+ } catch (metadataError) {
2450
+ console.warn("Could not write merged metadata:", metadataError);
2451
+ }
2452
+ }
2453
+ let pdfBytes = await combinedDoc.save();
2454
+ if (allExtractedFields.length > 0) {
2455
+ console.log(`[PageAssembler] Re-adding ${allExtractedFields.length} fields to combined document`);
2456
+ const fieldsForPdf = allExtractedFields.map((field) => ({
2457
+ name: field.name,
2458
+ type: field.type,
2459
+ position: field.position,
2460
+ label: field.label,
2461
+ required: field.required,
2462
+ options: field.options,
2463
+ placeholder: field.placeholder,
2464
+ assignedSignerEmail: field.assignedSignerEmail
2465
+ }));
2466
+ try {
2467
+ pdfBytes = await addFormFieldsToPdf(pdfBytes, fieldsForPdf, {
2468
+ removeExistingFields: true,
2469
+ // Remove any orphaned field references
2470
+ drawLabels: false
2471
+ });
2472
+ console.log("[PageAssembler] \u2705 Successfully re-added form fields to combined PDF");
2473
+ } catch (addFieldsError) {
2474
+ console.error("[PageAssembler] Error re-adding fields:", addFieldsError);
2475
+ }
2476
+ }
1675
2477
  const arrayBuffer = pdfBytes.buffer.slice(pdfBytes.byteOffset, pdfBytes.byteOffset + pdfBytes.byteLength);
1676
2478
  const blob = new Blob([arrayBuffer], { type: "application/pdf" });
1677
2479
  return blob;
@@ -1860,14 +2662,18 @@ function debounce(func, wait) {
1860
2662
  timeout = setTimeout(() => func(...args), wait);
1861
2663
  });
1862
2664
  }
1863
- var PageAssembler_default = PageAssembler;
1864
2665
 
1865
2666
  exports.Button = Button;
2667
+ exports.FormFieldType = FormFieldType;
1866
2668
  exports.PageAssembler = PageAssembler;
2669
+ exports.addFormFieldsToPdf = addFormFieldsToPdf;
1867
2670
  exports.createPdfBlobUrl = createPdfBlobUrl;
1868
- exports.default = PageAssembler_default;
1869
2671
  exports.downloadPdf = downloadPdf;
2672
+ exports.extractFieldsFromPdf = extractFieldsFromPdf;
2673
+ exports.getSigniphiMetadata = getSigniphiMetadata;
1870
2674
  exports.imageToPdf = imageToPdf;
2675
+ exports.mapFieldPositionsAfterAssembly = mapFieldPositionsAfterAssembly;
1871
2676
  exports.pdfToImages = pdfToImages;
2677
+ exports.setSigniphiMetadata = setSigniphiMetadata;
1872
2678
  //# sourceMappingURL=index.js.map
1873
2679
  //# sourceMappingURL=index.js.map