From b2279eeb40fa0557e6e61e3bada0de0373e6da28 Mon Sep 17 00:00:00 2001 From: Martin Thoma Date: Mon, 11 Jul 2022 07:59:54 +0200 Subject: [PATCH 1/4] DOC: Watermark and stamp (#1095) See #307 --- docs/user/add-watermark.md | 87 +++++++++++++++++++++++--------------- 1 file changed, 53 insertions(+), 34 deletions(-) diff --git a/docs/user/add-watermark.md b/docs/user/add-watermark.md index a0eabccca..4e852ae07 100644 --- a/docs/user/add-watermark.md +++ b/docs/user/add-watermark.md @@ -10,28 +10,35 @@ content stays the same. ## Stamp (Overlay) ```python -from PyPDF2 import PdfWriter, PdfReader - - -def stamp(content_page, image_page): - """Put the image over the content""" - # Note that this modifies the content_page in-place! - content_page.merge_page(image_page) - return content_page - +from pathlib import Path +from typing import Union, Literal, List -# Read the pages -reader_content = PdfReader("content.pdf") -reader_image = PdfReader("image.pdf") +from PyPDF2 import PdfWriter, PdfReader -# Modify it -modified = stamp(reader_content.pages[0], reader_image.pages[0]) -# Create the new document -writer = PdfWriter() -writer.add_page(modified) -with open("out-stamp.pdf", "wb") as fp: - writer.write(fp) +def stamp( + content_pdf: Path, + stamp_pdf: Path, + pdf_result: Path, + page_indices: Union[Literal["ALL"], List[int]] = "ALL", +): + reader = PdfReader(stamp_pdf) + image_page = reader.pages[0] + + writer = PdfWriter() + + reader = PdfReader(content_pdf) + if page_indices == "ALL": + page_indices = list(range(0, len(reader.pages))) + for index in page_indices: + content_page = reader.pages[index] + mediabox = content_page.mediabox + content_page.merge_page(image_page) + content_page.mediabox = mediabox + writer.add_page(content_page) + + with open(pdf_result, "wb") as fp: + writer.write(fp) ``` ![stamp.png](stamp.png) @@ -39,28 +46,40 @@ with open("out-stamp.pdf", "wb") as fp: ## Watermark (Underlay) ```python +from pathlib import Path +from typing import Union, Literal, List + from PyPDF2 import PdfWriter, PdfReader -def watermark(content_page, image_page): - """Put the image under the content""" - # Note that this modifies the image_page in-place! - image_page.merge_page(content_page) - return image_page +def watermark( + content_pdf: Path, + stamp_pdf: Path, + pdf_result: Path, + page_indices: Union[Literal["ALL"], List[int]] = "ALL", +): + reader = PdfReader(content_pdf) + if page_indices == "ALL": + page_indices = list(range(0, len(reader.pages))) + + reader_stamp = PdfReader(stamp_pdf) + image_page = reader_stamp.pages[0] + writer = PdfWriter() + for index in page_indices: + content_page = reader.pages[index] + mediabox = content_page.mediabox -# Read the pages -reader_content = PdfReader("content.pdf") -reader_image = PdfReader("image.pdf") + # You need to load it again, as the last time it was overwritten + reader_stamp = PdfReader(stamp_pdf) + image_page = reader_stamp.pages[0] -# Modify it -modified = stamp(reader_content.pages[0], reader_image.pages[0]) + image_page.merge_page(content_page) + image_page.mediabox = mediabox + writer.add_page(image_page) -# Create the new document -writer = PdfWriter() -writer.add_page(modified) -with open("out-watermark.pdf", "wb") as fp: - writer.write(fp) + with open(pdf_result, "wb") as fp: + writer.write(fp) ``` ![watermark.png](watermark.png) From d7b64dc817a948b275f221d326746ee07130e2de Mon Sep 17 00:00:00 2001 From: Martin Thoma Date: Tue, 12 Jul 2022 07:47:11 +0200 Subject: [PATCH 2/4] MAINT: Use add_bookmark_destination in add_bookmark_dict (#1099) Re-use code See #1098 --- PyPDF2/_writer.py | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/PyPDF2/_writer.py b/PyPDF2/_writer.py index bde7d15e6..463aad764 100644 --- a/PyPDF2/_writer.py +++ b/PyPDF2/_writer.py @@ -1055,7 +1055,7 @@ def getNamedDestRoot(self) -> ArrayObject: # pragma: no cover return self.get_named_dest_root() def add_bookmark_destination( - self, dest: PageObject, parent: Optional[TreeObject] = None + self, dest: Union[PageObject, TreeObject], parent: Optional[TreeObject] = None ) -> IndirectObject: dest_ref = self._add_object(dest) @@ -1096,18 +1096,7 @@ def add_bookmark_dict( action_ref = self._add_object(action) bookmark_obj[NameObject("/A")] = action_ref - bookmark_ref = self._add_object(bookmark_obj) - - outline_ref = self.get_outline_root() - - if parent is None: - parent = outline_ref - - parent = parent.get_object() # type: ignore - assert parent is not None, "hint for mypy" - parent.add_child(bookmark_ref, self) - - return bookmark_ref + return self.add_bookmark_destination(bookmark_obj, parent) def addBookmarkDict( self, bookmark: BookmarkTypes, parent: Optional[TreeObject] = None From c420beb32c89f17822fb0e25d23db3f25ebd9af9 Mon Sep 17 00:00:00 2001 From: Martin Thoma Date: Tue, 12 Jul 2022 08:20:59 +0200 Subject: [PATCH 3/4] MAINT: Use add_bookmark_destination in add_bookmark (#1100) Reduce code duplication See #1098 --- PyPDF2/_writer.py | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/PyPDF2/_writer.py b/PyPDF2/_writer.py index 463aad764..a0b84bae0 100644 --- a/PyPDF2/_writer.py +++ b/PyPDF2/_writer.py @@ -1055,7 +1055,9 @@ def getNamedDestRoot(self) -> ArrayObject: # pragma: no cover return self.get_named_dest_root() def add_bookmark_destination( - self, dest: Union[PageObject, TreeObject], parent: Optional[TreeObject] = None + self, + dest: Union[PageObject, TreeObject], + parent: Union[None, TreeObject, IndirectObject] = None, ) -> IndirectObject: dest_ref = self._add_object(dest) @@ -1153,21 +1155,11 @@ def add_bookmark( } ) action_ref = self._add_object(action) - - outline_ref = self.get_outline_root() - - if parent is None: - parent = outline_ref - bookmark = _create_bookmark(action_ref, title, color, italic, bold) - bookmark_ref = self._add_object(bookmark) - - assert parent is not None, "hint for mypy" - parent_obj = cast(TreeObject, parent.get_object()) - parent_obj.add_child(bookmark_ref, self) - - return bookmark_ref + if parent is None: + parent = self.get_outline_root() + return self.add_bookmark_destination(bookmark, parent) def addBookmark( self, From d376d0e71939decbe21de8e93d016f09b3ce2210 Mon Sep 17 00:00:00 2001 From: Martin Thoma Date: Tue, 12 Jul 2022 09:33:40 +0200 Subject: [PATCH 4/4] STY: Simplify code (#1101) --- PyPDF2/_writer.py | 47 ++++++++++++++++++----------------------------- 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/PyPDF2/_writer.py b/PyPDF2/_writer.py index a0b84bae0..7a469407d 100644 --- a/PyPDF2/_writer.py +++ b/PyPDF2/_writer.py @@ -1059,14 +1059,11 @@ def add_bookmark_destination( dest: Union[PageObject, TreeObject], parent: Union[None, TreeObject, IndirectObject] = None, ) -> IndirectObject: - dest_ref = self._add_object(dest) - - outline_ref = self.get_outline_root() - if parent is None: - parent = outline_ref + parent = self.get_outline_root() parent = cast(TreeObject, parent.get_object()) + dest_ref = self._add_object(dest) parent.add_child(dest_ref, self) return dest_ref @@ -1137,24 +1134,21 @@ def add_bookmark( :meth:`addLink()` for details. """ page_ref = NumberObject(pagenum) - action = DictionaryObject() - zoom_args: ZoomArgsType = [] - for a in args: - if a is not None: - zoom_args.append(NumberObject(a)) - else: - zoom_args.append(NullObject()) + zoom_args: ZoomArgsType = [ + NullObject() if a is None else NumberObject(a) for a in args + ] dest = Destination( NameObject("/" + title + " bookmark"), page_ref, NameObject(fit), *zoom_args ) - dest_array = dest.dest_array - action.update( - { - NameObject(GoToActionArguments.D): dest_array, - NameObject(GoToActionArguments.S): NameObject("/GoTo"), - } + + action_ref = self._add_object( + DictionaryObject( + { + NameObject(GoToActionArguments.D): dest.dest_array, + NameObject(GoToActionArguments.S): NameObject("/GoTo"), + } + ) ) - action_ref = self._add_object(action) bookmark = _create_bookmark(action_ref, title, color, italic, bold) if parent is None: @@ -1537,26 +1531,21 @@ def add_link( else: rect = RectangleObject(rect) - zoom_args: ZoomArgsType = [] - for a in args: - if a is not None: - zoom_args.append(NumberObject(a)) - else: - zoom_args.append(NullObject()) + zoom_args: ZoomArgsType = [ + NullObject() if a is None else NumberObject(a) for a in args + ] dest = Destination( NameObject("/LinkName"), page_dest, NameObject(fit), *zoom_args ) # TODO: create a better name for the link - dest_array = dest.dest_array - lnk = DictionaryObject() - lnk.update( + lnk = DictionaryObject( { NameObject("/Type"): NameObject(PG.ANNOTS), NameObject("/Subtype"): NameObject("/Link"), NameObject("/P"): page_link, NameObject("/Rect"): rect, NameObject("/Border"): ArrayObject(border_arr), - NameObject("/Dest"): dest_array, + NameObject("/Dest"): dest.dest_array, } ) lnk_ref = self._add_object(lnk)