使用 Apache PDFBox 从 PDF 文档中删除 OCR 文本

Remove OCR text from PDF document using Apache PDFBox

系统中部分PDF文件是通过扫描创建的,包含OCR文本。但是,OCR 执行不正确(混淆了西里尔字符和拉丁字符),尽管文档看起来可以搜索,但该信息完全不正确且无法使用。

在 Adob​​e Acrobat Reader DC(或 Google Chrome)中查看 PDF 文档时显示正确,但在使用 PDF.js 的网页上呈现文档,OCR 文本显示在前面,而不是原始文本的扫描图形显示。

想法是通过从 PDF 文档中删除 OCR 文本,同时保留原始文本的扫描图形表示来 "repair" 这些文档。

为此,我使用 Apache PDFBox 2.0.11 检查 PDF 文档的内容。以下代码片段打印出 PDF 文档中包含的全部文本,在本例中整个文本与 OCR 文本完全相同:

PDDocument document = PDDocument.load(new File("D:/input.pdf"));
PDFTextStripper stripper = new PDFTextStripper();
stripper.setStartPage(1);
stripper.setEndPage(document.getNumberOfPages());
String sText = stripper.getText(document);
System.out.println(sText);
document.close();

然后我使用了PDFBox提供的示例class RemoveAllText,希望从PDF文档中删除OCR文本。不幸的是,它不仅删除了 OCR 文本,还删除了原始扫描文本的图形显示。检查PDF文档中的文本元素并删除它们的方法如下所示:

private static List<Object> createTokensWithoutText(PDContentStream contentStream) throws IOException
{
    PDFStreamParser parser = new PDFStreamParser(contentStream);
    Object token = parser.parseNextToken();
    List<Object> newTokens = new ArrayList<Object>();
    while (token != null)
    {
        if (token instanceof Operator)
        {
            Operator op = (Operator) token;
            if ("TJ".equals(op.getName()) || "Tj".equals(op.getName()) ||
                "'".equals(op.getName()) || "\"".equals(op.getName()))
            {
                // remove the one argument to this operator
                newTokens.remove(newTokens.size() - 1);

                token = parser.parseNextToken();
                continue;
            }
        }
        newTokens.add(token);
        token = parser.parseNextToken();
    }
    return newTokens;
}

我认为应该以某种方式更改此方法(仅删除文本而不是删除其图形表示),但我不知道该怎么做。

这里是an example of PDF document before RemoveAllText, 这是 an example of PDF document after RemoveAllText.

您从 PDFBox 示例中复制的 createTokensWithoutText 代码确实存在错误。但是该示例从扫描的 PDF 中删除所有文本的原因是扫描仪已经从图像中删除了字母,为它们创建了临时字体,并使用这些字体将它们再次绘制为文本,因此该示例只是做了它的工作是为了做。

错误 createTokensWithoutText

虽然显示运算符 Tj'TJ 的文本确实只有一个单个参数," 有三个:

aw ac string " – Move to the next line and show a text string, using aw as the word spacing and ac as the character spacing (setting the corresponding parameters in the text state). aw and ac shall be numbers expressed in unscaled text space units.

(ISO 32000-1 Table 109 – 文本显示运算符)

如果流中有操作,那么createTokensWithoutText只去掉字符串参数和运算符,留下数字参数aw 和 ac 就位。这反过来导致 newTokens.

中后续指令的参数集无效

如何扫描示例 PDF

这里的OCR软件并不是简单地在图片中的字形前后添加不可见字符来提供文本提取功能(这是一种很常见的做法)。相反,它实际上从图像中的字形创建了特殊字体,从图像中删除了字形,并将它们明显地绘制在图像前面。

因此,剩余的图像只包含软件未与任何字形关联的一些污垢。

临时字体包含这样的字形:

如您所见,这些字体甚至包含同一识别字母的多个字形,例如对于 'H' 这里是 9、13 和 15。

这种方法的优点是可以更轻松地操作 PDF,可以编辑文本块。

不幸的是,对于您的情况,OCR 软件似乎只识别拉丁字符和阿拉伯数字,尤其是它似乎不识别西里尔字符。因此,它将西里尔字形分配给最相似的拉丁字符或阿拉伯数字。

这当然会使文本提取变得毫无意义。此外,一些观众使用一些标准字体而不是来自 ad-hoc 字体的字形来显示指定的拉丁字符,特别是在标记文本时,这样显示的文本也没有意义。


因此,您应该在关闭 OCR 的情况下再次扫描,或者将 PDF 导出为图像并仅从这些图像构建新的 PDF。