itext:如何调整文本提取?

4
我正在使用Java版的iText 5.5.8。按照默认的简单文本提取程序,即:
PdfTextExtractor.getTextFromPage(reader, pageNumber)

我很惊讶地发现输出中有几个错误,特别是所有字母d都变成了o
那么iText中的文本提取到底是如何工作的呢?它是一种OCR技术吗?
我探究了一下的工作方式,但我无法弄清楚太多。例如,似乎只确定线条和空格的存在,而TextRenderInfo通过在GraphicsStatefont字段上调用一些decode方法来提供文本,这就是我能做到的最好的了,否则会头痛难忍。
那么我的问题是:哪个类应该被重写或者哪个参数应该被调整,以便告诉iText“嘿,你读错了所有的d!”?
编辑:

样例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中复制/粘贴,都会出现问题。

我得出的结论是,获取一些可靠的文本提取的最可靠方法可能也是最出人意料的:使用优秀的OCR技术。

我现在正在尝试:

1)将PDF转换为图像(PDFBox非常擅长这样做 - 不要浪费时间尝试pdf-renderer)

2)对该图像进行OCR

我将在几天内发布结果。


请分享相关的PDF文件。很可能错误已经存在于其中,只是被隐藏了。 - mkl
当我点击链接到您的PDF时,我收到了403状态码。 - Brian Snow
@brian 抱歉 Brian,您应该附加文件名0116_LR.pdf(不希望文件被搜索引擎看到)。 - Henry Chinaski
@HenryChinaski 真正让我惊讶的是,到目前为止我尝试过的所有从PDF中检索文本的方法,包括从Adobe Reader复制/粘贴,都会出现问题 - 原因在于你的PDF 有意试图误导文本提取器。因此,遵循最佳实践将导致错误。 - mkl
@mkl 我对单词混淆中的故意性还不太信服。我正在处理的pdf文件是由Adobe Indesign生成的(我知道因为它写在pdf文件内部),这意味着它们是由一些平面设计师手动编制的。单词位置中的随机性可能反映了该人员将文本层添加到源中的顺序,奇怪的字符映射可能是某些不可思议的软件决策的结果。文本可访问性是任何一款受到重视的软件都不能故意忽略的问题(例如,面向视觉障碍者的文本转语音)。 - Henry Chinaski
显示剩余2条评论
2个回答

5

您的输入文档是以一种奇怪(但“合法”)的方式创建的。在资源中存在Unicode映射,将任意字形映射到Unicode点。特别地,字符号0x64,即ASCII中的d,被映射到具有Unicode点0x6f(UTF-8)的字形中,即o,在这个字体中。这本身并不是问题——任何PDF查看器都可以处理它——但很奇怪,因为使用的所有其他字形都没有"交叉映射"。例如,字符0x63被映射到Unicode点0x63(即c),等等。

Faulty Unicode entry

现在我们来讲述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查看器在第一行上打印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的内部结构一无所知,我自己永远也想不出来。现在我正在尝试考虑可能的解决方法......不一定是一个干净、强大的解决方案,但我应该假设这个奇怪的映射可能会不同——或者可能根本不存在——在下一个pdf中(我完全不知道这些pdf是如何生成的)。有什么建议吗?例如,是否有一种通过iText获取该映射的方法? - Henry Chinaski
啊,所以这个问题本质上是这个问题的重复。 - mkl
你可以获取到ToUnicode映射,但是对于计算机来说,很难猜测某个映射是否“错误”,因为在许多情况下,所有映射都是非平凡的,并且对于文本提取至关重要。最好的方法是查看如@mkl答案中所示的链接,了解如何编写自己的iText TextExtractionStrategy。 - blagae

0

这可能与PDF最初进行OCR的方式有关,而不是iTextSharp解析PDF内容的方式。尝试将PDF中的文本复制/粘贴到记事本中,看看是否仍会发生“ds -> os”转换。如果是这种情况,当从此特定PDF解析文本时,您需要执行以下操作:

  1. 识别字符串“os”的所有出现。
  2. 决定给定“os”实例所构成的单词是否为有效的英语/德语/西班牙语单词。
  3. 如果它是有效的单词,则不做任何处理。
  4. 如果它不是有效的单词,请执行反向的“os -> ds”转换,并再次检查您选择的语言字典。

试试将PDF中的文本复制/粘贴到记事本中,做了之后,惊喜地发现所有字母都是正确的...(抱歉,我得回去做我的真正工作了,唉,几个小时后再见!) - Henry Chinaski
当我这样做时,所有字母都不正确 - 存在许多OCR错误的实例。例如,在第16页上,您有“pelli miste,addolcenti”的行,它粘贴到记事本中为“pelli miste,aooolcenti”。 - Brian Snow
我在Adobe Acrobat Pro XI中得到了“pelli miste, add olcenti”,在Sumatra PDF和Foxit Reader上也得到了“pelli miste, aooolcenti”。 - blagae
@blagae 我明白了。看完你的回答,我的回答似乎非常错误。你认为我应该删除它吗?还是应该留在这里作为纪念? - Brian Snow
1
@BrianSnow 我觉得把它留在这里没问题,因为你的答案同样适用于一般情况。未来使用正确搜索词进行谷歌搜索的人可能想知道不完美的OCR处理也是一个非常可能的根本原因。 - blagae
谢谢Brian,不幸的是,使用字典查找可能会有问题,因为其中一些单词是品牌名称,很可能无法识别。我将把这条路考虑为最后的选择。 - Henry Chinaski

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接