DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workkloads.

Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Essential Security Measures for PDF Documents
  • Template-Based PDF Document Generation in JavaScript
  • Google Cloud Document AI Basics
  • Thumbnail Generator Microservice for PDF in Spring Boot

Trending

  • Segmentation Violation and How Rust Helps Overcome It
  • Optimize Deployment Pipelines for Speed, Security and Seamless Automation
  • How to Configure and Customize the Go SDK for Azure Cosmos DB
  • Why Documentation Matters More Than You Think
  1. DZone
  2. Coding
  3. JavaScript
  4. Building a PDF Viewer From Scratch

Building a PDF Viewer From Scratch

How to build a simple, custom PDF viewer using the PDF.js library. PDF.js is an open-source PDF viewer library by Mozilla, built with HTML5, that renders and parses PDFs.

By 
Albano Moura user avatar
Albano Moura
·
Oct. 30, 23 · Tutorial
Likes (1)
Comment
Save
Tweet
Share
8.3K Views

Join the DZone community and get the full member experience.

Join For Free

Portable Document Format (PDF) is widely used to share documents across various platforms. PDF is popular because it keeps the document's format and layout in such a way that no operating system or PDF viewer shows any signs of modification. PDFs are displayed using software tools called PDF viewers, which have many functionalities for interacting with documents, such as navigation, zooming in and out, and jumping to specific pages.

Each PDF viewer has specific capabilities, which limits the possibilities for new features. Hence, a developer might want to create their own PDF viewer software to cater to their particular needs and preferences, such as document analysis or data extraction. There are many advantages to creating a custom PDF viewer, some of which are given below:

  • Customization: Developing your own PDF viewer allows you to tailor its interface, features, and functionalities to suit your specific requirements.
  • Integration: Building a custom PDF viewer gives you seamless integration with your platform or application without sacrificing any features.
  • Security and privacy: A custom PDF viewer gives you control over security measures like encryption and access controls to ensure the confidentiality of sensitive content in a PDF file.

In this article, you'll be building a simple, custom PDF viewer using the PDF.js library. PDF.js  is an open-source PDF viewer library by Mozilla, built with HTML5, that renders and parses PDFs.

Developing a PDF Viewer Using the PDF.js Library

Before getting started, you'll need a code editor of your choice and the PDF.js library, which can be connected through a CDN (used in this tutorial) or downloaded from GitHub.

Setting Up the Environment

You'll first set up a Vanilla JS project that consists of a simple web page with HTML, CSS, and JavaScript functionality.

Set up a project folder named PDF Viewer and create three files in the folder as listed below:

  1. index.html
  2. styles.css
  3.  viewer.js

The project folder structure should look like this:

Project structure

Project structure

Creating the User Interface for the PDF Viewer

The next step is to create an interface to use the PDF viewer.

Open the index.html file, then enter the following code into the file and save it:

HTML
 
<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>Simple PDF Viewer</title>

  <link rel="stylesheet" href="styles.css">

</head>

<body>

  <h1>Simple PDF Viewer</h1>

  <div id="pdf-container"></div>

  <div id="page-controls">

    <button id="prev-page">Previous</button>

    <span id="page-num">Page 1</span>

    <button id="next-page">Next</button>

    <input type="number" id="go-to-page-input" placeholder="Go to Page">

    <button id="go-to-page-btn">Go</button>

  </div>

  <div>

    <button id="zoom-out">Zoom Out</button>

    <button id="zoom-in">Zoom In</button>

  </div>

  <input type="file" id="file-input" accept=".pdf">

</body>

</html>


The above code creates a simple interface with the following elements:

  • A container to display the PDF file
  • "Previous" and "Next" buttons to navigate the pages of the PDF file
  • An input field to jump to the specified page number of the PDF file
  • "Zoom In" and "Zoom Out" buttons
  • A button for choosing a PDF file from the user's local file explorer

Other than creating the user interface, there are two script tags for external JavaScript files that add functionality to the web page.

The first script tag, https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.min.js, is the link to the PDF.js library for rendering PDF documents. This integrates the PDF.js library into your project.

The second script tag, viewer.js, is the link to the custom JavaScript file that implements the PDF viewer functionality.

This concludes the code in the HTML file. The resulting web page should look like this:

PDF viewer

PDF viewer

Styling the Interface With CSS

While this tutorial is not focused on styling the UI, you can make it a little more visually appealing. Save the code below in styles.css:

CSS
 
/* styles.css */



