对于某些pdf,ITextSharp / PDFBox文本提取失败

时间:2015-11-03 13:49:48

标签: itextsharp pdfbox text-extraction

以下代码在许多情况下通过ITextSharp正确地从PDF中提取文本。

                using (var pdfReader = new PdfReader(filename))
                {
                    ITextExtractionStrategy strategy = new SimpleTextExtractionStrategy();
                    var currentText = PdfTextExtractor.GetTextFromPage(
                        pdfReader,
                        1,
                        strategy);

                    currentText =
                        Encoding.UTF8.GetString(Encoding.Convert(
                            Encoding.Default,
                            Encoding.UTF8,
                            Encoding.Default.GetBytes(currentText)));

                    Console.WriteLine(currentText);
                }

但是,对于这个PDF,我得到以下内容而不是文本:" \ u0001 \ u0002 \ u0003 \ u0004 \ u0005 \ u0006 \ a \ b \ t \ a \ u0001 \ U0002 \ U0003 \ U0004 \ u0005 \ u0006 \ U0003"

我尝试了不同的编码甚至是PDFBox,但仍无法正确解码PDF。关于如何解决这个问题的任何想法?

2 个答案:

答案 0 :(得分:3)

尽管提取文本

@Bruno's answer是应该给出的答案,PDF显然没有根据PDF规范的 9.10文本内容提取部分提供允许正确文本提取所需的信息{ {3}} ...

但实际上还有一种有点邪恶的方式从手头的PDF中提取文本!

在以下类的实例中包含一个文本提取策略,将乱码文本替换为正确的文本:

public class RemappingExtractionFilter : ITextExtractionStrategy
{
    ITextExtractionStrategy strategy;
    System.Reflection.FieldInfo stringField;

    public RemappingExtractionFilter(ITextExtractionStrategy strategy)
    {
        this.strategy = strategy;
        this.stringField = typeof(TextRenderInfo).GetField("text", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
    }

    public void RenderText(TextRenderInfo renderInfo)
    {
        DocumentFont font =renderInfo.GetFont();
        PdfDictionary dict = font.FontDictionary;
        PdfDictionary encoding = dict.GetAsDict(PdfName.ENCODING);
        PdfArray diffs = encoding.GetAsArray(PdfName.DIFFERENCES);

        ;
        StringBuilder builder = new StringBuilder();
        foreach (byte b in renderInfo.PdfString.GetBytes())
        {
            PdfName name = diffs.GetAsName((char)b);
            String s = name.ToString().Substring(2);
            int i = Convert.ToInt32(s, 16);
            builder.Append((char)i);
        }

        stringField.SetValue(renderInfo, builder.ToString());
        strategy.RenderText(renderInfo);
    }

    public void BeginTextBlock()
    {
        strategy.BeginTextBlock();
    }

    public void EndTextBlock()
    {
        strategy.EndTextBlock();
    }

    public void RenderImage(ImageRenderInfo renderInfo)
    {
        strategy.RenderImage(renderInfo);
    }

    public String GetResultantText()
    {
        return strategy.GetResultantText();
    }
}

可以像这样使用:

ITextExtractionStrategy strategy = new RemappingExtractionFilter(new LocationTextExtractionStrategy());
string text = PdfTextExtractor.GetTextFromPage(pdfReader, page, strategy);

小心 ,我不得不使用System.Reflection来访问私人会员。某些环境可能会禁止此操作。

Java中的相同

我最初用Java编写了iText代码,因为这是我的主要开发环境。因此,这里是最初的Java版本:

public class RemappingExtractionFilter implements TextExtractionStrategy
{
    public RemappingExtractionFilter(TextExtractionStrategy strategy) throws NoSuchFieldException, SecurityException
    {
        this.strategy = strategy;
        this.stringField = TextRenderInfo.class.getDeclaredField("text");
        this.stringField.setAccessible(true);
    }

    @Override
    public void renderText(TextRenderInfo renderInfo)
    {
        DocumentFont font =renderInfo.getFont();
        PdfDictionary dict = font.getFontDictionary();
        PdfDictionary encoding = dict.getAsDict(PdfName.ENCODING);
        PdfArray diffs = encoding.getAsArray(PdfName.DIFFERENCES);

        ;
        StringBuilder builder = new StringBuilder();
        for (byte b : renderInfo.getPdfString().getBytes())
        {
            PdfName name = diffs.getAsName((char)b);
            String s = name.toString().substring(2);
            int i = Integer.parseUnsignedInt(s, 16);
            builder.append((char)i);
        }

        try
        {
            stringField.set(renderInfo, builder.toString());
        }
        catch (IllegalArgumentException | IllegalAccessException e)
        {
            e.printStackTrace();
        }
        strategy.renderText(renderInfo);
    }

    @Override
    public void beginTextBlock()
    {
        strategy.beginTextBlock();
    }

    @Override
    public void endTextBlock()
    {
        strategy.endTextBlock();
    }

    @Override
    public void renderImage(ImageRenderInfo renderInfo)
    {
        strategy.renderImage(renderInfo);
    }

    @Override
    public String getResultantText()
    {
        return strategy.getResultantText();
    }

    final TextExtractionStrategy strategy;
    final Field stringField;
}

ISO 32000-1

可以像这样使用:

String extractRemapped(PdfReader reader, int pageNo) throws IOException, NoSuchFieldException, SecurityException
{
    TextExtractionStrategy strategy = new RemappingExtractionFilter(new LocationTextExtractionStrategy());
    return PdfTextExtractor.getTextFromPage(reader, pageNo, strategy);
}

(来自RemappingExtractionFilter.java

为什么这样做?

首先,这不是所有提取问题的解决方案,仅仅是用于从OP提供的PDF中提取文本。

此方法有效,因为PDF在其字体中使用的名称'编码差异数组可以解释,即使它们不是标准的。这些名称构建为 / G xx ,其中 xx 是此名称所代表的字符的ASCII代码的十六进制表示。

答案 1 :(得分:1)

检查PDF 是否允许正确提取文本的一个很好的测试是在Adobe Reader中打开它并复制和粘贴文本。

例如:我复制了单词ABSTRACT,并将其粘贴在Notepad ++中:

enter image description here

你在Notepad ++中看到了单词ABSTRACT吗?不,您看到%& SOH'“%GS。A表示为%,B表示为&,依此类推。

这清楚地表明PDF的内容无法访问:使用的编码(%= A,& = B,...)与人类可以使用的实际字符之间没有映射理解。

简而言之:PDF不允许您提取文本,不使用iText,不使用iTextSharp,不使用PDFBox。您将不得不找到一个OCR工具并OCR整个文档。

有关详细信息,您可能需要观看以下视频: