Create a Custom PDF Viewer With PDF.js and Restrict Downloading & Printing

Create a Custom PDF Viewer

Demo Download

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.

  1. Create an index.php for PDF viewer
  2. Add HTML for PDF viewer
  3. Add CSS for navigation and view of PDF viewer
  4. Add PDF.js library and JavaScript
  5. Add PHP in header to configure the CORS
  6. 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>
        &nbsp; &nbsp;
        <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://mozilla.github.io/pdf.js/build/pdf.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://mozilla.github.io/pdf.js/build/pdf.worker.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.

Demo Download

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

Article By
Javed Ur Rehman is a passionate blogger and web developer, he loves to share web development tutorials and blogging tips. He usually writes about HTML, CSS, JavaScript, Jquery, Ajax, PHP and MySQL.
  1. 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..

    1. 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.

  2. You have simply disabled java script in the browser so it can not be copied or downloaded.

  3. 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!

Leave a Reply

Your email address will not be published.