ScreenshotNeo

BlogHTML to image & PDF

How to Add an Image Watermark to a Generated PDF

Add a transparent image watermark to every PDF page with PyMuPDF or pypdf, control placement and opacity, and avoid common rotation and readability errors.

By the ScreenshotNeo team29 September 20269 min read

How to Add an Image Watermark to a Generated PDF

Direct answer: treat an image watermark as a background underlay, then place it on each page before saving a new PDF. PyMuPDF inserts the image directly into a page rectangle. pypdf converts the image into a one-page PDF and merges that page with over=False so it stays behind the document content. Use an overlay stamp when the image must sit above text. Check opacity, aspect ratio, page bounds, rotation, and clipping on representative pages before shipping.

1. Watermark or stamp: choose the layer first

The layer determines whether readers can still see the document. The pypdf documentation summarizes the distinction: “A stamp is adding something on top of the document, a watermark is in the background of the document.” A watermark is usually a low-opacity logo, classification mark, or ownership image under the content. A stamp is an approval mark, “COPY,” signature, or other element that must remain visible above the content.

A watermark underlay keeps the document content readable while adding a visible image mark.
A watermark underlay keeps the document content readable while adding a visible image mark.
Goal Layer Typical setting
Brand or classify a page while preserving text readability Underlay overlay=False in pypdf; overlay=False in PyMuPDF
Place an approval or review mark above content Overlay over=True in pypdf; overlay=True in PyMuPDF
Use a centered background image Underlay Scale to a rectangle inside the page bounds
Use a corner logo Usually overlay Place a smaller rectangle with a page-margin offset

Prepare the source image before writing code. A transparent PNG lets the page show through. If the image already contains transparency, inspect its alpha channel and avoid stacking additional opacity that makes it unreadable. Keep the intended aspect ratio; stretching a logo can make it look distorted.

2. PyMuPDF: insert the image directly

PyMuPDF’s documented approach opens the generated PDF, loops through pages, and calls insert_image with a rectangle. Passing overlay=False puts the image under existing page content. The full-page example below is useful for a background mark; replace the page rectangle with a smaller one for a corner or centered logo.

import pymupdf

input_path = "document.pdf"
watermark_path = "watermark.png"
output_path = "watermarked-document.pdf"

doc = pymupdf.open(input_path)
for page in doc:
    page.insert_image(
        page.bound(),
        filename=watermark_path,
        overlay=False,
    )

doc.save(output_path)
doc.close()

Install the library with pip install PyMuPDF. The page’s coordinate system and bounds matter. For a smaller centered image, calculate a rectangle from the page width and height instead of using page.bound() directly:

import pymupdf


def centered_rect(page, width_ratio=0.55, height_ratio=0.20):
    bounds = page.rect
    width = bounds.width * width_ratio
    height = bounds.height * height_ratio
    left = bounds.x0 + (bounds.width - width) / 2
    top = bounds.y0 + (bounds.height - height) / 2
    return pymupdf.Rect(left, top, left + width, top + height)


doc = pymupdf.open("document.pdf")
for page in doc:
    page.insert_image(
        centered_rect(page),
        filename="watermark.png",
        overlay=False,
    )
doc.save("watermarked-document.pdf")
doc.close()

For a corner logo, use a fixed margin and the image’s intended aspect ratio. Do not assume every page has the same size: PDFs can mix portrait and landscape pages. Derive the rectangle from each page’s page.rect, and clamp it to the page bounds to prevent clipping.

When the same image is inserted repeatedly, reference the image data once where your PyMuPDF version and workflow support it. The PyMuPDF guide recommends reuse to reduce memory and file-size overhead. Save to a new output path so the original generated document remains available for comparison or retry.

See the PyMuPDF image insertion documentation for the current method signature and rectangle behavior.

3. pypdf: convert the image, then merge it

pypdf merges PDF pages, so a raster image must first be represented as a PDF page. The official guide uses Pillow to create an in-memory PDF containing the image, reads that page, and merges it into each destination page. Use over=False for a background watermark.

from io import BytesIO
from PIL import Image
from pypdf import PdfReader, PdfWriter

# Convert the image file into a one-page PDF in memory.
image = Image.open("watermark.png").convert("RGBA")
watermark_pdf = BytesIO()
image.save(watermark_pdf, format="PDF")
watermark_pdf.seek(0)

stamp_page = PdfReader(watermark_pdf).pages[0]
reader = PdfReader("generated.pdf")
writer = PdfWriter()

for source_page in reader.pages:
    source_page.merge_page(stamp_page, over=False)
    writer.add_page(source_page)

with open("watermarked.pdf", "wb") as output:
    writer.write(output)

Install the dependencies with pip install pypdf Pillow. The converted page’s dimensions affect placement. If the image PDF is not the same size as the destination page, use a transformed merge to translate or scale it. The pypdf documentation describes transformation matrices for translation, rotation, and scaling.

Page rotation is a frequent source of surprises. A PDF may store a portrait page with a rotation value rather than physically rotating its content. If a watermark appears sideways or in an unexpected location, transfer the page rotation to its content before merging, then apply the merge. Also inspect the page’s media box and crop box; a rectangle based on one box can be clipped by another.

For an overlay stamp, change the call to source_page.merge_page(stamp_page, over=True). Keep the input image transparent if the underlying text must remain visible.

Read the current guidance in pypdf’s “Adding a Stamp or Watermark” documentation.

4. Apply the watermark to selected pages

