Back to blog
Subhajit Bhar
Subhajit Bhar
9 min read

pdfplumber vs PyMuPDF vs pypdf (PyPDF2): Which to Use

pdfplumber vs PyMuPDF vs pypdf: pick by tables, speed, and license. PyPDF2 is now pypdf. MIT vs AGPL, find_tables, and when to use both.

Table of Contents10 sections

Use PyMuPDF for speed, images, and page rendering. Use pdfplumber for tables and an MIT license. Use pypdf (the current name for PyPDF2) to split, merge, or pull basic text. Most production pipelines I build use pdfplumber and PyMuPDF together. pypdf stays in the stack for merge, split, and rotate.

The URL still says PyPDF2 because that is the name people typed when this page first ranked. The live package is pypdf. import fitz is PyMuPDF under its old name.

Quick comparison

pdfplumber PyMuPDF (fitz) pypdf (was PyPDF2)
Text extraction Good, layout-aware Excellent Basic
Table extraction Built-in, strongest find_tables() since 1.23 None
Layout / coordinates Character, word, line Blocks, spans, bbox Limited
Image extraction Limited Excellent None
Speed Slowest (pure Python) Fastest (C / MuPDF) Fine for merge/split
Scanned PDFs Text layer only Text layer; can render for OCR Text layer only
Markdown / RAG DIY pymupdf4llm DIY
License MIT AGPL-3.0 or paid BSD
Pick by the jobA hub labelled "Pick by the job" branching to 4 factors that vary between contracts.Pick bythe jobTablespdfplumber, MIT, visual debugSpeed, images, renderPyMuPDF, if AGPL is acceptableSplit, merge, rotatepypdf; leave extraction to the othersClosed-source productpdfplumber or pypdf, or pay Artifexsubhajitbhar.com
Pick the library for the job, then check the license.

pdfplumber vs PyMuPDF

These two overlap on text, then diverge.

pdfplumber sits on pdfminer.six. It sees every character, word, and line, with coordinates. That is why its table finder works: it reconstructs rows and columns from geometry. PDFs do not have tables. It is slower because that work happens in Python.

PyMuPDF binds the MuPDF C engine. Text comes out faster, images extract cleanly, and you can render a page to a pixmap for OCR. Table support arrived later as page.find_tables(), inspired by pdfplumber. On clean, ruled grids it is usable. On borderless or messy financial tables, I still reach for pdfplumber.

I use pdfplumber when the page is a table, I need debug_tablefinder(), or AGPL is off the table. I use PyMuPDF when volume, images, or rendering matter more than perfect cells. Mixed documents (invoices with line items plus letters, scans, logos) get both: table pages to pdfplumber, everything else to PyMuPDF.

pdfplumber and PyMuPDF in one pipelineA mixed PDF routes table pages to pdfplumber and text or image pages to PyMuPDF, then both paths join as one pipeline.Mixed PDFtables + textpdfplumberextract_tablesPyMuPDFtext · images · renderOne pipelinesame output schemasubhajitbhar.com
Route by page type. Both libraries write into the same schema.

Text extraction

import pdfplumber

with pdfplumber.open("invoice.pdf") as pdf:
    for page in pdf.pages:
        text = page.extract_text()
        print(text)

extract_text() walks left-to-right, top-to-bottom. On a single-column letter that is enough. Multi-column layouts and tightly packed forms often need a crop or word-level filtering instead.

import pymupdf

doc = pymupdf.open("invoice.pdf")
for page in doc:
    print(page.get_text())

For headers vs body vs labels, ask for the dict:

for page in doc:
    for block in page.get_text("dict")["blocks"]:
        if block["type"] != 0:
            continue
        for line in block["lines"]:
            for span in line["spans"]:
                print(span["text"], span["bbox"], span["size"])

import fitz still works. It is the same package. Current docs prefer import pymupdf.

Tables: extract_tables() vs find_tables()

pdfplumber is still the one I use when the table is the product.

with pdfplumber.open("invoice.pdf") as pdf:
    page = pdf.pages[0]
    tables = page.extract_tables()
    for table in tables:
        for row in table:
            print(row)

Borderless tables need a text strategy, not the default line strategy:

