2012-09-10 12 views
8

Al momento sto cercando di fare un po 'di PDF che si fonde con pyPdf, ma a volte gli input non sono nel giusto ordine, quindi sto cercando di raschiare ogni pagina per il suo numero di pagina a determinare l'ordine in cui dovrebbe entrare (ad es. se qualcuno divide un libro in 20 PDF di 10 pagine e voglio rimetterli insieme).Recupera numeri di pagina dal documento con pyPDF

ho due domande - 1.) So che a volte il numero di pagina è memorizzato nei dati del documento da qualche parte, come ho visto i PDF che rendono su Adobe come qualcosa di simile [1243] (10 su 150), ma Ho letto documenti di questo tipo in pyPDF e non riesco a trovare alcuna informazione che indichi il numero di pagina - dove è memorizzato?

2.) Se viale # 1 non è disponibile, penso di poter scorrere gli oggetti su una determinata pagina per cercare di trovare un numero di pagina - probabilmente sarebbe il suo oggetto che ha un solo numero in esso . Tuttavia, non riesco a trovare alcun modo chiaro per determinare il contenuto degli oggetti. Se corro:

pdf.getPage(0).getContents() 

Questo di solito sia i rendimenti:

{'/Filter': '/FlateDecode'} 

oppure restituisce una lista di IndirectObject (num, num) oggetti. Non so davvero cosa fare con nessuno di questi e non c'è una vera documentazione su di esso per quanto posso dire. Qualcuno ha familiarità con questo tipo di cose che potrebbero indicarmi la giusta direzione?

risposta

6

Per la documentazione completa, consultare la pagina 978 di Adobe PDF Reference. :-)

In particolare, il file PDF contiene metadati che indicano come le pagine fisiche del PDF vengono mappate ai numeri di pagina logica e come devono essere formattati i numeri di pagina. Questo è dove vai per risultati canonici. L'Esempio 2 of this page mostra come appare nella marcatura PDF. Dovrai pescarlo, analizzarlo ed eseguire una mappatura.

In PyPDF, per arrivare a queste informazioni, provare, come punto di partenza:

pdf.trailer["/Root"]["/PageLabels"]["/Nums"] 

Tra l'altro, quando si vede un'istanza IndirectObject, è possibile chiamare il suo metodo getObject() per recuperare l'oggetto vero e proprio essere indicato.

L'alternativa è, come dici tu, per controllare gli oggetti di testo e cercare di capire qual è il numero di pagina. È possibile utilizzare extractText() dell'oggetto pagina per questo, ma si otterrà una stringa indietro e si dovrà provare a pescare il numero di pagina da quello. (E naturalmente il numero di pagina potrebbe essere romano o alfabetico anziché numerico, e alcune pagine potrebbero non essere numerate.) Invece, date un'occhiata a come extractText() fa effettivamente il suo lavoro - PyPDF è scritto in Python, dopotutto - e lo usa come base di una routine che controlla singolarmente ogni oggetto di testo sulla pagina per vedere se è come un numero di pagina. Fai attenzione alle pagine indice/indice che contengono molti numeri di pagina!

+0

Ho provato a leggere, ma inutile .......... Puoi dare un esempio di codice funzionante? – dreamer

15

Di seguito ha lavorato per me:

from PyPDF2 import PdfFileReader 
pdf = PdfFileReader(open('path/to/file.pdf','rb')) 
pdf.getNumPages() 
+1

Ho dovuto cambiare 'pypdf' in' pyPdf' e il tipo di lettura in 'rb'. –

+7

Ho anche notato che questo in realtà non risponde alla domanda che stava chiedendo, ma è stato proprio quello che stavo cercando. (Il numero di pagine in un pdf) –

+4

Ricordarsi di utilizzare l'istruzione with per evitare perdite di memoria .... con open ('path/to/file.pdf', 'rb') come pdf: PdfFileReader (pdf) .getNumPages() – Taran

2

La risposta da Kindall è molto buona. Tuttavia, poiché un campione di codice funzionante è stato richiesto più tardi (dal sognatore) e poiché oggi ho avuto lo stesso problema, vorrei aggiungere alcune note.

  1. la struttura del pdf non è uniforme; ci sono poche cose su cui puoi fare affidamento, quindi è molto improbabile che un campione di codice funzionante funzioni per tutti.Una buona spiegazione può essere trovata in this answer.

  2. Come spiegato da kindall, è molto probabile che sia necessario esplorare il pdf che si sta trattando.

Come così:

import sys 
import PyPDF2 as pyPdf 

"""Open your pdf""" 
pdf = pyPdf.PdfFileReader(open(sys.argv[1], "rb")) 

"""Explore the /PageLabels (if it exists)""" 

try: 
    page_label_type = pdf.trailer["/Root"]["/PageLabels"] 
    print(page_label_type) 
except: 
    print("No /PageLabel object") 

"""Select the item that is most likely to contain the information you desire; e.g. 
     {'/Nums': [0, IndirectObject(42, 0)]} 
    here, we only have "/Num". """ 

