Pure Elixir PDF generation. No Chrome, no wkhtmltopdf, no NIFs, no ports. Font parsing, subsetting, hyphenation, line breaking, serialisation, encryption and signing are all Elixir/OTP.
Fork of ex_guten (Hugh Watkins, MIT), itself a port of Joe Armstrong’s erlguten. Most of the engine — TrueType/OpenType parser, Knuth-Plass line breaker, TeX hyphenation, PDF serialiser — is Hugh’s work. Git history preserved, copyright retained in LICENSE, full chain in NOTICE.
Tincture.new()
|> Tincture.page_size(:a4)
|> Tincture.register_ttf_font("Body", "Georgia.ttf")
|> Tincture.set_font("Body", 11)
|> Tincture.text_paragraph(50, 700, rich_text, 495,
align: :justified, line_break: :optimal)
|> Tincture.export()
Features
- Typography — TeX hyphenation (Liang patterns, 5 locales), Knuth-Plass global line breaking, configurable widow/orphan/hyphen penalties, GPOS kerning, GSUB ligatures
- Fonts — standard 14; TrueType/OpenType embedding, subsetting on by default (70–90% reduction)
- Layout — text boxes with spill, multi-column flow, tables with auto column widths, page templates with headers/footers/numbering, XML-driven documents
- Graphics — lines, rects, circles, Bézier paths, fill/stroke/clip; JPEG and PNG with alpha
- Documents — metadata, bookmarks, links, forms (text, choice, checkbox, radio group, push button, signature), AES-256 encryption
- Standards — tagged PDF (PDF/UA), PDF/A with built-in sRGB output intent, detached PKCS#7 signatures
- Telemetry — spans per document, page and embedded font;
:telemetryis an optional dep
Conformance
Checked with veraPDF 1.30.2 and OpenSSL 3.6, not asserted:
| Standard | Result |
|---|---|
| PDF/UA-1 (ISO 14289-1) | isCompliant=true, 106/106 rules |
| PDF/A-2b (ISO 19005-2) | isCompliant=true, 144/144 rules |
| PDF/A-2u | isCompliant=true, 146/146 rules |
| PDF/A-2a | isCompliant=true, 153/153 rules |
| PKCS#7 detached | verified by openssl cms -verify |
Example documents are committed. Reproduce:
verapdf --flavour ua1 examples/output/compliant.pdf
verapdf --flavour 2a examples/output/archival.pdf
set_pdf_a/2 writes a conformance claim into the file, so export/2 refuses to emit a document that breaks the rules it can check, listing each violation and clause. enforce: false overrides.
Install
{:tincture, "~> 0.1"}
0.x is deliberate — the API under this name is new and may move before 1.0.
1,225 tests, 89% coverage, Credo --strict clean, Dialyzer clean, CI on Elixir 1.16–1.19. Seven examples with output PDFs committed.
Hex: tincture | Hex
Docs: Tincture v0.1.0 — Documentation
Roadmap: tincture/ROADMAP.md at main · thatsme/tincture · GitHub
Examples: tincture/examples at main · thatsme/tincture · GitHub






















