refactor: replace pdf-sign with libpdf/core for PDF operations (#2403)

Migrate from @documenso/pdf-sign and @cantoo/pdf-lib to @libpdf/core
for all PDF manipulation and signing operations. This includes:

- New signing transports for Google Cloud KMS and local certificates
- Consolidated PDF operations using libpdf API
- Added TSA (timestamp authority) helper for digital signatures
- Removed deprecated flatten and insert utilities
- Updated tests to use new PDF library
This commit is contained in:
Lucas Smith
2026-01-21 15:16:23 +11:00
committed by GitHub
parent ed7a0011c7
commit 9035240b4d
37 changed files with 1065 additions and 1468 deletions
@@ -1,4 +1,4 @@
import { PDFDocument } from '@cantoo/pdf-lib';
import { PDF } from '@libpdf/core';
import { expect, test } from '@playwright/test';
import { DocumentStatus, FieldType } from '@prisma/client';
@@ -43,7 +43,7 @@ test.describe('Signing Certificate Tests', () => {
return fetch(documentUrl).then(async (res) => await res.arrayBuffer());
});
const originalPdf = await PDFDocument.load(documentData);
const originalPdf = await PDF.load(new Uint8Array(documentData));
// Sign the document
await page.goto(`/sign/${recipient.token}`);
@@ -101,7 +101,7 @@ test.describe('Signing Certificate Tests', () => {
const completedDocumentData = new Uint8Array(pdfData);
// Load the PDF and check number of pages
const pdfDoc = await PDFDocument.load(completedDocumentData);
const pdfDoc = await PDF.load(new Uint8Array(completedDocumentData));
expect(pdfDoc.getPageCount()).toBe(originalPdf.getPageCount() + 1); // Original + Certificate
});
@@ -153,7 +153,7 @@ test.describe('Signing Certificate Tests', () => {
return fetch(documentUrl).then(async (res) => await res.arrayBuffer());
});
const originalPdf = await PDFDocument.load(documentData);
const originalPdf = await PDF.load(new Uint8Array(documentData));
// Sign the document
await page.goto(`/sign/${recipient.token}`);
@@ -206,7 +206,7 @@ test.describe('Signing Certificate Tests', () => {
const completedDocumentData = new Uint8Array(pdfData);
// Load the PDF and check number of pages
const completedPdf = await PDFDocument.load(completedDocumentData);
const completedPdf = await PDF.load(new Uint8Array(completedDocumentData));
expect(completedPdf.getPageCount()).toBe(originalPdf.getPageCount() + 1); // Original + Certificate
});
@@ -258,7 +258,7 @@ test.describe('Signing Certificate Tests', () => {
return fetch(documentUrl).then(async (res) => await res.arrayBuffer());
});
const originalPdf = await PDFDocument.load(new Uint8Array(documentData));
const originalPdf = await PDF.load(new Uint8Array(documentData));
// Sign the document
await page.goto(`/sign/${recipient.token}`);
@@ -309,7 +309,7 @@ test.describe('Signing Certificate Tests', () => {
);
// Load the PDF and check number of pages
const completedPdf = await PDFDocument.load(completedDocumentData);
const completedPdf = await PDF.load(new Uint8Array(completedDocumentData));
expect(completedPdf.getPageCount()).toBe(originalPdf.getPageCount());
});
@@ -1,4 +1,4 @@
import { PDFDocument } from '@cantoo/pdf-lib';
import { PDF } from '@libpdf/core';
import { expect, test } from '@playwright/test';
import fs from 'node:fs';
import path from 'node:path';
@@ -39,24 +39,30 @@ const TEST_FORM_VALUES = {
* Returns true if the PDF has form fields, false if they've been flattened.
*/
async function pdfHasFormFields(pdfBuffer: Uint8Array): Promise<boolean> {
const pdfDoc = await PDFDocument.load(pdfBuffer);
const pdfDoc = await PDF.load(new Uint8Array(pdfBuffer));
const form = pdfDoc.getForm();
const fields = form.getFields();
const form = await pdfDoc.getForm();
return fields.length > 0;
if (!form) {
return false;
}
return form.fieldCount > 0;
}
/**
* Helper to get form field names from a PDF.
*/
async function getPdfFormFieldNames(pdfBuffer: Uint8Array): Promise<string[]> {
const pdfDoc = await PDFDocument.load(pdfBuffer);
const pdfDoc = await PDF.load(new Uint8Array(pdfBuffer));
const form = pdfDoc.getForm();
const fields = form.getFields();
const form = await pdfDoc.getForm();
return fields.map((field) => field.getName());
if (!form) {
return [];
}
return form.getFieldNames();
}
/**
@@ -66,17 +72,21 @@ async function getPdfTextFieldValue(
pdfBuffer: Uint8Array,
fieldName: string,
): Promise<string | undefined> {
const pdfDoc = await PDFDocument.load(pdfBuffer);
const pdfDoc = await PDF.load(new Uint8Array(pdfBuffer));
const form = pdfDoc.getForm();
const form = await pdfDoc.getForm();
try {
const textField = form.getTextField(fieldName);
return textField.getText() ?? '';
} catch {
if (!form) {
return undefined;
}
const textField = form.getTextField(fieldName);
if (!textField) {
return undefined;
}
return textField.getValue();
}
test.describe.configure({