【发布时间】:2018-10-25 12:51:27
【问题描述】:
我创建了一个 Java 项目,它在解析具有特定结构的可搜索 PDF 方面相当成功。其中的表格很复杂,具有合并的行或列,但在每个这样的 PDF 中,表格的结构保持不变,只有里面的文本发生了变化。借助 PDFBox、PDF2Dom 和 Tabula,我能够克服所有这些挑战。
但是,昨天当我收到一组经过扫描的新 PDF 时,问题就出现了。被扫描后,整个内容只是图像,无法搜索。感觉到对 OCR 的需求,我开始研究 Tesseract。但是,我发现仅使用它只会在没有任何上下文的情况下咳出 PDF 的整个文本内容,并且复选框会丢失。因此,我尝试使用 Ghostscript 和 Tesseract 的组合将 PDF 转换为可搜索的 PDF。我使用 Ghostscript 将扫描的 PDF 转换为 jpg 图像,方法如下:
File pdfFile = new File("D://Tess//inputFile.pdf");
List<Image> images = new ArrayList<Image>();
PDFDocument document = new PDFDocument();
document.load(pdfFile);
SimpleRenderer renderer = new SimpleRenderer();
renderer.setResolution(300);
images = renderer.render(document);
for (int i = 0; i < images.size(); i++) {
Image img = images.get(i);
ImageIO.write((RenderedImage) img, "jpg", new File(i + ".jpg"));
}
之后,我使用 Tesseract 将生成的图像转换回 PDF。
Tesseract tessInst = new Tesseract();
tessInst.setDatapath("D://Tess//tessdata");
List<RenderedFormat> list = new ArrayList<RenderedFormat>();
list.add(RenderedFormat.PDF);
for (int i = 0; i < images.size(); i++)
tess.createDocuments(i + ".jpg", "D://Tess//output" + i, list);
PDF 生成得很好,甚至可以搜索,但是当我选择一个单词时,选择突出显示与实际单词有点偏差。此外,无法选择复选框。我尝试使用 PDF2Dom 生成 DOM 结构,就像我一直在使用其他 PDF 一样,这些 PDF 无需 OCR 处理即可搜索并获得了很好的结果:
Document document = parser.createDOM(pdf);
这会引发以下异常:
java.io.IOException: java.io.IOException: Multi byte glyph name not supported.
at org.mabb.fontverter.pdf.PdfFontExtractor.convertType0FontToOpenType(PdfFontExtractor.java:217)
at org.fit.pdfdom.FontTable$Entry.loadType0TtfDescendantFont(FontTable.java:193)
at org.fit.pdfdom.FontTable$Entry.getData(FontTable.java:146)
at org.fit.pdfdom.FontTable$Entry.isEntryValid(FontTable.java:162)
at org.fit.pdfdom.FontTable.addEntry(FontTable.java:49)
at org.fit.pdfdom.PDFBoxTree.processFontResources(PDFBoxTree.java:381)
at org.fit.pdfdom.PDFBoxTree.updateFontTable(PDFBoxTree.java:358)
at org.fit.pdfdom.PDFDomTree.updateFontTable(PDFDomTree.java:544)
at org.fit.pdfdom.PDFBoxTree.processPage(PDFBoxTree.java:204)
at org.apache.pdfbox.text.PDFTextStripper.processPages(PDFTextStripper.java:319)
at org.apache.pdfbox.text.PDFTextStripper.writeText(PDFTextStripper.java:266)
at org.fit.pdfdom.PDFDomTree.createDOM(PDFDomTree.java:218)
at com.pv.pdf.PdfExtractor.extractCheckboxValues(PdfExtractor.java:403)
at com.pv.pdf.PdfExtractor.getMedicalRecordDetails(PdfExtractor.java:372)
at com.pv.servlet.OnServletLogin.doPost(OnServletLogin.java:32)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:707)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74)
at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
at io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:67)
at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
at org.wildfly.extension.undertow.security.SecurityContextAssociationHandler.handleRequest(SecurityContextAssociationHandler.java:78)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:131)
at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)
at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60)
at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77)
at io.undertow.security.handlers.NotificationReceiverHandler.handleRequest(NotificationReceiverHandler.java:50)
at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at org.wildfly.extension.undertow.security.jacc.JACCContextIdHandler.handleRequest(JACCContextIdHandler.java:61)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at org.wildfly.extension.undertow.deployment.GlobalRequestControllerHandler.handleRequest(GlobalRequestControllerHandler.java:68)
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:292)
at io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:81)
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:138)
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:135)
at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
at org.wildfly.extension.undertow.security.SecurityContextThreadSetupAction.lambda$create$0(SecurityContextThreadSetupAction.java:105)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1526)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1526)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1526)
at org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1526)
at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:272)
at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:81)
at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:104)
at io.undertow.server.Connectors.executeRootHandler(Connectors.java:360)
at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:830)
at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1985)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1487)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1378)
at java.lang.Thread.run(Unknown Source)
Caused by: java.io.IOException: Multi byte glyph name not supported.
at org.mabb.fontverter.converter.PsType0ToOpenTypeConverter.convertCmap(PsType0ToOpenTypeConverter.java:89)
at org.mabb.fontverter.converter.PsType0ToOpenTypeConverter.convert(PsType0ToOpenTypeConverter.java:50)
at org.mabb.fontverter.pdf.PdfFontExtractor.convertType0FontToOpenType(PdfFontExtractor.java:215)
... 57 more
我发现了 Ghostscript 中关于字形宽度的问题:
https://github.com/tesseract-ocr/tesseract/issues/712
但是,我不确定它在当前的用例中是否可以帮助我。但它也告诉我选择的文本突出显示是倾斜的,就像我的情况一样。我用的是Ghost4j 1.0.1版,相当于Ghostscript 9.25版,所以这里描述的问题应该已经解决了。
请帮我解决这个问题。提前谢谢你。
编辑
我并没有将错误归咎于 Ghostscript。但是我在搜索的时候发现了一个和我类似的问题,所以我在这里提供了,如果它确实指向了根本问题,那么更多有学问的人会比较容易回答我的问题。
编辑
我认为我的问题可以归结为 Tesseract 正在为输出 PDF 创建“无字形”字体,并且由于它是无字形的,因此无法生成 DOM 结构,因为它没有字形查找表字体。我尝试搜索如何更改输出字体,但没有运气。我得到的最接近的是:
但我不知道需要进行哪些更改才能使其正常工作。这应该由 Tesseract 作为可配置参数提供。
【问题讨论】:
-
我看不出 Ghostscript 在您的问题中的位置,因为您只是使用它来生成 JPEG 图像。因此,它不会影响 Tesseract 生成的 PDF 文件中的突出显示。我真的不认为这应该用 Ghostscritp 标记。
-
这里的问题是我不确定问题的根源是什么!我已经用我使用过的所有组件标记了这个问题,以便人们可以理解它的各个方面。 Ghostscript 在很大程度上是这个过程的一部分。
-
但如果 Ghostscript 只生成图像,那么 - 如果复选框在图像中看起来合理 - OCR 的任何“失败”都必须归结为 Tesseract。
-
那么,你能帮我解决这个 Tesseract 的问题吗?
-
“我认为我的问题可以归结为 Tesseract 正在为输出 PDF 创建“无字形”字体这一事实” - 不要对此解释太多“无字形”术语。字体有字形,它们只是都是空的。异常文本更清楚地表明“不支持多字节字形名称”,即您只是面临 PDF2DOM 的缺点或其依赖项。您可能想要求他们对此实施支持;但请做好准备,并准备一份说明手头问题的示例 PDF。
标签: java pdf ocr tesseract ghostscript