Skip to content

v0.1.0 — first release

Latest

Choose a tag to compare

@thatsme thatsme released this 26 Jul 09:08

First release under the Tincture name.

Tincture is a fork of ex_guten by Hugh Watkins (MIT), itself an Elixir port of Joe Armstrong's Erlang erlguten. Most of the engine — the TrueType/OpenType parser, the Knuth-Plass line breaker, the TeX hyphenation port, the PDF object serialiser — is his work, carried forward here. See NOTICE for the full attribution chain.

{:tincture, "~> 0.1.0"}

Why this exists

The two most-used PDF options in Elixir both shell out to a browser. Tincture is pure Elixir and Erlang/OTP — no Chrome, no wkhtmltopdf, no NIFs, no ports, and no runtime dependencies. It runs anywhere the BEAM runs.

The differentiator is typography: TeX hyphenation and Knuth-Plass line breaking, the same global optimisation TeX uses, rather than greedy first-fit.

New in this release

  • Hyperlinks and annotations — external URLs and internal page targets. The library previously emitted no /Annot objects at all.
  • Interactive forms (AcroForm) — text fields, checkboxes and choice fields, with field flags, max length, tooltips and initial values.
  • AES-256 encryption — standard security handler revision 6 (/V 5 /R 6, PDF 2.0), user and owner passwords, permission flags.
  • Font subsetting on by default — only the glyphs a document draws are embedded, typically cutting an embedded font by 70–90%.

Six real bugs fixed

Found while building the test suite out from 444 tests to 1,011:

  • Subsetting silently embedded the whole font for any text containing a space. A glyph with no outline is zero bytes, which the subsetter treated as malformed and aborted on — so subsetting was effectively never applied to real text.
  • A malformed name table crashed the parse. :unicode.characters_to_binary/3 signals bad input by returning a tuple rather than raising, so the rescue never fired.
  • The OS/2 Unicode range table was 91% missing — 11 of ~123 bits mapped, so any CJK, Hangul, punctuation or math codepoint was reported unsupported.
  • A deprecated File.stream!/3 argument order on the default hyphenation locale.
  • A type declared as a plain map where every caller requires a struct.
  • A divide-by-zero in the benchmark harness on fast machines.

Quality

Tests 1,011
Coverage 88.3% (80% floor enforced)
Credo --strict 0 issues
Dialyzer passing
CI Elixir 1.16, 1.17, 1.18, 1.19

Upstream's CI had never passed — all 13 runs failed at mix format --check-formatted, so its test suite never executed.

What's missing

ROADMAP.md sets out the gaps honestly and in order, including the ones that matter for enterprise adoption: tagged PDF (PDF/UA), PDF/A, digital signatures, spot colour, transparency and shading, and complex-script shaping for Arabic and Indic. Every gap listed was verified absent against the source.

Full detail in CHANGELOG.md.