Skip to main content

README.md

# 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.