Applying an image to every page is only one policy. You may need a cover-page logo, a classification mark on pages 2 through 5, or a watermark on odd pages. Both workflows can select pages by index:

for index, page in enumerate(doc):
    if index == 0 or index >= 2:
        page.insert_image(page.bound(), filename="watermark.png", overlay=False)

Remember that Python indexes pages from zero. If a user-facing requirement says “pages 2–5,” convert that to indexes 1–4 and validate the range against the actual page count. For hosted workflows, Adobe PDF Services documents an add-watermark operation with page selection and a source watermark PDF. Review its current REST or SDK examples when your application already uses that service; this research does not establish its price, performance, retention terms, or compatibility with your deployment.

5. Make the image readable without hiding content

  1. Use intentional opacity. Reduce opacity in the source PNG or in the image-generation step. A watermark that is technically behind text can still make text hard to read.
  2. Preserve aspect ratio. Calculate one dimension from the other, or use a rectangle that matches the image ratio. The PyMuPDF guide specifically calls out opacity and aspect ratio.
  3. Respect margins. Keep corner marks inside the page’s visible bounds and account for bleed when the PDF is printed.
  4. Check contrast. A light mark on a light page may disappear; a dark mark on dense text may dominate it.
  5. Inspect different page sizes. Test at least one portrait, one landscape, and one page with the most text or graphics.
Calculate placement from each page's bounds to avoid clipping and distortion across mixed page sizes.
Calculate placement from each page's bounds to avoid clipping and distortion across mixed page sizes.

6. Verification checklist

  • Open the output with a PDF viewer and inspect the first, middle, and last pages.
  • Confirm the image is behind text when an underlay is intended.
  • Check that no edge is clipped and that the logo is not stretched.
  • Check rotated pages separately.
  • Extract text from the output and confirm the watermark did not replace or remove content.
  • Compare file size when watermarking a long document; repeated embedded image data can increase output size.
  • Save to a new file and retain the original until validation succeeds.

7. Troubleshooting common failures

Symptom Likely cause Fix
Watermark covers text The image is opaque or merged as an overlay Use a transparent image, reduce opacity, and choose overlay=False or over=False.
Watermark is sideways Page rotation metadata was not normalized Transfer rotation to content before merging, then apply the watermark.
Only part of the image appears Image PDF and destination page boxes differ Scale and translate the watermark rectangle; inspect media and crop boxes.
Logo looks stretched Width and height were chosen independently Preserve the source aspect ratio when computing the rectangle.
Image is missing on some pages Page-selection logic or an off-by-one index Log the page count and selected zero-based indexes; test the first and last selected page.
Output file is unexpectedly large The same raster image was embedded repeatedly Reuse image references where supported, lower source resolution, and avoid unnecessarily large transparent canvases.
FileNotFoundError Input or watermark path is relative to another working directory Resolve absolute paths or print the current working directory before opening files.
PDF cannot be opened after writing Output was interrupted or the file handle was not closed Use a context manager, write to a temporary path, then replace the destination after success.

8. Performance, reliability, and cost considerations

Local libraries avoid an upload round trip and let you keep document data in your own process. Their main costs are CPU, memory, temporary storage, and operational maintenance. A full-page, high-resolution PNG repeated across hundreds of pages can dominate memory and output size, so choose the smallest image that meets the visual requirement and reuse it where possible.

A hosted API can simplify deployment and page selection when PDF processing is already centralized. Adobe’s documented operation is one option for teams already using Adobe PDF Services. The available research does not provide an apples-to-apples benchmark, current pricing comparison, retention guarantee, or universal compatibility claim, so measure those factors against your document and compliance requirements.

For either route, make the operation repeatable: write to a temporary output, validate that the page count is unchanged, and atomically move the result into place. If a job is retried, avoid applying the watermark twice by recording an output version or checking for an existing marker.

9. Or skip the browser setup

If your generated PDF starts as a web page and you need a reliable PDF capture before adding a watermark, ScreenshotNeo provides a single-request screenshot and PDF API. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.

Use the ScreenshotNeo API documentation for the current options. The basic request is:

cURL

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' }); const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo also supports PDF paper size, margins, landscape mode, page ranges, custom CSS and JavaScript, click and wait actions, blocking rules, custom headers and cookies, timezone and geolocation, caching with a chosen TTL, signed links, asynchronous jobs, webhooks, bulk capture, and a usage API. It provides 1,000 screenshots each month free with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account and capture your first 1,000 screenshots without a card.

10. FAQ

Should a watermark be above or below PDF text?

Usually below. Use an underlay for branding or classification and an overlay for an approval or review stamp that must remain prominent.

Can I watermark only one page?

Yes. Select pages by zero-based index in PyMuPDF or pypdf, and validate the selected range against the document’s page count.

Why does the watermark move on rotated pages?

Rotation may be stored as page metadata. Normalize or transfer the rotation to page content before merging, then calculate placement from the resulting page bounds.

Is a PNG required?

No. The examples use PNG because transparency is useful. Any image format supported by your library can work, provided its dimensions, color mode, and opacity are intentional.

Does adding a watermark prevent copying?

No. A watermark visually identifies or classifies a document; it is not a guarantee against extraction, editing, or redistribution.

Which approach should I start with?

Use PyMuPDF when direct image insertion and a local Python dependency fit your application. Use pypdf when your pipeline already manipulates PDF pages and needs merge transformations. Consider a hosted operation when deployment and document-handling requirements favor a service.