Blog Post

How to Add a Signature to a PDF Using Vue.js

Hulya Masharipov
Illustration: How to Add a Signature to a PDF Using Vue.js
Information

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.

  1. You can install the CLI using npm — which comes with Node.js — or yarn:

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.

  1. 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

  1. Add the PSPDFKit dependency to your project:

npm install pspdfkit
  1. Once installed, create a js directory under the project location with the following command:

mkdir -p public/js
  1. 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

  1. Rename the PDF document you want to display in your application to document.pdf, and then add the PDF document to the public directory. You can use [this demo document][] as an example.

  2. Next, create a new component to serve as a wrapper for the PSPDFKit library. In the src/components/ folder, create a file named PSPDFKitContainer.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>
  1. In the src folder, replace the contents of the App.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>
  1. 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:

  1. Fetch the certificate and private key PEM files from the certs directory.

  2. Use node-forge to create a DER-encoded PKCS#7 signature.

  3. 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

  1. Add a button to trigger the signing process.

  2. With your generatePKCS7 method in place, you can now call the PSPDFKit.Instance#signDocument method in your main Vue component (App.vue) to sign the document. The signDocument method takes two arguments:

    1. Options object (can be null if using defaults).

    2. 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 calls instance.signDocument(null, pdfContainer.generatePKCS7).

  • This initiates the signing process by passing null for default options and the generatePKCS7 method as the callback.

  1. 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.

Author
Hulya Masharipov Technical Writer

Hulya is a frontend web developer and technical writer at Nutrient who enjoys creating responsive, scalable, and maintainable web experiences. She’s passionate about open source, web accessibility, cybersecurity privacy, and blockchain.

Explore related topics

Related products
Share post
Free trial Ready to get started?
Free trial