The Problem: PDFs Are Not HTML

When you first start building web applications, you get used to the flexibility of the Document Object Model (DOM). HTML is just structured text. JSON is just structured text. If you want to read an HTML file or a JSON payload, you parse the text, traverse the tree, and extract what you need. It feels intuitive.

Then, you encounter the Portable Document Format (PDF).

Skip reading — generate your PPT now

Our AI creates professional, editable slides from any topic in under 30 seconds. Free, no signup.

Generate Free PPT →

You might think, "I'll just fetch the PDF and read its contents like a text file." If you try to open a PDF in a standard text editor, you will be greeted by a wall of incomprehensible characters, strange symbols, and binary gibberish. PDFs are not structured text documents like HTML or Markdown. They are complex binary files.

A PDF is essentially a container. Inside this container, you have a rigid structure composed of dictionaries, cross-reference tables (xref), binary streams for images, and embedded fonts. The text itself is often stored in compressed streams, mapped to specific physical coordinates on a page. A PDF does not say, "Here is a paragraph of text." Instead, it says, "Draw the character 'H' using Arial at X coordinate 150 and Y coordinate 700. Then draw the character 'e' at X 162, Y 700."

This exact, coordinate-based rendering is what makes PDFs look identical on every device and printer in the world. It is a fantastic feature for visual consistency. However, it makes extracting text or modifying the layout an absolute nightmare for developers. You cannot just "edit" a paragraph in a PDF because the concept of a paragraph does not exist at the binary level.

For a long time, the only way to handle PDFs reliably was to send them to a backend server running heavy tools like Ghostscript, pdftk, or proprietary Java libraries. This meant uploading user files to a server, processing them, and sending them back. This approach is slow, expensive to scale, and raises serious privacy concerns.

Thankfully, the JavaScript ecosystem has evolved. We can now read, parse, edit, split, and merge PDFs entirely within the browser. No servers, no uploads, no privacy risks. Let us look at how you can build powerful PDF tools using two specific libraries.

Two Libraries, Two Jobs

To work with PDFs in the browser effectively, you need to understand that reading a PDF and writing a PDF are fundamentally different tasks. Because of this, we rely on two different, specialized libraries.

