-
-
Save jgoux/399aaf4c67c47bc6b05b41293f4c1534 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { PDFNet } from "@pdftron/pdfnet-node"; | |
import fs from "fs/promises"; | |
import path from "path"; | |
import { env } from "~/app/config/env/pdftron"; | |
import { randomUUID } from "~/app/config/uuid"; | |
type SignPDFProps = { | |
file: Buffer; | |
signature: { | |
image: string; | |
coordinates: { | |
page_number: number; | |
x1: number; | |
x2: number; | |
y1: number; | |
y2: number; | |
}; | |
meta?: { | |
Name?: string; | |
Location?: string; | |
Reason?: string; | |
}; | |
}; | |
}; | |
async function signPDF(props: SignPDFProps) { | |
async function main() { | |
const doc = await PDFNet.PDFDoc.createFromBuffer(props.file); | |
await doc.initSecurityHandler(); | |
const page = await doc.getPage(props.signature.coordinates.page_number); | |
// Create a new signature form field in the PDFDoc. The name argument is optional; leaving it empty causes it to be auto-generated. However, you may need the name for later. Acrobat doesn't show digsigfield in side panel if it's without a widget. Using a Rect with 0 width and 0 height, or setting the NoPrint/Invisible flags makes it invisible. | |
const signatureField = await doc.createDigitalSignatureField(); | |
const signatureWidget = | |
await PDFNet.SignatureWidget.createWithDigitalSignatureField( | |
doc, | |
new PDFNet.Rect( | |
props.signature.coordinates.x1, | |
props.signature.coordinates.y1, | |
props.signature.coordinates.x2, | |
props.signature.coordinates.y2 | |
), | |
signatureField | |
); | |
await page.annotPushBack(signatureWidget); | |
// Add an appearance to the signature field. | |
const signatureImage = await PDFNet.Image.createFromMemory2( | |
doc, | |
Buffer.from(props.signature.image.split(",")[1], "base64") | |
); | |
await signatureWidget.createSignatureAppearance(signatureImage); | |
const savePath = path.join("/tmp", randomUUID() + ".pdf"); | |
try { | |
// Sign | |
await signatureField.signOnNextSave( | |
env.PDFTRON_SIGNATURE_CERTIFICATE_PATH, | |
env.PDFTRON_SIGNATURE_CERTIFICATE_PASSWORD | |
); | |
// Add meta informations to the signature field. | |
if (props.signature.meta?.Name) { | |
await signatureField.setContactInfo(props.signature.meta.Name); | |
} | |
if (props.signature.meta?.Location) { | |
await signatureField.setLocation(props.signature.meta.Location); | |
} | |
if (props.signature.meta?.Reason) { | |
await signatureField.setReason(props.signature.meta.Reason); | |
} | |
// We can't use doc.saveMemoryBuffer as it doesn't support e_incremental flag | |
await doc.save(savePath, PDFNet.SDFDoc.SaveOptions.e_incremental); | |
// Verification options | |
const opts = await PDFNet.VerificationOptions.create( | |
PDFNet.VerificationOptions.SecurityLevel.e_compatibility_and_archiving | |
); | |
await opts.addTrustedCertificateUString( | |
env.PDFTRON_SIGNATURE_ROOT_CERTIFICATE_PATH | |
); | |
await opts.enableOnlineCRLRevocationChecking(true); | |
// Timestamp | |
const tstConfig = await PDFNet.TimestampingConfiguration.createFromURL( | |
env.PDFTRON_SIGNATURE_TIMESTAMP_SERVER_URL | |
); | |
await tstConfig.setTimestampAuthorityServerUsername( | |
env.PDFTRON_SIGNATURE_TIMESTAMP_SERVER_USERNAME | |
); | |
await tstConfig.setTimestampAuthorityServerPassword( | |
env.PDFTRON_SIGNATURE_TIMESTAMP_SERVER_PASSWORD | |
); | |
await signatureField.timestampOnNextSave(tstConfig, opts); | |
await doc.save(savePath, PDFNet.SDFDoc.SaveOptions.e_incremental); | |
// LTV | |
const verificationResult = await signatureField.verify(opts); | |
if ( | |
!(await signatureField.enableLTVOfflineVerification(verificationResult)) | |
) { | |
throw new Error("Could not enable LTV"); | |
} | |
await doc.save(savePath, PDFNet.SDFDoc.SaveOptions.e_incremental); | |
const signedPDF = await fs.readFile(savePath); | |
return signedPDF; | |
} finally { | |
void fs.unlink(savePath); | |
} | |
} | |
return PDFNet.runWithCleanup(main, env.PDFTRON_SDK_LICENSE_KEY); | |
} | |
export { signPDF }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment