Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DOC: merge_transformed_page() #1647

Merged
merged 3 commits into from
Feb 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 20 additions & 19 deletions docs/user/add-watermark.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ Adding stamps or watermarks are two common ways to manipulate PDF files.
A stamp is adding something on top of the document, a watermark is in the
background of the document.

In both cases you might want to ensure that the mediabox/cropbox of the original
content stays the same.

## Stamp (Overlay)

Using the ``Transformation()`` class, one can translate, rotate, scale, etc. the stamp before merging it to the content page.

```python
from pathlib import Path
from typing import Union, Literal, List
Expand All @@ -22,8 +21,7 @@ def stamp(
pdf_result: Path,
page_indices: Union[Literal["ALL"], List[int]] = "ALL",
):
reader = PdfReader(stamp_pdf)
image_page = reader.pages[0]
stamp_page = PdfReader(stamp_pdf).pages[0]

writer = PdfWriter()

Expand All @@ -32,9 +30,10 @@ def stamp(
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
content_page.merge_transformed_page(
stamp_page,
Transformation(),
)
writer.add_page(content_page)

with open(pdf_result, "wb") as fp:
Expand All @@ -45,11 +44,15 @@ def stamp(

## Watermark (Underlay)

To merge the watermark *under* the content, use the argument ``over=False`` of the method ``merge_transformed_page()``.

Once again, watermark size and position (and more) can be customized using the ``Transformation()`` class.

```python
from pathlib import Path
from typing import Union, Literal, List

from pypdf import PdfWriter, PdfReader
from pypdf import PdfWriter, PdfReader, Transformation


def watermark(
Expand All @@ -60,20 +63,18 @@ def watermark(
):
reader = PdfReader(content_pdf)
if page_indices == "ALL":
page_indices = list(range(0, len(reader.pages)))
page_indices = range(len(reader.pages))

writer = PdfWriter()
watermark_page = PdfReader(stamp_pdf).pages[0]
for index in page_indices:
content_page = reader.pages[index]
mediabox = content_page.mediabox

# You need to load it again, as the last time it was overwritten
reader_stamp = PdfReader(stamp_pdf)
image_page = reader_stamp.pages[0]

image_page.merge_page(content_page)
image_page.mediabox = mediabox
writer.add_page(image_page)
content_page.merge_transformed_page(
watermark_page,
Transformation(),
over=False,
)
writer.add_page(content_page)

with open(pdf_result, "wb") as fp:
writer.write(fp)
Expand Down
83 changes: 83 additions & 0 deletions docs/user/cropping-and-transforming.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,3 +205,86 @@ In the mean time, you can add the following code to get the old behavior:
```python
pypdf._page.MERGE_CROP_BOX = "trimbox"
```

# Transforming several copies of the same page

We have designed the following business card (A8 format) to advertize our new startup.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hahaha, I love it 😄 ❤️


![](nup-source.png)

We would like to copy this card sixteen times on an A4 page, to print it, cut it, and give it to all our friends. Having learned about the ``merge_page()`` method and the ``Transformation`` class, we run the following code. Notice that we had to tweak the media box of the source page to extend it, which is already a dirty hack (in this case).

```python
from pypdf import PdfReader, PdfWriter, Transformation, PaperSize

# Read source file
reader = PdfReader("nup-source.pdf")
sourcepage = reader.pages[0]

# Create a destination file, and add a blank page to it
writer = PdfWriter()
destpage = writer.add_blank_page(width=PaperSize.A4.height, height=PaperSize.A4.width)

# Extend source page mediabox
sourcepage.mediabox = destpage.mediabox

# Copy source page to destination page, several times
for x in range(4):
for y in range(4):
# Translate page
sourcepage.add_transformation(
Transformation().translate(
x * PaperSize.A8.height,
y * PaperSize.A8.width,
)
)
# Merge translated page
destpage.merge_page(sourcepage)

# Write file
with open("nup-dest1.pdf", "wb") as fp:
writer.write(fp)
```

And the result is… unexpected.

![](nup-dest1.png)

The problem is that, having run ``add.transformation()`` several times on the *same* source page, those transformations add up: for instance, the sixteen transformations are applied to the last copy of the source page, so most of the business cards are *outside* the destination page.

We need a way to merge a transformed page, *without* modifying the source page. Here comes ``merge_transformed_page()``. With this method:
- we no longer need the media box hack of our first try;
- transformations are only applied *once*.

```python
from pypdf import PdfReader, PdfWriter, Transformation, PaperSize

# Read source file
reader = PdfReader("nup-source.pdf")
sourcepage = reader.pages[0]

# Create a destination file, and add a blank page to it
writer = PdfWriter()
destpage = writer.add_blank_page(width=PaperSize.A4.height, height=PaperSize.A4.width)

# Copy source page to destination page, several times
for x in range(4):
for y in range(4):
destpage.merge_transformed_page(
sourcepage,
Transformation().translate(
x * sourcepage.mediabox.width,
y * sourcepage.mediabox.height,
),
)

# Write file
with open("nup-dest2.pdf", "wb") as fp:
writer.write(fp)
```

We get the expected result.

![](nup-dest2.png)

There is still some work to do, for instance to insert margins between and around cards, but this is left as an exercise for the reader…
Binary file added docs/user/nup-dest1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/user/nup-dest2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/user/nup-source.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.