markupz for Erlang

markupz converts HTML fragments to Markdown. It uses z_html_parse from zotonic_stdlib and emits Markdown compatible with markdownz.

The converter has three presets:

Usage

1> markupz:to_markdown(<<"<h1>Hello</h1><p>Welcome <b>back</b>.</p>">>).
<<"Hello\n=====\n\nWelcome **back**.">>
2> markupz:to_email_text(EmailHtml).
<<"Hello\n\nRead the update <https://example.test/update>.">>

to_markdown/2 accepts a preset atom or an option map:

markupz:to_markdown(Html, default).
markupz:to_markdown(Html, faithful).
markupz:to_markdown(Html, email).
markupz:to_markdown(Html, #{
mode => default,
html => strip,
tables => text,
links => inline
}).

The options are:

OptionValuesDefaultFaithfulEmail
modedefault, faithful, emaildefaultfaithfulemail
htmlkeep, stripkeepkeepstrip
tablesmarkdown, textmarkdownmarkdowntext
linksmarkdown, inlinemarkdownmarkdowninline

An option map is merged over the defaults of its mode. For callers that do not want exceptions, convert/1,2 returns {ok, Markdown} or {error, Reason}. to_markdown/1,2 raises error({invalid_html, Reason}) on invalid input or options.

Conversion behavior

The default and faithful presets convert headings, paragraphs, hard breaks, emphasis, strong text, strikethrough, subscript, superscript, links, images, inline and fenced code, block quotes, nested ordered and unordered lists, task-list checkboxes, thematic breaks, and regular tables.

HTML that cannot be represented without losing structure or attributes is serialized as raw HTML when html => keep. For example, a div with classes and a table with rowspan or colspan remain HTML. With html => strip, the wrapper is removed and its readable content is retained.

When using the default preset:

The email preset additionally:

The converter parses but does not sanitize input. Raw HTML output is deliberate when html => keep; sanitize untrusted HTML before displaying the generated Markdown with raw HTML enabled.

Build

make compile
make test
make xref
make dialyzer

For the Zotonic email path, the old call:

z_markdown:to_markdown(Html, [no_html, no_tables])

can be replaced with:

markupz:to_email_text(Html)