Creating a Responsive Canvas for PDF.JS Viewer

I am facing an issue with displaying a PDF using the PDFJS library on my page. The fixed scale that I have set is causing the canvas, where the PDF is rendered, to not be responsive and fit within the bootstrap grid column width. Below is the HTML code snippet:

<div class="row">
   <div class="col-md-1" style="padding-right: 15px;">
     <input type="button" ng-click="openPreviousPage()"/>
   </div>
   <div class="col-md-8">
     <canvas id="the-canvas" style="border: 1px solid black;"></canvas>
    </div>
    <div class="col-md-1 col-md-offset-2" style="padding-right:15px;">
      <input type="button" ng-click="openNextPage()" />
    </div>
</div>

Here is the TypeScript code snippet used in the controller:

 openPage = (pdf: PDFDocumentProxy, pageNumber: number) => {
        pdf.getPage(pageNumber).then(function getPage(page) {
            var scale = 1;
            var viewport = page.getViewport(scale);

            var canvas = <HTMLCanvasElement>document.getElementById('the-canvas');
            var context = canvas.getContext('2d');
            canvas.height = viewport.height;
            canvas.width = viewport.width;


            var renderContext = {
                canvasContext: context,
                viewport: viewport
            };

            //...further actions
        });
    }

If anyone has any suggestions or hints on how to resolve this issue, it would be highly appreciated.

Answer №1

To easily customize the size of the canvas, assign an id to the container div and adjust the scale according to div.clientWidth and viewport.width.

In your HTML markup:

<div class="row">
  <div class="col-md-1" style="padding-right: 15px;">
    <input type="button" ng-click="openPreviousPage()"/>
  </div>
  <div class="col-md-8" id="canvas-container">
    <canvas id="main-canvas" style="border: 1px solid black;"></canvas>
  </div>
  <div class="col-md-1 col-md-offset-2" style="padding-right:15px;">
    <input type="button" ng-click="openNextPage()" />
  </div>
</div>

Then in the controller code:

loadPage = (pdf: PDFDocumentProxy, pageNumber: number) => {
    pdf.getPage(pageNumber).then(function onPageLoad(page) {
        var container = document.getElementById('canvas-container');
        var canvas = <HTMLCanvasElement>document.getElementById('main-canvas');
        var context = canvas.getContext('2d');

        var viewport = page.getViewport(1);
        var scale = container.clientWidth / viewport.width;
        viewport = page.getViewport(scale);

        canvas.height = viewport.height;
        canvas.width = viewport.width;

        var renderContext = {
            canvasContext: context,
            viewport: viewport
        };

        // Perform additional actions here...
    });
}

This approach should simplify the customization process.

Answer №2

If you are starting with the Acroforms example, you may have noticed that the viewport.width does not match what is being displayed on the screen when inspected. This discrepancy can lead to the PDF rendering larger than desired. In my case, the viewport.width was 612, but it was rendering to 816. I discovered that this issue stems from the CSS_UNITS variable within pdf_viewer.js. After researching further, I came across a discussion explaining its purpose:

"In the html/css world, 1in = 96pixels, whereas in PDF default, 1in = 72pixels. The scale is adjusted so a PDF of 8.5in should display as 8.5in on the screen at 100%."

To account for this variable, which is not global, I re-declared it locally and modified the scaling formula accordingly, as shown below:

    'use strict';

    PDFJS.workerSrc = '/assets/plugins/PDFjs/web/test/pdf.worker.js';

    var DEFAULT_URL = '/assets/plugins/PDFjs/web/test/form.pdf';
    var DEFAULT_SCALE = 1.0;
    var CSS_UNITS = 96/72;
    var container = document.getElementById('pageContainer');

    // Fetch the PDF document from the URL using promises.
    PDFJS.getDocument(DEFAULT_URL).then(function (doc) {

      // Use a promise to fetch and render the next page.
      var promise = Promise.resolve();
      for (var i = 1; i <= doc.numPages; i++) {
          promise = promise.then(function (pageNum) {
            return doc.getPage(pageNum).then(function (pdfPage) {

                var viewport = pdfPage.getViewport(DEFAULT_SCALE);
                var scale = container.clientWidth / (viewport.width * CSS_UNITS);

                // Create the page view.
                var pdfPageView = new PDFJS.PDFPageView({
                  container: container,
                  id: pageNum,
                  scale: scale,
                  defaultViewport: pdfPage.getViewport(scale),
                  annotationLayerFactory: new PDFJS.DefaultAnnotationLayerFactory(),
                  renderInteractiveForms: true,
                });

            // Associate the actual page with the view and draw it.
            pdfPageView.setPdfPage(pdfPage);
            return pdfPageView.draw();
          });
        }.bind(null, i));
      }
    });

This adjustment has resolved the sizing discrepancy for me. I hope sharing this information saves others time in troubleshooting similar issues. :)

Cheers!

Answer №3

To make your canvas responsive, utilize bootstrap and include the class = "img-fluid".

<div class="container" id="the-container">
    <canvas id="the-canvas" class="img-fluid" style="border: 1px solid black;"></canvas>
  </div>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

When resizing the screen, the contents of the Bootstrap 3 navbar disappear instead of collapsing as intended

