# OCR Reason Codes in pdf‑inspector: suspected_garbled_text, scanned, no_text, vector_text Explained

> Understand pdf-inspector OCR reason codes like suspected_garbled_text, scanned, no_text, and vector_text. Learn why your PDF needs OCR for accurate text extraction.

- Repository: [Firecrawl/pdf-inspector](https://github.com/firecrawl/pdf-inspector)
- Tags: deep-dive
- Published: 2026-08-14

---

**The `pdf-inspector` library uses four OCR reason codes—`suspected_garbled_text`, `scanned`, `no_text`, and `vector_text`—to explain why a PDF page requires optical character recognition rather than native text extraction.**

These codes originate from the Rust core library and surface through language bindings to help developers diagnose PDF content issues programmatically. This article breaks down each code's meaning, detection logic, and where to find the implementation in the source code.

---

## Where OCR Reason Codes Are Defined

The four reason codes are declared as public string constants in [`src/lib.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/lib.rs), which serves as the entry point for the `pdf-inspector` crate. These constants ensure consistent naming across the Rust core, Python bindings, and any future language wrappers.

| Constant | Value | Typical Location in Source |
|----------|-------|---------------------------|
| `OCR_REASON_SUSPECTED_GARBLED_TEXT` | `"suspected_garbled_text"` | [`src/lib.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/lib.rs) (constant definition) |
| `OCR_REASON_SCANNED` | `"scanned"` | [`src/lib.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/lib.rs) (constant definition) |
| `OCR_REASON_NO_TEXT` | `"no_text"` | [`src/lib.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/lib.rs) (constant definition) |
| `OCR_REASON_VECTOR_TEXT` | `"vector_text"` | [`src/lib.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/lib.rs) (constant definition) |

The library exposes these through the `ocr_reasons_by_page` API, which returns a mapping of page numbers to lists of applicable reasons.

---

## suspected_garbled_text: Low-Quality Text Layer Detection

The **`suspected_garbled_text`** reason triggers when the library detects a text layer that exists but appears corrupted or incorrectly encoded.

**Detection logic:** The [`src/text_quality.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/text_quality.rs) module analyzes character frequency distributions and cosine-similarity metrics against expected language patterns. When extracted text contains excessive non-printable characters, mismapped Unicode glyphs, or statistically improbable character sequences, the module flags the page.

**Common causes:**

- PDFs where font encoding tables are broken or missing
- Documents with subsetted fonts that reference incorrect glyph IDs
- Files processed through low-quality OCR engines that produced malformed text layers

**Example Python output:**

```python
{
    2: ["suspected_garbled_text"],
    7: ["suspected_garbled_text", "vector_text"]
}

```

---

## scanned: Image-Only Page Identification

The **`scanned`** reason indicates a page containing only raster image content with no extractable text layer.

**Detection logic:** The [`src/detector.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/detector.rs) module examines page content streams. When a page consists solely of bitmap XObjects (typically JPEG, PNG, or TIFF embedded images) without accompanying text operators, the detector classifies it as scanned.

This is the most straightforward OCR trigger—physical documents converted to PDF through scanning hardware or mobile capture apps typically produce these pages.

**Key distinction from `no_text`:** A scanned page has image content available for OCR, whereas `no_text` pages lack both text and image data.

---

## no_text: Empty Page Fallback

The **`no_text`** reason applies when a page contains neither detectable text nor recognizable image content.

**Detection logic:** The OCR fallback pipeline in [`src/lib.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/lib.rs) activates after both text extraction and image detection return empty results. This occurs for:

- Truly blank pages
- Pages with only vector graphics (charts, diagrams without labels)
- Corrupted content streams that fail to parse

Unlike `scanned`, pages with this reason may not benefit from OCR at all—there is simply nothing to recognize.

---

## vector_text: Outline-Based Text Detection

The **`vector_text`** reason identifies pages where text appears as vector path outlines rather than standard text glyphs.

**Detection logic:** The content stream parser in [`src/lib.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/lib.rs) (and supporting modules) distinguishes between text-show operators (`Tj`, `TJ`) and path construction operators (`m`, `l`, `c`, `re`) that form character shapes. When character-like paths are detected without corresponding text operators, the page receives this classification.

**Why this matters:** Vectorized text is visually readable but not searchable or selectable. This commonly occurs when:

- Scanned documents are processed through "PDF vectorization" tools that trace bitmaps into curves
- Designers convert text to outlines for font embedding compatibility
- CAD or illustration software exports drawings with text as geometry

Pages flagged with `vector_text` often appear alongside `suspected_garbled_text` when vectorization produces distorted or incomplete character shapes.

---

## Accessing OCR Reasons in Code

The Python bindings expose OCR reasons through the `ocr_reasons_by_page` return value. Here's a minimal working example:

```python
from pdf_inspector import inspect_pdf

result = inspect_pdf("document.pdf")

# Check which pages need OCR and why

for page_num, reasons in result["ocr_reasons_by_page"].items():
    print(f"Page {page_num}: requires OCR due to {reasons}")

```

The underlying Rust function populates this map by aggregating flags from [`src/text_quality.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/text_quality.rs), [`src/detector.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/detector.rs), and the core processing pipeline defined in [`src/lib.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/lib.rs).

---

## File Reference Guide

| File | Purpose |
|------|---------|
| [`src/lib.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/lib.rs) | Defines OCR reason constants and orchestrates the inspection pipeline |
| [`src/text_quality.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/text_quality.rs) | Implements statistical text quality analysis for `suspected_garbled_text` |
| [`src/detector.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/detector.rs) | Handles page content type detection for `scanned` classification |
| [`src/python.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/python.rs) | Marshals Rust data structures into Python-compatible dictionaries |
| [`tests/integration_tests.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/tests/integration_tests.rs) | Contains fixtures verifying each OCR reason triggers appropriately |

---

## Summary

- **`suspected_garbled_text`** — Text layer exists but appears corrupted or misencoded; detected via statistical quality metrics in [`src/text_quality.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/text_quality.rs)
- **`scanned`** — Page contains only raster images, no text layer; identified by [`src/detector.rs`](https://github.com/firecrawl/pdf-inspector/blob/main/src/detector.rs)
- **`no_text`** — Page has neither text nor image content; fallback classification when all detection pipelines return empty
- **`vector_text`** — Text rendered as vector outlines rather than glyphs; detected through content stream operator analysis

These codes enable precise diagnostics for PDF processing workflows, letting developers route documents appropriately—whether that means invoking OCR, flagging quality issues, or skipping unrecognizable pages entirely.

---

## Frequently Asked Questions

### How do I suppress certain OCR reason codes from triggering processing?

The `pdf-inspector` library does not currently expose a filter API for specific reason codes. You can post-process the `ocr_reasons_by_page` result to ignore codes that don't match your workflow requirements before deciding whether to invoke OCR.

### Can a single page have multiple OCR reason codes?

Yes. The `ocr_reasons_by_page` structure returns a list of reasons per page. A page with vectorized text that also fails quality checks might return `["suspected_garbled_text", "vector_text"]` simultaneously.

### What's the difference between `scanned` and `vector_text`?

`scanned` indicates raster image content—pixels that require OCR to extract text. `vector_text` indicates text rendered as mathematical paths—curves and lines that appear as text visually but lack searchable character data. The former needs bitmap OCR; the latter may need vector-to-text conversion or specialized handling.

### Which OCR reason is most common in enterprise document pipelines?

Based on typical document distributions, **`scanned`** appears most frequently due to the prevalence of legacy scanned documents and mobile capture workflows. However, **`suspected_garbled_text`** is increasingly common with PDFs from mixed-generation sources where automated conversion tools produce malformed text layers.