繁体   English   中英

如何从扫描的PDF中提取表格数据?

[英]How to extract table data from scanned PDF?

我创建了一个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结构,就像我一直在处理其他无需OCR处理并能获得出色结果的PDF一样:

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结构,因为它没有该字体的字形查找表。 我尝试搜索如何更改输出字体,但是没有运气。 我得到的最接近的是:

https://unix.stackexchange.com/questions/306051/tesseract-is-it-possible-to-change-font-output-in-ocred-pdf/353191#353191

但是我不知道要进行此工作需要进行哪些更改。 这应该由Tesseract作为可配置参数提供。

用PDF编写器创建的PDF文本提取已经不是一件容易的事。 增加了需要理解的复杂性,以表格形式进行了布局,这又增加了另一层复杂性。 必须对OCR扫描的图像进行转换以将它们转换为PDF中的不可见文本,这又增加了另一层复杂性。

OCR软件可能存在一个精度问题,即与图像叠加时字符在图像数据中的位置有关,它将文本放置在PDF页面上的位置。 这将导致使文本看起来不对劲。 在这种情况下,这可能是软件的一个缺点,或者仅仅是您需要调整一些可调整的参数来微调OCR结果。

一个好的测试可能是使用一种商业产品,例如Adobe Acrobat,对特定的仅包含图像的PDF执行OCR,然后尝试查看它们是否确实如您期望的那样定位,或者遇到类似问题。

就您的确切例外而言,我很幸运能在FontVert java库中将此查找到这里 (不确定您是否直接使用该库),这似乎是一个本地提供的产品。

也许您可以向该公司/个人查询这是否仅仅是其软件中的设计限制(我想是因为我不清楚在这种情况下为什么需要转换字体格式)。

阅读复选框是在OCR支持之外,而进入OMR支持。 这将为您今天的工作增加另一层复杂性。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM