# pdf_oxide — Elixir bindings
Idiomatic Elixir bindings for pdf_oxide via a NIF over the C ABI. CPU-bound
extraction runs on **dirty CPU schedulers** (`ERL_NIF_DIRTY_JOB_CPU_BOUND`) so
it never blocks the BEAM scheduler. Document/Pdf handles are NIF resources freed
by the GC; functions return `{:ok, value}` / `{:error, code}`. Page indices are
0-based.
## Install
Published to [Hex.pm](https://hex.pm/) as `pdf_oxide`:
```elixir
# mix.exs
def deps do
[
{:pdf_oxide, "~> 0.3.69"}
]
end
```
The NIF is built from source on install via `elixir_make`, linking the
default-feature `libpdf_oxide` cdylib — see "Build & test" for building it.
## Build & test
The NIF links the **default-feature cdylib** (not the Python wheel):
```bash
# 1. build the native library (shipped binding feature set)
cargo build --release --lib --features ocr,rendering,signatures,barcodes,tsa-client,system-fonts
# 2. compile the NIF + test (elixir_make builds c_src/pdf_oxide_nif.c)
cd elixir
mix deps.get
mix compile
LD_LIBRARY_PATH="$PWD/../target/release" mix test
LD_LIBRARY_PATH="$PWD/../target/release" mix run examples/basic_extraction.exs
```
## Use
```elixir
{:ok, pdf} = PdfOxide.from_markdown("# Hello\n\nbody\n")
{:ok, data} = PdfOxide.to_bytes(pdf)
{:ok, doc} = PdfOxide.open_bytes(data)
{:ok, n} = PdfOxide.page_count(doc)
{:ok, text} = PdfOxide.extract_text(doc, 0)
{:ok, md} = PdfOxide.to_markdown_all(doc)
```
## Layout
```
elixir/
lib/pdf_oxide.ex idiomatic API (PdfOxide, Document, Pdf, Error)
lib/pdf_oxide/native.ex NIF loader
c_src/pdf_oxide_nif.c dirty-CPU NIF over the C ABI
Makefile builds priv/pdf_oxide_nif.so (via elixir_make)
examples/basic_extraction.exs runnable example (asserted in CI)
test/pdf_oxide_test.exs ExUnit api-coverage (one test per function)
mix.exs
```
## Verification (CI — same set as every binding)
`.github/workflows/elixir.yml` on Linux + macOS: build cdylib → OTP/Elixir →
`mix compile` (builds the NIF) → `mix test` (api-coverage) → run example with an
output assertion.