Skip to content

Commit

Permalink
í¾¨ [placeholder-pdf-lib]
Browse files Browse the repository at this point in the history
* Use pdf-lib 1.17 types;
* Use the newly introduced FLAG enums;
* Reuse existing AcroForm (refs #198);
* Reuse existing Annots (refs #198);
* Fix mistaken signature length;
  • Loading branch information
vbuch committed Nov 14, 2023
1 parent a1b9528 commit 35b101b
Showing 1 changed file with 49 additions and 23 deletions.
72 changes: 49 additions & 23 deletions packages/placeholder-pdf-lib/src/pdflibAddPlaceholder.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
import {DEFAULT_BYTE_RANGE_PLACEHOLDER, DEFAULT_SIGNATURE_LENGTH, SUBFILTER_ADOBE_PKCS7_DETACHED} from '@signpdf/utils';
import {
PDFArray, PDFHexString, PDFName, PDFNumber, PDFString, toHexStringOfMinLength,
ANNOTATION_FLAGS,
DEFAULT_BYTE_RANGE_PLACEHOLDER,
DEFAULT_SIGNATURE_LENGTH,
SIG_FLAGS,
SUBFILTER_ADOBE_PKCS7_DETACHED,
} from '@signpdf/utils';
import {
PDFArray, PDFDict, PDFHexString, PDFName, PDFNumber, PDFString,
} from 'pdf-lib';

/**
* @typedef {import('pdf-lib').PDFDocumentFactory} PDFDocumentFactory
* @typedef {import('pdf-lib').PDFDocument} PDFDocument
*/

/**
* @typedef {object} InputType
* @property {PDFDocumentFactory} pdfDoc
* @property {PDFDocument} pdfDoc
* @property {string} reason
* @property {string} contactInfo
* @property {string} name
* @property {string} location
* @property {number} [signatureLength]
* @property {string} [byteRangePlaceholder]
* @property {string} [subFilter] One of SUBFILTER_* from @signpdf/utils
* @property {string} [subFilter] One of SUBFILTER_* from \@signpdf/utils
* @property {number[]} [widgetRect] [x1, y1, x2, y2] widget rectangle
*/

Expand All @@ -39,7 +45,7 @@ export const pdflibAddPlaceholder = ({
subFilter = SUBFILTER_ADOBE_PKCS7_DETACHED,
widgetRect = [0, 0, 0, 0],
}) => {
const pages = pdfDoc.getPages();
const page = pdfDoc.getPage(0);

// Create a placeholder where the the last 3 parameters of the
// actual range will be replaced when signing is done.
Expand All @@ -50,8 +56,7 @@ export const pdflibAddPlaceholder = ({
byteRange.push(PDFName.of(byteRangePlaceholder));

// Fill the contents of the placeholder with 00s.
const hexNull = toHexStringOfMinLength(0, 4);
const placeholder = PDFHexString.of(hexNull.repeat(signatureLength));
const placeholder = PDFHexString.of(String.fromCharCode(0).repeat(signatureLength));

// Create a signature dictionary to be referenced in the signature widget.
const signatureDict = pdfDoc.context.obj({
Expand All @@ -66,6 +71,7 @@ export const pdflibAddPlaceholder = ({
Name: PDFString.of(name),
Location: PDFString.of(location),
}, pdfDoc.index);
const signatureDictRef = pdfDoc.context.register(signatureDict);

// Create the signature widget
const rect = PDFArray.withContext(pdfDoc.context);
Expand All @@ -75,25 +81,45 @@ export const pdflibAddPlaceholder = ({
Subtype: 'Widget',
FT: 'Sig',
Rect: rect,
V: signatureDict,
V: signatureDictRef,
T: PDFString.of('Signature1'),
F: 4,
P: pages[0].ref,
F: ANNOTATION_FLAGS.PRINT,
P: page.ref,
}, pdfDoc.index);
const widgetDictRef = pdfDoc.context.register(widgetDict);

// Annotate the widget
pages[0].node.set(
PDFName.of('Annots'),
pdfDoc.context.obj([widgetDictRef]),
);
// Annotate the widget on the first page
let annotations = page.node.lookupMaybe(PDFName.of('Annots'), PDFArray);
if (typeof annotations === 'undefined') {
annotations = pdfDoc.context.obj([]);
}
annotations.push(widgetDictRef);
page.node.set(PDFName.of('Annots'), annotations);

// Add an AcroForm or update the existing one
let acroForm = pdfDoc.catalog.lookupMaybe(PDFName.of('AcroForm'), PDFDict);
if (typeof acroForm === 'undefined') {
// Need to create a new AcroForm
acroForm = pdfDoc.context.obj({Fields: []});
const acroFormRef = pdfDoc.context.register(acroForm);
pdfDoc.catalog.set(PDFName.of('AcroForm'), acroFormRef);
}

// Reference the widget in the AcroForm
pdfDoc.catalog.set(
PDFName.of('AcroForm'),
pdfDoc.context.obj({
SigFlags: 3,
Fields: [widgetDictRef],
}),
/**
* @type {PDFNumber}
*/
let sigFlags;
if (acroForm.has(PDFName.of('SigFlags'))) {
// Already has some flags, will merge
sigFlags = acroForm.get(PDFName.of('SigFlags'));
} else {
// Create blank flags
sigFlags = PDFNumber.of(0);
}
const updatedFlags = PDFNumber.of(
sigFlags.asNumber() | SIG_FLAGS.SIGNATURES_EXIST | SIG_FLAGS.APPEND_ONLY,
);
acroForm.set(PDFName.of('SigFlags'), updatedFlags);
const fields = acroForm.get(PDFName.of('Fields'));
fields.push(widgetDictRef);
};

0 comments on commit 35b101b

Please sign in to comment.