【问题标题】:Parsing a PDF with no /Root object using PDFMiner使用 PDFMiner 解析没有 /Root 对象的 PDF
【发布时间】:2012-07-08 16:06:40
【问题描述】:

我正在尝试使用 PDFMiner python 绑定从大量 PDF 中提取文本。我编写的模块适用于许多 PDF,但对于一部分 PDF,我得到了这个有点神秘的错误:

ipython 堆栈跟踪:

/usr/lib/python2.7/dist-packages/pdfminer/pdfparser.pyc in set_parser(self, parser)
    331                 break
    332         else:
--> 333             raise PDFSyntaxError('No /Root object! - Is this really a PDF?')
    334         if self.catalog.get('Type') is not LITERAL_CATALOG:
    335             if STRICT:

PDFSyntaxError: No /Root object! - Is this really a PDF?

当然,我立即检查了这些 PDF 是否已损坏,但它们可以正常阅读。

尽管没有根对象,有没有办法阅读这些 PDF?我不太确定从这里去哪里。

非常感谢!

编辑:

我尝试使用 PyPDF 以获得一些差异诊断。堆栈跟踪如下:

In [50]: pdf = pyPdf.PdfFileReader(file(fail, "rb"))
---------------------------------------------------------------------------
PdfReadError                              Traceback (most recent call last)
/home/louist/Desktop/pdfs/indir/<ipython-input-50-b7171105c81f> in <module>()
----> 1 pdf = pyPdf.PdfFileReader(file(fail, "rb"))

/usr/lib/pymodules/python2.7/pyPdf/pdf.pyc in __init__(self, stream)
    372         self.flattenedPages = None
    373         self.resolvedObjects = {}
--> 374         self.read(stream)
    375         self.stream = stream
    376         self._override_encryption = False

/usr/lib/pymodules/python2.7/pyPdf/pdf.pyc in read(self, stream)
    708             line = self.readNextEndLine(stream)
    709         if line[:5] != "%%EOF":
--> 710             raise utils.PdfReadError, "EOF marker not found"
    711 
    712         # find startxref entry - the location of the xref table


PdfReadError: EOF marker not found

Quonux 建议 PDFMiner 在到达第一个 EOF 字符后停止解析。这似乎暗示了其他情况,但我非常无能为力。有什么想法吗?

【问题讨论】:

  • 也许 PDFMiner 在第一个 %%EOF 标签之后终止对根节点的搜索但之后该标签可能会出现更多节点,因此它不会找到它。另一个原因可能是文件被压缩了?
  • @Quonux,我将如何测试是否是这种情况?是否有强制 PDFMiner 在整个文档中搜索根节点的选项?关于压缩的可能性,有没有办法检查这个?如果文件被压缩了怎么办?
  • @Quonux,我使用 pypdf 添加了来自类似尝试的堆栈跟踪。这是否有助于缩小原因?
  • 也许解析器需要一个 %%EOF 标签,但没有找到……也许你可以用以下方法修复它: - 打开“不正确”的文件 - 以二进制模式写入/追加 “%%EOF \n" 在文件末尾 - 关闭它 - 尝试再次解析

标签: python pypdf pdf-parsing pdf-manipulation


【解决方案1】:

slate pdf 中的解决方案是使用 'rb' --> 读取二进制模式。

因为 slate pdf 依赖于 PDFMiner,我也有同样的问题,这应该可以解决你的问题。

fp = open('C:\Users\USER\workspace\slate_minner\document1.pdf','rb')
doc = slate.PDF(fp)
print doc

【讨论】:

  • 我试过这个选项并得到完全相同的错误代码:No /Root object! - Is this really a PDF?
【解决方案2】:

有趣的问题。我进行了某种研究:

解析pdf的函数(来自矿工源代码):

def set_parser(self, parser):
        "Set the document to use a given PDFParser object."
        if self._parser: return
        self._parser = parser
        # Retrieve the information of each header that was appended
        # (maybe multiple times) at the end of the document.
        self.xrefs = parser.read_xref()
        for xref in self.xrefs:
            trailer = xref.get_trailer()
            if not trailer: continue
            # If there's an encryption info, remember it.
            if 'Encrypt' in trailer:
                #assert not self.encryption
                self.encryption = (list_value(trailer['ID']),
                                   dict_value(trailer['Encrypt']))
            if 'Info' in trailer:
                self.info.append(dict_value(trailer['Info']))
            if 'Root' in trailer:
                #  Every PDF file must have exactly one /Root dictionary.
                self.catalog = dict_value(trailer['Root'])
                break
        else:
            raise PDFSyntaxError('No /Root object! - Is this really a PDF?')
        if self.catalog.get('Type') is not LITERAL_CATALOG:
            if STRICT:
                raise PDFSyntaxError('Catalog not found!')
        return

如果您对 EOF 有疑​​问,将引发另一个异常: '''另一个源函数'''

