# Changelog
All notable changes to this project are documented here. The format is based on
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres
to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) (against *our* API,
not pdfium-render's).
## [Unreleased]
## 0.2.0 - 2026-06-25
### Added
- Phase 6 — forms & annotations (read), completing the read-only scope:
- `ExPdfium.form_type/1` → `:none` | `:acrobat` | `:xfa_full` | `:xfa_foreground`.
- `ExPdfium.form_fields/1` → AcroForm fields, one entry per widget across all
pages (`%{name, type, value, checked, read_only, required, page, bounds}`).
For checkbox/radio groups, `value` is the group's selected on-state and
`checked` flags the selected widget (pdfium does not expose per-option export
names). XFA form data is unavailable without a V8-enabled pdfium build.
- `ExPdfium.annotations/2` → a page's annotations, markup and widget alike
(`%{type, bounds, contents, name, hidden, printed}`; `type` is the PDF
`/Subtype`).
- Phase 5 — structure & navigation:
- `ExPdfium.outline/1` → the bookmark tree (`%{title, page, children}` nodes).
- `ExPdfium.links/2` → a page's links (`%{bounds, uri, page}`; `uri` for web
links, `page` for internal destinations).
- `ExPdfium.attachments/1` → embedded files (`%{index, name, size}`) and
`ExPdfium.attachment_data/2` → an attachment's bytes.
- Phase 4 — metadata, page geometry & permissions:
- `ExPdfium.metadata/1` → document info map (title/author/subject/keywords/
creator/producer/creation_date; `modification_date` is usually `nil`, a
pdfium-render limitation — see the docs).
- `ExPdfium.page_info/2` → `%{width, height, rotation, label, boxes}` (size in
points, rotation in degrees, boundary boxes media/crop/bleed/trim/art).
- `ExPdfium.permissions/1` → map of 8 boolean permission flags.
- Phase 3 — text extraction & search:
- `ExPdfium.extract_text/2` (one page) and `extract_text/1` (whole document,
pages joined by a form feed).
- `ExPdfium.text_segments/2` returns text runs with per-segment bounding boxes
(PDF points, origin bottom-left).
- `ExPdfium.search_text/3,4` with `:match_case` and `:whole_word` options; each
match carries its text and bounding rects. Empty query → `{:error, :empty_query}`.
- Phase 2 — render a page to a bitmap: `ExPdfium.render_page/3` returns
`{:ok, %ExPdfium.Bitmap{data, width, height, stride, format}}`, an uncompressed
4-channel buffer ready for `Vix.Vips.Image.new_from_binary/5`.
- Sizing by `:dpi` (default 72), `:scale`, or `:width`/`:height`.
- `:format` `:rgba` (default) or `:bgra`; `:background` `:white` (default) or
`:transparent`.
- Errors: `:page_out_of_bounds`, `:document_closed`, `:render_failed`,
`:unsupported_format`, `:unsupported_background`, `:bad_option`.
- GC-driven document close is deferred to a dedicated cleanup thread so it never
blocks a BEAM scheduler while a long render holds the pdfium lock.
- Phase 1 — open documents & page count:
- `ExPdfium.open/1,2` opens a PDF from a file path or in-memory binary, with an
optional `:password` for encrypted documents. Returns `{:ok, %ExPdfium.Document{}}`.
- `ExPdfium.page_count/1` returns `{:ok, n}`.
- `ExPdfium.close/1` releases the document early (idempotent); documents are
also closed automatically on garbage collection (no manual-close leak).
- Errors are mapped from pdfium: `:enoent`, `:invalid_pdf`, `:password_error`,
`:unsupported_security`, `:file_error`, `:io_error`, `:document_closed`.
- pdfium is not thread-safe, so all pdfium operations are serialized through a
single global lock; calls are safe from any number of BEAM processes but run
one at a time.
## [0.1.0] - 2026-06-24
First release — Phase 0: proves the toolchain and the precompiled-release path
end to end. PDF document/page/text APIs land in later phases (see `PORTING.md`).
### Added
- Project scaffold: `rustler_precompiled` config, tag-driven release pipeline,
and the porting plan (`PORTING.md`).
- Phase 0 (toolchain): `ExPdfium.pdfium_version/0`, a load-proof NIF that binds
and initializes pdfium. Pinned `pdfium-render = "=0.8.37"`. The dev/test build
binds pdfium dynamically; the libpdfium directory is passed to the NIF via a
`set_dynamic_lib_dir/1` function argument (env vars set with `System.put_env`
don't reach a NIF).
### Changed
- pdfium-render must be built with the `sync` feature (not just `thread_safe`):
only `sync` adds the `Send + Sync` impls that let the single global `Pdfium`
live in a `static`. `release.yml` updated accordingly.
- Pinned pdfium binary tag bumped `chromium/7506` → `chromium/7543` to match the
pdfium API version pdfium-render 0.8.37 binds (`pdfium_latest`).
- Shipping strategy: the precompiled NIF binds pdfium **dynamically** and bundles
the dynamic `libpdfium` inside each per-target tarball (rustler_precompiled
extracts it next to the NIF; the NIF self-locates it via `dladdr`). bblanchon
ships no static `libpdfium.a`, so static linking isn't used. The optional
`static`/`libcpp`/`libstdcpp` features remain for a user-supplied `.a`.