我正在使用iText 5.5.8 for Java.遵循默认的,简单的文本提取程序,即
PdfTextExtractor.getTextFromPage(reader, pageNumber)
我惊讶地发现,在输出几次失误,特别是所有的字母d的问世为Ø秒.
那么iText中的文本提取真的如何工作呢?是某种OCR吗?
我看了看引擎盖,试图掌握它是如何TextExtractionStrategy
工作的,但我想不出多少.SimpleTextExtractionStrategy
例如,似乎只是确定线条和空间的存在,而这TextRenderInfo
是通过调用一些提供文本解码的方法GraphicsState
的font
领域,这是据我可以去没有得到一个重大的偏头痛.
那么谁是我的男人?其中I类应该重写或参数我应该调整到能够告诉iText的:"嘿,你在阅读完所有d错!"
编辑:
样本PDF可以在http://www.fpozzi.com/stampastopper/download/找到,文件名是0116_LR.pdf对不起,无法共享直接链接.这是文本提取的一些基本代码
import java.io.File; import java.io.IOException; import com.itextpdf.text.pdf.PdfReader; import com.itextpdf.text.pdf.parser.PdfTextExtractor; public class Import { public static void importFromPdf(final File pdfFile) throws IOException { PdfReader reader = new PdfReader(pdfFile.getAbsolutePath()); try { for (int i = 1; i <= reader.getNumberOfPages(); i++) { System.out.println(PdfTextExtractor.getTextFromPage(reader, i)); System.out.println("----------------------------------"); } } catch (IOException e) { throw e; } finally { reader.close(); } } public static void main(String[] args) { try { importFromPdf(new File("0116_LR.pdf")); } catch (IOException e) { e.printStackTrace(); } } }
在@blagae和@mkl回答之后编辑
在开始使用iText之前,我尝试过从Apache PDFBox中提取文本(类似于我刚刚发现的iText的项目),但确实有同样的问题.
了解这些程序如何处理文本超出了我的奉献精神,所以我编写了一个简单的方法来从原始页面内容中提取文本,即BT和ET标记之间的任何内容.
import java.io.File; import java.io.IOException; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.itextpdf.text.io.RandomAccessSourceFactory; import com.itextpdf.text.pdf.PdfReader; import com.itextpdf.text.pdf.RandomAccessFileOrArray; import com.itextpdf.text.pdf.parser.ContentByteUtils; import com.itextpdf.text.pdf.parser.PdfTextExtractor; public class Import { private final static Pattern actualWordPattern = Pattern.compile("\\((.*?)\\)"); public static void importFromPdf(final File pdfFile) throws IOException { PdfReader reader = new PdfReader(pdfFile.getAbsolutePath()); Matcher matcher; String line, extractedText; boolean anyMatchFound; try { for (int i = 1; i <= 16; i++) { byte[] contentBytes = ContentByteUtils.getContentBytesForPage(reader, i); RandomAccessFileOrArray raf = new RandomAccessFileOrArray(new RandomAccessSourceFactory().createSource(contentBytes)); while ((line = raf.readLine()) != null && !line.equals("BT")); extractedText = ""; while ((line = raf.readLine()) != null && !line.equals("ET")) { anyMatchFound = false; matcher = actualWordPattern.matcher(line); while (matcher.find()) { anyMatchFound = true; extractedText += matcher.group(1); } if (anyMatchFound) extractedText += "\n"; } System.out.println(extractedText); System.out.println("+++++++++++++++++++++++++++"); String properlyExtractedText = PdfTextExtractor.getTextFromPage(reader, i); System.out.println(properlyExtractedText); System.out.println("---------------------------"); } } catch (IOException e) { throw e; } finally { reader.close(); } } public static void main(String[] args) { try { importFromPdf(new File("0116_LR.pdf")); } catch (IOException e) { e.printStackTrace(); } } }
至少在我的情况下,似乎字符是正确的.然而,单词甚至字母的顺序实际上是凌乱的,超级凌乱,所以这种方法也无法使用.
令我惊讶的是,到目前为止我尝试过的所有方法都是从PDF中检索文本,包括从Adobe Reader复制/粘贴,搞砸了一些东西.
我得出的结论是,获得一些体面文本提取的最可靠方法也可能是最意想不到的:一些好的OCR.我现在正在尝试:1)将pdf转换为图像(PDFBox很擅长这样做 - 甚至不用去尝试pdf-renderer)2)OCR该图像我将在几天内发布我的结果.
您的输入文档是以奇怪的(但"合法的")方式创建的.资源中有一个Unicode映射,可将任意字形映射到Unicode点.特别是,d
ASCII中的字符编号0x64映射到Unicode点0x6f(UTF-8)的字形,即o
此字体.这本身并不是问题 - 任何PDF查看器都可以处理它 - 但它很奇怪,因为使用的所有其他字形都不是"交叉映射".例如,字符0x63映射到Unicode点0x63(即c
)等.
现在因为Acrobat正确地进行了文本提取(除了空间),其他的都出错了.我们必须深入研究PDF语法:
[p, -17.9, e, -15.1, l, 1.4, l, 8.4, i, -20, m, 5.8, i, 14, st, -17.5, e, 31.2, ,, -20.1, a] TJ < >> BDC 5.102 0 Td [d, -14.2, d] TJ EMC
这告诉PDF查看器p-e-l-l-i- -m-i-st-e- -a
在第一行代码上打印,然后d-d
在第四行打印.但是,d
映射到o
,这显然只是文本提取的问题.Acrobat确实正确地进行了文本提取,因为有一个内容标记/ActualText
表明我们在BDC和EMC标记之间写入的内容必须解析为dd(0x64,0x64).
所以回答你的问题:iText与许多备受尊敬的观众处于同一水平,这些都忽略了/ActualText
标记.除了Acrobat,它尊重它并否决ToUnicode映射.
并且要真正回答你的问题:iText目前正在研究解析/ActualText
标记,但它可能需要一段时间才能进入正式版本.