Разбор PDF без объекта Root с использованием PDFMiner

Я пытаюсь извлечь текст из большого количества PDF файлов, используя привязки python PDFMiner. Модуль, который я написал, работает для многих 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 файлы, несмотря на отсутствие корневого объекта? Я не слишком уверен, куда идти отсюда.

Большое спасибо!

Edit:

Я попытался использовать 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. Казалось бы, это говорит иначе, но я очень невежественный. Любые мысли?

Ответ 1

интересная проблема. Я провел какое-то исследование:

которая анализировала 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 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

Объекты могут быть либо прямыми (встроенными в другой объект), либо косвенными. Косвенные объекты нумеруются с номером объекта и номером поколения. Таблица индексов, называемая таблицей xref, дает смещение байта каждого косвенного объекта с начала файла. Эта конструкция обеспечивает эффективный случайный доступ к объектам в файле, а также позволяет делать небольшие изменения без перезаписи всего файла (инкрементное обновление). Начиная с версии PDF 1.5, косвенные объекты также могут быть расположены в специальных потоках, известных как потоки объектов. Этот метод уменьшает размер файлов с большим количеством небольших косвенных объектов и особенно полезен для Tagged PDF.

i thk проблема заключается в том, что ваш "поврежденный pdf" имеет несколько "корневых элементов" на странице.

Possible solution:

вы можете загружать источники и записывать `print function 'в каждом месте, где были восстановлены объекты xref, и где парсер пытался проанализировать эти объекты. можно будет определить полный стек ошибки (до появления этой ошибки).

ps: Я думаю, что это какая-то ошибка в продукте.

Ответ 2

Решение в slate pdf используется в режиме 'rb' → read binary mode.

Поскольку slate pdf зависит от PDFMiner, и у меня такая же проблема, это должно решить вашу проблему.

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

Ответ 3

Правильный ответ. Эта ошибка появляется только в окнах, и обходной путь заключается в замене with open(path, 'rb') в fp = open(path,'rb')

Ответ 4

Я также получил эту ошибку и продолжал пробовать fp = open ('example', 'rb')

Тем не менее, я все еще получил сообщение об ошибке. Что я обнаружил, так это то, что в моем коде была ошибка, когда PDF все еще был открыт другой функцией.
Поэтому убедитесь, что у вас нет открытых файлов PDF в других местах.

Ответ 5

У меня была такая же проблема в Ubuntu. У меня очень простое решение. Просто распечатайте PDF файл в формате PDF. Если вы находитесь в Ubuntu:

  1. Откройте файл PDF с помощью (ubuntu) средства просмотра документов.

  2. Перейти к файлу

  3. Перейти к печати

  4. Выберите печать как файл и отметьте галочкой "pdf"

Если вы хотите сделать процесс автоматическим, следуйте, например, этому, то есть используйте этот скрипт для автоматической печати всех ваших PDF файлов. Подобный сценарий Linux также работает:

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

Обратите внимание, я назвал оригинальные (проблемные) PDF файлы как PDFX.