Skip to content
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,12 @@ wasm/ — Browser bindings (wasm-bindgen)

This detects 300+ page PDFs in milliseconds. The result includes `pages_needing_ocr` — a list of specific page numbers that lack text, enabling per-page OCR routing instead of all-or-nothing.

Fast `detect_pdf*` and `classify_pdf*` calls inspect structural signals only;
they do not extract text or validate font/character encoding. Therefore,
`pages_needing_ocr = []` is not a text-quality verdict. Use
`ProcessMode::Analyze`, `process_pdf*`, or `extract_pages_markdown*` when OCR
routing must also catch broken or garbled text encodings.

### Scan strategies

| Strategy | Behavior | Best for |
Expand Down
17 changes: 12 additions & 5 deletions docs/python.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,13 @@ headings = [
]
```

`detect_pdf*` and `classify_pdf*` are fast structural classifiers. They do not
extract text or validate font/character encoding, so
`pages_needing_ocr == []` is not a text-quality verdict. For OCR routing that
includes broken or garbled encodings, use `extract_pages_markdown*` or the full
`process_pdf*` APIs. Use `process_pdf_with_ocr*` when those routing decisions
should also run selective OCR.

## API reference

| Function | Description |
Expand All @@ -123,10 +130,10 @@ headings = [
| `process_pdf_bytes(data, pages=None)` | Full processing from bytes |
| `process_pdf_with_ocr(path, **options)` | Native extraction + selective OCR with provenance |
| `process_pdf_with_ocr_bytes(data, **options)` | Native extraction + selective OCR from bytes |
| `detect_pdf(path)` | Fast detection only (returns PdfResult) |
| `detect_pdf_bytes(data)` | Fast detection from bytes |
| `classify_pdf(path)` | Lightweight classification (returns PdfClassification) |
| `classify_pdf_bytes(data)` | Lightweight classification from bytes |
| `detect_pdf(path)` | Fast structural detection only; no text-quality analysis (returns PdfResult) |
| `detect_pdf_bytes(data)` | Fast structural detection from bytes; no text-quality analysis |
| `classify_pdf(path)` | Lightweight structural classification; no text-quality analysis (returns PdfClassification) |
| `classify_pdf_bytes(data)` | Lightweight structural classification from bytes; no text-quality analysis |
| `extract_text(path)` | Plain text extraction |
| `extract_text_bytes(data)` | Plain text extraction from bytes |
| `extract_text_with_positions(path, pages=None)` | Text with X/Y coords and font info |
Expand Down Expand Up @@ -203,7 +210,7 @@ class OcrPdfResult: # process_pdf_with_ocr / bytes
class PdfClassification: # classify_pdf
pdf_type: str
page_count: int
pages_needing_ocr: list[int] # 0-indexed
pages_needing_ocr: list[int] # 0-indexed structural signals only
confidence: float

class TextItem: # extract_text_with_positions
Expand Down
15 changes: 11 additions & 4 deletions docs/rust-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,12 @@ match info.pdf_type {
}
```

`detect_pdf*` and `classify_pdf_mem` only inspect structural signals. They do
not extract text or validate font/character encoding, so an empty
`pages_needing_ocr` is not a text-quality verdict. Use `ProcessMode::Analyze`
to run extraction and text-quality routing without generating Markdown, or use
`extract_pages_markdown*` when per-page Markdown is also needed.

Customize processing with `PdfOptions`:

```rust
Expand Down Expand Up @@ -492,18 +498,19 @@ for item in extract_text_with_positions("tagged.pdf")? {
| Mode | What it does | Returns |
|---|---|---|
| `ProcessMode::Full` (default) | Detect + extract + convert to Markdown | Everything populated |
| `ProcessMode::Analyze` | Detect + extract + layout analysis (no Markdown) | `markdown` is `None`, `layout` is populated |
| `ProcessMode::DetectOnly` | Classification only (fastest) | `markdown` is `None`, `layout` is default |
| `ProcessMode::Analyze` | Detect + extract + text-quality/layout analysis (no Markdown) | `markdown` is `None`, `layout` and encoding-quality OCR routing are populated |
| `ProcessMode::DetectOnly` | Structural classification only (fastest; no text-quality analysis) | `markdown` is `None`, `layout` is default |

## Functions

| Function | Description |
|---|---|
| `process_pdf(path)` | Full processing with defaults |
| `detect_pdf(path)` | Fast metadata-only detection (no extraction) |
| `detect_pdf(path)` | Fast structural detection (no extraction or text-quality analysis) |
| `process_pdf_with_options(path, options)` | Process with custom `PdfOptions` |
| `process_pdf_mem(bytes)` | Full processing from a byte buffer |
| `detect_pdf_mem(bytes)` | Fast detection from a byte buffer |
| `detect_pdf_mem(bytes)` | Fast structural detection from a byte buffer |
| `classify_pdf_mem(bytes)` | Lightweight structural classification; 0-indexed OCR pages, no text-quality analysis |
| `process_pdf_mem_with_options(bytes, options)` | Process from bytes with custom options |
| `extract_text(path)` | Plain text extraction |
| `extract_text_with_positions(path)` | Text with X/Y coordinates and font info |
Expand Down
14 changes: 10 additions & 4 deletions pdf_inspector.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,18 @@ class OcrPdfResult:
ocr_time_ms: int

class PdfClassification:
"""Lightweight PDF classification result."""
"""Lightweight structural PDF classification result.

This result does not include extracted-text encoding-quality analysis.
"""
pdf_type: str
"""'text_based', 'scanned', 'image_based', or 'mixed'."""
page_count: int
pages_needing_ocr: list[int]
"""0-indexed page numbers that need OCR."""
"""0-indexed pages with structural OCR signals.

An empty list does not assert that extracted text has a valid encoding.
"""
confidence: float

class TextItem:
Expand Down Expand Up @@ -204,11 +210,11 @@ def detect_pdf_bytes(data: bytes) -> PdfResult:
...

def classify_pdf(path: str) -> PdfClassification:
"""Lightweight classification — type, page count, and OCR pages (0-indexed)."""
"""Lightweight structural classification without text-quality analysis."""
...

def classify_pdf_bytes(data: bytes) -> PdfClassification:
"""Lightweight classification from bytes."""
"""Lightweight structural classification without text-quality analysis."""
...

def extract_text(path: str) -> str:
Expand Down
Loading