pdfplumber vs PyMuPDF: character-level geometry against a C rendering engine
Both hand you coordinates, but they get there differently. pdfplumber is built on pdfminer.six and materialises a Python object for every character, line and rectangle on a page, under the MIT licence; PyMuPDF wraps the MuPDF C engine and adds rendering, format conversion and an OCR route, under AGPL-3.0. The choice is usually the licence, and the exception is scanned input, where neither reads text and only one offers a way forward.
At a glance
| Project | jsvine/pdfplumber | pymupdf/PyMuPDF |
|---|---|---|
| Licence | MITPermissive: commercial use allowed | AGPL-3.0Network copyleft: hosting a modified version means sharing source |
| Maintenance | Commits in the last six monthsLast push October 15, 2026 | Commits in the last six monthsLast push November 6, 2026 |
| Language | Python | Python |
| GitHub stars | 10,885 | 11,085 |
| Read more | Our analysisGitHub | Our analysisGitHub |
Star history
- jsvine/pdfplumber10,885 ★ · +116
- pymupdf/PyMuPDF11,085 ★ · +327
| Repository | 2026-09-22 | 2026-11-07 |
|---|---|---|
| jsvine/pdfplumber | 10.8k | 10.9k |
| pymupdf/PyMuPDF | 10.8k | 11.1k |
Which one to choose
Choose pdfplumber if your input is machine-generated PDFs and your downstream code reasons about position, font or bounding boxes. It gives you .chars, .lines, .rects, .curves and .images as lists of per-object dictionaries, a crop method, extract_tables, form value extraction, a command line interface that writes CSV or JSON, and visual debugging for when a page does not look the way you expect. Choose it when licence terms rule out AGPL and when a page image is not part of your output.
Choose PyMuPDF if the page is something you have to see, not just measure. It rasterises at any DPI, renders SVG, converts Office and ebook formats, redacts, and finds tables that come back as Markdown or a Pandas DataFrame, all from a C engine with no mandatory dependencies. Choose it when rendering or format breadth is the requirement, when OCR is the answer to a scanned corpus, and when AGPL-3.0 fits your distribution model.
What each library makes of a page
pdfplumber starts at the smallest unit. Its description is to plumb a PDF for detailed information about each character, rectangle and line, and to extract text and tables on top of that. The README is explicit about the object model: .objects, .chars, .lines, .rects, .curves and .images are lists, and each list holds one dictionary for each such object embedded on the page. A page also reports .page_number, .width and .height, and .crop(bounding_box, relative=False, strict=True) returns a page restricted to a four-value box. So the library's unit of work is the glyph, and text and tables are derived from glyph positions by the layout engine of pdfminer.six, whose parameters you can pass in with the laparams keyword argument.
PyMuPDF starts at the rendered page and reads the text out of it. The README says it is built on MuPDF, a lightweight, fast C engine, and that it gives precise, low-level control over documents alongside high-level convenience APIs with no mandatory external dependencies. Its extraction model is hierarchical rather than per-object: get_text("dict") returns blocks, and a text block contains lines, and a line contains spans, and a span carries text, font and size. Its table model is detection rather than geometry, through find_tables.
The practical difference shows up in what you can do with the result. pdfplumber can tell you that a specific character sits at a specific coordinate with a specific font, which is what a layout checker, an invoice parser with a known form or a stamp remover needs. PyMuPDF can tell you what the page looks like at 200 DPI, which is what a thumbnail service, a preview pane or an OCR front end needs. Both answer the middle question, and only one answers the third.
Installation, dependencies and the Python floor
pdfplumber installs with pip install pdfplumber and is built on pdfminer.six. Its dependency list, as our analysis records it, is pdfminer.six, Pillow and pypdfium2, so the install brings a parsing library, an imaging library and a second PDF implementation with it. The README states that it is currently tested on Python 3.10, 3.11, 3.12, 3.13 and 3.14, and it links a Chinese translation of the documentation, which is a small sign of the audience it serves.
PyMuPDF installs with pip install pymupdf, with pre-built wheels for Windows, macOS and Linux on Python 3.10 to 3.14, and the README states that where no wheel matches, pip compiles from source and needs a C and C++ toolchain. Its core library carries no mandatory external dependency. The features beyond the core are separate packages: pymupdf-fonts for extended fonts in text output, pymupdf4llm for Markdown and JSON extraction aimed at retrieval and AI pipelines, pymupdfpro for Microsoft Office and Korean Office formats, and tesseract-ocr for scanned pages and images, with Tesseract itself installed separately through brew or apt.
So the install comparison is not simple versus complex. PyMuPDF's core is one package that compiles; pdfplumber is one command that resolves to three libraries. Where a build image is pinned, frozen or minimal, the question to ask is which of those two shapes your pipeline can absorb, and the answer is usually that a pre-built wheel is easier than three resolved versions, while a machine with no compiler is easier with pdfplumber.
Tables, text and the debugging loop
Both extract tables, and both do it by looking at where things sit on the page, so the difference is in the surrounding workflow.
pdfplumber's table story is joined to its visual debugging. Its documentation lists table extraction and visual debugging as separate topics, and the combination is the point: when extract_tables returns a shape you did not expect, the page can be inspected visually and cropped to a region with .crop until the geometry is what the code assumed. Our analysis gives that as the pre-commit check for the library: open one representative page from your own corpus, print first_page.chars[0], and print first_page.extract_tables(), then see whether the geometry you get matches what your downstream code expects. For a corpus of forms with a known layout, that loop is the fastest way to settle a question that a unit test would otherwise have to guess at.
PyMuPDF's table story ends in a data structure. find_tables returns table objects, and the README demonstrates table.to_markdown() and table.to_pandas(), so the output is a string or a DataFrame rather than a set of cells you post-process. Its text path is the same shape: get_text("dict") gives blocks, lines and spans with font and size, and the README's output list also includes JSON with bounding boxes, layout data and per-element detail, plus structure-aware Markdown through the separate pymupdf4llm package.
The choice follows the destination. A table that goes into a model or a spreadsheet wants to_pandas and no further thought. A table that goes into a validation rule, a diff against a previous run, or a check that a stamp is not overlapping a figure, wants the cells with their positions so the rule can be written against coordinates. pdfplumber's dictionary-per-object model is the better fit for the second, and PyMuPDF's DataFrame is the better fit for the first.
Scanned pages and the OCR question
Neither library reads a scan. This is the case where the comparison most often gets decided, so it is worth being exact about what each one does and does not do.
pdfplumber's README states that it works best on machine-generated rather than scanned PDFs, and our analysis draws the conclusion from the dependency list: pdfminer.six, Pillow and pypdfium2 contain no OCR step. A scanned page therefore arrives with no characters to enumerate, and .chars is empty or nearly so, which means a table extractor working from glyph positions has nothing to work with.
PyMuPDF does not read a scan either, but its README documents a route through one: an OCR example against a scanned document, with tesseract-ocr as a separate install and Tesseract itself installed on the machine. A page with no text layer can be rasterised with get_pixmap and handed to an OCR engine, which puts a text layer on the page. That is a pipeline you assemble, with a system dependency and an external recogniser in it, and it is still work. It is also the difference between a project that stops at the scan and one that gives you the two steps needed to get past it.
If your corpus is scans, the real answer is that the PDF library is not where the work is, and the choice between these two is a choice about the library you will use once recognition has happened. If your corpus is mixed, the practical pattern is to detect pages without a text layer first, run them through OCR, and then treat the whole corpus as machine-generated.
Where pdfplumber falls short
The per-object model is a cost. Every character, line, rectangle, curve, image and annotation on a page becomes a dictionary, and .objects holds them all for as long as the page is open. A text-heavy page of a long document is a lot of Python objects, and the cost is paid before any table or text logic runs. Our analysis does not put a number on this and neither should you guess one; the structural point is that the library's precision and its memory profile are the same decision.
Rendering is absent as a product feature. The README lists visual debugging, which exists to inspect a page, not to produce page images for a user interface or a document pipeline. If your output is a thumbnail, a preview or a rasterised page for another service, that work belongs to the other project.
Format breadth is narrow. pdfplumber's documented subject is PDF files: a path, a file object or a file-like object passed to open, plus a password keyword argument for protected files. PyMuPDF's format table lists PDF and derivatives such as XPS, EPUB, CBZ, MOBI, FB2, SVG, TXT and MD, images, and Office formats through pymupdfpro.
And the release cadence is slower. The release list runs v0.11.8 on 2025-11-08, v0.11.9 on 2026-01-05 and v0.11.10 on 2026-06-15, with the last recorded push on 2026-08-06. The project is being worked on, but it is on a 0.x line with months between releases, so a pin in a lockfile is a longer commitment than a version number on its own suggests.
Where PyMuPDF falls short
The licence comes first and it is not a technical limit. PyMuPDF is AGPL-3.0, and our analysis is direct: do not adopt it if you ship closed-source software and cannot comply with those terms. It also tells you to read the COPYING file and the licence section of the documentation before committing, which is a step worth taking for a library this central.
Second, the extraction model is less granular at the bottom. Spans carry text, font and size, and the JSON output carries bounding boxes and layout data, but there is no list that gives you one record per character, line and rectangle with a dictionary each. A rule that needs to know exactly where one glyph ends and the next begins, or whether two objects overlap by a fraction of a point, is asking for a model PyMuPDF does not present. The cropped page object lives in the other project as well: PyMuPDF can render a region, but it does not hand back a cropped page for you to keep analysing.
Third, the debugging loop is thinner. There is no documented counterpart to visual debugging that shows you a page with detected objects drawn on it, so when find_tables returns the wrong shape, the diagnosis is a process of elimination rather than a picture of what the detector saw.
Fourth, the dependency shape has a failure mode of its own. A source build needs a C and C++ toolchain, an interpreter outside 3.10 to 3.14 has no wheel, and the Office, Markdown and OCR features each add a package or a system program. A deployment that is pinned to one image has to carry all of it.
Licence, maintenance and the choice for concrete situations
Set the licences beside each other. pdfplumber is MIT, which is permissive, imposes no condition on what you ship and needs no discussion with a legal team. PyMuPDF is AGPL-3.0, which our analysis treats as a real obstacle for closed-source distribution. On the axis that most often decides a library choice, one of these two is trivially adoptable and the other is a decision.
Maintenance is closer than that axis makes it look. pdfplumber's last recorded push is 2026-08-06 and PyMuPDF's is 2026-09-29, and neither repository is archived. The difference is rhythm rather than intent: PyMuPDF released 1.28.0 on 2026-06-29 and 1.28.2 on 2026-08-06, while pdfplumber's releases are months apart on a 0.x line. A team that upgrades on a schedule prefers the first; a team that pins a version and does not want churn is comfortable with the second.
The situations below are clear.
A layout validator, an invoice parser with a known form, or a check that two regions of a page do not overlap. pdfplumber, because .chars, .rects and .crop give you the geometry to write the rule against.
A stamp, a watermark or a fixed header you need to detect by position. pdfplumber, and the same reason.
Tables going into a model, a spreadsheet or a report. PyMuPDF, because find_tables and to_pandas end the task in one call.
Previews, thumbnails, page images, redaction, or conversion between Office, ebook and PDF formats. PyMuPDF, because rendering and format breadth are what it is for.
A closed-source product that needs coordinates. pdfplumber, and the licence question disappears before the technical one starts.
A corpus with scans in it. Neither, on its own. Use PyMuPDF's render-then-OCR route for those pages, then use whichever library suits the machine-generated remainder.
Before committing to either, do what our analysis prescribes. For pdfplumber, open one representative page from your own corpus and print first_page.chars[0] and first_page.extract_tables(). For PyMuPDF, install it on your target Python version, run the two documented snippets against one of your own documents, then read COPYING and the licence section of the documentation.
Bottom line
pdfplumber is the right choice when your rule is written against positions, because it materialises a character, a line and a rectangle as objects you can inspect and crop, and it is MIT, so nothing about your distribution has to change; PyMuPDF is the right choice when the page has to be rendered, converted or recognised, because a C engine gives you images at any DPI, a wider set of input formats and a documented route to OCR. Neither reads a scan, and pdfplumber's 0.x release rhythm against PyMuPDF's frequent 1.x releases is a smaller consideration than the licence. Verify first that your representative page produces the geometry your code expects, that a wheel exists for your Python version or that a toolchain does, and that a scanned page in your corpus is not silently producing empty character lists. The judgement to take away is that this is not a speed contest, since both parse the same bytes and the deciding questions are the licence and whether your output is a number or a picture.
Sources
Was this useful?