Independent guide. Not affiliated with the pdfplumber project or its maintainers.Official repository: jsvine/pdfplumber ↗

Reference

Objects and their properties

Everything pdfplumber finds on a page is a plain Python dictionary: a char, a line, a rect, a curve, an image or an annotation. This page lists every key each one carries in version 0.11.10, with real values from our sample files.

Tested with pdfplumber 0.11.10 · Python 3.13Updated 2026-11-04Source of truth: official README ↗

Short answer

page.chars, page.lines, page.rects, page.curves, page.images and page.annots are lists of dicts; page.objects holds all of them by type. Every object has x0, top, x1, bottom (coordinates), page_number and object_type, plus the type-specific keys below.

x — · top —
Sample invoice with pdfplumber's detected words, lines and table cells drawn on top
Chars (blue), lines (red) and the one rectangle (filled blue: the shaded header row) on the sample invoice. Hover to inspect a character. Source: invoice.pdf (pdfplumber.dev sample, CC0)

char

The first character of the invoice, every key included:

one_char.py
import pdfplumber

with pdfplumber.open("samples/invoice.pdf") as pdf:
    char = pdf.pages[0].chars[0]

for key in sorted(char):
    value = char[key]
    print(f"{key:19} {round(value, 2) if isinstance(value, float) else value!r}")
Outputcaptured · Python 3.13.18 · pdfplumber 0.11.10
adv                 14.44
bottom              76.14
doctop              56.14
fontname            'Helvetica-Bold'
height              20.0
matrix              (1.0, 0.0, 0.0, 1.0, 54.0, 720.0)
mcid                None
ncs                 'DeviceRGB'
non_stroking_color  (0.066667, 0.066667, 0.066667)
object_type         'char'
page_number         1
size                20.0
stroking_color      (0,)
tag                 None
text                'H'
top                 56.14
upright             True
width               14.44
x0                  54.0
x1                  68.44
y0                  715.86
y1                  735.86
Key Meaning
text The character (a space is " "; undecodable glyphs are (cid:N))
fontname Font name as stored, often with a subset prefix like ABCDEF+
size Font size in points
x0, x1, top, bottom Box from the top-left corner; height equals size
y0, y1 Bottom and top measured from the page’s bottom edge
doctop top plus the heights of earlier pages
width, height Box size
adv Horizontal advance: glyph width × font size × scaling
upright False when the text is rotated or vertical
matrix Current transformation matrix: scale, rotation and position (below)
non_stroking_color, stroking_color Fill and outline colour (below)
ncs Name of the fill colour space, e.g. DeviceRGB, DeviceGray, DeviceCMYK (exposed for chars only)
mcid, tag Marked-content ID and tag in tagged PDFs (structure tree)

Words are not stored in the PDF; extract_words() builds them from chars.

Colours

Colours are tuples of numbers between 0 and 1 in the colour space named by ncs (for fills): one value for grey, three for RGB, four for CMYK. The report sample uses four text colours and a yellow highlight rectangle:

colours.py
import pdfplumber

with pdfplumber.open("samples/report.pdf") as pdf:
    page = pdf.pages[0]
    seen = {}
    for c in page.chars:
        seen.setdefault((c["ncs"], c["non_stroking_color"]), c["text"])
    for (space, colour), example in seen.items():
        print(f"{str(space):12} {str(colour):38} e.g. {example!r}")
    rect = page.rects[0]
    print("rect fill:", rect["fill"], rect["non_stroking_color"], "| stroke:", rect["stroke"])
Outputcaptured · Python 3.13.18 · pdfplumber 0.11.10
DeviceRGB    (0.4, 0.4, 0.4)                        e.g. 'H'
DeviceRGB    (0.066667, 0.066667, 0.066667)         e.g. 'A'
DeviceRGB    (0.752941, 0.223529, 0.168627)         e.g. '1'
DeviceRGB    (0.121569, 0.356863, 0.847059)         e.g. 'p'
rect fill: True (1.0, 0.952941, 0.65098) | stroke: False

Compare colours exactly as reported, and check the set of values first: “black” text is often (0,) in a grey space, (0, 0, 0) in RGB, (0, 0, 0, 1) in CMYK, or a near-black like the (0.066667, …) above. Patterns and spot colours can appear as names rather than numbers. The official docs/colors.md has the full rules.

