Print-ready HTML and PDF engine options for Tiptapex documents.
This module never shells out and never adds a dependency: it turns a
document plus its Tiptapex.Page setup into (a) a standalone HTML page
with the right @page geometry and (b) the exact options the two common
Elixir PDF engines need. You call the engine.
ChromicPDF
{source, opts} = Tiptapex.Export.PDF.chromic_pdf(article.body)
ChromicPDF.print_to_pdf(source, opts ++ [output: "article.pdf"])pdf_generator (wkhtmltopdf)
{html, opts} = Tiptapex.Export.PDF.pdf_generator(article.body)
{:ok, path} = PdfGenerator.generate(html, opts)Anything else (browser print, WeasyPrint, Gotenberg…)
html = Tiptapex.Export.PDF.to_html(article.body)Headers, footers and page numbers
Both engines draw running headers/footers themselves, which is the only
way to get a correct {pages} total and per-page numbering. The three
slots of Tiptapex.Page map straight onto each engine's native feature:
| Token | ChromicPDF (Chrome) | pdf_generator (wkhtmltopdf) |
|---|---|---|
{page} | <span class="pageNumber"> | [page] |
{pages} | <span class="totalPages"> | [topage] |
{date} | <span class="date"> | [date] |
{time} | (not supported — pass :tokens) | [time] |
{title} | <span class="title"> | [title] |
Substitute a token yourself with :tokens when you want a fixed value or
a specific format:
Tiptapex.Export.PDF.chromic_pdf(doc, tokens: %{"date" => "24/07/2026"})Logos and formatting
A header/footer slot can carry an image (see Tiptapex.Page) and a small
allow-listed HTML subset (see Tiptapex.Renderer.Markup) — <b>, <span style="color: …">, <h1>, <img> and friends. Chrome draws both straight
from the template.
wkhtmltopdf's text flags can do neither: they render literally. A region
with a logo or markup therefore needs --header-html/--footer-html — use
with_pdf_generator/3, which writes and cleans up those files for you.
Chrome renders the running header with no base URL, so a relative logo src
will not load. Pass :asset_url to rewrite it — most usefully into a
data: URI:
Tiptapex.Export.PDF.chromic_pdf(doc, asset_url: &MyApp.inline_asset/1)Because the engines own the margin boxes, to_html/2 emits @page { margin: 0 } on the engine paths — the margins travel as engine options
instead, so the header/footer land inside them.
Summary
Functions
Options for ChromicPDF.print_to_pdf/2, as {source, opts}.
Options for PdfGenerator.generate/2 (wkhtmltopdf), as {html, opts}.
A standalone HTML document for one running region — what wkhtmltopdf's
--header-html / --footer-html expects, and the only way to get a logo
into a wkhtmltopdf header.
The stylesheet embedded in exported HTML (the package's own CSS).
Renders a complete, standalone HTML document ready to print.
Runs fun with the {html, opts} pdf_generator/2 would return, having
first written any header/footer that carries a logo to a temporary HTML
file (wkhtmltopdf's --header-html / --footer-html). The files are
removed afterwards, whatever fun does.
Functions
Options for ChromicPDF.print_to_pdf/2, as {source, opts}.
{source, opts} = Tiptapex.Export.PDF.chromic_pdf(doc)
ChromicPDF.print_to_pdf(source, opts ++ [output: path])Paper size and margins become printToPDF parameters (in inches, the
unit Chrome speaks) and the header/footer slots become Chrome's
headerTemplate/footerTemplate.
Accepts every to_html/2 option, plus:
:tokens— token replacements applied before Chrome's own, e.g.%{"date" => "24/07/2026"}.:print_to_pdf— a map merged over the computed parameters, for anything else Chrome supports ("scale","pageRanges", …).:font_size— header/footer font size in CSS units, default"9pt".:asset_url— 1-arity function rewriting logosrcvalues, e.g. to absolutise them or inline them asdata:URIs. Chrome cannot resolve relative URLs in a running header.
Options for PdfGenerator.generate/2 (wkhtmltopdf), as {html, opts}.
{html, opts} = Tiptapex.Export.PDF.pdf_generator(doc)
{:ok, path} = PdfGenerator.generate(html, opts)Paper size, orientation, margins and the header/footer slots all become
wkhtmltopdf shell parameters, so page numbering is produced by the engine
([page] / [topage]).
Accepts every to_html/2 option, plus:
:tokens— token replacements applied before wkhtmltopdf's own.:font_size— header/footer font size in points, default9.:header_spacing/:footer_spacing— millimetres between the running element and the content.:header_html/:footer_html— a path or URL for wkhtmltopdf's--header-html/--footer-html, used instead of the text flags. Required for a region carrying a logo — seewith_pdf_generator/3.:shell_params— extra parameters appended last (wkhtmltopdf lets later flags win, so this can override anything computed here, including the--dpi 96 --disable-smart-shrinkingpair that keeps wkhtmltopdf on CSS reference pixels).
A standalone HTML document for one running region — what wkhtmltopdf's
--header-html / --footer-html expects, and the only way to get a logo
into a wkhtmltopdf header.
wkhtmltopdf appends page, topage, date and time to the URL as query
parameters; the returned document reads them back, so {page} and
{pages} resolve per page exactly as the native flags would.
Accepts :page, :tokens, :font_size and :asset_url.
@spec stylesheet() :: String.t()
The stylesheet embedded in exported HTML (the package's own CSS).
Renders a complete, standalone HTML document ready to print.
Options
:page— page setup override, as inTiptapex.Page.from_doc/2. Defaults to whatever the document carries; passtrueto force defaults onto a document with no page setup.:margins—:css(default) writes the margins into the@pagerule;:nonewritesmargin: 0because the PDF engine supplies them. The engine helpers below use:none.:title—<title>of the page, and the value Chrome/wkhtmltopdf substitute for{title}. Defaults to the page setup's:title.:lang—<html lang>, default"en".:stylesheet—:bundled(default) inlines the package stylesheet so the PDF matches the editor;:noneomits it.:css— extra CSS appended after the stylesheet.:renderer— options forwarded toTiptapex.Renderer.to_html/2.:body_class— extra classes on<body>.
@spec with_pdf_generator(map() | nil, keyword(), (String.t(), keyword() -> result)) :: result when result: term()
Runs fun with the {html, opts} pdf_generator/2 would return, having
first written any header/footer that carries a logo to a temporary HTML
file (wkhtmltopdf's --header-html / --footer-html). The files are
removed afterwards, whatever fun does.
Tiptapex.Export.PDF.with_pdf_generator(doc, [], &PdfGenerator.generate/2)This is the path to use when headers or footers have images; without it
wkhtmltopdf can only draw their text (see pdf_generator/2). Passing
:header_html / :footer_html yourself — a path or a URL you serve —
takes precedence and writes nothing.