Geoff McDonald
Geoff McDonald

Reputation: 65

Collapse Bookmarks using PyPDF2

When I use PyPDF2 to merge two PDF documents, I set the Page Mode to /UseOutlines so that the PDF will display the bookmark pane when the document is opened.

from PyPDF2 import PdfMerger, PdfReader

merger = PdfMerger()
merger.append(PdfReader(filename), import_bookmarks=True)
merger.setPageMode('/UseOutlines')
merger.setPageLayout('/SinglePage')

However, whenever the PDF document is opened the bookmarks are always expanded. Is there a property that I can modify to force the bookmarks to be collapsed when the document is opened?

Upvotes: 1

Views: 1914

Answers (6)

wainCam
wainCam

Reputation: 11

I have not seen any graphical pdf reader programs that complain about a zero value of /Count (they display the books marks as collapsed with this value), but to be fully PDF spec compliant one should instead set the bookmark /Count to -(number of entries) to make it display as compressed (this works as well in the graphical pdf readers I have checked).

Upvotes: 0

wainCam
wainCam

Reputation: 11

this is possible without changing the PyPDF2 source code:

from PyPDF2 import generic


def compress_picklist(writer, baseref=None):
    """sets /Count to zero to compress bookmark picklist"""
    parent = baseref
    if baseref is None:
        parent = writer.get_outline_root()
    parent = parent.get_object()
    parent[generic.NameObject("/Count")] = generic.NumberObject(0)


# call compress_picklist after every call to add_outline_item
pdf_writer.add_outline_item(item.title, n2, baseref)
compress_picklist(pdf_writer, baseref)

Upvotes: 1

Mark One
Mark One

Reputation: 41

I'm using pypdf==3.11.0.

The bookmarks (outline) tree cannot be collapsed by default using any parameter currently implemented. It is possible by changing:

pypdf/generic/_data_structures.py, Class TreeObject, def insert_child

From:

if "/Count" in parent:
    parent[NameObject("/Count")] = NumberObject(
        cast(int, parent[NameObject("/Count")]) + n
    )
    inc_parent_counter(parent.get("/Parent", None), n)

To:

if "/Count" in parent:
    parent[NameObject("/Count")] = NumberObject(0)
    inc_parent_counter(parent.get("/Parent", None), n)

Upvotes: 1

markgogo
markgogo

Reputation: 1

pymupdf can be used to collapse bookmarks:

...

doc = fitz.open("...pdf")
toc = doc.get_toc(False)
lvl1 = [(i, item[1]) for i, item in enumerate(toc) if item[0] == 1]

for i, title in lvl1:
    ...
    d['kind'] = 1
    d['collapse'] = True
    d['page'] = ...
    doc.set_toc_item(i, dest_dict=d)

# output pdf
doc.save(pdfname)

Upvotes: 0

Krux
Krux

Reputation: 41

Quite late but after a bit of digging and with the hint of @Eugene I found a solution.

You have to do small adjustments on the source code: (Tested for version 1.26.0)

PyPDF2/pdf.py:

Change the definition of the method addBookmark (~ line 690) to:

def addBookmark(self, title, pagenum, parent=None, color=None, bold=False, italic=False, fit='/Fit', collapse=False, *args):

(add the parameter collapse=False)

Then at the end of the same method change the line (~ line 750) to:

parent.addChild(bookmarkRef, self, collapse)

(add collapse)

PyPDF2/generic.py

Now we have to adjust the addChild method (~ line 665):

def addChild(self, child, pdf, collapse=False):

(again add the parameter collapse=False)

Then exchange the line (~ line 677) in the same method:

self[NameObject('/Count')] = NumberObject(self[NameObject('/Count')] + 1)

with

if collapse: self[NameObject('/Count')] = NumberObject(self[NameObject('/Count')] - 1)
else: self[NameObject('/Count')] = NumberObject(self[NameObject('/Count')] + 1)

That's it!

Usage

If you now call the method 'addBookmark()' with parameter 'collapse=True' all bookmarks are closed.

Upvotes: 4

Eugene
Eugene

Reputation: 2878

An open outline in PDF contains the /Count key in the dictionary indicating the number of children inside the outline. To display an outline as closed it should have this key removed or set to -1. But unfortunately no way to specify it in PyPDF2 yet.

Upvotes: 1

Related Questions