mirror of
https://github.com/OpenSignLabs/OpenSign.git
synced 2026-08-17 21:25:54 +02:00
feat: create duplicate template functionality feat: add support of csv and xlsx for bulk contacts import feat: add info text for forms and dashboard button feat: implement functionality to delete a PDF page while editing the document feat: show completed documents in which signers included but not owner in completed reports feat: add delete folder button in opensign drive feat: introduce Bcc email support for sending completed documents feat: allow merging multiple pdf while drafting document and template feat: add 'My Initials' tab for auto-signing in request-sign flow feat: add support for redirect URL to navigate after document completion feat: introduce privacy policy and digital signature terms before signing documents. feat: add feature to allow adding new pages to existing document feat: add save signature, initials, stamp functionality in sign pad for logged in user feat: add preferences menu feat: add save custom email template, notifyonsignature, set timezone, allow signature types in preferences feat: secure local url feat: implement Italian language translation feat: implement German language translation feat: update menu name from report to documents and shift contactbook in main menu feat: provide edit contact functionality fix: unable to delete folder when all its documents are deleted fix: fields.push is not function fix: document loading issue in opensign drive fix: adjust the guest signature flow to display the document in full screen, eliminating any blank space which is displayed below the place holder in mobile view fix: resolve issue of instance of pdfdict or pdfstream but got undefined build(deps): update dependencies refactor: change note text from add contact form
131 lines
3.7 KiB
JavaScript
131 lines
3.7 KiB
JavaScript
import {
|
|
DEFAULT_SIGNATURE_LENGTH,
|
|
DEFAULT_BYTE_RANGE_PLACEHOLDER,
|
|
SUBFILTER_ADOBE_PKCS7_DETACHED,
|
|
ANNOTATION_FLAGS,
|
|
SIG_FLAGS,
|
|
SignPdfError,
|
|
} from '@signpdf/utils';
|
|
import {
|
|
PDFArray,
|
|
PDFNumber,
|
|
PDFName,
|
|
PDFHexString,
|
|
PDFString,
|
|
PDFInvalidObject,
|
|
PDFDict,
|
|
} from 'pdf-lib';
|
|
|
|
export const Placeholder = ({
|
|
pdfDoc,
|
|
pdfPage,
|
|
reason,
|
|
contactInfo,
|
|
name,
|
|
location,
|
|
signingTime = new Date(),
|
|
signatureLength = DEFAULT_SIGNATURE_LENGTH,
|
|
byteRangePlaceholder = DEFAULT_BYTE_RANGE_PLACEHOLDER,
|
|
subFilter = SUBFILTER_ADOBE_PKCS7_DETACHED,
|
|
widgetRect = [0, 0, 0, 0],
|
|
appName,
|
|
}) => {
|
|
if (!pdfDoc && !pdfPage) {
|
|
throw new SignPdfError('PDFDoc or PDFPage must be set.', SignPdfError.TYPE_INPUT);
|
|
}
|
|
|
|
const doc = pdfDoc || pdfPage.doc;
|
|
const page = pdfPage || doc.getPages()[0];
|
|
|
|
const byteRange = PDFArray.withContext(doc.context);
|
|
byteRange.push(PDFNumber.of(0));
|
|
byteRange.push(PDFName.of(byteRangePlaceholder));
|
|
byteRange.push(PDFName.of(byteRangePlaceholder));
|
|
byteRange.push(PDFName.of(byteRangePlaceholder));
|
|
|
|
const placeholder = PDFHexString.of(String.fromCharCode(0).repeat(signatureLength));
|
|
|
|
const appBuild = appName ? { App: { Name: appName } } : {};
|
|
const signatureDict = doc.context.obj({
|
|
Type: 'Sig',
|
|
Filter: 'Adobe.PPKLite',
|
|
SubFilter: subFilter,
|
|
ByteRange: byteRange,
|
|
Contents: placeholder,
|
|
Reason: PDFString.of(reason),
|
|
M: PDFString.fromDate(signingTime),
|
|
ContactInfo: PDFString.of(contactInfo),
|
|
Name: PDFString.of(name),
|
|
Location: PDFString.of(location),
|
|
Prop_Build: {
|
|
Filter: { Name: 'Adobe.PPKLite' },
|
|
...appBuild,
|
|
},
|
|
});
|
|
|
|
const signatureBuffer = new Uint8Array(signatureDict.sizeInBytes());
|
|
signatureDict.copyBytesInto(signatureBuffer, 0);
|
|
const signatureObj = PDFInvalidObject.of(signatureBuffer);
|
|
const signatureDictRef = doc.context.register(signatureObj);
|
|
|
|
const rect = PDFArray.withContext(doc.context);
|
|
widgetRect.forEach(c => rect.push(PDFNumber.of(c)));
|
|
const apStream = doc.context.formXObject([], {
|
|
BBox: widgetRect,
|
|
Resources: {},
|
|
});
|
|
|
|
const widgetDict = doc.context.obj({
|
|
Type: 'Annot',
|
|
Subtype: 'Widget',
|
|
FT: 'Sig',
|
|
Rect: rect,
|
|
V: signatureDictRef,
|
|
T: PDFString.of('Signature1'),
|
|
F: ANNOTATION_FLAGS.PRINT,
|
|
P: page.ref,
|
|
AP: { N: doc.context.register(apStream) },
|
|
});
|
|
|
|
const widgetDictRef = doc.context.register(widgetDict);
|
|
|
|
let annotations = page.node.lookupMaybe(PDFName.of('Annots'), PDFArray);
|
|
if (!annotations) {
|
|
annotations = doc.context.obj([]);
|
|
}
|
|
annotations.push(widgetDictRef);
|
|
page.node.set(PDFName.of('Annots'), annotations);
|
|
|
|
let acroForm = doc.catalog.lookupMaybe(PDFName.of('AcroForm'), PDFDict);
|
|
if (!acroForm) {
|
|
acroForm = doc.context.obj({ Fields: [] });
|
|
const acroFormRef = doc.context.register(acroForm);
|
|
doc.catalog.set(PDFName.of('AcroForm'), acroFormRef);
|
|
}
|
|
|
|
let sigFlags = acroForm.has(PDFName.of('SigFlags'))
|
|
? acroForm.get(PDFName.of('SigFlags'))
|
|
: PDFNumber.of(0);
|
|
|
|
const updatedFlags = PDFNumber.of(
|
|
sigFlags.asNumber() | SIG_FLAGS.SIGNATURES_EXIST | SIG_FLAGS.APPEND_ONLY
|
|
);
|
|
acroForm.set(PDFName.of('SigFlags'), updatedFlags);
|
|
|
|
let fields = acroForm.get(PDFName.of('Fields'));
|
|
if (fields instanceof PDFArray) {
|
|
fields.push(widgetDictRef);
|
|
}
|
|
// else if (fields) {
|
|
// const newFields = PDFArray.withContext(doc.context);
|
|
// fields.asArray().forEach(field => newFields.push(field));
|
|
// newFields.push(widgetDictRef);
|
|
// acroForm.set(PDFName.of('Fields'), newFields);
|
|
// }
|
|
else {
|
|
const newFields = PDFArray.withContext(doc.context);
|
|
newFields.push(widgetDictRef);
|
|
acroForm.set(PDFName.of('Fields'), newFields);
|
|
}
|
|
};
|