pub struct PdfDocument {
pub source_bytes: Vec<u8>,
/* private fields */
}Expand description
PDF document.
This structure represents an open PDF document, providing access to:
- Document metadata (version, catalog, trailer)
- Page information (count, page tree)
- Object loading and dereferencing
§Example
use xberg_native_pdf::document::PdfDocument;
let mut doc = PdfDocument::open("sample.pdf")?;
println!("PDF version: {}.{}", doc.version().0, doc.version().1);
println!("Page count: {}", doc.page_count()?);§Memory management
The document maintains several internal caches for performance. The main
object cache is bounded at 64 MB (see DEFAULT_OBJECT_CACHE_MAX_BYTES)
uses FIFO eviction to prevent unbounded heap growth when processing
many pages sequentially.
§Thread Safety
All interior-mutable fields use Mutex / AtomicUsize, making
PdfDocument both Send and Sync.
Fields§
§source_bytes: Vec<u8>Raw bytes of the document. Every read of the file body addresses this
by absolute offset (see bytes_at), so the document carries no shared
read cursor and a page read never depends on what another page read
first. ~keep
Implementations§
Source§impl PdfDocument
impl PdfDocument
Sourcepub fn extract_annotation_appearance_text(
&self,
annotation: &Annotation,
excluded_layers: &HashSet<String>,
) -> Option<String>
pub fn extract_annotation_appearance_text( &self, annotation: &Annotation, excluded_layers: &HashSet<String>, ) -> Option<String>
Extract text from an annotation’s Normal Appearance stream (/AP/N).
AP streams are content streams with their own /Resources. This creates a temporary TextExtractor, loads fonts from the AP stream resources, and extracts text spans from the decoded stream data.
Source§impl PdfDocument
impl PdfDocument
Sourcepub fn catalog(&self) -> Result<Object>
pub fn catalog(&self) -> Result<Object>
Get the document catalog (root object).
The catalog is the root of the document’s object hierarchy. It contains references to the page tree, outlines, etc.
§Errors
Returns an error if:
- The /Root entry is present but is not a reference
- Loading the catalog object fails
- The trailer omits /Root and no
/Type /Catalogobject can be found by scanning (the recovery path: a missing /Root is not itself fatal — the Catalog is discovered by object scan, as Poppler / PDFium do — but it does error if that scan also fails)
§Example
let catalog = doc.catalog()?;Sourcepub fn structure_tree(&self) -> Result<Option<StructTreeRoot>>
pub fn structure_tree(&self) -> Result<Option<StructTreeRoot>>
Get the structure tree (logical structure) of the document.
Tagged PDFs contain a structure tree that defines the logical structure and reading order of the document. This is the PDF-spec-compliant way to determine reading order.
Returns Ok(Some(StructTreeRoot)) if the document has a structure tree,
Ok(None) if it’s not a tagged PDF, or an error if parsing fails.
§Example
if let Some(struct_tree) = doc.structure_tree()? {
println!("This is a Tagged PDF with logical structure");
} else {
println!("This PDF does not have a structure tree");
}Sourcepub fn structure_tree_with_budget(
&self,
budget: Option<Duration>,
) -> Result<Option<StructTreeRoot>>
pub fn structure_tree_with_budget( &self, budget: Option<Duration>, ) -> Result<Option<StructTreeRoot>>
Returns the document’s structure tree, bounding the parse work by an
optional wall-clock budget.
None parses the complete tree (identical to Self::structure_tree);
Some(duration) returns Ok(None) if parsing exceeds that budget, so a
latency-sensitive caller can fall back to another strategy. Prefer None
unless you have a concrete responsiveness requirement.
Sourcepub fn prefers_structure_reading_order(&self) -> bool
pub fn prefers_structure_reading_order(&self) -> bool
Whether text extraction uses the Tagged-PDF logical structure order (a
depth-first traversal of /StructTreeRoot) rather than geometric
page-content order for this document.
Returns true exactly when the document carries a trustworthy structure
tree per ISO 32000-1:2008 §14.8.2.3.1 / §14.7.1: it is /Marked or the
catalog references a /StructTreeRoot, the tree resolves non-empty, and
/MarkInfo /Suspects is not true. When false, extraction falls back
to geometric reading order. This is a read-only introspection accessor;
it does not change extraction behaviour.
Sourcepub fn output_intent_cmyk_profile(&self) -> Option<Arc<IccProfile>>
pub fn output_intent_cmyk_profile(&self) -> Option<Arc<IccProfile>>
Find the document’s default CMYK output-intent profile.
Per ISO 32000-1:2008 §14.11.5, an /OutputIntents array in the
catalog advertises the colour characteristics of the target
output device. Each entry is a dictionary; the DestOutputProfile
key (when present) references an ICC profile stream identifying
the intended press / display calibration.
This method returns the first CMYK DestOutputProfile it
finds (N = 4) — the usual match for “here is how my CMYK ink
should look” on PDF/X files. Callers can use it as a fallback
profile for plain /DeviceCMYK images that lack their own ICC
colour space.
Returns None when no output intent exists, no CMYK entry is
present, or the profile stream can’t be parsed as ICC.
Sourcepub fn has_output_intents_declaration(&self) -> bool
pub fn has_output_intents_declaration(&self) -> bool
True when the document catalog declares an /OutputIntents
array, regardless of whether the contained profile bytes
successfully parse. Coupled with
Self::output_intent_cmyk_profile returning None, this
distinguishes “no OutputIntent requested” (acceptable silent
fallback) from “OutputIntent requested but unusable” (degraded
press output that callers should warn about). Tracks upstream
issue upstream issue #712 on swallowed profile-parse
diagnostics.
Sourcepub fn mark_info(&self) -> Result<MarkInfo>
pub fn mark_info(&self) -> Result<MarkInfo>
Get the MarkInfo dictionary from the document catalog.
The MarkInfo dictionary indicates whether the document conforms to Tagged PDF conventions and whether the structure tree might contain suspect (unreliable) content.
Per ISO 32000-1:2008 Section 14.7.1, the MarkInfo dictionary contains:
/Marked- Whether the document conforms to Tagged PDF conventions/Suspects- Whether the document contains suspect content/UserProperties- Whether the document uses user properties
§Returns
Returns MarkInfo with the parsed values, or default values if
the MarkInfo dictionary is not present.
§Example
let mark_info = doc.mark_info()?;
if mark_info.is_structure_reliable() {
println!("Structure tree can be trusted for reading order");
} else if mark_info.suspects {
println!("Structure tree may contain unreliable content");
}Source§impl PdfDocument
impl PdfDocument
Sourcepub fn extract_text_filtered(
&self,
page_index: usize,
excluded_layers: HashSet<String>,
excluded_inks: HashSet<String>,
) -> Result<String>
pub fn extract_text_filtered( &self, page_index: usize, excluded_layers: HashSet<String>, excluded_inks: HashSet<String>, ) -> Result<String>
Extract text from a page, excluding content from specified layers and inks.
Uses the same full text assembly pipeline as extract_text
(structure-tree ordering, table detection, column detection), but with
layer/ink-excluded spans removed before assembly.
Ink filtering note: For DeviceN color spaces, text is suppressed if ANY ink in the DeviceN array matches an excluded ink name. Tint values are not evaluated — this is an all-or-nothing match.
§Arguments
page_index- Zero-based page indexexcluded_layers- OCG layer names to suppress (empty = no layer filtering)excluded_inks- Separation/DeviceN ink names to suppress (empty = no ink filtering)
Sourcepub fn extract_text_filtered_in_rect(
&self,
page_index: usize,
excluded_layers: HashSet<String>,
excluded_inks: HashSet<String>,
region: Rect,
mode: RectFilterMode,
) -> Result<String>
pub fn extract_text_filtered_in_rect( &self, page_index: usize, excluded_layers: HashSet<String>, excluded_inks: HashSet<String>, region: Rect, mode: RectFilterMode, ) -> Result<String>
Extract text from a region of a page with layer/ink filtering applied.
Composes Self::extract_text_filtered with Self::extract_text_in_rect: spans
are filtered by layer/ink first, then by region, then assembled via
the full text pipeline (structure-tree ordering, table detection,
column detection, whitespace + line breaks).
Sourcepub fn extract_spans_with_reading_order(
&self,
page_index: usize,
reading_order: ReadingOrder,
) -> Result<Vec<TextSpan>>
pub fn extract_spans_with_reading_order( &self, page_index: usize, reading_order: ReadingOrder, ) -> Result<Vec<TextSpan>>
Extract text spans from a page using a specified reading order strategy.
This method extracts text spans identically to extract_spans,
then applies the chosen reading order strategy to sort them.
§Arguments
page_index- Zero-based page indexreading_order- The reading order strategy to apply
§Returns
Vector of TextSpan objects sorted according to the chosen reading order.
§Examples
let mut doc = PdfDocument::open("two_column.pdf")?;
let spans = doc.extract_spans_with_reading_order(0, ReadingOrder::ColumnAware)?;
for span in spans {
println!("{}", span.text);
}Sourcepub fn extract_spans_filtered_with_reading_order(
&self,
page_index: usize,
reading_order: ReadingOrder,
excluded_layers: HashSet<String>,
excluded_inks: HashSet<String>,
) -> Result<Vec<TextSpan>>
pub fn extract_spans_filtered_with_reading_order( &self, page_index: usize, reading_order: ReadingOrder, excluded_layers: HashSet<String>, excluded_inks: HashSet<String>, ) -> Result<Vec<TextSpan>>
Extract positioned spans in a reading order, excluding optional-content layers and/or Separation/DeviceN inks.
This is extract_spans_with_reading_order
and extract_text_filtered combined: the
former cannot filter, and the latter returns assembled text rather than
positioned spans. A consumer that lays spans out itself - an HTML/XML
emitter placing each span at its own rectangle - needs both at once.
The motivating case is render/extract parity. render_page honours the
document’s default configuration /OCProperties/D, but span extraction
treats everything as visible unless the caller names layers (see the
optional_content module note). Passing
optional_content::compute_default_off_ocgs(&doc) as excluded_layers
makes extraction agree with what the page actually displays - without it,
a default-OFF layer holding a copy of the page contributes a SECOND copy
of every word.
Empty sets are exactly equivalent to the unfiltered call, so this is a superset of the existing API and costs nothing when no filtering is asked for.
§Arguments
page_index- Zero-based page indexreading_order- The reading order strategy to applyexcluded_layers- OCG layer names to suppress (empty = no filtering)excluded_inks- Separation/DeviceN ink names to suppress (empty = none)
§Examples
let doc = PdfDocument::open("layered.pdf")?;
// Agree with what the page displays: drop the default-off layers.
let hidden = optional_content::compute_default_off_ocgs(&doc);
let spans = doc.extract_spans_filtered_with_reading_order(
0,
ReadingOrder::ColumnAware,
hidden,
Default::default(),
)?;
for span in spans {
println!("{}", span.text);
}Rotation is handled differently here than in Self::extract_spans.
This function calls only extract_spans_raw/_filtered plus
drop_offpage_spans (see the comment in the body below) and deliberately
skips postprocess_spans, so map_span_into_rotated_frame never runs on
this path and every returned span’s page_rotation_applied is always
0. Bboxes here stay in raw user space even on a /Rotated page; the
caller owns the upright transform. xberg’s own extraction layer — the
only caller of this function — already does this via upright_origin,
which rotates by the span’s own content-matrix angle. Making this path
call postprocess_spans “for consistency” would be a regression, not a
fix: upright_origin would then rotate a bbox postprocess_spans had
already mapped into the displayed frame, double-transforming it into a
third, wrong coordinate — this was worked out numerically, not assumed.
Sourcepub fn extract_page_text(&self, page_index: usize) -> Result<PageText>
pub fn extract_page_text(&self, page_index: usize) -> Result<PageText>
Extract complete page text data in a single call.
Returns a PageText containing spans in reading order, per-character
data derived from those spans (using font-metric widths when available),
and the page dimensions. Uses the default TopToBottom reading order.
§Arguments
page_index- Zero-based page index
§Examples
let mut doc = PdfDocument::open("example.pdf")?;
let page_text = doc.extract_page_text(0)?;
println!("Page {}x{} pt", page_text.page_width, page_text.page_height);
println!("{} spans, {} chars", page_text.spans.len(), page_text.chars.len());Sourcepub fn extract_structured(&self, page_index: usize) -> Result<StructuredPage>
pub fn extract_structured(&self, page_index: usize) -> Result<StructuredPage>
Extract a page as typed StructuredPage
regions.
Returns the page’s text grouped into
StructuredRegions — body blocks,
headings, header/footer/page-number chrome, and marginal labels — in
reading order, with a best-effort column_index for two-column bodies.
Roles are derived from signals already attached to each span: /Artifact
marked content (ISO 32000-1:2008 §14.8.2.2), structure-tree heading levels
(§14.7.2), and span geometry (§14.8.2.3.1). A tagged PDF with a
trustworthy /StructTreeRoot (see
prefers_structure_reading_order)
therefore yields tree-driven roles; untagged PDFs use the geometric /
font-size fallbacks. This is an additive aggregation layer — it does not
change any existing extraction output.
§Arguments
page_index- Zero-based page index
§Examples
let mut doc = PdfDocument::open("two_column.pdf")?;
let page = doc.extract_structured(0)?;
for region in &page.regions {
println!("{:?} col={:?}: {}", region.kind, region.column_index, region.text);
}Sourcepub fn extract_structured_with_column_mode(
&self,
page_index: usize,
column_mode: ColumnMode,
) -> Result<StructuredPage>
pub fn extract_structured_with_column_mode( &self, page_index: usize, column_mode: ColumnMode, ) -> Result<StructuredPage>
Extract a page as StructuredPage
regions with an explicit column-detection
ColumnMode (Fix 3).
Auto runs the geometric gutter heuristic (same as
extract_structured); Two forces a
two-column split for layouts the conservative heuristic rejects (short,
ragged reference-edition lines); Single suppresses column detection.
The override applies to the geometric path only — ISO 32000-1:2008
§14.8.2.3 leaves untagged reading order undefined — and never overrides a
trustworthy structure tree.
Sourcepub fn extract_page_text_with_options(
&self,
page_index: usize,
reading_order: ReadingOrder,
) -> Result<PageText>
pub fn extract_page_text_with_options( &self, page_index: usize, reading_order: ReadingOrder, ) -> Result<PageText>
Extract complete page text data with a specific reading order.
Like extract_page_text but allows choosing
between TopToBottom and ColumnAware reading order.
§Arguments
page_index- Zero-based page indexreading_order- Reading order strategy to apply
§Examples
let mut doc = PdfDocument::open("two_column.pdf")?;
let page_text = doc.extract_page_text_with_options(0, ReadingOrder::ColumnAware)?;
for span in &page_text.spans {
println!("{}", span.text);
}Sourcepub fn extract_spans_with_config(
&self,
page_index: usize,
config: SpanMergingConfig,
) -> Result<Vec<TextSpan>>
pub fn extract_spans_with_config( &self, page_index: usize, config: SpanMergingConfig, ) -> Result<Vec<TextSpan>>
Extract text spans from a page with custom configuration.
This method allows controlling span merging behavior through configuration, including adaptive threshold settings for improved extraction quality.
§Arguments
page_index- Zero-based page indexconfig- SpanMergingConfig controlling extraction parameters
§Returns
A vector of TextSpan objects extracted from the page with applied configuration.
§Examples
let mut doc = PdfDocument::open("example.pdf")?;
// Use adaptive threshold configuration
let config = SpanMergingConfig::adaptive();
let spans = doc.extract_spans_with_config(0, config)?;Sourcepub fn get_layers(&self) -> Result<Vec<String>>
pub fn get_layers(&self) -> Result<Vec<String>>
Extract individual characters from a PDF page.
This is a low-level API for character-level granularity. For most use cases,
prefer extract_spans() which provides complete text strings as PDF defines them.
§Character-level extraction details:
- Returns individual
TextCharobjects with position, font, and style information - Characters are sorted in reading order (top-to-bottom, left-to-right)
- Overlapping characters (rendered multiple times for effects) are deduplicated
- Useful for layout analysis, debugging, or custom text processing pipelines
§Arguments
page_index- Page number (0-indexed)
§Returns
Vector of TextChar objects in reading order, or error if extraction fails
§Examples
let mut doc = PdfDocument::open("document.pdf")?;
let chars = doc.extract_chars(0)?;
for ch in chars {
println!("'{}' at ({:.1}, {:.1}), font: {}",
ch.char, ch.bbox.x, ch.bbox.y, ch.font_name);
}List all Optional Content Group (OCG) layer names in the document.
Reads /OCProperties from the document catalog and returns the /Name
of each OCG dictionary listed in /OCGs. These names can be passed to
extract_text_filtered / extract_chars_filtered via excluded_layers.
Returns an empty vec if the document has no optional content.
Sourcepub fn get_page_inks(&self, page_index: usize) -> Result<Vec<String>>
pub fn get_page_inks(&self, page_index: usize) -> Result<Vec<String>>
List ink / separation names used on a specific page.
Scans the page’s /Resources /ColorSpace dictionary for /Separation
and /DeviceN color space definitions and returns their ink names.
These names can be passed to extract_text_filtered /
extract_chars_filtered via excluded_inks.
Note: Only the page’s own /Resources is walked. Spot inks
declared inside a Form XObject’s local /Resources /ColorSpace
dictionary will not be enumerated — even though the renderer and
extractor will still honor them at use time. Callers populating a
UI picker from this list may miss XObject-local inks.
For the full walk that follows Do operators into Form XObject
resources, use Self::get_page_inks_deep — that is what the
separation renderer uses to allocate plates.
Sourcepub fn get_page_inks_deep(&self, page_index: usize) -> Result<Vec<String>>
pub fn get_page_inks_deep(&self, page_index: usize) -> Result<Vec<String>>
List ink / separation names declared on a page including those
declared inside Form XObjects reached through the page’s content-stream
Do operators.
Walks the page’s content stream looking for Do operators that invoke
Form XObjects (§8.10), recurses into each form’s /Resources/ColorSpace
dictionary, and accumulates /Separation and /DeviceN ink names from
every visited resource tree.
Cycle handling: indirect XObject references are deduplicated by
ObjectRef; recursion depth is bounded at MAX_RECURSION_DEPTH (100).
A cycle below the depth bound is silently terminated; a tree deeper
than the bound returns Error::RecursionLimitExceeded.
Out of scope: tiling / shading patterns (§8.7) and annotation appearance streams (§12.5.5) — both can declare their own colour spaces but the separation renderer does not paint into them, so surfacing their inks here would create plates that stay empty.
Sourcepub fn extract_chars(&self, page_index: usize) -> Result<Vec<TextChar>>
pub fn extract_chars(&self, page_index: usize) -> Result<Vec<TextChar>>
§Performance Note
Character extraction is typically 30-50% faster than span extraction because it skips the text grouping and merging logic.
Sourcepub fn extract_chars_filtered(
&self,
page_index: usize,
excluded_layers: HashSet<String>,
excluded_inks: HashSet<String>,
) -> Result<Vec<TextChar>>
pub fn extract_chars_filtered( &self, page_index: usize, excluded_layers: HashSet<String>, excluded_inks: HashSet<String>, ) -> Result<Vec<TextChar>>
Extract characters from a page, excluding content from specified layers and inks.
§Arguments
page_index- Zero-based page indexexcluded_layers- OCG layer names to suppress (empty = no layer filtering)excluded_inks- Separation/DeviceN ink names to suppress (empty = no ink filtering)
Sourcepub fn extract_words_with_thresholds(
&self,
page_index: usize,
word_gap_threshold: Option<f32>,
profile: Option<ExtractionProfile>,
) -> Result<Vec<Word>>
pub fn extract_words_with_thresholds( &self, page_index: usize, word_gap_threshold: Option<f32>, profile: Option<ExtractionProfile>, ) -> Result<Vec<Word>>
Extract words from a page with optional threshold and profile overrides.
When word_gap_threshold is None, the adaptive threshold is computed
automatically from page statistics (median character width × 0.3).
Providing a value (in PDF points) overrides the adaptive computation,
which is useful for tuning word segmentation on specific document types.
When profile is provided, it controls how the underlying text spans are
extracted from the PDF content stream (TJ offset thresholds, word margin
ratios). This affects the raw character data before word clustering.
Sourcepub fn extract_words_with_thresholds_no_artifacts(
&self,
page_index: usize,
word_gap_threshold: Option<f32>,
profile: Option<ExtractionProfile>,
) -> Result<Vec<Word>>
pub fn extract_words_with_thresholds_no_artifacts( &self, page_index: usize, word_gap_threshold: Option<f32>, profile: Option<ExtractionProfile>, ) -> Result<Vec<Word>>
Same as Self::extract_words_with_thresholds but drops spans tagged
as /Artifact (running headers/footers, page numbers, watermarks;
ISO 32000-1:2008 §14.8.2.2.1). The spec-correct variant.
Sourcepub fn extract_text_lines_with_thresholds(
&self,
page_index: usize,
word_gap_threshold: Option<f32>,
line_gap_threshold: Option<f32>,
profile: Option<ExtractionProfile>,
) -> Result<Vec<TextLine>>
pub fn extract_text_lines_with_thresholds( &self, page_index: usize, word_gap_threshold: Option<f32>, line_gap_threshold: Option<f32>, profile: Option<ExtractionProfile>, ) -> Result<Vec<TextLine>>
Extract text lines from a page with optional threshold and profile overrides.
When thresholds are None, adaptive values are computed automatically
from page statistics. Providing values (in PDF points) overrides the
adaptive computation for fine-grained control over segmentation.
When profile is provided, it controls how the underlying text spans are
extracted from the PDF content stream (TJ offset thresholds, word margin
ratios). This affects the raw character data before word/line clustering.
§Arguments
page_index- Zero-based page indexword_gap_threshold- Optional override for the horizontal gap (in PDF points) used to split characters into words. Smaller values produce more words.line_gap_threshold- Optional override for the vertical gap (in PDF points) used to group words into lines. Smaller values produce more lines.profile- Optional extraction profile for span-level tuning.
§Example
// Use adaptive thresholds (default behavior)
let lines = doc.extract_text_lines_with_thresholds(0, None, None, None)?;
// Tune both thresholds for dense forms
let lines = doc.extract_text_lines_with_thresholds(0, Some(1.5), Some(4.0), None)?;Sourcepub fn extract_text_lines_with_thresholds_no_artifacts(
&self,
page_index: usize,
word_gap_threshold: Option<f32>,
line_gap_threshold: Option<f32>,
profile: Option<ExtractionProfile>,
) -> Result<Vec<TextLine>>
pub fn extract_text_lines_with_thresholds_no_artifacts( &self, page_index: usize, word_gap_threshold: Option<f32>, line_gap_threshold: Option<f32>, profile: Option<ExtractionProfile>, ) -> Result<Vec<TextLine>>
Same as Self::extract_text_lines_with_thresholds but drops spans
tagged as /Artifact (running headers/footers, page numbers,
watermarks; ISO 32000-1:2008 §14.8.2.2.1). Spec-correct variant.
Sourcepub fn apply_intelligent_text_processing(
&self,
spans: Vec<TextSpan>,
) -> Vec<TextSpan>
pub fn apply_intelligent_text_processing( &self, spans: Vec<TextSpan>, ) -> Vec<TextSpan>
Apply intelligent text post-processing to extracted text spans.
This method applies several text quality improvements:
- Ligature expansion (fi, fl, ffi, ffl → component characters)
- Hyphenation reconstruction (rejoins words split across lines)
- Whitespace normalization (removes excess spaces within words)
- Special character spacing (Greek letters, math symbols)
- OCR text cleanup (when font_name == “OCR” or from known OCR engines)
§Arguments
spans- Vector of TextSpan extracted from pages
§Returns
Processed spans with improved text quality
§Examples
let mut doc = PdfDocument::open("example.pdf")?;
// Extract spans from page
let spans = doc.extract_spans(0)?;
// Apply intelligent processing
let processed = doc.apply_intelligent_text_processing(spans);
for span in &processed {
println!("{}", span.text); // Ligatures expanded, hyphenation fixed
}Sourcepub fn extract_hierarchical_content(
&self,
page_index: usize,
) -> Result<Option<StructureElement>>
pub fn extract_hierarchical_content( &self, page_index: usize, ) -> Result<Option<StructureElement>>
Extract hierarchical content structure from a page.
Returns the page’s hierarchical content structure with all children populated. For tagged PDFs with structure trees, returns the structure with extracted content. For untagged PDFs, returns a synthetic hierarchy based on geometric analysis.
§Arguments
page_index- The page to extract from (0-indexed)
§Returns
Ok(Some(structure)) if structure is found or generated,
Ok(None) if no structure is available,
Err if an error occurs during extraction
§PDF Spec Compliance
- ISO 32000-1:2008, Section 14.7 - Logical Structure
- ISO 32000-1:2008, Section 14.8 - Tagged PDF
§Example
let mut doc = PdfDocument::open("example.pdf")?;
// Extract hierarchical structure from first page
if let Some(structure) = doc.extract_hierarchical_content(0)? {
println!("Document structure type: {}", structure.structure_type);
println!("Number of children: {}", structure.children.len());
}Source§impl PdfDocument
impl PdfDocument
Sourcepub fn resolve_object(&self, obj: &Object) -> Result<Object>
pub fn resolve_object(&self, obj: &Object) -> Result<Object>
Resolve an object reference.
This is useful when working with indirect object references in content streams or resource dictionaries.
Sourcepub fn get_or_load_font_for_rendering(
&self,
font_obj: &Object,
) -> Result<Arc<FontInfo>>
pub fn get_or_load_font_for_rendering( &self, font_obj: &Object, ) -> Result<Arc<FontInfo>>
Look up a font from the per-document font_cache, parsing and inserting
on a cache miss. Used by the page renderer so that FontInfo::from_dict
(which decodes widths, CID maps, ToUnicode CMaps, and extracts embedded
font bytes) is called at most once per PDF object reference, even when
multiple pages share the same font resources.
Sourcepub fn load_fonts_public(
&self,
resources: &Object,
extractor: &mut TextExtractor<'_>,
) -> Result<()>
pub fn load_fonts_public( &self, resources: &Object, extractor: &mut TextExtractor<'_>, ) -> Result<()>
Public wrapper for load_fonts (normally pub(crate)).
Loads font dictionaries from a resources object into a TextExtractor.
Sourcepub fn page_font_face_lookups(&self) -> Result<Vec<HashMap<String, String>>>
pub fn page_font_face_lookups(&self) -> Result<Vec<HashMap<String, String>>>
Per-page mapping of PDF font-resource names (e.g. "F75") to their
canonical face name (e.g. "TeXGyreTermesX-Regular", with any
subset-prefix ABCDEF+ stripped).
Used by the layout-preserving DOCX writer so each text span can be
emitted with the actual face name in <w:rFonts> instead of a
PDF-internal resource id. The vector is pages × map; map[i]
covers all fonts referenced by page i’s Resources.
Sourcepub fn extract_embedded_fonts(&self) -> Result<Vec<(String, Vec<u8>)>>
pub fn extract_embedded_fonts(&self) -> Result<Vec<(String, Vec<u8>)>>
Extract every embedded font program (TrueType / OpenType bytes) used
anywhere in the document, deduplicated by BaseFont name.
Walks every page’s font dictionary, loads each font via the same path
extract_text uses, and returns the unique set of fonts that have
embedded FontFile2/FontFile3 streams. The String is the base
font name (with any subset prefix like ABCDEF+ stripped) and the
Vec<u8> is the raw font program — directly suitable for re-embedding
into another container (DOCX word/fonts/, another PDF, etc.).
Fonts without embedded data (standard 14, missing FontFile streams) are skipped — there’s nothing to extract.
Sourcepub fn extract_embedded_fonts_with_unicode_maps(
&self,
) -> Result<Vec<(String, Vec<u8>, HashMap<u32, u16>)>>
pub fn extract_embedded_fonts_with_unicode_maps( &self, ) -> Result<Vec<(String, Vec<u8>, HashMap<u32, u16>)>>
Like Self::extract_embedded_fonts but additionally returns a
per-font Unicode → GID map reconstructed from the source PDF’s
/ToUnicode CMap and the font’s CID/byte→GID table.
CFF font subsets in PDFs (the typical Word/LibreOffice output)
often ship without a Unicode cmap because CIDs encode the
glyph stream directly. The font program parses fine but
EmbeddedFont::glyph_lookup is empty; downstream font
registration treats the font as unusable and falls back to
Helvetica.
The map returned here lets office_oxide / xberg-native-pdf write
pipelines call [crate::writer::EmbeddedFont::extend_glyph_lookup]
to re-populate the missing Unicode→GID entries from the
source-PDF’s own /ToUnicode. Result: CFF subset fonts
register and render with the source typeface program instead
of base-14 Helvetica.
Sourcepub fn extract_embedded_fonts_with_unicode_maps_and_widths(
&self,
) -> Result<Vec<(String, Vec<u8>, HashMap<u32, u16>, HashMap<u16, u16>)>>
pub fn extract_embedded_fonts_with_unicode_maps_and_widths( &self, ) -> Result<Vec<(String, Vec<u8>, HashMap<u32, u16>, HashMap<u16, u16>)>>
Like Self::extract_embedded_fonts_with_unicode_maps but also
returns the per-glyph widths from the source PDF’s /W array
(in 1/1000 em units, keyed by GID). Required for re-embedding
CFF font subsets whose synthetic OpenType wrapper carries no
hmtx table — without this, ttf-parser returns 0 for every
glyph advance and the round-trip writer emits a /W of zeros.
Source§impl PdfDocument
impl PdfDocument
Sourcepub fn check_for_circular_references(&self) -> Vec<(ObjectRef, ObjectRef)>
pub fn check_for_circular_references(&self) -> Vec<(ObjectRef, ObjectRef)>
Check for circular references in the object graph.
This is a diagnostic method that performs a depth-first search through the object graph to detect cycles.
§Returns
A vector of tuples representing edges that create cycles. Each tuple is (from_object, to_object) where to_object is already in the path when encountered again.
§Example
let cycles = doc.check_for_circular_references();
if !cycles.is_empty() {
println!("Found {} circular references", cycles.len());
}Sourcepub fn extract_images(&self, page_index: usize) -> Result<Vec<PdfImage>>
pub fn extract_images(&self, page_index: usize) -> Result<Vec<PdfImage>>
Extract images from a page.
Extracts all images from the specified page by processing the content stream. This includes:
- Images referenced via
Dooperators (XObject calls) - Images in nested Form XObjects (with recursion)
- Inline images (BI…ID…EI sequences)
This method processes PDF content streams instead of only iterating the XObject
dictionary. This ensures that images referenced via the Do operator in the content
stream are properly extracted, including those in nested Form XObjects. ColorSpace
indirect references are also resolved.
Returns a vector of PdfImage objects representing the extracted images.
§Arguments
page_index- Zero-based page index
§Returns
A vector of PdfImage objects, one for each image found on the page.
§Errors
Returns an error if the page cannot be accessed or if image extraction fails.
§Examples
let images = doc.extract_images(0)?;
println!("Found {} images on page 1", images.len());
for (i, image) in images.iter().enumerate() {
image.save_as_png(&format!("image_{}.png", i))?;
}Sourcepub fn extract_embedded_files(&self) -> Result<Vec<(String, Vec<u8>)>>
pub fn extract_embedded_files(&self) -> Result<Vec<(String, Vec<u8>)>>
Extract embedded files / attachments (WS1.8a, ISO 32000-1 §7.11.4).
Walks the catalog’s /Names /EmbeddedFiles name tree and returns
(filename, decoded bytes) for each /Filespec’s /EF /F (or /UF)
embedded-file stream. Complements the existing embedded-file writer.
Returns an empty vector when the document has no attachments.
Sourcepub fn page_image_handles(
&self,
page_index: usize,
) -> Result<Vec<PdfImageHandle<'_>>>
pub fn page_image_handles( &self, page_index: usize, ) -> Result<Vec<PdfImageHandle<'_>>>
Enumerate images on a page without decompressing any stream (Phase 1).
Walks the page content stream once and reads image metadata (dimensions, colour space, filter chain, compressed size) directly from each Image XObject dictionary. No pixel data is decoded. Returns a handle per image in content-stream paint order.
Call crate::PdfImageHandle::decode on individual handles to materialise only
the images you need, or crate::PdfImageHandle::raw_compressed_bytes to forward
compressed data (e.g. JPEG bytes) without recompression.
Form XObjects (subtype /Form) are recursed into, matching the behaviour
of PdfDocument::extract_images. Cycle detection (depth limit 100) and
the document’s Form stream cache are used. Images inside nested or shared
Forms receive the correct final CTM-composed bbox / rotation_degrees.
§Examples
let doc = PdfDocument::from_bytes(bytes).unwrap();
// Decode only images larger than a thumbnail threshold
let images: Vec<_> = doc.page_image_handles(0)?
.into_iter()
.filter(|h| h.width >= 200 && h.height >= 200)
.map(|h| h.decode())
.collect::<Result<_, _>>()?;Sourcepub fn extract_images_to_files(
&self,
page_index: usize,
output_dir: impl AsRef<Path>,
prefix: Option<&str>,
start_index: Option<usize>,
) -> Result<Vec<ExtractedImageRef>>
Available on non-WebAssembly only.
pub fn extract_images_to_files( &self, page_index: usize, output_dir: impl AsRef<Path>, prefix: Option<&str>, start_index: Option<usize>, ) -> Result<Vec<ExtractedImageRef>>
Extract images from a page and save them to files.
Each image is saved as a separate file in output_dir with the given
prefix and an incrementing index starting from start_index.
Sourcepub fn get_page_for_debug(&self, page_index: usize) -> Result<Object>
pub fn get_page_for_debug(&self, page_index: usize) -> Result<Object>
Public wrapper for get_page (normally private).
Exposed for profiling examples that need to time page tree lookup separately.
Sourcepub fn may_contain_text_public(data: &[u8]) -> bool
pub fn may_contain_text_public(data: &[u8]) -> bool
Public wrapper for may_contain_text (normally pub(crate)).
Returns true if the content stream might contain text operators (BT or Do).
Source§impl PdfDocument
impl PdfDocument
Sourcepub fn version(&self) -> (u8, u8)
pub fn version(&self) -> (u8, u8)
Get the PDF version.
Returns a tuple (major, minor) representing the PDF version. For example, PDF 1.7 returns (1, 7).
§Example
let (major, minor) = doc.version();
println!("PDF version: {}.{}", major, minor);Sourcepub fn trailer(&self) -> &Object
pub fn trailer(&self) -> &Object
Get a reference to the trailer dictionary.
The trailer dictionary contains important document metadata including:
- /Root: Reference to the catalog dictionary
- /Info: Reference to the document info dictionary (optional)
- /Size: Number of entries in the cross-reference table
- /Encrypt: Encryption dictionary (if encrypted)
- /ID: File identifier array
§Example
let trailer = doc.trailer();
if let Some(dict) = trailer.as_dict() {
if let Some(info_ref) = dict.get("Info") {
println!("Document has an Info dictionary");
}
}Sourcepub fn all_object_ids(&self) -> Vec<u32>
pub fn all_object_ids(&self) -> Vec<u32>
Return every object ID known to this document.
Unions the cross-reference table with any object IDs that were recovered from compressed object streams (which may not have an explicit xref entry). The result is sorted and deduplicated so callers can iterate once and write each object exactly once.
Used by DocumentEditor::write_full_to_writer to sweep any
objects that were not reached during the shallow page-tree
traversal (e.g. embedded font sub-objects such as
DescendantFonts, FontFile2, ToUnicode, FontDescriptor).
Sourcepub fn load_object(&self, obj_ref: ObjectRef) -> Result<Object>
pub fn load_object(&self, obj_ref: ObjectRef) -> Result<Object>
Load an object by its reference.
This function:
- Checks the object cache first
- If not cached, looks up the byte offset in the xref table
- Seeks to that offset and parses the object
- Caches the result for future access
- If object not in xref but is critical, scans file for it
§Errors
Returns an error if:
- The object reference is not in the xref table and file scan fails
- The object is not in use (free object)
- Seeking to the object offset fails
- Parsing the object fails
- A circular reference is detected
- The recursion depth limit is exceeded
§Example
let obj_ref = ObjectRef::new(1, 0);
let obj = doc.load_object(obj_ref)?;Sourcepub fn resolve_references(
&self,
obj: &Object,
max_depth: usize,
) -> Result<Object>
pub fn resolve_references( &self, obj: &Object, max_depth: usize, ) -> Result<Object>
Resolve references within an object recursively.
This utility method resolves indirect references within an object, handling nested dictionaries and arrays up to a specified depth. Useful for processing complex PDF structures where properties may be stored as indirect references.
§Arguments
obj- The object to resolve references withinmax_depth- Maximum recursion depth to prevent infinite loops
§Returns
The object with all references resolved up to max_depth levels. If a reference cannot be resolved, it is left as-is.
§Example
// Resolve all references in a dictionary up to 3 levels deep
let resolved = doc.resolve_references(&obj, 3)?;Sourcepub fn is_form_xobject(&self, obj_ref: ObjectRef) -> bool
pub fn is_form_xobject(&self, obj_ref: ObjectRef) -> bool
Peek at an XObject’s /Subtype without loading the full object. Returns true if the XObject is a Form XObject, false if Image or unknown. For compressed objects or on any error, returns true (conservative — will load fully).
Source§impl PdfDocument
impl PdfDocument
Sourcepub fn from_bytes(data: Vec<u8>) -> Result<Self>
pub fn from_bytes(data: Vec<u8>) -> Result<Self>
Open a PDF document from in-memory bytes.
This is the primary constructor for cases where the PDF data is already fully loaded in memory. This parses the PDF by wrapping the bytes in a memory reader and delegating to internal parsers.
§Errors
Returns an error if the PDF data is invalid, unsupported, or cannot be parsed.
Sourcepub fn open_from_bytes(data: Vec<u8>) -> Result<Self>
👎Deprecated since 0.3.15: Use from_bytes instead
pub fn open_from_bytes(data: Vec<u8>) -> Result<Self>
Use from_bytes instead
Deprecated alias for from_bytes.
Sourcepub fn open(path: impl AsRef<Path>) -> Result<Self>
pub fn open(path: impl AsRef<Path>) -> Result<Self>
Open a PDF document from a file path.
Reads the entire file into memory, then parses the PDF structure. This is the standard constructor for desktop/server environments.
§Errors
Returns an error if:
- The file cannot be opened or read
- The PDF header is invalid
- The cross-reference table is corrupted
- The trailer dictionary is invalid
§Example
use xberg_native_pdf::document::PdfDocument;
let doc = PdfDocument::open("sample.pdf")?;Sourcepub fn open_with_config(
path: impl AsRef<Path>,
_config: impl Any,
) -> Result<Self>
Available on non-WebAssembly only.
pub fn open_with_config( path: impl AsRef<Path>, _config: impl Any, ) -> Result<Self>
Open with custom extraction profile.
Currently, the profile is not used at the document level but is reserved for future integration with document-type-specific extraction settings.
Sourcepub fn authenticate(&self, password: &[u8]) -> Result<bool>
pub fn authenticate(&self, password: &[u8]) -> Result<bool>
Authenticate with a password to decrypt encrypted PDFs.
If the PDF is encrypted, open() automatically tries an empty password.
Call this method to authenticate with a non-empty password.
§Arguments
password- The password as bytes
§Returns
Ok(true) if authentication succeeded, Ok(false) if the password was wrong,
or Ok(true) if the PDF is not encrypted (no authentication needed).
Sourcepub fn is_encrypted(&self) -> bool
pub fn is_encrypted(&self) -> bool
Check if the PDF is encrypted.
Returns true if the PDF has an /Encrypt entry in its trailer,
regardless of whether it has been authenticated.
§Example
if doc.is_encrypted() {
println!("PDF is encrypted");
}Sourcepub fn is_authenticated(&self) -> bool
pub fn is_authenticated(&self) -> bool
Whether content extraction is permitted right now — true if the
PDF is unencrypted, or encrypted and successfully authenticated.
Cheap, side-effect-free preflight for the auto-extraction
classifier: lets it emit
ReasonCode::EncryptedNoExtractPermission
gracefully instead of attempting extraction and erroring.
Sourcepub fn document_producer(&self) -> Option<String>
pub fn document_producer(&self) -> Option<String>
Document Info dictionary /Producer (decoded, trimmed), if present
and non-empty. A weak document-level prior for the scanner-vs-
authoring heuristic (case P) — never decisive.
Sourcepub fn document_creator(&self) -> Option<String>
pub fn document_creator(&self) -> Option<String>
Document Info dictionary /Creator (decoded, trimmed), if present
and non-empty. See document_producer.
Sourcepub fn classify_page(&self, page: usize) -> Result<PageClassification>
pub fn classify_page(&self, page: usize) -> Result<PageClassification>
Cheap per-page text-vs-OCR classification (the classify_page
preflight — no OCR, no rasterisation). Returns kind +
confidence + typed ReasonCode
- the raw signals (explainable).
Fails closed on an encrypted-unauthenticated document
(Error::EncryptedPdf, case L) — consistent with every other
extract_*; the graceful warn+fallback applies to extraction
(extract_page_auto), not this preflight.
Sourcepub fn classify_document(&self) -> Result<DocumentClassification>
pub fn classify_document(&self) -> Result<DocumentClassification>
Cheap whole-document classification: per-page kinds (the
decision is per-page, never one forced doc mode — case Q),
the 0-based pages_needing_ocr list, and an aggregate summary.
Fails closed on an encrypted-unauthenticated document
(Error::EncryptedPdf) — a security op must never be silently
degraded to a benign Empty. Any non-security
per-page failure degrades to Empty (graceful — only security
ops fail closed).
Sourcepub fn extract_text_auto(&self, page: usize) -> Result<String>
pub fn extract_text_auto(&self, page: usize) -> Result<String>
One-shot convenience for the 90% case: equivalent to
AutoExtractor::new().extract_text(self, page). Strictly
additive — the existing extract_text is
byte-identical/unchanged; this is a new opt-in entry point that
auto-routes text-vs-OCR with graceful native fallback.
Source§impl PdfDocument
impl PdfDocument
Sourcepub fn page_count(&self) -> Result<usize>
pub fn page_count(&self) -> Result<usize>
Get the number of pages in the document.
This function:
- Loads the catalog (root object)
- Follows the /Pages reference to the page tree root
- Extracts the /Count value from the page tree
§Errors
Returns an error if:
- The catalog cannot be loaded
- The /Pages entry is missing or invalid
- The page tree root does not contain a /Count entry
§Example
let count = doc.page_count()?;
println!("Document has {} pages", count);Sourcepub fn get_page_media_box(
&self,
page_index: usize,
) -> Result<(f32, f32, f32, f32)>
pub fn get_page_media_box( &self, page_index: usize, ) -> Result<(f32, f32, f32, f32)>
Get the MediaBox of a page.
MediaBox defines the physical boundaries of the page in user space units.
Sourcepub fn get_page_rotation(&self, page_index: usize) -> Result<i32>
pub fn get_page_rotation(&self, page_index: usize) -> Result<i32>
Page /Rotate normalised to one of {0, 90, 180, 270}
(ISO 32000-1 §7.7.3.3); 0 when absent or invalid.
Pure inspection (no feature gate) for the auto-extraction
classifier (case I — transformed-bbox coverage / OCR
orientation). Resolves via get_page, so the
inheritable /Rotate attribute (ISO 32000-1 §7.7.3.4) is walked
up the page tree — a /Rotate set on an ancestor /Pages node
is honoured, not just one on the leaf page object.
Sourcepub fn get_page_rotation_status(
&self,
page_index: usize,
) -> Result<PageRotation>
pub fn get_page_rotation_status( &self, page_index: usize, ) -> Result<PageRotation>
Sibling of get_page_rotation that keeps the
three cases the latter folds into 0 (absent, non-numeric, and a
numeric value that is not a multiple of 90) distinct. Same inheritance
resolution via get_page.
Sourcepub fn page_count_u32(&self) -> u32
👎Deprecated since 0.1.0: Use page_count() instead, which returns Result
pub fn page_count_u32(&self) -> u32
Use page_count() instead, which returns Result
Get page count as u32 (legacy API).
This is a convenience method that returns the page count as a u32.
It calls page_count() internally but converts the result
returns 0 if an error occurs (for backward compatibility).
Sourcepub fn page_indices(&self) -> Range<usize> ⓘ
pub fn page_indices(&self) -> Range<usize> ⓘ
Returns the page index range 0..page_count, or an empty range
when page_count() fails.
Designed for for i in doc.page_indices() { ... } so callers
don’t have to write for i in 0..doc.page_count()?. The
fallible-vs-iterator tension that motivated the issue is
resolved by treating a metadata-broken document as having no
pages at the iteration level — every per-page extraction call
is already fallible and surfaces the real error.
§Example
for i in doc.page_indices() {
let text = doc.extract_text(i)?;
println!("page {}: {} chars", i, text.len());
}Source§impl PdfDocument
impl PdfDocument
Sourcepub fn extract_paths(&self, page_index: usize) -> Result<Vec<PathContent>>
pub fn extract_paths(&self, page_index: usize) -> Result<Vec<PathContent>>
Extract path (vector graphics) content from a page.
This extracts all vector graphics operations from the page’s content stream, including lines, curves, rectangles, and shapes.
§Arguments
page_index- Zero-based page index
§Returns
A vector of PathContent objects representing all paths on the page.
§Examples
let mut doc = PdfDocument::open("example.pdf")?;
// Extract paths from first page
let paths = doc.extract_paths(0)?;
for path in paths {
println!("Path with {} operations, bbox: {:?}",
path.operations.len(), path.bbox);
if path.has_stroke() {
println!(" Stroked with width: {}", path.stroke_width);
}
if path.has_fill() {
println!(" Filled");
}
}Sourcepub fn extract_rects(&self, page_index: usize) -> Result<Vec<PathContent>>
pub fn extract_rects(&self, page_index: usize) -> Result<Vec<PathContent>>
Extract rectangles from a page.
Identifies paths that form axis-aligned rectangles.
Sourcepub fn extract_lines(&self, page_index: usize) -> Result<Vec<PathContent>>
pub fn extract_lines(&self, page_index: usize) -> Result<Vec<PathContent>>
Extract straight lines from a page.
Identifies paths that form a single straight line segment.
Sourcepub fn extract_paths_in_rect(
&self,
page_index: usize,
region: Rect,
) -> Result<Vec<PathContent>>
pub fn extract_paths_in_rect( &self, page_index: usize, region: Rect, ) -> Result<Vec<PathContent>>
Extract paths from a specific rectangular region of a page.
Only paths whose bounding box intersects the specified region are returned.
§Arguments
page_index- Zero-based page indexregion- The rectangular region to extract from
§Returns
A vector of PathContent objects within the specified region.
§Examples
let mut doc = PdfDocument::open("example.pdf")?;
// Extract paths from a specific region (e.g., header area)
let header_region = Rect::new(0.0, 700.0, 612.0, 92.0);
let paths = doc.extract_paths_in_rect(0, header_region)?;
println!("Found {} paths in header region", paths.len());Source§impl PdfDocument
impl PdfDocument
Sourcepub fn assemble_text_via_reading_order(
&self,
page_index: usize,
) -> Result<(Vec<TextSpan>, ReadingOrderClass)>
pub fn assemble_text_via_reading_order( &self, page_index: usize, ) -> Result<(Vec<TextSpan>, ReadingOrderClass)>
Assemble the page’s text spans via the reading-order
pipeline, classifying each region with the per-class
detectors in crate::pipeline::reading_order::detectors.
Returns the assembled spans plus the detector class that
fired on each region.
The four detectors handle layout shapes that the plain y-then-x assembly cannot produce correctly:
- DramaticScript: Macbeth-style speaker-tag layouts — row-major join required.
- DenseSingleLine: SEC DEF 14A 8pt-body interleave — single-row regroup required.
- SubSuperBaselineReattach: chemical-formula subscripts — baseline reattach required.
- NarrowTrackedJustified: stretched justified columns — per-line median-gap threshold normalisation required.
Regions that don’t match any specific layout fall through to
Default (plain y-then-x assembly within the block).
Callers can use this as a pre-step before applying their own
assembly logic, or rely on the classified ReadingOrderClass
to dispatch their assembly strategy. extract_text consumes
this implicitly through extract_spans + the existing
XYCutStrategy.
Sourcepub fn has_text_layer(&self, page_index: usize) -> Result<bool>
pub fn has_text_layer(&self, page_index: usize) -> Result<bool>
Returns true if the page has any text-bearing content (fonts in
resources + at least one BT/Do operator in the content stream),
false if the page is image-only or genuinely empty.
Callers can route image-only pages to their own OCR pipeline instead of receiving an empty string with no signal.
Conservative: returns true when the page resources can’t be
inspected (load error, encrypted-not-authenticated, etc.) so the
caller still attempts extraction.
§PDF spec basis
§8.8 (Image XObjects): image-only pages have /Resources whose
only /XObject entries are /Subtype /Image with no /Font
resources.
Sourcepub fn permissions(&self) -> Option<PdfPermissions>
pub fn permissions(&self) -> Option<PdfPermissions>
Returns the document’s /P permission flags as a PdfPermissions
struct if the document is encrypted; None otherwise.
Per PDF spec §7.6.3.2 the /P flag is advisory — xberg-native-pdf
does not enforce restrictions — but callers who want to
enforce them (e.g., refuse copy-protected PDF extraction) can
do so themselves by checking the returned permissions.
§PDF spec basis
§7.6.3.2 Table 22 (/P Standard Encryption Dictionary entry).
Decoding is implemented in encryption::permissions::PdfPermissions::from_p_flag.
Sourcepub fn extract_spans(&self, page_index: usize) -> Result<Vec<TextSpan>>
pub fn extract_spans(&self, page_index: usize) -> Result<Vec<TextSpan>>
Extract text spans from a page (PDF spec compliant - RECOMMENDED).
This is the recommended method for text extraction. It extracts complete text strings as the PDF provides them via Tj/TJ operators, following the PDF specification ISO 32000-1:2008.
§Benefits over extract_chars
- Avoids overlapping character issues
- Preserves PDF’s text positioning intent
- More robust for complex layouts
- Matches industry best practices (PyMuPDF, etc.)
§Arguments
page_index- Zero-based page index
§Returns
Vector of TextSpan objects in reading order
§Examples
let mut doc = PdfDocument::open("document.pdf")?;
let spans = doc.extract_spans(0)?;
for span in spans {
println!("Text: {} at ({}, {})", span.text, span.bbox.x, span.bbox.y);
}Sourcepub fn prepare_search(&self) -> Result<()>
pub fn prepare_search(&self) -> Result<()>
Build the search index for every page up front.
search() builds this lazily one page at a time as needed, so calling
this is optional — it exists for callers who want the first search()
call to be as fast as the rest, at the cost of paying full-document
extraction immediately instead of spread across the first sweep.
Sourcepub fn clear_search_index(&self)
pub fn clear_search_index(&self)
Drop the cached search index, if any, freeing its memory.
search()/search_page() will rebuild it lazily on next use.
Source§impl PdfDocument
impl PdfDocument
Sourcepub fn extract_text_in_rect(
&self,
page_index: usize,
region: Rect,
mode: RectFilterMode,
) -> Result<String>
pub fn extract_text_in_rect( &self, page_index: usize, region: Rect, mode: RectFilterMode, ) -> Result<String>
Extract text from a specific rectangular region of a page.
Only spans whose bounding boxes match region under mode are kept;
the retained spans are assembled through the full text pipeline
(reading order, tables, line breaks) so the output matches the
quality of Self::extract_text. Calling this with a region that covers
the whole page is equivalent to Self::extract_text.
Sourcepub fn extract_words_in_rect(
&self,
page_index: usize,
region: Rect,
mode: RectFilterMode,
) -> Result<Vec<Word>>
pub fn extract_words_in_rect( &self, page_index: usize, region: Rect, mode: RectFilterMode, ) -> Result<Vec<Word>>
Extract words from a specific rectangular region of a page.
Sourcepub fn extract_text_lines_in_rect(
&self,
page_index: usize,
region: Rect,
mode: RectFilterMode,
) -> Result<Vec<TextLine>>
pub fn extract_text_lines_in_rect( &self, page_index: usize, region: Rect, mode: RectFilterMode, ) -> Result<Vec<TextLine>>
Extract text lines from a specific rectangular region of a page.
Sourcepub fn extract_spans_in_rect(
&self,
page_index: usize,
region: Rect,
mode: RectFilterMode,
) -> Result<Vec<TextSpan>>
pub fn extract_spans_in_rect( &self, page_index: usize, region: Rect, mode: RectFilterMode, ) -> Result<Vec<TextSpan>>
Extract text spans from a specific rectangular region of a page.
Sourcepub fn extract_text_excluding_rects(
&self,
page_index: usize,
exclude: &[Rect],
mode: RectFilterMode,
) -> Result<String>
pub fn extract_text_excluding_rects( &self, page_index: usize, exclude: &[Rect], mode: RectFilterMode, ) -> Result<String>
Extract text from a page excluding specific rectangular regions.
The excluded spans are removed before the full text-assembly pipeline
runs, so the output has the same structure — line breaks, tables,
reading order — as Self::extract_text. Calling this with an empty
exclude slice is equivalent to Self::extract_text.
mode controls the overlap rule:
crate::layout::RectFilterMode::Intersects(default): drop any span with any overlapcrate::layout::RectFilterMode::FullyContained: drop only spans lying entirely insideRectFilterMode::MinOverlap(t): drop spans where at least fractiontof the span’s area overlaps an excluded region
For Tagged PDFs the extractor already honours /Artifact marked-content
(PDF spec §14.8.2.2). This method provides the same capability for
untagged PDFs where spatial coordinates are the only available signal.
Exclusion is unconditional: spans inside a region are dropped regardless
of their structure-tree role.
Sourcepub fn extract_words_excluding_rects(
&self,
page_index: usize,
exclude: &[Rect],
mode: RectFilterMode,
) -> Result<Vec<Word>>
pub fn extract_words_excluding_rects( &self, page_index: usize, exclude: &[Rect], mode: RectFilterMode, ) -> Result<Vec<Word>>
Extract words from a page excluding specific rectangular regions.
See Self::extract_text_excluding_rects for a description of exclude and mode.
Returns the low-level crate::layout::Word stream; use Self::extract_text_excluding_rects
for fully-assembled text with line breaks and tables.
Sourcepub fn extract_spans_excluding_rects(
&self,
page_index: usize,
exclude: &[Rect],
mode: RectFilterMode,
) -> Result<Vec<TextSpan>>
pub fn extract_spans_excluding_rects( &self, page_index: usize, exclude: &[Rect], mode: RectFilterMode, ) -> Result<Vec<TextSpan>>
Extract text spans from a page excluding specific rectangular regions.
See Self::extract_text_excluding_rects for a description of exclude and mode.
Returns raw crate::layout::TextSpan objects with bounding boxes and font metadata;
use Self::extract_text_excluding_rects for fully-assembled text output.
Sourcepub fn extract_rects_in_rect(
&self,
page_index: usize,
region: Rect,
) -> Result<Vec<PathContent>>
pub fn extract_rects_in_rect( &self, page_index: usize, region: Rect, ) -> Result<Vec<PathContent>>
Extract rectangles from a specific rectangular region of a page.
Sourcepub fn extract_lines_in_rect(
&self,
page_index: usize,
region: Rect,
) -> Result<Vec<PathContent>>
pub fn extract_lines_in_rect( &self, page_index: usize, region: Rect, ) -> Result<Vec<PathContent>>
Extract straight lines from a specific rectangular region of a page.
Sourcepub fn extract_chars_in_rect(
&self,
page_index: usize,
region: Rect,
mode: RectFilterMode,
) -> Result<Vec<TextChar>>
pub fn extract_chars_in_rect( &self, page_index: usize, region: Rect, mode: RectFilterMode, ) -> Result<Vec<TextChar>>
Extract individual characters from a specific rectangular region of a page.
Sourcepub fn extract_images_in_rect(
&self,
page_index: usize,
region: Rect,
) -> Result<Vec<PdfImage>>
pub fn extract_images_in_rect( &self, page_index: usize, region: Rect, ) -> Result<Vec<PdfImage>>
Extract images from a specific rectangular region of a page.
Sourcepub fn get_page_info(&self, page_index: usize) -> Result<PageInfo>
pub fn get_page_info(&self, page_index: usize) -> Result<PageInfo>
Get information about a page, including its dimensions.
This is useful for rendering and layout calculations.
Sourcepub fn get_page_resources(&self, page_index: usize) -> Result<Object>
pub fn get_page_resources(&self, page_index: usize) -> Result<Object>
Get the resources dictionary for a page.
Resources contain fonts, images, patterns, and other objects used when rendering the page.
Source§impl PdfDocument
impl PdfDocument
Sourcepub fn erase_region(&self, page_index: usize, rect: Rect) -> Result<()>
pub fn erase_region(&self, page_index: usize, rect: Rect) -> Result<()>
Mark a specific rectangular region on a page for erasure.
Content in this region will be excluded from all subsequent text extractions.
Sourcepub fn clear_erase_regions(&self, page_index: usize) -> Result<()>
pub fn clear_erase_regions(&self, page_index: usize) -> Result<()>
Clear all erase regions for a page.
Sourcepub fn remove_headers(&self, threshold: f32) -> Result<usize>
pub fn remove_headers(&self, threshold: f32) -> Result<usize>
Identify and remove headers.
Uses spec-compliant /Artifact tags when available (100% accuracy), or falls back to heuristic analysis of the top 15% of pages.
Identify and remove footers.
Uses spec-compliant /Artifact tags when available (100% accuracy), or falls back to heuristic analysis of the bottom 15% of pages.
Sourcepub fn remove_artifacts(&self, threshold: f32) -> Result<usize>
pub fn remove_artifacts(&self, threshold: f32) -> Result<usize>
Identify and remove both headers and footers.
Prioritizes ISO 32000 spec-compliant /Artifact tags, with a heuristic fallback for untagged PDFs.
§Arguments
threshold- Fraction of pages (0.0-1.0) where text must repeat to be removed (heuristic mode only).
Sourcepub fn erase_header(&self, page_index: usize) -> Result<()>
pub fn erase_header(&self, page_index: usize) -> Result<()>
Erase existing header content.
Identifies existing text in the header area (top 15%) and marks it for erasure.
Sourcepub fn edit_header(&self, page_index: usize) -> Result<()>
👎Deprecated: use erase_header instead
pub fn edit_header(&self, page_index: usize) -> Result<()>
use erase_header instead
Deprecated: Use erase_header instead.
Erase existing footer content.
Identifies existing text in the footer area (bottom 15%) and marks it for erasure.
👎Deprecated: use erase_footer instead
use erase_footer instead
Deprecated: Use erase_footer instead.
Sourcepub fn erase_artifacts(&self, page_index: usize) -> Result<()>
pub fn erase_artifacts(&self, page_index: usize) -> Result<()>
Erase both header and footer content.
This is a convenience method that calls both erase_header and erase_footer.
Source§impl PdfDocument
impl PdfDocument
Sourcepub fn page_font_stats(&self, page_index: usize) -> Result<PageFontStats>
pub fn page_font_stats(&self, page_index: usize) -> Result<PageFontStats>
Return per-page font statistics for use in heading detection and layout analysis.
crate::layout::PageFontStats contains:
dominant_em: the mode font size weighted by character count — the body text “1 em”dominant_line_height: median baseline-to-baseline distancedominant_char_width: average character advance widthbody_font_name: name of the most-used font
The primary use-case is heading detection in downstream tools: compare
span.font_size / stats.dominant_em against a threshold (e.g. 1.4×
for H2, 1.8× for H1) to classify large-font spans as headings without
depending on any hardcoded point sizes.
let stats = doc.page_font_stats(0)?;
let spans = doc.extract_spans(0)?;
for span in &spans {
let ratio = span.font_size / stats.dominant_em;
if ratio >= 1.8 { println!("H1: {}", span.text); }
else if ratio >= 1.4 { println!("H2: {}", span.text); }
}Source§impl PdfDocument
impl PdfDocument
Sourcepub fn extract_tables(&self, page_index: usize) -> Result<Vec<Table>>
pub fn extract_tables(&self, page_index: usize) -> Result<Vec<Table>>
Extract tables from a page.
Uses a hybrid spatial algorithm that combines text alignment and vector lines for robust table detection without explicit structure markup.
§Example
let tables = doc.extract_tables(0)?;
for table in tables {
println!("Table with {} rows and {} columns", table.rows.len(), table.col_count);
}Sourcepub fn extract_tables_with_config(
&self,
page_index: usize,
config: TableDetectionConfig,
) -> Result<Vec<Table>>
pub fn extract_tables_with_config( &self, page_index: usize, config: TableDetectionConfig, ) -> Result<Vec<Table>>
Extract tables from a page using a custom configuration.
Sourcepub fn extract_tables_in_rect(
&self,
page_index: usize,
region: Rect,
) -> Result<Vec<Table>>
pub fn extract_tables_in_rect( &self, page_index: usize, region: Rect, ) -> Result<Vec<Table>>
Extract tables from a specific rectangular region of a page.
Sourcepub fn extract_tables_in_rect_with_config(
&self,
page_index: usize,
region: Rect,
config: TableDetectionConfig,
) -> Result<Vec<Table>>
pub fn extract_tables_in_rect_with_config( &self, page_index: usize, region: Rect, config: TableDetectionConfig, ) -> Result<Vec<Table>>
Extract tables from a specific region using custom configuration.
Source§impl PdfDocument
impl PdfDocument
Sourcepub fn extract_text(&self, page_index: usize) -> Result<String>
pub fn extract_text(&self, page_index: usize) -> Result<String>
Circular references and recursion limit errors are handled gracefully with warning messages in the output.
§Example
let text = doc.extract_text(0)?;
println!("Page 1 text: {}", text);§Extract text from a page
xberg-native-pdf exposes three plain-text surfaces with different strengths. Pick by document shape:
extract_text(page)(this method) — glyph-walk assembly with row-aware ordering, inline table rendering, and artifact filtering. The most discoverable default; strongest on single-column prose.to_plain_text(page, opts)/to_plain_text_all(opts)— runs the full pipeline (reading-order strategy incl. XY-cut). Best on multi-column / complex layouts where reading order matters.to_markdown_all(opts)then strip markup — preserves structure (headings, lists, tables) and often scores highest on heavily structured documents; lossiest for pure prose.
No single mode wins on every PDF; when extraction quality is critical
and the layout is unknown, compare to_plain_text_all and
markdown-stripped output and keep whichever is better for your corpus.
Sourcepub fn extract_text_with_options(
&self,
page_index: usize,
options: &ConversionOptions,
) -> Result<String>
pub fn extract_text_with_options( &self, page_index: usize, options: &ConversionOptions, ) -> Result<String>
Extract text from a page with specific options.
Sourcepub fn extract_all_text(&self) -> Result<String>
pub fn extract_all_text(&self) -> Result<String>
Extract text from all pages of the document.
Concatenates text from every page, separated by form feed characters (\x0c).
This is a convenience method equivalent to calling extract_text() for each page.
§Returns
The combined text from all pages.
§Examples
let mut doc = PdfDocument::open("paper.pdf")?;
let all_text = doc.extract_all_text()?;
println!("Full document: {} chars", all_text.len());Source§impl PdfDocument
impl PdfDocument
Sourcepub fn warnings(&self) -> Vec<String>
pub fn warnings(&self) -> Vec<String>
Return all extraction warnings accumulated since this document was opened.
Warnings are recorded when silent fallbacks occur during text extraction
(e.g., missing ToUnicode CMap, font not found, malformed structure tree).
They do NOT consume the warning list — use Self::take_warnings to drain it.
This API makes previously invisible extraction degradations programmatically
observable without requiring callers to hook into the log crate.
Sourcepub fn take_warnings(&self) -> Vec<String>
pub fn take_warnings(&self) -> Vec<String>
Drain and return all accumulated extraction warnings, clearing the list.
After this call, Self::warnings returns an empty Vec until new warnings
are generated. Useful for incremental processing pipelines that want to
inspect warnings on a per-page or per-operation basis.
Sourcepub fn structured_warnings(&self) -> Vec<Warning>
pub fn structured_warnings(&self) -> Vec<Warning>
Return the document’s accumulated structured warnings as a
snapshot. Each entry carries the warning’s
WarningCategory,
page (if applicable), human-readable message, and PDF spec
section reference (when applicable).
Unlike Self::warnings which returns plain strings, this
accessor returns structured records callers can filter, route
to observability dashboards, or assert on in tests without
parsing message text. Pairs with the pyo3_log per-target
default-level downgrade to give Python users a clean stderr
experience plus an opt-in structured surface.
Returns the warnings in insertion order. The vector is
non-destructive: subsequent calls return the same entries
plus any new ones pushed since the last call. Use
Self::take_structured_warnings to drain.
Merges the process-wide GLOBAL_WARNING_SINK (where
free-function log sites like SPEC VIOLATION,
operator-cap-exceeded, and Type0/Type3 font fallbacks push
their structured records) into the per-document sink on each
call. The drain attribution follows the “first caller wins”
rule documented at the global sink — process-wide scope means
the first document to call structured_warnings collects
the global tail that accumulated since the last drain.
Renamed from flatten_warnings in to avoid colliding
with the pre-existing DocumentEditor::flatten_warnings
(which returns the form-flattening side-effect log, a
&[String] — different feature). Both the Rust and Python
(PyDocument) surfaces now agree on structured_warnings.
Sourcepub fn take_structured_warnings(&self) -> Vec<Warning>
pub fn take_structured_warnings(&self) -> Vec<Warning>
Drain and return all accumulated structured warnings.
Companion to Self::structured_warnings.
Sourcepub fn push_structured_warning(&self, warning: Warning)
pub fn push_structured_warning(&self, warning: Warning)
Record a structured warning. Hook called from migrated
tracing::warn! sites that also want to surface the warning as
structured data.
Exposed as pub so external diagnostic sources (custom
extractors, FFI hooks) can also push warnings into the same
sink that Self::structured_warnings surfaces.
Source§impl PdfDocument
impl PdfDocument
Sourcepub fn get_annotations(&self, page_index: usize) -> Result<Vec<Annotation>>
pub fn get_annotations(&self, page_index: usize) -> Result<Vec<Annotation>>
Get all annotations for a specific page.
Returns a list of annotations (comments, highlights, notes, etc.) present on the specified page.
§Arguments
page_index- Zero-based page index
§Returns
Ok(Vec<Annotation>)- List of annotations (may be empty)Err- Error accessing page or annotations
§Example
use xberg_native_pdf::PdfDocument;
let mut doc = PdfDocument::open("sample.pdf")?;
let annotations = doc.get_annotations(0)?;
for annot in annotations {
if let Some(contents) = annot.contents {
println!("Comment: {}", contents);
}
}Source§impl PdfDocument
impl PdfDocument
Sourcepub fn get_outline(&self) -> Result<Option<Vec<OutlineItem>>>
pub fn get_outline(&self) -> Result<Option<Vec<OutlineItem>>>
Get the document outline (bookmarks) if present.
Returns a hierarchical structure of bookmarks that can be used for document navigation.
§Returns
Ok(Some(Vec<OutlineItem>))- Bookmarks found and parsedOk(None)- No bookmarks in documentErr- Error parsing bookmarks
§Example
use xberg_native_pdf::PdfDocument;
let mut doc = PdfDocument::open("sample.pdf")?;
if let Some(outline) = doc.get_outline()? {
for item in outline {
println!("Bookmark: {}", item.title);
}
}Sourcepub fn resolve_named_destination(&self, name: &str) -> Result<Option<usize>>
pub fn resolve_named_destination(&self, name: &str) -> Result<Option<usize>>
Resolve a named destination (PDF 1.1 catalog /Dests dict, or
PDF 1.2+ /Names → /Dests name tree, ISO 32000-1 §12.3.2.3 /
§7.9.6) to a 0-based page index. Ok(None) when the name is
genuinely unresolvable (caller keeps Destination::Named).
Named destinations are referenced by string from Link/GoTo actions and outline items; a consumer that follows those references needs to turn a name into the page it targets.
§Example
use xberg_native_pdf::PdfDocument;
let doc = PdfDocument::open("doc.pdf")?;
if let Some(page) = doc.resolve_named_destination("section.1")? {
println!("named destination -> page {page}");
}Trait Implementations§
Source§impl Debug for PdfDocument
impl Debug for PdfDocument
Source§impl Drop for PdfDocument
impl Drop for PdfDocument
Source§fn drop(&mut self)
fn drop(&mut self)
Emits the one-per-document recovery summary (GH#1547).
Drop rather than an explicit call at the end of extraction: a document is opened once and then extracted from, rendered, and queried through many separate public entry points, so drop is the only point that sees the whole document’s totals exactly once. Silent when nothing was recovered. ~keep