I need to add a text element with the Signage timestamp to a pdf that then needs to be digitally signed.
I tried using pdf-lib for the timestamp but the resulting pdf buffer crashes the code of my colleague.
we suspect that the removal of the trailer by pdf lib may be triggering the Xref undefined error that we got.
Using the same pdf-lib library, Adding the Byterange should help to solve it. You will need to recreate the document by adding Byte range to the buffer.
Here is the snippet:
const fs = require('fs');
const signer = require('node-signpdf');
const {
PDFDocument,
PDFName,
PDFNumber,
PDFHexString,
PDFString,
} = require('pdf-lib');
// Custom code to add Byterange to PDF
const PDFArrayCustom = require('./PDFArrayCustom');
// The PDF we're going to sign
const pdfBuffer = fs.readFileSync('./input.pdf');
// The p12 certificate we're going to sign with
const p12Buffer = fs.readFileSync('./cert.p12');
const SIGNATURE_LENGTH = 4540;
(async () => {
const pdfDoc = await PDFDocument.load(pdfBuffer);
const pages = pdfDoc.getPages();
const ByteRange = PDFArrayCustom.withContext(pdfDoc.context);
ByteRange.push(PDFNumber.of(0));
ByteRange.push(PDFName.of(signer.DEFAULT_BYTE_RANGE_PLACEHOLDER));
ByteRange.push(PDFName.of(signer.DEFAULT_BYTE_RANGE_PLACEHOLDER));
ByteRange.push(PDFName.of(signer.DEFAULT_BYTE_RANGE_PLACEHOLDER));
const signatureDict = pdfDoc.context.obj({
Type: 'Sig',
Filter: 'Adobe.PPKLite',
SubFilter: 'adbe.pkcs7.detached',
ByteRange,
Contents: PDFHexString.of('A'.repeat(SIGNATURE_LENGTH)),
Reason: PDFString.of('We need your signature for reasons...'),
M: PDFString.fromDate(new Date()),
});
const signatureDictRef = pdfDoc.context.register(signatureDict);
const widgetDict = pdfDoc.context.obj({
Type: 'Annot',
Subtype: 'Widget',
FT: 'Sig',
Rect: [0, 0, 0, 0],
V: signatureDictRef,
T: PDFString.of('Signature1'),
F: 4,
P: pages[0].ref,
});
const widgetDictRef = pdfDoc.context.register(widgetDict);
// Add our signature widget to the first page
pages[0].node.set(PDFName.of('Annots'), pdfDoc.context.obj([widgetDictRef]));
// Create an AcroForm object containing our signature widget
pdfDoc.catalog.set(
PDFName.of('AcroForm'),
pdfDoc.context.obj({
SigFlags: 3,
Fields: [widgetDictRef],
}),
);
const modifiedPdfBytes = await pdfDoc.save({ useObjectStreams: false });
const modifiedPdfBuffer = Buffer.from(modifiedPdfBytes);
const signObj = new signer.SignPdf();
const signedPdfBuffer = signObj.sign(modifiedPdfBuffer, p12Buffer, {
passphrase: 'password',
});
// Write the signed file
fs.writeFileSync('./signed.pdf', signedPdfBuffer);
})();
PDFArrayCustom.js
const { PDFArray, CharCodes } = require('pdf-lib');
/**
* Extends PDFArray class in order to make ByteRange look like this:
* /ByteRange [0 /********** /********** /**********]
* Not this:
* /ByteRange [ 0 /********** /********** /********** ]
*/
class PDFArrayCustom extends PDFArray {
static withContext(context) {
return new PDFArrayCustom(context);
}
clone(context) {
const clone = PDFArrayCustom.withContext(context || this.context);
for (let idx = 0, len = this.size(); idx < len; idx++) {
clone.push(this.array[idx]);
}
return clone;
}
toString() {
let arrayString = '[';
for (let idx = 0, len = this.size(); idx < len; idx++) {
arrayString += this.get(idx).toString();
if (idx < len - 1) arrayString += ' ';
}
arrayString += ']';
return arrayString;
}
sizeInBytes() {
let size = 2;
for (let idx = 0, len = this.size(); idx < len; idx++) {
size += this.get(idx).sizeInBytes();
if (idx < len - 1) size += 1;
}
return size;
}
copyBytesInto(buffer, offset) {
const initialOffset = offset;
buffer[offset++] = CharCodes.LeftSquareBracket;
for (let idx = 0, len = this.size(); idx < len; idx++) {
offset += this.get(idx).copyBytesInto(buffer, offset);
if (idx < len - 1) buffer[offset++] = CharCodes.Space;
}
buffer[offset++] = CharCodes.RightSquareBracket;
return offset - initialOffset;
}
}
module.exports = PDFArrayCustom;
Refer this answer for the solution : https://github.com/Hopding/pdf-lib/issues/112#issuecomment-1049456413
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With