web-dev-qa-db-ja.com

pdf.jsとImageDataを使用して.pdfを単一のCanvasにレンダリングする

PDF.jsを使用して.pdfドキュメント全体を読み取り、すべてのページを単一のキャンバスにレンダリングしようとしています。

私の考え:各ページをキャンバスにレンダリングしてImageData(context.getImageData())を取得し、キャンバスをクリアして次のページを実行します。すべてのImageDataを配列に格納し、すべてのページがそこに配置されたら、配列のすべてのImageDataを単一のキャンバスに配置します。

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;
    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);
                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);
                context.clearRect(0, 0, canvas.width, canvas.height);
                p.Out("pre-rendered page " + i);
            });
        }

    //Now we have all 'dem Pages in "pages" and need to render 'em out
    canvas.height = 0;
    var start = 0;
    for(var i = 0; i < pages.length; i++){
        if(canvas.width < pages[i].width) canvas.width = pages[i].width;
        canvas.height = canvas.height + pages[i].height;
        context.putImageData(pages[i], 0, start);
        start += pages[i].height;
    }
    });

だから私はこれがうまくいくはずだということを理解していますか?これを実行すると、PDFのすべてのページを含めるのに十分な大きさのキャンバスが表示されますが、PDFは表示されません...

助けてくれてありがとう。

12
H_end-rik

PDFをキャンバスにレンダリングするコードの一部についてはお話しできませんが、いくつか問題があります。

  • everycanvas.widthまたはcanvas.heightをリセットすると、キャンバスの内容が自動的にクリアされます。したがって、上のセクションでは、すべてのpage.renderの前にcanvas.widthによってキャンバスがクリアされるため、clearRectは必要ありません。
  • さらに重要なのは、下部のセクションで、以前のすべてのPDF図面がすべてのキャンバスのサイズ変更(おっと!)によってクリアされることです。
  • getImageData()は、arrayを取得します。各ピクセルは、その配列の4つの連続する要素(赤、緑、青、アルファ)で表されます。 getImageData()は配列であるため、pages [i] .widthやpages [i] .heightはなく、pages [i] .lengthしかありません。その配列の長さを使用して幅または高さを決定することはできません。

だからあなたを始めるために、私はあなたのコードをこれに変更することから始めます(非常に、非常にテストされていません!):

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]);
    }

});

あるいは、ここでは、タスクを達成するためのより伝統的な方法を示します:

単一の「表示」キャンバスを使用して、ユーザーが目的の各ページを「ページ送り」できるようにします。

すでに各ページをキャンバスに描画することから始めているので、各ページに個別の非表示のキャンバスを保持してみませんか。次に、ユーザーがpage#6を表示したい場合は、非表示のcanvas#6を表示キャンバスにコピーするだけです。

Mozilla開発者は、pdfJSデモでこのアプローチを使用しています。 http://mozilla.github.com/pdf.js/web/viewer.html

ここでビューアのコードをチェックアウトできます: http://mozilla.github.com/pdf.js/web/viewer.js

8
markE

PDF操作はすべての段階で非同期です。これは、最後のレンダリングでもプロミスをキャッチする必要があることを意味します。それをキャッチしない場合、レンダリングが行われていないため、空白のキャンバスしか取得できませんループが次のページに続く前に終了していません。

ヒント:getImageData以外のものを使用することをお勧めします。これは、圧縮されていないビットマップ、たとえば圧縮データであるdata-uriなどを格納するためです。

これは、forループを排除するわずかに異なるアプローチであり、この目的のためにより良い約束を使用します:

LIVE FIDDLE

var canvas = document.createElement('canvas'), // single off-screen canvas
    ctx = canvas.getContext('2d'),             // to render to
    pages = [],
    currentPage = 1,
    url = 'path/to/document.pdf';              // specify a valid url

PDFJS.getDocument(url).then(iterate);   // load PDF document

/* To avoid too many levels, which easily happen when using chained promises,
   the function is separated and just referenced in the first promise callback
*/

function iterate(pdf) {

    // init parsing of first page
    if (currentPage <= pdf.numPages) getPage();

    // main entry point/function for loop
    function getPage() {

        // when promise is returned do as usual
        pdf.getPage(currentPage).then(function(page) {

            var scale = 1.5;
            var viewport = page.getViewport(scale);

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

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

            // now, tap into the returned promise from render:
            page.render(renderContext).then(function() {

                // store compressed image data in array
                pages.Push(canvas.toDataURL());

                if (currentPage < pdf.numPages) {
                    currentPage++;
                    getPage();        // get next page
                }
                else {
                    done();           // call done() when all pages are parsed
                }
            });
        });
    }

}

その後、ページを取得する必要がある場合は、単に画像要素を作成し、data-uriをソースとして設定します。

function drawPage(index, callback) {
    var img = new Image;
    img.onload = function() {
        /* this will draw the image loaded onto canvas at position 0,0
           at the optional width and height of the canvas.
           'this' is current image loaded 
        */
        ctx.drawImage(this, 0, 0, ctx.canvas.width, ctx.canvas.height);
        callback();          // invoke callback when we're done
    }
    img.src = pages[index];  // start loading the data-uri as source
}

画像の読み込みが原因で、画像も非同期になるため、コールバックが必要になります。非同期の性質が必要ない場合は、データURIの代わりに画像要素を格納する上記のレンダープロミスでこの手順(画像要素の作成と設定)を行うこともできます。

お役に立てれば!

20
user1693593

これは答えではありませんが、情報がより完全になるようなHTMLデータ全体です。 helloworld example は1ページしかレンダリングできないため、目的は最小限のpdf.jsソリューションを使用して複数のpdfページを表示することです。次のJavasScriptが機能していません。誰かが問題を解決できることを願っています。

<!doctype html>
<html>
<head>
<meta charset=utf-8>
<!-- Use latest PDF.js build from Github -->
<script src=https://raw.github.com/mozilla/pdf.js/gh-pages/build/pdf.js></script>
</head>
<body>
<canvas id=the-canvas style="border:1px solid black"></canvas>

<script>
var pdf = null;
PDFJS.disableWorker = true;
var pages = new Array();
var canvas = document.getElementById('the-canvas');
var context = canvas.getContext('2d');
var scale = 1.5;
var canvasWidth = 0;
var canvasHeight = 0;
var pageStarts = new Array();
pageStarts[0] = 0;
var url = 'pdfjs.pdf';

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);
      canvas.width = viewport.width;    // changing canvas.width and/or canvas.height auto-clears the canvas
      canvas.height = viewport.height;
      page.render({canvasContext:context, viewport:viewport});
      pages[i-1] = context.getImageData(0, 0, canvas.width, canvas.height);
      if(canvas.width>canvasWidth) {  // calculate the width of the final display canvas
        canvasWidth = canvas.width;
      }
      canvasHeight += canvas.height;   // calculate the accumulated with of the final display canvas
      pageStarts[i] = pageStarts[i-1] + canvas.height;    // save the "Y" starting position of this pages[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]);
  }
});
</script>

</body>
</html>
2
Randy Tang

あなたは番号ページをpromiseに渡し、そのページのキャンバスデータを取得し、キャンバス上で正しい順序でレンダリングできます。

    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);

完全な例

   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);






    });





}
0
Cesar