Skip to main content

PdfDocument

Struct PdfDocument 

Source
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

Source

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

Source

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 /Catalog object 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()?;
Source

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");
}
Source

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.

Source

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.

Source

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.

Source

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.

Source

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

Source

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 index
  • excluded_layers - OCG layer names to suppress (empty = no layer filtering)
  • excluded_inks - Separation/DeviceN ink names to suppress (empty = no ink filtering)
Source

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).

Source

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 index
  • reading_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);
}
Source

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 index
  • reading_order - The reading order strategy to apply
  • excluded_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.

Source

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());
Source

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);
}
Source

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.

Source

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 index
  • reading_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);
}
Source

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 index
  • config - 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)?;
Source

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 TextChar objects 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.

Source

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.

Source

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.

Source

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.

Source

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 index
  • excluded_layers - OCG layer names to suppress (empty = no layer filtering)
  • excluded_inks - Separation/DeviceN ink names to suppress (empty = no ink filtering)
Source

pub fn extract_words(&self, page_index: usize) -> Result<Vec<Word>>

Extract words from a page.

Groups characters into words based on spatial proximity. Uses adaptive thresholds based on the document’s font size and spacing.

§Example
ⓘ
let words = doc.extract_words(0)?;
for word in words {
    println!("Word: {} at {:?}", word.text, word.bbox);
}
Source

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.

Source

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.

Source

pub fn extract_text_lines(&self, page_index: usize) -> Result<Vec<TextLine>>

Extract text lines from a page.

Groups words into lines based on vertical proximity.

§Example
ⓘ
let lines = doc.extract_text_lines(0)?;
for line in lines {
    println!("Line: {} at {:?}", line.text, line.bbox);
}
Source

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 index
  • word_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)?;
Source

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.

Source

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
}
Source

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

pub fn get_page_content_data(&self, page_index: usize) -> Result<Vec<u8>>

Get the raw content stream data for a page.

This returns the decoded content stream bytes for the specified page. The content stream contains PDF operators that define the page’s appearance.

Source§

impl PdfDocument

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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

Source

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());
}
Source

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 Do operators (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))?;
}
Source

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.

Source

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<_, _>>()?;
Source

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>>

Available on non-WebAssembly only.

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.

Source

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.

Source

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

Source

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);
Source

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");
    }
}
Source

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).

Source

pub fn load_object(&self, obj_ref: ObjectRef) -> Result<Object>

Load an object by its reference.

This function:

  1. Checks the object cache first
  2. If not cached, looks up the byte offset in the xref table
  3. Seeks to that offset and parses the object
  4. Caches the result for future access
  5. 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)?;
Source

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 within
  • max_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)?;
Source

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

Source

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.

Source

pub fn open_from_bytes(data: Vec<u8>) -> Result<Self>

👎Deprecated since 0.3.15:

Use from_bytes instead

Deprecated alias for from_bytes.

Source

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")?;
Source

pub fn open_with_config( path: impl AsRef<Path>, _config: impl Any, ) -> Result<Self>

Available on non-WebAssembly only.

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.

Source

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).

Source

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");
}
Source

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.

Source

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.

Source

pub fn document_creator(&self) -> Option<String>

Document Info dictionary /Creator (decoded, trimmed), if present and non-empty. See document_producer.

Source

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.

Source

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).

Source

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

Source

pub fn page_count(&self) -> Result<usize>

Get the number of pages in the document.

This function:

  1. Loads the catalog (root object)
  2. Follows the /Pages reference to the page tree root
  3. 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);
Source

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.

Source

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.

Source

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.

Source

pub fn page_count_u32(&self) -> u32

👎Deprecated since 0.1.0:

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).

Source

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

pub fn get_page(&self, page_index: usize) -> Result<Object>

Get a page object by index (0-based).

§Arguments
  • page_index - Zero-based page index
§Returns

The page dictionary object.

§Errors

Returns an error if the page index is out of bounds or if the page tree structure is invalid.

Source§

impl PdfDocument

Source

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");
    }
}
Source

pub fn extract_rects(&self, page_index: usize) -> Result<Vec<PathContent>>

Extract rectangles from a page.

Identifies paths that form axis-aligned rectangles.

Source

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.

Source

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 index
  • region - 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

Source

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.

Source

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.

Source

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.

Source

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);
}

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.

Source

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

Source

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.

Source

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.

Source

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.

Source

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.

Source

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:

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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

Source

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.

Source

pub fn clear_erase_regions(&self, page_index: usize) -> Result<()>

Clear all erase regions for a page.

Source

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.

Source

pub fn remove_footers(&self, threshold: f32) -> Result<usize>

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.

Source

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).
Source

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.

Source

pub fn edit_header(&self, page_index: usize) -> Result<()>

👎Deprecated:

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

Deprecated: Use erase_footer instead.

Source

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

Source

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 distance
  • dominant_char_width: average character advance width
  • body_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

Source

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);
}
Source

pub fn extract_tables_with_config( &self, page_index: usize, config: TableDetectionConfig, ) -> Result<Vec<Table>>

Extract tables from a page using a custom configuration.

Source

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.

Source

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

Source

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.

Source

pub fn extract_text_with_options( &self, page_index: usize, options: &ConversionOptions, ) -> Result<String>

Extract text from a page with specific options.

Source

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

Source

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.

Source

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.

Source

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.

Source

pub fn take_structured_warnings(&self) -> Vec<Warning>

Drain and return all accumulated structured warnings. Companion to Self::structured_warnings.

Source

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

Source

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

Source

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 parsed
  • Ok(None) - No bookmarks in document
  • Err - 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);
    }
}
Source

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

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Drop for PdfDocument

Source§

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

Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> ErasedDestructor for T
where T: 'static,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more