def load(self, parser, debug=0):
        while 1:
            try:
                (pos, line) = parser.nextline()
                if not line.strip(): continue
            except PSEOF:
                raise PDFNoValidXRef('Unexpected EOF - file corrupted?')
            if not line:
                raise PDFNoValidXRef('Premature eof: %r' % parser)
            if line.startswith('trailer'):
                parser.seek(pos)
                break
            f = line.strip().split(' ')
            if len(f) != 2:
                raise PDFNoValidXRef('Trailer not found: %r: line=%r' % (parser, line))
            try:
                (start, nobjs) = map(long, f)
            except ValueError:
                raise PDFNoValidXRef('Invalid line: %r: line=%r' % (parser, line))
            for objid in xrange(start, start+nobjs):
                try:
                    (_, line) = parser.nextline()
                except PSEOF:
                    raise PDFNoValidXRef('Unexpected EOF - file corrupted?')
                f = line.strip().split(' ')
                if len(f) != 3:
                    raise PDFNoValidXRef('Invalid XRef format: %r, line=%r' % (parser, line))
                (pos, genno, use) = f
                if use != 'n': continue
                self.offsets[objid] = (int(genno), long(pos))
        if 1 <= debug:
            print >>sys.stderr, 'xref objects:', self.offsets
        self.load_trailer(parser)
        return

来自 wiki(pdf 规格): PDF 文件主要由对象组成,其中有八种类型:

Boolean values, representing true or false
Numbers
Strings
Names
Arrays, ordered collections of objects
Dictionaries, collections of objects indexed by Names
Streams, usually containing large amounts of data
The null object

对象可以是直接的(嵌入到另一个对象中)或间接的。间接对象​​用对象编号和世代编号编号。 称为外部参照表的索引表给出了每个间接对象相对于文件开头的字节偏移量。这种设计允许对文件中的对象进行有效的随机访问,并且还允许在不重写整个文件的情况下进行小的更改(增量更新)。从 PDF 版本 1.5 开始,间接对象也可能位于称为对象流的特殊流中。这种技术可以减少包含大量小型间接对象的文件的大小,对于标记的 PDF 尤其有用。

我认为问题是您的“损坏的 pdf”在页面上有一些“根元素”。

Possible solution:

您可以在检索到外部参照对象以及解析器尝试解析这些对象的每个位置下载源代码并编写“打印函数”。将有可能确定完整的错误堆栈(在出现此错误之前)。

ps:我认为这是产品中的某种错误。

【讨论】:

  • 德米特里,感谢您的回复。如果我理解正确,您怀疑这是 PDFMiner 中的错误?我很惊讶,因为在 PyPDF 中也观察到了类似的行为。还是您的意思是该错误存在于创建“损坏” PDF 的任何软件中?关于您的解决方案,您的意思是我应该在 PDFParser 对象方法中添加打印行,无论它们管理外部参照对象吗?我有点不清楚我应该做什么。谢谢!
  • 美好的一天。只需获取两个文件(正常和损坏)并尝试在 pdf 分析器工具中分析每个文件。我认为在损坏的 pdf 中将是无效的外部参照结构。经过分析尝试修复pdf结构(w3.org/WAI/GL/WCAG20-TECHS/pdf.html)
  • pdflabs.com/tools/pdftk-the-pdf-toolkit 用于执行此类操作的出色工具集。
  • Dimitry,感谢您的链接。我会给 pdftk 一个机会。如果工作已经完成,为什么还要麻烦修复一些东西;-)
  • 如果您在python中制作pdf修复工具,请给我反馈=)
【解决方案3】:

我在 Ubuntu 中也遇到过同样的问题。我有一个非常简单的解决方案。只需将 pdf 文件打印为 pdf。如果你在 Ubuntu:

  1. 使用 (ubuntu) 文档查看器打开一个 pdf 文件。

  2. 转到文件

  3. 转到打印

  4. 选择打印为文件并勾选“pdf”

如果您想使该过程自动进行,请按照 this 为例,即使用此脚本自动打印您的所有 pdf 文件。像这样的 linux 脚本也可以工作:

for f in *.pdfx
do
lowriter --headless --convert-to pdf "$f"
done

请注意,我将原始(有问题的)pdf 文件称为 pdfx。

【讨论】:

    【解决方案4】:

    我也遇到了这个错误并继续尝试 fp = open('example','rb')

    但是,我仍然收到指示的错误 OP。我发现我的代码中有错误,PDF 仍然被另一个函数打开。
    因此,请确保您没有在其他地方的内存中打开 PDF。

    【讨论】:

      【解决方案5】:

      上面的答案是对的。这个错误只出现在windows中,解决方法是更换 with open(path, 'rb') 至 fp = open(path,'rb')

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-09
        • 2014-05-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-07-16
        • 1970-01-01
        相关资源
        最近更新 更多