Using PDF.JS with Ionic 3.x

/ August 21, 2017/ Ionic

(Last Updated On: April 30, 2018) A little while ago I was assigned to develop an Hybrid App using Ionic 3 for read and comment PDF files and was requested to use PDF.js. It was kind of difficult at the beginning because almost all the (scarce) documentation does not show directly how to work with Webpack based projects. Let’s see how I did it.

I will use a blank Ionic start project as base.

The first thing that we need to do is to install PDF.js on our project (the dist version) and the correspond Typings

npm install --save [email protected]
npm install --save-dev @types/pdfjs-dist

If you find find an unmet dependency warning UNMET PEER DEPENDENCY webpack@>=0.9 <2 || ^2.1.0-beta || ^2.2.0 just ignore it, it is not important now.

Now copy a PDF file to your assets folder src/assets/pdf1.pdf.

Change your project files to reflect this:

Ad:


home.ts

import { Component, ElementRef, ViewChild } from '@angular/core';
import { NavController } from 'ionic-angular';
import * as PDFJS from "pdfjs-dist/webpack.js";
import { PDFPageProxy, PDFPageViewport, PDFRenderTask } from 'pdfjs-dist';

@Component({
    selector: 'page-home',
    templateUrl: 'home.html'
})
export class HomePage {
    pdfDocument: PDFJS.PDFDocumentProxy;
    PDFJSViewer = PDFJS;
    pageContainerUnique = {
        width: 0 as number,
        height: 0 as number,
        element: null as HTMLElement,
        canvas: null as HTMLCanvasElement,
        textContainer: null as HTMLElement,
        canvasWrapper: null as HTMLElement
    }
    @ViewChild('pageContainer') pageContainerRef: ElementRef;
    @ViewChild('viewer') viewerRef: ElementRef;
    @ViewChild('canvas') canvasRef: ElementRef;
    @ViewChild('canvasWrapper') canvasWrapperRef: ElementRef;
    @ViewChild('textContainer') textContainerRef: ElementRef;

    constructor(public navCtrl: NavController) {
        console.log(this.PDFJSViewer);
    }

    ionViewDidLoad() {
        this.pageContainerUnique.element = this.pageContainerRef.nativeElement as HTMLElement;
        this.pageContainerUnique.canvasWrapper = this.canvasWrapperRef.nativeElement as HTMLCanvasElement;
        this.pageContainerUnique.canvas = this.canvasRef.nativeElement as HTMLCanvasElement;
        this.pageContainerUnique.textContainer = this.textContainerRef.nativeElement as HTMLCanvasElement;
        this.loadPdf('assets/pdf1.pdf');
    }
    
    loadPdf(pdfPath: string): Promise<boolean> {

        return this.PDFJSViewer.getDocument(pdfPath)
            .then(pdf => {
                this.pdfDocument = pdf;
                console.log("pdf loaded:"); console.dir(this.pdfDocument);
                return this.loadPage(1);
            }).then((pdfPage) => {
                console.dir(pdfPage);
            }).catch(e => {
                console.error(e);
                return false;
            });
    }

    loadPage(pageNum: number = 1) {
        let pdfPage: PDFPageProxy;

        return this.pdfDocument.getPage(pageNum).then(thisPage => {
            pdfPage = thisPage;
            return this.renderOnePage(pdfPage);
        }).then(() => {
            return pdfPage;
        });

    } // loadpage()



    async renderOnePage(pdfPage: PDFPageProxy) {

        let textContainer: HTMLElement;
        let canvas: HTMLCanvasElement;
        let wrapper: HTMLElement;

        let canvasContext: CanvasRenderingContext2D;
        let page: HTMLElement

        page = this.pageContainerUnique.element;
        textContainer = this.pageContainerUnique.textContainer;
        canvas = this.pageContainerUnique.canvas;
        wrapper = this.pageContainerUnique.canvasWrapper;

        canvasContext = canvas.getContext('2d') as CanvasRenderingContext2D;
        canvasContext.imageSmoothingEnabled = false;
        canvasContext.webkitImageSmoothingEnabled = false;
        canvasContext.mozImageSmoothingEnabled = false;
        canvasContext.oImageSmoothingEnabled = false;

        let viewport = pdfPage.getViewport(1) as PDFPageViewport;

        canvas.width = viewport.width;
        canvas.height = viewport.height;
        page.style.width = `${viewport.width}px`;
        page.style.height = `${viewport.height}px`;
        wrapper.style.width = `${viewport.width}px`;
        wrapper.style.height = `${viewport.height}px`;
        textContainer.style.width = `${viewport.width}px`;
        textContainer.style.height = `${viewport.height}px`;

        //fix for 4K


        if (window.devicePixelRatio > 1) {
            let canvasWidth = canvas.width;
            let canvasHeight = canvas.height;

            canvas.width = canvasWidth * window.devicePixelRatio;
            canvas.height = canvasHeight * window.devicePixelRatio;
            canvas.style.width = canvasWidth + "px";
            canvas.style.height = canvasHeight + "px";

            canvasContext.scale(window.devicePixelRatio, window.devicePixelRatio);
        }

        // THIS RENDERS THE PAGE !!!!!!


        let renderTask: PDFRenderTask = pdfPage.render({
            canvasContext,
            viewport
        });

        let container = textContainer;

        return renderTask.then(() => {
            //console.error("I WORK JUST UNTIL HERE");


            return pdfPage.getTextContent();

        }).then((textContent) => {

            let textLayer: HTMLElement;


            textLayer = this.pageContainerUnique.textContainer


            while (textLayer.lastChild) {
                textLayer.removeChild(textLayer.lastChild);
            }

            this.PDFJSViewer.renderTextLayer({
                textContent,
                container,
                viewport,
                textDivs: []
            });

            return true;
        });
    }
}
Ad:


