簡體   English   中英

使用pdf.js和ImageData將.pdf渲染為單個Canvas

[英]Render .pdf to single Canvas using pdf.js and ImageData

我試圖使用PDF.js讀取整個.pdf文檔,然后在單個畫布上渲染所有頁面。

我的想法:將每個頁面渲染到畫布上並獲取ImageData(context.getImageData()),清除畫布做下一頁。 我將所有ImageDatas存儲在一個數組中,一旦所有頁面都在那里,我想將數組中的所有ImageDatas放到一個畫布上。

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

謝謝你的幫忙。

PDF操作在所有階段都是異步的。 這意味着您還需要在最后一次渲染時捕獲承諾。 如果你沒有抓住它,你將只獲得一個空白畫布,因為在循環繼續到下一頁之前渲染沒有完成。

提示:我還建議您使用除了getImageData以外的其他內容,因為這將存儲未壓縮的位圖,例如data-uri而不是壓縮數據。

這是一種略微不同的方法,消除了for循環,並為此目的更好地使用promises:

生氣

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
}

由於圖像加載,它本質上也是異步的,這就是我們需要回調的原因。 如果您不想要異步性質,那么您也可以在渲染承諾上執行此步驟(創建和設置圖像元素),然后存儲圖像元素而不是data-uris。

希望這可以幫助!

我不能說你的代碼中將pdf渲染成畫布的部分,但我確實看到了一些問題。

  • 每次重置canvas.width或canvas.height都會自動清除畫布內容。 因此,在頂部,不需要clearRect,因為畫布在每個pages.render之前被canvas.width清除。
  • 更重要的是,在底部,所有以前的pdf圖紙都會被每個畫布大小調整(oops!)。
  • getImageData()獲取一個數組 ,其中每個像素由該數組的4個連續元素表示(紅色然后是綠色,然后是藍色,然后是alpha)。 因為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]);
    }

});

或者,這是一種更傳統的完成任務的方式:

使用單個“顯示”畫布,允許用戶“翻閱”每個所需頁面。

由於您已經開始將每個頁面繪制到畫布中,為什么不為每個頁面保留單獨的隱藏畫布。 然后,當用戶想要查看第6頁時,您只需將隱藏的畫布#6復制到顯示畫布上。

Mozilla開發人員在他們的pdfJS演示中使用這種方法: http ://mozilla.github.com/pdf.js/web/viewer.html

您可以在此處查看查看器的代碼: http//mozilla.github.com/pdf.js/web/viewer.js

您可以將數字頁面傳遞給promises,獲取該頁面畫布數據並在畫布上以正確的順序呈現

  

完整的例子

  

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM