Как получить номер страницы закладки

from typing import List
from PyPDF2 import PdfFileReader
from PyPDF2.generic import Destination


def get_outlines(pdf_filepath: str) -> List[Destination]:
    """Get the bookmarks of a PDF file."""
    with open(pdf_filepath, "rb") as fp:
        pdf_file_reader = PdfFileReader(fp)
        outlines = pdf_file_reader.getOutlines()
    return outlines


print(get_outlines("PDF-export-example.pdf"))

pyPdf.pdf.Destination имеет много свойств, но я не могу найти номер ссылающейся страницы этой закладки. Как я могу получить номер страницы закладок?


Например, outlines[1].page.idnum возвращает число, которое примерно в 3 раза больше, чем указанный номер страницы в документе PDF, который, как я предполагаю, ссылается на некоторый объект, меньший, чем страница, поскольку запуск .page.idnum для всего контура документа PDF возвращает массив чисел, который даже линейно не коррелирует с реальным назначения номера страницы в документе PDF, и он примерно кратен ~ 3


Обновление: этот вопрос такой же, как и этот: разделить PDF-файл на основе структуры, хотя Я не понимаю, что автор сделал в своем ответе там. Мне кажется слишком сложным, чтобы им можно было пользоваться


person theta    schedule 30.11.2011    source источник


Ответы (4)


Как указал @theta, "разделить PDF-файл на основе схемы" имеет код требуется для извлечения номеров страниц. Если вы чувствуете, что это сложно, я скопировал часть кода, который сопоставляет идентификаторы страниц с номерами страниц, и сделал это функцией. Вот рабочий пример, который печатает номер страницы закладки o[0]:

from PyPDF2 import PdfFileReader


def _setup_page_id_to_num(pdf, pages=None, _result=None, _num_pages=None):
    if _result is None:
        _result = {}
    if pages is None:
        _num_pages = []
        pages = pdf.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(pdf, page.getObject(), _result, _num_pages)
    elif t == "/Page":
        _num_pages.append(1)
    return _result
# main
f = open('document.pdf','rb')
p = PdfFileReader(f)
# map page ids to page numbers
pg_id_num_map = _setup_page_id_to_num(p)
o = p.getOutlines()
pg_num = pg_id_num_map[o[0].page.idnum] + 1
print(pg_num)

вероятно, слишком поздно для @theta, но может помочь другим :) кстати, мой первый пост в stackoverflow, так что извините, если я не следовал обычному формату

Чтобы расширить это: если вы хотите получить точное местоположение на странице для закладки, это облегчит вашу работу:

from PyPDF2 import PdfFileReader
import PyPDF2 as pyPdf

def _setup_page_id_to_num(pdf, pages=None, _result=None, _num_pages=None):
    if _result is None:
        _result = {}
    if pages is None:
        _num_pages = []
        pages = pdf.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(pdf, page.getObject(), _result, _num_pages)
    elif t == "/Page":
        _num_pages.append(1)
    return _result
def outlines_pg_zoom_info(outlines, pg_id_num_map, result=None):
    if result is None:
        result = dict()
    if type(outlines) == list:
        for outline in outlines:
            result = outlines_pg_zoom_info(outline, pg_id_num_map, result)
    elif type(outlines) == pyPdf.pdf.Destination:
        title = outlines['/Title']
        result[title.split()[0]] = dict(title=outlines['/Title'], top=outlines['/Top'], \
        left=outlines['/Left'], page=(pg_id_num_map[outlines.page.idnum]+1))
    return result

# main
pdf_name = 'document.pdf'
f = open(pdf_name,'rb')
pdf = PdfFileReader(f)
# map page ids to page numbers
pg_id_num_map = _setup_page_id_to_num(pdf)
outlines = pdf.getOutlines()
bookmarks_info = outlines_pg_zoom_info(outlines, pg_id_num_map)
print(bookmarks_info)

