2013-03-11 12 views
9

Sto provando a leggere un intero documento .pdf utilizzando PDF.js e quindi il rendering di tutte le pagine su una singola tela.Rendering .pdf su singola tela utilizzando pdf.js e ImageData

La mia idea: rendere ogni pagina su una tela e ottenere ImageData (context.getImageData()), deselezionare la tela fare la pagina successiva. Conservo tutti i ImageDatas in un array e una volta che tutte le pagine sono lì, voglio mettere tutti gli ImageDatas dall'array su una singola tela.

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

Quindi dal modo in cui capisco cosa dovrebbe funzionare, giusto? Quando lo eseguo, finisco con la tela che è grande abbastanza per contenere tutte le pagine del pdf ma non mostra il pdf ...

Grazie per l'aiuto.

risposta

7

Non riesco a parlare con la parte del codice che rende il pdf su una tela, ma vedo alcuni problemi.

  • Ogni reset canvas.width o canvas.height cancella automaticamente il contenuto di tela. Quindi nella parte superiore, clearRect non è necessario perché la tela viene cancellata da canvas.width prima di ogni page.render.
  • Ancora più importante, nella sezione inferiore, tutti i precedenti disegni PDF vengono cancellati da ogni ridimensionamento del canvas (oops!).
  • getImageData() ottiene un array in cui ogni pixel è rappresentato da 4 elementi consecutivi di tale array (rosso poi verde poi blu poi alfa). Poiché getImageData() è un array, quindi non ha pagine [i] .width o pagine [i] .height: ha solo pagine [i] .length. La lunghezza dell'array non può essere utilizzata per determinare larghezze o altezze.

Quindi, per iniziare, vorrei iniziare modificando il codice per questo (molto, molto testato!):

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

}); 

In alternativa, ecco un modo più tradizionale di realizzare il vostro compito :

Utilizzare una singola tela "display" e consentire all'utente di "sfogliare" ogni pagina desiderata.

Poiché si inizia già disegnando ogni pagina in una tela, perché non tenere una tela separata e nascosta per ogni pagina. Quindi, quando l'utente vuole vedere la pagina n. 6, basta copiare la tela nascosta n. 6 sulla tela del display.

Gli sviluppatori di Mozilla utilizzare questo approccio nelle loro pdfJS demo qui: http://mozilla.github.com/pdf.js/web/viewer.html

è possibile controllare il codice per lo spettatore qui: http://mozilla.github.com/pdf.js/web/viewer.js

+0

La compensazione di contenuti davvero si è rivelato essere un problema grazie ^^ –

+0

@markE, Ho provato a soluzione, ma non ha funzionato. Tutto ciò di cui ho bisogno è usare l'esempio di Helloworld per visualizzare tutte le pagine pdf (il progetto pdf.js è troppo complicato e non si adatta alle mie esigenze). Consiglieresti alcune correzioni? –

+0

Molti/tutti i browser impongono un [limite di dimensioni massime] (http://stackoverflow.com/questions/6081483/maximum-size-of-a-canvas-element) su elementi canvas, quindi per PDF sufficientemente grandi, ha vinto ' t lavorare comunque. Ultimamente ho lottato con tutto questo, e la soluzione migliore IMO era, come suggerivi, di mostrare una pagina alla volta. – Marxama

1

Questa non è una risposta, ma un insieme di dati HTML in modo tale che l'informazione potrebbe essere più completa. L'obiettivo è utilizzare una soluzione minima pdf.js per visualizzare più pagine PDF perché lo helloworld example può eseguire il rendering di una sola pagina. Il seguente JavasScript non funziona, speriamo che qualcuno possa risolvere il problema.

<!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> 
15

Le operazioni PDF sono asincrone in tutte le fasi. Ciò significa che devi anche cogliere la promessa anche nell'ultimo rendering.Se non lo prendi, otterrai una tela bianca solo quando il rendering non sarà terminato prima che il ciclo continui alla pagina successiva.

Suggerimento: Raccomanderei anche di utilizzare qualcosa di diverso da getImageData poiché questo memorizzerà bitmap non compressi, ad esempio invece il data-uri che è dati compressi.

Ecco un approccio leggermente diverso eliminando il ciclo for e utilizza le migliori promesse per questo scopo:

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

} 

Quando poi bisogno di recuperare una pagina è sufficiente creare un elemento immagine e impostare il data-uri come fonte:

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 
} 

A causa dell'immagine carica Sarà anche di natura asincrona ed è per questo che abbiamo bisogno del callback. Se non si desidera la natura asincrona, si potrebbe anche fare questo passo (creando e impostando l'elemento immagine) nella promessa di rendering sopra la memorizzazione degli elementi dell'immagine invece di data-uris.

Spero che questo aiuti!

+0

Grazie mille per la risposta, Espistemex. Ho provato soluzioni ma non ci sono riuscito. A causa della mia scarsa familiarità con la tela, per favore mi illumini ulteriormente. Domande: 1) Ho appena aggiunto un'altra dichiarazione "var url = 'example.pdf';" di fronte ai tuoi codici per specificare la fonte del file pdf. È corretto? 2) Non ho idea di come usare il secondo pezzo del tuo codice (la parte img). Vuoi approfondire di più? Grazie. –

+1

@ yltang52 Ho aggiunto un violino/demo. L'url deve essere specificato prima con un url relativo relativo o assoluto valido. Ho aggiunto più commenti/informazioni nella risposta, ma forse la demo è ancora più chiara in quanto mostra cosa succede. – K3N

+0

Ho provato il tuo violino e ha funzionato. Terrific! Ulteriori domande: 1) Ho cambiato l'URL in "http: // www.cyut.edu.tw/~ yltang/example.pdf'' e non renderà. Il file è effettivamente lì comunque. Ti dispiace provarlo per capire perché il tuo file ha funzionato, ma non il mio? 2) Come posso visualizzare il file una pagina dopo l'altra in verticale? 3) La soluzione funziona solo su Firefox, giusto? –

0

È possibile passare il numero di pagina alle promesse, ottenere che i dati pagina di tela e rendere nel giusto ordine su tela

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

Esempio completo

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






    }); 





} 

Problemi correlati