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

> Easily extract text from specific PDF regions using pdf-inspector's extract_text_in_regions_mem function. Get precise text extraction without rendering, with OCR fallback for quality.

- Repository: [Firecrawl/pdf-inspector](https://github.com/firecrawl/pdf-inspector)
- Tags: how-to-guide
- Published: 2026-08-10

---

**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`](https://github.com/firecrawl/pdf-inspector/blob/main/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 `TextItem`s 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:

```rust
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`](https://github.com/firecrawl/pdf-inspector/blob/main/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.

```rust
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`](https://github.com/firecrawl/pdf-inspector/blob/main/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`](https://github.com/firecrawl/pdf-inspector/blob/main/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`](https://github.com/firecrawl/pdf-inspector/blob/main/src/extractor/content_stream.rs)** – `extract_page_text_items` parses PDF content streams, returning positioned `TextItem`s with font and transformation matrix information

- **[`src/text_utils.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/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`](https://github.com/firecrawl/pdf-inspector/blob/main/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`](https://github.com/firecrawl/pdf-inspector/blob/main/src/lib.rs) with supporting modules in [`src/extractor/content_stream.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/extractor/content_stream.rs), [`src/text_utils.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/text_utils.rs), and [`src/tables/mod.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/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`](https://github.com/firecrawl/pdf-inspector/blob/main/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.