Примечание. Мои закладки — это номера разделов (например, 1.1 Введение), и я сопоставляю информацию о закладке с номером раздела. Если ваши закладки отличаются, измените эту часть кода:

    elif type(outlines) == pyPdf.pdf.Destination:
        title = outlines['/Title']
        result[title.split()[0]] = dict(title=outlines['/Title'], top=outlines['/Top'], \
        left=outlines['/Left'], page=(pg_id_num_map[outlines.page.idnum]+1))
person vjayky    schedule 12.02.2014
comment
Никогда не поздно :) Спасибо за скрипт, который я протестировал и обнаружил, что он работает так, как ожидалось. - person theta; 12.02.2014
comment
Нет проблем, я должен поблагодарить вас за ссылку на другой пост ;). Я расширил решение, чтобы также извлечь местоположение закладки на странице. - person vjayky; 12.02.2014
comment
Объект «NumberObject» не имеет атрибута «idnum», по какой причине я это получаю? Я использую ваш первый набор примеров и получаю сообщение об ошибке pg_num = pg_id_num_map[o[0].page.idnum] + 1 Я использую PyPDF2 - person Steve Byrne; 11.11.2015
comment
Я попробовал PyPDF2, и он отлично работает. Вы получаете эту ошибку для всех файлов PDF или только для некоторых? Вы смотрели на структуру «о», на что она похожа? - person vjayky; 11.11.2015

В 2019 году для тех, кто заинтересован в более быстром способе, можно использовать:

from PyPDF2 import PdfFileReader

def printPageNumberFrom(filename):
    with open(filename, "rb") as f:
       pdf = PdfFileReader(f)
       bookmarks = pdf.getOutlines()
       for b in bookmarks:
           print(pdf.getDestinationPageNumber(b) + 1) #page count starts from 0 
person Giulio Dell'Oro    schedule 03.09.2019

Управляйте закладками рекурсивно с vjayky и предложением Giulio D

PyPDF2 ›= v1.25

from PyPDF2 import PdfFileReader

def printBookmarksPageNumbers(pdf):
    def review_and_print_bookmarks(bookmarks, lvl=0):
        for b in bookmarks:
            if type(b) == list:
                review_and_print_bookmarks(b, lvl + 4)
                continue
            pg_num = pdf.getDestinationPageNumber(b) + 1 #page count starts from 0
            print("%s%s: Page %s" %(" "*lvl, b.title, pg_num))
    review_and_print_bookmarks(pdf.getOutlines())

with open('document.pdf', "rb") as f:
    pdf = PdfFileReader(f)
    printBookmarksPageNumbers(pdf)

PyPDF2 ‹ v1.25

from PyPDF2 import PdfFileReader

def printBookmarksPageNumbers(pdf):
    # Map page ids to page numbers
    pg_id_to_num = {}
    for pg_num in range(0, pdf.getNumPages()):
        pg_id_to_num[pdf.getPage(pg_num).indirectRef.idnum] = pg_num

    def review_and_print_bookmarks(bookmarks, lvl=0):
        for b in bookmarks:
            if type(b) == list:
                review_and_print_bookmarks(b, lvl + 4)
                continue
            pg_num = pg_id_to_num[b.page.idnum] + 1 #page count starts from 0 
            print("%s%s: Page %s" %(" "*lvl, b.title, pg_num))
    review_and_print_bookmarks(pdf.getOutlines())

with open('document.pdf', "rb") as f:
    pdf = PdfFileReader(f)
    printBookmarksPageNumbers(pdf)
person Diblo Dk    schedule 06.09.2020

Я не уверен, но согласно документам для pyPdf.Destination в http://pybrary.net/pyPdf/pythondoc-pyPdf.pdf.html#pyPdf.pdf.Destination.page-attribute номер страницы для закладки — просто Destination.page .

person Zachary Richey    schedule 30.11.2011
comment
.page или в моем примере o[1].page возвращает pyPdf.generic.IndirectObject, который не предоставляет дополнительных возвращаемых значений, кроме свойства .idnum, как указано выше. - person theta; 30.11.2011