【问题标题】:How to get bookmark's page number如何获取书签的页码
【发布时间】:2011-11-30 16:52:25
【问题描述】:
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 返回的数字大约是 PDF 文档中引用的页码的 3 倍,我假设它引用了一些小于页面的对象,因为在整个 PDF 文档大纲上运行 .page.idnum 返回的数字数组甚至与 PDF 文档中的“真实”页码目标没有线性相关,它大约是 3 倍左右


更新:这个问题与此相同:split a pdf based on outline 虽然我不明白作者在那里的自我回答中做了什么。对我来说似乎太复杂而无法使用

【问题讨论】:

    标签: python pypdf2 pypdf


    【解决方案1】:

    正如@theta 指出的那样,“split a pdf based on outline”具有提取页码所需的代码。如果您觉得这很复杂,我复制了将页面 ID 映射到页码的部分代码,并使其成为一个函数。这是一个打印书签 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))
    

    【讨论】:

    • 永远不会太晚 :) 感谢我测试并发现它按预期工作的脚本。
    • 没问题,我应该感谢您提供指向其他帖子的链接;)。我将解决方案扩展为还提取页面上书签的位置。
    • 'NumberObject' 对象没有属性 'idnum',为什么我得到这个?我正在使用您的第一个示例集,并且在“pg_num = pg_id_num_map[o[0].page.idnum] + 1”上收到错误我正在使用 PyPDF2
    • 我试过 PyPDF2,它工作正常。您是否对所有 pdf 文件或仅部分文件都收到此错误?你看过'o'的结构吗,它是什么样子的?
    【解决方案2】:

    使用 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

    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)
    

    【讨论】:

      【解决方案3】:

      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 
      

      【讨论】:

        【解决方案4】:

        我不确定,但根据http://pybrary.net/pyPdf/pythondoc-pyPdf.pdf.html#pyPdf.pdf.Destination.page-attribute 的 pyPdf.Destination 文档,书签的页码只是 Destination.page 。

        【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-04-02
        • 2016-05-13
        • 2012-08-21
        • 1970-01-01
        • 2010-12-21
        • 1970-01-01
        相关资源
        最近更新 更多