# How book-to-skill Handles Scanned or Image-Only PDFs: Detection and Extraction Guide

> Discover how book-to-skill handles scanned PDFs. Learn about its detection methods and extraction process. Get the guide to managing image-only PDFs effectively.

- Repository: [Virgilio Junior/book-to-skill](https://github.com/virgiliojr94/book-to-skill)
- Tags: how-to-guide
- Published: 2026-08-26

---

**book-to-skill detects scanned PDFs using a fast pre-flight check in `looks_image_only` and deliberately raises an `ExtractionError` directing users to run OCR externally rather than attempting automatic text extraction on image-based pages.**

When processing document collections with [virgiliojr94/book-to-skill](https://github.com/virgiliojr94/book-to-skill), encountering scanned books or image-only PDFs requires special handling. Unlike tools that attempt silent OCR failures, this library implements a deterministic two-stage detection system that fails fast with actionable guidance. Understanding exactly how book-to-skill handles scanned or image-only PDFs helps developers integrate proper preprocessing steps into their document pipelines.

## Two-Stage Detection Architecture

The library identifies image-only documents through a coordinated pipeline spanning two core modules. This architecture separates detection logic from extraction orchestration, ensuring that scanned PDFs are caught early before resource-heavy processing begins.

### Stage 1: Fast Pre-Flight Detection in [`pdf.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/pdf.py)

The `looks_image_only` function in [`book_to_skill/parsers/pdf.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/book_to_skill/parsers/pdf.py) (lines 93-107) performs rapid screening by running `pdftotext` on the first N pages of the document. By default, the system checks the first 5 pages. If the command executes successfully but returns an empty string, the function returns `True`, signaling that the PDF contains no extractable text layers.

This approach intentionally avoids loading entire documents into memory, making the check lightweight even for large scanned books.

### Stage 2: Extraction Guard in [`utils.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/utils.py)

Following detection, the main extraction flow in [`book_to_skill/utils.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/book_to_skill/utils.py) (lines 805-815) evaluates the boolean result. When `looks_image_only` returns `True`, the pipeline immediately raises an `ExtractionError` with specific instructions to apply OCR preprocessing. This guard prevents the fallback extraction chain—Docling → pdftotext → pypdf → pdfminer—from attempting futile text extraction on raster images.

## The `looks_image_only` Implementation

The detection logic relies on external `pdftotext` utility execution to probe for text content. Here is how the function behaves in practice:

```python
from book_to_skill.parsers.pdf import looks_image_only

# Check if PDF requires OCR preprocessing

if looks_image_only("scanned_book.pdf"):
    print("📄 This PDF appears to be image-only; run OCR first.")

```

Internally, the function samples the initial pages (configurable via the default parameter) and examines the stdout of the `pdftotext` command. An empty response indicates the absence of text-based content, triggering the scanned PDF classification.

## Pipeline Integration and Error Handling

When the extraction guard identifies an image-only document, book-to-skill fails explicitly rather than returning empty or partial results. The error handling in [`utils.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/utils.py) constructs a detailed `ExtractionError` message that includes the specific command needed to resolve the issue.

The error output follows this pattern:

```python

# Simplified logic from utils.py extraction flow

if ext == ".pdf":
    print(f"Extracting PDF: {input_str}")
    if looks_image_only(input_str):
        raise ExtractionError(
            f"{input_path.name} looks like a scanned (image-only) PDF …\n"
            "Run OCR on it first, then retry:\n"
            "  ocrmypdf input.pdf output.pdf"
        )
    # ... continues with Docling, pdftotext, pypdf, pdfminer chain

```

As implemented in virgiliojr94/book-to-skill, this behavior ensures users receive immediate, actionable feedback rather than silent data loss.

## Why book-to-skill Does Not Perform OCR Automatically

The library intentionally excludes automatic OCR capabilities to maintain flexibility and reduce dependency overhead. By raising `ExtractionError` defined in [`book_to_skill/exceptions.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/book_to_skill/exceptions.py), the codebase delegates optical character recognition to specialized external tools like `ocrmypdf`. This design choice allows users to:

- Select their preferred OCR engine based on language requirements
- Control preprocessing quality settings independently
- Avoid bloating the installation with heavy OCR dependencies

After applying external OCR (e.g., `ocrmypdf input.pdf output.pdf`), the processed file passes through the standard extraction pipeline successfully.

## Testing the Scanned PDF Detection

The test suite in [`tests/test_book_to_skill.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/tests/test_book_to_skill.py) validates this early-fail behavior, confirming that image-only PDFs trigger the appropriate exception path with OCR hints. This ensures the detection mechanism remains reliable across updates to the underlying PDF parsing dependencies.

## Summary

- **book-to-skill** detects scanned PDFs by probing the first 5 pages with `pdftotext` via the `looks_image_only` function in [`book_to_skill/parsers/pdf.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/book_to_skill/parsers/pdf.py).
- Image-only documents trigger an immediate `ExtractionError` in [`book_to_skill/utils.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/book_to_skill/utils.py) (lines 805-815) before resource-intensive extraction begins.
- The library **does not perform automatic OCR**; instead, it provides explicit command-line instructions for external tools like `ocrmypdf`.
- After OCR preprocessing, the standard extraction chain (Docling → pdftotext → pypdf → pdfminer) processes the document normally.
- The `ExtractionError` class in [`book_to_skill/exceptions.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/book_to_skill/exceptions.py) standardizes error messaging for unscanned document workflows.

## Frequently Asked Questions

### Does book-to-skill automatically perform OCR on scanned PDFs?

No. According to the source code in [`book_to_skill/utils.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/book_to_skill/utils.py), the library deliberately raises an `ExtractionError` when encountering image-only PDFs rather than attempting automatic OCR. Users must preprocess documents using external tools like `ocrmypdf` before retrying extraction.

### How does book-to-skill detect if a PDF is image-only?

The detection occurs in [`book_to_skill/parsers/pdf.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/book_to_skill/parsers/pdf.py) (lines 93-107) through the `looks_image_only` function. This helper runs `pdftotext` on the first 5 pages of the document; if the command returns an empty string, the PDF is classified as scanned or image-only.

### What error message does book-to-skill display for scanned PDFs?

When `looks_image_only` returns `True`, the extraction pipeline raises an `ExtractionError` with a message instructing users to run OCR first, specifically suggesting: `ocrmypdf input.pdf output.pdf`. This appears in the exception handling logic at lines 805-815 of [`book_to_skill/utils.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/book_to_skill/utils.py).

### Can I configure the number of pages checked for image-only detection?

The `looks_image_only` function accepts parameters to adjust the sampling scope, though the default configuration checks the first 5 pages. Developers can modify the function call in [`utils.py`](https://github.com/virgiliojr94/book-to-skill/blob/main/utils.py) or invoke `looks_image_only` directly with custom page limits when implementing custom preprocessing workflows.