我们如何使用带有空格的itextsharp从PDF中提取文本
本文关键字:PDF 提取 取文本 itextsharp 何使用 空格 我们 | 更新日期: 2023-09-27 18:12:22
我使用下面的方法逐行提取pdf文本。但问题是,它不能读取单词和数字之间的空格。这个问题的解决方案是什么?
我只是想创建一个字符串列表,列表对象中的每个字符串都有一个pdf文本行,因为它在pdf中包括空格。
public void readtextlinebyline(string filename) {
List<string> strlist = new List<string>();
PdfReader reader = new PdfReader(filename);
string text = string.Empty;
for (int page = 1; page <= 1; page++)
{
text += PdfTextExtractor.GetTextFromPage(reader, page ,new LocationTextExtractionStrategy())+" ";
}
reader.Close();
string[] words = text.Split(''n');
foreach (string word in words)
{
strlist.Add(word);
}
foreach (string st in strlist)
{
Response.Write(st +"<br/>");
}
}
我已经尝试过这种方法通过改变策略SimpleTextExtractionStrategy,以及,但它也不适合我。
关于为什么单词之间的空格有时不能被iText(Sharp)或其他PDF文本提取器正确识别的背景,已经在"iText java PDF到文本创建"的回答中解释了:这些"空格"不一定是使用空格字符创建的,而是使用创建小间隙的操作。但是,这些操作也用于其他目的(不中断单词),因此文本提取器必须使用启发式方法来确定这样的间隔是否为单词中断…
这尤其意味着您永远无法获得100%安全的破字检测。
你能做的,是改进所使用的启发式。
iText和iTextSharp标准文本提取策略,例如,如果
a)有一个空格字符或
b)至少有半个空格字符那么宽
项目a是一个确定的命中,但项目b可能经常失败的情况下密集设置的文本。上面参考的答案的问题的OP使用空格字符宽度的四分之一来代替,得到了相当好的结果。
您可以通过复制和更改您选择的文本提取策略来调整这些标准。
在SimpleTextExtractionStrategy
中,您可以在renderText
方法中找到此标准:
if (spacing > renderInfo.GetSingleSpaceWidth()/2f){
AppendTextChunk(' ');
}
在LocationTextExtractionStrategy
的情况下,这一准则同时也有了自己的方法:
/**
* Determines if a space character should be inserted between a previous chunk and the current chunk.
* This method is exposed as a callback so subclasses can fine tune the algorithm for determining whether a space should be inserted or not.
* By default, this method will insert a space if the there is a gap of more than half the font space character width between the end of the
* previous chunk and the beginning of the current chunk. It will also indicate that a space is needed if the starting point of the new chunk
* appears *before* the end of the previous chunk (i.e. overlapping text).
* @param chunk the new chunk being evaluated
* @param previousChunk the chunk that appeared immediately before the current chunk
* @return true if the two chunks represent different words (i.e. should have a space between them). False otherwise.
*/
protected bool IsChunkAtWordBoundary(TextChunk chunk, TextChunk previousChunk) {
float dist = chunk.DistanceFromEndOf(previousChunk);
if(dist < -chunk.CharSpaceWidth || dist > chunk.CharSpaceWidth / 2.0f)
return true;
return false;
}
将其放入其自己的方法的意图仅仅是需要对策略进行简单的子类化并重写该方法以调整启发式标准。这在等效的iText Java类的情况下工作得很好,但不幸的是,在移植到iTextSharp期间,没有将virtual
添加到声明中(截至版本5.4.4)。因此,目前iTextSharp仍然需要复制整个策略。
@Bruno你可能想告诉iText -> iTextSharp移植团队这件事。
虽然您可以在这些代码位置微调文本提取,但您应该意识到,您不会在这里找到100%的标准。原因如下:
- 在密集设置的文本中,单词之间的间隙可以小于字距或其他用于单词内部某些光学效果的间隙。因此,这里没有放之四海而皆准的因素。
- 在根本不使用空格字符的pdf中(因为您可以始终使用空格,这是可能的),"空格字符的宽度"可能是一些随机值或根本无法确定!
- 有一些有趣的pdf滥用空格字符宽度(可以在任何时候单独拉伸以进行后续操作)来做一些表格格式,同时使用空格进行断行。在这样的PDF中,空格字符的当前宽度值不能用于确定换行符。 有时候,你会发现在打印的一行中,有一些单词被隔开来强调。这些可能会被大多数启发式算法解析为一个字母单词的集合。
通过考虑所有字符之间的实际视觉自由空间(使用PDF呈现或字体信息分析机制),您可以获得比ittext启发式更好的启发式和由它派生的其他常量,但要获得可感知的改进,您必须投入大量时间。
我有自己的实现,它工作得很好。
/// <summary>
/// Read a PDF file and returns the string content.
/// </summary>
/// <param name="par">ByteArray, MemoryStream or URI</param>
/// <returns>FileContent.</returns>
public static string ReadPdfFile(object par)
{
if (par == null) throw new ArgumentNullException("par");
PdfReader pdfReader = null;
var text = new StringBuilder();
if (par is MemoryStream)
pdfReader = new PdfReader((MemoryStream)par);
else if (par is byte[])
pdfReader = new PdfReader((byte[])par);
else if (par is Uri)
pdfReader = new PdfReader((Uri)par);
if (pdfReader == null)
throw new InvalidOperationException("Unable to read the file.");
for (var page = 1; page <= pdfReader.NumberOfPages; page++)
{
var strategy = new SimpleTextExtractionStrategy();
var currentText = PdfTextExtractor.GetTextFromPage(pdfReader, page, strategy);
currentText = Encoding.UTF8.GetString(Encoding.Convert(Encoding.Default, Encoding.UTF8, Encoding.Default.GetBytes(currentText)));
text.Append(currentText);
}
pdfReader.Close();
return text.ToString();
}
using (PdfReader reader = new PdfReader(path))
{
StringBuilder text = new StringBuilder();
StringBuilder textfinal = new StringBuilder();
String page = "";
for (int i = 1; i <= reader.NumberOfPages; i++)
{
text.Append(PdfTextExtractor.GetTextFromPage(reader, i));
page = PdfTextExtractor.GetTextFromPage(reader, i);
string[] lines = page.Split(''n');
foreach (string line in lines)
{
string[] words = line.Split(''n');
foreach (string wrd in words)
{
}
textfinal.Append(line);
textfinal.Append(Environment.NewLine);
}
page = "";
}
}