从PDF中的文本获取书目数据并导出到窗口表单

时间:2011-12-06 13:58:31

标签: c# pdf itext metadata bibliography

我使用iText5 for .NET通过使用下面的代码从PDF中提取文本。

private void button1_Click(object sender, EventArgs e)
{
  PdfReader reader2 = new PdfReader("Scharfetter1969.pdf");

  int pagen = reader2.NumberOfPages;
  reader2.Close();

  ITextExtractionStrategy its = new iTextSharp.text.pdf.parser.SimpleTextExtractionStrategy();
  for (int i = 1; i < 2; i++)
  {
    textBox1.Text = "";
    PdfReader reader = new PdfReader("Scharfetter1969.pdf");
    String s = PdfTextExtractor.GetTextFromPage(reader, i, its);
    s = Encoding.UTF8.GetString(ASCIIEncoding.Convert(Encoding.Default, Encoding.UTF8, Encoding.Default.GetBytes(s)));
    textBox1.Text = s;
    reader.Close();
  }
}

但我想从研究论文pdf中获取书目数据。

以下是从此pdf中提取的数据示例(尾注格式),这是a link

%0 Journal Article
%T Repeated temperature modulation epitaxy for p-type doping and light-emitting diode based on ZnO
%A Tsukazaki, A.
%A Ohtomo, A.
%A Onuma, T.
%A Ohtani, M.
%A Makino, T.
%A Sumiya, M.
%A Ohtani, K.
%A Chichibu, S.F.
%A Fuke, S.
%A Segawa, Y.
%J Nature Materials
%V 4
%N 1
%P 42-46
%@ 1476-1122
%D 2004
%I Nature Publishing Group

但请记住,这是书目信息,在此pdf的元数据中不可用。我想访问文章类型(%O),标题(%T),作者(%A),日期(%D)和(%I),并以窗口形式显示给不同的指定文本框。

我正在使用C#,如果任何人有任何代码,或指导我如何做到这一点。

1 个答案:

答案 0 :(得分:2)

PDF是一种单向格式。您将数据放入其中以便在所有设备(监视器,打印机等)上一致地呈现,但该格式从未打算将数据撤回。任何和所有这样做的尝试都是纯粹的猜测工作。 iText的PdfTextExtractor有效,但您必须根据自己的任意规则将事物拼凑在一起,这些规则可能会从PDF变为PDF。提供的PDF是由InDesign创建的,它可以很好地使文本看起来很好,实际上更难以解析数据。

也就是说,如果您的PDF在视觉上一致,您可以尝试在retaining formatting时拉出数据并使用格式规则来猜测是什么。该帖子将为您提供一些您可以猜到的HTML格式。 (如果这确实有效,我建议您返回比HTML更具体的内容,但我会将其留给您。)

根据提供的PDF运行它会显示标题在大约17磅处使用字体HelveticaNeue-LightExt,因此您可以编写规则来查找在该大小使用该字体的所有行并将它们组合在一起。作者在HelveticaNeue-Condensed大约10分钟完成,这是另一条规则。

