我正在使用LocationTextExtractionStrategy
结合自定义ITextExtractionStrategy
类来阅读PDF。使用此代码,我可以毫无问题地阅读基于coords的文档部分。
现在我得到的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文件。
更新
如果我用LocationTextExtractionStrategy
更改SimpleTextExtractionStrategy
,它会识别没有奇怪字符的整行(PDF结构?)。
更新2
我现在可以分享文件了!有问题的页面是2°和3°
Test solution to read the file
更新3
mkl向我指出了正确的方向,我修复了将FistChar
,LastChar
和Widths
添加到所有缺少默认值属性的字体。
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;
}
答案 0 :(得分:2)
此问题的原因是PDF包含一个不完整的字体字典。 PDF中的大多数字体词典都是完整的,但有一个例外,对象28中的字典用于共享资源中的字体 Fo0 ,用于填充&#34;填写&#34;第二页和第三页的字段:
<<
/Name /Fo0
/Subtype /TrueType
/BaseFont /CourierNew
/Type /Font
/Encoding /WinAnsiEncoding
>>
特别是此字体字典不包含必需的宽度条目,其值将是字体字形宽度的数组。
因此,iTextSharp不知道字形实际有多宽,并使用0作为默认值。
除此之外,对于非常有限的Type 1字体(即所谓的标准14字体),允许使用这种不完整的字体字典(尽管已弃用)。 TrueType字体&#34; CourierNew&#34;显然不在其中。但是,创建负责上述不完整结构的软件的开发人员可能并不关心查看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
, 对它们进行排序,而是将相应的字符添加到结果字符串中。如果在内容流中显示操作的文本按阅读顺序发生,则策略返回的结果也是正确的阅读顺序。
如果遇到破损的PDF,不同的程序可以尝试不同的策略来应对这种情况。
手头的Adobe Reader最有可能在操作系统中搜索CourierNew TrueType字体程序并使用那里的宽度信息。这很可能是那个破碎字体结构的创造者希望的。