All formats

MD4C

LanguageC (usable from C++)
LicenseMIT
Repositorygithub.com/mity/md4c
Documentationcomments in md4c.h and md4c-html.h
Depends onnothing
Read / writeparses 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#

Shell
# 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#

C
#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.

C
#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#

C
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.

C
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#