以下代码是与上述链接的修改版本。它是一个完整的C#2010 WinForms应用程序,面向iTextSharp 5.1.1.0。它为所提供的PDF提取标题和作者,但您需要针对其他PDF和元数据进行调整。有关具体实现的详细信息,请参阅代码中的注释。

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using iTextSharp.text.pdf.parser;
using iTextSharp.text.pdf;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            PdfReader reader = new PdfReader(System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "nmat4-42.pdf"));
            TextWithFontExtractionStategy S = new TextWithFontExtractionStategy();
            string F = iTextSharp.text.pdf.parser.PdfTextExtractor.GetTextFromPage(reader, 1, S);

            //Buffers to hold various parts from the PDF
            List<string> titles = new List<string>();
            List<string> authors = new List<string>();

            //Array of lines of text
            string[] lines = F.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);

            //Temporary string
            string t;

            //Loop through each line in the array
            foreach (string line in lines)
            {
                //See if the line looks like a "title"
                if (line.Contains("HelveticaNeue-LightExt") && line.Contains("font-size:17.28003"))
                {
                    //Remove the HTML tags
                    titles.Add(System.Text.RegularExpressions.Regex.Replace(line, "</?span.*?>", "").Trim());
                }
                    //See if the line looks like an "author"
                else if (line.Contains("HelveticaNeue-Condensed") && line.Contains("font-size:9.995972"))
                {
                    //Remove the HTML tags and trim extra characters
                    t = System.Text.RegularExpressions.Regex.Replace(line, "</?span.*?>", "").Trim(new char[] { ' ', ',', '*' });
                    //Make sure we have a valid name, probably need some more exceptions here, too
                    if (!string.IsNullOrWhiteSpace(t) && t != "AND")
                    {
                        authors.Add(t);
                    }
                }
            }
            //Write out the title to the console
            Console.WriteLine("Title  : {0}", string.Join(" ", titles.ToArray()));
            //Write out each author
            foreach (string author in authors)
            {
                Console.WriteLine("Author : {0}", author);
            }
            Console.WriteLine(F);

            this.Close();
        }

        public class TextWithFontExtractionStategy : iTextSharp.text.pdf.parser.ITextExtractionStrategy
        {
            //HTML buffer
            private StringBuilder result = new StringBuilder();

            //Store last used properties
            private Vector lastBaseLine;
            private string lastFont;
            private float lastFontSize;

            //http://api.itextpdf.com/itext/com/itextpdf/text/pdf/parser/TextRenderInfo.html
            private enum TextRenderMode
            {
                FillText = 0,
                StrokeText = 1,
                FillThenStrokeText = 2,
                Invisible = 3,
                FillTextAndAddToPathForClipping = 4,
                StrokeTextAndAddToPathForClipping = 5,
                FillThenStrokeTextAndAddToPathForClipping = 6,
                AddTextToPaddForClipping = 7
            }



            public void RenderText(iTextSharp.text.pdf.parser.TextRenderInfo renderInfo)
            {
                string curFont = renderInfo.GetFont().PostscriptFontName;
                //Check if faux bold is used
                if ((renderInfo.GetTextRenderMode() == (int)TextRenderMode.FillThenStrokeText))
                {
                    curFont += "-Bold";
                }

                //This code assumes that if the baseline changes then we're on a newline
                Vector curBaseline = renderInfo.GetBaseline().GetStartPoint();
                Vector topRight = renderInfo.GetAscentLine().GetEndPoint();
                iTextSharp.text.Rectangle rect = new iTextSharp.text.Rectangle(curBaseline[Vector.I1], curBaseline[Vector.I2], topRight[Vector.I1], topRight[Vector.I2]);
                Single curFontSize = rect.Height;

                //See if something has changed, either the baseline, the font or the font size
                if ((this.lastBaseLine == null) || (curBaseline[Vector.I2] != lastBaseLine[Vector.I2]) || (curFontSize != lastFontSize) || (curFont != lastFont))
                {
                    //if we've put down at least one span tag close it
                    if ((this.lastBaseLine != null))
                    {
                        this.result.AppendLine("</span>");
                    }
                    //If the baseline has changed then insert a line break
                    if ((this.lastBaseLine != null) && curBaseline[Vector.I2] != lastBaseLine[Vector.I2])
                    {
                        this.result.AppendLine("<br />");
                    }
                    //Create an HTML tag with appropriate styles
                    this.result.AppendFormat("<span style=\"font-family:{0};font-size:{1}\">", curFont, curFontSize);
                }

                //Append the current text
                this.result.Append(renderInfo.GetText());

                //Set currently used properties
                this.lastBaseLine = curBaseline;
                this.lastFontSize = curFontSize;
                this.lastFont = curFont;
            }

            public string GetResultantText()
            {
                //If we wrote anything then we'll always have a missing closing tag so close it here
                if (result.Length > 0)
                {
                    result.Append("</span>");
                }
                return result.ToString();
            }

            //Not needed
            public void BeginTextBlock() { }
            public void EndTextBlock() { }
            public void RenderImage(ImageRenderInfo renderInfo) { }
        }
    }
}