logo
post image

How to Display PDF in HTML Page using Javascript PDF.JS Library

Mozilla's PDF.JS is PDF viewer made with HTML5 technologies. It can help your application in custom rendering of PDF files — showing a PDF file in a <div> in your HTML page and browsing through its pages using Javascript.

Please note that PDF.JS is just a PDF viewer and not a PDF editor. It is not a library to create PDF files.

PDF.JS is used by Firefox internally to display PDF files. In fact PDF.JS is so good that even Opera decided to use it.

Example — Displaying PDF in a div Container

Click on the below button to display this PDF file.

Loading PDF ...
Page
of
Loading page ...

PDF.JS APIs Used in This Tutorial

When you include the PDF.JS script files, you get a pdfjsLib global object. This object provides a number of APIs that you can call in your Javscript code.

  • pdfjsLib.getDocument({ url: pdf_url })

    This asynchonous method loads the PDF file. The return value is a Promise which resolves with a PDFDocumentProxy object. In simple words, PDFDocumentProxy is the handle of the current PDF file.

    pdf_url is the url to a PDF file in your server. Cross Domain PDFs are allowed but CORS headers need to be set in the server.

    In case you would like to display the PDF during upload, a local url of the PDF can be generated through URL.createObjectURL() function.

    You can also pass binary data as a parameter. If you have a base-64 encoded data, you can convert it to a binary string through atob function.

    // normal url to a PDF or a local object url created while uploading PDF
    // pdf_doc holds the handle to the PDF document
    pdf_doc = await pdfjsLib.getDocument({ url: 'http://yourserver.com/sample.pdf' });
    
    // binary data
    pdf_doc = pdfjsLib.getDocument({ data: binary_data });
    

    Note that if using await to wait for the Promise to get settled, it needs to be wrapped in an async function.

  • pdf_doc.numPages

    It is a read-only property that gets the number of pages in the PDF file.

    var total_pages = pdf_doc.numPages;
  • pdf_doc.getPage(page_no)

    This asynchonous method loads the specified page of the PDF. The return value is a Promise which resolves with a PDFPageProxy object. In simple words, PDFPageProxy is the handle of the specified page of the PDF.

    Note that this function just loads the page, and not renders the page on the screen.

    // page holds the handle of the given PDF page number
    page = await pdf_doc.getPage(page_no);
    
  • page.getViewport(scale)

    This synchonous method returns the dimensions of the current page of the PDF (in px) at a specified zoom level.

    // get viewport at scale = 1
    var viewport = page.getViewport(1);
    
    // height of the page
    var height = viewport.height;
    
    // width of the page
    var width = viewport.width;
    
  • page.render(renderContext)

    This asynchonous method renders the current page of the PDF on the screen.

    Rendering can be done in either a <canvas> or <svg> element. In this tutorial, a canvas element is used.

    // get viewport at scale=1
    var viewport = page.getViewport(1);
    
    // holds viewport properties where page will be rendered
    var render_context = {
        canvasContext: document.querySelector('#pdf-canvas').getContext('2d'),
        viewport: viewport
    };
    
    // wait for the page to render
    await page.render(render_context);
    

Writing Code, Step 1 : Including PDF.JS Script Files

  • Go to PDF.JS Home Page and download the files. There will be 2 files in the "build" directory. Include them in your HTML. In this tutorial, version 2.2 of PDF.JS has been used.

    <script src="js/pdf.js"></script>
    <script src="js/pdf.worker.js"></script>
    

    PDF.JS files are pretty huge. It is better if you minify them. You can use an online UglifyJS minifier.

  • Alternatively you can include PDFJS from a CDN.
    <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.2.228/pdf.min.js"></script>

Step 2 : Preparing HTML

<button id="show-pdf-button">Show PDF</button> 

<div id="pdf-main-container">
    <div id="pdf-loader">Loading document ...</div>
    <div id="pdf-contents">
        <div id="pdf-meta">
            <div id="pdf-buttons">
                <button id="pdf-prev">Previous</button>
                <button id="pdf-next">Next</button>
            </div>
            <div id="page-count-container">Page <div id="pdf-current-page"></div> of <div id="pdf-total-pages"></div></div>
        </div>
        <canvas id="pdf-canvas" width="400"></canvas>
        <div id="page-loader">Loading page ...</div>
    </div>
</div>

  • #show-pdf-button button will start loading the PDF.
  • #pdf-loader is the container where a "PDF Loading" message would be shown while the PDF is being loaded.
  • #pdf-prev & #pdf-next are buttons that will go the Previous & Next page of the PDF.
  • #pdf-current-page will hold the current page no of the PDF.
  • #pdf-total-pages will hold the total number pages in the PDF.
  • #pdf-canvas is the canvas element where the PDF will be rendered.
  • #page-loader will show a "Page Loading" message while a page is being rendered.

Step 3 : Defining some Javascript variables

We need a few global variables that will hold properties used throughout the code.

