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

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 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 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; 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:
  7. Markdown Conversion – tables::table_to_markdown in 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.

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 exposes the same functionality to JavaScript through the extract_tables_in_regions function, maintaining identical parameter signatures.

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

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.

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 →