# ExPdfInspector

[![Hex.pm](https://img.shields.io/hexpm/v/ex_pdf_inspector.svg)](https://hex.pm/packages/ex_pdf_inspector)
[![HexDocs](https://img.shields.io/badge/HexDocs-documentation-blue.svg)](https://hexdocs.pm/ex_pdf_inspector)

Elixir bindings for [Firecrawl's `pdf-inspector`](https://github.com/firecrawl/pdf-inspector),
implemented as a Rust NIF with [Rustler](https://github.com/rusterlium/rustler).

ExPdfInspector classifies PDFs as text-based, scanned, image-based, or mixed;
identifies the pages that should be sent to OCR; extracts text and positional
metadata; and converts native PDF text to Markdown. Processing is local: the
library does not upload documents or call an external service, and it does not
perform OCR itself.

## Features

- Process a PDF from a file path or an in-memory binary.
- Classify documents and return a confidence score.
- Identify individual pages that need OCR and explain why.
- Extract plain text or positioned text items with font and style metadata.
- Produce Markdown for a whole document or selected pages.
- Configure detection, extraction, layout analysis, and Markdown generation.
- Handle password-protected PDFs when a password is supplied.

## Installation

Add `ex_pdf_inspector` to your dependencies:

```elixir
def deps do
  [
    {:ex_pdf_inspector, "~> 0.2.0"}
  ]
end
```

The NIF is compiled when the dependency is built. A working Rust toolchain with
Cargo must therefore be available in the build environment. Install one from
[rustup.rs](https://rustup.rs/) if necessary, then run:

```console
mix deps.get
mix compile
```

## Quick start

Process a file and use native extraction when possible:

```elixir
case ExPdfInspector.process_pdf("document.pdf") do
  {:ok, %{pdf_type: :text_based, markdown: markdown}} ->
    markdown

  {:ok, %{pages_needing_ocr: pages}} ->
    {:send_to_ocr, pages}

  {:error, %{code: code, message: message}} ->
    {:error, {code, message}}
end
```

The same operation accepts PDF bytes:

```elixir
pdf = File.read!("document.pdf")
{:ok, result} = ExPdfInspector.process_pdf_bytes(pdf)
```

Detection can be run without Markdown extraction:

```elixir
{:ok, result} = ExPdfInspector.detect_pdf("document.pdf")

result.pdf_type
#=> :text_based
```

Customize the processing pipeline with option structs:

```elixir
alias ExPdfInspector.{DetectionOptions, MarkdownOptions, PdfOptions}

options = %PdfOptions{
  mode: :full,
  page_filter: [0, 2],
  detection: %DetectionOptions{strategy: :full},
  markdown: %MarkdownOptions{profile: :compact}
}

{:ok, result} = ExPdfInspector.process_pdf("document.pdf", options)
```

Page indexes in `page_filter` and the page-extraction functions are zero-based.
The `pages_needing_ocr` field returned by the underlying library uses human-readable
page numbers. See the [Getting Started](docs/getting-started.md) and
[Processing and Options](docs/processing-and-options.md) guides for the complete
workflow and option reference.

## Errors

Functions that read or process PDFs return `{:ok, value}` or
`{:error, %{code: code, message: message}}`. Codes are stable atoms defined by
the library (`:io_error`, `:not_a_pdf`, `:invalid_structure`, `:parse_error`, and
`:encrypted`), making them safe and convenient for pattern matching. Error
messages remain strings. For example:

```elixir
case ExPdfInspector.process_pdf(path) do
  {:error, %{code: :encrypted}} -> {:error, :password_required}
  other -> other
end
```

Invalid Elixir argument types still raise an exception at the NIF boundary.

## Safety and scheduling

PDF work runs on BEAM dirty CPU schedulers so it does not block regular
schedulers. As with any native dependency, a defect in NIF code can affect the
entire VM; validate untrusted input and apply resource limits appropriate to your
application.

## Documentation

The complete API reference is available on
[HexDocs](https://hexdocs.pm/ex_pdf_inspector). Documentation for the Rust library
is maintained by Firecrawl in the
[`pdf-inspector` repository](https://github.com/firecrawl/pdf-inspector).

## Acknowledgements

This package is an independent Elixir NIF wrapper around the
[`pdf-inspector`](https://crates.io/crates/pdf-inspector) crate created and
maintained by [Firecrawl](https://github.com/firecrawl). Firecrawl owns the
original Rust implementation and deserves credit for the PDF inspection and
extraction engine. ExPdfInspector is not an official Firecrawl package.

## License

ExPdfInspector is released under the [MIT License](LICENSE). The upstream
`pdf-inspector` project is also distributed under the MIT License; consult the
upstream project for its copyright and licensing details.
