Я создал проект Java, который довольно успешно анализирует PDF-файлы с поиском, имеющие определенную структуру.Таблицы в нем являются сложными, объединяя строки или столбцы, но в каждом таком PDF структура таблиц остается неизменной, изменяется только текст внутри.Мне удалось преодолеть все эти проблемы, вооружившись PDFBox, PDF2Dom и Tabula.
Однако проблема возникла вчера, когда мне предоставили свежий набор PDF-файлов, которые были отсканированы.При сканировании весь контент представлял собой только изображения и не был доступен для поиска.Почувствовав необходимость в оптическом распознавании символов, я начал исследовать Тессеракт.Тем не менее, я обнаружил, что только его использование просто выкашливает весь текст PDF без какого-либо контекста, и флажки будут потеряны.Поэтому я попытался преобразовать PDF-файл в файл с возможностью поиска, используя комбинацию Ghostscript и Tesseract.Я преобразовал отсканированные PDF в изображения jpg с помощью Ghostscript следующим образом:
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"));
}
После этого я преобразовал сгенерированные изображения обратно в PDF с использованием Tesseract.
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-файлы былигенерируется нормально и даже доступен для поиска, но когда я выбираю слово, выделение выделения немного искажается от фактического слова.Кроме того, флажки не могут быть выбраны.Я попытался сгенерировать структуру DOM с использованием PDF2Dom, как я делал это с другими 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 не может быть сгенерирована, так каку него нет таблицы поиска глифов для шрифта.Я пытался найти, как изменить выходной шрифт, но не повезло.Самое близкое, что я получил, было это:
https://unix.stackexchange.com/questions/306051/tesseract-is-it-possible-to-change-font-output-in-ocred-pdf/353191#353191
Но я не знаю, какие изменения потребуются для того, чтобы это работало.Tesseract должен был предоставить это в качестве настраиваемого параметра.