The PDF.js JavaScript library renders Portable Document Format files using the web standards-compliant HTML5 Canvas, which is led by the Mozilla Corporation after Andreas Gal launched it in 2011. Many PDF documents support interactive fields such as text boxes, checkboxes, and radio buttons to allow users to fill in data before downloading a filled-out copy of the file to their computer. Some PDFs also support annotations, which are notes and highlights drawn on a document that can be saved and shared with others.
In this tutorial, we will show you how to create a PDF viewer using PDF.js. We will be using Tailwind CSS to style the page. Tailwind CSS makes inline style super easy and we highly recommend it.
Firstly, let's create an HTML template named "index.html":
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>PDF Viewer</title>
</head>
<body>
</body>
</html>
Nothing special above, it is just a plain HTML skeleton.
In the header section of the page, include PDF.js and Tailwind CSS from their respective CDN.
Tailwind CSS is hosted on its CDN:
PDF.js is hosted on three free CDN:
Include them respectively before the closing header
tag:
<head>
...
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/pdfjs-dist@2.0.943/build/pdf.min.js"></script>
...
</head>
Let's design the PDF viewer using Tailwind CSS's utility classes:
<div id="pdf-viewer" class="max-w-5xl mx-auto flex flex-col gap-4 items-center justify-center py-10">
<div id="canvas-container" class="space-y-4">
<div class="flex w-full items-center justify-between">
<div id="nav" class="flex items-center justify-start gap-2">
<button id="prev"
class="py-2 px-3 bg-indigo-600 hover:bg-indigo-200 text-white transition delay-150 duration-300 ease-in-out">
←
</button>
<span id="current">1</span>
<button id="next"
class="py-2 px-3 bg-indigo-600 hover:bg-indigo-200 text-white transition delay-150 duration-300 ease-in-out">
→
</button>
</div>
<div class="flex items-center justify-end gap-2">
<button id="zoom-in"
class="rounded-full py-1 px-3 bg-indigo-600 hover:bg-indigo-200 text-white transition delay-150 duration-300 ease-in-out">
+
</button>
<button id="zoom-out"
class="rounded-full py-1 px-3 bg-indigo-600 hover:bg-indigo-200 text-white transition delay-150 duration-300 ease-in-out">
-
</button>
</div>
</div>
<canvas id="pdf-renderer" class="border"></canvas>
</div>
</div>
Paste the HTML code to the body section of the page, you should get something similar to the page below:
There are a couple of elements to take note:
pdf-renderer
: This is the canvas element we are going to load the PDF content to.prev
: This element controls the PDF viewer to move to its previous page.next
: This element controls the PDF viewer to move to its next page.zoom-in
: This element controls the PDF viewer to zoom in on the page by 50%.zoom-out
: This element controls the PDF viewer to zoom out the page by 50%.Now we have built the look and feel of the PDF viewer. The first function we are going to implement is to render the PDF file to the viewer.
Before the closing body
tag, paste the code below:
<script>
var myState = {
pdf: null,
currentPage: 1,
zoom: 1
}
pdfjsLib.getDocument('./sample.pdf').then((pdf) => {
myState.pdf = pdf;
render();
});
function render() {
myState.pdf.getPage(myState.currentPage).then((page) => {
var canvas = document.getElementById('pdf-renderer');
var ctx = canvas.getContext('2d');
var viewport = page.getViewport(myState.zoom);
canvas.width = viewport.width;
canvas.height = viewport.height;
page.render({
canvasContext: ctx,
viewport: viewport
});
});
}
</script>
</body>
</html>
The myState
variable stores the current state of our PDF viewer including the actual PDF.js object, current viewing page, and zoom level.
We then use pdfjsLib.getDocument
to load the local PDF via a promise.
The function function render()
figures which page of the PDF file to render with respective size and render it in the pdf-renderer
canvas element.
If we open the viewer on the browser now, we will notice that only a single page is rendered and we are not able to navigate through the PDF file.
Time to implement the page controls, paste the code below after the code previous section:
document.getElementById('prev')
.addEventListener('click', (e) => {
if (myState.pdf == null || myState.currentPage == 1)
return;
myState.currentPage -= 1;
document.getElementById('current').textContent = myState.currentPage;
render();
});
document.getElementById('next')
.addEventListener('click', (e) => {
if(myState.pdf == null || myState.currentPage > myState.pdf._pdfInfo.numPages)
return;
myState.currentPage += 1;
document.getElementById('current').textContent = myState.currentPage;
render();
});
The code above figures the correct page to render and call the render()
function to complete the task.
Currently, if we try to view the PDF file, the words might be a bit blurry due to the zoom level. We need to implement the zoom controls to make sure everyone can view the PDF content. Paste the code below after the code previous section:
document.getElementById('zoom-in')
.addEventListener('click', (e) => {
if(myState.pdf == null) return;
myState.zoom += 0.5;
render();
});
document.getElementById('zoom-out')
.addEventListener('click', (e) => {
if(myState.pdf == null) return;
myState.zoom -= 0.5;
render();
});
The code above figures the correct zoom level to render and call the render()
function to complete the task.
Now our PDF viewer should be working nicely with its page control as well as zoom controls: