mrkeyoor.com_
Sat 08 Aug 22:51 UTC
PyPIDataupdated 08 Aug 2026

PyPDF2

PyPDF2 is a pure-Python library for working with existing PDF files: reading pages, pulling out text and metadata, splitting and merging documents, rotating and scaling and stamping pages, editing form field values, and adding or removing passwords. It has no native dependencies, so it installs anywhere Python does. It is also over: the project renamed itself back to pypdf, 3.0.1 from December 2022 is the last PyPDF2 release, and importing the package emits a DeprecationWarning telling you to move. The repository link still resolves, but it now points at the pypdf project that replaced it.

Verdict

A capable pure-Python PDF toolkit that stopped at 3.0.1 in December 2022 and now warns you about itself on import. Keep it only to service existing code, and budget an afternoon to rename the import to pypdf, which is the same project still being maintained.

API stability3/53.0.1 is final, so nothing will move under you now. Getting there was noisy, though: the 2.x line renamed the entire surface from camelCase to snake_case, and 3.0 was supposed to remove the old names but did not, so PdfFileReader, addPage and extractText are still exported and still warn. Argument names moved as well, with encrypt taking user_password while user_pwd survives as a deprecated alias. The result is a stable but doubled API where two spellings of everything coexist and only one of them is correct.
Docs3/5The 3.x documentation is well organised, with task-based pages for merging, cropping, transforming, text extraction, metadata and encryption, and the docstrings in the source are specific about parameters and deprecations. Two things pull it down. The Documentation link published in the package metadata returns 404, so the obvious route in is broken and you have to find the 3.x branch of the site yourself. And search results overwhelmingly return pypdf's current documentation, which describes methods this version does not have.
Maintenance1/5The last release was 3.0.1 on 2022-12-31, and the README states plainly that 3.0.X is the last version of PyPDF2 and that development continues as pypdf. The package raises a DeprecationWarning about itself at import time. The GitHub repository redirects to py-pdf/pypdf, so the activity you see there, including pushes this month and 10,147 stars, belongs to the successor rather than to this distribution. For dependency purposes this package is end of life with a clearly named replacement.
Ecosystem4/5More than six million weekly installs and a decade of accumulated answers mean almost any PDF question has a PyPDF2 answer somewhere, and older LangChain, Airflow and document-processing recipes were written against it. Type hints ship with the package, including a py.typed marker, so editors work well. The catch is that the ecosystem has moved: current integrations target pypdf, so new examples you find will assume methods this release does not have, and old ones will use the camelCase names it deprecates.

Use it if

  • You are maintaining code that already imports PyPDF2 and need to know exactly what 3.0.1 can do before planning the move to pypdf
  • You need page-level manipulation of existing PDFs, splitting, merging, rotating, cropping, stamping, with no compiled dependency and no system libraries
  • You are on a locked-down environment where a pure-Python wheel is the only thing you can install and Python 3.6 or 3.7 is still in play
  • You want to read metadata, outlines or AcroForm field values out of a document without rendering anything
Skip it if

Setup reality

pip install PyPDF2 is quick and pure Python, with typing_extensions pulled in only below 3.10 and dataclasses only below 3.7. The extras matter more than people expect: install PyPDF2[crypto] to get PyCryptodome if you must open AES-encrypted documents, and PyPDF2[image] to get Pillow for the images property. Then the first thing you meet is a DeprecationWarning at import time, emitted from the package's own __init__, saying PyPDF2 is deprecated and to move to pypdf. That is not a lint warning, it is the maintainers' position, and if your test suite turns warnings into errors your build fails on the import line. Naming is the next hurdle. Version 3.0 completed the rename from the old camelCase surface, so PdfReader, PdfWriter and PdfMerger are the current names while PdfFileReader, PdfFileWriter and PdfFileMerger are still exported and still warn, which means old tutorials and current code disagree on every example. Inside the classes the same split repeats: add_page next to addPage, extract_text next to extractText, get_page next to getPage. Reading defaults to strict=False, so a malformed file is repaired silently where an older version would have raised; pass strict=True while you are debugging and you will see problems the default hides. Encryption is the part to check before shipping. The encrypt method writes standard security handler revisions 2 and 3, which is RC4 at 40 or 128 bits, and there is no option for AES; the document identifier it generates comes from MD5 over a repr of the clock and of random.random, which is not a cryptographic source. Treat PDF passwords from this library as access friction, not as protection. Finally, expect documentation friction: the Documentation URL in the package metadata points at a readthedocs path that now returns 404, and the surviving docs live under the 3.x branch of the same site.

Patterns

Open a PDF and count its pagesread-pages

from PyPDF2 import PdfReader

reader = PdfReader("input.pdf")
print(len(reader.pages))
first = reader.pages[0]

The import itself raises a DeprecationWarning pointing at pypdf. Under pytest with -W error that warning fails the test before any of your code runs.

Pull text out of a pageextract-text

from PyPDF2 import PdfReader

