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

时间:2018-09-04 13:20:59

标签: apache pdf ocr pdfbox pdf.js

系统中的某些PDF文档是通过扫描包含OCR文本的方式创建的。但是,OCR没有正确执行(将西里尔字母和拉丁字符混合在一起),尽管该文档看起来像可搜索的,但该信息完全不正确且无法使用。

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

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

为此,我使用了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随附的示例类 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

1 个答案:

答案 0 :(得分:2)

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

createTokensWithoutText中的错误

虽然显示运算符 Tj ' TJ 的文本实际上只有一个参数有三个:

  

a w a c 字符串    –   移至下一行并显示文本字符串,使用 a w 作为单词间距,并以 a c 作为字符间距(在文本状态下设置相应的参数)。 a w a c 应该是以无标度的文本空间单位表示的数字。

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

如果流中有一个操作,则createTokensWithoutText仅删除字符串参数和运算符,而保留数字参数a w 和a c 。这反过来会导致newTokens中以下指令的参数集无效。

如何扫描示例PDF

此处的OCR软件并没有简单地在图像字形的前面或后面添加不可见字符以提供文本提取功能(这是一种非常常见的方法)。相反,它实际上是从图像中的字形创建临时字体,从图像中删除字形,然后将其明显地画在图像前面。

因此,其余图像仅包含一些软件没有与任何字形关联的污垢。

临时字体包含如下所示的字形:

screenshot

如您所见,字体甚至包含相同识别字母的多个字形,例如分别为9、13和15中的“ H”。

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

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

这当然使文本提取变得毫无意义。此外,一些查看者使用某种标准字体而不是即席字体的字形来显示分配的拉丁字符,特别是在标记文本时,这样显示的文本也没有意义。


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