MD4C
| Language | C (usable from C++) |
|---|---|
| License | MIT |
| Repository | github.com/mity/md4c |
| Documentation | comments in md4c.h and md4c-html.h |
| Depends on | nothing |
| Read / write | parses CommonMark with optional GFM and other extensions; HTML renderer included |
MD4C (“Markdown for C”) is a CommonMark-compliant parser that doesn’t build a tree. Instead it calls your functions as it enters and leaves blocks (paragraph, heading, list, table, …) and spans (emphasis, link, code, …) and for each piece of text. That makes it fast, keeps memory use tiny and lets you render straight into your own structures. Qt’s QTextDocument::setMarkdown uses it.
Extensions are enabled with flags: tables, strikethrough, task lists, permissive autolinks, LaTeX math spans, wiki links, underline, and in recent versions GitHub alerts (admonitions) and footnotes. MD_DIALECT_GITHUB enables the GitHub set. The optional md4c-html library renders HTML.
Installation#
# Debian / Ubuntu
sudo apt install libmd4c-dev libmd4c-html0-dev
# Fedora
sudo dnf install md4c-devel
# macOS
brew install md4c
# Windows (vcpkg)
vcpkg install md4c
Or add src/md4c.c, src/md4c-html.c and src/entity.c to your project. Link with -lmd4c-html -lmd4c.
Basic usage: render Markdown to HTML#
#include <md4c-html.h>
#include <stdio.h>
#include <string.h>
static void write_out(const MD_CHAR *text, MD_SIZE size, void *userdata) {
fwrite(text, 1, size, (FILE *)userdata);
}
int main(void) {
const char *md = "# Title\n\n| a | b |\n|---|---|\n| 1 | 2 |\n\n- [x] done\n- ~~old~~\n";
int rc = md_html(md, (MD_SIZE)strlen(md), write_out, stdout,
MD_DIALECT_GITHUB, /* parser flags */
0); /* renderer flags, e.g. MD_HTML_FLAG_XHTML */
return rc == 0 ? 0 : 1;
}
md_html streams output in pieces through the callback; append them to a buffer if you need one string.
How to list headings and links with the parser callbacks#
#include <md4c.h>
#include <stdio.h>
#include <string.h>
static int in_heading = 0;
static int enter_block(MD_BLOCKTYPE type, void *detail, void *ud) {
if (type == MD_BLOCK_H) {
printf("h%u: ", ((MD_BLOCK_H_DETAIL *)detail)->level);
in_heading = 1;
}
return 0; /* non-zero aborts parsing */
}
static int leave_block(MD_BLOCKTYPE type, void *detail, void *ud) {
if (type == MD_BLOCK_H) { printf("\n"); in_heading = 0; }
return 0;
}
static int enter_span(MD_SPANTYPE type, void *detail, void *ud) {
if (type == MD_SPAN_A) {
MD_ATTRIBUTE *href = &((MD_SPAN_A_DETAIL *)detail)->href;
printf("[link: %.*s] ", (int)href->size, href->text);
}
return 0;
}
static int leave_span(MD_SPANTYPE type, void *detail, void *ud) { return 0; }
static int text(MD_TEXTTYPE type, const MD_CHAR *t, MD_SIZE size, void *ud) {
if (in_heading) printf("%.*s", (int)size, t);
return 0;
}
int main(void) {
const char *md = "# Intro\n\nSee [the spec](https://spec.commonmark.org).\n\n## Usage\n";
MD_PARSER parser;
memset(&parser, 0, sizeof(parser));
parser.abi_version = 0;
parser.flags = MD_DIALECT_GITHUB;
parser.enter_block = enter_block;
parser.leave_block = leave_block;
parser.enter_span = enter_span;
parser.leave_span = leave_span;
parser.text = text;
return md_parse(md, (MD_SIZE)strlen(md), &parser, NULL);
}
Attribute strings (link URLs, titles, code languages) are not zero-terminated: use text and size.
How to disable raw HTML#
md_html(md, len, write_out, stdout, MD_DIALECT_GITHUB | MD_FLAG_NOHTML, 0);
MD_FLAG_NOHTML treats HTML blocks and inline HTML as plain text, which the HTML renderer escapes.
How to enable math and wiki links#
unsigned flags = MD_DIALECT_GITHUB | MD_FLAG_LATEXMATHSPANS | MD_FLAG_WIKILINKS;
/* $x^2$ and $$...$$ become MD_SPAN_LATEXMATH / MD_SPAN_LATEXMATH_DISPLAY,
[[Page]] becomes MD_SPAN_WIKILINK */
Tips#
- Input must be UTF-8 (or define
MD4C_USE_UTF16on Windows forWCHARinput). MD_HTML_FLAG_SKIP_UTF8_BOMmakes the HTML renderer ignore a leading BOM.- The
md2htmlprogram in the repository is a complete command line converter built onmd_html.