itext: как настроить извлечение текста?

Я использую iText 5.5.8 для Java. Следуя стандартным процедурам извлечения текста, т.е.

PdfTextExtractor.getTextFromPage(reader, pageNumber)

Я был удивлен, обнаружив несколько ошибок в выводе, в частности, все буквы d вышли как o s.

Так как же на самом деле работает извлечение текста в iText? Это какой-то OCR?

Я заглянул под капот, пытаясь понять, как TextExtractionStrategy работает, но я не мог понять многое. SimpleTextExtractionStrategy например, кажется, просто определить наличие линий и пробелов, в то время как это TextRenderInfo который предоставляет текст, вызывая некоторый метод декодирования на GraphicsState "s font поле, и это так далеко, как я мог бы пойти без большой мигрени.

Так кто мой мужчина? Какой класс я должен переопределить или какой параметр я должен настроить, чтобы иметь возможность сказать iText "эй, вы все неправильно читаете!"

редактировать:

Образец PDF можно найти по адресу http://www.fpozzi.com/stampastopper/download/ Имя файла - 0116_LR.pdf. Извините, не могу поделиться прямой ссылкой. Это базовый код для извлечения текста

import java.io.File;
import java.io.IOException;

import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.parser.PdfTextExtractor;

public class Import
{

    public static void importFromPdf(final File pdfFile) throws IOException
    {
        PdfReader reader = new PdfReader(pdfFile.getAbsolutePath());

        try
        {

            for (int i = 1; i <= reader.getNumberOfPages(); i++)
            {
                System.out.println(PdfTextExtractor.getTextFromPage(reader, i));
                System.out.println("----------------------------------");
            }

        }
        catch (IOException e)
        {
            throw e;
        }
        finally
        {
            reader.close();
        }
    }

    public static void main(String[] args)
    {
        try
        {
            importFromPdf(new File("0116_LR.pdf"));
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }
}

редактировать после ответов @blagae и @mkl

Прежде чем начать играть с iText, я попытался извлечь текст из Apache PDFBox (проект, похожий на iText, который я только что обдумал), но у него есть та же проблема.

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

import java.io.File;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.itextpdf.text.io.RandomAccessSourceFactory;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.RandomAccessFileOrArray;
import com.itextpdf.text.pdf.parser.ContentByteUtils;
import com.itextpdf.text.pdf.parser.PdfTextExtractor;

public class Import
{

    private final static Pattern actualWordPattern = Pattern.compile("\\((.*?)\\)");

    public static void importFromPdf(final File pdfFile) throws IOException
    {
        PdfReader reader = new PdfReader(pdfFile.getAbsolutePath());

        Matcher matcher;
        String line, extractedText;
        boolean anyMatchFound;
        try
        {
            for (int i = 1; i <= 16; i++)
            {
                byte[] contentBytes = ContentByteUtils.getContentBytesForPage(reader, i);
                RandomAccessFileOrArray raf = new RandomAccessFileOrArray(new RandomAccessSourceFactory().createSource(contentBytes));
                while ((line = raf.readLine()) != null && !line.equals("BT"));

                extractedText = "";
                while ((line = raf.readLine()) != null && !line.equals("ET"))
                {
                    anyMatchFound = false;
                    matcher = actualWordPattern.matcher(line);
                    while (matcher.find())
                    {
                        anyMatchFound = true;
                        extractedText += matcher.group(1);
                    }
                    if (anyMatchFound)
                        extractedText += "\n";
                }
                System.out.println(extractedText);
                System.out.println("+++++++++++++++++++++++++++");
                String properlyExtractedText = PdfTextExtractor.getTextFromPage(reader, i);
                System.out.println(properlyExtractedText);
                System.out.println("---------------------------");
            }
        }
        catch (IOException e)
        {
            throw e;
        }
        finally
        {
            reader.close();
        }
    }

    public static void main(String[] args)
    {
        try
        {
            importFromPdf(new File("0116_LR.pdf"));
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }
}

Похоже, по крайней мере, в моем случае, что символы правильные. Однако порядок слов или даже букв грязный, на самом деле очень грязный, поэтому такой подход также непригоден.

Что действительно удивляет меня, так это то, что все методы, которые я до сих пор пытался извлечь из PDF-файлов, включая копирование / вставку из Adobe Reader, что-то напортачили.

Я пришел к выводу, что наиболее надежный способ получить достойное извлечение текста также может быть самым неожиданным: хороший распознавание текста. Сейчас я пытаюсь: 1) преобразовать pdf в изображение (PDFBox отлично справляется с этим - даже не пытайтесь попробовать pdf-renderer) 2) OCR этого изображения я опубликую свои результаты через несколько дней.

2 ответа

Решение

Ваш входной документ был создан странным (но "легальным") способом. В ресурсах есть сопоставление Юникода, которое отображает произвольные символы в точки Юникода. В частности, символ номер 0x64, d в ASCII отображается на глиф с точкой Unicode 0x6f (UTF-8), которая oв этом шрифте. Это не проблема как таковая - любой просмотрщик PDF может справиться с этим - но это странно, потому что все другие используемые глифы не являются "перекрестными". например, символ 0x63 отображается в точку Unicode 0x63 (что c), так далее.

Неправильная запись Unicode

Теперь по той причине, что Acrobat правильно извлекает текст (кроме пробела), а остальные ошибаются. Для этого нам нужно углубиться в синтаксис PDF:

[p, -17.9, e, -15.1, l, 1.4, l, 8.4, i, -20,  m, 5.8, i, 14, st, -17.5, e, 31.2, ,, -20.1,  a] TJ
<</ActualText <fffffffeffffffff00640064> >> BDC
5.102 0 Td
[d, -14.2, d] TJ
EMC

Это говорит PDF Viewer для печати p-e-l-l-i- -m-i-st-e- -a в первой строке кода и d-d после этого на четвертой строчке. Тем не мение, d карты для o, что, по-видимому, является проблемой только для извлечения текста. Acrobat правильно делает извлечение текста, потому что есть маркер содержимого /ActualText это говорит о том, что все, что мы пишем между маркерами BDC и EMC, должно быть проанализировано как dd (0x64,0x64).

Итак, чтобы ответить на ваш вопрос: iText делает это на том же уровне, что и многие уважаемые зрители, которые все игнорируют /ActualText маркер. За исключением Acrobat, который уважает его и отменяет отображение ToUnicode.

И чтобы действительно ответить на ваш вопрос: iText в настоящее время изучает анализ /ActualText маркер, но, вероятно, пройдет некоторое время, прежде чем он попадет в официальный релиз.

Это, вероятно, связано с тем, как PDF с OCR в первую очередь, а не с тем, как iTextSharp анализирует содержимое PDF. Попробуйте скопировать / вставить текст из PDF-файла в Блокнот и посмотреть, происходит ли преобразование "ds -> os". Если это так, вам придется сделать следующее при разборе текста из этого конкретного PDF:

  1. Определите все вхождения строки "os".
  2. Решите, является ли слово, из которого состоит данный экземпляр "os", допустимым английским / немецким / испанским / словом.
  3. Если это верное слово, ничего не делай.
  4. Если это НЕ допустимое слово, выполните обратное преобразование "os -> ds" и снова проверьте словарь на выбранном вами языке.
Другие вопросы по тегам