1. pdfjs-dist (Mozilla's pdf.js) — The Reader

Built by Mozilla, pdf.js is the engine that powers the built-in PDF viewer in Firefox. It is an incredibly robust, battle-tested library designed to do two things very well: render PDF pages onto HTML5 Canvas elements, and extract text/metadata from the PDF document structure.

If your goal is to extract the text content of a resume, display a preview of a document, or count the number of pages, you use pdf.js. It reads PDFs. It does not write them.

2. pdf-lib — The Writer

While pdf.js is great for reading, it cannot save a modified PDF. For that, we use pdf-lib. This library allows you to create PDF documents from scratch or modify existing ones. You can draw text, embed images, draw shapes, split a document into multiple files, or merge several PDFs together.

If your goal is to add a watermark to a document, fill out a PDF form, merge ten invoices into one file, or build a resume generator that exports an ATS-friendly file, you use pdf-lib. It writes and edits PDFs.

Together, these two libraries give you complete control over PDF files directly in the browser.

Step-by-Step: Reading PDF Text with pdf.js

Let us start with reading. Imagine you are building a feature that allows users to upload their resume, and you want to extract the text to pre-fill a web form. We will use pdfjs-dist for this.

Installation

First, install the library. Note that we are using the pdfjs-dist package, which is the distribution version of pdf.js.

npm install pdfjs-dist

Setup and Worker Initialization

pdf.js uses Web Workers to parse the PDF in the background. This prevents the heavy parsing logic from freezing the main UI thread. You must specify the path to the worker script.

import * as pdfjsLib from 'pdfjs-dist';

// Set the worker source. 
// In a real bundler setup like Vite or Webpack, you might need to point this to a local copy in your public folder.
pdfjsLib.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjsLib.version}/pdf.worker.min.js`;

Loading the File and Extracting Text

We will use a standard HTML file input to get the file from the user. We will read the file as an ArrayBuffer using the FileReader API. Then, we pass that buffer to pdf.js.

The extraction process involves looping through every page in the document, requesting the text content for that page, and joining the text items together.

// A simple function to extract text from an ArrayBuffer
async function extractTextFromPDF(arrayBuffer) {
  // Load the PDF document
  const loadingTask = pdfjsLib.getDocument({ data: arrayBuffer });
  const pdfDocument = await loadingTask.promise;
  
  const numPages = pdfDocument.numPages;
  let fullText = "";

  // Loop through each page sequentially
  for (let pageNum = 1; pageNum <= numPages; pageNum++) {
    const page = await pdfDocument.getPage(pageNum);
    
    // Get the text content (returns an object with an array of text items)
    const textContent = await page.getTextContent();
    
    // The items array contains objects with 'str' (the actual text)
    const pageStrings = textContent.items.map(item => item.str);
    
    // Join the strings for this page and add to the full text
    fullText += pageStrings.join(" ") + "\n\n";
  }

  return fullText;
}

// How you would use this with a file input:
document.getElementById('file-upload').addEventListener('change', async (event) => {
  const file = event.target.files[0];
  if (!file) return;

  const arrayBuffer = await file.arrayBuffer();
  
  try {
    const text = await extractTextFromPDF(arrayBuffer);
    console.log("Extracted PDF Text:", text);
    // Display the text in your UI
  } catch (error) {
    console.error("Failed to parse PDF:", error);
  }
});

This script reads the binary file locally, decodes the compressed streams, loops through the coordinate-mapped characters, and reconstructs the text flow as best as it can. It is fast, and because it runs client-side, the document remains entirely on the user's computer.

Step-by-Step: Editing a PDF with pdf-lib

Now let us look at editing. Suppose you want to take an existing PDF and add a custom watermark to the first page, or you want to merge two different PDFs together. We will use pdf-lib.

Installation

npm install pdf-lib

Adding Text to an Existing PDF

To modify an existing PDF, you load its binary data into a PDFDocument instance, fetch the pages, and then use drawing methods on those pages. Finally, you serialize the document back into a binary byte array.

import { PDFDocument, rgb, StandardFonts } from 'pdf-lib';

async function addWatermarkToPDF(fileArrayBuffer, watermarkText) {
  // Load the existing PDF
  const pdfDoc = await PDFDocument.load(fileArrayBuffer);
  
  // Embed a standard font
  const helveticaFont = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
  
  // Get all pages
  const pages = pdfDoc.getPages();
  const firstPage = pages[0];
  
  // Get page dimensions
  const { width, height } = firstPage.getSize();
  
  // Draw the text at the bottom left of the page
  firstPage.drawText(watermarkText, {
    x: 50,
    y: 50,
    size: 24,
    font: helveticaFont,
    color: rgb(0.95, 0.1, 0.1), // Red color
    opacity: 0.5, // Make it slightly transparent
  });
  
  // Serialize the PDFDocument to bytes (a Uint8Array)
  const pdfBytes = await pdfDoc.save();
  
  // Create a Blob to trigger a download
  const blob = new Blob([pdfBytes], { type: 'application/pdf' });
  const downloadUrl = URL.createObjectURL(blob);
  
  return downloadUrl; // You can set this URL to an <a> tag's href
}

Merging Multiple PDFs

Merging PDFs is a very common requirement. With pdf-lib, you create a new, empty PDFDocument, and then copy pages from multiple source documents into it.

import { PDFDocument } from 'pdf-lib';

async function mergePDFs(pdfBuffer1, pdfBuffer2) {
  // Create a new blank document
  const mergedPdf = await PDFDocument.create();

  // Load the source documents
  const doc1 = await PDFDocument.load(pdfBuffer1);
  const doc2 = await PDFDocument.load(pdfBuffer2);

  // Copy all pages from document 1
  const copiedPages1 = await mergedPdf.copyPages(doc1, doc1.getPageIndices());
  copiedPages1.forEach((page) => {
    mergedPdf.addPage(page);
  });

  // Copy all pages from document 2
  const copiedPages2 = await mergedPdf.copyPages(doc2, doc2.getPageIndices());
  copiedPages2.forEach((page) => {
    mergedPdf.addPage(page);
  });

  // Save the merged document
  const mergedPdfBytes = await mergedPdf.save();
  return mergedPdfBytes;
}

Splitting a PDF

Splitting a PDF is just the reverse of merging. You load the source document, create a new document, and only copy the specific pages you want to keep. If you want to extract just the first page of a document, you pass an array [0] to the copyPages method.

Real-World Usage at PPT Maker

We use these exact techniques extensively across the tools we build at PPT Maker. Because we handle educational and professional documents, user privacy and speed are critical. Running these operations in the browser solves both problems.

1. PDF-to-PPT Converter

Our PDF to PPT tool relies heavily on pdf.js. When a user drops a PDF into the browser, we use pdfjs-dist to parse the document and extract the raw text content from every page. We then structure that extracted text and pass it to the Gemini AI API to generate presentation slides. The PDF itself never goes to our servers; only the extracted, sanitized text goes to the AI.

2. The PDF Editor Suite

We built a comprehensive PDF Editor suite entirely on the client side using pdf-lib. Users can Merge PDF files seamlessly. They load multiple files into the browser memory, arrange the order, and we execute the exact merging logic shown above. We also offer tools to split and Compress PDF files—all without a backend server processing the heavy binary data.

3. Resume Builder

When users construct their professional profiles using our Resume Builder, the final output is generated dynamically in the browser using pdf-lib (often in conjunction with layout libraries). This allows us to ensure the generated PDF is perfectly structured with clean, readable text layers, making it highly ATS-friendly (Applicant Tracking System), unlike some canvas-based export tools that just spit out images inside a PDF wrapper.

The Privacy Advantage

Building PDF tools in the browser is not just a neat technical trick; it represents a fundamental shift in how we handle user data.

For years, processing a PDF meant uploading it. If a user wanted to merge their tax returns or edit an NDA, they had to trust an unknown third-party server to hold that document temporarily, process it, and delete it. As developers, maintaining those servers is expensive. Processing thousands of large binary files requires significant CPU and memory allocation.

By moving the computation to the client, you achieve a zero-trust, high-privacy architecture. The user's browser does the heavy lifting. The file stays local. As a developer, your server costs plummet because you are just serving static JavaScript bundles. It is a rare win-win in web development: cheaper to host, faster to run, and vastly more secure for the end-user.

Start Building

PDFs do not have to be a black box. With pdf.js for reading and extracting, and pdf-lib for writing and editing, you have all the tools you need to build robust document processing pipelines directly in the browser.

Want to see these concepts in action? Check out our suite of free, client-side tools at PPT Maker. Try our PDF Editor or convert your notes with the PDF to PPT generator, and experience the speed of browser-based processing yourself.

Frequently Asked Questions

Can I edit a PDF entirely in the browser without uploading to a server?

Yes, you can edit PDFs entirely within the browser using client-side JavaScript libraries like pdf-lib. This approach processes the binary file directly in the user's browser memory. Because the file never leaves the user's device, it is faster and significantly more secure than traditional server-side processing.

What is the difference between pdfjs-dist and pdf-lib?

pdfjs-dist (built by Mozilla) is primarily designed for reading and rendering PDFs. It is excellent at extracting text, images, and rendering pages onto HTML5 canvases. pdf-lib, on the other hand, is designed for creating and modifying PDFs. It allows you to draw text, add images, merge documents, and split pages. Use pdfjs-dist to read, and pdf-lib to write.

How do I extract text from a scanned PDF?

Standard JavaScript libraries like pdfjs-dist cannot extract text from scanned PDFs because the text is embedded as an image, not as actual font characters. To extract text from a scanned document, you need Optical Character Recognition (OCR) technology. You would typically use a library like Tesseract.js to perform OCR on the images extracted or rendered by pdf.js.

Can I merge multiple PDFs using JavaScript?

Yes. Using the pdf-lib library, you can easily merge multiple PDFs in the browser. You load each PDF into a separate PDFDocument instance, create a new blank PDFDocument, and then copy the pages from the source documents into the new document before saving it.

Is it safe to edit PDFs in the browser?

Editing PDFs in the browser is the safest method available. When you use client-side libraries, the file is never uploaded to a remote server. All parsing, modification, and exporting happen locally on your machine. This guarantees complete data privacy for sensitive documents like bank statements, legal contracts, or resumes.

How does PPT Maker's PDF editor work without server uploads?

PPT Maker's PDF tools use WebAssembly and pure JavaScript libraries running directly in your browser. When you select a file, it is loaded into the browser's memory using the File API. We then manipulate the binary data locally using tools like pdf-lib and generate a new downloadable file via Blob URLs, ensuring zero server contact.

Chandrakant Kelgire — BCA Student & Product Builder

Chandrakant Kelgire is a BCA student and the creator of Student Suite. He writes about AI tools, productivity hacks, and modern presentation techniques to help students and professionals save time and work smarter.