line and rect

lines_and_rects.py
import pdfplumber

with pdfplumber.open("samples/invoice.pdf") as pdf:
    page = pdf.pages[0]
    line = page.lines[0]
    print({k: line[k] for k in ("x0", "top", "x1", "bottom", "linewidth", "stroking_color")})
    print(len(page.rects), "rect(s) +", len(page.lines), "lines ->", len(page.edges), "edges",
          f"({len(page.rect_edges)} from rect sides)")
Outputcaptured · Python 3.13.18 · pdfplumber 0.11.10
{'x0': 54.0, 'top': 250.0, 'x1': 558.0, 'bottom': 250.0, 'linewidth': 0.6, 'stroking_color': (0.6, 0.6, 0.6)}
1 rect(s) + 15 lines -> 19 edges (4 from rect sides)
Key Meaning
linewidth Stroke width in points
stroking_color, non_stroking_color Outline and fill colour
stroke, fill Whether the path is stroked and/or filled
evenodd Fill rule
dash ([dash array], phase) for dashed lines
pts, path The points and drawing commands that make the shape

A line is any straight path segment; a rect is a rectangle drawn as one (table cell borders are often rects, not lines). page.edges combines lines with the four sides of every rect (rect_edges) and the segments of every curve (curve_edges), split into horizontal_edges and vertical_edges. Those are what the table finder works from.

curve

Curves add pts (points along the curve, as (x, top)), path (the full drawing commands, including Bézier control points) and fill. Charts, logos, rounded boxes and text converted to outlines appear as curves. Thousands of curves and no chars usually mean outlined text, which needs OCR.

image

image_object.py
import pdfplumber

with pdfplumber.open("samples/scanned-invoice.pdf") as pdf:
    image = pdf.pages[0].images[0]

for key in ["name", "x0", "top", "x1", "bottom", "srcsize", "colorspace", "bits", "imagemask"]:
    print(f"{key:10} {image[key]}")
Outputcaptured · Python 3.13.18 · pdfplumber 0.11.10
name       FormXob.6211e950b26a27486c359b5cf1d03322
x0         0.0
top        0.0
x1         612.0
bottom     792.0
srcsize    (1275, 1651)
colorspace [/'DeviceGray']
bits       8
imagemask  None

srcsize is the image’s own pixel size; the box is where it is drawn. stream is the raw pdfminer.six stream with the encoded data. Saving image files and rendering pages are covered on images.

Annotations (links, comments, highlights, form widgets) are in page.annots; those with a URL are also in page.hyperlinks. Each has uri, title, contents and the raw annotation dictionary in data. The text under a link is found by position:

links.py
import pdfplumber

with pdfplumber.open("samples/report.pdf") as pdf:
    page = pdf.pages[0]
    for link in page.hyperlinks:
        text = page.within_bbox((link["x0"], link["top"], link["x1"], link["bottom"])).extract_text()
        print(repr(text), "->", link["uri"])
Outputcaptured · Python 3.13.18 · pdfplumber 0.11.10
'pdfplumber.dev/cookbook/' -> https://pdfplumber.dev/cookbook/

Rotation and the matrix

A char’s matrix is the PDF’s current transformation matrix (a, b, c, d, e, f). pdfplumber’s CTM helper turns it into readable values such as the rotation:

char_rotation.py
import pdfplumber
from pdfplumber.ctm import CTM

with pdfplumber.open("samples/real/nics-background-checks-2015-11-rotated.pdf") as pdf:
    char = pdf.pages[0].chars[100]
    ctm = CTM(*char["matrix"])
    print(char["text"], "upright:", char["upright"], "| matrix:", char["matrix"])
    print("skew_x (rotation in degrees):", round(ctm.skew_x, 1))
Outputcaptured · Python 3.13.18 · pdfplumber 0.11.10
e upright: False | matrix: (0.0, -5.76, 5.76, 0.0, 534.7, 503.17536000000007)
skew_x (rotation in degrees): -90.0

Use upright for a quick test and the matrix when you need the angle, for example to separate rotated axis labels from body text.

Layout objects from pdfminer.six

With laparams passed to pdfplumber.open(), pages also expose pdfminer.six’s grouping: textboxhorizontals, textboxverticals, textlinehorizontals and textlineverticals (example).