'PyPDF2 nested bookmarks with same name not working

When you try and nest several bookmarks with the same name, PyPDF2 does not take it into account. Below self-contained python code to test what I mean (you need at have 3 pdf files named a, b and c in the working folder to test it out)

from PyPDF2 import PdfFileReader, PdfFileMerger


def main():
    merger = PdfFileMerger()
    first_one = True
    for file in ["a.pdf", "b.pdf", "c.pdf"]:
        print("next row")
        reader = PdfFileReader(file)
        merger.append(reader)
        if first_one:
            child = merger.addBookmark(title="blabla", pagenum=1)
            first_one = False
        else:
            child = merger.addBookmark(title="blabla", pagenum=1, parent=child)

    merger.write("test.pdf")


if __name__ == "__main__":
    main()

I would expect the resulting pdf to have three levels of nested bookmarks

blabla
    blabla
        blabla

but instead I get

blabla
    blabla
    blabla

Is there any way to make sure this does not happen ?

EDIT : I have removed the pagenum variable as I want those 3 bookmarks to point to the same page.



Solution 1:[1]

This seems to be a bug with PdfFileMerger.addBookmark() method. There is some detail here

Below is a work-around using PdfFileWriter and its addBookmark() method. Using this I can get 3 nested bookmarks, with same name, all on the same page:

blabla
    blabla
        blabla

Code using PdfFileWriter work-around:

from PyPDF2 import PdfFileReader, PdfFileWriter


def main():
    writer = PdfFileWriter()
    pagenum = 0
    first_one = True
    for file in ["a.pdf", "b.pdf", "c.pdf"]:
        print("next row")
        reader = PdfFileReader(file)
        writer.appendPagesFromReader(reader)
        if first_one:
            child = writer.addBookmark(
                title="blabla", pagenum=pagenum, parent=None
            )
            first_one = False
        else:
            child = writer.addBookmark(
                title="blabla", pagenum=pagenum, parent=child
            )

    with open("test.pdf", "wb") as d:
        writer.write(d)


if __name__ == "__main__":
    main()

Alternatively, I had a go at modifying the PyPDF2 library to resolve this issue, although I'm not very experienced at python so may have introduced new/other issues! Have submitted a pull-request to the maintainers, but until then you could clone my fork, and install PyPDF2 from there:

git clone https://github.com/khalida/PyPDF2.git
cd PyPDF2
python setup.py sdist
sudo -H pip uninstall -y PyPDF2
sudo -H pip install dist/PyPDF2-1.26.0.tar.gz

After that you should be able to get the nesting you want from PdfFileMerger.addBookmark(). I've tested it for the case above, but haven't done any testing beyond that.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Martin Thoma