-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Description
Bug Report: merge_page() clips content when merging transformed pages
Environment
- Library: pypdf 6.1.1 (also affects PyPDF2)
- Python: 3.9
- OS: macOS (ARM64)
Description
When using merge_page()
to create a 2-up booklet layout by placing two transformed pages side-by-side on an A4 landscape page, the right-hand page gets clipped at the right edge even though mathematically the page should fit within the bounds.
Expected Behavior
Both left and right pages should be fully visible within the output page boundaries without any clipping.
Actual Behavior
The right-hand page is clipped/cut off on the right edge, despite:
- The calculated position ending exactly at the page boundary (841.9 pts)
- Both pages being scaled and positioned to fit within their allocated cells
- Using proper transformation chains (translate to origin → scale → translate to position)
Minimal Reproducible Example
import io
from pypdf import PdfReader, PdfWriter, Transformation
from pypdf._page import PageObject
from reportlab.lib.pagesizes import A4
def clone_page(page):
w = PdfWriter()
w.add_page(page)
buf = io.BytesIO()
w.write(buf)
buf.seek(0)
return PdfReader(buf).pages[0]
# Read input PDF
reader = PdfReader("input.pdf")
writer = PdfWriter()
# A4 landscape dimensions
a4_w, a4_h = A4[1], A4[0] # 841.9 x 595.3 pts
gutter = 28.35 # 10mm spacing
cell_w = (a4_w - gutter) / 2.0 # 406.8 pts per cell
# Create output page
output = PageObject.create_blank_page(width=a4_w, height=a4_h)
# Left page - works fine
left = clone_page(reader.pages[0])
lb = left.mediabox
lx0, ly0 = float(lb.lower_left[0]), float(lb.lower_left[1])
lw, lh = float(lb.width), float(lb.height)
scale_l = min(cell_w / lw, a4_h / lh)
lx = (cell_w - lw * scale_l) / 2.0
ly = (a4_h - lh * scale_l) / 2.0
t_left = (
Transformation()
.translate(tx=-lx0, ty=-ly0)
.scale(sx=scale_l, sy=scale_l)
.translate(tx=lx, ty=ly)
)
left.add_transformation(t_left)
output.merge_page(left)
# Right page - gets clipped on right edge
right = clone_page(reader.pages[1])
rb = right.mediabox
rx0, ry0 = float(rb.lower_left[0]), float(rb.lower_left[1])
rw, rh = float(rb.width), float(rb.height)
scale_r = min(cell_w / rw, a4_h / rh)
rx = cell_w + gutter + (cell_w - rw * scale_r) / 2.0
ry = (a4_h - rh * scale_r) / 2.0
t_right = (
Transformation()
.translate(tx=-rx0, ty=-ry0)
.scale(sx=scale_r, sy=scale_r)
.translate(tx=rx, ty=ry)
)
right.add_transformation(t_right)
output.merge_page(right) # This causes clipping
writer.add_page(output)
with open("output.pdf", "wb") as f:
writer.write(f)
Debug Information
Right page position: x=435.1
Right page width (scaled): 406.8
Right page ends at: 435.1 + 406.8 = 841.9 pts
Output page width: 841.9 pts
Mathematically, the right page should fit exactly, but visually it's clipped.
Workaround
Using PyMuPDF (fitz) with show_pdf_page()
works correctly and doesn't have this clipping issue:
import fitz
src = fitz.open("input.pdf")
dst = fitz.open()
out_page = dst.new_page(width=a4_w, height=a4_h)
# Place pages using show_pdf_page
out_page.show_pdf_page(
fitz.Rect(rx, ry, rx + rw_scaled, ry + rh_scaled),
src,
page_number
)
Additional Context
- Tried reducing scale factor to 95%, 90%, 85% - clipping persists
- Tried adding explicit cropbox to limit visible area - didn't help
- Tried different transformation approaches - same issue
- Left page never has clipping issues, only right page
- This affects real-world use case: creating print-ready booklets from PDFs
Files
I can provide:
- Sample input PDF
- Test script to reproduce
- Output PDFs showing the clipping
Question
Is this a known limitation of merge_page()
? Should there be a different approach for placing multiple transformed pages on a single output page?
Links:
- PyPDF2 is deprecated: https://github.com/py-pdf/PyPDF2
- pypdf (active fork): https://github.com/py-pdf/pypdf