Reference
Pages
A PDF in pdfplumber is mostly a list of Page objects. Each page knows its number, size and rotation, holds its own objects, and has every extraction method. Counting pages is instant; the content is only parsed when you ask for it.
Short answer
len(pdf.pages) counts pages; pdf.pages[0] is the first page; page.page_number is the 1-based number shown in viewers.
import time
import pdfplumber
t0 = time.perf_counter()
with pdfplumber.open("samples/real/federal-register-2020-17221.pdf") as pdf:
n = len(pdf.pages)
print(n, "pages, counted in", round((time.perf_counter() - t0) * 1000), "ms")15 pages, counted in 6 msCounting reads only the PDF’s page tree, which is why 15 pages take a few milliseconds. Characters, lines and images are parsed lazily, the first time you use them on a given page.
Loop over pages
import pdfplumber
with pdfplumber.open("samples/bank-statement.pdf") as pdf:
for page in pdf.pages:
first = page.extract_text().splitlines()[0]
print(page.page_number, f"{page.width:.0f}x{page.height:.0f}", "rotation", page.rotation, "|", first)1 595x842 rotation 0 | Riverbend Community Bank
2 595x842 rotation 0 | Riverbend Community Bank
3 595x842 rotation 0 | Riverbend Community BankIndexing is 0-based (pdf.pages[2] is the third page) and page_number is 1-based. When you open with pages=[…] (how), page_number still refers to the original document, which keeps log messages and citations correct.
Size, orientation and paper format
page.width and page.height are in points (72 per inch, about 2.83 per millimetre) and describe the page as it is displayed, after rotation:
import pdfplumber
SIZES = {"US Letter": (612, 792), "A4": (595.28, 841.89), "US Legal": (612, 1008)}
def paper(page):
w, h = sorted((page.width, page.height))
for name, (sw, sh) in SIZES.items():
if abs(w - sw) < 2 and abs(h - sh) < 2:
return name
return f"{w / 72:.2f} x {h / 72:.2f} in"
for name in ["invoice", "bank-statement", "real/nics-background-checks-2015-11", "page-boxes"]:
with pdfplumber.open(f"samples/{name}.pdf") as pdf:
p = pdf.pages[0]
orient = "landscape" if p.width > p.height else "portrait"
print(f"{name.split('/')[-1]:34} {paper(p):16} {orient}")invoice US Letter portrait
bank-statement A4 portrait
nics-background-checks-2015-11 US Legal landscape
page-boxes 8.66 x 12.09 in portraitpage.rotation is the page’s /Rotate value. A landscape page can be a landscape MediaBox (the NICS report above) or a portrait page rotated by 90°; text extraction differs between the two (rotated pages). The boxes behind width and height (MediaBox, CropBox and others) are described in page boxes.
What is on a page
page.objects groups every object by type; the shortcuts page.chars, page.lines, page.rects, page.curves, page.images and page.annots return the same lists:
import pdfplumber
with pdfplumber.open("samples/report.pdf") as pdf:
page = pdf.pages[0]
for kind, objs in page.objects.items():
print(f"{kind:6} {len(objs)}")char 621
rect 1
line 20Every property of each type is listed on objects.
Find the page that contains something
import pdfplumber
with pdfplumber.open("samples/report.pdf") as pdf:
hits = [page.page_number for page in pdf.pages if page.search("Site 30")]
print("'Site 30' appears on page(s):", hits)'Site 30' appears on page(s): [2]page.search() accepts regular expressions and returns positions as well (search text). For a whole document, stop at the first hit when you only need one page; each search() parses its page.
Pages are objects you can narrow
page.crop(), within_bbox(), outside_bbox(), filter() and dedupe_chars() return a derived page with the same methods, limited to part of the original. They do not copy the PDF, so they are cheap; chain them as needed:
import pdfplumber
with pdfplumber.open("samples/report.pdf") as pdf:
page = pdf.pages[1]
body = page.crop((0, 60, page.width, page.height - 50)).dedupe_chars()
text = body.extract_text()
print(text.splitlines()[:2], "| page_number:", body.page_number, "| bbox:", body.bbox)['Site Result Reading (ppb)', 'Site 15 Pass 4.4'] | page_number: 2 | bbox: (0, 60, 612, 742)Derived pages keep the original page’s coordinates and page_number (cropping a cropped page).