如何使用pdf.js从pdf中正确提取文本

时间:2016-11-16 15:29:59

标签: javascript pdf callback es6-promise pdf.js

我是ES6和Promise的新手。我正在尝试使用pdf.js将pdf文件的所有页面中的文本提取到字符串数组中。提取完成后,我想以某种方式解析数组。说pdf文件(通过typedarray正确传递)有4个页面,我的代码是:

let str = [];
PDFJS.getDocument(typedarray).then(function(pdf) {
  for(let i = 1; i <= pdf.numPages; i++) {
    pdf.getPage(i).then(function(page) {
      page.getTextContent().then(function(textContent) {
        for(let j = 0; j < textContent.items.length; j++) {
          str.push(textContent.items[j].str);
        }
        parse(str);
      });
    });
  }
});

它设法工作,但是,当然,问题是我的parse函数被称为4次。我只想在完成所有4页提取后才调用parse。欢迎任何建议。请点亮我。

6 个答案:

答案 0 :(得分:7)

https://stackoverflow.com/a/40494019/1765767类似 - 使用Promise.all收集页面承诺,不要忘记将其链接起来:

&#13;
&#13;
function gettext(pdfUrl){
var pdf = PDFJS.getDocument(pdfUrl);
return pdf.then(function(pdf) { // get all pages text
     var maxPages = pdf.pdfInfo.numPages;
     var countPromises = []; // collecting all page promises
     for (var j = 1; j <= maxPages; j++) {
        var page = pdf.getPage(j);

        var txt = "";
        countPromises.push(page.then(function(page) { // add page promise
            var textContent = page.getTextContent();
            return textContent.then(function(text){ // return content promise
                return text.items.map(function (s) { return s.str; }).join(''); // value page text 

            });
        }));
     }
     // Wait for all pages and join text
     return Promise.all(countPromises).then(function (texts) {
       
       return texts.join('');
     });
});
}
// waiting on gettext to finish completion, or error
gettext("https://cdn.mozilla.net/pdfjs/tracemonkey.pdf").then(function (text) {
  alert('parse ' + text);
}, function (reason) {
  console.error(reason);
});
&#13;
<script src="https://npmcdn.com/pdfjs-dist/build/pdf.js"></script>
&#13;
&#13;
&#13;

答案 1 :(得分:2)

这是一个较短(不一定更好)的版本:

async function getPdfText(data) {
    let doc = await pdfjsLib.getDocument({data}).promise;
    let pageTexts = Array.from({length: doc.numPages}, async (v,i) => {
        return (await (await doc.getPage(i+1)).getTextContent()).items.map(token => token.str).join('');
    });
    return (await Promise.all(pageTexts)).join('');
}

在这里,data是一个字符串或缓冲区(或者您可以将其更改为采用url等)。

答案 2 :(得分:1)

@ async5更加干净的版本,并根据"pdfjs-dist": "^2.0.943"的最新版本进行了更新


import PDFJS from "pdfjs-dist";

PDFJS.disableTextLayer = true;
PDFJS.disableWorker = true;

const getPageText = async (pdf: Pdf, pageNo: number) => {
  const page = await pdf.getPage(pageNo);
  const tokenizedText = await page.getTextContent();
  const pageText = tokenizedText.items.map(token => token.str).join("");
  return pageText;
};

export const getPDFText = async (source: PDFSource): Promise<string> => {
  const pdf: Pdf = await PDFJS.getDocument(source).promise;
  const maxPages = pdf.numPages;
  const pageTextPromises = [];
  for (let pageNo = 1; pageNo <= maxPages; pageNo += 1) {
    pageTextPromises.push(getPageText(pdf, pageNo));
  }
  const pageTexts = await Promise.all(pageTextPromises);
  return pageTexts.join(" ");
};



这是我在有人需要时使用的相应打字稿声明文件。

declare module "pdfjs-dist";

type TokenText = {
  str: string;
};
type PageText = {
  items: TokenText[];
};
type PdfPage = {
  getTextContent: () => Promise<PageText>;
};
type Pdf = {
  numPages: number;
  getPage: (pageNo: number) => Promise<PdfPage>;
};

type PDFSource = Buffer | string;

答案 3 :(得分:1)

这是另一个基于其他答案的带有 awaitPromise.all 的 Typescript 版本:

import { getDocument } from "pdfjs-dist";
import {
  DocumentInitParameters,
  PDFDataRangeTransport,
  TypedArray,
} from "pdfjs-dist/types/display/api";

export const getPdfText = async (
  src: string | TypedArray | DocumentInitParameters | PDFDataRangeTransport
): Promise<string> => {
  const pdf = await getDocument(src).promise;

  const pageList = await Promise.all(Array.from({ length: pdf.numPages }, (_, i) => pdf.getPage(i + 1)));

  const textList = await Promise.all(pageList.map((p) => p.getTextContent()));

  return textList
    .map(({ items }) => items.map(({ str }) => str).join(""))
    .join("");
};

答案 4 :(得分:0)

如果您使用PDFViewer组件,这是我的解决方案,它不涉及任何承诺或异步:

function getDocumentText(viewer) {
    let text = '';
    for (let i = 0; i < viewer.pagesCount; i++) {
        const { textContentItemsStr } = viewer.getPageView(i).textLayer;
        for (let item of textContentItemsStr)
            text += item;
    }
    return text;
}

答案 5 :(得分:0)

我也不知道该怎么做,但多亏了 async5 我做到了。我复制了他的代码并将其更新为 pdf.js 的新版本。 我做了最少的更正,并且还冒昧地不将所有页面分组为一个字符串。此外,我使用了一个正则表达式,它删除了 PDF 最终创建的许多空白区域(它不能解决所有情况,但解决了绝大多数情况)。 我这样做的方式应该是大多数人会感到舒适的工作方式,但是,您可以随意删除正则表达式或进行任何其他更改。

// pdf-to-text.js v1, require pdf.js ( https://mozilla.github.io/pdf.js/getting_started/#download )
// load pdf.js and pdf.worker.js
function pdfToText(url, separator = ' ') {
    let pdf = pdfjsLib.getDocument(url);
    return pdf.promise.then(function(pdf) { // get all pages text
        let maxPages = pdf._pdfInfo.numPages;
        let countPromises = []; // collecting all page promises
        for (let i = 1; i <= maxPages; i++) {
            let page = pdf.getPage(i);
            countPromises.push(page.then(function(page) { // add page promise
                let textContent = page.getTextContent();
                return textContent.then(function(text) { // return content promise
                    return text.items.map(function(obj) {
                        return obj.str;
                    }).join(separator); // value page text
                });
            }));
        };
        // wait for all pages and join text
        return Promise.all(countPromises).then(function(texts) {
            for(let i = 0; i < texts.length; i++){
                texts[i] = texts[i].replace(/\s+/g, ' ').trim();
            };
            return texts;
        });
    });
};

// example of use:
// waiting on pdfToText to finish completion, or error
pdfToText('files/pdf-name.pdf').then(function(pdfTexts) {
    console.log(pdfTexts);
    // RESULT: ['TEXT-OF-PAGE-1', 'TEXT-OF-PAGE-2', ...]
}, function(reason) {
    console.error(reason);
});