Compare
pdfplumber vs PyMuPDF
PyMuPDF is much faster and can render and edit PDFs; pdfplumber gives you finer geometry, more reliable tables and an MIT licence. We ran both on the same files and the same tasks. Here is where each one wins, with the code side by side.
Short answer
| pdfplumber 0.11.10 | PyMuPDF 2.1 | |
|---|---|---|
| Text, 15-page document | 1.60 s | 0.06 s |
| Peak memory | 193 MB | 55 MB |
| Ruled table, defaults | 100% of rows exact | 0% (structure found, cell text garbled) |
| Two-column reading order, defaults | 28% | 99% |
| Licence | MIT | AGPL-3.0 or paid commercial |
| Installed size with dependencies | 58.7 MB | 282.9 MB |
| Render, edit, create PDFs | No (renders only for debugging) | Yes |
Choose PyMuPDF for high-volume text, rendering or editing, if the AGPL works for you. Choose pdfplumber for tables, precise geometry and a permissive licence. Measured on 2026-11-04; method on choosing a library.
Plain text
import pdfplumber
import pymupdf # imported as "fitz" in older code
path = "samples/invoice.pdf"
with pdfplumber.open(path) as pdf:
print("pdfplumber:", pdf.pages[0].extract_text().splitlines()[:3])
with pymupdf.open(path) as doc:
print("PyMuPDF: ", doc[0].get_text().splitlines()[:3])pdfplumber: ['Hollow Pine Supply Co. INVOICE', '418 Quarry Road, Unit 6', 'Invoice no. INV-2026-0417']
PyMuPDF: ['Hollow Pine Supply Co.', '418 Quarry Road, Unit 6', 'Millbrook, OR 97000']Both return the same words. The order differs: pdfplumber groups by line across the page (so INVOICE, far right, joins the company name), while PyMuPDF follows the blocks in the order the PDF draws them. On multi-column pages that difference decides who gets reading order right with defaults: PyMuPDF did, in our test. pdfplumber needs a crop per column (how).
Speed is PyMuPDF’s clearest win: about 28× faster on the 15-page document. PyMuPDF wraps the MuPDF C library; pdfplumber is Python on top of the pure-Python pdfminer.six.
Words and coordinates
import pdfplumber
import pymupdf
path = "samples/invoice.pdf"
with pdfplumber.open(path) as pdf:
w = pdf.pages[0].extract_words()[0]
print("pdfplumber:", w["text"], round(w["x0"], 1), round(w["top"], 1), round(w["x1"], 1), round(w["bottom"], 1))
with pymupdf.open(path) as doc:
x0, y0, x1, y1, text, *_ = doc[0].get_text("words")[0]
print("PyMuPDF: ", text, round(x0, 1), round(y0, 1), round(x1, 1), round(y1, 1))pdfplumber: Hollow 54.0 56.1 119.6 76.1
PyMuPDF: Hollow 54.0 50.6 119.6 78.1Both use a top-left origin and points, so x0, x1 match. The vertical edges differ: pdfplumber’s box is exactly as tall as the font size (20 pt here, pdfminer.six’s convention), while PyMuPDF’s spans the font’s ascender to descender (27.5 pt). Neither is wrong; just never mix coordinates from the two libraries in one calculation.
Regions
import pdfplumber
import pymupdf
path, box = "samples/invoice.pdf", (50, 125, 300, 225) # x0, top, x1, bottom
with pdfplumber.open(path) as pdf:
print("pdfplumber:", pdf.pages[0].crop(box).extract_text().splitlines())
with pymupdf.open(path) as doc:
print("PyMuPDF: ", doc[0].get_text(clip=pymupdf.Rect(*box)).splitlines())pdfplumber: ['BILL TO', 'Larkspur Robotics GmbH', 'Attn: Accounts Payable', 'Feldstraße 12', '20357 Hamburg, Germany']
PyMuPDF: ['BILL TO', 'Larkspur Robotics GmbH', 'Attn: Accounts Payable', 'Feldstraße 12', '20357 Hamburg, Germany']Identical results. PyMuPDF’s clip= works on text; pdfplumber’s crop() returns a whole page object, so tables, lines, chars and filter() all work inside the region too (crop and coordinates).
Tables
import pdfplumber
import pymupdf
path = "samples/invoice.pdf"
with pdfplumber.open(path) as pdf:
print("pdfplumber:", pdf.pages[0].extract_table()[1])
with pymupdf.open(path) as doc:
table = doc[0].find_tables().tables[0]
print("PyMuPDF: ", table.extract()[1])pdfplumber: ['HP-1042', 'Stainless hex bolts M8 x 40 (box of 100)', '12', '18.40', '220.80']
PyMuPDF: ['HP1042\n-', 'StainlesshexboltsM8x40(boxof100)', '12', '1840\n.', '22080\n.']Both found the table and its cells. With PyMuPDF 2.1, cell text lost its spaces and punctuation moved to a separate line, on every table we tried, including the FBI table from pdfplumber’s test suite. PyMuPDF’s table finder has a strategy="text" option for borderless tables; in our test it did no better. pdfplumber’s table settings and visual debugger are more mature, and with the settings from our guides it reproduced all three test tables exactly (results).
Lines, rectangles and curves
import pdfplumber
import pymupdf
path = "samples/invoice.pdf"
with pdfplumber.open(path) as pdf:
page = pdf.pages[0]
print("pdfplumber: lines", len(page.lines), "| rects", len(page.rects), "| first line", page.lines[0]["x0"], page.lines[0]["top"])
with pymupdf.open(path) as doc:
drawings = doc[0].get_drawings()
items = [item[0] for d in drawings for item in d["items"]]
print("PyMuPDF: drawings", len(drawings), "| path items", {k: items.count(k) for k in set(items)})pdfplumber: lines 15 | rects 1 | first line 54.0 250.0
PyMuPDF: drawings 16 | path items {'re': 1, 'l': 15}pdfplumber exposes vector graphics as ready-to-use objects with x0, top, x1, bottom, colours and widths, which is what its table finder and your own geometry code consume. PyMuPDF’s get_drawings() returns path descriptions ('l' for line, 're' for rectangle, 'c' for curve) that you interpret yourself.
What only one of them does
| Only pdfplumber | Only PyMuPDF |
|---|---|
Tunable table finder with debug_tablefinder() overlays |
Fast rendering to images at any resolution |
crop(), within_bbox(), filter() on all object types |
Editing: insert text and images, redact, merge, split |
Character-level colours, fonts, upright, matrix |
OCR integration through Tesseract |
page.search() with regex and positions |
Markdown output via the companion pymupdf4llm package |
| Pure-Python code (its dependencies ship prebuilt wheels) | Support for EPUB, XPS and other formats |
fitz and pymupdf
Old tutorials use import fitz. That is the same library: PyMuPDF’s original module name, kept for compatibility.
import pymupdf
try:
import fitz
print("import fitz works; same module:", fitz.Document is pymupdf.Document)
except ImportError as e:
print("import fitz failed:", e)
print("PyMuPDF", pymupdf.VersionBind)import fitz works; same module: True
PyMuPDF 2.1Use import pymupdf in new code. There is an unrelated PyPI package called fitz; if pip install fitz was run by mistake, uninstall it and install pymupdf.
The licence question
pdfplumber is MIT: use it in any product, open or closed, without obligations beyond keeping the copyright notice. PyMuPDF is AGPL-3.0: if you distribute software that uses it, or let users interact with it over a network (a SaaS document pipeline counts), you must offer your complete source code under the AGPL, or buy a commercial licence from Artifex. Version 2.1 also installs pymupdf4llm and onnxruntime, which is why it measured 282.9 MB with dependencies.
Using both
The libraries combine well: PyMuPDF for fast text and rendering across a whole collection, pdfplumber for the pages where you need tables or precise geometry. Open the same file with each; they do not interfere. Keep each library’s coordinates within its own calculations.