All formats

cmark

LanguageC (usable from C++)
LicenseBSD-2-Clause (some bundled parts under MIT-style licenses)
Repositorygithub.com/commonmark/cmark
Documentationcmark.h, man 3 cmark
Depends onnothing
Read / writeparses CommonMark; renders HTML, XML, LaTeX, groff man and CommonMark

cmark is written by the authors of the CommonMark spec and passes the whole spec test suite. It parses a document in linear time (no pathological inputs), builds an AST, and renders it to several formats. It’s very fast: thousands of times faster than the original Perl Markdown, and among the fastest parsers in any language. Many language bindings (Python’s cmarkgfm, Ruby’s old commonmarker 0.x, Lua, R) wrap it or its fork.

cmark implements only core CommonMark: no tables, task lists or footnotes. For those use cmark-gfm. Raw HTML is replaced by a comment unless you pass CMARK_OPT_UNSAFE.

Installation#

Shell
# Debian / Ubuntu
sudo apt install libcmark-dev cmark
# Fedora
sudo dnf install cmark-devel
# macOS
brew install cmark
# Windows (vcpkg)
vcpkg install cmark

Link with -lcmark (or pkg-config --libs libcmark). With CMake: find_package(cmark REQUIRED) and link cmark::cmark.

Basic usage: convert Markdown to HTML#

C
#include <cmark.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void) {
    const char *md = "# Hello\n\nSome *emphasis* and a [link](https://commonmark.org).\n";
    char *html = cmark_markdown_to_html(md, strlen(md), CMARK_OPT_DEFAULT);
    printf("%s", html);
    free(html);   /* returned string is malloc'ed */
    return 0;
}

Output:

XML
<h1>Hello</h1>
<p>Some <em>emphasis</em> and a <a href="https://commonmark.org">link</a>.</p>

How to convert a Markdown file#

cmark_parse_file reads from a FILE*; then pick a renderer:

C
FILE *f = fopen("README.md", "rb");
cmark_node *doc = cmark_parse_file(f, CMARK_OPT_DEFAULT);
fclose(f);

char *html = cmark_render_html(doc, CMARK_OPT_DEFAULT | CMARK_OPT_SMART);
printf("%s", html);
free(html);
cmark_node_free(doc);

CMARK_OPT_SMART turns straight quotes into curly quotes and -- into dashes; CMARK_OPT_UNSAFE lets raw HTML through; CMARK_OPT_HARDBREAKS renders every newline as <br />.

C
cmark_node *doc = cmark_parse_document(md, strlen(md), CMARK_OPT_DEFAULT);
cmark_iter *iter = cmark_iter_new(doc);
cmark_event_type ev;
while ((ev = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
    cmark_node *node = cmark_iter_get_node(iter);
    if (ev != CMARK_EVENT_ENTER) continue;
    switch (cmark_node_get_type(node)) {
    case CMARK_NODE_HEADING: {
        cmark_node *text = cmark_node_first_child(node);
        printf("h%d: %s\n", cmark_node_get_heading_level(node),
               text && cmark_node_get_type(text) == CMARK_NODE_TEXT ? cmark_node_get_literal(text) : "");
        break;
    }
    case CMARK_NODE_LINK:
        printf("link: %s\n", cmark_node_get_url(node));
        break;
    default:
        break;
    }
}
cmark_iter_free(iter);
cmark_node_free(doc);

How to normalize Markdown (render back to CommonMark)#

C
cmark_node *doc = cmark_parse_document(md, strlen(md), CMARK_OPT_DEFAULT);
char *out = cmark_render_commonmark(doc, CMARK_OPT_DEFAULT, 80);  /* wrap at 80 columns, 0 = no wrap */
printf("%s", out);
free(out);
cmark_node_free(doc);

How to use the command line tool#

Shell
cmark README.md > README.html
cmark --to latex README.md > README.tex
cmark --to commonmark --width 80 messy.md > clean.md

Tips#