Compare
pdfplumber vs pdfminer.six
This is not a contest between rivals: pdfplumber is built on pdfminer.six and installs it for you. The real questions are what pdfplumber adds on top, what it costs, and when it is worth calling pdfminer.six yourself.
Short answer
| pdfminer.six 20260107 | pdfplumber 0.11.10 | |
|---|---|---|
| Relationship | The parser | Built on pdfminer.six; pins it exactly |
| Text, 15-page document | 0.56 s, 42 MB | 1.60 s, 193 MB |
| Two-column reading order (default) | 99% similarity | 28%: reads across columns |
| Tables | No | Yes |
| Crop, filter, search, visual debugging | No | Yes |
| Coordinates | From the bottom of the page | From the top (top, bottom), plus pdfminer’s y0, y1 |
Use pdfplumber for tables, regions and anything positional. Use pdfminer.six directly for fast plain text where its layout analysis gets the reading order right, or when you need its lower-level parser. You can also have both at once: pdfplumber exposes pdfminer’s layout analysis through laparams.
One library on top of the other
pdfminer.six reads the PDF: it decodes fonts and content streams and produces characters, lines, rectangles and curves with their positions. It can also group characters into lines and text boxes (layout analysis, configured with LAParams), which is what its extract_text() uses.
pdfplumber takes pdfminer.six’s objects, turns each into a plain dictionary with coordinates measured from the top of the page, and builds its own tools on them: words with adjustable tolerances, text that follows the page’s visual layout, the table finder, cropping and filtering, search(), and drawing on rendered pages. By default it skips pdfminer’s layout analysis and groups characters itself.
Each pdfplumber release requires one exact pdfminer.six version (0.11.10 requires pdfminer.six==20260107), so the two always move together. Do not install or upgrade pdfminer.six separately; upgrade pdfplumber instead (releases lists the pairs). The two security fixes in pdfminer.six in 2025 reached pdfplumber users this way (FAQ).
Reading order: pdfminer’s strongest point
On a two-column page, pdfminer.six’s layout analysis finds the two columns as separate text boxes and reads them in order. pdfplumber’s extract_text() reads each line straight across the page, so the columns interleave. You can get pdfminer’s result without leaving pdfplumber: pass laparams when opening the file, and the text boxes appear as objects on each page:
import pdfplumber
from pdfminer.high_level import extract_text
PATH = "samples/two-column.pdf"
squash = lambda s: " ".join(s.split())
miner = extract_text(PATH, page_numbers=[0]) # pdfminer.six: layout analysis on
with pdfplumber.open(PATH) as pdf:
plumber = pdf.pages[0].extract_text() # pdfplumber: lines straight across
with pdfplumber.open(PATH, laparams={}) as pdf: # pdfplumber with pdfminer's analysis
boxes = pdf.pages[0].objects["textboxhorizontal"]
plumber_boxes = "".join(box["text"] for box in boxes)
print("pdfminer :", squash(miner)[160:250])
print("pdfplumber :", squash(plumber)[160:250])
print("same as pdfminer with laparams:", squash(plumber_boxes) == squash(miner))pdfminer : ublish quality reports every year, but the numbers rarely travel far beyond the PDF. Analy
pdfplumber : ublish quality reports every page 2. The table has 8 columns and is ruled only year, but t
same as pdfminer with laparams: TrueThe pdfplumber line jumps from the left column (every) into the right one (page 2. The table…). Joining pdfminer’s text boxes gives exactly pdfminer’s text. The multi-column guide shows a geometric alternative that also handles headers, footers and full-width elements, and laparams options are covered under pdfplumber.open().
Layout analysis is not always right either. On pages with tables, forms or scattered labels, its text boxes can group things in surprising ways, which is one reason pdfplumber does not use it by default.
Coordinates: from the bottom or from the top
PDF coordinates start at the bottom-left corner of the page, and pdfminer.six keeps that: y0 is the bottom of a character, y1 its top, both measured upwards. pdfplumber adds top and bottom measured from the top edge, which match how people and images count:
import pdfplumber
from pdfminer.high_level import extract_pages
from pdfminer.layout import LTChar, LTTextContainer
PATH = "samples/invoice.pdf"
page = next(extract_pages(PATH))
first = next(c for box in page if isinstance(box, LTTextContainer)
for line in box for c in line if isinstance(c, LTChar))
print(f"pdfminer {first.get_text()!r} x0={first.x0:.1f} y0={first.y0:.1f} y1={first.y1:.1f} (from the bottom)")
with pdfplumber.open(PATH) as pdf:
p = pdf.pages[0]
c = next(ch for ch in p.chars if ch["x0"] == first.x0 and ch["y0"] == first.y0)
print(f"pdfplumber {c['text']!r} x0={c['x0']:.1f} top={c['top']:.1f} bottom={c['bottom']:.1f} (from the top)")
print("top == page height - y1:", round(c["top"], 3) == round(p.height - first.y1, 3))
print("keys on a pdfplumber char:", len(c))pdfminer 'H' x0=54.0 y0=715.9 y1=735.9 (from the bottom)
pdfplumber 'H' x0=54.0 top=56.1 bottom=76.1 (from the top)
top == page height - y1: True
keys on a pdfplumber char: 22pdfplumber’s characters still carry y0 and y1 too, so code ported from pdfminer keeps working; the conversion is top = page.height - y1. Every pdfplumber character is a dictionary with 22 keys, which is convenient and also why pdfplumber needs more memory: see speed and memory.
Speed and memory
- pdfminer.six0.56 s
- pdfplumber1.6 s
Source: our benchmark, 2026-11-04, Intel(R) Xeon(R) Processor @ 2.10GHz; script lab/bench/run_bench.py
pdfminer.six was about 2.9 times faster and peaked at 42 MB against pdfplumber’s 193 MB. Both are pure Python. pdfplumber’s extra time and memory go into building a dictionary per object and keeping each parsed page cached, which you can release with page.close() (performance). If you need text faster still, compiled libraries such as PyMuPDF and pypdfium2 are another order of magnitude quicker (comparison).
Command-line tools
pdfminer.six installs two command-line tools along with pdfplumber: pdf2txt.py (text, HTML or XML output, with layout analysis options) and dumppdf.py (the raw PDF objects, useful when debugging a broken file). pdfplumber has its own pdfplumber command, which writes characters, lines and other objects as CSV, JSON or text. All three are in the same environment once pdfplumber is installed.
When to use pdfminer.six directly
- Large volumes of plain text from born-digital documents, where its layout analysis handles columns well and you do not need tables.
- Low-level work: walking the document’s objects, custom devices or interpreters, or reading fonts and encodings in detail.
- Debugging a file:
dumppdf.pyshows what is actually in a PDF when both libraries return something odd.
For tables, positions, regions, and checking results visually, stay with pdfplumber. Errors from the parser surface in pdfplumber wrapped in PdfminerException, with pdfminer.six’s original exception inside (error index).