Python pypdf Metadata and Watermarks
python pypdf metadata and watermarks: Practical guide to reading and writing PDF metadata and adding watermarks with Python pypdf, including page alignment and error h...
When you need to handle PDF metadata and watermarks in Python, pypdf is the library most developers reach for. This article covers python pypdf metadata and watermarks: reading document information, updating it, and overlaying a watermark on every page. The examples use pypdf's reader and writer APIs, which are pure Python and work across platforms.
Reading PDF Metadata with pypdf
The PdfReader class exposes document-level metadata through the .metadata attribute. This attribute is a dictionary-like object that maps PDF Info keys to their values. Common keys include /Title, /Author, /Subject, /Creator, /Producer, and date fields. To read them:
from pypdf import PdfReader reader = PdfReader("input.pdf") meta = reader.metadata print(meta.title) # or meta["/Title"] print(meta.author) print(meta.subject)
The metadata object supports both attribute access and dictionary-style access with the leading slash. Not all keys are guaranteed to be present, so use .get() or check membership when you need a default value.
Writing and Updating Metadata
To write or update metadata, create a PdfWriter, copy pages from the reader, and call add_metadata(). The keys must include the leading slash:
from pypdf import PdfWriter writer = PdfWriter() writer.append_pages_from_reader(reader) writer.add_metadata({ "/Title": "Updated Title", "/Author": "Jane Doe", "/Subject": "Technical guide" }) with open("output.pdf", "wb") as f: writer.write(f)
If you want to preserve the original metadata, copy it from the reader before adding changes:
if reader.metadata: writer.add_metadata(reader.metadata) writer.add_metadata({"/Title": "New Title"})
The second call overrides the title while keeping other fields.
Adding a Watermark to Every Page
A common watermarking approach is to merge a single watermark page onto each page of the source document. The merge_page() method overlays the content of the watermark page onto the target page. Here is the basic pattern:
from pypdf import PdfReader, PdfWriter reader = PdfReader("input.pdf") watermark = PdfReader("watermark.pdf").pages[0] writer = PdfWriter() for page in reader.pages: page.merge_page(watermark) writer.add_page(page) with open("watermarked.pdf", "wb") as f: writer.write(f)
This places the watermark at the bottom-left corner of each page by default. If the watermark page is larger than the source page, it will be clipped. You will usually need to scale and position it.
Controlling Watermark Size and Position
To control where the watermark appears, use merge_transformed_page() with a Transformation. The transformation can scale, translate, and rotate the watermark. For example, to center a watermark that is half the page width:
from pypdf import Transformation watermark_page = PdfReader("watermark.pdf").pages[0] page_width = float(page.mediabox.width) page_height = float(page.mediabox.height) wm_width = float(watermark_page.mediabox.width) wm_height = float(watermark_page.mediabox.height) scale = min(page_width / wm_width, page_height / wm_height) * 0.5 watermark_page.scale_by(scale) tx = (page_width - wm_width * scale) / 2 ty = (page_height - wm_height * scale) / 2 transformation = Transformation().translate(tx, ty) page.merge_transformed_page(watermark_page, transformation)
This snippet scales the watermark to 50% of the page's smaller dimension and centers it. You can adjust the scale factor and translation values to place the watermark anywhere.
Handling Rotated Pages and Page Sizes
PDF pages can have a rotation attribute (0, 90, 180, 270). When you merge a watermark onto a rotated page, the watermark is applied in the page's coordinate space, so it may appear rotated relative to the viewer. To handle this, check page.rotation and apply a compensating rotation to the watermark:
if page.rotation: watermark_page.rotate_clockwise(-page.rotation)
Also, if the document contains pages of different sizes, you must recalculate the scale and translation for each page. The code in the previous section should be placed inside the page loop, using each page's own dimensions.
Performance and Memory Considerations
pypdf loads the entire PDF into memory when you create a PdfReader. The PdfWriter also accumulates all pages in memory. For large documents, this can consume significant RAM. If you are watermarking a huge file, consider processing it in chunks or using a library that supports streaming. In practice, for most business documents, the memory usage is acceptable. The merge_page() operation modifies the page object in place, so you do not create extra copies of the page content, but the watermark page itself is referenced by every page, which can increase memory if the watermark is complex.
Error Handling and Compatibility
pypdf raises PdfReadError when it encounters a malformed or encrypted PDF. For encrypted files, pass the password to the reader:
reader = PdfReader("encrypted.pdf", password="secret")
If the password is wrong, pypdf raises FileNotDecryptedError. Always wrap file operations in try/except blocks to handle missing files and permission errors. When writing metadata, remember that some PDF viewers ignore non-standard keys, and date fields should follow the PDF date format. Also, if you create a new writer, you must explicitly copy metadata from the reader; otherwise, the output PDF will have no metadata. Finally, watermarking does not alter the original file; you always write a new output file, which is the safest approach for preserving the source document.