Pdf 从表格单元格中提取文本
我有一个pdf文件。pdf包含一个表。该表包含许多单元格(>100)。我知道表格中每个单元格的确切位置(x,y)和尺寸(w,h)。Pdf 从表格单元格中提取文本,pdf,itextsharp,Pdf,Itextsharp,我有一个pdf文件。pdf包含一个表。该表包含许多单元格(>100)。我知道表格中每个单元格的确切位置(x,y)和尺寸(w,h)。 我需要使用itextsharp从单元格中提取文本。使用PdfReadContentParser+FilteredTextRenderListener(使用类似的代码),我可以提取文本,但我需要为每个单元格运行整个过程。我的pdf有很多单元格,程序运行时间太长。有没有办法从“矩形”列表中提取文本?我需要知道每个矩形的文本。我正在寻找类似PDFTextStripperB
我需要使用itextsharp从单元格中提取文本。使用PdfReadContentParser+FilteredTextRenderListener(使用类似的代码),我可以提取文本,但我需要为每个单元格运行整个过程。我的pdf有很多单元格,程序运行时间太长。有没有办法从“矩形”列表中提取文本?我需要知道每个矩形的文本。我正在寻找类似PDFTextStripperByArea by PdfBox(您可以根据需要定义任意多个区域,并使用.getTextForRegion(“区域名称”)获取文本)的内容。此选项不会立即包含在iTextSharp发行版中,但很容易实现。在下文中,我使用iText(Java)类、接口和方法名,因为我更熟悉Java。它们应该很容易翻译成iTextSharp(C#)名称 如果使用
LocationTextExtractionStrategy
,则可以使用其后验TextChunkFilter
机制,而不是链接到的示例中使用的先验FilteredRenderListener
机制。该机制已在5.3.3版中引入
为此,首先使用LocationTextExtractionStrategy
解析整个页面内容,而不应用任何FilteredRenderListener
过滤。这使得策略对象为包含相关基线段的页面上的所有PDF文本对象收集textcunk
对象
然后使用TextChunkFilter
参数调用策略的getResultantText
重载(而不是常规的无参数重载):
对于每个表单元格,使用不同的TextChunkFilter
实例调用它。您必须实现这个过滤器接口,这并不太困难,因为它只定义了一个方法:
public static interface TextChunkFilter
{
/**
* @param textChunk the chunk to check
* @return true if the chunk should be allowed
*/
public boolean accept(TextChunk textChunk);
}
因此,给定单元格的过滤器的accept方法必须测试所涉及的文本块是否在单元格内
(当然,您也可以创建一个实例,而不是为每个单元格创建单独的实例,该实例的参数(即单元格坐标)可以在getResultantText
调用之间更改。)
PS:如OP所述,此textcunkfilter
尚未移植到iTextSharp。不过,要做到这一点并不难,因为只有一个小界面和一种方法可以添加到策略中
PPS:在被询问的评论中
然后,当使用
getResultantText()
时,您是否仍然调用pdftextractor.getTextFromPage()
,或者它以某种方式替换了该调用?如果是,那么如何指定要提取到的页面
实际上,PdfTextExtractor.getTextFromPage()
内部已经使用了无参数getResultantText()
重载:
public String getResultantText(TextChunkFilter chunkFilter)
public static String getTextFromPage(PdfReader reader, int pageNumber, TextExtractionStrategy strategy, Map<String, ContentOperator> additionalContentOperators) throws IOException
{
PdfReaderContentParser parser = new PdfReaderContentParser(reader);
return parser.processContent(pageNumber, strategy, additionalContentOperators).getResultantText();
}
但是,在当前的上下文中,我们只想分析一次页面内容并应用多个过滤器,每个单元格一个过滤器,我们可以将其概括为:
public static List<String> getTextFromPage(PdfReader reader, int pageNumber, LocationTextExtractionStrategy strategy, Map<String, ContentOperator> additionalContentOperators, Iterable<TextChunkFilter> chunkFilters) throws IOException
{
PdfReaderContentParser parser = new PdfReaderContentParser(reader);
parser.processContent(pageNumber, strategy, additionalContentOperators)
List<String> result = new ArrayList<>();
for (TextChunkFilter chunkFilter : chunkFilters)
{
result.add(strategy).getResultantText(chunkFilter);
}
return result;
}
public static List getTextFromPage(PdfReader阅读器、int pageNumber、LocationTextExtractionStrategy策略、Map additionalContentOperators、Iterable chunkFilters)引发IOException
{
PdfReaderContentParser=新的PdfReaderContentParser(读取器);
processContent(页码、策略、additionalContentOperators)
列表结果=新建ArrayList();
for(TextChunkFilter chunkFilter:chunkFilters)
{
result.add(策略).getResultantText(chunkFilter);
}
返回结果;
}
(您可以通过使用Java 8 collection streaming(而不是老式的
for
循环)使其看起来更华丽。)此选项不会立即包含在iTextSharp发行版中,但很容易实现。在下文中,我使用iText(Java)类、接口和方法名,因为我更熟悉Java。它们应该很容易翻译成iTextSharp(C#)名称
如果使用LocationTextExtractionStrategy
,则可以使用其后验TextChunkFilter
机制,而不是链接到的示例中使用的先验FilteredRenderListener
机制。该机制已在5.3.3版中引入
为此,首先使用LocationTextExtractionStrategy
解析整个页面内容,而不应用任何FilteredRenderListener
过滤。这使得策略对象为包含相关基线段的页面上的所有PDF文本对象收集textcunk
对象
然后使用TextChunkFilter
参数调用策略的getResultantText
重载(而不是常规的无参数重载):
对于每个表单元格,使用不同的TextChunkFilter
实例调用它。您必须实现这个过滤器接口,这并不太困难,因为它只定义了一个方法:
public static interface TextChunkFilter
{
/**
* @param textChunk the chunk to check
* @return true if the chunk should be allowed
*/
public boolean accept(TextChunk textChunk);
}
因此,给定单元格的过滤器的accept方法必须测试所涉及的文本块是否在单元格内
(当然,您也可以创建一个实例,而不是为每个单元格创建单独的实例,该实例的参数(即单元格坐标)可以在getResultantText
调用之间更改。)
PS:如OP所述,此textcunkfilter
尚未移植到iTextSharp。不过,要做到这一点并不难,因为只有一个小界面和一种方法可以添加到策略中
PPS:在被询问的评论中
然后,当使用
getResultantText()
时,您是否仍然调用pdftextractor.getTextFromPage()
,或者它以某种方式替换了该调用?如果是,那么如何指定要提取到的页面
实际上pdftextractor.getTextFromPage()
内部已经使用了no参数getResu
using (PdfReader pdfReader = new PdfReader(stream))
{
for (int page = 1; page <= pdfReader.NumberOfPages; page++)
{
TableExtractionStrategy tableExtractionStrategy = new TableExtractionStrategy();
string pageText = PdfTextExtractor.GetTextFromPage(pdfReader, page, tableExtractionStrategy);
var table = tableExtractionStrategy.GetTable();
}
}
public class TableExtractionStrategy : LocationTextExtractionStrategy
{
public float NextCharacterThreshold { get; set; } = 1;
public int NextLineLookAheadDepth { get; set; } = 500;
public bool AccomodateWordWrapping { get; set; } = true;
private List<TableTextChunk> Chunks { get; set; } = new List<TableTextChunk>();
public override void RenderText(TextRenderInfo renderInfo)
{
base.RenderText(renderInfo);
string text = renderInfo.GetText();
Vector bottomLeft = renderInfo.GetDescentLine().GetStartPoint();
Vector topRight = renderInfo.GetAscentLine().GetEndPoint();
Rectangle rectangle = new Rectangle(bottomLeft[Vector.I1], bottomLeft[Vector.I2], topRight[Vector.I1], topRight[Vector.I2]);
Chunks.Add(new TableTextChunk(rectangle, text));
}
public List<List<string>> GetTable()
{
List<List<string>> lines = new List<List<string>>();
List<string> currentLine = new List<string>();
float? previousBottom = null;
float? previousRight = null;
StringBuilder currentString = new StringBuilder();
// iterate through all chunks and evaluate
for (int i = 0; i < Chunks.Count; i++)
{
TableTextChunk chunk = Chunks[i];
// determine if we are processing the same row based on defined space between subsequent chunks
if (previousBottom.HasValue && previousBottom == chunk.Rectangle.Bottom)
{
if (chunk.Rectangle.Left - previousRight > 1)
{
currentLine.Add(currentString.ToString());
currentString.Clear();
}
currentString.Append(chunk.Text);
previousRight = chunk.Rectangle.Right;
}
else
{
// if we are processing a new line let's check to see if this could be word wrapping behavior
bool isNewLine = true;
if (AccomodateWordWrapping)
{
int readAheadDepth = Math.Min(i + NextLineLookAheadDepth, Chunks.Count);
if (previousBottom.HasValue)
for (int j = i; j < readAheadDepth; j++)
{
if (previousBottom == Chunks[j].Rectangle.Bottom)
{
isNewLine = false;
break;
}
}
}
// if the text was not word wrapped let's treat this as a new table row
if (isNewLine)
{
if (currentString.Length > 0)
currentLine.Add(currentString.ToString());
currentString.Clear();
previousBottom = chunk.Rectangle.Bottom;
previousRight = chunk.Rectangle.Right;
currentString.Append(chunk.Text);
if (currentLine.Count > 0)
lines.Add(currentLine);
currentLine = new List<string>();
}
else
{
if (chunk.Rectangle.Left - previousRight > 1)
{
currentLine.Add(currentString.ToString());
currentString.Clear();
}
currentString.Append(chunk.Text);
previousRight = chunk.Rectangle.Right;
}
}
}
return lines;
}
private struct TableTextChunk
{
public Rectangle Rectangle;
public string Text;
public TableTextChunk(Rectangle rect, string text)
{
Rectangle = rect;
Text = text;
}
public override string ToString()
{
return Text + " (" + Rectangle.Left + ", " + Rectangle.Bottom + ")";
}
}
}