@signiphi/page-assembly 0.2.0-beta.12 → 0.2.0-beta.14

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.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, PDFName, PDFString, PDFDict, PDFArray, PDFHexString, PDFInvalidObject, PDFObjectParser, PDFRawStream, PDFObjectStreamParser, PDFRef, PDFBool, PDFNumber } from 'pdf-lib';
17
+ import { PDFDocument, PDFName, PDFTextField, PDFCheckBox, PDFDropdown, PDFRadioGroup, PDFHexString, StandardFonts, rgb, PDFString, 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,51 @@ 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
+ for (const source of sources) {
1390
+ const meta = source.metadata;
1391
+ if (!meta || source.fields.length === 0) continue;
1392
+ if (meta.radioLabelsBaked === false) radioLabelsBaked = false;
1393
+ else if (meta.radioLabelsBaked === true && radioLabelsBaked === void 0) radioLabelsBaked = true;
1394
+ if (meta.textLabelsBaked === false) textLabelsBaked = false;
1395
+ else if (meta.textLabelsBaked === true && textLabelsBaked === void 0) textLabelsBaked = true;
1396
+ }
1397
+ for (const source of sources) {
1398
+ const meta = source.metadata;
1399
+ if (!meta) continue;
1400
+ const sourceTextLabelsBaked = meta.textLabelsBaked !== false;
1401
+ for (const { name, page } of source.fields) {
1402
+ const key = `${name}_p${page}`;
1403
+ const entry = meta.fields?.[name];
1404
+ if (entry) {
1405
+ const carried = toCombinedPage(entry, page);
1406
+ merged.fields[key] = textLabelsBaked === false && sourceTextLabelsBaked ? { ...carried, textLabelBaked: true } : carried;
1407
+ }
1408
+ const fieldId = meta.fieldIdIndex?.[name];
1409
+ if (fieldId) merged.fieldIdIndex[key] = fieldId;
1410
+ }
1411
+ }
1412
+ if (radioLabelsBaked !== void 0) merged.radioLabelsBaked = radioLabelsBaked;
1413
+ if (textLabelsBaked !== void 0) merged.textLabelsBaked = textLabelsBaked;
1414
+ const hasEntries = Object.keys(merged.fields).length > 0;
1415
+ const hasMarkers = radioLabelsBaked !== void 0 || textLabelsBaked !== void 0;
1416
+ if (!hasEntries && !hasMarkers) return null;
1417
+ if (Object.keys(merged.fieldIdIndex).length === 0) delete merged.fieldIdIndex;
1418
+ return merged;
1419
+ }
1375
1420
  function getSigniphiMetadata(pdfDoc) {
1376
1421
  try {
1377
1422
  const infoRef = pdfDoc.context.trailerInfo.Info;
@@ -1387,12 +1432,17 @@ function getSigniphiMetadata(pdfDoc) {
1387
1432
  if (!metadataObj) {
1388
1433
  return null;
1389
1434
  }
1390
- const metadataStr = metadataObj.toString();
1391
- let jsonStr = metadataStr;
1392
- if (metadataStr.startsWith("(") && metadataStr.endsWith(")")) {
1393
- jsonStr = metadataStr.slice(1, -1);
1394
- } else if (metadataStr.startsWith("<") && metadataStr.endsWith(">")) {
1395
- jsonStr = metadataStr.slice(1, -1);
1435
+ let jsonStr;
1436
+ if (typeof metadataObj.decodeText === "function") {
1437
+ jsonStr = metadataObj.decodeText();
1438
+ } else {
1439
+ const metadataStr = metadataObj.toString();
1440
+ jsonStr = metadataStr;
1441
+ if (metadataStr.startsWith("(") && metadataStr.endsWith(")")) {
1442
+ jsonStr = metadataStr.slice(1, -1);
1443
+ } else if (metadataStr.startsWith("<") && metadataStr.endsWith(">")) {
1444
+ jsonStr = metadataStr.slice(1, -1);
1445
+ }
1396
1446
  }
1397
1447
  const metadata = JSON.parse(jsonStr);
1398
1448
  return metadata;
@@ -1585,7 +1635,7 @@ function setSigniphiMetadata(pdfDoc, metadata) {
1585
1635
  }
1586
1636
  const infoDict = infoObj;
1587
1637
  const metadataString = JSON.stringify(metadata);
1588
- infoDict.set(PDFName.of("SigniphiMetadata"), PDFString.of(metadataString));
1638
+ infoDict.set(PDFName.of("SigniphiMetadata"), PDFHexString.fromText(metadataString));
1589
1639
  } catch (error) {
1590
1640
  console.error("Failed to set Signiphi metadata:", error);
1591
1641
  throw error;
@@ -1945,6 +1995,7 @@ function mapFieldPositionsAfterAssembly(fields, pageMapping) {
1945
1995
  return pageMapping.has(mappingKey) || pageMapping.size === 0;
1946
1996
  });
1947
1997
  }
1998
+ var VISIBLE_PAGE_ROWS = 2;
1948
1999
  var isMultiSelectModifier = (event) => {
1949
2000
  return event.ctrlKey || event.metaKey;
1950
2001
  };
@@ -2271,21 +2322,48 @@ var ReorderablePageGrid = ({
2271
2322
  newPages.splice(hoverIndex, 0, draggedPage);
2272
2323
  onReorderPages(file.id, newPages);
2273
2324
  }, [file.id, file.pages, onReorderPages]);
2274
- return /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-4", children: file.pages.map((page, index) => /* @__PURE__ */ jsx(
2275
- DraggablePageThumbnail,
2325
+ const gridRef = useRef(null);
2326
+ const [maxHeight, setMaxHeight] = useState(void 0);
2327
+ useEffect(() => {
2328
+ const grid = gridRef.current;
2329
+ if (!grid || typeof ResizeObserver === "undefined") return;
2330
+ const measure = () => {
2331
+ const firstThumbnail = grid.firstElementChild;
2332
+ if (!firstThumbnail) return;
2333
+ const rowHeight = firstThumbnail.getBoundingClientRect().height;
2334
+ if (rowHeight <= 0) return;
2335
+ const rowGap = parseFloat(getComputedStyle(grid).rowGap) || 0;
2336
+ setMaxHeight(Math.ceil(rowHeight * VISIBLE_PAGE_ROWS + rowGap * (VISIBLE_PAGE_ROWS - 1)));
2337
+ };
2338
+ measure();
2339
+ const observer = new ResizeObserver(measure);
2340
+ observer.observe(grid);
2341
+ if (grid.firstElementChild) observer.observe(grid.firstElementChild);
2342
+ return () => observer.disconnect();
2343
+ }, [file.pages]);
2344
+ return /* @__PURE__ */ jsx(
2345
+ "div",
2276
2346
  {
2277
- page,
2278
- fileId: file.id,
2279
- index,
2280
- isSelected: selectedPageIds.includes(page.id),
2281
- onSingleSelect: onSingleSelectPage,
2282
- onToggleSelect: onTogglePageSelect,
2283
- onRangeSelect: onRangeSelectPage,
2284
- movePageInFile,
2285
- isDraggingPageRef
2286
- },
2287
- page.id
2288
- )) });
2347
+ ref: gridRef,
2348
+ className: "grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-4 overflow-y-auto overflow-x-hidden",
2349
+ style: { maxHeight },
2350
+ children: file.pages.map((page, index) => /* @__PURE__ */ jsx(
2351
+ DraggablePageThumbnail,
2352
+ {
2353
+ page,
2354
+ fileId: file.id,
2355
+ index,
2356
+ isSelected: selectedPageIds.includes(page.id),
2357
+ onSingleSelect: onSingleSelectPage,
2358
+ onToggleSelect: onTogglePageSelect,
2359
+ onRangeSelect: onRangeSelectPage,
2360
+ movePageInFile,
2361
+ isDraggingPageRef
2362
+ },
2363
+ page.id
2364
+ ))
2365
+ }
2366
+ );
2289
2367
  };
