2009-12-16 6 views
5

je voudrais utiliser pyPdf pour diviser un fichier pdf basé sur le contour où chaque destination dans le plan se réfère à une page différente dans le pdf.diviser un pdf basé sur le contour

exemple les grandes lignes:

 
main  --> points to page 1 
    sect1 --> points to page 1 
    sect2 --> points to page 15 
    sect3 --> points to page 22 

il est facile au sein pypdf itérer sur chaque page du document ou de chaque destination dans les grandes lignes du document; Cependant, je ne peux pas comprendre comment obtenir le numéro de la page où les points de destination.

Est-ce que quelqu'un sait comment trouver le numéro de page de référence pour chaque destination dans le contour?

Répondre

6

i figured it out:

 
    class Darrell(pyPdf.PdfFileReader): 

     def getDestinationPageNumbers(self): 
      def _setup_outline_page_ids(outline, _result=None): 
       if _result is None: 
        _result = {} 
       for obj in outline: 
        if isinstance(obj, pyPdf.pdf.Destination): 
         _result[(id(obj), obj.title)] = obj.page.idnum 
        elif isinstance(obj, list): 
         _setup_outline_page_ids(obj, _result) 
       return _result 

      def _setup_page_id_to_num(pages=None, _result=None, _num_pages=None): 
       if _result is None: 
        _result = {} 
       if pages is None: 
        _num_pages = [] 
        pages = self.trailer["/Root"].getObject()["/Pages"].getObject() 
       t = pages["/Type"] 
       if t == "/Pages": 
        for page in pages["/Kids"]: 
         _result[page.idnum] = len(_num_pages) 
         _setup_page_id_to_num(page.getObject(), _result, _num_pages) 
       elif t == "/Page": 
        _num_pages.append(1) 
       return _result 

      outline_page_ids = _setup_outline_page_ids(self.getOutlines()) 
      page_id_to_page_numbers = _setup_page_id_to_num() 

      result = {} 
      for (_, title), page_idnum in outline_page_ids.iteritems(): 
       result[title] = page_id_to_page_numbers.get(page_idnum, '???') 
      return result 

    pdf = Darrell(open(PATH-TO-PDF, 'rb')) 
    template = '%-5s %s' 
    print template % ('page', 'title') 
    for p,t in sorted([(v,k) for k,v in pdf.getDestinationPageNumbers().iteritems()]): 
     print template % (p+1,t) 
1

classe de Darrell peut être légèrement modifié pour produire une table à plusieurs niveaux de contenu pour un pdf

