cmark
| Language | C (usable from C++) |
|---|---|
| License | BSD-2-Clause (some bundled parts under MIT-style licenses) |
| Repository | github.com/commonmark/cmark |
| Documentation | cmark.h, man 3 cmark |
| Depends on | nothing |
| Read / write | parses 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#
# 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#
#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:
<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:
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 />.
How to list headings and links (walk the AST)#
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)#
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#
cmark README.md > README.html
cmark --to latex README.md > README.tex
cmark --to commonmark --width 80 messy.md > clean.md
Tips#
- Feed large or streamed input with
cmark_parser_new,cmark_parser_feed(any chunk size) andcmark_parser_finish. - Input must be UTF-8; invalid sequences are replaced with U+FFFD. Pass
CMARK_OPT_VALIDATE_UTF8to check explicitly. - Nodes can be created and moved (
cmark_node_new,cmark_node_append_child,cmark_node_insert_before) to transform documents before rendering.