我使用了LocationTextExtractionStrategy的扩展版本来提取一个pdf的连接文本及其位置/大小。我是通过使用locationalResult来做到这一点的。在我测试一个包含不同字体文本的pdf格式之前,这是很好的。突然,这些文字被分割成单个字符或小片段。
例如,"Detail“不是locationalResult列表中的任何一个对象,而是拆分为六个项(D、e、t、a、i、l)。
我尝试通过公开HorizontalTextExtractionStrategy方法来使用getLocationalResult:
public List<TextChunk> GetLocationalResult()
{
return (List<TextChunk>)locationalResultField.GetValue(this);
}
并使用PdfReaderContentParser提取文本:
reader = new PdfReader("some_pdf");
PdfReaderContentParser parser = new PdfReaderContentParser(reader);
var strategy = parser.ProcessContent(i, HorizontalTextExtractionStrategy());
foreach (HorizontalTextExtractionStrategy.HorizontalTextChunk chunk in strategy.GetLocationalResult())
{
// Do something with the chunk
}
但这也会返回相同的结果。有没有其他方法从pdf中提取连接的文本?
发布于 2016-11-21 08:28:59
在深入调试iTextSharp库之后,我发现我的文本是用TJ操作符绘制的,mkl也提到了这一点。
[<0027> -0.2<00280037> 0.2<0024002c> 0.2<002f> -0.2<0003>] TJ
iText不是将这些文本作为一个PdfString
来处理,而是作为一个PdfObjects
数组来处理,该数组的结果是为其中的每个PdfString
项调用renderListener.RenderText(renderInfo)
(参见ShowTextArray类和DisplayPdfString方法)。然而,在RenderText
方法中,有关数组中pdf字符串关系的信息丢失了,每个项目都作为一个独立的对象添加到locationalResult
中。
因为我的目标是提取“单个文本绘图指令的参数”,所以我扩展了PdfContentStreamProcessor
类关于一个新方法ProcessTexts
,该方法返回这些原子字符串的列表。我的解决方法不是很好,因为我不得不从原始源复制一些私有字段和方法,但是它对我很有用。
class PdfContentStreamProcessorEx : PdfContentStreamProcessor
{
private IDictionary<int, CMapAwareDocumentFont> cachedFonts = new Dictionary<int, CMapAwareDocumentFont>();
private ResourceDictionary resources = new ResourceDictionary();
private CMapAwareDocumentFont font = null;
public PdfContentStreamProcessorEx(IRenderListener renderListener) : base(renderListener)
{
}
public List<string> ProcessTexts(byte[] contentBytes, PdfDictionary resources)
{
this.resources.Push(resources);
var texts = new List<string>();
PRTokeniser tokeniser = new PRTokeniser(new RandomAccessFileOrArray(new RandomAccessSourceFactory().CreateSource(contentBytes)));
PdfContentParser ps = new PdfContentParser(tokeniser);
List<PdfObject> operands = new List<PdfObject>();
while (ps.Parse(operands).Count > 0)
{
PdfLiteral oper = (PdfLiteral)operands[operands.Count - 1];
if ("Tj".Equals(oper.ToString()))
{
texts.Add(getText((PdfString)operands[0]));
}
else if ("TJ".Equals(oper.ToString()))
{
string text = string.Empty;
foreach (PdfObject entryObj in (PdfArray)operands[0])
{
if (entryObj is PdfString)
{
text += getText((PdfString)entryObj);
}
}
texts.Add(text);
}
else if ("Tf".Equals(oper.ToString()))
{
PdfName fontResourceName = (PdfName)operands[0];
float size = ((PdfNumber)operands[1]).FloatValue;
PdfDictionary fontsDictionary = resources.GetAsDict(PdfName.FONT);
CMapAwareDocumentFont _font;
PdfObject fontObject = fontsDictionary.Get(fontResourceName);
if (fontObject is PdfDictionary)
_font = GetFont((PdfDictionary)fontObject);
else
_font = GetFont((PRIndirectReference)fontObject);
font = _font;
}
}
this.resources.Pop();
return texts;
}
string getText(PdfString @in)
{
byte[] bytes = @in.GetBytes();
return font.Decode(bytes, 0, bytes.Length);
}
private CMapAwareDocumentFont GetFont(PRIndirectReference ind)
{
CMapAwareDocumentFont font;
cachedFonts.TryGetValue(ind.Number, out font);
if (font == null)
{
font = new CMapAwareDocumentFont(ind);
cachedFonts[ind.Number] = font;
}
return font;
}
private CMapAwareDocumentFont GetFont(PdfDictionary fontResource)
{
return new CMapAwareDocumentFont(fontResource);
}
private class ResourceDictionary : PdfDictionary
{
private IList<PdfDictionary> resourcesStack = new List<PdfDictionary>();
virtual public void Push(PdfDictionary resources)
{
resourcesStack.Add(resources);
}
virtual public void Pop()
{
resourcesStack.RemoveAt(resourcesStack.Count - 1);
}
public override PdfObject GetDirectObject(PdfName key)
{
for (int i = resourcesStack.Count - 1; i >= 0; i--)
{
PdfDictionary subResource = resourcesStack[i];
if (subResource != null)
{
PdfObject obj = subResource.GetDirectObject(key);
if (obj != null) return obj;
}
}
return base.GetDirectObject(key); // shouldn't be necessary, but just in case we've done something crazy
}
}
}
发布于 2016-11-20 22:48:46
我使用了
LocationTextExtractionStrategy
的扩展版本来提取一个pdf的连接文本及其位置/大小。我是通过使用locationalResult
来做到这一点的。这是很好的工作,直到我测试了一个pdf包含不同字体的文本(ttf)。突然,这些文字被分割成单个字符或小片段。
这个问题是由于对LocationTextExtractionStrategy.locationalResult
私有列表成员变量内容的错误期望造成的。
这个TextChunk
实例列表包含从解析框架转发到策略的文本片段(或者可能是某些过滤器类预处理的文本),该框架分别转发它在内容流中遇到的每个字符串。
因此,如果内容流中看似连接的单词实际上是使用多个字符串绘制的,则可以获得多个TextChunk
实例。
实际上,getResultantText
方法中有一些“智能”,可以正确地连接这些块,在必要时添加一个空间等等。
在您的文档中,“详细信息”通常如下所示:
[<0027> -0.2<00280037> 0.2<0024002c> 0.2<002f> -0.2<0003>] TJ
如您所见,在“D”和“E”、“T”和“A”、“I”和“L”以及“L”和“”之间有轻微的文本插入点移动。(这种小幅度的移动通常代表着市场竞争。)因此,您将得到“D”、“ET”、“AI”和“L”的单独TextChunk
实例。
诚然,LocationTextExtractionStrategy.locationalResult
成员没有很好的文档;但由于它是一个私人成员,这个IMHO是可以原谅的。
对于许多文档来说,这是因为许多PDF创建者没有应用kerning和使用单个字符串对象简单地绘制连接的文本。
HorizontalTextExtractionStrategy
是从LocationTextExtractionStrategy
派生而来的,主要区别在于它将TextChunk
实例排列成一个字符串的方式。因此,您将在这里看到同样的碎片。
有没有其他方法从pdf中提取连接的文本?
如果您想要像“内容流中的原子字符串对象”中的“连接文本”,那么您已经拥有它们了。
如果您想要“连接文本”(如“可视连接文本,无论组成字母在内容流中绘制在哪里”),则必须将这些TextChunk
实例(如getResultantText
中的LocationTextExtractionStrategy
和HorizontalTextExtractionStrategy
do )与它们各自的TextChunkLocationDefaultImp
和HorizontalTextChunkLocation
实现中的比较方法结合在一起。
https://stackoverflow.com/questions/40659056
复制相似问题