table_settings = {
    "vertical_strategy": "text",
    "horizontal_strategy": "text",
    "intersection_tolerance": 5,
}
tables = page.extract_tables(table_settings)

For cropping, merged cells, multi-page stitches, and debug_tablefinder(), use the table extraction guide.

PyMuPDF, since 1.23:

import pymupdf

doc = pymupdf.open("invoice.pdf")
page = doc[0]
found = page.find_tables()
for table in found:
    print(table.extract())
    # df = table.to_pandas()
    # print(table.to_markdown())

find_tables() exists, so yes, PyMuPDF extracts tables. On the invoices and lab reports I see, pdfplumber still wins the ugly cases. I have also seen find_tables() run slower than pdfplumber on some pages. The C engine helps get_text(), not every table heuristic.

Images and rendering

PyMuPDF is the tool for embedded images and for turning a page into pixels:

doc = pymupdf.open("report.pdf")
for page_num, page in enumerate(doc):
    for img_index, img in enumerate(page.get_images(full=True)):
        xref = img[0]
        info = doc.extract_image(xref)
        with open(f"page{page_num}_img{img_index}.png", "wb") as f:
            f.write(info["image"])

pdfplumber can render a page debug image. It is not an image-extraction library.

Speed

I don’t have timings of my own for this post. PyMuPDF is a C binding; pdfplumber walks characters in Python via pdfminer.six. On a handful of pages you will not care. On thousands of documents a day, PyMuPDF is the default for text and I keep pdfplumber for the table pages.

If someone quotes “10×” or “50×”, treat it as order of magnitude, not a number you can put in an SLA. Throughput on your PDFs depends on page count, image density, and whether you are extracting text or reconstructing tables.

pypdf vs PyMuPDF vs pdfplumber

pypdf is the library people still install first, often under the old name.

It is the right tool for merging, splitting, rotating, cropping, and encrypting PDFs. Text extraction is a side effect, and a weak one: multi-column pages and odd spacing come out garbled. It has no table API.

pypdf vs PyMuPDF: PyMuPDF wins extraction, rendering, and images. pypdf wins a permissive BSD license and a pure-Python install. If you only need to concatenate 400 statements into one file, pypdf is enough and simpler to deploy.

pypdf vs pdfplumber: pdfplumber wins anything that looks like data. pypdf wins file surgery. Do not extract invoice line items with pypdf.

pypdf vs PyPDF2: PyPDF2 was merged back into pypdf. Development continues on pypdf. New code should pip install pypdf. The import change for the common path is:

from pypdf import PdfReader  # was: from PyPDF2 import PdfReader

reader = PdfReader("invoice.pdf")
for page in reader.pages:
    print(page.extract_text())

People still search PyPDF2, so the old name stays in the title. For a new project, type pypdf.

License: pdfplumber MIT vs PyMuPDF AGPL

Check the license before you pip install.

pdfplumber is MIT. You can use it in a closed-source commercial product. Read the file in the repo if your legal team needs the text.

pypdf is BSD. Permissive, fine in proprietary software.

PyMuPDF is AGPL-3.0. If you ship a closed-source product that includes PyMuPDF (including a SaaS that lets users upload PDFs), AGPL’s copyleft is a problem unless you buy a commercial license from Artifex. Plenty of teams only discover this at security review.

If the product cannot take AGPL and you need tables, pdfplumber is the default. If you need PyMuPDF’s speed in a proprietary stack, budget for the paid license rather than hoping AGPL “probably doesn’t apply.”

fitz vs PyMuPDF

They are the same library. The PyPI package is pymupdf. For years the Python import was fitz, the historical name of MuPDF’s binding. You will still see import fitz in older code and in Stack Overflow answers.

import pymupdf
# equivalent: import fitz

pip install fitz is a different, unrelated package. Install pymupdf.

Scanned PDFs

None of these three reads text that is only pixels. If page.extract_text() or page.get_text() comes back empty on a page you can clearly see, the PDF is almost certainly a scan (or a hybrid with scanned pages).

The usual path is render with PyMuPDF, then OCR:

import io
import pymupdf
import pytesseract
from PIL import Image

