Skip to content

fix(extraction): ignore image placeholders in the Mixed OCR retry sample - #471

Open
maximilliangrand wants to merge 1 commit into
firecrawl:mainfrom
maximilliangrand:fix/ocr-retry-image-placeholders
Open

fix(extraction): ignore image placeholders in the Mixed OCR retry sample#471
maximilliangrand wants to merge 1 commit into
firecrawl:mainfrom
maximilliangrand:fix/ocr-retry-image-placeholders

Conversation

@maximilliangrand

@maximilliangrand maximilliangrand commented Aug 30, 2026

Copy link
Copy Markdown

Fixes #466.

Root cause

The Mixed invisible-text (Tr 3) retry in process_document decides whether to fire by building a sample string from the normal extraction pass. That sample kept every item, including the [Image: ...] placeholders synthesized for image XObjects. On a scan whose only visible content is a raster, the placeholders are the whole sample: non-empty and not garbage, so the retry never fires and the document reports no extractable text even though a complete OCR layer sits in the content stream.

The crate already draws this line in non_placeholder_alnum: placeholders mark that pixels exist, not that text was read. The retry decision predates that helper and never got the same filter.

Fix

Exclude ItemType::Image from the retry sample, so the decision is made on text that was actually read. Page-filter behaviour is unchanged.

Test

test_mixed_ocr_retry_ignores_image_placeholders_in_the_sample builds a two page PDF in memory where each page draws a full bleed image and then paints its text at render mode 3, the shape OCRmyPDF output takes. With the fix reverted the test fails on empty markdown; with it applied both pages return their OCR lines.

cargo test is green (1029 unit, 3 bin, 166 integration, 2 doc), cargo fmt --check is clean, and cargo clippy -- -D warnings passes.


Summary by cubic

Fixes #466. The Mixed PDF retry that recovers invisible (Tr 3) OCR text now ignores [Image: ...] placeholders when sampling extracted items, so scanned pages whose only visible content is a raster properly retry and serve their embedded OCR layer instead of reporting no extractable text.

Test

  • Adds test_mixed_ocr_retry_ignores_image_placeholders_in_the_sample, which builds a two-page scan-like PDF with invisible OCR lines and fails if the fix is reverted.

Written for commit f6afb21. Summary will update on new commits.

Review in cubic

The invisible-text (Tr 3) retry for Mixed PDFs decides whether to fire by
sampling the items from the normal extraction pass. That sample counted
`[Image: ...]` placeholders as text, so a scan whose only items are raster
placeholders produced a non-empty, non-garbage sample and the retry never
ran. The document then reported no extractable text even though a complete
OCR layer was sitting in the content stream.

Filter `ItemType::Image` out of the sample, matching the rule
`non_placeholder_alnum` already applies: placeholders mark that pixels
exist, not that text was read.

fixes firecrawl#466

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 2 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/lib.rs">

<violation number="1" location="src/lib.rs:4186">
P2: With image placeholders excluded, the retry now also fires on Mixed pages that are image-only but carry no invisible (Tr 3) OCR layer, paying a second full-document content-stream parse for no recoverable text. This widens the trigger into the image-only-scan-without-OCR-layer case that was previously gated out (and is explicitly avoided in `extract_text_in_regions_mem`). Gate the retry on whether the visible pass actually suppressed invisible text, so placeholder-only pages without an OCR layer keep the single-pass path.</violation>
</file>

Shadow auto-approve: would not auto-approve because issues were found.

Fix all with cubic | Re-trigger cubic

Comment thread src/lib.rs
.page_filter
.as_ref()
.is_none_or(|filter| filter.contains(&item.page))
!matches!(item.item_type, types::ItemType::Image)

@cubic-dev-ai cubic-dev-ai Bot Aug 30, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: With image placeholders excluded, the retry now also fires on Mixed pages that are image-only but carry no invisible (Tr 3) OCR layer, paying a second full-document content-stream parse for no recoverable text. This widens the trigger into the image-only-scan-without-OCR-layer case that was previously gated out (and is explicitly avoided in extract_text_in_regions_mem). Gate the retry on whether the visible pass actually suppressed invisible text, so placeholder-only pages without an OCR layer keep the single-pass path.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/lib.rs, line 4186:

<comment>With image placeholders excluded, the retry now also fires on Mixed pages that are image-only but carry no invisible (Tr 3) OCR layer, paying a second full-document content-stream parse for no recoverable text. This widens the trigger into the image-only-scan-without-OCR-layer case that was previously gated out (and is explicitly avoided in `extract_text_in_regions_mem`). Gate the retry on whether the visible pass actually suppressed invisible text, so placeholder-only pages without an OCR layer keep the single-pass path.</comment>

<file context>
@@ -4172,15 +4172,22 @@ fn process_document(
-                            .page_filter
-                            .as_ref()
-                            .is_none_or(|filter| filter.contains(&item.page))
+                        !matches!(item.item_type, types::ItemType::Image)
+                            && options
+                                .page_filter
</file context>
Fix with cubic
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant