NativeElixirPdfUtilities.HtmlToPdf (native_elixir_pdf_utilities v1.0.0)
View SourcePublic facade for native HTML/CSS to PDF rendering.
Supported controls generate interactive AcroForm fields by default. Set
forms: :static for static artwork. Names use HTML name or a generated
TYPE_PAGENR_ELEMENT key; see the forms guide.
The renderer is intentionally structured as a small pipeline:
- parse HTML into a document tree
- compute styles
- resolve every text grapheme to an available font
- lay out the styled tree
- paginate layout boxes
- write PDF bytes
The supported surface is a strict, document-oriented HTML/CSS subset. Malformed structure and unsupported features return errors instead of using browser-like guessing. Unsupported text graphemes are visibly replaced by default. See the HTML-to-PDF compatibility guide for the current element, CSS, layout, image, and font support.
Summary
Types
Context supplied to a caller-provided asset resolver.
A callback that supplies bytes without giving the renderer network access.
Caller-approved bytes or a trusted local asset file.
An explicitly tagged inline stylesheet or local stylesheet file.
Functions
Renders an HTML document to a PDF binary.
Reads an HTML file, renders it to PDF, and writes the PDF to output_path.
Types
@type asset_request() :: %{ reference: String.t(), kind: :image | :background_image | :font }
Context supplied to a caller-provided asset resolver.
@type asset_resolver() :: (asset_request() -> {:ok, binary()} | :not_found | {:error, term()})
A callback that supplies bytes without giving the renderer network access.
Caller-approved bytes or a trusted local asset file.
@type detailed_error_reason() :: {error_reason(), error_detail()}
@type error_detail() :: NativeElixirPdfUtilities.Diagnostics.diagnostic()
@type error_reason() :: :invalid_document | :invalid_css | :invalid_encoding | :invalid_form | :invalid_html | :invalid_layout | :invalid_margin | :invalid_options | :invalid_outlines | :invalid_page_size | :invalid_path | :invalid_pdf_input | :not_implemented | :resource_limit_exceeded | :unsupported_form | :unsupported_glyph | :unsupported_html | File.posix()
@type outlines() :: :headings | [NativeElixirPdfUtilities.Outlines.item_input()] | false | nil
@type page_furniture() :: [header: page_furniture_variants(), footer: page_furniture_variants()] | %{ optional(:header) => page_furniture_variants(), optional(:footer) => page_furniture_variants() }
@type page_furniture_template() :: String.t() | false | nil
@type page_furniture_variants() :: page_furniture_template() | [ default: page_furniture_template(), first: page_furniture_template(), odd: page_furniture_template(), even: page_furniture_template() ] | %{ optional(:default) => page_furniture_template(), optional(:first) => page_furniture_template(), optional(:odd) => page_furniture_template(), optional(:even) => page_furniture_template() }
@type page_margin() :: NativeElixirPdfUtilities.HtmlToPdf.PageGeometry.margin_input()
@type page_size() :: NativeElixirPdfUtilities.HtmlToPdf.PageGeometry.page_size_input()
@type pdf_metadata() :: keyword() | %{ optional(:title) => String.t(), optional(:author) => String.t(), optional(:subject) => String.t(), optional(:keywords) => String.t() | [String.t()], optional(:producer) => String.t(), optional(:creation_date) => Date.t() | NaiveDateTime.t() | DateTime.t() | String.t(), optional(:modification_date) => Date.t() | NaiveDateTime.t() | DateTime.t() | String.t() }
@type render_option() :: {:forms, :interactive | :static} | {:page_size, page_size()} | {:margin, page_margin()} | {:base_url, String.t() | nil} | {:assets, %{optional(String.t()) => asset_source()}} | {:asset_resolver, asset_resolver() | nil} | {:stylesheets, [stylesheet_source()]} | {:default_font, String.t() | [String.t()]} | {:fonts, [map() | keyword() | {String.t(), String.t()}]} | {:system_font_discovery, boolean()} | {:metadata, pdf_metadata()} | {:outlines, outlines()} | {:page_furniture, page_furniture() | false | nil} | {:unsupported_glyphs, unsupported_glyphs()} | {:compress_streams, boolean()} | {:subset_fonts, boolean()}
An explicitly tagged inline stylesheet or local stylesheet file.
@type unsupported_glyphs() :: :replace | :error
Functions
@spec render(String.t(), [render_option()]) :: {:ok, binary()} | {:error, detailed_error_reason()}
Renders an HTML document to a PDF binary.
Returns {:ok, pdf_binary} when rendering succeeds or
{:error, {reason, diagnostic}} when
parsing, styling, layout, pagination, or PDF writing cannot be completed.
Rendering failures include a broad reason and diagnostic detail, for example
{:error, {:invalid_css, %{message: "...", line: 18, source: "..."}}}.
Supported options include :page_size, :margin, :base_url,
:stylesheets, :default_font, explicit local :fonts,
:system_font_discovery, PDF :metadata, opt-in :page_furniture headers
and footers, PDF :outlines, :unsupported_glyphs, :compress_streams, and
:subset_fonts.
Stream compression and TrueType font subsetting are enabled by default to keep
PDFs small. Set compress_streams: false or subset_fonts: false to opt out.
Subsetting retains the original glyph IDs for PDF character mapping and keeps
the full font when its license forbids subsetting.
Metadata supports title, author, subject, keywords, producer, creation date,
and modification date. An HTML <title> supplies the PDF title when
metadata[:title] is not set.
Unsupported graphemes are replaced visibly with U+FFFD by default. Set
unsupported_glyphs: :error to return an :unsupported_glyph diagnostic
instead.
Page furniture accepts :header and :footer HTML templates. Each can be a
string used on every page or variants named :default, :first, :odd, and
:even. A variant set to false or nil is omitted. The :first variant
has precedence on page one, followed by the matching odd/even variant and
then :default. Templates can contain {{page}} and {{pages}} tokens.
Furniture is disabled when :page_furniture is omitted, nil, or false.
Enabled furniture must fit inside the page margin.
Set :outlines to :headings to turn visible h1 through h6 elements
into nested PDF bookmarks using their final positions after pagination. An
exact outline list accepted by NativeElixirPdfUtilities.Outlines.put/2 may
also be supplied. Outlines are omitted by default.
:page_size accepts the CSS named sizes :a5, :a4, :a3, :b5, :b4,
:jis_b5, :jis_b4, :letter, :legal, and :ledger, optionally paired
with :portrait or :landscape, or a positive {width, height} tuple. Tuple
values up to 20 x 20 are interpreted as inches for compatibility with
ChromicPDF-style custom label sizes; larger tuples are interpreted as PDF
points. CSS two-length strings retain their declared units.
:margin accepts a nonnegative point number, a CSS string containing one to
four absolute lengths, or a map with :top, :right, :bottom, and :left
values. Explicit renderer :page_size and :margin options override
stylesheet @page defaults.
:stylesheets accepts a list of {:css, css} and {:file, path} tuples.
The explicit tag determines whether content is parsed directly or read from
the local filesystem; bare strings are rejected.
:base_url is also the authorization root for document-selected local image
and @font-face paths. Relative paths and absolute paths beneath that root
are accepted; traversal outside it and symlink components are rejected.
Explicit :fonts and {:file, stylesheet} paths are trusted caller
configuration and are not reclassified as document-selected resources.
@spec render_file(String.t(), String.t(), [render_option()]) :: :ok | {:error, detailed_error_reason()}
Reads an HTML file, renders it to PDF, and writes the PDF to output_path.
Returns :ok after writing the output file or {:error, {reason, diagnostic}} if reading,
rendering, or writing fails. Rendering options are the same as render/2.