Create a Custom PDF Viewer With PDF.js and Restrict Downloading & Printing
In this tutorial, I will create a custom PDF viewer with PDF.js, which will restrict users to download and print PDF file.
This is often required by many PDF books sellers who let the user to read their PDF books online on their websites but do not allow user to download or print their PDF file without making a certain payment of that PDF file or book.
Whenever you view any PDF file on any web browser such as Google Chrome, Mozilla Firefox, IE, Opera, Safari and etc. They all uses their custom PDF file viewer which allow user to download PDF file and also print those PDF files easily.
There are several paid solution available to this problem but I will share a way in which you can resolve the issue without making any payment.
PDF.js is the most useful library which will allow me to achieve my goal of creating a custom PDF viewer with JavaScript with read only feature.
I will also create a Next and Previous button which will allow me to move to next or previous page of PDF file.
I will also display the current and total pages of PDF file on the same custom PDF viewer on the top right side.
Steps to Create a Custom PDF Viewer With JavaScript
I will follow the below steps to create a custom PDF viewer with JavaScript through PDF.js.
- Create an index.php for PDF viewer
- Add HTML for PDF viewer
- Add CSS for navigation and view of PDF viewer
- Add PDF.js library and JavaScript
- Add PHP in header to configure the CORS
- Create .htaccess file to revoke direct PDF file access via URL
1. Create an index.php for PDF viewer
First of all create an index.php file which will act as PDF viewer file. Once the file is created I will add HTML, CSS, PDF.js and PHP code in it.
2. Add HTML for PDF viewer
Add the below HTML code for PDF viewer, pagination of Next, Previous buttons, current and total number of pages.
<div class="pagination">
<div class="wrap">
<button id="prev">Previous</button>
<button id="next">Next</button>
<span>Page: <span id="page_num"></span> /
<span id="page_count"></span></span>
</div>
</div>
<canvas id="the-canvas"></canvas>
3. Add CSS for navigation and view of PDF viewer
Add the below CSS to style your PDF viewer.
body{
background: black;
margin:0px;
}
.pagination{
background: #ffffff;
width: 100%;
float: left;
}
.pagination .wrap{
float:right;
width: 300px;
}
#the-canvas {
border: 1px solid black;
direction: ltr;
margin: 0 auto;
display: block;
}
@media print {
body {display:none;}
}
4. Add PDF.js library and JavaScript
Now, it is the time to add the PDF.js library and add required JavaScript to make it functional.
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js"></script>
<script>
// Disable Canvas Image Downloading
document.addEventListener('contextmenu', event => event.preventDefault());
// If absolute URL from the remote server is provided, configure the CORS
// header on that server.
var url = '<?php echo $pdf_file;?>'; // your file location and file name with ext.
// Loaded via <script> tag, create shortcut to access PDF.js exports.
var pdfjsLib = window['pdfjs-dist/build/pdf'];
// The workerSrc property shall be specified.
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js';
var pdfDoc = null,
pageNum = 1,
pageRendering = false,
pageNumPending = null,
scale = 2,
canvas = document.getElementById('the-canvas'),
ctx = canvas.getContext('2d');
/**
* Get page info from document, resize canvas accordingly, and render page.
* @param num Page number.
*/
function renderPage(num) {
pageRendering = true;
// Using promise to fetch the page
pdfDoc.getPage(num).then(function(page) {
var viewport = page.getViewport({scale: scale});
canvas.height = viewport.height;
canvas.width = viewport.width;
// Render PDF page into canvas context
var renderContext = {
canvasContext: ctx,
viewport: viewport
};
var renderTask = page.render(renderContext);
// Wait for rendering to finish
renderTask.promise.then(function() {
pageRendering = false;
if (pageNumPending !== null) {
// New page rendering is pending
renderPage(pageNumPending);
pageNumPending = null;
}
});
});
// Update page counters
document.getElementById('page_num').textContent = num;
}
/**
* If another page rendering in progress, waits until the rendering is
* finised. Otherwise, executes rendering immediately.
*/
function queueRenderPage(num) {
if (pageRendering) {
pageNumPending = num;
} else {
renderPage(num);
}
}
/**
* Displays previous page.
*/
function onPrevPage() {
if (pageNum <= 1) {
return;
}
pageNum--;
queueRenderPage(pageNum);
}
document.getElementById('prev').addEventListener('click', onPrevPage);
/**
* Displays next page.
*/
function onNextPage() {
if (pageNum >= pdfDoc.numPages) {
return;
}
pageNum++;
queueRenderPage(pageNum);
}
document.getElementById('next').addEventListener('click', onNextPage);
/**
* Asynchronously downloads PDF.
*/
pdfjsLib.getDocument(url).promise.then(function(pdfDoc_) {
pdfDoc = pdfDoc_;
document.getElementById('page_count').textContent = pdfDoc.numPages;
// Initial/first page rendering
renderPage(pageNum);
});
</script>
5. Add PHP in header to configure the CORS
Everything is ready now but it will not work because of Cross-Origin Resource Sharing (CORS) issue. Therefore, I will add the below PHP code to resolve the CORS issue in the top of index.php file.
// Allow from any origin
if (isset($_SERVER['HTTP_ORIGIN'])) {
// should do a check here to match $_SERVER['HTTP_ORIGIN'] to a
// whitelist of safe domains
header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Max-Age: 86400'); // cache for 1 day
}
// Access-Control headers are received during OPTIONS requests
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']))
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']))
header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}");
}
if(!isset($_GET['pdf_file']) || empty($_GET['pdf_file'])){
header("Location: error.php");
exit();
}else{
$pdf_file = $_GET['pdf_file'];
}
The above code also getting the PDF file name through $_GET[] super global variable. If it is an empty then you can not view the PDF file, file name must be available to read the file on server therefore the URL must contain pdf_file parameter, for example like below URL.
https://www.allphptricks.com/demo/2022/apr/custom-pdf-viewer/index.php?pdf_file=sample-file1.pdf
6. Create .htaccess file to revoke direct PDF file access via URL
This is the most important step, although the application is ready but user can still access the PDF file directly hitting the below URL in the browser.
https://www.allphptricks.com/demo/2022/apr/custom-pdf-viewer/sample-file1.pdf
Therefore, I will add some code that will revoke direct PDF file access via URL like above.
# Simple File List Access Restricter
RewriteEngine On
# 1) If NOT the current host
RewriteCond %{HTTP_HOST}@@%{HTTP_REFERER} !^([^@]*)@@https?://\1/.*
# 2) Deny access to these types
RewriteRule \.(gif|jpg|jpeg|png|tif|pdf|wav|wmv|wma|avi|mov|mp4|m4v|mp3|zip?)$ - [F]
Save the above file with .htaccess and placed it in the same folder where PDF files exist.
Conclusion
By following the above steps, you can easily create your custom PDF file viewer using JavaScript and PDF.js library.
I hope this will solve your issue and help you to achieve your desired goal to restrict PDF files downloading and printing.
If you found this tutorial helpful, share it with your friends and developers group.
I spent several hours to create this tutorial, if you want to say thanks so like my page on Facebook, Twitter and share it.
Facebook Official Page: All PHP Tricks
Twitter Official Page: All PHP Tricks
Hi Ulrich,
I’ve been using your example in projects, works perfect, but recently this viewer is broken, I see that also your example is broken, any idea what could causing this?
Dear Jimmy,
Thanks for the info, I will check and try to figure out the issue and try to fix it.
Dear Jimmy,
Issue has been resolved, PDF.js CDN URLs have been changed. Now it is working fine. Hope this will solve your issue.
Hello. Sounds like what I have been looking for, but I’m a dummy, I don’t code.
Where can I find someone to implement this for me. My mother passed away. She used to publish newsletters on how to grow cactus plants. I need a PDF embedder to put her journals online so they can’t be printed or downloaded. Thanks.
thanks sir above example was very useful. but i am trying to annotate the pdf and store only annotation in database and when same file is accessed the stored annotation should be applied to it.
Please can you help in this regard I am using php and mysql and pdfjs
Dear Raghavendra, my tutorial is just to display the PDF on browser, but you can not edit the PDF with this tutorial, adding annotate to PDF is like editing PDF which is not possible with this tutorial.
Great work and thank you.
It displays A4 pdf OK but when I tried a office365 excel pdf in A3 form it goes off the screen and it needs the scrolling bars. Would it be possible to display A3 in just the screen size.
Thanks again and sorry to ask
Phil
Great tutorial, thank you for sharing.
I have noticed that no URLs are active and cannot be clicked to open the URL?
How do you allow URL’s to be clicked and opened in browser?
Cheers!
Susan as you can see that we are using PDF.js library to achieve our desired goal.
Hi Javed,
This is a great project. Thank you for producing it. I have a real
need to apply this on my website but I am having a problem getting
the code to work. I am probably doing something wrong. Here’s the
string I am trying to use to start the viewer.
No matter what I try it just doesn’t work. Can you tell me why the viewer does not get the
input file name to load the PDF. The viewer box displays but not the
PDF. Any help would really be appreciated. Thank you.
Dear Fredrick,
Well, my code is working fine, I have tested it. If you are making any changes in the code, then I will need to view the code to find the issue.
Interesting project. Seems to work well.
Is there a way to display the zoom button? Can you re-flow the page to fit the viewport, without shrinking it (make a standard 8 1/2″ x 11″ page fit legibly on a smaller screen/device)?
Thanks….Rick..
Hi Rick, thanks for your input. I tried to explain this example as simple as possible. However, you can do modification as per your need. Just read out the documentation of PDF.js.
You have simply disabled java script in the browser so it can not be copied or downloaded.
Thanks for this so helpful and timely
You welcome Abbey.
Hi Javed, that’s great. So many thanks for all your tutorials!
Do you have something similar for restrict downloading images?
Have a nice day!
Hi Ulrich,
.htaccess file will not allow to download any jpg image file as well because I already mentioned these file extension as well.