IText 无法正确读取 PDF 区域

本文关键字:读取 PDF 区域 IText | 更新日期: 2023-09-27 17:57:30

我正在使用LocationTextExtractionStrategy与自定义ITextExtractionStrategy类结合使用来阅读PDF。使用此代码,我可以毫无问题地阅读基于坐标的文档部分。

现在我得到一个看起来像其他PDF的PDF,但如果我尝试阅读它,我会得到这样的文本:

2  D   80 D   8 1 M 13M2 R   V / 8  3B 3 3 710 022/F//0 R8 8        1 0 / 3

这是我正在使用的代码:

private static string ReadFilePart(string fileName,int pageNumber, int fromLeft, int fromBottom, int width, int height)
{
        var rect = new System.util.RectangleJ(fromLeft, fromBottom, width, height);
        var pdfReader = new PdfReader(fileName);
        var filters = new RenderFilter[1];
        filters[0] = new RegionTextRenderFilter(rect);
        var strategy = new FilteredTextRenderListener(new LocationTextExtractionStrategy(), filters);
        var pageText = PdfTextExtractor.GetTextFromPage(pdfReader, pageNumber, new LimitedTextStrategy(strategy));
        pdfReader.Close();
        return pageText;
}
private class LimitedTextStrategy : ITextExtractionStrategy
{
        public readonly ITextExtractionStrategy textextractionstrategy;
        public LimitedTextStrategy(ITextExtractionStrategy strategy)
        {
            textextractionstrategy = strategy;
        }
        public void RenderText(TextRenderInfo renderInfo)
        {
            foreach (TextRenderInfo info in renderInfo.GetCharacterRenderInfos())
            {
                textextractionstrategy.RenderText(info);
            }
        }
        public string GetResultantText()
        {
            return textextractionstrategy.GetResultantText();
        }
        public void BeginTextBlock()
        {
            textextractionstrategy.BeginTextBlock();
        }
        public void EndTextBlock()
        {
            textextractionstrategy.EndTextBlock();
        }
        public void RenderImage(ImageRenderInfo renderInfo)
        {
            textextractionstrategy.RenderImage(renderInfo);
        }
 }

由于敏感数据,我无法共享PDF文件。

更新

如果我用SimpleTextExtractionStrategy更改LocationTextExtractionStrategy,它可以识别没有奇怪字符的整行(PDF 结构?

更新 2

我现在可以共享文件了!有问题的页面是 2° 和 3°

文档文档

测试解决方案以读取文件

更新 3

mkl 为我指明了正确的方向,我修复了向所有缺少默认值属性的字体添加 FistCharLastCharWidths

private static PdfReader FontFix(PdfReader pdfReader)
{
        for (var p = 1; p <= pdfReader.NumberOfPages; p++)
        {
            var dic = pdfReader.GetPageN(p);
            var resources = dic.GetAsDict(PdfName.RESOURCES);
            var fonts = resources?.GetAsDict(PdfName.FONT);
            if (fonts == null) continue;
            foreach (var key in fonts.Keys)
            {
                var font = fonts.GetAsDict(key);
                var firstChar = font.Get(PdfName.FIRSTCHAR);
                if(firstChar==null)
                    font.Put(PdfName.FIRSTCHAR, new PdfNumber(0));
                var lastChar = font.Get(PdfName.LASTCHAR);
                if (lastChar == null)
                    font.Put(PdfName.LASTCHAR, new PdfNumber(255));
                var widths= font.GetAsArray(PdfName.WIDTHS);
                if (widths == null)
                {
                    var array=new int[256];
                    array=Enumerable.Repeat(600, 256).ToArray();
                    font.Put(PdfName.WIDTHS, new PdfArray(array));
                }
            }
        }
        return pdfReader;
  } 

IText 无法正确读取 PDF 区域

PDF 中的错误

此问题的原因是 PDF 包含一个不完整的字体词典。PDF 中的大多数字体词典都是完整的,但有一个例外,对象 28 中的字典用于共享资源中的字体 Fo0,用于"填写"第二页和第三页上的字段:

<<
/Name       /Fo0
/Subtype    /TrueType
/BaseFont   /CourierNew
/Type       /Font
/Encoding   /WinAnsiEncoding
>>

特别是,此字体字典不包含所需的 Widths 条目,其值将是字体字形宽度的数组。

因此,iTextSharp 不知道字形的实际宽度,并使用 0 作为默认值。

顺便说一句,对于非常有限的类型 1 字体集,即所谓的标准 14 字体,允许(尽管已弃用(这种不完整的字体字典TrueType字体"CourierNew"显然不在其中。但是创建导致上述不完整结构的软件的开发人员可能并不关心查看PDF规范,而只是遵循那些特殊的Type 1字体的示例。

对代码的影响

在您的LimitedTextStrategy.RenderText实施中

public void RenderText(TextRenderInfo renderInfo)
{
    foreach (TextRenderInfo info in renderInfo.GetCharacterRenderInfos())
    {
        textextractionstrategy.RenderText(info);
    }
}

renderInfo(描述较长的字符串(拆分为多个TextRenderInfo实例(每个实例描述一个字形(。如果 renderInfo 的字体是关键的 Fo0,则所有这些TextRenderInfo实例都具有相同的位置,因为 iTextSharp 假定字形宽度为 0。

。使用LocationTextExtractionStrategy

然后,这些TextRenderInfo实例被过滤并转发到LocationTextExtractionStrategy稍后按位置对它们进行排序。由于位置重合,并且使用的排序算法不会将具有相同位置的元素保持其原始顺序,因此这种排序有效地打乱了它们。最终,你会以混乱的顺序获得所有相应的字符。

。使用SimpleTextExtractionStrategy

在这种情况下,这些TextRenderInfo实例将被过滤并转发到SimpleTextExtractionStrategy, 不会对它们进行排序,而是将相应的字符添加到结果字符串中。如果在内容流中显示操作的文本按读取顺序发生,则策略返回的结果也按正确的读取顺序进行。

为什么 Adobe Reader 以正确的顺序显示文本?

如果面对损坏的PDF,不同的程序可以尝试不同的策略来应对这种情况。

在手头的情况下,Adobe Reader最有可能在操作系统中搜索CourierNew TrueType字体程序,并使用那里的宽度信息。这很可能是该损坏字体结构的创建者所希望的。