How to Get X/Y Position Coordinates for Extracted Text in pdf-inspector
pdf-inspector exposes every text element's exact coordinates through TextItem structs returned by the extract_text_with_positions* family of functions, with x and y values measured in PDF points from the bottom-left origin.
The firecrawl/pdf-inspector repository is a Rust-based PDF parsing library that preserves spatial information during text extraction. Unlike simple text dumpers, it tracks where each glyph appears on the page—essential for layout analysis, OCR verification, and document reconstruction workflows.
Where Coordinates Are Stored: The TextItem Struct
Position data lives in src/types.rs, where the TextItem struct defines the schema for extracted text elements:
pub struct TextItem {
pub text: String,
pub x: f32, // horizontal position in PDF points
pub y: f32, // vertical position in PDF points
pub page: u32, // 1-indexed page number
pub font: Option<String>,
pub size: Option<f32>,
// ... additional metadata
}
Coordinates use the standard PDF coordinate system: origin at the bottom-left corner of the page, with Y increasing upward. One point equals 1/72 of an inch.
The Extraction Pipeline
The core logic resides in src/extractor/mod.rs. The pipeline follows this sequence:
- Document loading —
load_document_from_path_with_passwordorload_document_from_memparses the PDF structure - Font mapping —
FontCMaps::from_docbuilds glyph-to-Unicode conversion tables - Content stream processing —
extract_positioned_text_implwalks text-showing operators (Tj,TJ) - Matrix transformation — the current text matrix converts glyph positions to page-space coordinates
- Struct population —
TextItemfields are filled and collected intoVec<TextItem>
The implementation automatically handles page rotation, scaling, and MediaBox offsets. You receive coordinates in normalized, upright space regardless of how the PDF was constructed.
Public APIs for Position-Aware Extraction
These functions are re-exported from src/lib.rs:
extract_text_with_positions(path)— all pages, file-basedextract_text_with_positions_pages(path, page_filter)— subset of 1-indexed pagesextract_text_with_positions_mem(buffer)— in-memory PDFextract_text_with_positions_mem_pages(buffer, page_filter)— in-memory with page filter
Each returns Result<Vec<TextItem>, PdfError> containing the full position metadata.
Code Examples
Rust: Extracting Coordinates from File
use pdf_inspector::{extract_text_with_positions, TextItem};
fn main() -> Result<(), pdf_inspector::PdfError> {
let items: Vec<TextItem> = extract_text_with_positions("invoice.pdf")?;
for item in items {
println!(
"Page {} | ({:.2}, {:.2}) | \"{}\"",
item.page, item.x, item.y, item.text
);
}
Ok(())
}
Python: Using the PyO3 Bindings
The Python bindings in src/python.rs expose identical functionality through PyTextItem objects:
import pdf_inspector
items = pdf_inspector.extract_text_with_positions("invoice.pdf")
for itm in items:
print(f"Page {itm.page} | ({itm.x:.2f}, {itm.y:.2f}) | \"{itm.text}\"")
Filtering by Specific Pages
For targeted extraction (pages 2 and 4 only):
# Python
items = pdf_inspector.extract_text_with_positions("document.pdf", pages=[2, 4])
// Rust
use std::collections::HashSet;
let mut pages = HashSet::new();
pages.insert(2);
pages.insert(4);
let items = extract_text_with_positions_pages("document.pdf", Some(&pages))?;
Working with PDF Coordinates
Unit Conversions
PDF points are the native unit. Convert as needed:
let inches = item.x / 72.0;
let centimeters = item.x / 72.0 * 2.54;
Coordinate Transformations
For top-down coordinate systems (common in UI frameworks), flip the Y axis:
// Assuming standard US Letter page (792 pt height)
let page_height = 792.0;
let y_from_top = page_height - item.y;
Relying on MediaBox dimensions from the PDF itself is more robust than hardcoded values.
Performance Characteristics
Position-aware extraction incurs minimal overhead. The coordinate calculations piggyback on matrix operations already performed during text rendering. Use the *_mem variants when processing PDFs from network streams or databases to eliminate redundant I/O.
Summary
- Data structure:
TextIteminsrc/types.rsholdsx: f32andy: f32fields - Entry points:
extract_text_with_positions*functions insrc/extractor/mod.rs - Coordinate system: PDF points, bottom-left origin, pre-normalized for rotation
- Language support: Native Rust API with equivalent Python bindings in
src/python.rs - Page filtering:
*_pagesvariants acceptHashSet<u32>(Rust) orlist[int](Python)
Frequently Asked Questions
What units are the X and Y coordinates in?
PDF points, where 1 point = 1/72 inch ≈ 0.3528 mm. These are the native units of the PDF specification and require no transformation for accurate spatial calculations.
Do I need to handle page rotation manually?
No. The extraction pipeline in src/extractor/mod.rs applies the page's rotation matrix before populating TextItem. The coordinates you receive are always in upright, readable orientation regardless of how the PDF was authored.
Can I get coordinates for a specific region of a page?
pdf-inspector returns all text items with their positions. Apply bounding-box filtering in your consumer code:
items = pdf_inspector.extract_text_with_positions("doc.pdf")
region_items = [i for i in items if 100 < i.x < 300 and 500 < i.y < 700]
Built-in region filtering is not exposed in the current API surface.
Are the coordinates precise enough for exact glyph placement?
Yes. The values derive from the PDF content stream's text matrices, which define glyph positioning at the specification level. Sub-point precision is preserved through the f32 fields.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →