How to Extract Text from Specific Bounding Box Regions in PDF Pages with pdf‑inspector

The pdf-inspector library provides extract_text_in_regions_mem to extract native PDF text from arbitrary rectangular regions without page rendering, falling back to OCR only when text quality checks fail.

When you need precision text extraction from specific areas of a PDF—such as headers, footers, or isolated table cells—pdf-inspector offers a low‑level Rust API that operates directly on PDF text objects. This approach, implemented in firecrawl/pdf-inspector, avoids the performance cost of full‑page rasterization and gives you fine‑grained control over which regions to process.

Overview of the Region Extraction API

The entry point for bounding‑box extraction is extract_text_in_regions_mem in src/lib.rs. This function accepts raw PDF bytes and a structured list of page‑region pairs, returning extracted text with automatic quality validation.

The workflow follows these stages:

  • Single document load – parse the PDF once with load_document_from_mem
  • Fast font caching – build minimal ToUnicode maps via FontCMaps::from_doc_pages_fast, skipping expensive TrueType fallbacks
  • Text item extraction – retrieve raw TextItems per page through extract_page_text_items
  • Region assignment – calculate overlap between text items and user‑defined bounding boxes
  • Quality gating – flag regions needing OCR if decoding issues, CID garbage, or encoding problems are detected
  • Text assembly – concatenate matched items with adaptive spacing into final strings

Defining Bounding Boxes for Extraction

