【问题标题】:how can i get text formatting with iTextSharp如何使用 iTextSharp 获取文本格式
【发布时间】:2011-10-16 11:05:08
【问题描述】:

我正在使用 iTextSharp 从 PDF 中读取文本内容。我也能读懂。但是我丢失了字体、颜色等文本格式。有没有办法也可以得到这种格式。

下面是我用来精确文本的代码段 -

PdfReader reader = new PdfReader("F:\\EBooks\\AspectsOfAjax.pdf");
textBox1.Text = ExtractTextFromPDFBytes(reader.GetPageContent(1));

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 "";
    }
}

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;
}

【问题讨论】:

    标签: c# .net itextsharp


    【解决方案1】:

    让我试着为你指出一个不同的方向。 iTextSharp 有一个非常漂亮和简单的文本提取系统,可以处理一些基本的标记。不幸的是,它不处理颜色信息,而是according to @Mark Storer it might not be too hard to implement yourself

    开始编辑

    我开始着手实现颜色信息。有关详细信息,请参阅my blog post here。 (抱歉格式不好,马上去吃饭。)

    结束编辑

    下面的代码结合了这里的几个问题和答案,包括this one to get the font height(虽然它不准确)以及另一个(对于我的一生,我似乎再也找不到了),它显示了如何检测虚假粗体。

    PostscriptFontName 在字体名称前返回一些额外的字符,我认为这与您嵌入字体子集时有关。

    下面是一个完整的 WinForms 应用程序,它以 iTextSharp 5.1.1.0 为目标并将文本提取为 HTML。

    示例 PDF 的屏幕截图

    提取为 HTML 的示例文本

    <span style="font-family:NJNSWD+Papyrus-Regular;font-size:11.61407">Hello </span>
    <span style="font-family:NJNSWD+Papyrus-Regular-Bold;font-size:11.61407">w</span>
    <span style="font-family:NJNSWD+Papyrus-Regular-Bold;font-size:37.87201">o</span>
    <span style="font-family:NJNSWD+Papyrus-Regular-Bold;font-size:11.61407">rl</span>
    <span style="font-family:NJNSWD+Papyrus-Regular;font-size:11.61407">d </span>
    <br />
    <span style="font-family:NJNSWD+Papyrus-Regular;font-size:11.61407">Test </span>
    

    代码

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Windows.Forms;
    using iTextSharp.text.pdf.parser;
    using iTextSharp.text.pdf;
    
    namespace WindowsFormsApplication2
    {
        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), "Document.pdf"));
                TextWithFontExtractionStategy S = new TextWithFontExtractionStategy();
                string F = iTextSharp.text.pdf.parser.PdfTextExtractor.GetTextFromPage(reader, 1, S);
                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) { }
            }
        }
    }
    

    【讨论】:

    • 感谢 Chris 提供宝贵的解决方案和有用的链接。我会尝试实现这一点。
    • 非常感谢克里斯,您的解决方案对我们帮助很大,但我们在一个地方感到震惊,我们如何才能找到文本是否有下划线?
    • 嗨@Deepesh,不幸的是,PDF 规范不支持原始文本级别的下划线。相反,它是通过以下两种方式之一完成的,要么通过注释(罕见),要么仅在某些文本下绘制一个非常薄的矩形(最常见)。在这两种情况下,您都必须计算文本相对于矩形的位置。虽然在技术上是可行的,但我认为这将是一个巨大的头痛。但是,如果您的文档是统一的,您也许可以编写一些任意规则。
    • 太棒了。谢谢你的新课。 :)
    • 好例子!但是,我正在尝试获取 PDF 文件中每个单词的坐标。有时它会尝试一次渲染几个单词,所以我无法获得每个单词的精确坐标。有什么想法可以做到这一点吗?
    【解决方案2】:

    如果有人在寻找它,我将@Chris 代码转换为 Java

    import com.itextpdf.text.Rectangle;
    import com.itextpdf.text.pdf.parser.ImageRenderInfo;
    import com.itextpdf.text.pdf.parser.TextExtractionStrategy;
    import com.itextpdf.text.pdf.parser.TextRenderInfo;
    import com.itextpdf.text.pdf.parser.Vector;
    
    public class TextWithFontExtractionStategy implements TextExtractionStrategy {
    //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);
    
        private int value;
    
        TextRenderMode(int value) {
            this.value = value;
        }
    
        public int getValue() {
            return value;
        }
    }
    
        public void renderText(TextRenderInfo renderInfo)
        {
            String curFont = renderInfo.getFont().getPostscriptFontName();
            //Check if faux bold is used
            if ((renderInfo.getTextRenderMode() == TextRenderMode.FillThenStrokeText.getValue()))
            {
                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();
            Rectangle rect = new Rectangle(curBaseline.get(Vector.I1), curBaseline.get(Vector.I2), topRight.get(Vector.I1), topRight.get(Vector.I2));
            float curFontSize = rect.getHeight();
    
            //See if something has changed, either the baseline, the font or the font size
            if ((this.lastBaseLine == null) || (curBaseline.get(Vector.I2) != lastBaseLine.get(Vector.I2)) || (curFontSize != lastFontSize) || (curFont != lastFont))
            {
                //if we've put down at least one span tag close it
                if ((this.lastBaseLine != null))
                {
                    this.result.append("</span>").append("\n");
                }
                //If the baseline has changed then insert a line break
                if ((this.lastBaseLine != null) && curBaseline.get(Vector.I2) != lastBaseLine.get(Vector.I2))
                {
                    this.result.append("<br />").append("\n");
                }
                //Create an HTML tag with appropriate styles
                this.result.append(String.format("<span style=\"font-family:{%s};font-size:{%s}\">", 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) { }
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-04-25
      • 2011-11-22
      • 2013-04-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-18
      相关资源
      最近更新 更多