2009-12-16 13 views
5

Vorrei utilizzare pyPdf per dividere un file pdf in base alla struttura in cui ogni destinazione nel contorno si riferisce a una pagina diversa all'interno del pdf.dividere un pdf in base al profilo

esempio contorno:

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

è facile all'interno pyPdf per iterare su ogni pagina del documento o ogni destinazione a grandi linee del documento; tuttavia, non riesco a capire come ottenere il numero di pagina in cui i punti di destinazione.

qualcuno sa come trovare il numero di pagina di riferimento per ciascuna destinazione nella struttura?

risposta

6

ho capito:

 
    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 di Darrell può essere modificato leggermente per produrre un tavolo multi-livello di contenuti per un pdf

(alla maniera di pdftoc nel toolkit pdftk.) La mia modifica aggiunge un altro parametro a _setup_page_id_to_num, un intero "livello" che assume come valore predefinito 1. Ogni invocazione incrementa il livello. Invece di memorizzare solo il numero di pagina nel risultato, memorizziamo la coppia di numero di pagina e il livello. Si dovrebbero applicare le opportune modifiche quando si utilizza il risultato restituito.

Lo sto utilizzando per implementare il visualizzatore di documenti "PDF Hacks" basato su browser con un indice della barra laterale che riflette i segnalibri LaTeX sezione, sottosezione, ecc. Sto lavorando su un sistema condiviso dove pdftk non può essere installato ma dove python è disponibile.

0

Questo è proprio quello che stavo cercando. Le aggiunte di Darrell a PdfFileReader dovrebbero far parte di PyPDF2.

Ho scritto una piccola ricetta che utilizza PyPDF2 e sejda-console per dividere un PDF da segnalibri. Nel mio caso ci sono diverse sezioni di livello 1 che voglio tenere insieme. Questo script mi ​​consente di farlo e dare ai file risultanti nomi significativi.

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

Piccolo aggiornamento per @darrell classe di essere in grado di analizzare UTF-8 contorni, che ho posto come risposta perché commento sarebbe difficile da leggere.

problema è in pyPdf.pdf.Destination.title che può essere restituito in due versioni:

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

modo che l'uscita dal _setup_outline_page_ids() funzione restituisce anche due tipi diversi per title oggetto, che non riesce con UnicodeDecodeError se il titolo del profilo contiene qualcosa allora ASCII.

ho aggiunto questo codice per risolvere il problema:

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

di tutta la classe:

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 
Problemi correlati