home.html

<ion-header>
    <ion-navbar>
        <ion-title>
            PDFJS on IONIC
        </ion-title>
    </ion-navbar>
</ion-header>

<ion-content padding>
    <div #viewerContainer
         class="viewer-container">
        <div #viewer
             class="viewer">
            <ng-container #pagesContainer>
                <div #pageContainer
                     class="page"
                     [style.width.px]="pageContainerUnique.width"
                     [style.height.px]="pageContainerUnique.height">

                    <div class="canvas-wrapper"
                         #canvasWrapper>
                        <canvas class="page-canvas"
                                #canvas></canvas>

                    </div>
                    <div #textContainer
                         class="text-layer selectable">
                    </div>
                </div>
            </ng-container>
        </div>
    </div>
</ion-content>

Ad:


home.scss

page-home {
    .viewer-container {
        width: 100%;
        overflow: hidden;
        padding: 0 16px;
        .viewer {
            position: relative;
            width: 100%;
            .page {
                direction: ltr; 
                width: 100%;
                position: relative;
                overflow: visible;
                background-clip: content-box;
                background-color: white;
                margin: 0 auto 35px;
                .canvas-wrapper {
                    overflow: hidden;
                    position: absolute;
                } 
                &[data-loaded='true'] {
                    .textLayer {
                        margin: 0 auto;
                        position: relative;
                        border: 1px solid #b7b7b7;
                        box-shadow: 0px 0px 20px 1px rgba(0, 0, 0, 0.43);
                    }
                }
                .text-layer {
                     ::selection {
                        background: map-get( $colors, dark); //color: #fff;
                    }
                     ::-moz-selection {
                        background: map-get( $colors, dark); //color: #fff;
                    }
                    &.selectable {
                        -webkit-user-select: text;
                        -moz-user-select: text;
                        -ms-user-select: text;
                        user-select: text;
                    }
                    position: absolute;
                    left: 0;
                    top: 0;
                    right: 0;
                    bottom: 0;
                    overflow: hidden;
                    opacity: 0.2;
                    line-height: 1.0;
                    & > div {
                        color: transparent;
                        position: absolute;
                        white-space: pre;
                        cursor: cell;
                        -webkit-transform-origin: 0% 0%;
                        -moz-transform-origin: 0% 0%;
                        -o-transform-origin: 0% 0%;
                        -ms-transform-origin: 0% 0%;
                        transform-origin: 0% 0%;
                    }
                    cursor: cell;
                    &.selectable {
                        cursor: default;
                        & > div {
                            cursor: text;
                        }
                    }
                }
            }
        }
    }
}

The complete implementation will depend on what exactly do you need, this is just a little part of the code I needed but i think it is a good start point.

Ad:


Edit: 11.Feb.2018 I have to say as much as I respect this lib it is really hard to use inside an Angular project!

Spread the love

71
Leave a Reply

avatar
23 Comment threads
48 Thread replies
2 Followers
 
Most reacted comment
Hottest comment thread
23 Comment authors
Saninn Salas DiazrizalrmJoeajay vermaEnrico Didan Recent comment authors
  Subscribe  
newest oldest most voted
Notify of
Eduardo
Guest
Eduardo

Hi, i’m getting “PDFDocumentProxy not found”, I think i need to add the pdf.js worker to ionic, any tips?

Eduardo
Guest
Eduardo

I only had to change:

pdfDocument: PDFDocumentProxy;

to

pdfDocument: PDFJSViewer.PDFDocumentProxy;

and everything worked.

Pedro Henrique
Guest
Pedro Henrique

Hi Saninn, thanks a lot for show how you did the pdf viewer! I’m having an issue, ERROR Error: Uncaught (in promise): UnexpectedResponseException: Unexpected server response (416) while retrieving PDF “http://localhost:8100/assets/livro.pdf”. Do you know how i can solve that problem? I can click on close and then the pdf viewer… Read more »

Syed
Guest
Syed

It is giving error that

File URL not supported

when running on device. PDF does not render

Syed
Guest
Syed

and how to make it responsive?

Manoj
Guest
Manoj

