Use PyMuPDF when you need to stamp the same logo, watermark, signature, badge, or product image onto every page of a PDF without touching each page by hand. It is fast, simple to script, and works well for both one-off documents and bulk processing jobs.
TLDR: Install PyMuPDF, open the PDF, loop through its pages, and insert the same image into a fixed rectangle on each page. For example, a legal assistant can add a “Confidential” PNG watermark to a 120-page contract in under 10 seconds instead of spending 20 minutes in a PDF editor. If your team processes 300 PDFs a month, even saving 3 minutes per file adds up to 15 hours back.
Adding the same image to every PDF page sounds like a tiny task until you actually have to do it. A company logo in the header. A paid stamp. A QR code. A scanned signature. A watermark that says Draft. Doing this manually is dull, error-prone, and oddly easy to mess up on page 37.
Python is a better fit. You write the placement rules once, run the script, and let the computer repeat the boring part perfectly.
Why PyMuPDF is a good choice
There are several Python libraries for PDF work, including pypdf, ReportLab, and PyMuPDF. For this task, PyMuPDF is usually the most direct option because it can open an existing PDF and insert an image onto each page with just a few lines of code.
It also gives you precise control over:
- Position: place the image in a header, footer, corner, or center.
- Size: scale the image to exact dimensions.
- Layering: put the image above or below existing PDF content.
- Page count: apply it to every page automatically.
Install it with:
pip install pymupdf
You will also need an image file, such as logo.png, watermark.png, or stamp.jpg. PNG is a strong choice if you need transparency.
Basic script: add one image to every page
Here is a complete example. It opens an existing PDF, inserts an image in the top-right corner of every page, and saves a new PDF.
import fitz # PyMuPDF
input_pdf = "input.pdf"
output_pdf = "output_with_image.pdf"
image_file = "logo.png"
doc = fitz.open(input_pdf)
for page in doc:
page_width = page.rect.width
image_width = 120
image_height = 60
margin = 36
x0 = page_width - image_width - margin
y0 = margin
x1 = x0 + image_width
y1 = y0 + image_height
rect = fitz.Rect(x0, y0, x1, y1)
page.insert_image(rect, filename=image_file)
doc.save(output_pdf)
doc.close()
This places the image 36 points from the top and right edges. PDF measurements use points, where 72 points equal one inch. So a margin of 36 points is half an inch.
The placement rectangle controls the final image size. If the source image is larger, PyMuPDF scales it to fit. If it is smaller, PyMuPDF scales it up. That can make tiny images look blurry, so start with a high-quality source file.
Understanding the placement rectangle
The most common source of annoyance is positioning. It drives me crazy that PDF coordinates feel simple until the image appears in the wrong corner. The fix is to think in four values:
x0: left edge of the image boxy0: top edge of the image boxx1: right edge of the image boxy1: bottom edge of the image box
For a top-left logo, use a small x0 and y0:
rect = fitz.Rect(36, 36, 156, 96)
For a footer image near the bottom center, calculate the position from the page width and height:
image_width = 180
image_height = 50
x0 = (page.rect.width - image_width) / 2
y0 = page.rect.height - image_height - 36
x1 = x0 + image_width
y1 = y0 + image_height
rect = fitz.Rect(x0, y0, x1, y1)
This is useful for page numbers, certification bars, approval stamps, or small brand strips at the bottom of each page.
Adding a watermark across the page
If you want a watermark, create a transparent PNG first. A pale gray “Draft” or “Confidential” image works well. Then place it in the middle of each page.
import fitz
doc = fitz.open("report.pdf")
image_file = "confidential.png"
for page in doc:
page_width = page.rect.width
page_height = page.rect.height
image_width = 350
image_height = 140
x0 = (page_width - image_width) / 2
y0 = (page_height - image_height) / 2
x1 = x0 + image_width
y1 = y0 + image_height
rect = fitz.Rect(x0, y0, x1, y1)
page.insert_image(rect, filename=image_file, overlay=True)
doc.save("report_watermarked.pdf")
doc.close()
The overlay=True setting places the image on top of the existing page content. Use overlay=False if you want it behind the text. The catch is that some PDFs have solid white backgrounds, so a behind-the-page watermark may disappear. If that happens, use a transparent, low-opacity PNG and place it on top.
Processing many PDFs at once
Once the single-file version works, batch processing is easy. The following script adds the same image to every PDF in a folder and writes the edited files to a separate output folder.
import fitz
from pathlib import Path
input_folder = Path("pdfs")
output_folder = Path("finished")
output_folder.mkdir(exist_ok=True)
image_file = "logo.png"
for pdf_path in input_folder.glob("*.pdf"):
doc = fitz.open(pdf_path)
for page in doc:
rect = fitz.Rect(36, 36, 156, 96)
page.insert_image(rect, filename=image_file)
output_path = output_folder / f"{pdf_path.stem}_stamped.pdf"
doc.save(output_path)
doc.close()
print(f"Saved {output_path}")
This is where Python really pays off. If each manual edit takes one minute and you have 500 documents, that is more than 8 hours of clicking. A batch script can often finish the same work during a coffee break.
Common problems and quick fixes
- The image looks blurry: use a higher-resolution source image and scale down, not up.
- The image covers text: move it to a margin area or use a transparent PNG.
- The image is too large: reduce the rectangle width and height in the script.
- The file size grows too much: compress the image before inserting it.
- The image is on the wrong page area: print
page.rect.widthandpage.rect.heightto inspect page size.
One practical tip: before running a batch job, test the script on a two-page sample PDF. Open the output, zoom in, and confirm the position. Expect to waste time on tiny placement tweaks if you skip this step.
When to use this approach
This technique is ideal for recurring document tasks. Finance teams can stamp invoice PDFs. Schools can add department logos to handouts. Agencies can brand client reports. Legal teams can mark drafts. Sellers can place QR codes on digital catalogs.
It is less ideal if every page needs a different image or custom placement. Python can still handle that, but you will need rules that map images to pages. For example, page 1 gets a cover badge, pages 2 through 8 get a watermark, and the final page gets a signature block.
A clean reusable function
For regular use, wrap the logic in a function:
import fitz
def add_image_to_pdf(input_pdf, output_pdf, image_file, rect):
doc = fitz.open(input_pdf)
for page in doc:
page.insert_image(rect, filename=image_file)
doc.save(output_pdf)
doc.close()
stamp_area = fitz.Rect(36, 36, 156, 96)
add_image_to_pdf(
"input.pdf",
"output.pdf",
"logo.png",
stamp_area
)
This keeps your script tidy. You can reuse it in internal tools, scheduled jobs, or small desktop utilities. You can also extend it with options for placement, opacity through prepared transparent images, batch folders, or different stamps for different document types.
The simple version is enough for most needs: open the PDF, loop through pages, insert the image, and save a new file. That small script can remove hours of repetitive editing from your week, and it does it without asking you to drag the same logo onto page after page like it is still 2009.