doc = pymupdf.open("scanned_report.pdf")
for page in doc:
    pix = page.get_pixmap(matrix=pymupdf.Matrix(300 / 72, 300 / 72))
    img = Image.open(io.BytesIO(pix.tobytes("png")))
    print(pytesseract.image_to_string(img))

Detecting scanned pages, DPI, and what to do after OCR is in extract data from scanned PDFs.

Markdown, RAG, and pymupdf4llm

If the output is Markdown for an LLM, PyMuPDF4LLM wraps to_markdown() around PyMuPDF. That is a better starting point than reconstructing headings from pdfplumber word lists.

Markdown is still raw extraction. For invoices, CoAs, and anything you will load into a database, I still want a Pydantic schema and validation, not a pile of Markdown. Use pymupdf4llm for retrieval. Use a schema when a field has to be right.

Other names you will hit

  • pdfminer.six: what pdfplumber wraps. Use pdfplumber unless you need the lower-level API.
  • Camelot: another table specialist. Worth a look when pdfplumber’s merged-cell behaviour is the blocker.
  • pypdfium2: PDFium bindings. Fast rendering alternative to PyMuPDF; different license story.
  • Docling: layout-aware parsing aimed at LLM pipelines. Heavier than these three. Relevant when rule-based tables are not enough.

When the PDF layout itself keeps shifting across suppliers, see handling layout variations.

Decision framework

Which Python PDF library to useScanned pages go to PyMuPDF plus OCR. Tables go to pdfplumber. Speed and images go to PyMuPDF after checking AGPL. Merge or split jobs go to pypdf.YESYESYESNONONOOpen a PDFScanned?PyMuPDF + OCRno text layerTables?pdfplumberMIT, debug_tablefinderSpeed /images?PyMuPDFcheck AGPL firstpypdf, or eithermerge / split / rotatesubhajitbhar.com
Yes goes right. License is a stop, not a footnote: if AGPL is off the table, skip PyMuPDF.

In production I usually import both pdfplumber and PyMuPDF. pypdf only if the pipeline also mutates files.

Common failure modesA grid of 4 common failure modes, each a card with a title and short description.PyPDF2 for tablesNo table API.Switch to pdfplumber.AGPL at the endPyMuPDF is AGPL-3.0.Check before you ship.Empty extract_text()Usually a scan.OCR, then extract.pip install fitzWrong package.Install pymupdf.subhajitbhar.com
Wrong package, wrong license, empty text, no tables.

FAQ

Is pdfplumber or PyMuPDF better?

PyMuPDF is better for speed, images, and rendering. pdfplumber is better for tables and for a permissive MIT license. If you have to pick one library for mixed business PDFs, start with both and route by page type.

Does PyMuPDF extract tables?

Yes. page.find_tables() has been in PyMuPDF since 1.23, and it was inspired by pdfplumber’s table finder. It is fine on simple ruled tables. For borderless or irregular tables I still use pdfplumber.

Is PyPDF2 deprecated?

The project was merged into pypdf. Install pypdf and import PdfReader from there. Old PyPDF2 code often ports with a one-line import change.

Can I use PyMuPDF commercially?

You can use it if you comply with AGPL-3.0 (which usually means open-sourcing the product that includes it) or you buy a commercial license from Artifex. pdfplumber (MIT) and pypdf (BSD) do not have that constraint.

Is pdfplumber free for commercial use?

Yes. MIT license. That is the main reason it stays in proprietary pipelines even when PyMuPDF would be faster.

What is the difference between fitz and PyMuPDF?

fitz is the old import name for PyMuPDF. Use pip install pymupdf. Do not pip install fitz.

Why does extract_text() return None?

On a page that visibly has content, you are probably looking at a scanned image with no text layer. Confirm with PyMuPDF: empty get_text() plus images on the page. Then OCR. Details in the scanned PDF guide.

Should I use pdfplumber and PyMuPDF together?

Yes, for production document mix. PyMuPDF for text, images, and scans-to-raster. pdfplumber for table pages. I do this on invoice and lab-report pipelines; the cost of two dependencies is small next to bad rows.

Is pymupdf4llm a replacement for pdfplumber?

No. pymupdf4llm produces Markdown for RAG. pdfplumber produces tables and coordinates.

Last updated on

More on this topic