reader = PdfReader("input.pdf")
text = "\n".join(page.extract_text() for page in reader.pages)

This walks content streams with no layout model, so multi-column pages interleave and tables lose their structure. It returns an empty string for scanned pages, which have no text at all.

Limit extraction to one text orientationextract-rotated-text

from PyPDF2 import PdfReader

page = PdfReader("input.pdf").pages[0]
upright = page.extract_text(orientations=(0,))
sideways = page.extract_text(orientations=(90,))

orientations defaults to all four quarter turns, so rotated stamps and margin notes land in the middle of your body text. Restricting it is usually the quickest cleanup.

Write selected pages to a new filesplit-pages

from PyPDF2 import PdfReader, PdfWriter

reader = PdfReader("input.pdf")
writer = PdfWriter()
for page in reader.pages[0:5]:
    writer.add_page(page)

with open("first-five.pdf", "wb") as fh:
    writer.write(fh)

Open the output in binary mode. write also accepts a path directly and returns a tuple, which trips up code expecting None.

Concatenate several PDFsmerge-documents

from PyPDF2 import PdfWriter

writer = PdfWriter()
for path in ["a.pdf", "b.pdf", "c.pdf"]:
    writer.append(path)

writer.write("merged.pdf")

PdfWriter.append exists in 3.0.1 and takes a page range and outline options. PdfMerger still works but is the older path and was removed in later pypdf majors.

Turn pages by a quarter turnrotate-pages

from PyPDF2 import PdfReader, PdfWriter

reader = PdfReader("input.pdf")
writer = PdfWriter()
for page in reader.pages:
    writer.add_page(page.rotate(90))

writer.write("rotated.pdf")

The angle must be a multiple of 90 or it raises. rotate returns the page so it chains; rotateClockwise is the deprecated spelling that still works and warns.

Overlay one page onto anotherstamp-a-watermark

from PyPDF2 import PdfReader, PdfWriter

stamp = PdfReader("watermark.pdf").pages[0]
reader = PdfReader("input.pdf")
writer = PdfWriter()

for page in reader.pages:
    page.merge_page(stamp)
    writer.add_page(page)

writer.write("stamped.pdf")

merge_page draws the stamp on top at the origin with no scaling. Use add_transformation with a Transformation first if the two page sizes differ.

Scale and move page contenttransform-page

from PyPDF2 import PdfReader, PdfWriter, Transformation

page = PdfReader("input.pdf").pages[0]
page.add_transformation(Transformation().scale(0.5, 0.5).translate(100, 200))

writer = PdfWriter()
writer.add_page(page)
writer.write("scaled.pdf")

The transformation affects content only; the media box stays the same size, so scaled content sits inside the original page dimensions unless you resize the box too.

Open a password-protected documentread-encrypted

from PyPDF2 import PdfReader, PasswordType

reader = PdfReader("secret.pdf")
if reader.is_encrypted:
    result = reader.decrypt("hunter2")
    if result == PasswordType.NOT_DECRYPTED:
        raise SystemExit("wrong password")
print(len(reader.pages))

PdfReader also takes password= directly at construction. AES-encrypted files need the crypto extra, which installs PyCryptodome.

Add a password, and know what it is worthencrypt-output

from PyPDF2 import PdfReader, PdfWriter

writer = PdfWriter()
writer.append_pages_from_reader(PdfReader("input.pdf"))
writer.encrypt(user_password="open-me", owner_password="admin", use_128bit=True)
writer.write("protected.pdf")

3.0.1 writes RC4 at 40 or 128 bits only, with no AES option, and the file identifier comes from MD5 over the clock and random.random. This deters casual opening; it is not confidentiality.

Read and rewrite document informationread-metadata

from PyPDF2 import PdfReader, PdfWriter

reader = PdfReader("input.pdf")
print(reader.metadata.title, reader.metadata.author)

writer = PdfWriter()
writer.append_pages_from_reader(reader)
writer.add_metadata({"/Title": "Quarterly report", "/Producer": "internal"})
writer.write("tagged.pdf")

Metadata keys are PDF names and need the leading slash. add_metadata merges into whatever the writer already carries rather than replacing it.

Rename the import and drop the warningmigrate-to-pypdf

# pip uninstall PyPDF2 && pip install pypdf

-from PyPDF2 import PdfReader, PdfWriter
+from pypdf import PdfReader, PdfWriter

# same call sites; check any use of PdfMerger,
# which later pypdf majors removed in favour of PdfWriter.append

Most codebases need only the import line. Run the suite with -W error::DeprecationWarning afterwards to catch camelCase methods that pypdf has since removed outright.

Alternatives

PackageRegistryPick it when
pypdfPyPIYou want this library under its current name, still released, with AES writing and years of extraction and parsing fixes
pymupdfPyPIYou need rendering, accurate text extraction with positions, or speed on large documents and can accept a compiled dependency and its licence terms
pdfplumberPyPIYou are pulling structured content out of PDFs, especially tables and word positions, rather than editing pages