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()?; // Result<PdfPageText<'_>, PdfiumError>
let content: String = text.all(); // whole-page text
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 onPdfDocument; 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 walkpage.objects()yourself you MUST recurse intoPdfPageXObjectFormObjectcontainers (seepdfium-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()asOption<char>: PDFium returnsNonefor characters with no single-charmapping. NEVER.unwrap()it blindly. - ALWAYS handle
is_generated()/is_hyphen()asResult<bool, PdfiumError>, not as a plainbool.
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() {
// is_generated() is a Result: a generated char is one PDFium
// synthesized (such as a space between words) that is not in the
// content stream itself.
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) {
// `matched` is a PdfPageTextSegments collection: one match.
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
PdfPageTextSegmentandPdfPageTextCharwere added in 0.7.6.PdfSearchOptionsandPdfPageText::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 apdfium_*feature ofpdfium_6611or higher.- 0.9.1 relaxed lifetime restrictions on
PdfPageTextSegmentandPdfPageTextChars, 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 forPdfPageText,PdfPageTextChar(s),PdfPageTextSegment(s),PdfPageTextSearch, andPdfSearchOptions.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 aPdfPagefrom the document.pdfium-syntax-page-objects: walkingpage.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.