Regions are specified in PDF points with a top‑left origin coordinate system. Each entry in the request list contains:

  • page_number_0indexed – zero‑based page index (internally converted to lopdf's 1‑indexed format)
  • Vec<[x1, y1, x2, y2]> – array of rectangles where x1,y1 is the top‑left corner and x2,y2 is the bottom‑right corner

This coordinate scheme aligns directly with layout model outputs, making integration straightforward.

Coordinate System Notes

The API handles page rotation automatically. When a page is rotated 90°, the RegionBounds construction adjusts the coordinate math so your bounding boxes still map correctly to text items.

Basic Usage: Extract Text from Multiple Regions

Here is a complete example demonstrating multi‑page, multi‑region extraction:

use pdf_inspector::{extract_text_in_regions_mem, PdfError};

fn main() -> Result<(), PdfError> {
    // 1️⃣ Read the PDF file into memory.
    let pdf_bytes = std::fs::read("report.pdf")?;

    // 2️⃣ Define the regions you want to extract.
    //    (page, vec of [x1, y1, x2, y2] in PDF points, top‑left origin)
    let regions = vec![
        // Page 0 (the first page) – two separate rectangles
        (0_u32, vec![
            [50.0, 100.0, 300.0, 150.0],   // Header block
            [50.0, 200.0, 300.0, 350.0],   // Body paragraph
        ]),
        // Page 2 – a single rectangle
        (2_u32, vec![
            [30.0, 400.0, 400.0, 500.0],
        ]),
    ];

    // 3️⃣ Call the extractor.
    let results = extract_text_in_regions_mem(&pdf_bytes, &regions)?;

    // 4️⃣ Process the results.
    for page_res in results {
        println!("--- Page {} ---", page_res.page + 1);
        for (i, region) in page_res.regions.iter().enumerate() {
            if region.needs_ocr {
                println!("Region {} needs OCR (reason: {:?})", i, region.ocr_reason);
            } else {
                println!("Region {} extracted text:\n{}", i, region.text);
            }
        }
    }

    Ok(())
}

The extract_text_in_regions_mem function returns a Vec<PageRegionResult>. Each result contains:

  • page – the page index (zero‑based)
  • regions – a Vec<RegionText> with one entry per requested bounding box

Each RegionText provides:

  • text – the extracted string (empty if no text objects overlap the region)
  • needs_ocr – boolean flag indicating reliability concerns
  • ocr_reason – optional description such as "suspected_garbled_text"

Handling Overlapping Regions

The region assignment algorithm uses exclusive assignment by maximum overlap. For each text item, the code calculates overlap area with all candidate regions and assigns the item to exactly one region—the one with the largest overlap.

This prevents duplicate text when bounding boxes overlap. If you need overlapping regions with shared text, make separate API calls.

Quality Detection and OCR Fallback

After collecting text items for a region, pdf-inspector runs the same quality checks used for full‑page extraction (defined in src/text_utils.rs):

Check Function Purpose
Decoding issues region_items_have_decoding_issue Detect font decoding failures
CID garbage is_cid_garbage Identify raw CID values masquerading as text
Encoding problems detect_encoding_issues Catch mojibake and malformed UTF‑16BE

If any check fails, needs_ocr is set to true and ocr_reason is populated. Your application can then route the region to GPU‑based OCR for reliable extraction.

Extracting Structured Tables from Regions

For tabular data extraction, use the companion function extract_tables_in_regions_mem. It follows identical region selection logic but applies table detection heuristics, returning markdown pipe tables when structure is recognized.

use pdf_inspector::{extract_tables_in_regions_mem, PdfError};

fn main() -> Result<(), PdfError> {
    let pdf_bytes = std::fs::read("financials.pdf")?;
    let regions = vec![(0_u32, vec![[72.0, 500.0, 540.0, 720.0]])];

    let table_results = extract_tables_in_regions_mem(&pdf_bytes, &regions)?;

    for page in table_results {
        for region in page.regions {
            if region.needs_ocr {
                println!("Region needs OCR – fall back to image OCR.");
            } else {
                println!("Extracted markdown table:\n{}", region.text);
            }
        }
    }

    Ok(())
}

The table detection pipeline lives in src/tables/mod.rs and operates on the same TextItem stream used for plain text extraction.

Key Source Files and Implementation Details

Understanding the internal architecture helps debug extraction issues and customize behavior:

  • src/lib.rs – extract_text_in_regions_mem and extract_tables_in_regions_mem implement the public API; coordinates the full workflow from document loading through result assembly

  • src/extractor/content_stream.rs – extract_page_text_items parses PDF content streams, returning positioned TextItems with font and transformation matrix information

  • src/text_utils.rs – quality helpers including region_items_have_decoding_issue for decoding validation and collect_text_from_matched_items for adaptive‑spacing text concatenation

  • src/tables/mod.rs – table detection pipelines that transform region items into structured markdown output

Performance Characteristics

The region‑based approach delivers significant advantages over rasterization‑based extraction:

  • No page rendering – operations stay in vector space, eliminating GPU memory overhead
  • Minimal font processing – FontCMaps::from_doc_pages_fast skips TrueType fallback parsing, building only essential ToUnicode caches
  • Targeted item streaming – only pages with requested regions are processed; text items outside all regions are ignored

Fonts that cannot be decoded produce empty or garbage text, which triggers the OCR flag rather than crashing extraction. This fail‑forward design maintains throughput on documents with corrupted or exotic font encodings.

Summary

  • Use extract_text_in_regions_mem to pull native PDF text from arbitrary rectangular regions without page rendering
  • Specify regions in PDF points with top‑left origin; the API handles rotated pages automatically
  • Each text item is assigned exclusively to the region with maximum overlap area
  • needs_ocr and ocr_reason flag unreliable extractions so you can fall back to GPU OCR
  • Use extract_tables_in_regions_mem for structured table extraction within the same bounding boxes
  • Core logic resides in src/lib.rs with supporting modules in src/extractor/content_stream.rs, src/text_utils.rs, and src/tables/mod.rs

Frequently Asked Questions

How do I convert pixel coordinates from an image to PDF points for region extraction?

PDF points are a physical unit (1/72 inch) independent of image resolution. If you have pixel coordinates from a rendered preview, you need the page's crop box dimensions in points and the image DPI used for rendering. The conversion is: point = pixel × (72 / DPI). For non‑cropped pages, use the MediaBox width/height from the PDF as your reference rectangle.

What happens if a region contains no text objects?

The text field returns an empty string and needs_ocr is set to false. An empty region with needs_ocr == false means the extraction succeeded but found no content—distinguishing it from a region with needs_ocr == true where content was found but deemed unreliable.

Can I extract text from the same region across all pages without listing each page?

The current API requires explicit page numbers. You must build the regions vector with entries for every target page. A common pattern iterates over your page range and clones the coordinate array for each page index before calling extract_text_in_regions_mem.

Why does my region return needs_ocr even when text appears correct?

The quality gates in src/text_utils.rs are conservative. Certain font encodings—especially legacy Type 1 fonts with non‑standard encodings or subset fonts with incomplete ToUnicode maps—can pass visual inspection but fail heuristic checks. Inspect the ocr_reason field: "suspected_garbled_text" suggests encoding issues, while CID‑related reasons indicate raw glyph IDs escaping the parser.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →