如何使用C#中的iTextSharp获取pdf文件中的特定段落

本文关键字:段落 文件 pdf 何使用 中的 iTextSharp 获取 | 更新日期: 2023-09-27 18:20:22

我在C#winform应用程序中使用iTextSharp。我想在PDF文件中获取特定的段落。这在iTextSharp中可能吗?

如何使用C#中的iTextSharp获取pdf文件中的特定段落

是和否

首先是否定的。PDF格式没有文本结构的概念,比如段落、句子甚至单词,它只是有连续的文本。事实上,两段文字彼此靠近,因此我们认为它们是结构化的,这是一件人类的事情。当你在PDF中看到一个看起来像三行的段落时,实际上,生成PDF的程序实际上完成了将文本分割成三行不相关的文本,然后在特定的x、y坐标处绘制每一行的工作。更糟糕的是,根据设计者的需求,每一行文本都可能由更小的行组成,这些行可能是单词,甚至只是字符。所以它可能是draw "the cat in the hat" at 10,10,也可能是draw "t" at 10,10, then draw "h" at 14,10, then draw "e" at 18,10等等。这在像Adobe InDesign这样精心设计的程序中的PDF中非常常见。

现在是的。事实上这是一个可能。如果你愿意投入一点工作,你可能会让iTextSharp做你想要的事情。有一个名为PdfTextExtractor的类,它有一个称为GetTextFromPage的方法,可以从页面中获取所有原始文本。此方法的最后一个参数是实现ITextExtractionStrategy接口的对象。如果您创建自己的类来实现这个接口,那么您可以处理每次运行的文本并执行自己的逻辑。

在这个接口中,有一个名为RenderText的方法,每次运行文本都会调用它。您将得到一个iTextSharp.text.pdf.parser.TextRenderInfo对象,您可以从中获取运行的原始文本,以及其他内容,如开始的当前坐标、当前字体等。由于文本的可视行可以由多个运行组成,因此可以使用此方法将运行的基线(开始的x坐标)与上一次运行进行比较,以确定它是否属于同一可视行。

以下是该接口的实现示例:

    public class TextAsParagraphsExtractionStrategy : iTextSharp.text.pdf.parser.ITextExtractionStrategy {
        //Text buffer
        private StringBuilder result = new StringBuilder();
        //Store last used properties
        private Vector lastBaseLine;
        //Buffer of lines of text and their Y coordinates. NOTE, these should be exposed as properties instead of fields but are left as is for simplicity's sake
        public List<string> strings = new List<String>();
        public List<float> baselines = new List<float>();
        //This is called whenever a run of text is encountered
        public void RenderText(iTextSharp.text.pdf.parser.TextRenderInfo renderInfo) {
            //This code assumes that if the baseline changes then we're on a newline
            Vector curBaseline = renderInfo.GetBaseline().GetStartPoint();
            //See if the baseline has changed
            if ((this.lastBaseLine != null) && (curBaseline[Vector.I2] != lastBaseLine[Vector.I2])) {
                //See if we have text and not just whitespace
                if ((!String.IsNullOrWhiteSpace(this.result.ToString()))) {
                    //Mark the previous line as done by adding it to our buffers
                    this.baselines.Add(this.lastBaseLine[Vector.I2]);
                    this.strings.Add(this.result.ToString());
                }
                //Reset our "line" buffer
                this.result.Clear();
            }
            //Append the current text to our line buffer
            this.result.Append(renderInfo.GetText());
            //Reset the last used line
            this.lastBaseLine = curBaseline;
        }
        public string GetResultantText() {
            //One last time, see if there's anything left in the buffer
            if ((!String.IsNullOrWhiteSpace(this.result.ToString()))) {
                this.baselines.Add(this.lastBaseLine[Vector.I2]);
                this.strings.Add(this.result.ToString());
            }
            //We're not going to use this method to return a string, instead after callers should inspect this class's strings and baselines fields.
            return null;
        }
        //Not needed, part of interface contract
        public void BeginTextBlock() { }
        public void EndTextBlock() { }
        public void RenderImage(ImageRenderInfo renderInfo) { }
    }

称之为

        PdfReader reader = new PdfReader(workingFile);
        TextAsParagraphsExtractionStrategy S = new TextAsParagraphsExtractionStrategy();
        iTextSharp.text.pdf.parser.PdfTextExtractor.GetTextFromPage(reader, 1, S);
        for (int i = 0; i < S.strings.Count; i++) {
            Console.WriteLine("Line {0,-5}: {1}", S.baselines[i], S.strings[i]);
        }

实际上,我们放弃了GetTextFromPage中的值,而是检查工作人员的baselinesstrings数组字段。这方面的下一步是比较基线,并尝试确定如何将行分组为段落。

我应该注意的是,并不是所有段落的间距都与单独的文本行不同。例如,如果你通过上面的代码运行下面创建的PDF,你会看到每一行文本都相距18点,无论这一行是否构成新的段落。如果你打开它在Acrobat中创建的PDF,除了每一行的第一个字母之外,你会发现你的眼睛甚至分不清换行符和段落换行符。

        using (FileStream fs = new FileStream(workingFile, FileMode.Create, FileAccess.Write, FileShare.None)) {
            using (Document doc = new Document(PageSize.LETTER)) {
                using (PdfWriter writer = PdfWriter.GetInstance(doc, fs)) {
                    doc.Open();
                    doc.Add(new Paragraph("Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas porttitor congue massa. Fusce posuere, magna sed pulvinar ultricies, purus lectus malesuada libero, sit amet commodo magna eros quis urna."));
                    doc.Add(new Paragraph("This"));
                    doc.Add(new Paragraph("Is"));
                    doc.Add(new Paragraph("A"));
                    doc.Add(new Paragraph("Test"));
                    doc.Close();
                }
            }
        }