All notable changes to this project are documented here. The format is based on Keep a Changelog, and this project adheres to Semantic Versioning (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,valueis the group's selected on-state andcheckedflags 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};typeis 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};urifor web links,pagefor internal destinations).ExPdfium.attachments/1→ embedded files (%{index, name, size}) andExPdfium.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_dateis usuallynil, 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) andextract_text/1(whole document, pages joined by a form feed).ExPdfium.text_segments/2returns text runs with per-segment bounding boxes (PDF points, origin bottom-left).ExPdfium.search_text/3,4with:match_caseand:whole_wordoptions; each match carries its text and bounding rects. Empty query →{:error, :empty_query}.
- Phase 2 — render a page to a bitmap:
ExPdfium.render_page/3returns{:ok, %ExPdfium.Bitmap{data, width, height, stride, format}}, an uncompressed 4-channel buffer ready forVix.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.
- Sizing by
- Phase 1 — open documents & page count:
ExPdfium.open/1,2opens a PDF from a file path or in-memory binary, with an optional:passwordfor encrypted documents. Returns{:ok, %ExPdfium.Document{}}.ExPdfium.page_count/1returns{:ok, n}.ExPdfium.close/1releases 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_precompiledconfig, 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. Pinnedpdfium-render = "=0.8.37". The dev/test build binds pdfium dynamically; the libpdfium directory is passed to the NIF via aset_dynamic_lib_dir/1function argument (env vars set withSystem.put_envdon't reach a NIF).
Changed
- pdfium-render must be built with the
syncfeature (not justthread_safe): onlysyncadds theSend + Syncimpls that let the single globalPdfiumlive in astatic.release.ymlupdated accordingly. - Pinned pdfium binary tag bumped
chromium/7506→chromium/7543to 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
libpdfiuminside each per-target tarball (rustler_precompiled extracts it next to the NIF; the NIF self-locates it viadladdr). bblanchon ships no staticlibpdfium.a, so static linking isn't used. The optionalstatic/libcpp/libstdcppfeatures remain for a user-supplied.a.