【问题标题】:How to correctly extract text from a pdf using pdf.js如何使用 pdf.js 从 pdf 中正确提取文本
【发布时间】:2017-03-30 21:05:01
【问题描述】:

我是 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。

【问题讨论】:

  • 类似于stackoverflow.com/a/40494019/1765767 -- 使用 Promise.all 收集页面承诺,不要忘记链接 then 的。
  • @async5 它有效!我首先尝试了this,并对其稍作修改,但您提供的答案看起来更正确。请回复它作为答案,以便我可以接受。谢谢!

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


【解决方案1】:

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

function gettext(pdfUrl){
  var pdf = pdfjsLib.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);
});
&lt;script src="https://npmcdn.com/pdfjs-dist/build/pdf.js"&gt;&lt;/script&gt;

【讨论】:

  • ""Uncaught ReferenceError: PDFJS is not defined"
  • 有什么方法可以识别页眉和页脚吗?它们都排在第一位,但它们与文本的其余部分之间没有明确的界限
  • @MartinThoma PDFJS 现在已弃用。请改用pdfjsLib。
【解决方案2】:

更简洁的@async5 版本并根据"pdfjs-dist": "^2.0.943" 的最新版本更新

import PDFJS from "pdfjs-dist";
import PDFJSWorker from "pdfjs-dist/build/pdf.worker.js"; // add this to fit 2.3.0

PDFJS.disableTextLayer = true;
PDFJS.disableWorker = true; // not availaible anymore since 2.3.0 (see imports)

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

/* see example of a PDFSource below */
export const getPDFText = async (source: PDFSource): Promise<string> => {
  Object.assign(window, {pdfjsWorker: PDFJSWorker}); // added to fit 2.3.0
  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;

declare module 'pdfjs-dist/build/pdf.worker.js'; // needed in 2.3.0

如何从带有缓冲区的文件(从节点类型)获取 PDFSource 的示例:

file.arrayBuffer().then((ab: ArrayBuffer) => {
  const pdfSource: PDFSource = Buffer.from(ab);
});

【讨论】:

    【解决方案3】:

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

    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 等)。

    【讨论】:

      【解决方案4】:

      这是另一个基于其他答案的带有 await 和 Promise.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("");
      };
      

      【讨论】:

        【解决方案5】:

        如果您使用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;
        }
        

        【讨论】:

          【解决方案6】:

          我也不知道该怎么做,但是感谢 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);
          });
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2014-05-09
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-12-05
            • 2023-04-02
            相关资源
            最近更新 更多