【问题标题】:Problems to extract text from PDF for certain pdfs only C#仅针对某些 pdf 从 PDF 中提取文本的问题 C#
【发布时间】:2018-07-25 22:44:12
【问题描述】:

我需要从 PDF 文件中提取一些数据。 我正在使用 iTextSharp 来做到这一点。

我正在使用我在网上创建的这段代码:

using System;
using System.IO;
using iTextSharp.text.pdf;

namespace PdfToText
{
/// <summary>
/// Parses a PDF file and extracts the text from it.
/// </summary>
public class PDFParser
{
    /// BT = Beginning of a text object operator 
    /// ET = End of a text object operator
    /// Td move to the start of next line
    ///  5 Ts = superscript
    /// -5 Ts = subscript

    #region Fields

    #region _numberOfCharsToKeep
    /// <summary>
    /// The number of characters to keep, when extracting text.
    /// </summary>
    private static int _numberOfCharsToKeep = 15;
    #endregion

    #endregion

    #region ExtractText
    /// <summary>
    /// Extracts a text from a PDF file.
    /// </summary>
    /// <param name="inFileName">the full path to the pdf file.</param>
    /// <param name="outFileName">the output file name.</param>
    /// <returns>the extracted text</returns>
    public bool ExtractText(string inFileName, string outFileName)
    {
        StreamWriter outFile = null;
        try
        {
            outFileName = String.Empty;

            outFileName = Path.GetDirectoryName(System.AppDomain.CurrentDomain.BaseDirectory);
            //string currentDirectory = Directory.GetCurrentDirectory();
            //string filePath = System.IO.Path.Combine(currentDirectory, "Data", "myfile.txt");
            // extract the text
            //string test = "";
            outFileName += @"\test.txt";
            // Create a reader for the given PDF file
            PdfReader reader = new PdfReader(inFileName);
            //outFile = File.CreateText(outFileName);
            outFile = new StreamWriter(outFileName, true, System.Text.Encoding.UTF8);

            Console.Write("Processing: ");

            int totalLen = 68;
            float charUnit = ((float)totalLen) / (float)reader.NumberOfPages;
            int totalWritten = 0;
            float curUnit = 0;

            for (int page = 1; page <= reader.NumberOfPages; page++)
            {
                outFile.Write(ExtractTextFromPDFBytes(reader.GetPageContent(page)) + " ");

                // Write the progress.
                if (charUnit >= 1.0f)
                {
                    for (int i = 0; i < (int)charUnit; i++)
                    {
                        Console.Write("#");
                        totalWritten++;
                    }
                }
                else
                {
                    curUnit += charUnit;
                    if (curUnit >= 1.0f)
                    {
                        for (int i = 0; i < (int)curUnit; i++)
                        {
                            Console.Write("#");
                            totalWritten++;
                        }
                        curUnit = 0;
                    }

                }
            }

            if (totalWritten < totalLen)
            {
                for (int i = 0; i < (totalLen - totalWritten); i++)
                {
                    Console.Write("#");
                }
            }
            return true;
        }
        catch(Exception ex)
        {
            return false;
        }
        finally
        {
            if (outFile != null) outFile.Close();
        }
    }
    #endregion

    #region ExtractTextFromPDFBytes
    /// <summary>
    /// This method processes an uncompressed Adobe (text) object 
    /// and extracts text.
    /// </summary>
    /// <param name="input">uncompressed</param>
    /// <returns></returns>
    private string ExtractTextFromPDFBytes(byte[] input)
    {
        if (input == null || input.Length == 0) return "";

        try
        {
            string resultString = "";

            // Flag showing if we are we currently inside a text object
            bool inTextObject = false;

            // Flag showing if the next character is literal 
            // e.g. '\\' to get a '\' character or '\(' to get '('
            bool nextLiteral = false;

            // () Bracket nesting level. Text appears inside ()
            int bracketDepth = 0;

            // Keep previous chars to get extract numbers etc.:
            char[] previousCharacters = new char[_numberOfCharsToKeep];
            for (int j = 0; j < _numberOfCharsToKeep; j++) previousCharacters[j] = ' ';


            for (int i = 0; i < input.Length; i++)
            {
                char c = (char)input[i];

                if (inTextObject)
                {
                    // Position the text
                    if (bracketDepth == 0)
                    {
                        if (CheckToken(new string[] { "TD", "Td" }, previousCharacters))
                        {
                            resultString += "\n\r";
                        }
                        else
                        {
                            if (CheckToken(new string[] { "'", "T*", "\"" }, previousCharacters))
                            {
                                resultString += "\n";
                            }
                            else
                            {
                                if (CheckToken(new string[] { "Tj" }, previousCharacters))
                                {
                                    resultString += " ";
                                }
                            }
                        }
                    }

                    // End of a text object, also go to a new line.
                    if (bracketDepth == 0 &&
                        CheckToken(new string[] { "ET" }, previousCharacters))
                    {

                        inTextObject = false;
                        resultString += " ";
                    }
                    else
                    {
                        // Start outputting text
                        if ((c == '(') && (bracketDepth == 0) && (!nextLiteral))
                        {
                            bracketDepth = 1;
                        }
                        else
                        {
                            // Stop outputting text
                            if ((c == ')') && (bracketDepth == 1) && (!nextLiteral))
                            {
                                bracketDepth = 0;
                            }
                            else
                            {
                                // Just a normal text character:
                                if (bracketDepth == 1)
                                {
                                    // Only print out next character no matter what. 
                                    // Do not interpret.
                                    if (c == '\\' && !nextLiteral)
                                    {
                                        nextLiteral = true;
                                    }
                                    else
                                    {
                                        if (((c >= ' ') && (c <= '~')) ||
                                            ((c >= 128) && (c < 255)))
                                        {
                                            resultString += c.ToString();
                                        }

                                        nextLiteral = false;
                                    }
                                }
                            }
                        }
                    }
                }

                // Store the recent characters for 
                // when we have to go back for a checking
                for (int j = 0; j < _numberOfCharsToKeep - 1; j++)
                {
                    previousCharacters[j] = previousCharacters[j + 1];
                }
                previousCharacters[_numberOfCharsToKeep - 1] = c;

                // Start of a text object
                if (!inTextObject && CheckToken(new string[] { "BT" }, previousCharacters))
                {
                    inTextObject = true;
                }
            }
            return resultString;
        }
        catch
        {
            return "";
        }
    }
    #endregion

