【问题标题】:itext: how to tweak text extraction?itext:如何调整文本提取?
【发布时间】:2016-01-03 18:09:42
【问题描述】:

我正在为 Java 使用 iText 5.5.8。 遵循默认的、直接的文本提取程序,即

PdfTextExtractor.getTextFromPage(reader, pageNumber)

我惊讶地发现输出中有几个错误,特别是所有字母 ds 都输出为 os。

那么 iText 中的文本提取到底是如何工作的呢?是某种 OCR 吗?

我查看了引擎盖,试图了解TextExtractionStrategy 的工作原理,但我想不通。例如SimpleTextExtractionStrategy 似乎只是确定是否存在行和空格,而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 中检索文本的所有方法,包括从 Adob​​e Reader 复制/粘贴,都搞砸了。

我得出的结论是,获得一些体面的文本提取的最可靠方法也可能是最出乎意料的:一些好的 OCR。 我现在正在尝试: 1) 将 pdf 转换为图像(PDFBox 非常擅长这样做 - 甚至不用费心尝试 pdf-renderer) 2)OCR该图像 我会在几天后发布我的结果。

【问题讨论】:

  • 请分享有问题的 PDF。错误很可能已经在其中,尽管被隐藏了。
  • 当我点击你的 PDF 链接时,我得到一个 403 状态码。
  • 感谢 mkl,添加了 PDF 链接(抱歉,它是意大利语)
  • @brian 对不起,brian,你应该附加文件名 0116_LR.pdf(不希望文件被搜索引擎看到)
  • @HenryChinaski 真正让我吃惊的是,到目前为止,我尝试从 PDF 中检索文本的所有方法,包括从 Adob​​e Reader 复制/粘贴,都搞砸了 - 原因是您的 PDF 故意试图误导文本提取器。因此,遵循最佳做法将导致错误。

标签: itext text-extraction


【解决方案1】:

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

现在是因为 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 获取该映射?
  • 啊,所以这个问题本质上是this one的副本。
  • 您可以访问 ToUnicode 映射,但计算机不可能猜测某个映射“关闭”,因为在很多情况下,所有映射对于文本提取都是重要且必不可少的.最好的办法是考虑编写自己的 iText TextExtractionStrategy,如@mkl 答案中的链接所示
【解决方案2】:

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

  1. 识别所有出现的字符串“os”。
  2. 确定给定“os”实例所包含的单词是否是有效的英语/德语/西班牙语/单词。
  3. 如果它是一个有效的词,什么也不做。
  4. 如果它不是有效的单词,请执行反向“os -> ds”转换,并再次检查您选择的语言的字典。

【讨论】:

  • “尝试将 PDF 中的文本复制/粘贴到记事本中”这样做了,令人惊讶的是,所有字母都是正确的......(抱歉,我必须回到我的真正工作 - 叹气 - 再见几个小时后再来一次!)
  • 当我这样做时,所有字母都不正确 - 有很多 OCR 错误实例。例如,在第 16 页上,您有一行“pelli miste, addolcenti”,它以“pelli miste, aooolcenti”的形式粘贴到记事本中
  • 我在 Sumatra PDF 和 Foxit Reader 上都收到“pelli miste, add olcenti”(来自 Adob​​e Acrobat Pro XI)和“pelli miste, aooolcenti”
  • @blagae 我明白了。看了你的回答,我的回答似乎很错误。你认为我应该删除它吗?还是我应该把它留在这里留给后代?
  • @BrianSnow 我把它留在这里没有问题,因为您的回答对于一般情况同样可能是正确的。将来使用正确搜索词进行谷歌搜索的任何人都可能想知道,不完美的 OCR 工作也是一个非常可能的根本原因。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-01
  • 2014-01-05
  • 1970-01-01
相关资源
最近更新 更多