web-dev-qa-db-fra.com

extraire le texte d'un pdf en Javascript

Je me demande s'il est possible d'obtenir le texte à l'intérieur d'un fichier PDF en utilisant uniquement Javascript? Si oui, quelqu'un peut-il me montrer comment?

Je sais qu'il existe des bibliothèques Java côté serveur, c #, etc., mais je préférerais ne pas utiliser de serveur. Merci

37
nacho4d

C'est une question ancienne, mais parce que pdf.js s'est développé au fil des ans, je voudrais donner une nouvelle réponse. Autrement dit, cela peut être fait localement sans impliquer aucun serveur ou service externe. Le nouveau pdf.js a une fonction: page.getTextContent (). Vous pouvez obtenir le contenu textuel de cela. Je l'ai fait avec succès avec le code suivant.

  1. Ce que vous obtenez à chaque étape est une promesse. Vous devez coder de cette façon: .then( function(){...}) pour passer à l'étape suivante.

    1) PDFJS.getDocument( data ).then( function(pdf) {

    2) pdf.getPage(i).then( function(page){

    3) page.getTextContent().then( function(textContent){

  2. Ce que vous obtenez finalement est un tableau de chaînes textContent.bidiTexts[]. Vous les concaténez pour obtenir le texte d'une page. Les coordonnées des blocs de texte sont utilisées pour juger si la nouvelle ligne ou l'espace doit être inséré. (Ce n'est peut-être pas totalement robuste, mais d'après mon test, cela semble correct.)

  3. Le paramètre d'entrée data doit être une URL ou des données de type ArrayBuffer. J'ai utilisé la fonction ReadAsArrayBuffer (fichier) dans l'API FileReader pour obtenir les données.

J'espère que cela t'aides.

Remarque: Selon un autre utilisateur, la bibliothèque a été mise à jour et a provoqué la rupture du code. Selon le commentaire de async5 ci-dessous, vous devez remplacer textContent.bidiTexts avec textContent.items.

    function Pdf2TextClass(){
     var self = this;
     this.complete = 0;

    /**
     *
     * @param data ArrayBuffer of the pdf file content
     * @param callbackPageDone To inform the progress each time
     *        when a page is finished. The callback function's input parameters are:
     *        1) number of pages done;
     *        2) total number of pages in file.
     * @param callbackAllDone The input parameter of callback function is 
     *        the result of extracted text from pdf file.
     *
     */
     this.pdfToText = function(data, callbackPageDone, callbackAllDone){
     console.assert( data  instanceof ArrayBuffer  || typeof data == 'string' );
     PDFJS.getDocument( data ).then( function(pdf) {
     var div = document.getElementById('viewer');

     var total = pdf.numPages;
     callbackPageDone( 0, total );        
     var layers = {};        
     for (i = 1; i <= total; i++){
        pdf.getPage(i).then( function(page){
        var n = page.pageNumber;
        page.getTextContent().then( function(textContent){
          if( null != textContent.bidiTexts ){
            var page_text = "";
            var last_block = null;
            for( var k = 0; k < textContent.bidiTexts.length; k++ ){
                var block = textContent.bidiTexts[k];
                if( last_block != null && last_block.str[last_block.str.length-1] != ' '){
                    if( block.x < last_block.x )
                        page_text += "\r\n"; 
                    else if ( last_block.y != block.y && ( last_block.str.match(/^(\s?[a-zA-Z])$|^(.+\s[a-zA-Z])$/) == null ))
                        page_text += ' ';
                }
                page_text += block.str;
                last_block = block;
            }

            textContent != null && console.log("page " + n + " finished."); //" content: \n" + page_text);
            layers[n] =  page_text + "\n\n";
          }
          ++ self.complete;
          callbackPageDone( self.complete, total );
          if (self.complete == total){
            window.setTimeout(function(){
              var full_text = "";
              var num_pages = Object.keys(layers).length;
              for( var j = 1; j <= num_pages; j++)
                  full_text += layers[j] ;
              callbackAllDone(full_text);
            }, 1000);              
          }
        }); // end  of page.getTextContent().then
      }); // end of page.then
    } // of for
  });
 }; // end of pdfToText()
}; // end of class
61
gm2008

Je n'ai pas pu faire fonctionner l'exemple de gm2008 (la structure de données interne sur pdf.js a apparemment changé), j'ai donc écrit ma propre solution entièrement basée sur des promesses qui n'utilise aucun élément DOM, sélecteur de requêtes ou canevas, en utilisant le pdf mis à jour .js de l'exemple à mozilla

Il mange un chemin de fichier pour le téléchargement depuis que je l'utilise avec node-webkit. Vous devez vous assurer que les cmaps sont téléchargés et pointés quelque part et que vous avez besoin de pdf.js et pdf.worker.js pour que cela fonctionne.

    /**
     * Extract text from PDFs with PDF.js
     * Uses the demo pdf.js from https://mozilla.github.io/pdf.js/getting_started/
     */
    this.pdfToText = function(data) {

        PDFJS.workerSrc = 'js/vendor/pdf.worker.js';
        PDFJS.cMapUrl = 'js/vendor/pdfjs/cmaps/';
        PDFJS.cMapPacked = true;

        return PDFJS.getDocument(data).then(function(pdf) {
            var pages = [];
            for (var i = 0; i < pdf.numPages; i++) {
                pages.Push(i);
            }
            return Promise.all(pages.map(function(pageNumber) {
                return pdf.getPage(pageNumber + 1).then(function(page) {
                    return page.getTextContent().then(function(textContent) {
                        return textContent.items.map(function(item) {
                            return item.str;
                        }).join(' ');
                    });
                });
            })).then(function(pages) {
                return pages.join("\r\n");
            });
        });
    }

usage:

 self.pdfToText(files[0].path).then(function(result) {
      console.log("PDF done!", result);
 })
10
SchizoDuckie

Voici du code JavaScript qui fait ce que vous voulez en utilisant Pdf.js de http://hublog.hubmed.org/archives/001948.html :

var input = document.getElementById("input");  
var processor = document.getElementById("processor");  
var output = document.getElementById("output");  

// listen for messages from the processor  
window.addEventListener("message", function(event){  
  if (event.source != processor.contentWindow) return;  

  switch (event.data){  
    // "ready" = the processor is ready, so fetch the PDF file  
    case "ready":  
      var xhr = new XMLHttpRequest;  
      xhr.open('GET', input.getAttribute("src"), true);  
      xhr.responseType = "arraybuffer";  
      xhr.onload = function(event) {  
        processor.contentWindow.postMessage(this.response, "*");  
      };  
      xhr.send();  
    break;  

    // anything else = the processor has returned the text of the PDF  
    default:  
      output.textContent = event.data.replace(/\s+/g, " ");  
    break;  
  }  
}, true);

... et voici un exemple:

http://git.macropus.org/2011/11/pdftotext/example/

7
rphv

Pour toutes les personnes qui souhaitent réellement l'utiliser sur un serveur de noeud:

/**
 * Created by velten on 25.04.16.
 */
"use strict";
let pdfUrl = "http://example.com/example.pdf";
let request = require('request');
var pdfParser = require('pdf2json');

let pdfPipe = request({url: pdfUrl, encoding:null}).pipe(pdfParser);

pdfPipe.on("pdfParser_dataError", err => console.error(err) );
pdfPipe.on("pdfParser_dataReady", pdf => {
    //optionally:
    //let pdf = pdfParser.getMergedTextBlocksIfNeeded();

    let count1 = 0;
    //get text on a particular page
    for (let page of pdf.formImage.Pages) {
        count1 += page.Texts.length;
    }

    console.log(count1);
    pdfParser.destroy();
});
0
velop

Remarque: ce code suppose que vous utilisez nodejs. Cela signifie que vous analysez un fichier local au lieu d'un à partir d'une page Web, car la question d'origine ne pose pas explicitement la question de l'analyse des fichiers PDF sur une page Web.

La réponse de @ gm2008 était un excellent point de départ (veuillez la lire et ses commentaires pour plus d'informations), mais avait besoin de quelques mises à jour (19/08) et avait du code inutilisé. J'aime aussi les exemples plus complets. Il y a plus de refactoring et de peaufinage qui pourraient être faits (par exemple avec await), mais pour l'instant c'est aussi proche de cette réponse originale que cela pourrait l'être.

Comme précédemment, cela utilise la bibliothèque PDFjs de Mozilla. Le package npmjs est à https://www.npmjs.com/package/pdfjs-dist .

D'après mon expérience, cela ne fait pas bien de trouver où mettre des espaces, mais c'est un problème pour une autre fois.

[Modifier: je crois que la mise à jour de l'utilisation de .transform a restauré l'espace blanc tel qu'il se comportait à l'origine.]

// This file is called myPDFfileToText.js and is in the root folder
let PDFJS = require('pdfjs-dist');

let pathToPDF = 'path/to/myPDFfileToText.pdf';

let toText = Pdf2TextObj();
let onPageDone = function() {}; // don't want to do anything between pages
let onFinish = function(fullText) { console.log(fullText) };
toText.pdfToText(pathToPDF, onPageDone, onFinish);

function Pdf2TextObj() {
    let self = this;
    this.complete = 0;

    /**
     *
     * @param path Path to the pdf file.
     * @param callbackPageDone To inform the progress each time
     *        when a page is finished. The callback function's input parameters are:
     *        1) number of pages done.
     *        2) total number of pages in file.
     *        3) the `page` object itself or null.
     * @param callbackAllDone Called after all text has been collected. Input parameters:
     *        1) full text of parsed pdf.
     *
     */
    this.pdfToText = function(path, callbackPageDone, callbackAllDone) {
        // console.assert(typeof path == 'string');
        PDFJS.getDocument(path).promise.then(function(pdf) {

            let total = pdf.numPages;
            callbackPageDone(0, total, null);

            let pages = {};
            // For some (pdf?) reason these don't all come in consecutive
            // order. That's why they're stored as an object and then
            // processed one final time at the end.
            for (let pagei = 1; pagei <= total; pagei++) {
                pdf.getPage(pagei).then(function(page) {
                    let pageNumber = page.pageNumber;
                    page.getTextContent().then(function(textContent) {
                        if (null != textContent.items) {
                            let page_text = "";
                            let last_item = null;
                            for (let itemsi = 0; itemsi < textContent.items.length; itemsi++) {
                                let item = textContent.items[itemsi];
                                // I think to add whitespace properly would be more complex and
                                // would require two loops.
                                if (last_item != null && last_item.str[last_item.str.length - 1] != ' ') {
                                    let itemX = item.transform[5]
                                    let lastItemX = last_item.transform[5]
                                    let itemY = item.transform[4]
                                    let lastItemY = last_item.transform[4]
                                    if (itemX < lastItemX)
                                        page_text += "\r\n";
                                    else if (itemY != lastItemY && (last_item.str.match(/^(\s?[a-zA-Z])$|^(.+\s[a-zA-Z])$/) == null))
                                        page_text += ' ';
                                } // ends if may need to add whitespace

                                page_text += item.str;
                                last_item = item;
                            } // ends for every item of text

                            textContent != null && console.log("page " + pageNumber + " finished.") // " content: \n" + page_text);
                            pages[pageNumber] = page_text + "\n\n";
                        } // ends if has items

                        ++self.complete;

                        callbackPageDone(self.complete, total, page);


                        // If all done, put pages in order and combine all
                        // text, then pass that to the callback
                        if (self.complete == total) {
                            // Using `setTimeout()` isn't a stable way of making sure 
                            // the process has finished. Watch out for missed pages.
                            // A future version might do this with promises.
                            setTimeout(function() {
                                let full_text = "";
                                let num_pages = Object.keys(pages).length;
                                for (let pageNum = 1; pageNum <= num_pages; pageNum++)
                                    full_text += pages[pageNum];
                                callbackAllDone(full_text);
                            }, 1000);
                        }
                    }); // ends page.getTextContent().then
                }); // ends page.then
            } // ends for every page
        });
    }; // Ends pdfToText()

    return self;
}; // Ends object factory

Exécutez dans le terminal:

node myPDFfileToText.js

0
knod