    #region CheckToken
    /// <summary>
    /// Check if a certain 2 character token just came along (e.g. BT)
    /// </summary>
    /// <param name="search">the searched token</param>
    /// <param name="recent">the recent character array</param>
    /// <returns></returns>
    private bool CheckToken(string[] tokens, char[] recent)
    {
        foreach (string token in tokens)
        {
            if ((recent[_numberOfCharsToKeep - 3] == token[0]) &&
                (recent[_numberOfCharsToKeep - 2] == token[1]) &&
                ((recent[_numberOfCharsToKeep - 1] == ' ') ||
                (recent[_numberOfCharsToKeep - 1] == 0x0d) ||
                (recent[_numberOfCharsToKeep - 1] == 0x0a)) &&
                ((recent[_numberOfCharsToKeep - 4] == ' ') ||
                (recent[_numberOfCharsToKeep - 4] == 0x0d) ||
                (recent[_numberOfCharsToKeep - 4] == 0x0a))
                )
            {
                return true;
            }
        }
        return false;
    }
    #endregion
}

}

我是这样用的:

 PDFParser pdfParser = new PDFParser();
 pdfParser.ExtractText(pdfFile,Path.GetFileNameWithoutExtension(pdfFile) + ".txt");

所以pdf内容写在一个txt文件中。 它适用于某些 pdf-s,但对于我真正需要使用的 pdf 文件,txt 文件始终为空。我没有收到错误,但由于某种原因,它没有写任何东西,尽管正如您在此屏幕截图中看到的那样,它识别出 pdf,它有 2 页...

这是我需要的pdf,但txt始终为空。(黑线是我添加的,所以我想在txt中写时没有出现)

这是另一个 pdf。为此,该程序可以正常工作,并且写入的是一个txt文件。它比其他 pdf 大得多,但我仍然可以提取文本,而其他我不能。

您知道可能是什么问题吗?

【问题讨论】:

    标签: c# pdf


    【解决方案1】:

    评论太长,可能是您不喜欢得到的答案:

    在 PDF 中,“Text your see”(即字体外观如何)和“What the glyphs mean”(即字形映射到哪个 utf8 字母)是不同的东西。

    它们存储在 pdf 的不同部分 - 完全有可能 pdf 看起来完全没有问题,但如果您尝试提取文本,它将不会给您任何信息,因为它只包含您的文本字形的形状而不是它们“意义”。

    尝试打开 pdf 并选择 + 复制您之后的文本,如果您将其粘贴到编辑器中并注意到那里,您的 pdf 缺少“此字形显示什么 utf8 字母”的信息。

    或者:

    也可能是您的 pdf 仅包含文本的图像 - 可以说是照片。你可以阅读它,iTextSharp 只看到一个“图片” - 没有文字。


    那些是可能的'为什么会回答你的问题。至于如何解决:

    关于 SO 上损坏的 PDF 有几个问题:

    How to repair a PDF file and embed missing fonts

    Embedded fonts in PDF: copy and paste problems(this answer)

    复制和粘贴与文本解析有关,因此可能会帮助您解决问题。


    您的编辑显示了有关您的解析的详细信息,为什么不利用 iTextSharp 呢?

    using iTextSharp.text.pdf;
    using iTextSharp.text.pdf.parser;
    
    public static string ExtractTextFromPdf(string path)
    {
      using (PdfReader reader = new PdfReader(path))
      {
        StringBuilder text = new StringBuilder();
    
        for (int i = 1; i <= reader.NumberOfPages; i++)
        {
            text.Append(PdfTextExtractor.GetTextFromPage(reader, i));
        }
    
        return text.ToString();
      }
    

    来自:http://www.squarepdf.net/parsing-pdf-files-using-itextsharp

    或喜欢这里:parse-pdf-with-itextsharp-and-then-extract-specific-text-to-the-screen?

    【讨论】:

    • 我已经尝试过您提出的将文本从 PDF 复制到文本文件的方法,我可以复制。所以这不是问题。我将阅读您刚刚粘贴在答案中的链接,
    • @Orsi 都与“无法复制”有关,因此在您的情况下,它们似乎没有帮助。
    • @Orsi 为什么不使用 iTextSharp 附带的有关文本提取的工具?见编辑。
    • 你拯救了我的一天!谢谢!这样它就可以工作了。 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-03-17
    • 1970-01-01
    • 1970-01-01
    • 2022-10-05
    • 2015-07-28
    • 1970-01-01
    • 2020-11-03
    相关资源
    最近更新 更多