Skip to content Skip to sidebar Skip to footer

Render .pdf To Single Canvas Using Pdf.js And ImageData

I am trying to read an entire .pdf Document using PDF.js and then render all the pages on a single canvas. My idea: render each page onto a canvas and get the ImageData (context.ge

Solution 1:

I can’t speak to the part of your code that renders the pdf into a canvas, but I do see some problems.

  • Every resetting canvas.width or canvas.height automatically clears the canvas contents. So in the top section, your clearRect is not needed because the canvas is cleared by canvas.width prior to your every page.render.
  • More importantly, in the bottom section, all your previous pdf drawings are cleared by every canvas resizing (oops!).
  • getImageData() gets an array where each pixel is represented by 4 consecutive elements of that array (red then green then blue then alpha). Since getImageData() is an array, so it doesn’t have a pages[i].width or pages[i].height—it only has a pages[i].length. That array length cannot be used to determine widths or heights.

So to get you started, I would start by changing your code to this (very, very untested!):

var pdf = null;
PDFJS.disableWorker = true;
var pages = new Array();
//Prepare some things
var canvas = document.getElementById('cv');
var context = canvas.getContext('2d');
var scale = 1.5;
var canvasWidth=0;
var canvasHeight=0;
var pageStarts=new Array();
pageStarts[0]=0;

PDFJS.getDocument(url).then(function getPdfHelloWorld(_pdf) {
    pdf = _pdf;
    //Render all the pages on a single canvas
    for(var i = 1; i <= pdf.numPages; i ++){
        pdf.getPage(i).then(function getPage(page){
            var viewport = page.getViewport(scale);
            // changing canvas.width and/or canvas.height auto-clears the canvas
            canvas.width = viewport.width;
            canvas.height = viewport.height;
            page.render({canvasContext: context, viewport: viewport});
            pages[i-1] = context.getImageData(0, 0, canvas.width, canvas.height);
            // calculate the width of the final display canvas
            if(canvas.width>maxCanvasWidth){
              maxCanvasWidth=canvas.width;
            }
            // calculate the accumulated with of the final display canvas
            canvasHeight+=canvas.height;
            // save the "Y" starting position of this pages[i]
            pageStarts[i]=pageStarts[i-1]+canvas.height;
            p.Out("pre-rendered page " + i);
        });
    }


    canvas.width=canvasWidth; 
    canvas.height = canvasHeight;  // this auto-clears all canvas contents
    for(var i = 0; i < pages.length; i++){
        context.putImageData(pages[i], 0, pageStarts[i]);
    }

});

Alternatively, here’s a more traditional way of accomplishing your task:

Use a single “display” canvas and allow the user to “page through” each desired page.

Since you already start by drawing each page into a canvas, why not keep a separate, hidden canvas for each page. Then when the user wants to see page#6, you just copy the hidden canvas#6 onto your display canvas.

The Mozilla devs use this approach in their pdfJS demo here: http://mozilla.github.com/pdf.js/web/viewer.html

You can check out the code for the viewer here: http://mozilla.github.com/pdf.js/web/viewer.js


Solution 2:

You can pass the number page to the promises , get that page canvas data and render in the right order on canvas

    var renderPageFactory = function (pdfDoc, num) {
        return function () {

            var localCanvas = document.createElement('canvas');

            ///return pdfDoc.getPage(num).then(renderPage);
            return  pdfDoc.getPage(num).then((page) => {
                renderPage(page, localCanvas, num);
            });


        };
    };

    var renderPages = function (pdfDoc) {
        var renderedPage = $q.resolve();
        for (var num = 1; num <= pdfDoc.numPages; num++) {
            // Wait for the last page t render, then render the next
            renderedPage = renderedPage.then(renderPageFactory(pdfDoc, num));
        }
    };

    renderPages(pdf);

Complete example

   function renderPDF(url, canvas) {



    var pdf = null;
    PDFJS.disableWorker = true;
    var pages = new Array();

    var context = canvas.getContext('2d');
    var scale = 1;

    var canvasWidth = 256;
    var canvasHeight = 0;
    var pageStarts = new Array();
    pageStarts[0] = 0;





    var k = 0;

    function finishPage(localCanvas, num) {
        var ctx = localCanvas.getContext('2d');

        pages[num] = ctx.getImageData(0, 0, localCanvas.width, localCanvas.height);

        // calculate the accumulated with of the final display canvas
        canvasHeight += localCanvas.height;
        // save the "Y" starting position of this pages[i]
        pageStarts[num] = pageStarts[num -1] + localCanvas.height;

        if (k + 1 >= pdf.numPages)
        {


            canvas.width = canvasWidth;
            canvas.height = canvasHeight;  // this auto-clears all canvas contents
            for (var i = 0; i < pages.length; i++) {
                context.putImageData(pages[i+1], 0, pageStarts[i]);
            }

            var img = canvas.toDataURL("image/png");
            $scope.printPOS(img);
        }

        k++;


    }

    function renderPage(page, localCanvas, num) {

        var ctx = localCanvas.getContext('2d');

        var viewport = page.getViewport(scale);


        // var viewport = page.getViewport(canvas.width / page.getViewport(1.0).width);
        // changing canvas.width and/or canvas.height auto-clears the canvas
        localCanvas.width = viewport.width;

        /// viewport.width = canvas.width;
        localCanvas.height = viewport.height;

        var renderTask = page.render({canvasContext: ctx, viewport: viewport});


        renderTask.then(() => {
            finishPage(localCanvas, num);
        });


    }





    PDFJS.getDocument(url).then(function getPdfHelloWorld(_pdf) {

        pdf = _pdf;



        var renderPageFactory = function (pdfDoc, num) {
            return function () {

                var localCanvas = document.createElement('canvas');

                ///return pdfDoc.getPage(num).then(renderPage);
                return  pdfDoc.getPage(num).then((page) => {
                    renderPage(page, localCanvas, num);
                });


            };
        };

        var renderPages = function (pdfDoc) {
            var renderedPage = $q.resolve();
            for (var num = 1; num <= pdfDoc.numPages; num++) {
                // Wait for the last page t render, then render the next
                renderedPage = renderedPage.then(renderPageFactory(pdfDoc, num));
            }
        };

        renderPages(pdf);






    });





}


Post a Comment for "Render .pdf To Single Canvas Using Pdf.js And ImageData"