firecrawl / firecrawl/pdf-inspector
[Image: …] placeholder items defeat the invisible-text retry decision: sample and density count treat pixels as text
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 19.2k
- Forks
- 1.3k
- Avg merge
- 9h 21m
- Merged PRs (30d)
- 51
Description
Summary
The invisible-text (Tr 3) retry in process_document decides whether to fire by sampling the items from the normal extraction pass. That sample — and the sparse-extraction heuristic proposed in #385 — counts [Image: RXX] placeholder items as if they were text. On scanned pages the placeholders are often the only items, so the sample is non-empty and non-garbage, and the retry that would have recovered the OCR layer never fires.
Root cause
In the retry decision (currently src/lib.rs ~4175, Mixed branch):
let sample: String = items
.iter()
.filter(|item| /* page filter */)
.take(200)
.map(|item| item.text.as_str())
.collect();
if is_garbage_text(&sample) || sample.trim().is_empty() { /* retry */ }
items includes ItemType::Image placeholders, whose synthesized text ([Image: R15]) is ~60% alphanumeric. A page with a raster image and an invisible OCR layer therefore produces a sample like [Image: R15][Image: R16]… that passes is_garbage_text and is not empty — the retry is skipped and the document reports "no extractable text". The same bias affects any item-count density floor (as suggested in #385): five images on one page already satisfy items.len() >= 5 * page_count.
Elsewhere the crate already has this rule: non_placeholder_alnum() (src/lib.rs ~1000) explicitly excludes ItemType::Image "they mark that pixels exist, not that text was read". The retry decision predates that helper and never got the same filter.
Fix
Filter placeholders out of both the sample and any density count:
.filter(|item| !matches!(item.item_type, types::ItemType::Image))
Measured impact
On a private corpus of 4,114 real-world PDFs (Dutch/German administrative documents, OCRmyPDF-processed scans): 96 documents fail extraction on v1.14.2. Adding this filter (together with the retry widening from #385) recovers 13 of them that fail purely because of placeholder bias; no output changed for any previously-succeeding document.
Related: #385 (retry never fires for dense TextBased OCR layers — same code path, independent defect).
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in src/lib.rs around process_document's Mixed-branch retry decision and compare its filtering with non_placeholder_alnum(). Trace the sample and any item-count density logic, then verify that ItemType::Image placeholders are excluded from both; done means placeholder-only scanned pages can trigger the OCR retry without changing previously successful extraction.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 82/100