body {

    display: flex;

    flex-direction: column;

    align-items: center;

    justify-content: center;

    height: 100vh;

    margin: 0;

    font-family: Arial, sans-serif;

  }



  #pdf-container {

    border: 1px solid #ccc;

    overflow: auto;

    width: 80%;

    max-width: 800px;

    height: 70vh;

  }



  .button-group, #zoom-controls, #file-controls {

    display: flex;

    align-items: center;

    margin-top: 10px;

  }



  .button-group button, #zoom-controls button, #file-controls button, #file-controls input[type="file"] {

    margin: 0 5px;

    padding: 5px 10px;

    border: none;

    background-color: #007bff;

    color: #fff;

    cursor: pointer;

    border-radius: 4px;

    font-size: 14px;

    transition: background-color 0.3s ease;

  }



  .button-group button:hover, #zoom-controls button:hover, #file-controls button:hover, #file-controls input[type="file"]:hover {

    background-color: #0056b3;

  }



  #go-to-page-input, #go-to-page-btn {

    margin-left: 5px;

  } 


The above code centers the elements, organizes a space for displaying the PDF, and aligns all the buttons and input fields.

The resulting web page should now look like this:

PDF viewer with CSS styling

PDF viewer with CSS styling

Adding Functionality With JavaScript Using PDF.js

You've now organized the interface of the PDF viewer with CSS. The final step is to add functionality with JavaScript and the PDF.js library.

First, to add functionality to the PDF viewer, you'll need to place all the relevant code into an event listener called DOMContentLoaded. This ensures that the JavaScript code is executed after the HTML document is fully loaded:

JavaScript
 
document.addEventListener('DOMContentLoaded', function() {

    // All the code here

});


You'll also need to enable interaction with the HTML (DOM) elements, such as buttons and input fields, through JavaScript. The document.getElementById() function allows access to the Document Object Model (DOM) elements, which allows you to create variables for every element to be used later:

JavaScript
 
const pdfContainer = document.getElementById('pdf-container');

const prevPageBtn = document.getElementById('prev-page');

const nextPageBtn = document.getElementById('next-page');

const pageNumSpan = document.getElementById('page-num');

const goToPageInput = document.getElementById('go-to-page-input'); 

const goToPageBtn = document.getElementById('go-to-page-btn'); 

const zoomOutBtn = document.getElementById('zoom-out');

const zoomInBtn = document.getElementById('zoom-in');

const fileInput = document.getElementById('file-input');


You'll then initialize the following three variables: pdfDoc, pageNum, and scale. The pdfDoc variable stores the PDF document instance that is loaded, the pageNum variable keeps track of the current page number displayed and the scale variable is used to keep track of the zoom scale for the zoom-in and out functionality:

JavaScript
 
let pdfDoc = null;

let pageNum = 1;

let scale = 1.0;


Next, the renderPage() function renders a specific page of the loaded PDF file. It fetches a PDF page, adjusts canvas dimensions to match the page, clears the container, and renders the page's content onto the canvas, effectively displaying the page within the container:

JavaScript
 
async function renderPage(num) {
  const page = await pdfDoc.getPage(num);

  const viewport = page.getViewport({ scale: scale });

  const canvas = document.createElement('canvas');

  const canvasContext = canvas.getContext('2d');


  canvas.height = viewport.height;

  canvas.width = viewport.width;

  pdfContainer.innerHTML = '';

  pdfContainer.appendChild(canvas);

  const renderContext = {

    canvasContext,

    viewport,

  };

  await page.render(renderContext);
}


The loadPDF() function loads a PDF document from a given URL using the pdfjsLib.getDocument() method. It then calls renderPage() to display the initial page and update the page count:

JavaScript
 
async function loadPDF(url) {
  const loadingTask = pdfjsLib.getDocument(url);

  pdfDoc = await loadingTask.promise;

  renderPage(pageNum);

  pageNumSpan.textContent = `Page ${pageNum} of ${pdfDoc.numPages}`;
}


Next, you'll add event listeners to multiple buttons for click functionality, such as:

  • The prevPageBtn and nextPageBtn buttons for page navigation, which also update the page number to be shown accordingly
  • The goToPageBtn button, which targets the value (page number) entered in the goToPageInput field to jump to a specific page of the PDF file
  • The zoomOutBtn and zoomInBtn buttons for zooming in and out of the displayed page
JavaScript
 
prevPageBtn.addEventListener('click', () => {

  if (pageNum > 1) {

    pageNum--;

    renderPage(pageNum);

    pageNumSpan.textContent = `Page ${pageNum} of ${pdfDoc.numPages}`;

  }

});



nextPageBtn.addEventListener('click', () => {

  if (pageNum < pdfDoc.numPages) {

    pageNum++;

    renderPage(pageNum);

    pageNumSpan.textContent = `Page ${pageNum} of ${pdfDoc.numPages}`;

  }

});



goToPageBtn.addEventListener('click', () => { // Event listener for the "Go to Page" button

  const targetPage = parseInt(goToPageInput.value);

  if (targetPage >= 1 && targetPage <= pdfDoc.numPages) {

    pageNum = targetPage;

    renderPage(pageNum);

    pageNumSpan.textContent = `Page ${pageNum} of ${pdfDoc.numPages}`;

  }

});



