| name | pdfium-syntax-text |
| description | Use when extracting text from a PDF page with pdfium-render, reading text character by character, getting the text inside a rectangle, or searching a page for a string. Prevents missing text held inside XObject form containers and trusting chars_for_object on an outdated PDFium build. Covers PdfPage::text, PdfPageText::all, the chars and segments collections, PdfPageTextChar, region extraction, and PdfPageText::search with PdfSearchOptions. Keywords: pdfium-render extract text, PdfPageText, page text all, PdfPageTextChar, PdfPageTextSegment, text search, PdfSearchOptions, PdfSearchDirection, chars_for_object, is_generated, is_hyphen, how do I extract text from a PDF, text extraction misses words, garbled text from PDF, search text in PDF, unicode_char
|
| license | MIT |
| compatibility | Designed for Claude Code. Requires pdfium-render 0.8,0.9. |
| metadata | {"author":"OpenAEC-Foundation","version":"1.0"} |
pdfium-syntax-text
Text extraction and search both start from PdfPage::text(), which returns a
PdfPageText handle. From that handle you read the whole page as a String,
walk individual characters, extract text inside a rectangle, or run a search.
Scope: reading and searching text on a page. For page objects and the
read-versus-edit split see pdfium-syntax-page-objects; for the units used by
bounds and rectangles see pdfium-core-coordinates.
Quick Reference
Entry point
let text = page.text()?;
let content: String = text.all();
PdfPageText borrows the PdfPage and implements Drop (it closes the
underlying PDFium text page). Keep it only as long as you need it.
The text API map (0.9.x)
| You want | Call | Returns |
|---|
| whole-page text | text.all() | String |
| text inside a rectangle | text.inside_rect(rect) | String |
| text of one text object | text.for_object(&obj) | String |
| every character | text.chars() | PdfPageTextChars |
| characters in a rectangle | text.chars_inside_rect(rect) | Result<PdfPageTextChars, _> |
| characters of one object | text.chars_for_object(&obj) | Result<PdfPageTextChars, _> |
| rectangular text segments | text.segments() | PdfPageTextSegments |
| search for a string | text.search(needle, &options) | Result<PdfPageTextSearch, _> |
Core rules
- ALWAYS get text through
page.text()?. There is no text accessor on
PdfDocument; text is per page.
- ALWAYS use
text.all() for plain whole-page extraction. It already covers
text nested inside XObject form objects.
- NEVER assume
text.for_object() / chars_for_object() reaches nested text:
when you walk page.objects() yourself you MUST recurse into
PdfPageXObjectFormObject containers (see pdfium-syntax-page-objects).
- NEVER trust
chars_for_object() on a PDFium build older than 6611: it
returned wrong characters before pdfium-render 0.8.31 (issue #98).
- ALWAYS treat
unicode_char() as Option<char>: PDFium returns None for
characters with no single-char mapping. NEVER .unwrap() it blindly.
- ALWAYS handle
is_generated() / is_hyphen() as Result<bool, PdfiumError>,
not as a plain bool.
Decision Trees
Which text accessor do I use?
What do you need?
The full readable text of the page -> text.all()
Text within a known rectangle -> text.inside_rect(rect)
Per-character data (position, font, flags) -> text.chars(), then iterate
Text belonging to one text object -> text.for_object(&text_object)
Rectangular layout blocks (lines / runs) -> text.segments(), then iterate
Find where a string appears -> text.search(needle, &options)
How do I find every occurrence of a string?
Build PdfSearchOptions with new() and the builder methods you need:
case-sensitive -> .match_case(true)
whole words only -> .match_whole_word(true)
Call text.search(needle, &options)?
Iterate matches with .iter(PdfSearchDirection::SearchForward).
Each match is a PdfPageTextSegments collection: iterate its segments for the
matched text and its bounding rectangles.
Patterns
Pattern 1: Extract whole-page text
use pdfium_render::prelude::*;
fn page_text(page: &PdfPage) -> Result<String, PdfiumError> {
Ok(page.text()?.all())
}
all() returns every readable character on the page, including text inside
XObject form containers. This is the correct default for plain extraction.
Pattern 2: Iterate characters
fn visible_chars(page: &PdfPage) -> Result<String, PdfiumError> {
let text = page.text()?;
let mut out = String::new();
for ch in text.chars().iter() {
if ch.is_generated()? {
continue;
}
if let Some(c) = ch.unicode_char() {
out.push(c);
}
}
Ok(out)
}
PdfPageTextChar also exposes tight_bounds(), loose_bounds(), origin(),
font_name(), scaled_font_size(), and index(); see references/methods.md.
Pattern 3: Text inside a rectangle
fn text_in_region(page: &PdfPage, rect: PdfRect) -> Result<String, PdfiumError> {
Ok(page.text()?.inside_rect(rect))
}
inside_rect returns a String; chars_inside_rect returns the matching
PdfPageTextChars. Construct a PdfRect per pdfium-core-coordinates; PDFium
coordinates have their origin at the bottom-left of the page.
Pattern 4: Search a page
fn find_all(page: &PdfPage, needle: &str) -> Result<usize, PdfiumError> {
let text = page.text()?;
let options = PdfSearchOptions::new().match_whole_word(true);
let search = text.search(needle, &options)?;
let mut count = 0;
for matched in search.iter(PdfSearchDirection::SearchForward) {
for segment in matched.iter() {
let _matched_text: String = segment.text();
let _box: PdfRect = segment.bounds();
}
count += 1;
}
Ok(count)
}
PdfSearchOptions defaults to case-insensitive, substring matching. Use
.match_case(true) and .match_whole_word(true) to narrow it.
Pattern 5: Text of a single object
fn object_text(text: &PdfPageText, object: &PdfPageTextObject) -> String {
text.for_object(object)
}
for_object and chars_for_object take a &PdfPageTextObject, NOT the
general PdfPageObject enum. When you walk page.objects() to collect text
object by object, a PdfPageXObjectFormObject is a container: its child text
objects are reached only by recursing. See pdfium-syntax-page-objects.
Version Notes: 0.8.x vs 0.9.x
PdfPageTextSegment and PdfPageTextChar were added in 0.7.6.
PdfSearchOptions and PdfPageText::search() were added in 0.8.13.
PdfPageTextChar::text_object() was added in 0.8.23.
PdfPageTextChar::is_hyphen() was added in 0.8.34.
chars_for_object() returns correct characters only with PDFium build 6611
or newer; the fix landed in pdfium-render 0.8.31 (issue #98). Pin a
pdfium_* feature of pdfium_6611 or higher.
- 0.9.1 relaxed lifetime restrictions on
PdfPageTextSegment and
PdfPageTextChars, which makes storing them in your own types easier.
Anti-Patterns (summary)
| Anti-pattern | Why it fails | Fix |
|---|
| object-by-object text without recursing | XObject form containers hide child text objects | recurse into as_x_object_form_object(), or use text.all() |
chars_for_object on PDFium build < 6611 | returned wrong characters before the #98 fix | pin pdfium_6611+ and pdfium-render 0.8.31+ |
unicode_char().unwrap() | PDFium returns None for chars with no single-char mapping | match the Option, or use unicode_string() |
treating is_generated() as bool | it returns Result<bool, PdfiumError> | propagate with ? |
search with an empty needle | an empty search string yields no useful matches | guard against an empty needle before searching |
Full failure transcripts and fixes are in references/anti-patterns.md.
Reference Files
references/methods.md: exact verified signatures and version annotations
for PdfPageText, PdfPageTextChar(s), PdfPageTextSegment(s),
PdfPageTextSearch, and PdfSearchOptions.
references/examples.md: complete, verified Rust programs for whole-page
extraction, character iteration, region extraction, and search.
references/anti-patterns.md: each text-extraction failure with the cause
and the corrected code.
Related Skills
pdfium-syntax-pages: getting a PdfPage from the document.
pdfium-syntax-page-objects: walking page.objects() and the XObject form
recursion needed for object-level text.
pdfium-core-coordinates: PdfRect, PdfPoints, and the PDF coordinate
origin used by bounds and region accessors.
pdfium-impl-fonts: font handling and the garbled-text failure mode.