How to create a PDF Viewer in JavaScript

PDF.js is a JavaScript library maintained by Mozilla and designed for handling PDFs in JavaScript.

We are going to create a PDF viewer that has the following functionalities:

  • View a PDF
  • Go to the next page
  • Go to the previous page
  • Go to a particular page number

Steps

Create an index.html file that includes:

  • canvas → Where the pdf will be rendered.
  • previous button → To go to the previous page.
  • next button → To go to the next page.
  • input box → To enter a page number.
  • Go to page button → Button to go to a particular page.
  • 2-span elements → Display the current page number and total pages of the PDF.
<canvas id="pdf_canvas"></canvas>
<div>
        <button id="prev_page">Previos Page</button>
        <button id="next_page">next Page</button>
        <span id="current_page_num"></span>
            of
        <span id="total_page_num"></span>

        <input type="text" id="page_num">
        <button id="go_to_page">Go To Page</button>
</div>

<script src="//mozilla.github.io/pdf.js/build/pdf.js"></script>
<script src="script.js" charset="utf-8"></script>

Initializing the JavaScript file for rendering the PDF

In addition to the index.html file, we will create a script.js file where we can write our JavaScript code to create a PDF viewer.

Initialize the variables

let pdf ; // to store pdf data 
let canvas; // to render pdf
let isPageRendering; // to check if the pdf is currently rendering
let  pageRenderingQueue = null; // to store next page number to render
let canvasContext; // context of canvas
let totalPages; // total  pages of pdf
let currentPageNum = 1;

Next, add event listeners to handle the PDF renderer once the page loads:

window.addEventListener('load', function () {
    isPageRendering= false;
    pageRenderingQueue = null;
    canvas = document.getElementById('pdf_canvas');
    canvasContext = canvas.getContext('2d');
    
    initEvents(); Add events
    initPDFRenderer(); // render first page
});

Implement initPDFRenderer function

  • We need to initialize the PDF.js with a source PDF
  • We can use the getDocument method to get a promise that resolves to pdfData
  • The PDF data has a getPage function
  • The getPage will return a promise
  • Once the promise is resolved, we get the page data
  • We can then use the render method in the page data to render it in the canvas
function initPDFRenderer() {
    let url = 'https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf';
    // const url = 'filepath.pdf'; // to load pdf from our machine
    let option  = { url};
    
    pdfjsLib.getDocument(option)
            .promise
            .then( pdfData => {
                  totalPages = pdfData.numPages; // total number of pages 
                  let pagesCounter= document.getElementById('total_page_num'); // update total pages text
                  pagesCounter.textContent = totalPages;
                  // assigning read pdfContent to global variable
                  pdf = pdfData;
                  console.log(pdfData);
                  renderPage(currentPageNum);
            });
}

Now, when we call initPdfRenderer it will assign the pdfData to the PDF variable.

Add events for pagination buttons

Add events for previousButton, nextButton, and goToPage buttons.

function initEvents() {
    let prevPageBtn = document.getElementById('prev_page');
    let nextPageBtn = document.getElementById('next_page');
    let goToPage = document.getElementById('go_to_page');
    prevPageBtn.addEventListener('click', renderPreviousPage);
    nextPageBtn.addEventListener('click',renderNextPage);
    goToPage.addEventListener('click', goToPageNum);
}

Implement renderPage function

Now, let’s create a renderPage function to render the PDF page to the canvas.

function renderPage(pageNumToRender = 1) {
    isPageRendering = true; 
    document.getElementById('current_page_num').textContent = pageNumToRender;
    // use getPage method
    
    pdf
      .getPage(pageNumToRender)
      .then( page => {
        const viewport = page.getViewport({scale :1});
        canvas.height = viewport.height;
        canvas.width = viewport.width;  
        let renderCtx = {canvasContext ,viewport};
        
        page
          .render(renderCtx)
          .promise
          .then(()=> {
            isPageRendering = false;
            // this is to check if there is next page to be rendered in the queue
            if(pageRenderingQueue !== null) { 
                renderPage(pageRenderingQueue);
                pageRenderingQueue = null; 
            }
        });
    }); 
}

We have a method to get pdfData and render the page. Let’s write our pageRenderingQueue.

If the user clicks next page/previous page, it will add/subtract 1 to the currentPageNum and pass it to the renderPageQueue method. This will check if the pageRenderingQueue is null. If it is null, then we call the renderPage method, or else it will assign the page number that is to be rendered to the queue. Once the page rendering is complete, it will check if the pageQueue is empty and perform the respective action (if needed).

function renderPageQueue(pageNum) {
    if(pageRenderingQueue != null) {
        pageRenderingQueue = pageNum;
    } else {
        renderPage(pageNum);
    }
}

Let’s create a renderNextPage and renderPreviousPage method. If the user clicks:

  • next pagecurrentPageNum + 1 and render page.
  • previous pagecurrentPageNum — 1 and render page.
function renderNextPage(ev) {
    if(currentPageNum >= totalPages) {
        alert("This is the last page");
        return ;
    } 
    currentPageNum++;
    renderPageQueue(currentPageNum);
}
function renderPreviousPage(ev) {
    if(currentPageNum<=1) {
        alert("This is the first page");
        return ;
    }
    currentPageNum--;
    renderPageQueue(currentPageNum);
}

Now, let’s implement the “go to page number” function.

Get the page number from the input box, then check if the number is valid and call the renderPage method.

function goToPageNum(ev) {
    let numberInput = document.getElementById('page_num');
    let pageNumber = parseInt(numberInput.value);
    if(pageNumber) {
        if(pageNumber <= totalPages && pageNumber >= 1){
            currentPageNum = pageNumber;
            numberInput.value ="";
            renderPageQueue(pageNumber);
            return ;
        }
    }
    alert("Enter a valide page numer");
}

Free Resources

Attributions:
  1. undefined by undefined