Извлечь весь текст со строковыми позициями из PDF

Это может показаться старым вопросом, но я не нашел исчерпывающего ответа, потратив полчаса на поиски по всему SO.

Я использую PDFBox и хочу извлечь весь текст из файла PDF вместе с координатами каждой строки. Я использую их PrintTextLocations пример (http://pdfbox.apache.org/apidocs/org/apache/pdfbox/examples/util/PrintTextLocations.html), но с типом pdf, который я использую (электронные билеты), программа не распознает строки, печатая каждый символ отдельно. Результатом является список строк (каждая из которых представляет объект TextPosition), например:

String[414.93896,637.2442 fs=1.0 xscale=8.0 height=4.94 space=2.2240002 width=4.0] s
String[418.93896,637.2442 fs=1.0 xscale=8.0 height=4.94 space=2.2240002 width=4.447998] a
String[423.38696,637.2442 fs=1.0 xscale=8.0 height=4.94 space=2.2240002 width=1.776001] l
String[425.16296,637.2442 fs=1.0 xscale=8.0 height=4.94 space=2.2240002 width=4.447998] e

Пока я бы хотел, чтобы программа распознавала строку "продажа" как уникальную TextPosition и выдавала мне ее позицию. Я также пробовал играть с методами setSpacingTolerance() и setAverageCharacterTolerance() PDFTextStripper, устанавливая разные значения выше и ниже стандартных значений (которые, к вашему сведению, равны 0,5 и 0,3 соответственно), но результат совсем не изменился. Где я ошибаюсь? Заранее спасибо.


person Andrea Sprega    schedule 02.04.2012    source источник
comment
Ах, радости PDF. В зависимости от того, что его создало, вполне может быть, что «текст» — это просто набор глифов в определенных позициях, поэтому вам придется делать предположения на основе позиций, чтобы выяснить, где находятся слова и пробелы.   -  person Joey    schedule 02.04.2012


Ответы (2)


Как упомянул Джоуи, PDF — это просто набор инструкций, говорящих вам, где должен быть напечатан определенный символ.

Чтобы извлечь слова или строки, вам придется выполнить некоторую сегментацию данных: изучение ограничивающих рамок символов должно позволить вам распознать те, которые находятся на одной строке, а затем какие из них образуют слова.

person Nicolas W.    schedule 04.06.2012
comment
Спасибо за ваш ответ. Вот что я в итоге сделал: создал набор прямоугольников для каждого шаблона PDF и применил его для извлечения частей текста в зависимости от положения. Для этого потребуется много ручной работы, но это кажется единственным надежным подходом. - person Andrea Sprega; 04.06.2012

Вот ваше решение: 1. Чтение файла 2. Извлечение каждой страницы в текст с помощью PDFParserTextStripper 3. Каждая позиция текста будет напечатана символом.

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.List;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.pdfbox.text.TextPosition;
class PDFParserTextStripper extends PDFTextStripper {
    public PDFParserTextStripper(PDDocument pdd) throws IOException {
        super();
        document = pdd;
    }
    public void stripPage(int pageNr) throws IOException {
        this.setStartPage(pageNr + 1);
        this.setEndPage(pageNr + 1);
        Writer dummy = new OutputStreamWriter(new ByteArrayOutputStream());
        writeText(document, dummy); // This call starts the parsing process and calls writeString repeatedly.
    }
    @Override
    protected void writeString(String string, List<TextPosition> textPositions) throws IOException {
        for (TextPosition text : textPositions) {
            System.out.println("String[" + text.getXDirAdj() + "," + text.getYDirAdj() + " fs=" + text.getFontSizeInPt()
                    + " xscale=" + text.getXScale() + " height=" + text.getHeightDir() + " space="
                    + text.getWidthOfSpace() + " width=" + text.getWidthDirAdj() + " ] " + text.getUnicode());
        }
    }
    public static void extractText(InputStream inputStream) {
        PDDocument pdd = null;
        try {
            pdd = PDDocument.load(inputStream);
            PDFParserTextStripper stripper = new PDFParserTextStripper(pdd);
            stripper.setSortByPosition(true);
            for (int i = 0; i < pdd.getNumberOfPages(); i++) {
                stripper.stripPage(i);
            }
        } catch (IOException e) {
            // throw error
        } finally {
            if (pdd != null) {
                try {
                    pdd.close();
                } catch (IOException e) {
                }
            }
        }
    }
    public static void main(String[] args) throws IOException {
        File f = new File("C://PDFLOCATION//target.pdf");
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(f);
            extractText(fis);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fis != null)
                    fis.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
}
person Yaniv Levy    schedule 26.02.2020
comment
Пожалуйста, добавьте объяснение, как вы пришли к этому результату. - person Julian; 26.02.2020
comment
Этот код похож на пример PrintTextLocations. - person Tilman Hausherr; 26.02.2020
comment
Точно такая же концепция. - person Yaniv Levy; 30.04.2020