pypdf
| Language | Python 3.9+ |
|---|---|
| License | BSD-3-Clause |
| Repository | github.com/py-pdf/pypdf |
| Documentation | pypdf.readthedocs.io |
| Depends on | nothing required; cryptography for AES encryption, Pillow for image extraction |
| Read / write | yes / yes: text extraction, merge, split, rotate, crop, forms, annotations, metadata, encryption, attachments |
pypdf (the continuation of PyPDF2, which merged back into it) reads and writes PDF files in pure Python. It’s the standard tool for structural operations: merging and splitting, rotating and cropping pages, filling AcroForms, adding and removing metadata, attachments, outlines and annotations, encrypting with AES-256 and decrypting. Text extraction is good for simple documents and has a layout mode that keeps columns aligned.
pypdf doesn’t render pages and isn’t meant for creating documents from scratch (use ReportLab or fpdf2). It’s slower than PyMuPDF or pikepdf on big files, but installs everywhere.
Installation#
pip install pypdf
# with AES support for encrypted files
pip install "pypdf[crypto]"
Basic usage: extract text from a PDF#
from pypdf import PdfReader
reader = PdfReader("in.pdf")
print(len(reader.pages), "pages")
for number, page in enumerate(reader.pages, start=1):
print(f"--- page {number} ---")
print(page.extract_text())
page.extract_text(extraction_mode="layout") keeps the horizontal layout (columns, tables) using spaces.
How to merge PDF files#
from pypdf import PdfWriter
writer = PdfWriter()
for path in ["a.pdf", "b.pdf"]:
writer.append(path)
writer.write("merged.pdf")
writer.close()
writer.append("c.pdf", pages=(0, 2)) appends only the first 2 pages (a range-style tuple, 0-based).
How to split a PDF into pages#
from pypdf import PdfReader, PdfWriter
reader = PdfReader("in.pdf")
for i, page in enumerate(reader.pages):
writer = PdfWriter()
writer.add_page(page)
writer.write(f"page-{i + 1}.pdf")
How to rotate pages#
from pypdf import PdfWriter
writer = PdfWriter(clone_from="in.pdf")
for page in writer.pages:
page.rotate(90) # clockwise, multiple of 90
writer.write("rotated.pdf")
How to fill a PDF form#
from pypdf import PdfReader, PdfWriter
reader = PdfReader("form.pdf")
print(reader.get_fields().keys()) # all fields
print(reader.get_form_text_fields()) # text fields and their values
writer = PdfWriter(clone_from="form.pdf")
writer.update_page_form_field_values(writer.pages[0], {"name": "Jane Doe"}, auto_regenerate=False)
writer.write("filled.pdf")
How to read and change PDF metadata#
from pypdf import PdfReader, PdfWriter
reader = PdfReader("in.pdf")
meta = reader.metadata
print(meta.title, meta.author, meta.producer, meta.creation_date)
print(reader.pdf_header) # e.g. %PDF-1.7
writer = PdfWriter(clone_from="in.pdf")
writer.add_metadata({"/Title": "New title", "/Author": "Jane"})
writer.write("out.pdf")
How to encrypt and decrypt a PDF#
from pypdf import PdfReader, PdfWriter
writer = PdfWriter(clone_from="in.pdf")
writer.encrypt(user_password="user", owner_password="owner", algorithm="AES-256")
writer.write("encrypted.pdf")
reader = PdfReader("encrypted.pdf")
if reader.is_encrypted:
reader.decrypt("owner")
writer = PdfWriter(clone_from=reader)
writer.write("decrypted.pdf")
Tips#
page.imageslists embedded images;image.imageis a Pillow image andimage.datathe raw bytes.writer.compress_identical_objects()andpage.compress_content_streams()make merged files smaller.PdfReader(path, strict=False)(the default) tolerates many broken files and logs warnings instead of raising.