2290
2368
  var FileDropZone = ({
2291
2369
  targetIndex,
@@ -2870,7 +2948,7 @@ var PageAssembler = forwardRef(
2870
2948
  setIsLoading(true);
2871
2949
  setProcessingStatus("Assembling PDF...");
2872
2950
  try {
2873
- const { PDFDocument: PDFDocument5, PDFName: PDFName4, PDFString: PDFString4 } = await import('pdf-lib');
2951
+ const { PDFDocument: PDFDocument5, PDFName: PDFName4 } = await import('pdf-lib');
2874
2952
  const loadedPdfs = /* @__PURE__ */ new Map();
2875
2953
  const fileMap = /* @__PURE__ */ new Map();
2876
2954
  const pdfFiles = state.files.filter((f) => f.type === "pdf" && f.originalBytes?.length > 0);
@@ -2949,11 +3027,7 @@ var PageAssembler = forwardRef(
2949
3027
  }
2950
3028
  console.log("[PageAssembler] Starting multi-PDF assembly with field preservation");
2951
3029
  const allExtractedFields = [];
2952
- const mergedMetadata = {
2953
- version: "1.0",
2954
- fields: {},
2955
- fieldIdIndex: {}
2956
- };
3030
+ const metadataSources = [];
2957
3031
  const filePageUsage = /* @__PURE__ */ new Map();
2958
3032
  for (const file of state.files) {
2959
3033
  for (const page of file.pages) {
@@ -2978,28 +3052,12 @@ var PageAssembler = forwardRef(
2978
3052
  }
2979
3053
  console.log("[PageAssembler] Page mapping:", Object.fromEntries(pageIdToNewPageNumber));
2980
3054
  const extractAndMapFields = async (fileId, fileName, pdfBytes2, sourcePdf) => {
2981
- let sourceMetadata = null;
2982
- if (sourcePdf) {
2983
- try {
2984
- const sourceInfoRef = sourcePdf.context.trailerInfo.Info;
2985
- if (sourceInfoRef) {
2986
- const sourceInfoDict = sourcePdf.context.lookup(sourceInfoRef);
2987
- if (sourceInfoDict && typeof sourceInfoDict.get === "function") {
2988
- const metadataObj = sourceInfoDict.get(PDFName4.of("SigniphiMetadata"));
2989
- if (metadataObj) {
2990
- const metadataStr = metadataObj.toString();
2991
- let jsonStr = metadataStr;
2992
- if (metadataStr.startsWith("(") && metadataStr.endsWith(")")) {
2993
- jsonStr = metadataStr.slice(1, -1);
2994
- }
2995
- sourceMetadata = JSON.parse(jsonStr);
2996
- }
2997
- }
2998
- }
2999
- } catch (metadataError) {
3000
- console.warn(`Could not read metadata from PDF ${fileName}:`, metadataError);
3001
- }
3055
+ const sourceMetadata = sourcePdf ? getSigniphiMetadata(sourcePdf) : null;
3056
+ if (sourcePdf && !sourceMetadata) {
3057
+ console.warn(`[PageAssembler] No readable SigniphiMetadata in "${fileName}"`);
3002
3058
  }
3059
+ const placements = [];
3060
+ metadataSources.push({ metadata: sourceMetadata, fields: placements });
3003
3061
  console.log(`[PageAssembler] Extracting fields from "${fileName}"`);
3004
3062
  const extractedFields = await extractFieldsFromPdf(pdfBytes2);
3005
3063
  console.log(`[PageAssembler] Found ${extractedFields.length} fields in "${fileName}"`);
@@ -3033,13 +3091,7 @@ var PageAssembler = forwardRef(
3033
3091
  };
3034
3092
  allExtractedFields.push(trackedField);
3035
3093
  console.log(`[PageAssembler] Mapped field "${field.name}" from page ${originalPage} to page ${newPageNumber}`);
3036
- const metadataKey = `${field.name}_p${newPageNumber}`;
3037
- if (sourceMetadata?.fields?.[field.name]) {
3038
- mergedMetadata.fields[metadataKey] = sourceMetadata.fields[field.name];
3039
- }
3040
- if (sourceMetadata?.fieldIdIndex?.[field.name]) {
3041
- mergedMetadata.fieldIdIndex[metadataKey] = sourceMetadata.fieldIdIndex[field.name];
3042
- }
3094
+ placements.push({ name: field.name, page: newPageNumber });
3043
3095
  }
3044
3096
  }
3045
3097
  }
@@ -3117,21 +3169,15 @@ var PageAssembler = forwardRef(
3117
3169
  }
3118
3170
  }
3119
3171
  }
3120
- if (Object.keys(mergedMetadata.fields).length > 0) {
3172
+ const mergedMetadata = mergeSourceMetadata(metadataSources);
3173
+ if (mergedMetadata) {
3121
3174
  try {
3122
- const newInfoRef = combinedDoc.context.trailerInfo.Info;
3123
- if (newInfoRef) {
3124
- const newInfoDict = combinedDoc.context.lookup(newInfoRef);
3125
- if (newInfoDict && typeof newInfoDict.set === "function") {
3126
- newInfoDict.set(
3127
- PDFName4.of("SigniphiMetadata"),
3128
- PDFString4.of(JSON.stringify(mergedMetadata))
3129
- );
3130
- }
3131
- }
3175
+ setSigniphiMetadata(combinedDoc, mergedMetadata);
3132
3176
  } catch (metadataError) {
3133
3177
  console.warn("Could not write merged metadata:", metadataError);
3134
3178
  }
3179
+ } else {
3180
+ console.warn("[PageAssembler] No source metadata survived the merge; the assembled document will read as unprepared downstream");
3135
3181
  }
3136
3182
  let pdfBytes = await combinedDoc.save();
3137
3183
  if (allExtractedFields.length > 0) {