How to Add Annotations to PDF Using Vue.js
In this tutorial, you’ll explore how to leverage the pdf-lib library in combination with Vue.js to seamlessly add annotations to PDF documents.
In the first part of this tutorial, you’ll explore how to integrate the pdf-lib library with Vue.js to add annotations to existing PDF documents. In the second part of the tutorial, you’ll take your PDF annotation capabilities to the next level with PSPDFKit. PSPDFKit is a comprehensive PDF SDK that offers advanced tools for rendering, annotating, and manipulating PDFs with exceptional performance.
What Are PDF Annotations?
PDF annotations are interactive elements added to a PDF document without modifying its content. They include text, graphics, highlights, and more, facilitating document review, collaboration, and interactive features like forms.
With Vue.js and other frontend technologies, annotations can now be done directly within web applications, ensuring user privacy, as all processing occurs client-side. This approach offers a seamless and secure annotation experience, ideal for situations where data privacy is crucial.
To learn more about annotation types, check out our PDF annotations post.
Understanding pdf-lib
pdf-lib is a powerful JavaScript library that provides functionality for creating, modifying, and extracting data from PDF documents. It’s built on top of modern web technologies and offers a straightforward API to interact with PDFs programmatically. pdf-lib is versatile and well-suited for frontend projects, making it an excellent choice for adding annotations to PDFs within Vue.js applications.
Prerequisites
Before starting, ensure you have Node.js and npm (Node Package Manager) installed on your system. Also, it’s important to have a basic understanding of Vue.js and JavaScript.
Step 1 — Project Setup
-
First, make sure you have Vue CLI installed globally. If not, you can install it using the following command:
npm install -g @vue/cli
-
Create a Vue project using the Vue CLI by running the following command:
vue create pdf-modifier-app
This will ask some configuration questions.
-
Select Default (Vue 3) ([Vue 3] babel, eslint) from the list, and change the directory to
pdf-modifier-app
:
cd pdf-modifier-app
-
Navigate to the project directory and create a new file called
PdfModifier.vue
. This will be a Vue.js component, and it’ll be located in thesrc/components
folder. The component will contain the template, script, and style sections.
Step 2 — Installing pdf-lib and axios
To use the pdf-lib library and fetch PDFs from URLs, you need to install the necessary dependencies. Open a terminal in your project directory and run the following commands:
npm install pdf-lib axios
Step 3 — Modifying the PdfModifier Component
Now, you’ll dive into the code to understand each part of the PdfModifier
component.
In the template section, add a button with a click event that will trigger the handleButtonClick
method when clicked:
<template>
<div>
<button @click="handleButtonClick">
Add Text and Download PDF
</button>
</div>
</template>
In the script section, import the necessary modules from the pdf-lib and axios libraries. Then, define the PdfModifier
Vue component and initialize the pdfUrl
data property with the URL of the PDF you want to modify:
<script> import { degrees, PDFDocument, rgb, StandardFonts } from 'pdf-lib'; import axios from 'axios'; export default { name: 'PdfModifier', data() { return { pdfUrl: 'https://pdf-lib.js.org/assets/with_update_sections.pdf', // URL to the PDF with update sections }; }, methods: { // ... (explained below) }, }; </script>
The modifyPdf
method fetches the existing PDF from the specified URL using axios. It then loads the PDF using pdf-lib’s PDFDocument.load
method. After embedding the Helvetica font, access the first page of the PDF. Using the drawText
method, add text to the page, setting style properties like color, size, and rotation. The modified PDF is then saved to the pdfBytes
variable using pdf-lib’s save
method, and finally, it’s returned:
async modifyPdf() { const url = this.pdfUrl; // Fetch the PDF using axios. const response = await axios.get(url, { responseType: 'arraybuffer' }); const existingPdfBytes = response.data; // Load the PDF using pdf-lib. const pdfDoc = await PDFDocument.load(existingPdfBytes); const helveticaFont = await pdfDoc.embedFont(StandardFonts.Helvetica); // Modify the PDF. const firstPage = pdfDoc.getPages()[0]; const { height } = firstPage.getSize(); firstPage.drawText('This text was added with JavaScript!', { x: 5, y: height / 2 + 300, size: 50, font: helveticaFont, color: rgb(0.95, 0.1, 0.1), rotate: degrees(-45), }); // Save the modified PDF to a variable (`pdfBytes`). const pdfBytes = await pdfDoc.save(); // Now, you can use `pdfBytes` for any additional operations (e.g. downloading the modified PDF). return pdfBytes; },
The handleButtonClick
method is called when the button is clicked. It invokes the modifyPdf
method to modify the PDF and obtain the pdfBytes
containing the modified PDF. It then calls the downloadPdf
method to initiate the download:
async handleButtonClick() { try { const pdfBytes = await this.modifyPdf(); this.downloadPdf(pdfBytes); } catch (error) { console.error('Error modifying PDF:', error); } },
The downloadPdf
method converts the pdfBytes
into a blob with the application/pdf
type. It creates a temporary anchor link with the blob’s URL and sets the download attribute to specify the file name. Clicking the anchor element initiates the download of the modified PDF:
downloadPdf(pdfBytes) { const blob = new Blob([pdfBytes], { type: 'application/pdf' }); const link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = 'modified_pdf.pdf'; link.click(); },
Step 4 — Running the Application
After setting up the project, run the application:
npm run serve
Click the Add Text and Download PDF button to modify the PDF and download the annotated version.
Leveraging PSPDFKit’s Vue.js PDF Annotation Library
When it comes to PDF annotations in a Vue.js application, you might wonder about options beyond pdf-lib. This is where PSPDFKit’s Vue.js PDF annotation library comes into play, providing a robust set of features:
- 17 annotation types — PSPDFKit offers a wide range of annotation types, with even more on the horizon.
- Customization — Tailor annotations to your needs by adjusting colors, shapes, and sizes.
- Cross-device syncing — With server deployment, annotations can be synced seamlessly across multiple devices.
- Real-time comment threads — Enable real-time conversations within documents, fostering collaboration (requires server deployment).
- Customizable tooltips — Design tooltips to suit your specific requirements.
- Framework support — PSPDFKit supports various web frameworks, eliminating the need to write APIs for different programming languages.
To explore the full capabilities of PSPDFKit and how it can enhance your project, take a look at our demo.
Integrating PSPDFKit with Vue.js
Now you’ll learn how to integrate PSPDFKit into your Vue.js project.
-
Create a new Vue.js project for PSPDFKit integration:
vue create pspdfkit-vue-project
This will ask some configuration questions.
-
Select Default (Vue 3) ([Vue 3] babel, eslint) from the list, and change the directory to
pspdfkit-vue-project
:
cd pspdfkit-vue-project
Adding PSPDFKit
-
Install
pspdfkit
as a dependency withnpm
:
npm install pspdfkit
-
Now, you can start building your Vue.js project. First, create a
js
directory under thepublic
directory. Go to your terminal and run:
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
This will copy the pspdfkit-lib
directory from within node_modules/
into the public/js/
directory to make it available to the SDK at runtime.
Displaying the PDF
-
Add the PDF document you want to display to the
public
directory. You can use our demo document as an example. -
Add a component wrapper for the PSPDFKit library and save it as
src/components/PSPDFKitContainer.vue
:
// src/components/PSPDFKitContainer.vue <template> <div class="pdf-container"></div> </template> <script> import PSPDFKit from 'pspdfkit'; export default { name: 'PSPDFKit', /** * The component receives the `pdfFile` prop, which is of type `String` and is required. */ props: { pdfFile: { type: String, required: true, }, }, /** * We wait until the template has been rendered to load the document into the library. */ mounted() { this.loadPSPDFKit().then((instance) => { this.$emit('loaded', instance); }); }, /** * We watch for `pdfFile` prop changes and trigger unloading and loading when there's a new document to load. */ watch: { pdfFile(val) { if (val) { this.loadPSPDFKit(); } }, }, /** * Our component has the `loadPSPDFKit` method. This unloads and cleans up the component and triggers document loading. */ methods: { async loadPSPDFKit() { PSPDFKit.unload('.pdf-container'); return PSPDFKit.load({ // To access the `pdfFile` from props, use `this` keyword. document: this.pdfFile, container: '.pdf-container', }); }, }, /** * Clean up when the component is unmounted so it's ready to load another document (not needed in this example). */ beforeUnmount() { PSPDFKit.unload('.pdf-container'); }, }; </script> <style scoped> .pdf-container { height: 100vh; } </style>
Here’s what’s happening in your component:
-
The
template
section is rendering adiv
with thepdf-container
class. This will help you declaratively bind the rendered DOM to the underlying component instance’s data. -
The
script
section is defining a Vue.js instance namedPSPDFKit
and creating methods for mounting, loading, and unloading PDF files into thepdf-container
. -
The
style
section is defining the height of the container.
-
Now, replace the contents of
src/App.vue
with the following:
// src/App.vue <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 || '/example.pdf', }; }, /** * Render the `PSPDFKitContainer` component. */ components: { PSPDFKitContainer, }, /** * Our component has two methods — one to check when the document is loaded, and the other to open the document. */ methods: { handleLoaded(instance) { console.log('PSPDFKit has loaded: ', instance); // Do something. }, openDocument() { // To access the Vue instance data properties, use `this` keyword. if (this.pdfFile) { window.URL.revokeObjectURL(this.pdfFile); } this.pdfFile = window.URL.createObjectURL( event.target.files[0], ); }, }, }; </script> <style> #app { 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>
-
In the
template
section, you have a file upload input and thePSPDFKitContainer
component.
Vue.js uses directives to handle some types of functionality. For the input field, you’re using the
v-on
directive to attach an event listener to the element. In your case, it’s thechange
event. There’s a shortcut tov-on
that removes the keyword and uses an@
symbol instead.
v-on:change="openDocument" v-on:loaded="handleLoaded" // or @change="openDocument" @loaded="handleLoaded"
Similar to the input field, for the PSPDFKitContainer
component, you’re using the v-bind
directive to bind the pdfFile
property to the pdfFile
property of the component and attaching an event listener for the loaded
event:
<PSPDFKitContainer :pdfFile="pdfFile" @loaded="handleLoaded" />
-
In the
script
section, you can see the implementation of thehandleLoaded
andopenDocument
methods. There’s also a data function that returns thepdfFile
property.
The data keeps track of reactive state within the current component. It’s always a function and returns an object. The object’s top-level properties are exposed via the component instance.
-
In the
style
section, there are styles for custom file input, and there are some general styles for theapp
.
-
Start the app:
npm run serve
You can see the application running on localhost:8080
.
In the demo application, you can open different PDF files by clicking the Open PDF button. You can add signatures, annotations, stamps, and more.
All the finished code is available on GitHub. 😎 You can find the example code for Vue 2 in the
vue-2
branch.
Adding Text Annotations to a PDF Using PSPDFKit and Vue.js
PSPDFKit provides the PSPDFKit.Annotations.TextAnnotation
class to create text annotations and customize them with various properties such as font, color, size, alignment, and position.
Inside the PSPDFKitContainer.vue
component, add the createTextAnnotation
method to add text annotations to the PDF:
<script> import PSPDFKit from 'pspdfkit'; export default { // ... Other component options. methods: { // ... Other methods. async createTextAnnotation({ PSPDFKit, instance }) { const annotation = new PSPDFKit.Annotations.TextAnnotation({ pageIndex: 0, // Specify the page number for the annotation. text: { format: 'plain', value: 'Welcome to PSPDFKit', // Text to embed. }, font: 'Helvetica', isBold: true, horizontalAlign: 'left', // Align the annotation to the center of the bounding box. boundingBox: new PSPDFKit.Geometry.Rect({ // Position of the annotation. left: 50, top: 200, width: 100, height: 80, }), fontColor: PSPDFKit.Color.BLUE, // Color of the text. }); // Attach this annotation to your PDF. const createdAnnotation = await instance.create(annotation); return createdAnnotation; }, }, }; </script>
In the mounted
lifecycle hook of the PSPDFKitContainer.vue
component, call the createTextAnnotation
method after loading PSPDFKit to add the text annotation:
<script> import PSPDFKit from 'pspdfkit'; export default { // ... Other component options. mounted() { this.loadPSPDFKit().then((instance) => { this.$emit('loaded', instance); this.createTextAnnotation({ PSPDFKit, instance }); // Call the method to add the text annotation. }); }, // ... Other methods. }; </script>
When you run the application, you can see the text annotation added to the PDF.
Adding Ink Annotations to a PDF Using PSPDFKit and Vue.js
To insert ink annotations into your Vue.js app using PSPDFKit, follow the steps below.
-
Inside the
PSPDFKitContainer.vue
file, add the followingcreateInkAnnotation
function:
// PSPDFKitContainer.vue <script> import PSPDFKit from 'pspdfkit'; export default { // ... (other component options) methods: { async createInkAnnotation({ x1, y1, x2, y2 }) { // Extract the `List`, `DrawingPoint`, `Rect`, and `InkAnnotation` properties from PSPDFKit. // These are needed to render annotations onto the screen. const { List } = PSPDFKit.Immutable; const { DrawingPoint, Rect } = PSPDFKit.Geometry; const { InkAnnotation } = PSPDFKit.Annotations; // Create your ink annotation. const annotation = new InkAnnotation({ pageIndex: 0, boundingBox: new Rect({ width: 400, height: 100 }), // position of annotation strokeColor: new PSPDFKit.Color({ r: 255, g: 0, b: 255 }), // color of stroke lines: List([ // Coordinates of stroke. List([ new DrawingPoint({ x: x1, y: y1 }), new DrawingPoint({ x: x2, y: y2 }), ]), ]), }); const instance = await this.loadPSPDFKit(); // Attach stroke to annotation: const createdAnnotation = await instance.create(annotation); return createdAnnotation; }, async addInkAnnotation() { await this.createInkAnnotation({ x1: 5, // Starting x-coordinate. y1: 5, // Starting y-coordinate. x2: 100, // Ending x-coordinate. y2: 100, // Ending y-coordinate. }); }, }, // ... (other component options) }; </script>
-
Call the
addInkAnnotation
method inside the mounted hook of thePSPDFKitContainer.vue
component:
<script> import PSPDFKit from 'pspdfkit'; export default { // ... (other component options) mounted() { PSPDFKit.load({ document: this.pdfFile, container: '.pdf-container', }).then((instance) => { this.$emit('loaded', instance); this.addInkAnnotation(instance); // Add the ink annotation after PSPDFKit is loaded. }); }, // ... (other component options) }; </script>
Conclusion
In this tutorial, you learned how to use the pdf-lib library with Vue.js to add text annotations to existing PDF documents and download the modified PDF. You also explored integrating PSPDFKit, a powerful PDF SDK, into a Vue.js project for advanced PDF annotation capabilities, including text and ink annotations. With these tools, you can create interactive and collaborative PDF annotation features in your Vue.js applications.
If you’re interested in exploring PSPDFKit further, you can request a free trial of our SDK or visit our demo page to experience the capabilities of our product firsthand.
FAQ
What types of annotations can I add to a PDF using Vue.js?
You can add various types of annotations, including text, highlights, drawings, and shapes, depending on the library you choose.
Is it necessary to have a backend server to add annotations?
No, you can add annotations directly in the client-side application using libraries like pdf-lib and PSPDFKit.
Can I integrate other PDF libraries with Vue.js?
Yes, you can integrate multiple libraries with Vue.js, but ensure they are compatible with the framework.
How can I download the modified PDF after adding annotations?
After making modifications, you can convert the PDF data into a blob and trigger a download in the browser.
Are there any performance considerations when adding annotations to large PDFs?
Yes, performance can be impacted, especially with larger PDFs; optimizing the rendering and annotation processes is recommended.