var _PDF_DOC,
    _CURRENT_PAGE,
    _TOTAL_PAGES,
    _PAGE_RENDERING_IN_PROGRESS = 0,
    _CANVAS = document.querySelector('#pdf-canvas');

  • _PDF_DOC will hold the PDFDocumentProxy object that is resolved on the getDocument() Promise.
  • _CURRENT_PAGE will hold the current page number. _TOTAL_PAGES will hold the total no of pages in the PDF.
  • _PAGE_RENDERING_IN_PROGRESS is a flag that will hold whether a currently being rendered or not. If rendering of a page is in progress, then UI should not start rendering of another page. This is to prevent a page-content mismatch. Remember page rendering is asynchronous, it will take at least a few milliseconds to render a page.
  • _CANVAS will hold the canvas element.

Step 4 : Rendering the PDF with Javascript

Two custom functions shown below handle most of the code.

showPDF loads the PDF. It accepts the url of the PDF as parameter. On successful loading it calls the showPage function that will show the first page of the PDF.

showPage loads and renders a specified page of the PDF. While a page is being rendered, Previous and Next buttons are disbaled. A very important point is to note that we have to change the scale of the rendered page as per the width of the canvas element. In the current case, the width of the canvas element is less than the actual width of the PDF, so when PDF is rendered in the canvas it has to be scaled down.

Event handlers on the Previous / Next buttons simple decrement / increment the current page shown and call the showPage function.

var _PDF_DOC,
    _CURRENT_PAGE,
    _TOTAL_PAGES,
    _PAGE_RENDERING_IN_PROGRESS = 0,
    _CANVAS = document.querySelector('#pdf-canvas');

// initialize and load the PDF
async function showPDF(pdf_url) {
    document.querySelector("#pdf-loader").style.display = 'block';

    // get handle of pdf document
    try {
        _PDF_DOC = await pdfjsLib.getDocument({ url: pdf_url });
    }
    catch(error) {
        alert(error.message);
    }

    // total pages in pdf
    _TOTAL_PAGES = _PDF_DOC.numPages;
    
    // Hide the pdf loader and show pdf container
    document.querySelector("#pdf-loader").style.display = 'none';
    document.querySelector("#pdf-contents").style.display = 'block';
    document.querySelector("#pdf-total-pages").innerHTML = _TOTAL_PAGES;

    // show the first page
    showPage(1);
}

// load and render specific page of the PDF
async function showPage(page_no) {
    _PAGE_RENDERING_IN_PROGRESS = 1;
    _CURRENT_PAGE = page_no;

    // disable Previous & Next buttons while page is being loaded
    document.querySelector("#pdf-next").disabled = true;
    document.querySelector("#pdf-prev").disabled = true;

    // while page is being rendered hide the canvas and show a loading message
    document.querySelector("#pdf-canvas").style.display = 'none';
    document.querySelector("#page-loader").style.display = 'block';

    // update current page
    document.querySelector("#pdf-current-page").innerHTML = page_no;
    
    // get handle of page
    try {
        var page = await _PDF_DOC.getPage(page_no);
    }
    catch(error) {
        alert(error.message);
    }

    // original width of the pdf page at scale 1
    var pdf_original_width = page.getViewport(1).width;
    
    // as the canvas is of a fixed width we need to adjust the scale of the viewport where page is rendered
    var scale_required = _CANVAS.width / pdf_original_width;

    // get viewport to render the page at required scale
    var viewport = page.getViewport(scale_required);

    // set canvas height same as viewport height
    _CANVAS.height = viewport.height;

    // setting page loader height for smooth experience
    document.querySelector("#page-loader").style.height =  _CANVAS.height + 'px';
    document.querySelector("#page-loader").style.lineHeight = _CANVAS.height + 'px';

    var render_context = {
        canvasContext: _CANVAS.getContext('2d'),
        viewport: viewport
    };
        
    // render the page contents in the canvas
    try {
        await page.render(render_context);
    }
    catch(error) {
        alert(error.message);
    }

    _PAGE_RENDERING_IN_PROGRESS = 0;

    // re-enable Previous & Next buttons
    document.querySelector("#pdf-next").disabled = false;
    document.querySelector("#pdf-prev").disabled = false;

    // show the canvas and hide the page loader
    document.querySelector("#pdf-canvas").style.display = 'block';
    document.querySelector("#page-loader").style.display = 'none';
}

// click on "Show PDF" buuton
document.querySelector("#show-pdf-button").addEventListener('click', function() {
    this.style.display = 'none';
    showPDF('https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf');
});

// click on the "Previous" page button
document.querySelector("#pdf-prev").addEventListener('click', function() {
    if(_CURRENT_PAGE != 1)
        showPage(--_CURRENT_PAGE);
});

// click on the "Next" page button
document.querySelector("#pdf-next").addEventListener('click', function() {
    if(_CURRENT_PAGE != _TOTAL_PAGES)
        showPage(++_CURRENT_PAGE);
});

Browser Compatibility

The above code will work good in all major browsers, including IE 10+.

Enabling Text Selection ?

To enable text selection, some extra steps need to be followed. See How to Enable Text Selection in PDF.JS for more.