How to build a Node.js PDF editor with pdf-lib
In this tutorial, you’ll explore how to use the pdf-lib
library in Node.js to edit PDFs. It’ll cover various functionalities, including reading and writing PDF files, modifying existing PDFs, copying pages from different PDFs, and embedding images into PDF documents.
In the second part of the tutorial, you’ll learn about PSPDFKit API as an alternative solution for PDF editing. Leveraging the power of PSPDFKit API, you can efficiently manipulate PDF documents, streamline workflows, and enhance document management processes.
Comparing PSPDFKit API and pdf-lib
When choosing a Node.js PDF editor, it’s important to consider the available options and compare their features. The following sections will summarize the key points of PSPDFKit API and pdf-lib
to help you make an informed decision.
PSPDFKit API
PSPDFKit API is a hosted service that provides more than 30 tools for developers to build automated document processing workflows. Key benefits include:
-
Security — PSPDFKit API is SOC 2-compliant, ensuring the highest level of security for your document data. It doesn’t store any document data, and API endpoints are served through encrypted connections.
-
Easy Integration — PSPDFKit API provides well-documented APIs and code samples, making it easy to integrate into your existing workflows. The seamless integration saves you time and effort in development.
-
Versatile Actions — With PSPDFKit API, you have access to more than 30 tools and functionalities. You can perform various operations on a single document, such as conversion, OCR, rotation, watermarking, and more, with a single API call.
-
Transparent Pricing — PSPDFKit API offers transparent pricing based on the number of documents processed. It eliminates the need to consider factors like file size, merged datasets, or different API actions, providing straightforward pricing.
pdf-lib
-
Modification of Existing Documents —
pdf-lib
specializes in modifying (editing) existing PDF documents, allowing you to add text, images, and more to your PDFs. -
Wide JavaScript Environment Compatibility —
pdf-lib
works in all JavaScript environments, providing flexibility in usage. -
Open Source —
pdf-lib
is an open source library, which means it’s free to use and has an active community contributing to its development. -
Suitable for Basic Editing Needs —
pdf-lib
is a good option for basic PDF editing requirements, but it may not have all the advanced features and capabilities offered by a commercial solution like PSPDFKit API.
Both PSPDFKit API and pdf-lib
have their strengths and are suitable for different use cases. PSPDFKit API offers convenience and performance when processing documents, while pdf-lib
is a great open source project for simple PDF editing or document modifications.
Prerequisites for Building a pdf-lib and Node.js PDF Editor
Before you dive into building your Node.js PDF editor, ensure you have the following prerequisites:
-
Basic knowledge of JavaScript and Node.js.
-
Node.js installed on your system.
-
A text editor of your choice.
-
A package manager for installing packages. You can use npm or Yarn.
Setting Up the Project
You’ll begin by setting up a new Node.js project. Open your terminal and follow the steps outlined below.
-
Create a new directory for your project:
mkdir pdf-editor cd pdf-editor
-
Initialize a new Node.js project and install
pdf-lib
andaxios
:
npm init -y npm install pdf-lib axios
-
Create a new JavaScript file, e.g.
index.js
, and open it in your preferred code editor. -
Import the necessary dependencies:
const { PDFDocument, rgb, StandardFonts, degrees } = require('pdf-lib'); const fs = require('fs'); const axios = require('axios');
With the project set up, you can now implement the PDF editing functionality.
Reading and Writing PDF Files
Before you can edit a PDF, you need the ability to read existing PDF files and write modified PDFs to disk. You’ll create two functions — readPDF
and writePDF
— to handle these tasks:
async function readPDF(path) { const file = await fs.promises.readFile(path); return await PDFDocument.load(file); } async function writePDF(pdf, path) { const pdfBytes = await pdf.save(); await fs.promises.writeFile(path, pdfBytes); console.log('PDF saved successfully!'); }
The readPDF
function loads a PDF document from a file path and returns a PDFDocument
object. Meanwhile, the writePDF
function takes a PDFDocument
and a file path as parameters, saves the modified PDF, and writes it to the specified path.
To demonstrate the use, you’ll load an existing PDF document, access its content, and save it as a new file:
(async () => { const pdf = await readPDF('input.pdf'); console.log(pdf); // Access and use the loaded PDF object. // Perform modifications on the PDF document. const pdfDoc = await PDFDocument.create(); const page = pdfDoc.addPage(); page.drawText('Hello, PDF!'); await writePDF(pdf, 'output.pdf'); })();
Modifying PDFs: Adding Text with JavaScript
The pdf-lib
library allows you to modify existing PDF documents by adding various elements such as text, images, and more. In this section, you’ll see an example of how to add text to a PDF document using pdf-lib
.
Adding Text to a PDF
To add text to a PDF, use pdf-lib
library’s PDFDocument
API:
async function modifyPdf() { const filePath = 'input.pdf'; const existingPdfBytes = fs.readFileSync(filePath); const pdfDoc = await PDFDocument.load(existingPdfBytes); const helveticaFont = await pdfDoc.embedFont( StandardFonts.Helvetica, ); const pages = pdfDoc.getPages(); const firstPage = pages[0]; const { width, 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), }); const pdfBytes = await pdfDoc.save(); fs.writeFileSync('modified.pdf', pdfBytes); console.log('Modified PDF saved successfully!'); } // Usage. modifyPdf();
In this example, you start by loading an existing PDF document from a local file (input.pdf
). You then embed the Helvetica font using pdfDoc.embedFont
to ensure it’s available for use. Next, you access the first page of the PDF using pdfDoc.getPages()[0]
and obtain its width and height. Using the drawText
method, you add a text annotation to the page, specifying the position, font, size, color, and rotation.
Once the modifications are complete, you save the modified PDF as modified.pdf
using pdfDoc.save()
, and a success message is logged to the console.
Embedding Images into PDFs
In addition to text, you can also add images to a PDF using pdf-lib
:
async function embedImages() { const jpgUrl = 'https://pdf-lib.js.org/assets/cat_riding_unicorn.jpg'; const pngUrl = 'https://pdf-lib.js.org/assets/minions_banana_alpha.png'; const jpgResponse = await axios.get(jpgUrl, { responseType: 'arraybuffer', }); const pngResponse = await axios.get(pngUrl, { responseType: 'arraybuffer', }); const jpgImageBytes = jpgResponse.data; const pngImageBytes = pngResponse.data; const pdfDoc = await PDFDocument.create(); const jpgImage = await pdfDoc.embedJpg(jpgImageBytes); const pngImage = await pdfDoc.embedPng(pngImageBytes); const jpgDims = jpgImage.scale(0.5); const pngDims = pngImage.scale(0.5); const page = pdfDoc.addPage(); page.drawImage(jpgImage, { x: page.getWidth() / 2 - jpgDims.width / 2, y: page.getHeight() / 2 - jpgDims.height / 2 + 250, width: jpgDims.width, height: jpgDims.height, }); page.drawImage(pngImage, { x: page.getWidth() / 2 - pngDims.width / 2 + 75, y: page.getHeight() / 2 - pngDims.height + 250, width: pngDims.width, height: pngDims.height, }); const pdfBytes = await pdfDoc.save(); fs.writeFileSync('output3.pdf', pdfBytes); console.log('PDF saved successfully!'); } embedImages();
In this example, you use axios
to fetch two image files: a JPEG image (cat_riding_unicorn.jpg
), and a PNG image (minions_banana_alpha.png
). You create a new PDF document and embed the JPEG and PNG images into it. The images are scaled down by 50 percent and placed on a new page. The modified PDF is saved as output3.pdf
.
Copying Pages from Multiple PDFs
Another powerful feature of the pdf-lib
library is the ability to extract pages from different PDF documents and combine them into a new document:
async function copyPages() { const url1 = 'https://pdf-lib.js.org/assets/with_update_sections.pdf'; const url2 = 'https://pdf-lib.js.org/assets/with_large_page_count.pdf'; const firstDonorPdfResponse = await axios.get(url1, { responseType: 'arraybuffer', }); const secondDonorPdfResponse = await axios.get(url2, { responseType: 'arraybuffer', }); const firstDonorPdfBytes = firstDonorPdfResponse.data; const secondDonorPdfBytes = secondDonorPdfResponse.data; const firstDonorPdfDoc = await PDFDocument.load(firstDonorPdfBytes); const secondDonorPdfDoc = await PDFDocument.load( secondDonorPdfBytes, ); const pdfDoc = await PDFDocument.create(); const [firstDonorPage] = await pdfDoc.copyPages(firstDonorPdfDoc, [ 0, ]); const [secondDonorPage] = await pdfDoc.copyPages(secondDonorPdfDoc, [ 742, ]); pdfDoc.addPage(firstDonorPage); pdfDoc.insertPage(0, secondDonorPage); const pdfBytes = await pdfDoc.save(); fs.writeFileSync('output2.pdf', pdfBytes); console.log('PDF saved successfully!'); } copyPages();
In this example, you fetch two PDF documents (with_update_sections.pdf
and with_large_page_count.pdf
) from remote URLs using axios
. You load the PDF data using the PDFDocument.load
method from pdf-lib
to create PDFDocument
objects for each donor PDF. Then, using the copyPages
method, you combine specific pages from the donor PDFs into a new PDF. The modified PDF is saved as output2.pdf
.
Integrating with PSPDFKit API
PSPDFKit API is an HTTP API that provides powerful features for manipulating PDF files. With the PDF Editor API, you can perform various actions, such as merging, splitting, deleting, flattening, and duplicating PDF documents. Here are some key functionalities:
-
Merge — Combine multiple PDF files into a single document using the merge PDF API. This allows you to merge large files efficiently.
-
Split — Select specific pages from an existing PDF file and create a new document using the split PDF API.
-
Delete — Remove one or multiple pages from a PDF using the PDF page deletion API.
-
Flatten — Flatten annotations in single or multiple PDF files with the flatten PDF API, ensuring a simplified viewing experience.
-
Duplicate — Create a copy of a page within a PDF document using the duplicate PDF page API.
Requirements
To get started, you’ll need:
You also need to install the following dependencies for all the examples:
-
axios
— This package is used for making REST API calls. -
Form-Data — This package is used for creating form data.
npm install form-data axios
To access your PSPDFKit API key, sign up for a free account. Your account lets you generate 100 documents for free every month. Once you’ve signed up, you can find your API key in the Dashboard > API Keys section.
Merging PDFs Using PSPDFKit API
In this example, learn how to merge multiple PDF files using the Merge PDF API.
-
Create a new folder named
merge_pdf
and open it in a code editor. -
Create a new file named
processor.js
in the root of the folder. Copy the following code and paste it into the file:
const axios = require('axios'); const FormData = require('form-data'); const fs = require('fs'); const formData = new FormData(); formData.append( 'instructions', JSON.stringify({ parts: [ { file: 'first_half', }, { file: 'second_half', }, ], }), ); formData.append('first_half', fs.createReadStream('first_half.pdf')); formData.append('second_half', fs.createReadStream('second_half.pdf')); (async () => { try { const response = await axios.post( 'https://api.pspdfkit.com/build', formData, { headers: formData.getHeaders({ Authorization: 'Bearer YOUR_API_KEY', // Replace `YOUR_API_KEY` with your API key. }), responseType: 'stream', }, ); response.data.pipe(fs.createWriteStream('result.pdf')); } catch (e) { const errorString = await streamToString(e.response.data); console.log(errorString); } })(); function streamToString(stream) { const chunks = []; return new Promise((resolve, reject) => { stream.on('data', (chunk) => chunks.push(Buffer.from(chunk))); stream.on('error', (err) => reject(err)); stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')), ); }); }
Replace
YOUR_API_KEY
with your API key.
Add your PDF files to the merge_pdf
folder. In this example, you’re using first_half.pdf
and second_half.pdf
as your input files.
This code snippet demonstrates the process of merging two PDF files into a single PDF document. It utilizes the axios
library for making the API request, FormData
for handling form data, and fs
for file system operations. The code creates a new FormData
instance, appends the merging instructions and the PDF files to be merged, and then performs an API call to PSPDFKit API. The resulting merged PDF document is streamed and saved as result.pdf
.
Splitting PDFs Using PSPDFKit API
In this example, explore the process of splitting PDF files using the Split PDF API.
-
Create a new folder named
split_pdf
and open it in a code editor. -
Inside the
split_pdf
folder, create a new file namedprocessor.js
. Copy the following code and paste it into theprocessor.js
file:
// processor.js const axios = require('axios'); const FormData = require('form-data'); const fs = require('fs'); (async () => { const firstHalf = new FormData(); firstHalf.append( 'instructions', JSON.stringify({ parts: [ { file: 'document', pages: { end: -2, }, }, ], }), ); firstHalf.append('document', fs.createReadStream('input.pdf')); // Add your document here. const secondHalf = new FormData(); secondHalf.append( 'instructions', JSON.stringify({ parts: [ { file: 'document', pages: { start: -1, }, }, ], }), ); secondHalf.append('document', fs.createReadStream('input.pdf')); await executeRequest(firstHalf, 'first_half.pdf'); await executeRequest(secondHalf, 'second_half.pdf'); })(); async function executeRequest(formData, outputFile) { try { const response = await axios.post( 'https://api.pspdfkit.com/build', formData, { headers: formData.getHeaders({ Authorization: 'Bearer YOUR_API_KEY_HERE', // Replace with your API key. }), responseType: 'stream', }, ); response.data.pipe(fs.createWriteStream(outputFile)); } catch (e) { const errorString = await streamToString(e.response.data); console.log(errorString); } } function streamToString(stream) { const chunks = []; return new Promise((resolve, reject) => { stream.on('data', (chunk) => chunks.push(Buffer.from(chunk))); stream.on('error', (err) => reject(err)); stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')), ); }); }
Replace
YOUR_API_KEY_HERE
with your API key.
Add the PDF file you want to split and name it input.pdf
. Ensure the file is in the same folder as the processor.js
file.
The code snippet uses an immediately invoked async function expression (IIFE) to execute the PDF splitting process. It leverages the FormData
object to configure splitting instructions and attach the PDF file. With specified page ranges, you can precisely define the parts you need.
The executeRequest
function handles the API request to PSPDFKit API, ensuring a secure and reliable connection. It streams the response containing the split PDF content and saves it to separate output files using fs.createWriteStream
.
Deleting Pages Using PSPDFKit API
In this example, learn how to delete one or more pages from a PDF file using the Delete PDF Page API.
-
Create a new folder named
delete_pages
and open it in a code editor. -
Add the PDF file you want to delete pages from and name it
input.pdf
. -
Create a new file named
processor.js
and copy the following code into it:
const axios = require('axios'); const FormData = require('form-data'); const fs = require('fs'); const formData = new FormData(); formData.append( 'instructions', JSON.stringify({ parts: [ { file: 'document', pages: { end: 2, }, }, { file: 'document', pages: { start: 4, }, }, ], }), ); formData.append('document', fs.createReadStream('input.pdf')); // Add your document here. (async () => { try { const response = await axios.post( 'https://api.pspdfkit.com/build', formData, { headers: formData.getHeaders({ Authorization: 'Bearer YOUR_API_KEY_HERE', // Replace with your API key. }), responseType: 'stream', }, ); response.data.pipe(fs.createWriteStream('result.pdf')); } catch (e) { const errorString = await streamToString(e.response.data); console.log(errorString); } })(); function streamToString(stream) { const chunks = []; return new Promise((resolve, reject) => { stream.on('data', (chunk) => chunks.push(Buffer.from(chunk))); stream.on('error', (err) => reject(err)); stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')), ); }); }
Replace
YOUR_API_KEY_HERE
with your API key.
After importing the necessary packages, a FormData
object was created to hold the instructions for the API processing. In this case, the code demonstrates the removal of page number 4, but you can modify it to target a different page. The input document was read using the fs.createReadStream
function. The API call was made using axios
, and the resulting response was stored in a file named result.pdf
.
Flattening PDFs Using PSPDFKit API
Learn how to programmatically flatten PDFs using the Flatten PDF API. Streamline your workflow by automating the flattening process, ensuring accurate printing and compatibility with PDF viewers, and protecting form field data.
In this example, you’ll flatten all the annotations present in your provided document, effectively rendering them non-editable. This process guarantees that the annotations become a permanent part of the PDF and can no longer be modified.
-
Create a new folder named
flatten
and open it in a code editor. -
Add the PDF file you want to flatten and name it
input.pdf
. -
Create a new file named
processor.js
and copy the following code into it:
const axios = require('axios'); const FormData = require('form-data'); const fs = require('fs'); const formData = new FormData(); formData.append( 'instructions', JSON.stringify({ parts: [ { file: 'document', }, ], actions: [ { type: 'flatten', }, ], }), ); formData.append('document', fs.createReadStream('input.pdf')); // Add your document here. (async () => { try { const response = await axios.post( 'https://api.pspdfkit.com/build', formData, { headers: formData.getHeaders({ Authorization: 'Bearer YOUR_API_KEY_HERE', // Replace with your API key. }), responseType: 'stream', }, ); response.data.pipe(fs.createWriteStream('result.pdf')); } catch (e) { const errorString = await streamToString(e.response.data); console.log(errorString); } })(); function streamToString(stream) { const chunks = []; return new Promise((resolve, reject) => { stream.on('data', (chunk) => chunks.push(Buffer.from(chunk))); stream.on('error', (err) => reject(err)); stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')), ); }); }
Replace
YOUR_API_KEY_HERE
with your API key.
This code snippet creates a FormData
object with instructions to flatten the specified document. The API request is made using axios.post()
, and the flattened PDF response is saved as result.pdf
using a writable stream.
Duplicating Pages Using PSPDFKit API
In this example, you’ll explore how to duplicate specific PDF pages using the Duplicate PDF Page API provided by PSPDFKit. By duplicating PDF pages using the Duplicate PDF Page API, you can perform operations such as watermarking, merging, and preserving backups, allowing for enhanced document workflows with ease and automation.
-
Create a new folder named
duplicate_pages
and open it in a code editor. -
Add a PDF file you want to duplicate pages from and name it
input.pdf
. -
Create a new file named
processor.js
and copy the following code into it:
const axios = require('axios'); const FormData = require('form-data'); const fs = require('fs'); const formData = new FormData(); formData.append( 'instructions', JSON.stringify({ parts: [ { file: 'document', pages: { start: 0, end: 0, }, }, { file: 'document', }, { file: 'document', pages: { start: -1, end: -1, }, }, ], }), ); formData.append('document', fs.createReadStream('input.pdf')); (async () => { try { const response = await axios.post( 'https://api.pspdfkit.com/build', formData, { headers: formData.getHeaders({ Authorization: 'Bearer YOUR_API_KEY_HERE', // Replace with your API key. }), responseType: 'stream', }, ); response.data.pipe(fs.createWriteStream('result.pdf')); } catch (e) { const errorString = await streamToString(e.response.data); console.log(errorString); } })(); function streamToString(stream) { const chunks = []; return new Promise((resolve, reject) => { stream.on('data', (chunk) => chunks.push(Buffer.from(chunk))); stream.on('error', (err) => reject(err)); stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')), ); }); }
Replace
YOUR_API_KEY_HERE
with your API key.
In this code example, the first and last pages of a document are duplicated. By providing the specific pages to be duplicated, the code makes a request to PSPDFKit API. The API returns the duplicated PDF as a response, which is then saved as result.pdf
for further use.
Conclusion
In this tutorial, you explored the capabilities of the pdf-lib
library and learned how to build a PDF editor in Node.js. You covered various tasks, such as reading and writing PDF files, adding text annotations, combining pages from different PDFs, and embedding images.
In the second part of the tutorial, you learned about PSPDFKit API as an alternative solution for PDF editing. Leveraging the power of PSPDFKit API, you can efficiently manipulate PDF documents, streamline workflows, and enhance document management processes.
By comparing PSPDFKit API and pdf-lib
, this post summarized the key points of each solution to help you make an informed decision. PSPDFKit API offers advanced features, SOC 2-compliant security, easy integration, versatile actions, and transparent pricing. Meanwhile, pdf-lib
is suitable for basic editing needs, compatible with various JavaScript environments, and an open source solution.
To get started with PSPDFKit API, create a free account and obtain your API key. You can also check out the PSPDFKit API documentation for more information.
FAQ
How can I install the pdf-lib library in my Node.js project?
You can install pdf-lib using npm with the command npm install pdf-lib
.
What are the primary differences between pdf-lib and PSPDFKit API?
pdf-lib is an open source library suitable for basic PDF editing, while PSPDFKit API is a commercial solution with advanced features like document automation.
Can I use pdf-lib to add images to PDFs?
Yes, pdf-lib allows you to embed images in PDF documents using the embedJpg
and embedPng
methods.
Is PSPDFKit API a free tool?
PSPDFKit API offers a free tier that allows you to process up to 100 documents per month, but further usage requires a paid plan.
How do I merge PDF files using PSPDFKit API?
You can merge PDFs by sending a request to PSPDFKit API’s Merge endpoint, including the files you want to combine.