【问题标题】:ITextSharp / PDFBox text extract fails for certain pdfs某些 pdf 的 ITextSharp / PDFBox 文本提取失败
【发布时间】:2015-11-03 13:49:47
【问题描述】:

以下代码在许多情况下通过 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。关于如何解决这个问题的任何想法?

【问题讨论】:

  • 虽然这可能无法解决您的问题,但请完全删除 currentText = Encoding...because it doesn't do what you think it does
  • 我已经阅读了您之前的工作,谢谢@ChrisHaas。我决定将它包含在这篇文章中,因为在之前的所有 IText 问题中,它通常作为第一个解决方案提供。真幸运,你真的看过这个问题!
  • 我不会回答所有问题(我确实有一份日常工作;))但我至少会阅读每一个问题!

标签: itextsharp pdfbox text-extraction


【解决方案1】:

仍然提取文本

@Bruno's answer 是应该在这里给出的答案,根据 PDF 规范 ISO 32000-19.10 文本内容提取 部分,PDF 显然没有提供允许正确提取文本所需的信息...

但实际上有一种稍微邪恶的方法可以从手头的 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;
}

(RemappingExtractionFilter.java)

可以这样使用:

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

(来自RemappedExtraction.java

为什么会这样?

首先,这并不是所有提取问题的解决方案,仅用于像 OP 所呈现的那样从 PDF 中提取文本。

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

【讨论】:

  • 我已经将代码翻译成 C# 并且正在测试它!谢谢你的回答,我不敢相信一旦你强调了这个问题就这么简单。
【解决方案2】:

确定 PDF 是否允许文本被正确提取的一个很好的测试是在 Adob​​e Reader 中打开它并复制和粘贴文本。

例如:我复制了 ABSTRACT 这个词,然后将其粘贴到 Notepad++ 中:

你在 Notepad++ 中看到了 ABSTRACT 这个词吗?不,你看到 %&SOH'"%GS。A 表示为 %,B 表示为 &,以此类推。

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

简而言之:PDF 不允许您提取文本,iText 不允许,iTextSharp 不允许,PDFBox 不允许。您必须找到一个 OCR 工具,然后对完整的文档进行 OCR。

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

【讨论】:

  • 稍后我会关注您的链接并观看视频,但是当您查看 PDF 属性时,它指出允许复制内容
  • 允许“内容复制”这一事实无关紧要。 (1.) 可以使用密码加密设置该权限,这只是心理上的;这种加密很容易被删除。 (2.) 内容本身会阻止您复制它。如果你不相信我,我可以详细说明。
  • 感谢您提供的信息,显然您知道自己的东西!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-28
  • 2015-12-03
  • 2012-12-30
相关资源
最近更新 更多