# Processing and Options

The default call uses `%ExPdfInspector.PdfOptions{}` and runs the complete
pipeline. Build option structs explicitly when you need lower latency, selected
pages, a password, or different Markdown output.

## Processing modes

Set `ExPdfInspector.PdfOptions.mode` to one of:

| Mode | Detection | Layout analysis | Markdown extraction |
| --- | --- | --- | --- |
| `:detect_only` | yes | no | no |
| `:analyze` | yes | yes | no |
| `:full` | yes | yes | yes |

For routing decisions where Markdown is not yet needed:

```elixir
options = %ExPdfInspector.PdfOptions{mode: :detect_only}
{:ok, result} = ExPdfInspector.process_pdf("document.pdf", options)
```

The dedicated `detect_pdf/1` and `detect_pdf_bytes/1` functions are convenient
shortcuts for detection without extraction.

## Detection strategies

`ExPdfInspector.DetectionOptions.strategy` controls which pages are inspected:

| Strategy | Behavior |
| --- | --- |
| `:early_exit` | Stops after enough evidence is available. |
| `:full` | Inspects every page. |
| `{:sample, count}` | Inspects a sample of at most `count` pages. |
| `{:pages, pages}` | Inspects only the specified page indexes. |

The wrapper defaults to `{:sample, 8}`. Use `:full` when accurate classification
of mixed documents matters more than minimizing inspection work:

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

options = %PdfOptions{
  mode: :detect_only,
  detection: %DetectionOptions{
    strategy: :full,
    min_text_ops_per_page: 3,
    text_page_ratio_threshold: 0.6
  }
}
```

`min_text_ops_per_page` is the minimum number of PDF text operations required
for a page to count as text-based. `text_page_ratio_threshold` is the minimum
ratio of text pages used during classification.

## Page selection and encrypted PDFs

`page_filter` limits extraction to zero-based page indexes:

```elixir
%ExPdfInspector.PdfOptions{page_filter: [0, 4, 9]}
```

Set `password` to open a password-protected PDF:

```elixir
%ExPdfInspector.PdfOptions{password: "document-password"}
```

If no valid password is supplied, processing returns an error with the
`:encrypted` code. Error codes are stable library-defined atoms suitable for
pattern matching, while error messages remain strings.

## Markdown profiles

`ExPdfInspector.MarkdownOptions.profile` accepts:

- `:fidelity` — preserves more of the source document's detected structure;
- `:compact` — favors simpler, more token-efficient output.

```elixir
options = %ExPdfInspector.PdfOptions{
  markdown: %ExPdfInspector.MarkdownOptions{profile: :compact}
}
```

Fine-grained fields control detection and output:

| Field | Default | Purpose |
| --- | --- | --- |
| `detect_headers` | `true` | Detect heading levels. |
| `detect_lists` | `true` | Detect list structures. |
| `detect_code` | `true` | Detect code blocks. |
| `base_font_size` | `nil` | Override the inferred body font size. |
| `remove_page_numbers` | `true` | Remove detected page numbers. |
| `format_urls` | `true` | Format detected URLs as Markdown links. |
| `fix_hyphenation` | `true` | Rejoin words split across lines. |
| `detect_bold` | `true` | Preserve detected bold text. |
| `detect_italic` | `true` | Preserve detected italic text. |
| `detect_underline` | `true` | Preserve detected underlined text. |
| `include_images` | `false` | Include image placeholders. |
| `include_links` | `true` | Include links. |
| `include_page_numbers` | `false` | Include page-number markers in output. |
| `strip_headers_footers` | `true` | Remove recurring headers and footers. |

For standalone conversion, `to_markdown/2` accepts plain text and Markdown
options:

```elixir
ExPdfInspector.to_markdown(
  "TITLE\n\nDocument body",
  %ExPdfInspector.MarkdownOptions{profile: :compact}
)
```

The `to_markdown_from_items/2` and
`to_markdown_from_items_with_rects/3` functions are useful when positioned
items have already been extracted or produced by another pipeline.

## Concurrency and deployment

The Rust functions are scheduled as dirty CPU NIFs. Callers can use them from
regular Elixir processes without blocking a normal BEAM scheduler, but each
document still consumes CPU and memory. Bound concurrency when processing large
batches or untrusted documents.

The NIF is native code. Build releases on a platform compatible with the target
runtime, or compile the dependency in the target build environment.
