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, 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:
  • 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/7506chromium/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.