Not getting what actually causes this.ERROR Error: Uncaught (in promise): DataCloneError: Failed to execute 'postMessage' on 'Worker': function (delegate, current, target, task, applyThis, applyArgs) { try { onEnter(zo...... } could not be cloned. Error: Failed to execute 'postMessage' on 'Worker': function (delegate, current, target, task, applyThis, applyArgs) { try {… Read more »

Ricardo
Guest
Ricardo

Hi there, do you know if it’s possible create annotate. I’ve been looking for, but without success.

Frederick
Guest
Frederick

Hello, thank you for this tutorial. Please am facing a challenge when trying to load pdf file from the device directory(cordova.file.externalDataDirectory) , please can you help me ,
thanks in advance

Rob
Guest
Rob

There seems to be a problem with import { PDFJS as PDFJSViewer } from 'pdfjs-dist/webpack';, VsCode warns me could not find a declaration file for module.

when running I get Cannot read property ‘getDocument’ of undefined. Is the webpack reference changed?

Any help would be appreciated.

Thanks!

amenkey
Guest
amenkey

thank for the tuto. l got cannot resolve symbol PDFJS. Please help

Jo Cool
Guest
Jo Cool

If the content of pdf is big, I cannot scroll horizontally. How do i enable scroll and zoom?

Kees Schouten
Guest
Kees Schouten

Thanks! Great starting point.

kishan
Guest
kishan

Bro, it is displaying only first page of the PDF, how to fix this?

Luca
Guest
Luca

Hi, i create my project ionic, load only frist page pdf, why?
Tnx

khaidaloo
Guest
khaidaloo

Hi, I’m having an issue
=={Uncaught (in promise): DataCloneError: The object could not be cloned. [email protected]://localhost:8100/build/vendor.js:131007:7 [email protected]://localhost:8100/build/vendor.js:130824:7 [email protected]://localhost:8100/build/vendor.js:130867:9 WorkerTransport_setupMessageHandler/</sink.onPull/<@http://localhost:8100/build/vendor.js:138918:13 F</l</[email protected]://localhost:8100/build/polyfills.js:3:14974 [email protected]://localhost:8100/build/vendor.js:5134:24 F</l</[email protected]://localhost:8100/build/polyfills.js:3:14901 F</c</[email protected]://localhost:8100/build/polyfills.js:3:10124 f/<@http://localhost:8100/build/polyfills.js:3:20240 F</l</[email protected]://localhost:8100/build/polyfills.js:3:15649 [email protected]://localhost:8100/build/vendor.js:5125:24 F</l</[email protected]://localhost:8100/build/polyfills.js:3:15562 F</c</[email protected]://localhost:8100/build/polyfills.js:3:10815 [email protected]://localhost:8100/build/polyfills.js:3:7887 F</h</[email protected]://localhost:8100/build/polyfills.js:3:16823 [email protected]://localhost:8100/build/polyfills.js:2:27646 [email protected]://localhost:8100/build/polyfills.js:2:27967}

please help me

Naruto
Guest
Naruto

I am working show and zoom pinch pdf in ionic by libary PDFJS.
How to zoom multi-page canvas by pdfjs in IONIC ???
Help me! Thanks.
Sorry my English !

Naruto
Guest
Naruto

Hi Saninn, thanks a lot for show how you did the pdf viewer! I’m having an issue, How to pinch zoom at a cursor point with gesture in IONIC. Tks!

DAMINI VYAS
Guest
DAMINI VYAS

Hey, what is the role of the text-container, Like I may omit it if I don’t want to display any text after the pdf? Or is it something related to the pdf?

Mustajab Jafry
Guest
Mustajab Jafry

Thanks for the great tutorial.
Can you give a demo to show a toolbar when pdf document is taped. I want to implement search, page up and page down and horizontal scrolling functionality.

Salvation
Guest
Salvation

hello diaz,
thanks for an excellent tutorials i need some help in adding and removing text selection with varying colors from pdf. i will appreciate if you can give me any pointer thanks

Enrico Didan
Guest
Enrico Didan

hi, i want to ask
how if file from FileChooser in native ionic ? how to show it in pdf?

ajay verma
Guest
ajay verma

Able to view pdf on browser using ionic serve ,but not able to view pdf on device(Testing on android right now). Please see below for error msg.Sorry for preview incomplete comment polyfills.js:3 Fetch API cannot load file:///android_asset/www/assets/imgs/11.pdf. URL scheme “file” is not supported. (anonymous) @ polyfills.js:3 t @ 0.js:1 value… Read more »

Joe
Guest
Joe

I can not import * as PDFJS from “pdfjs-dist/webpack.js”. I checked and saw file webpack.js is existed in node_module, but i can not import it in ts file

rizalrm
Guest
rizalrm

Hi Sanin,
thank you so much, you have share your knowledge
i have problem when i load file from URL, example i want view pdf document from http://www.africau.edu/images/default/sample.pdf. The error is “Error: PDFDocument: stream must have data”