# `PDFium`

Bindings to pdfium: open, inspect, render, flatten, stamp and write PDF
documents. `PDFium.Toolbox` does whole jobs on top of these.

Every call on a document waits its turn on one lock, since pdfium is not
thread safe, and runs on a dirty scheduler.

# `load_error`

```elixir
@type load_error() :: :unknown | :file | :format | :password | :security | :page
```

Why pdfium would not open a document: unreadable `:file`, broken `:format`,
a missing or wrong `:password`, an unsupported `:security` scheme, an
unreadable `:page`, or `:unknown`.

# `page_box`

```elixir
@type page_box() :: %{
  left: float(),
  bottom: float(),
  right: float(),
  top: float(),
  rotation: 0 | 90 | 180 | 270
}
```

The box a page is drawn in, its MediaBox clipped by its CropBox, in points
with corners sorted, and its rotation in degrees. Neither corner has to sit
at the origin.

# `page_size`

```elixir
@type page_size() :: %{width: float(), height: float()}
```

The size of a page as a reader shows it, in points.

# `placement`

```elixir
@type placement() :: {number(), number(), number(), number(), number(), number()}
```

Where an overlay goes, as the matrix `{a, b, c, d, e, f}` in the page as a
reader shows it, origin at its bottom left. `{1, 0, 0, 1, 0, 0}` leaves the
overlay where and how large it was drawn.

# `security`

```elixir
@type security() :: :keep | :remove
```

Whether a written document keeps its encryption or is written in the clear.

# `source`

```elixir
@type source() :: Path.t() | {:bytes, binary()}
```

A document to open: a path, or its contents.

# `close_document`

```elixir
@spec close_document(reference()) :: :ok
```

Closes a document and frees what it held. A closed document answers
`{:error, :document_closed}` to everything.

# `create_document`

```elixir
@spec create_document() :: {:ok, reference()}
```

Makes an empty document.

    {:ok, document} = PDFium.create_document()

# `flatten_page`

```elixir
@spec flatten_page(reference(), non_neg_integer(), :display | :print) ::
  :ok | {:error, atom()}
```

Draws a page's annotations into its content and drops them, in place.

    :ok = PDFium.flatten_page(document, 0)
    :ok = PDFium.flatten_page(document, 1, :print)

`usage` picks the appearance drawn: `:display` is what a reader shows,
`:print` what it would print.

# `get_annotation_counts`

```elixir
@spec get_annotation_counts(reference()) ::
  {:ok, [non_neg_integer()]} | {:error, atom()}
```

How many annotations each page carries, in order.

    {:ok, [2, 0]} = PDFium.get_annotation_counts(document)

# `get_meta_text`

```elixir
@spec get_meta_text(reference(), binary()) :: {:ok, binary()} | {:error, atom()}
```

An entry of the document's Info dictionary, or `""` when it has none.

    {:ok, "A contract"} = PDFium.get_meta_text(document, "Title")

# `get_page_bitmap`

```elixir
@spec get_page_bitmap(reference(), non_neg_integer(), pos_integer()) ::
  {:ok, {binary(), non_neg_integer(), non_neg_integer()}} | {:error, atom()}
```

Renders a page to RGBA pixels at `dpi`, annotations included.

    {:ok, {pixels, width, height}} = PDFium.get_page_bitmap(document, 0, 150)

# `get_page_boxes`

```elixir
@spec get_page_boxes(reference()) :: {:ok, [page_box()]} | {:error, atom()}
```

The box and rotation of every page, in order: what placing content in a
page's own coordinates needs.

    {:ok, [%{left: 0.0, bottom: 0.0, right: 612.0, top: 792.0, rotation: 0}]} =
      PDFium.get_page_boxes(document)

# `get_page_count`

```elixir
@spec get_page_count(reference()) :: {:ok, non_neg_integer()} | {:error, atom()}
```

How many pages the document has.

    {:ok, 3} = PDFium.get_page_count(document)

# `get_page_sizes`

```elixir
@spec get_page_sizes(reference()) :: {:ok, [page_size()]} | {:error, atom()}
```

The size of every page as a reader shows it, in points, in order.

    {:ok, [%{width: 612.0, height: 792.0}]} = PDFium.get_page_sizes(document)

# `get_security_handler_revision`

```elixir
@spec get_security_handler_revision(reference()) ::
  {:ok, pos_integer() | nil} | {:error, atom()}
```

The revision of the standard security handler the document is encrypted
with, or `nil` when it is not encrypted.

    {:ok, 6} = PDFium.get_security_handler_revision(document)

# `import_pages`

```elixir
@spec import_pages(
  reference(),
  reference(),
  :all | [non_neg_integer()],
  non_neg_integer()
) ::
  :ok | {:error, atom()}
```

Copies pages of `source` into `destination` at position `at`. `pages` is
`:all` or page indexes counted from zero, in the order they should arrive.

    :ok = PDFium.import_pages(destination, source, :all)
    :ok = PDFium.import_pages(destination, source, [2, 0], 1)

# `open_document`

```elixir
@spec open_document(source(), [{:password, binary()}]) ::
  {:ok, reference()} | {:error, load_error()}
```

Opens a document from a path or from `{:bytes, contents}`.

    {:ok, document} = PDFium.open_document("contract.pdf")
    {:ok, document} = PDFium.open_document({:bytes, contents}, password: "secret")

The document keeps the contents for as long as it is open.

# `save`

```elixir
@spec save(reference(), Path.t(), security()) :: :ok | {:error, atom()}
```

Writes the document to `output_path`.

    :ok = PDFium.save(document, "out.pdf")
    :ok = PDFium.save(document, "out.pdf", :remove)

# `save_to_binary`

```elixir
@spec save_to_binary(reference(), security()) :: {:ok, binary()} | {:error, atom()}
```

The document as it would be written.

    {:ok, <<"%PDF-", _::binary>>} = PDFium.save_to_binary(document)

# `stamp`

```elixir
@spec stamp(reference(), [
  {reference(), non_neg_integer(), non_neg_integer(), placement()}
]) ::
  :ok | {:error, atom()}
```

Draws overlays over pages of `document`, in place.

    :ok = PDFium.stamp(document, [{overlay, 0, 0, {1, 0, 0, 1, 0, 0}}])

A placement is `{overlay, overlay_page, page_index, matrix}`, pages counted
from zero. Placements on one page stack in the order given. A placement that
fails leaves the document as it was.

# `with_document`

```elixir
@spec with_document(source(), (reference() -&gt; result), [{:password, binary()}]) ::
  result | {:error, load_error()}
when result: term()
```

Opens `source`, runs `function` with the document, and closes it again, even
if the work raises.

    {:ok, 3} = PDFium.with_document("contract.pdf", &PDFium.get_page_count/1)

# `with_new_document`

```elixir
@spec with_new_document((reference() -&gt; result)) :: result | {:error, atom()}
when result: term()
```

Runs `function` with an empty document and closes it again.

    {:ok, binary} =
      PDFium.with_new_document(fn output ->
        with :ok <- PDFium.import_pages(output, source, [0]), do: PDFium.save_to_binary(output)
      end)

---

*Consult [api-reference.md](api-reference.md) for complete listing*
