Formatting options

edit

HtmlToMarkdownOptions chooses the Markdown syntax of the elements that Markdown can write in several ways. Create it once and pass it to every call of HtmlToMarkdown.Convert.

OptionDefaultValues
HeadingStyleAtxAtx (# Title), Setext (title underlined with = or -)
EmphasisMarkerAsteriskAsterisk (*em*, **strong**), Underscore (_em_, __strong__)
CodeBlockStyleFencedFenced, Indented (four spaces, no language)
CodeBlockFenceCharacter`` or ~
UnorderedListMarker--, * or +
ThematicBreak---The text of a horizontal rule (hr)
LineBreakStyleTrailingSpacesTrailingSpaces, Backslash
UseSimplePunctuationfalsetrue replaces the typographic punctuation with ASCII characters
EmojiShortcodeModeNoneNone, GitHub (:heart:), Unicode (:red_heart:)
UnknownElementHandlingPassThroughPassThrough, Strip, StripKeepContent: see Unknown elements

Headings

HeadingStyle.Setext underlines the h1 headings with = and the h2 headings with -. Markdown has no Setext syntax for the other levels: they stay ATX headings.

var markdown = HtmlToMarkdown.Convert("<h1>Title</h1><h2>Section</h2><h3>Subsection</h3>", new HtmlToMarkdownOptions
{
    HeadingStyle = HeadingStyle.Setext,
});
Assert.Equal("""
    Title
    =====

    Section
    -------

    ### Subsection
    """, markdown);

Emphasis, lists and horizontal rules

EmphasisMarker, UnorderedListMarker and ThematicBreak follow the style of the documents you add the Markdown to:

var markdown = HtmlToMarkdown.Convert("<p><strong>Note</strong>: <em>read</em> this.</p><hr><ul><li>One</li><li>Two</li></ul>", new HtmlToMarkdownOptions
{
    EmphasisMarker = EmphasisMarker.Underscore,
    ThematicBreak = "* * *",
    UnorderedListMarker = '+',
});
Assert.Equal("""
    __Note__: _read_ this.

    * * *

    + One
    + Two
    """, markdown);

ThematicBreak is written as is: give a valid Markdown horizontal rule, such as ---, ***, ___ or * * *. UnorderedListMarker and CodeBlockFenceCharacter throw an ArgumentOutOfRangeException when they are set to another character:

var options = new HtmlToMarkdownOptions();
Assert.Throws<ArgumentOutOfRangeException>("value", () => options.UnorderedListMarker = '#');
Assert.Throws<ArgumentOutOfRangeException>("value", () => options.CodeBlockFenceCharacter = '"');

Code blocks

CodeBlockStyle.Indented writes the code indented by four spaces. An indented block has no language: use the fenced style, the default, to keep it. Code blocks shows both styles.

Line breaks

A br becomes two spaces at the end of the line by default. The spaces are invisible and some editors remove them: LineBreakStyle.Backslash writes a \ instead.

var html = "<p>Line 1<br>Line 2</p>";

// Two trailing spaces by default
var markdown = HtmlToMarkdown.Convert(html);
Assert.Equal("Line 1  \nLine 2", markdown);

var backslash = HtmlToMarkdown.Convert(html, new HtmlToMarkdownOptions { LineBreakStyle = LineBreakStyle.Backslash });
Assert.Equal("Line 1\\\nLine 2", backslash);

In a table cell, a br stays <br> whatever the option: a row of a pipe table cannot hold a line break.

Simple punctuation

UseSimplePunctuation replaces the typographic quotes, dashes, ellipsis and guillemets of the text with ASCII characters: “ and ” with ", ‘ and ’ with ', – with --, — with ---, … with ..., « with << and » with >>:

var markdown = HtmlToMarkdown.Convert(
    "<p>“Hello” ‘Hello’ — – … « »</p>",
    new HtmlToMarkdownOptions { UseSimplePunctuation = true });
Assert.Equal("""
    "Hello" 'Hello' \-\-\- \-\- ... \<\< \>\>
    """, markdown);

The replacement happens before the text is escaped, so the dashes and the guillemets are escaped and render as the characters -, < and >. Only the text is changed: the code blocks and code spans keep their characters.

Emoji shortcodes

EmojiShortcodeMode replaces the emoji of the text with shortcodes, such as the ones of GitHub:

var markdown = HtmlToMarkdown.Convert("<p>I ❤️ Markdown</p>", new HtmlToMarkdownOptions
{
    EmojiShortcodeMode = EmojiShortcodeMode.GitHub,
});
Assert.Equal("I :heart: Markdown", markdown);

var unicodeMarkdown = HtmlToMarkdown.Convert("<p>I ❤️ Markdown</p>", new HtmlToMarkdownOptions
{
    EmojiShortcodeMode = EmojiShortcodeMode.Unicode,
});
Assert.Equal("I :red_heart: Markdown", unicodeMarkdown);
  • GitHub uses the shortcodes of GitHub. It also finds an emoji written with or without its variation selector (the invisible character that asks for the emoji presentation).
  • Unicode uses shortcodes made from the Unicode names of the emoji.

An emoji that has no shortcode stays as it is. As with the punctuation, the code blocks and code spans are not changed. To render the shortcodes as emoji, the Markdown renderer must support them: see Rendering the Markdown.