zoomOutBtn.addEventListener('click', () => {

  if (scale > 0.25) {

    scale -= 0.25;

    renderPage(pageNum);

  }

});



zoomInBtn.addEventListener('click', () => {

  if (scale < 3) {

    scale += 0.25;

    renderPage(pageNum);

  }

});


The final bit to add is the fileInput listener that checks for change to load and render any chosen file from the user's local file explorer:

JavaScript
 
fileInput.addEventListener('change', async (event) => {

  const selectedFile = event.target.files[0];

  if (selectedFile) {

    const fileURL = URL.createObjectURL(selectedFile);

    loadPDF(fileURL);

  }
});


The complete code for adding the functions is provided below. Open the already-created viewer.js file, then enter the following code and save it:

JavaScript
 
document.addEventListener('DOMContentLoaded', function() {

    const pdfContainer = document.getElementById('pdf-container');

    const prevPageBtn = document.getElementById('prev-page');

    const nextPageBtn = document.getElementById('next-page');

    const pageNumSpan = document.getElementById('page-num');

    const goToPageInput = document.getElementById('go-to-page-input'); // Added input element

    const goToPageBtn = document.getElementById('go-to-page-btn'); // Added button element

    const zoomOutBtn = document.getElementById('zoom-out');

    const zoomInBtn = document.getElementById('zoom-in');

    const fileInput = document.getElementById('file-input');

 

    let pdfDoc = null;

    let pageNum = 1;

    let scale = 1.0;

 

    async function renderPage(num) {

      const page = await pdfDoc.getPage(num);

      const viewport = page.getViewport({ scale: scale });

 

      const canvas = document.createElement('canvas');

      const canvasContext = canvas.getContext('2d');

 

      canvas.height = viewport.height;

      canvas.width = viewport.width;

      pdfContainer.innerHTML = '';

      pdfContainer.appendChild(canvas);

 

      const renderContext = {

        canvasContext,

        viewport,

      };

      await page.render(renderContext);

    }

 

    async function loadPDF(url) {

      const loadingTask = pdfjsLib.getDocument(url);

      pdfDoc = await loadingTask.promise;

      renderPage(pageNum);

      pageNumSpan.textContent = `Page ${pageNum} of ${pdfDoc.numPages}`;

    }

 

    prevPageBtn.addEventListener('click', () => {

      if (pageNum > 1) {

        pageNum--;

        renderPage(pageNum);

        pageNumSpan.textContent = `Page ${pageNum} of ${pdfDoc.numPages}`;

      }

    });

 

    nextPageBtn.addEventListener('click', () => {

      if (pageNum < pdfDoc.numPages) {

        pageNum++;

        renderPage(pageNum);

        pageNumSpan.textContent = `Page ${pageNum} of ${pdfDoc.numPages}`;

      }

    });

 

    goToPageBtn.addEventListener('click', () => { // Event listener for the "Go to Page" button

      const targetPage = parseInt(goToPageInput.value);

      if (targetPage >= 1 && targetPage <= pdfDoc.numPages) {

        pageNum = targetPage;

        renderPage(pageNum);

        pageNumSpan.textContent = `Page ${pageNum} of ${pdfDoc.numPages}`;

      }

    });

 

    zoomOutBtn.addEventListener('click', () => {

      if (scale > 0.25) {

        scale -= 0.25;

        renderPage(pageNum);

      }

    });

 

    zoomInBtn.addEventListener('click', () => {

      if (scale < 3) {

        scale += 0.25;

        renderPage(pageNum);

      }

    });

 

    fileInput.addEventListener('change', async (event) => {

      const selectedFile = event.target.files[0];

      if (selectedFile) {

        const fileURL = URL.createObjectURL(selectedFile);

        loadPDF(fileURL);

      }

    });

});


This concludes the development of a custom PDF viewer using PDF.js.

Testing the PDF Viewer

With the PDF viewer project completed using PDF.js, let's now try rendering some PDFs and see how it works.

If you render a PDF file, it will look like this:

PDF viewer testing

PDF viewer testing

Zoom in:

PDF viewer: zooming in

PDF viewer: zooming in



PDF viewer: zooming out

PDF viewer: zooming out


Next page:

PDF viewer: next page

PDF viewer: next page

PDF viewer: previous page

PDF viewer: previous page

Conclusion

In this article, you explored how to develop a custom PDF viewer from scratch using the PDF.js library. You can build on the steps here to create many variations of a PDF viewer, adding any functions you need.

You can find all the code used in this article in this GitHub repository.

Document PDF JavaScript

Opinions expressed by DZone contributors are their own.

Related

  • Essential Security Measures for PDF Documents
  • Template-Based PDF Document Generation in JavaScript
  • Google Cloud Document AI Basics
  • Thumbnail Generator Microservice for PDF in Spring Boot

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!