3

我正在使用以下方法逐行提取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 来尝试过这种方法,但它也不适合我。

4

3 回答 3

15

关于为什么 iText(Sharp) 或其他 PDF 文本提取器有时无法正确识别单词之间的空格的背景,已在“itext java pdf to text creation”的答案中进行了解释:这些“空格”不一定是使用空格创建的字符,而是使用创建一个小间隙的操作。但是,这些操作也用于其他目的(不会断词),因此文本提取器必须使用启发式方法来确定这样的间隙是否是断词......

这尤其意味着您永远不会获得 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 渲染或字体信息分析机制),您可以比 iText 启发式和使用其他常量派生的启发式更好,但要获得可感知的改进,您必须投入大量时间.

于 2013-11-18T14:09:21.380 回答
0

我有自己的实现,而且效果很好。

    /// <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();
    }
于 2013-11-18T11:59:01.727 回答
0
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 = "";
                }
           }
于 2016-04-28T08:19:29.047 回答