14 votes

Analyser un PDF sans objet /Root en utilisant PDFMiner

Je suis en train d'extraire du texte à partir d'un grand nombre de PDF en utilisant les liaisons python PDFMiner. Le module que j'ai écrit fonctionne pour de nombreux PDF, mais j'obtiens cette erreur quelque peu cryptique pour un sous-ensemble de PDF :

Traceback de l'interpréteur 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?

Évidemment, j'ai immédiatement vérifié si ces PDF étaient corrompus, mais ils peuvent être lus sans problème.

Existe-t-il un moyen de lire ces PDF malgré l'absence d'un objet racine ? Je ne suis pas sûr de savoir quoi faire à partir de là.

Merci beaucoup !

Édition :

J'ai essayé d'utiliser PyPDF dans le but d'obtenir des diagnostics différentiels. La trace de la pile est ci-dessous :

In [50]: pdf = pyPdf.PdfFileReader(file(fail, "rb"))
---------------------------------------------------------------------------
PdfReadError                              Traceback (most recent call last)
/home/louist/Desktop/pdfs/indir/ in ()
----> 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 a suggéré que peut-être PDFMiner s'est arrêté de parser après avoir atteint le premier caractère EOF. Cela semblerait suggérer le contraire, mais je suis vraiment perdu. Des idées ?

6voto

Carlos Neves Points 1

La solution en pdf ardoise est d'utiliser 'rb' --> mode binaire de lecture.

Parce que le pdf ardoise dépend de PDFMiner et j'ai le même problème, cela devrait résoudre votre problème.

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

5voto

Zagorulkin Dmitry Points 3630

Problème intéressant. j'avais effectué une sorte de recherche:

fonction qui analysait le pdf (du code source des mineurs):

def set_parser(self, parser):
        "Définit le document à utiliser un objet PDFParser donné."
        if self._parser: return
        self._parser = parser
        # Récupère les informations de chaque en-tête qui a été ajouté
        # (éventuellement plusieurs fois) à la fin du document.
        self.xrefs = parser.read_xref()
        for xref in self.xrefs:
            trailer = xref.get_trailer()
            if not trailer: continue
            # S'il y a des infos de chiffrement, mémorisez-les.
            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:
                #  Chaque fichier PDF doit avoir exactement un dictionnaire /Root.
                self.catalog = dict_value(trailer['Root'])
                break
        else:
            raise PDFSyntaxError('Aucun objet /Root! - S\'agit-il vraiment d\'un PDF?')
        if self.catalog.get('Type') is not LITERAL_CATALOG:
            if STRICT:
                raise PDFSyntaxError('Catalogue non trouvé!')
        return

si vous avez un problème avec EOF une autre exception sera levée: '''une autre fonction à partir de la source'''

def load(self, parser, debug=0):
        while 1:
            try:
                (pos, line) = parser.nextline()
                if not line.strip(): continue
            except PSEOF:
                raise PDFNoValidXRef('EOF inattendu - fichier corrompu?')
            if not line:
                raise PDFNoValidXRef('EOF prématuré: %r' % parser)
            if line.startswith('trailer'):
                parser.seek(pos)
                break
            f = line.strip().split(' ')
            if len(f) != 2:
                raise PDFNoValidXRef('Trailer non trouvé: %r: line=%r' % (parser, line))
            try:
                (start, nobjs) = map(long, f)
            except ValueError:
                raise PDFNoValidXRef('Ligne invalide: %r: line=%r' % (parser, line))
            for objid in xrange(start, start+nobjs):
                try:
                    (_, line) = parser.nextline()
                except PSEOF:
                    raise PDFNoValidXRef('EOF inattendu - fichier corrompu?')
                f = line.strip().split(' ')
                if len(f) != 3:
                    raise PDFNoValidXRef('Format XRef invalide: %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, 'objets xref:', self.offsets
        self.load_trailer(parser)
        return

de wiki(pdf specs): Un fichier PDF se compose principalement d'objets, parmi lesquels il existe huit types:

Valeurs booléennes, représentant vrai ou faux
Nombres
Chaînes de caractères
Noms
Tableaux, collections ordonnées d'objets
Dictionnaires, collections d'objets indexées par des noms
Flux, contenant généralement de grandes quantités de données
L'objet nul

Les objets peuvent être soit directs (incorporés dans un autre objet) soit indirects. Les objets indirects sont numérotés avec un numéro d'objet et un numéro de génération. Une table d'index appelée table xref donne le décalage en octets de chaque objet indirect depuis le début du fichier. Cette conception permet un accès aléatoire efficace aux objets du fichier, et permet également de petites modifications sans réécrire l'intégralité du fichier (mise à jour incrémentielle). À partir de la version 1.5 de PDF, les objets indirects peuvent également être situés dans des flux spéciaux appelés flux d'objet. Cette technique réduit la taille des fichiers qui ont un grand nombre de petits objets indirects et est particulièrement utile pour les PDF balisés.

Je pense que le problème est que votre "fichier PDF endommagé" contient quelques "éléments racine" sur la page.

Solution possible:

vous pouvez télécharger les sources et écrire la fonction `print' à chaque endroit où les objets xref sont récupérés et où le parseur a essayé de les analyser. il sera possible de déterminer la pile complète d'erreurs (avant que cette erreur ne se produise).

ps: je pense que c'est une sorte de bogue dans le produit.

1voto

DanielTheRocketMan Points 2886

J'ai eu le même problème sous Ubuntu. J'ai une solution très simple. Il suffit d'imprimer le fichier pdf en tant que pdf. Si vous êtes sous Ubuntu :

  1. Ouvrez un fichier pdf en utilisant le visualiseur de documents (ubuntu).

  2. Allez dans Fichier

  3. Allez dans Imprimer

  4. Choisissez Imprimer en tant que fichier et cochez la case "pdf"

Si vous voulez rendre le processus automatique, suivez par exemple ce lien, c'est-à-dire, utilisez ce script pour imprimer automatiquement tous vos fichiers pdf. Un script linux comme celui-ci fonctionne également :

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

Remarque : j'ai nommé les fichiers pdf d'origine (problématiques) pdfx.

0voto

dasvootz Points 246

J'ai également eu cette erreur et j'ai continué à essayer fp = open('example','rb')

Cependant, j'ai toujours eu l'erreur indiquée par l'OP. Ce que j'ai trouvé, c'est que j'avais un bogue dans mon code où le PDF était toujours ouvert par une autre fonction.
Assurez-vous donc que le PDF n'est pas ouvert en mémoire ailleurs également.

-1voto

AeroSM Points 21

Une réponse ci-dessus est correcte. Cette erreur n'apparaît que sous Windows, et la solution de contournement consiste à remplacer with open(path, 'rb') par fp = open(path,'rb')

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X