How to Add a Signature to a PDF Using Vue.js
This article was first published in February 2023 and was updated in August 2024.
In this blog post, you’ll add a signature to a PDF document using PSPDFKit and Vue.js. You’ll start by setting up a viewer to display PDF documents and then learn how to integrate signing functionality.
PSPDFKit is a JavaScript PDF library that can quickly be integrated into an application to enable electronic and encrypted-based digital signatures.
Vue.js is a frontend JavaScript framework mainly used for creating user interfaces (UIs) and one-page applications. It allows users to make prototypes and build applications quickly and reliably. One of the best things about Vue.js is it can be used for both desktop and mobile app development, which makes it a great frontend tool for working on any application or system.
Signing PDFs in Vue.js
Users can add signatures to PDFs by drawing, typing, or selecting an image. The drawing option allows users to freehand draw a signature. The typing option lets them type their names and then add a style to their signatures. The final option is to upload an image that’s already saved in their gallery by selecting and dragging the image into the signature box.
Signature Support
The Vue.js signature library supports:
-
Electronic signatures, which are created through ink, bitmaps, text, and drawings.
-
Digital signatures, which include a certificate that provides a unique identifier for the signer. These are used as proof of the document’s origin and can prevent modification by any other person.
PSPDFKit’s Vue.js PDF Library
At PSPDFKit, we offer a commercial, feature-rich, and completely customizable Vue.js PDF library that’s easy to integrate and comes with well-documented APIs to handle advanced use cases. It comes with:
-
Annotation tools — Draw, circle, highlight, comment, and add notes to documents with 15+ prebuilt annotation tools.
-
File support — View PNG, JPG, and TIFF files on the client side. With server deployment, users can view MS Office documents.
-
30+ features — Easily add features like PDF editing, digital signatures, form filling, real-time document collaboration, and more.
-
Dedicated support — Deploy faster by working 1-on-1 with our developers.
Try it for free, or visit our web demo to see it in action.
Requirements
For adding a signature to a PDF, you’ll need the following software installed:
-
Git — An open source version control system used to control projects efficiently.
-
Node.js — An open source server provider that uses JavaScript on its server. This post uses version 16.13.0.
Step 1 — Setting Up a Vue.js Project
To create, build, and run Vue.js applications, you need to install Vue CLI, which is standard tooling for Vue.js.
-
You can install the CLI using
npm
— which comes with Node.js — oryarn
:
npm install -g @vue/cli
yarn global add @vue/cli
You can check the version of Vue.js by running the following:
vue --version
This post uses Vue.js CLI version 3.2.37.
-
Now, create a new Vue.js project to integrate with PSPDFKit using the following command:
vue create pspdfkit-signature-project
You’ll be asked some configuration questions. Select the default option, and then change your directory to pspdfkit-signature-project
:
cd pspdfkit-signature-project
Step 2 — Adding PSPDFKit
-
Add the PSPDFKit dependency to your project:
npm install pspdfkit
-
Once installed, create a
js
directory under the project location with the following command:
mkdir -p public/js
-
Copy the PSPDFKit for Web library assets to the
public/js
directory:
cp -R ./node_modules/pspdfkit/dist/pspdfkit-lib public/js/pspdfkit-lib
Step 3 — Displaying a PDF
-
Rename the PDF document you want to display in your application to
document.pdf
, and then add the PDF document to thepublic
directory. You can use [this demo document][] as an example. -
Next, create a new component to serve as a wrapper for the PSPDFKit library. In the
src/components/
folder, create a file namedPSPDFKitContainer.vue
with the following content:
<template> <div class="pdf-container"></div> </template> <script> import PSPDFKit from "pspdfkit"; /** * PSPDFKit for Web example component. */ export default { name: 'PSPDFKit', props: { pdfFile: { type: String, required: true, }, }, mounted() { this.loadPSPDFKit().then((instance) => { this.$emit("loaded", instance); }); }, watch: { pdfFile(val) { if (val) { this.loadPSPDFKit(); } }, }, methods: { async loadPSPDFKit() { PSPDFKit.unload(".pdf-container"); return PSPDFKit.load({ document: this.pdfFile, container: ".pdf-container", }); }, }, beforeUnmount() { PSPDFKit.unload(".pdf-container"); }, }; </script> <style scoped> .pdf-container { height: 100vh; } </style>
-
In the
src
folder, replace the contents of theApp.vue
file with the following to include the newly created component in your app:
<template> <div id="app"> <label for="file-upload" class="custom-file-upload"> Open PDF </label> <input id="file-upload" type="file" @change="openDocument" class="btn" /> <PSPDFKitContainer :pdfFile="pdfFile" @loaded="handleLoaded" /> </div> </template> <script> import PSPDFKitContainer from "@/components/PSPDFKitContainer"; export default { data() { return { pdfFile: this.pdfFile || "/document.pdf", }; }, components: { PSPDFKitContainer, }, methods: { handleLoaded(instance) { console.log("PSPDFKit has loaded: ", instance); // Do something. }, openDocument(event) { if (this.pdfFile && this.pdfFile.startsWith('blob:')) { window.URL.revokeObjectURL(this.pdfFile); } this.pdfFile = window.URL.createObjectURL(event.target.files[0]); }, }, }; </script> <style> #app { font-family: Avenir, Helvetica, Arial, sans-serif; text-align: center; color: #2c3e50; } body { margin: 0; } input[type="file"] { display: none; } .custom-file-upload { border: 1px solid #ccc; border-radius: 4px; display: inline-block; padding: 6px 12px; cursor: pointer; background: #4A8FED; padding: 10px; color: #fff; font: inherit; font-size: 16px; font-weight: bold; } </style>
-
Start the app:
npm serve
Open http://localhost:8080/ in your browser to view the website.
After setting up the viewer, proceed with adding signature functionality to your Vue.js application.
Step 4 — Adding Digital Signature Functionality
Now your project is ready for the digital signature to be added to the PDF, but you have to create a digital signature. This requires two things, outlined below.
-
First, you need the X.509 certificate that contains the public key and the signer information to sign digitally. Verify the PKCS#7 certificate using the following command:
openssl pkcs7 -noout -text -print_certs -in example.p7b
-
Second, you need the self-signed private key and the certificate pair.
Refer to the add digital signatures to PDFs using JavaScript guide for more information on how to create a digital signature.
Step 5 — Installing the node-forge Library
You’ll now generate a valid digital signature using the cryptographic Distinguished Encoding Rules (DER) PKCS#7 format. For this, use the node-forge
library to handle cryptographic functions.
To use node-forge for handling cryptographic functions, you need to install it in your Vue.js project. You can do this with either yarn
or npm
.
-
Using
yarn
:
yarn add node-forge
-
Using
npm
:
npm install node-forge
Step 6 — Preparing the PSPDFKitContainer.vue Component
In the PSPDFKitContainer.vue
component, you’ll need to add the code necessary to generate a PKCS#7 signature and sign the document. Below are the steps:
-
Fetch the certificate and private key PEM files from the certs directory.
-
Use
node-forge
to create a DER-encoded PKCS#7 signature. -
Convert the result to
ArrayBuffer
format for signing.
Update your PSPDFKitContainer.vue
component with the following code:
<script> import PSPDFKit from 'pspdfkit'; import forge from 'node-forge'; export default { name: 'PSPDFKitContainer', props: { pdfFile: { type: String, required: true, }, }, data() { return { instance: null, }; }, mounted() { this.loadPSPDFKit().then((instance) => { this.$emit('loaded', instance); this.instance = instance; // Save the instance for later use. }); }, watch: { pdfFile(val) { if (val) { this.loadPSPDFKit(); } }, }, methods: { async loadPSPDFKit() { PSPDFKit.unload('.pdf-container'); return PSPDFKit.load({ document: this.pdfFile, container: '.pdf-container', }); }, generatePKCS7({ fileContents }) { const certificatePromise = fetch( 'certs/certificate.pem', ).then((response) => response.text()); const privateKeyPromise = fetch( 'certs/private-key.pem', ).then((response) => response.text()); return new Promise((resolve, reject) => { Promise.all([certificatePromise, privateKeyPromise]) .then(([certificatePem, privateKeyPem]) => { const certificate = forge.pki.certificateFromPem( certificatePem, ); const privateKey = forge.pki.privateKeyFromPem( privateKeyPem, ); const p7 = forge.pkcs7.createSignedData(); p7.content = new forge.util.ByteBuffer(fileContents); p7.addCertificate(certificate); p7.addSigner({ key: privateKey, certificate: certificate, digestAlgorithm: forge.pki.oids.sha256, authenticatedAttributes: [ { type: forge.pki.oids.contentType, value: forge.pki.oids.data, }, { type: forge.pki.oids.messageDigest }, { type: forge.pki.oids.signingTime, value: new Date(), }, ], }); p7.sign({ detached: true }); const result = this.stringToArrayBuffer( forge.asn1.toDer(p7.toAsn1()).getBytes(), ); resolve(result); }) .catch(reject); }); }, stringToArrayBuffer(binaryString) { const buffer = new ArrayBuffer(binaryString.length); let bufferView = new Uint8Array(buffer); for (let i = 0, len = binaryString.length; i < len; i++) { bufferView[i] = binaryString.charCodeAt(i); } return buffer; }, }, beforeUnmount() { PSPDFKit.unload('.pdf-container'); }, }; </script>
Step 7 — Calling the PSPDFKit.Instance#signDocument Method
-
Add a button to trigger the signing process.
-
With your
generatePKCS7
method in place, you can now call thePSPDFKit.Instance#signDocument
method in your main Vue component (App.vue
) to sign the document. ThesignDocument
method takes two arguments:-
Options object (can be
null
if using defaults). -
Callback that provides an
ArrayBuffer
containing the file contents.
-
In your App.vue
file, update the signDocument
method to call the signDocument
function as follows:
<template> <div id="app"> <label for="file-upload" class="custom-file-upload"> Open PDF </label> <input id="file-upload" type="file" @change="openDocument" class="btn" /> <!-- Step: Add the "Sign Document" button --> <button @click="signDocument">Sign Document</button> <!-- Use ref to reference the PSPDFKitContainer component --> <PSPDFKitContainer ref="pdfContainer" :pdfFile="pdfFile" @loaded="handleLoaded" /> </div> </template> <script> import PSPDFKitContainer from '@/components/PSPDFKitContainer'; export default { data() { return { pdfFile: this.pdfFile || '/document.pdf', instance: null, // Add instance to manage PSPDFKit instance. }; }, components: { PSPDFKitContainer, }, methods: { handleLoaded(instance) { console.log('PSPDFKit has loaded: ', instance); this.instance = instance; // Save the instance for later use. }, openDocument(event) { if (this.pdfFile && this.pdfFile.startsWith('blob:')) { window.URL.revokeObjectURL(this.pdfFile); } this.pdfFile = window.URL.createObjectURL( event.target.files[0], ); }, signDocument() { // Access the `PSPDFKitContainer` instance via `$refs`. const pdfContainer = this.$refs.pdfContainer; if (this.instance && pdfContainer) { this.instance .signDocument(null, pdfContainer.generatePKCS7) // Access the method in `PSPDFKitContainer`. .then(() => { console.log('Document signed.'); }) .catch((error) => { console.error( 'The document could not be signed.', error, ); }); } else { console.error('PSPDFKit instance is not loaded.'); } }, }, }; </script> <style> #app { font-family: Avenir, Helvetica, Arial, sans-serif; text-align: center; color: #2c3e50; } body { margin: 0; } input[type='file'] { display: none; } .custom-file-upload { border: 1px solid #ccc; border-radius: 4px; display: inline-block; padding: 6px 12px; cursor: pointer; background: #4a8fed; padding: 10px; color: #fff; font: inherit; font-size: 16px; font-weight: bold; } button { margin-top: 20px; padding: 10px 20px; background-color: #4a8fed; color: white; border: none; cursor: pointer; font-size: 16px; } </style>
In the code above:
-
The
signDocument
method callsinstance.signDocument(null, pdfContainer.generatePKCS7)
. -
This initiates the signing process by passing
null
for default options and thegeneratePKCS7
method as the callback.
-
Test the signing functionality by running the application:
npm run serve
Visit http://localhost:8080 in your browser. Load a PDF document and click the Sign Document button to sign it digitally.
Conclusion
In this post, you learned how to create a Vue.js signature pad using PSPDFKit’s library. If you have any issues doing this, don’t hesitate to reach out to our Support team for help.
If you’d like to test the PSPDFKit for Web SDK, you can request a free trial. Alternatively, you can browse our demo to see what our SDK is capable of.