(à la manière d'pdftoc dans la boîte à outils pdftk.) Ma modification ajoute un paramètre supplémentaire à _setup_page_id_to_num, un entier "level" qui vaut 1. Chaque invocation incrémente le niveau. Au lieu de stocker uniquement le numéro de page dans le résultat, nous stockons la paire de numéro de page et le niveau. Des modifications appropriées doivent être appliquées lors de l'utilisation du résultat renvoyé. J'utilise ceci pour implémenter la visionneuse de document page par page "PDF Hacks" basée sur un navigateur avec une table des matières de la barre latérale qui reflète la section LaTeX, la sous-section etc bookmarks. Je travaille sur un système partagé où pdftk ne peut pas être installé mais où python est disponible.

0

C'est exactement ce que je cherchais. Les ajouts de Darrell à PdfFileReader devraient faire partie de PyPDF2.

J'ai écrit une petite recette qui utilise PyPDF2 et sejda-console pour diviser un PDF par des signets. Dans mon cas, il y a plusieurs sections de niveau 1 que je veux garder ensemble. Ce script me permet de faire cela et donne aux fichiers résultants des noms significatifs.

import operator 
import os 
import subprocess 
import sys 
import time 

import PyPDF2 as pyPdf 

# need to have sejda-console installed 
# change this to point to your installation 
sejda = 'C:\\sejda-console-1.0.0.M2\\bin\\sejda-console.bat' 

class Darrell(pyPdf.PdfFileReader): 
    ... 

if __name__ == '__main__': 
    t0= time.time() 

    # get the name of the file to split as a command line arg 
    pdfname = sys.argv[1] 

    # open up the pdf 
    pdf = Darrell(open(pdfname, 'rb')) 

    # build list of (pagenumbers, newFileNames) 
    splitlist = [(1,'FrontMatter')] # Customize name of first section 

    template = '%-5s %s' 
    print template % ('Page', 'Title') 
    print '-'*72 
    for t,p in sorted(pdf.getDestinationPageNumbers().iteritems(), 
         key=operator.itemgetter(1)): 

     # Customize this to get it to split where you want 
     if t.startswith('Chapter') or \ 
      t.startswith('Preface') or \ 
      t.startswith('References'): 

      print template % (p+1, t) 

      # this customizes how files are renamed 
      new = t.replace('Chapter ', 'Chapter')\ 
        .replace(': ', '-')\ 
        .replace(': ', '-')\ 
        .replace(' ', '_') 
      splitlist.append((p+1, new)) 

    # call sejda tools and split document 
    call = sejda 
    call += ' splitbypages' 
    call += ' -f "%s"'%pdfname 
    call += ' -o ./' 
    call += ' -n ' 
    call += ' '.join([str(p) for p,t in splitlist[1:]]) 
    print '\n', call 
    subprocess.call(call) 
    print '\nsejda-console has completed.\n\n' 

    # rename the split files 
    for p,t in splitlist: 
     old ='./%i_'%p + pdfname 
     new = './' + t + '.pdf' 
     print 'renaming "%s"\n  to "%s"...'%(old, new), 

     try: 
      os.remove(new) 
     except OSError: 
      pass 

     try: 
      os.rename(old, new) 
      print' succeeded.\n' 
     except: 
      print' failed.\n' 

    print '\ndone. Spliting took %.2f seconds'%(time.time() - t0) 
0

Petite mise à jour @darrell classe pour pouvoir analyser UTF-8 lignes, que je poste en réponse, car commentaire serait difficile à lire.

problème est pyPdf.pdf.Destination.title qui peut être retourné en deux saveurs:

  • pyPdf.generic.TextStringObject
  • pyPdf.generic.ByteStringObject

de sorte que la sortie de _setup_outline_page_ids() fonction renvoie également deux types différents pour objet title, qui échoue avec UnicodeDecodeError si le titre du contour contient quelque chose alors ASCII.

J'ajouté ce code pour résoudre le problème:

if isinstance(title, pyPdf.generic.TextStringObject): 
    title = title.encode('utf-8') 

de classe entière:

class PdfOutline(pyPdf.PdfFileReader): 

    def getDestinationPageNumbers(self): 

     def _setup_outline_page_ids(outline, _result=None): 
      if _result is None: 
       _result = {} 
      for obj in outline: 
       if isinstance(obj, pyPdf.pdf.Destination): 
        _result[(id(obj), obj.title)] = obj.page.idnum 
       elif isinstance(obj, list): 
        _setup_outline_page_ids(obj, _result) 
      return _result 

     def _setup_page_id_to_num(pages=None, _result=None, _num_pages=None): 
      if _result is None: 
       _result = {} 
      if pages is None: 
       _num_pages = [] 
       pages = self.trailer["/Root"].getObject()["/Pages"].getObject() 
      t = pages["/Type"] 
      if t == "/Pages": 
       for page in pages["/Kids"]: 
        _result[page.idnum] = len(_num_pages) 
        _setup_page_id_to_num(page.getObject(), _result, _num_pages) 
      elif t == "/Page": 
       _num_pages.append(1) 
      return _result 

     outline_page_ids = _setup_outline_page_ids(self.getOutlines()) 
     page_id_to_page_numbers = _setup_page_id_to_num() 

     result = {} 
     for (_, title), page_idnum in outline_page_ids.iteritems(): 
      if isinstance(title, pyPdf.generic.TextStringObject): 
       title = title.encode('utf-8') 
      result[title] = page_id_to_page_numbers.get(page_idnum, '???') 
     return result 
Questions connexes