I'm currently working on developing a responsive navigation bar that collapses when the screen size is reduced. However, I seem to be encountering an issue where the only visible element after resizing the screen is the header which remains active at ...

Guide to waiting for API responses with redux-saga

I have a React-Typescript app with backend calls using React Saga. I'm facing an issue where when one of my frontend functions makes a backend call, the next function starts executing before the previous one finishes. Currently, I'm using the SE ...

Combining the power of Visual Studio Code with NodeJs allows for seamless detection of missing package namespaces

Recently, I've encountered a frustrating problem. It occurs when: I create a new Node project without any installed modules I use import '' and press ctrl+space between the brackets, resulting in unnecessary inferred namespaces. Alth ...

Exploring the Possibilities of OpenLayers with Scalable Vector

Trying to create a webpage with an image that can be navigated using drag and scroll events, similar to Google Maps. Instead of building it from scratch, I attempted to achieve this using OpenLayers, with the intention of using the image in place of a map. ...

Creating a randomly generated array within a Reactjs application

Every time a user clicks a button in reactjs, I want to create a random array of a specific size. The code for generating the array looks like this: const generateArray = (arraySize: number): number[] => { return [...Array(arraySize)].map(() => ~~( ...

Encountering an error stating "Module 'custom-module' not found, along with its type declarations" when attempting to add a module from GitHub

After using the command yarn add github:username/custom-module to install a custom module in my project, I noticed that while the module and its contents are present in my node_modules folder, I am encountering difficulties importing anything from it. I a ...

Is it possible to deduce the types of a particular value based on an interface that is provided as a generic parameter?

As a newcomer to TypeScript, I may be approaching this issue the wrong way. I am curious if there is a generic type that can handle an interface like {[key: string]: string | boolean} and allow for knowing the specific type of any value inside the consumin ...

The attribute 'data' is not found within the type 'Promise<void | AxiosResponse>'

I am encountering this issue: Property 'data' is missing on type 'Promise<void | AxiosResponse<{ data: { email: string; username: string; }; }, any>>'.ts(2339) when trying to make a post request like so: const sendRequest = ...

leveraging the useReducer hook with the possibility of dispatching actions

I am seeking assistance in adding types to a reducer function that includes optional dispatch. Below is the source of the pattern: https://twitter.com/FernandoTheRojo/status/1521312171262681090?s=20&t=oerzPqJ8cb5Ts3sHVMH_5Q Here is the code snippet: [ ...

Broadening Cypress.config by incorporating custom attributes using Typescript

I'm attempting to customize my Cypress configuration by including a new property using the following method: Cypress.Commands.overwrite('getUser', (originalFn: any) => { const overwriteOptions = { accountPath: `accounts/${opti ...

The NullInjectorError has occurred due to the absence of a provider for the InjectionToken angularfire2.app

I am currently working on inserting form data into a Cloud Firestore database. Below is my x.component.ts file where I encountered an error in the constructor section: private firestore: AngularFireStore import { Component, OnInit } from '@angula ...

An issue has arisen with AngularJS and Twitter Bootstrap, displaying an error message stating that the function element.focus

I recently implemented the angularjs twitter bootstrap datepicker and everything seemed to be working smoothly. However, I encountered an error when trying to click on the text box for the popup datepicker. This issue has left me puzzled as I am still new ...

The style fails to load correctly upon the page's initial loading

I am utilizing <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="4d26282823603e212429283f0d7b63756378">[email protected]</a> in a <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="026c677a76 ...

I am encountering an issue when sending data using queryParams. It seems to work fine when I pass a string or number

i'm attempting to retrieve object data from one component and pass it to another using queryParams. I've noticed that when passing a string or number, everything works correctly. However, when trying to pass an object or array, all I get is [Obje ...

Checking the interceptor response in NestJs testing

I created a basic interceptor that removes a specific field from a response: import { CallHandler, ExecutionContext, Injectable, NestInterceptor, } from '@nestjs/common'; import { Observable } from 'rxjs'; import { map } ...

Creating a TypeScript function that automatically infers the type of the returned function using generics

Suppose I want to execute the generateFunction() method which will yield the following function: // The returned function const suppliedFunction = <T>(args: T) => { return true; }; // The returned function // This is how it can be used suppli ...

Searching for similar but not identical results using Knex.js

I am seeking a solution to retrieve similar posts without including the post itself. Here is my approach: export async function getSimilars(slug: string) { const excludeThis = await getBySlug(slug) const posts = await knex('posts') .whe ...

Setting up ESLint for TypeScript with JSX configuration

I am encountering problems with TypeScript configuration. Below is the code snippet from my tsconfig.json: { "compilerOptions": { "target": "es5", "lib": [ "dom", "dom.iterable", "esnext" ], "allowJs": true, "skipLib ...

What benefits does redux-thunk offer?

Why is redux-thunk necessary? It seems like using a thunk just adds an extra layer of complexity by wrapping expressions and using middleware. The sample code from redux-thunk further confuses the process. import thunk from 'redux-thunk'; // No ...

Generating a collection of objects containing a variable number of attributes

I want to generate an array of objects filled with random properties. Each object should have the following structure: { systemStar: "something random", planets: ["random1", "random2", etc]} Here's the current code I a ...