PDF使用C#读取突出显示的文本(突出显示注释)

时间:2014-01-08 15:47:35

标签: c# pdf itextsharp itext

我使用iTextSharp编写了一个提取工具,可以从PDF文档中提取注释信息。对于高亮注释,我只获得页面上突出显示的区域的矩形。

我的目标是提取已突出显示的文本。为此,我使用`PdfTextExtractor'。

Rectangle rect = new Rectangle(
    pdfArray.GetAsNumber(0).FloatValue, 
    pdfArray.GetAsNumber(1).FloatValue,
    pdfArray.GetAsNumber(2).FloatValue,
    pdfArray.GetAsNumber(3).FloatValue);

RenderFilter[] filter = { new RegionTextRenderFilter(rect) };
ITextExtractionStrategy strategy = new FilteredTextRenderListener(new LocationTextExtractionStrategy(), filter);
string textInsideRect = PdfTextExtractor.GetTextFromPage(pdfReader, pageNo, strategy);
return textInsideRect;

PdfTextExtractor返回的结果并不完全正确。例如,它返回“将消除纸张追逐”,即使只有“消除”被突出显示。

有趣的是,包含突出显示的“消除”的TJ的整个文本是“将消除纸张追逐”(TJ是写入文本的PDF指令到页面。)

我很想听到有关此问题的任何意见 - 也包括不涉及iTextSharp的解决方案。

2 个答案:

答案 0 :(得分:8)

原因

  

有趣的是,包含突出显示的“消除”的TJ的整个文本是“将消除纸张追逐”(TJ是将文本写入页面的PDF指令)。

这实际上是您的问题的原因。 iText解析器类将文本转发到它们在内容流中作为连续字符串找到的片段中的渲染侦听器。您使用的过滤机制会过滤这些碎片。因此,过滤器接受整个句子。

因此,您需要的是一些预处理步骤,将这些片段分成各自的字符,然后将这些字符分别转发到过滤后的渲染侦听器。

这实际上很容易实现。转发文本片段的参数类型TextRenderInfo,提供了一种自我分割的方法:

/**
 * Provides detail useful if a listener needs access to the position of each individual glyph in the text render operation
 * @return A list of {@link TextRenderInfo} objects that represent each glyph used in the draw operation. The next effect is if there was a separate Tj opertion for each character in the rendered string
 * @since 5.3.3
 */
public List<TextRenderInfo> getCharacterRenderInfos() // iText / Java
virtual public List<TextRenderInfo> GetCharacterRenderInfos() // iTextSharp / .Net

因此,您所要做的就是创建并使用RenderListener / IRenderListener实现,该实现将它获得的所有调用转发给另一个侦听器(在您的情况下为过滤的侦听器),其扭曲为{ {1}} / renderText拆分其RenderText参数,然后逐个转发碎片。

Java样本

随着OP要求更多细节,这里有更多代码。但是,由于我主要使用Java,因此我使用Java为iText提供它。但是很容易为iTextSharp移植到C#。

如上所述,需要一个预处理步骤,将文本片段分成各自的字符,并将它们单独转发到过滤后的渲染器监听器。

对于此步骤,您可以使用此课程TextRenderInfo

TextRenderInfoSplitter

如果你有一个package stackoverflow.itext.extraction; import com.itextpdf.text.pdf.parser.ImageRenderInfo; import com.itextpdf.text.pdf.parser.TextExtractionStrategy; import com.itextpdf.text.pdf.parser.TextRenderInfo; public class TextRenderInfoSplitter implements TextExtractionStrategy { public TextRenderInfoSplitter(TextExtractionStrategy strategy) { this.strategy = strategy; } public void renderText(TextRenderInfo renderInfo) { for (TextRenderInfo info : renderInfo.getCharacterRenderInfos()) { strategy.renderText(info); } } public void beginTextBlock() { strategy.beginTextBlock(); } public void endTextBlock() { strategy.endTextBlock(); } public void renderImage(ImageRenderInfo renderInfo) { strategy.renderImage(renderInfo); } public String getResultantText() { return strategy.getResultantText(); } final TextExtractionStrategy strategy; } (就像你的TextExtractionStrategy strategy一样),那么现在可以使用这样的单字符new FilteredTextRenderListener(new LocationTextExtractionStrategy(), filter)实例来提供它:

TextRenderInfo

我使用this answer为区域

创建的PDF对其进行了测试
String textInsideRect = PdfTextExtractor.getTextFromPage(reader, pageNo, new TextRenderInfoSplitter(strategy));

作为参考,我在PDF中标记了区域:

Screenshot of PDF with marked area

按区域过滤的文本提取没有Rectangle rect = new Rectangle(200, 600, 200, 135); 会导致:

TextRenderInfoSplitter

I am trying to create a PDF file with a lot of text contents in the document. I am using PDFBox 区域过滤的文字提取结果为:

TextRenderInfoSplitter

顺便说一句,你在这里看到了将文本分成早期单个字符的缺点:最终文本行使用非常大的字符间距进行排版。如果您保留PDF中的文本片段,文本提取策略仍然可以很容易地看到该行包含两个词 using PDFBox 。只要您逐字地将文本片段提供给文本提取策略,他们就可能会将这些广泛设置的单词解释为多个单字母单词。

改进

  

突出显示的单词“消除”例如被提取为“消除t”。通过双击单词并在Adobe Acrobat Reader中突出显示,突出显示了这一点。

在我的上面的示例中发生了类似的事情,几乎没有触及感兴趣区域的字母使其成为结果。

这是由于 to create a PDF f ntents in the docu n g P D F RegionTextRenderFilter的实现,允许所有文本继续其基线与相关矩形相交,即使交集只包含一个点:

allowText

鉴于您首先将文本拆分为字符,您可能需要检查它们各自的基线是否完全包含在相关区域中,即实现自己的 public boolean allowText(TextRenderInfo renderInfo){ LineSegment segment = renderInfo.getBaseline(); Vector startPoint = segment.getStartPoint(); Vector endPoint = segment.getEndPoint(); float x1 = startPoint.get(Vector.I1); float y1 = startPoint.get(Vector.I2); float x2 = endPoint.get(Vector.I1); float y2 = endPoint.get(Vector.I2); return filterRect.intersectsLine(x1, y1, x2, y2); } 复制RenderFilter然后替换

RegionTextRenderFilter

通过

return filterRect.intersectsLine(x1, y1, x2, y2);

根据Adobe Acrobat Reader 中突出显示文本的确切内容,您可能希望以完全自定义的方式更改此内容。

答案 1 :(得分:3)

突出显示注释表示四边形的集合,这些四边形表示页面上/QuadPoints条目中注释所包围的页面上的区域。

他们为什么这样?

实际上这是我的错。在Acrobat 1.0中,我处理了“查找文本”代码,该代码最初仅使用矩形来表示页面上所选区域。在处理代码时,我对结果非常不满意,特别是对于文本跟随土地细节的地图。

结果,我让查找工具在页面上构建了一组四边形,并在可能的情况下退火,以构建单词。

在Acrobat 2.0中,负责完整广义文本提取的工程师构建了一个名为Wordy的算法,该算法比我的第一次切割更好,但他保留了四边形代码,因为这是对页面上内容的最准确表示。

几乎所有与文本相关的代码都被重构为使用此代码。

然后我们得到高亮注释。将标记注释添加到Acrobat时,它们用于装饰页面上已有的文本。当用户点击页面时,Wordy将文本提取到适当的数据结构中,然后文本选择工具将鼠标移动映射到四边形集上。当创建文本高亮注释时,来自Wordy的四边形子集将被放入新文本高亮注释中。

如何获得突出显示的页面上的文字。棘手。你必须在页面上提取文本(你没有Wordy,抱歉),然后从注释中找到集合中包含的所有四边形。