# How to Extract Tables from Specific Regions of a PDF Page Using pdf-inspector

> Extract tables from specific PDF regions using pdf-inspector. Learn how to precisely target areas on a page and get structured Markdown output for each bounding box.

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

---

**pdf-inspector enables precise table extraction from user-defined rectangular regions on PDF pages through the `extract_tables_in_regions_mem` Rust function and its Node.js N-API wrapper, returning structured Markdown tables for each bounding box.**

The pdf-inspector library from Firecrawl provides a high-level API for targeted table extraction, allowing developers to isolate specific areas of a PDF page rather than processing the entire document. This region-aware approach is implemented in Rust with bindings for Node.js, making it ideal for extracting tables from complex layouts or multi-table pages without interference from decorative graphics or adjacent tables.

## How Region-Aware Table Extraction Works

The extraction pipeline is deliberately designed to operate only on the subset of content that falls inside caller-supplied bounding boxes. This architecture makes it possible to extract a single table from a multi-table page or ignore decorative elements outside your area of interest.

The process follows eight distinct stages:

1. **PDF Validation** – The loader at [`src/lib.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/lib.rs) validates the byte stream and builds a `lopdf::Document` to guarantee the file is a valid PDF before extraction begins.
2. **Font Mapping** – `FontCMaps::from_doc_pages_fast` pre-loads Unicode mappings only for the pages you request, keeping memory usage low and operations fast.
3. **Content Stream Parsing** – The extractor in [`src/extractor/content_stream.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/extractor/content_stream.rs) walks the page's content streams, decodes text operators, and returns three collections per page: `TextItem`, `PdfRect`, and `PdfLine`.
4. **Region Filtering** – Functions like `region_overlaps_item`, `region_overlaps_rect`, and `region_overlaps_line` filter the collections to keep only items intersecting your supplied bounding box.
5. **Quality Validation** – Garbage-text, CID-garbage, and encoding-issue checks run in [`src/lib.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/lib.rs); if the region appears corrupted, the function returns `needs_ocr = true` so you can fall back to OCR.
6. **Table Detection** – Three strategies execute in order until one produces a plausible table:
   - **Rect-backed detector** ([`src/tables/detect_rects.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/tables/detect_rects.rs)) – First-choice strategy using vector rectangles
   - **Line-grid detector** ([`src/tables/detect_lines.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/tables/detect_lines.rs)) – Second-choice strategy using ruling lines
   - **Heuristic text-only detector** ([`src/tables/detect_heuristic.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/tables/detect_heuristic.rs)) – Fallback when vector data is insufficient
7. **Markdown Conversion** – `tables::table_to_markdown` in [`src/tables/format.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/tables/format.rs) converts the internal `Table` representation into Markdown pipe-table syntax.
8. **Result Assembly** – The function returns a `RegionText` struct containing the `text`, `needs_ocr` boolean, and `ocr_reason` for each region.

## Extracting Tables from Specific Regions in Rust

Use the `extract_tables_in_regions_mem` function from the public crate to process multiple regions across multiple pages in a single call.

```rust
use pdf_inspector::{extract_tables_in_regions_mem, PageRegionResult};

fn main() -> Result<(), pdf_inspector::PdfError> {
    // Load a PDF file into memory
    let pdf_bytes = std::fs::read("report.pdf")?;

    // Define one or more regions: (page_index, Vec<[x1, y1, x2, y2]>)
    // Page index is zero-based. Coordinates are in PDF user space (origin bottom-left).
    let regions = vec![
        (0, vec![[50.0, 500.0, 550.0, 750.0]]), // page 0, a single rectangle
        (2, vec![[100.0, 200.0, 400.0, 600.0], [420.0, 200.0, 720.0, 600.0]]), // page 2, two boxes
    ];

    // Run the extractor
    let results: Vec<PageRegionResult> = extract_tables_in_regions_mem(&pdf_bytes, &regions)?;

    // Iterate over the results
    for page_res in results {
        for (idx, region) in page_res.regions.iter().enumerate() {
            if region.needs_ocr {
                eprintln!("Region {} on page {} needs OCR", idx, page_res.page);
            } else {
                println!("--- Table from page {} region {} ---\n{}", page_res.page, idx, region.text);
            }
        }
    }

    Ok(())
}

```

The `regions` parameter accepts a vector where each tuple contains a zero-based page index and a vector of bounding boxes. Each bounding box is defined as `[x1, y1, x2, y2]` in PDF user space coordinates (origin at bottom-left). The function returns a `Vec<PageRegionResult>` where each entry corresponds to a page and contains a list of `RegionText` results matching your input order.

## Node.js Implementation Using N-API Bindings

The N-API wrapper defined in [`napi/src/lib.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/napi/src/lib.rs) exposes the same functionality to JavaScript through the `extract_tables_in_regions` function, maintaining identical parameter signatures.

