Save and Store Electronic Signatures in Our JavaScript Viewer
PSPDFKit allows you to implement your own mechanism for storing signatures.
If you provide such a mechanism, then signatures may optionally be saved at the same time they’re added to a document. The saving of signatures is based on the SIGNATURE_SAVE_MODE
: This defaults to showing a toggle in the UI that allows the user to choose whether to save, but you can change this option to hide the toggle and instead always save signatures without giving the user the choice.
If you provide stored signatures to PSPDFKit, then when the user selects a signature form field or the signature tool, the list of stored signatures will be shown instead of the signature creation UI.
To do this, you can register event listeners for the storedSignatures.create
and storedSignatures.delete
events. Once these event listeners are registered, the signatures UI is updated to display a checkbox to allow users to decide if they want their signatures stored or not.
storedSignatures.create
is invoked with the signature annotation created by the user. There, you can serialize the signature and send it to a backend server, or add it to a browser storage mechanism such as IndexedDB or local storage.
ℹ️ Note: The
storedSignatures.create
payload returnsnull
values for the annotationid
andname
. We return these values because the created signature isn’t attached to the document, hence it isn’t assigned anid
orname
. If you want to retrieve a complete list of values of the signature annotation, we suggest listening to theannotations.create
event.
As an example, the code below shows how to add signatures using window.localStorage
. Please note that local storage is a blocking API, so it isn’t ideal for production applications, especially when dealing with images:
const STORAGE_KEY = 'signatures_storage'; const ATTACHMENTS_KEY = 'attachments_storage'; instance.addEventListener('storedSignatures.create', async (annotation) => { const signaturesString = localStorage.getItem(STORAGE_KEY); const storedSignatures = signaturesString ? JSON.parse(signaturesString) : []; const serializedAnnotation = PSPDFKit.Annotations.toSerializableObject( annotation, ); if (annotation.imageAttachmentId) { const attachment = await instance.getAttachment( annotation.imageAttachmentId, ); // Create the data URL and add it to local storage. // Note: This is done only for demonstration purposes. // Storing potentially large chunks of data using local storage is // considered bad practice due to the synchronous nature of the API. // For production applications, please consider alternatives such as // dedicated backend storage or IndexedDB. const url = await fileToDataURL(attachment); const attachmentsString = localStorage.getItem(ATTACHMENTS_KEY); const attachmentsArray = attachmentsString ? JSON.parse(attachmentsString) : []; attachmentsArray.push({ url, id: annotation.imageAttachmentId }); // Separate the `localStorage` item to store attachments. localStorage.setItem(ATTACHMENTS_KEY, JSON.stringify(attachmentsArray)); } storedSignatures.push(serializedAnnotation); localStorage.setItem(STORAGE_KEY, JSON.stringify(storedSignatures)); // Add a new annotation so that it renders as part of the UI on the current session. instance.setStoredSignatures((signatures) => signatures.push(annotation)); });
Similarly, it’s necessary to implement deleting the signature from the underlying storage when the user chooses to delete one of the existing records through the UI:
instance.addEventListener('storedSignatures.delete', (annotation) => { const signaturesString = localStorage.getItem(STORAGE_KEY); const storedSignatures = signaturesString ? JSON.parse(signaturesString) : []; const annotations = storedSignatures.map( PSPDFKit.Annotations.fromSerializableObject, ); const updatedAnnotations = annotations.filter( (currentAnnotation) => !currentAnnotation.equals(annotation), ); localStorage.setItem( STORAGE_KEY, JSON.stringify( updatedAnnotations.map(PSPDFKit.Annotations.toSerializableObject), ), ); // Use the `setStoredSignatures` API so that the current UI is properly updated. instance.setStoredSignatures((signatures) => signatures.filter((signature) => !signature.equals(annotation)), ); if (annotation.imageAttachmentId) { // Remove the attachment from the array. const attachmentsString = localStorage.getItem(ATTACHMENTS_KEY); if (attachmentsString) { let attachmentsArray = JSON.parse(attachmentsString); attachmentsArray = attachmentsArray.filter( (attachment) => attachment.id !== annotation.imageAttachmentId, ); localStorage.setItem( ATTACHMENTS_KEY, JSON.stringify(attachmentsArray), ); } } });
To set the list of available signatures to pick from, you can use the PSPDFKit.Configuration#populateStoredSignatures
configuration option or the PSPDFKit.Instance#setStoredSignatures
instance method:
const signaturesString = localStorage.getItem(STORAGE_KEY); const storedSignatures = JSON.parse(signaturesString); // Construct annotations from serialized entries and call the `setStoredSignatures` API. const list = PSPDFKit.Immutable.List( storedSignatures.map(PSPDFKit.Annotations.fromSerializableObject), ); instance.setStoredSignatures(list); // Retrieve attachments and add them to the instance. const attachmentsString = localStorage.getItem(ATTACHMENTS_KEY); if (attachmentsString) { const attachmentsArray = JSON.parse(attachmentsString); // Instantiate blob objects from the data URLs on local storage. const blobs = await Promise.all( attachmentsArray.map(({ url }) => fetch(url).then((res) => res.blob())), ); // Create an attachment for each blob. blobs.forEach(instance.createAttachment); }