使用 iText7 8.0.4 从 PDF 文件中提取文本
MemoryStream pdfStream = ...
pdfStream.Position = 0;
var strategy = new LocationTextExtractionStrategy();
var reader = new PdfReader(pdfStream);
using var pdfDocument = new PdfDocument(reader);
for (int i = 1; i <= pdfDocument.GetNumberOfPages(); ++i)
{
var page = pdfDocument.GetPage(i);
var text = PdfTextExtractor.GetTextFromPage(page, strategy);
}
在文件指针 39747 处抛出异常错误:
iText.IO.Exceptions.IOException: Error at file pointer 39747. --->
iText.IO.Exceptions.IOException: '>' not expected.
--- End of inner
exception stack trace --- at
iText.IO.Source.PdfTokenizer.ThrowError(String error, Object[]
messageParams) at iText.IO.Source.PdfTokenizer.NextToken() at
iText.Kernel.Pdf.Canvas.Parser.Util.PdfCanvasParser.NextValidToken()
at iText.Kernel.Pdf.Canvas.Parser.Util.PdfCanvasParser.ReadObject()
at iText.Kernel.Pdf.Canvas.Parser.Util.PdfCanvasParser.Parse(IList`1
ls) at
iText.Kernel.Pdf.Canvas.Parser.PdfCanvasProcessor.ProcessContent(Byte[]
contentBytes, PdfResources resources) at
iText.Kernel.Pdf.Canvas.Parser.PdfCanvasProcessor.ProcessPageContent(PdfPage
page) at
iText.Kernel.Pdf.Canvas.Parser.PdfTextExtractor.GetTextFromPage(PdfPage
page, ITextExtractionStrategy strategy, IDictionary`2
additionalContentOperators) at
iText.Kernel.Pdf.Canvas.Parser.PdfTextExtractor.GetTextFromPage(PdfPage
page, ITextExtractionStrategy strategy)
如何从此 PDF 中提取文本? PDF 已在
https://wetransfer.com/downloads/0a2cb3abb921863f1f7aa109fd58e90e20240522072618/3e3501
此问题是由 PDF 页面内容流中的内嵌图像引起的。它的流数据包含 EI 内联图像结束标记的字节序列,使得 iText 过早地将其视为内联图像的结尾。它尝试将以下流数据解释为内容流指令,但由于观察到的异常而失败。
有两种方法可以解决这个问题,并且都需要一些工作。
请注意,由于这种方法需要更改
private
和/或 sealed
代码,您要么必须修补 iText 源代码并编译您自己的发行版,要么必须将 iText 内容流解析框架的大部分内容复制到您的命名空间中,改进您的副本,并从现在开始使用您的代码进行流解析。
这里主要感兴趣的类是
InlineImageParsingUtils
命名空间中的 iText.Kernel.Pdf.Canvas.Parser.Util
。这里有两个主要的代码位置需要改进:
ParseSamples
方法主要通过查找“EI”后跟空格来识别内嵌图像数据的结尾。但在 2018 年 6 月之前,它曾经寻找被空格包围的“EI”。
您可以考虑回滚该更改并再次需要前导空格。该更改已在提交 0e44a96b2f3b90fb6656310d2c0f5615b05d4391 中引入(这是 iText/Java 提交 e0df12db9cd8869928bbcad7e038f3a5d1aef71c 的自动移植)。
但请考虑一下,此提交的标题是“修复处理内联图像的末尾”。并引用了 DEVSIX-1914 问题,因此 iText 客户很可能遇到了在“EI”标记之前没有空格的 PDF,从而导致了此更改。
对找到的“EI”还有一个额外的测试,调用方法
InlineImageStreamBytesAreComplete
来检查识别的流是否完整,如果此方法返回false
,则ParseSamples
继续搜索正确的“EI” ”.
此方法通过应用所有声明的过滤器(压缩、二进制到文本等)来检查流,并在发生异常时返回
false
。不幸的是,所使用的过滤器实现大多是为了容错而构建的。
您可以考虑通过在此处创建和应用容错性较低的过滤器实现来改进这一点,以增加识别截止流的可能性。 iText 在这里已经使用了更严格的 Flate 过滤器;您可以类似地强制使用替代过滤器实现。此外,您可以分析应用过滤器后检索的数据:根据确切的图像类型(也由过滤器暗示),您可以验证流是否代表完整的图像。
这些改进中的任何一个都会对您的示例 PDF 有所帮助:“EI”iText 查找并假定内嵌图像的末尾前面没有空格,并且容错性较差 ASCII85Decode 和 LZWDecode实现将识别不完整的数据,就像对返回数据的大小进行分析一样。
或者,如果您不想修补或复制 iText 内容流解析代码,您可以切换到两阶段方法:在第一阶段,您可以通过加载所有内容流来准备 PDF 文档,并使用您的内容解析它们。自己的内联图像识别代码,删除您找到的所有内联图像,并将这些操纵的流存储回文档中;在第二阶段,您使用常规的 iText 解析来进行文本提取。