```javascript
const { readFileSync } = require('fs');
const { extract_tables_in_regions } = require('pdf-inspector'); // npm package

(async () => {
  const pdf = readFileSync('report.pdf');

  // Regions follow the same convention as the Rust API.
  const regions = [
    [0, [[50, 500, 550, 750]]],
    [2, [[100, 200, 400, 600], [420, 200, 720, 600]]],
  ];

  try {
    const results = await extract_tables_in_regions(pdf, regions);
    results.forEach(page => {
      page.regions.forEach((r, i) => {
        if (r.needs_ocr) {
          console.warn(`Page ${page.page} region ${i} needs OCR`);
        } else {
          console.log(`--- Table from page ${page.page} region ${i} ---\n${r.text}`);
        }
      });
    });
  } catch (e) {
    console.error('Extraction failed:', e);
  }
})();

```

The JavaScript wrapper forwards directly to `extract_tables_in_regions_mem`, ensuring consistent behavior across languages. Region coordinates use the same PDF user space system, and the returned objects contain identical `needs_ocr` flags and Markdown-formatted text.

## Handling OCR Fallbacks and Quality Guards

When `extract_tables_in_regions_mem` detects encoding issues or garbage text in a region, it sets `needs_ocr` to `true` rather than returning corrupted data. This quality guard, implemented in [`src/lib.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/lib.rs) between lines 1016-1020, allows your application to decide whether to process the Markdown text or route the specific region to a raster OCR pipeline.

Check the `ocr_reason` field in the `RegionText` struct to determine why OCR was recommended—common reasons include CID-garbage detection, font encoding issues, or insufficient text items in the bounding box.

## Core Source Files for Region Extraction

Understanding the source structure helps debug extraction issues or extend functionality:

- **[`src/lib.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/lib.rs)** – Entry point exposing `extract_tables_in_regions_mem` and quality validation logic.
- **[`src/extractor/content_stream.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/extractor/content_stream.rs)** – Parses PDF operators and builds `TextItem`, `PdfRect`, and `PdfLine` collections.
- **[`src/tables/detect_rects.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/tables/detect_rects.rs)** – Rect-based table detector used as the primary strategy.
- **[`src/tables/detect_lines.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/tables/detect_lines.rs)** – Line-grid detector for tables defined by ruling lines.
- **[`src/tables/detect_heuristic.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/tables/detect_heuristic.rs)** – Heuristic text-only fallback when vector graphics are absent.
- **[`src/tables/format.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/tables/format.rs)** – Converts detected `Table` structs to Markdown pipe-table syntax.
- **[`napi/src/lib.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/napi/src/lib.rs)** – N-API bindings at line 405 that bridge Rust and Node.js.

## Summary

- **Region-specific extraction** uses `extract_tables_in_regions_mem` (Rust) or `extract_tables_in_regions` (Node.js) to target exact bounding boxes on PDF pages.
- **Coordinate system** expects PDF user space with origin at bottom-left, using `[x1, y1, x2, y2]` arrays.
- **Three-phase detection** tries rect-based, line-grid, and heuristic strategies in sequence until a valid table is found.
- **Quality assurance** returns `needs_ocr = true` when regions contain corrupted or garbled text, allowing selective OCR fallback.
- **Output format** is Markdown pipe-table text contained in the `text` field of each `RegionText` result.

## Frequently Asked Questions

### What coordinate system does pdf-inspector use for regions?

pdf-inspector uses standard PDF user space coordinates with the origin (0,0) at the bottom-left corner of the page. When defining regions, provide coordinates as `[x1, y1, x2, y2]` where x1,y1 represents the lower-left corner and x2,y2 represents the upper-right corner of your target rectangle. Page indices are zero-based.

### How does pdf-inspector handle tables that span multiple pages?

Each region definition is tied to a specific page index in the `regions` parameter. To extract a table that spans multiple pages, define separate bounding boxes for each page segment. The function returns a `PageRegionResult` for each page containing an array of `RegionText` objects matching the order of your input regions.

### What happens if the specified region contains no table or corrupted text?

If the region contains corrupted encoding or garbage text, `extract_tables_in_regions_mem` sets `needs_ocr` to `true` and provides an `ocr_reason` explaining the failure. If no table structure is detected but the text is clean, you may receive the raw text content or an empty result depending on whether the heuristic detector finds tabular patterns.

### Can I extract multiple tables from different regions on the same page?

Yes. Supply multiple bounding boxes in the vector for a single page index. For example, `(0, vec![[50.0, 500.0, 300.0, 700.0], [320.0, 500.0, 570.0, 700.0]])` extracts two separate tables from page 0. The results array maintains the same order as your input regions, allowing you to correlate outputs with specific bounding boxes.