Skip to content

Commit 07848e5

Browse files
authored
MAINT: Make PdfFileMerger.addBookmark() behave life PdfFileWriters' (#339)
People stumbled over this inconsistency: * #40 * https://stackoverflow.com/a/42991101/562769 This was also tested with: https://stackoverflow.com/questions/42941742/pypdf2-nested-bookmarks-with-same-name-not-working/42991101#comment73249244_42991101
1 parent 6729b80 commit 07848e5

File tree

2 files changed

+53
-19
lines changed

2 files changed

+53
-19
lines changed

PyPDF2/merger.py

Lines changed: 52 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -498,36 +498,70 @@ def findBookmark(self, bookmark, root=None):
498498

499499
return None
500500

501-
def addBookmark(self, title, pagenum, parent=None):
501+
502+
def addBookmark(self, title, pagenum, parent=None, color=None, bold=False, italic=False, fit='/Fit', *args):
502503
"""
503504
Add a bookmark to this PDF file.
504505
505506
:param str title: Title to use for this bookmark.
506507
:param int pagenum: Page number this bookmark will point to.
507508
:param parent: A reference to a parent bookmark to create nested
508509
bookmarks.
510+
:param tuple color: Color of the bookmark as a red, green, blue tuple
511+
from 0.0 to 1.0
512+
:param bool bold: Bookmark is bold
513+
:param bool italic: Bookmark is italic
514+
:param str fit: The fit of the destination page. See
515+
:meth:`addLink()<addLin>` for details.
509516
"""
510-
if parent is None:
511-
iloc = [len(self.bookmarks)-1]
512-
elif isinstance(parent, list):
513-
iloc = parent
517+
if len(self.output.getObject(self.output._pages)['/Kids']) > 0:
518+
pageRef = self.output.getObject(self.output._pages)['/Kids'][pagenum]
514519
else:
515-
iloc = self.findBookmark(parent)
520+
pageRef = self.output.getObject(self.output._pages)
516521

517-
dest = Bookmark(TextStringObject(title), NumberObject(pagenum), NameObject('/FitH'), NumberObject(826))
522+
action = DictionaryObject()
523+
zoomArgs = []
524+
for a in args:
525+
if a is not None:
526+
zoomArgs.append(NumberObject(a))
527+
else:
528+
zoomArgs.append(NullObject())
529+
dest = Destination(NameObject("/"+title + " bookmark"), pageRef, NameObject(fit), *zoomArgs)
530+
destArray = dest.getDestArray()
531+
action.update({
532+
NameObject('/D') : destArray,
533+
NameObject('/S') : NameObject('/GoTo')
534+
})
535+
actionRef = self.output._addObject(action)
536+
537+
outlineRef = self.output.getOutlineRoot()
518538

519539
if parent is None:
520-
self.bookmarks.append(dest)
521-
else:
522-
bmparent = self.bookmarks
523-
for i in iloc[:-1]:
524-
bmparent = bmparent[i]
525-
npos = iloc[-1]+1
526-
if npos < len(bmparent) and isinstance(bmparent[npos], list):
527-
bmparent[npos].append(dest)
528-
else:
529-
bmparent.insert(npos, [dest])
530-
return dest
540+
parent = outlineRef
541+
542+
bookmark = TreeObject()
543+
544+
bookmark.update({
545+
NameObject('/A'): actionRef,
546+
NameObject('/Title'): createStringObject(title),
547+
})
548+
549+
if color is not None:
550+
bookmark.update({NameObject('/C'): ArrayObject([FloatObject(c) for c in color])})
551+
552+
format = 0
553+
if italic:
554+
format += 1
555+
if bold:
556+
format += 2
557+
if format:
558+
bookmark.update({NameObject('/F'): NumberObject(format)})
559+
560+
bookmarkRef = self.output._addObject(bookmark)
561+
parent = parent.getObject()
562+
parent.addChild(bookmarkRef, self.output)
563+
564+
return bookmarkRef
531565

532566
def addNamedDestination(self, title, pagenum):
533567
"""

Tests/test_merger.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ def test_merge():
5151
# Check if bookmarks are correct
5252
pdfr = PyPDF2.PdfFileReader(tmp_path)
5353
assert [el.title for el in pdfr.getOutlines() if isinstance(el, Destination)] == [
54+
"A bookmark",
5455
"Foo",
5556
"Bar",
5657
"Baz",
@@ -61,7 +62,6 @@ def test_merge():
6162
"Bar",
6263
"Baz",
6364
"True",
64-
"A bookmark",
6565
]
6666

6767
# Clean up

0 commit comments

Comments
 (0)