try: 
    page_label_type = pdf.trailer["/Root"]["/PageLabels"]["/Nums"] 
    print(page_label_type) 
except: 
    print("No /PageLabel object") 

"""If you see a list, like 
     [0, IndirectObject(42, 0)] 
    get the correct item from it""" 

try: 
    page_label_type = pdf.trailer["/Root"]["/PageLabels"]["/Nums"][1] 
    print(page_label_type) 
except: 
    print("No /PageLabel object") 

"""If you then have an indirect object, like 
     IndirectObject(42, 0) 
    use getObject()""" 

try: 
    page_label_type = pdf.trailer["/Root"]["/PageLabels"]["/Nums"][1].getObject() 
    print(page_label_type) 
except: 
    print("No /PageLabel object") 

"""Now we have e.g. 
     {'/S': '/r', '/St': 21} 
    meaning roman numerals, starting with page 21, i.e. xxi. We can now also obtain the two variables directly.""" 

try: 
    page_label_type = pdf.trailer["/Root"]["/PageLabels"]["/Nums"][1].getObject()["/S"] 
    print(page_label_type) 
    start_page = pdf.trailer["/Root"]["/PageLabels"]["/Nums"][1].getObject()["/St"] 
    print(start_page) 
except: 
    print("No /PageLabel object") 
  1. Come si può vedere dal PDF 1.7 specifica ISO (sezione relativa here) ci sono un sacco di possibilità di come etichettare pagine. Come semplice esempio di lavoro in considerazione questo script che sarà almeno affare con decimali (arabo) e con i numeri romani:

Script:

import sys 
import PyPDF2 as pyPdf 

def arabic_to_roman(arabic): 
    roman = '' 
    while arabic >= 1000: 
     roman += 'm' 
     arabic -= 1000 
    diffs = [900, 500, 400, 300, 200, 100, 90, 50, 40, 30, 20, 10, 9, 5, 4, 3, 2, 1] 
    digits = ['cm', 'd', 'cd', 'ccc', 'cc', 'c', 'xc', 'l', 'xl', 'xxx', 'xx', 'x', 'ix', 'v', 'iv', 'iii', 'ii', 'i'] 
    for i in range(len(diffs)): 
     if arabic >= diffs[i]: 
     roman += digits[i] 
     arabic -= diffs[i] 
    return(roman) 

def get_page_labels(pdf): 
    try: 
     page_label_type = pdf.trailer["/Root"]["/PageLabels"]["/Nums"][1].getObject()["/S"] 
    except: 
     page_label_type = "/D" 
    try: 
     page_start = pdf.trailer["/Root"]["/PageLabels"]["/Nums"][1].getObject()["/St"] 
    except: 
     page_start = 1 
    page_count = pdf.getNumPages() 
    ##or, if you feel fancy, do: 
    #page_count = pdf.trailer["/Root"]["/Pages"]["/Count"] 
    page_stop = page_start + page_count 

    if page_label_type == "/D": 
     page_numbers = list(range(page_start, page_stop)) 
     for i in range(len(page_numbers)): 
      page_numbers[i] = str(page_numbers[i]) 
    elif page_label_type == '/r': 
     page_numbers_arabic = range(page_start, page_stop) 
     page_numbers = [] 
     for i in range(len(page_numbers_arabic)): 
      page_numbers.append(arabic_to_roman(page_numbers_arabic[i])) 

    print(page_label_type) 
    print(page_start) 
    print(page_count) 
    print(page_numbers) 

pdf = pyPdf.PdfFileReader(open(sys.argv[1], "rb")) 
get_page_labels(pdf) 
1

Le altre risposte utilizzano PyPDF/PyPDF2 che sembra di leggere il intero file. Questo richiede molto tempo per i file di grandi dimensioni.

Nel frattempo ho scritto qualcosa di veloce e sporco che non ci mette quasi più tempo a correre. Fa una chiamata a shell ma non ero a conoscenza di nessun altro modo per farlo. Può ottenere il numero di pagine per i pdf che sono ~ 5000 pagine molto rapidamente.

Funziona semplicemente chiamando il comando di shell "pdfinfo", quindi probabilmente funziona solo in linux. L'ho provato solo su Ubuntu finora.

Uno strano comportamento che ho visto è che circonda questo in un tentativo/eccetto il blocco non rileva errori, devi escludere subprocess.CalledProcessError.

from subprocess import check_output 
def get_num_pages(pdf_path): 
    output = check_output(["pdfinfo", pdf_path]).decode() 
    pages_line = [line for line in output.splitlines() if "Pages:" in line][0] 
    num_pages = int(pages_line.split(":")[1]) 
    return num_pages 
+0

Appena realizzato che la domanda era specifica per Pypdf, ma questo è il primo risultato quando si cerca su Google come ottenere il numero di pagine in un pdf usando python, quindi questa risposta sarà rilevante per la maggior parte. –