Discount is free software released under the terms of a BSD-style license.
If you find it useful, please consider making a contribution to help support onward development.
Discount 3.0.1.1, released 7-Sep-2025
This is my implementation of John Gruber’s Markdown text to html language. There’s not much here that differentiates it from any of the existing Markdown implementations except that it’s written in C instead of one of the vast flock of scripting languages that are fighting it out for the Perl crown.
Markdown provides a library that gives you formatting functions suitable for marking down entire documents or lines of text, a command-line program that you can use to mark down documents interactively or from a script, and a tiny (3 programs so far) suite of example programs that show how to fully utilize the markdown library.
My markdown also does, by default, various smartypants-style substitutions.
The API is not too horrifically unsimple, and is described on the API page.
I have tried to keep all of the versions of discount available on the downloads page.
I have an experimental C++ binding that
lives on Github in
mkdio.h++. It implements a couple of RAII objects;
MKIOT
– can’t call the class MMIOT
because it clashes with the C
MMIOT
it wraps – for standard markdown (plus my extensions,
of course) and GFIOT
for github-flavo(u)red markdown. Alas, it
is undocumented, but the mkdio.h++
header file is pretty simple and
a trivial program that uses it is included in the mkdio.h++
sccs tree.
text
‘’ is translated to “text”."double-quoted text"
becomes “double-quoted text”'single-quoted text'
becomes ‘single-quoted text’don't
is “don’t.” as well as anything-else’t.
(But foo'tbar
is just foo'tbar.)it's
is “it’s,” as well as anything-else’s
(except not foo'sbar and the like.)(tm)
becomes ™(r)
becomes ®(c)
becomes ©1/4th
? ¼th. Ditto for 1/4
(¼), 1/2
(½),
3/4ths
(¾ths), and 3/4
(¾)....
becomes …. . .
also becomes …---
becomes —--
becomes –A^B
becomes AB. Complex superscripts can
be enclosed in ()
s, so A^(B+2)
becomes AB+2.My markdown was written so I could replace the fairly gross homemade
text to html prettifier that I wrote for annotations, so I’ve extended
it in a few ways; I’ve put support for paragraph centering in
so that I don’t have to hand enter the <center>
and </center>
tags
(nowadays I generate a css-styled <p>
block, because that’s xhtml
compatible instead of the now-depreciated <center>
block element.)
I’ve added support for specifying image sizes, and I’ve written a
not-earthshatteringly-horrible markup extension for definition lists.
->
and <-
.
->this is a test<-
produces
this is a test
=
widthx
height field to the image tag:

produces
=
characters, then put the body of the list
item on the next line, indented 4 spaces.
=hey!= This is a definition list
produces
<dt>hey!</dt> <dd>This is a definition list</dd>
A definition list label is just a regular line of markdown code, so you can put links and images into it.
In discount 1.2.3, the definition list syntax has been
extended so that you can define sequential <dt>
blocks by doing
=tag1=
=tag2=
data.
which generates
<dt>tag1</dt>
<dt>tag2</dt>
<dd>data.</dd>
(If you want a definition list with a trailing empty tag, give it a body that’s just a html comment, like:
=placeholder!= <!-- this space intentionally left blank -->
produces
<dt>placeholder!</dt> <dd><!-- this space intentionally left blank --></dd>
In discount 2.0.4 I extended the definition list syntax to allow php markdown extra definition lists which means that source like
tag1
: data
now generates
<dt>tag1</dt>
<dd>data</dd>
Ordered lists with alphabetic labels (enabled by --enable-alpha-list
during configuration) are supported in the same way that numeric ordered
lists are:
a. first item
b. second item
generates
I wanted to be able to apply styles inline without having
to manually enter the <span class="
xxx">
…</span>
html. So I redid the [][]
code to support some new
“protocols” within my markdown:
abbr:
description<abbr title="
description">
…</abbr>
class:
name<span class="
name">
…</span>
id:
name<a id="
name">
…</a>
raw:
textText will be written verbatim to the output. The protocol
was inspired by a short thread on the markdown mailing list
about someone wanting to embed LaTeX inside <!-- -->
and
finding, to their distress, that markdown mangled it.
Passing text through in comments seems to be a path to unreadable madness, so I didn’t want to do that. This is, to my mind, a better solution.
<style>
…</style>
blocks and set them aside for printing
via mkd_style()
.> %class%
will become
<div class="
class">
instead of a <blockquote>
.PHP Markdown Extra-style tables are supported;
aaa | bbbb
-----|------
hello|sailor
becomes the following table:
aaa | bbbb |
---|---|
hello | sailor |
And much of the rest of the current table syntax (alignment, handling of orphan columns) follows the PHP Markdown Extra spec.
Pandoc-style document headers are supported; if the first three
lines in the document begin with a %
character, they are taken
to be a document header in the form of
% Document title
% Document author
% Document date
and can be retrieved by the library functions
mkd_doc_title()
, mkd_doc_author()
, and mkd_doc_date()
.
Note that I implement Pandoc document headers as they were documented in 2008; any Pandoc changes since then will not be reflected in my implementation.
If called with the MKD_FENCEDCODE
option, Pandoc-style
fenced code blocks are supported; blocks of code wrapped in ~~~
lines are treated as code just as if it was indented the traditional
4 spaces. Github-flavored-markdown fenced code blocks (blocks wrapped
in backtick lines) are also supported.
Both of these formats support the github-flavored-markdown class extension where you can put a word at the end of the opening backtick line and have the block given that class.
If called with the MKD_LATEX
option, text wrapped in $$
…$$
,
\[
…\]
, and \(
…\)
is passed unchanged (except for encoding <
,
>
, and &
) to the output for processing by a LaTeX renderer.
This collides with how Markdown escapes ‘[’, ‘]’, ‘(’, and ‘)’ – if discount is called with MKD_LATEX
, \(
and \[
will only map to (
and [
if corresponding \)
or \]
s are not found in the same paragraph.
--github-checkbox
flag, discount will understand
github-style checkboxes
and generate checkboxes using either html entities (--github-checkbox
w/o an argument) or <input>
elements (--github-checkbox=input
)When I run the standard test suite (version 1.0.3) from
daringfireball, MarkdownTest.pl
reports:
$ MARKDOWN_FLAGS=0x20004 ./MarkdownTest.pl --tidy --script=/usr/local/bin/markdown Amps and angle encoding ... OK Auto links ... OK Backslash escapes ... OK Blockquotes with code blocks ... OK Code Blocks ... OK Code Spans ... OK Hard-wrapped paragraphs with list-like lines ... OK Horizontal rules ... OK Inline HTML (Advanced) ... OK Inline HTML (Simple) ... OK Inline HTML comments ... OK Links, inline style ... OK Links, reference style ... OK Links, shortcut references ... OK Literal quotes in titles ... OK Markdown Documentation - Basics ... OK Markdown Documentation - Syntax ... OK Nested blockquotes ... OK Ordered and unordered lists ... OK Strong and em together ... OK Tabs ... OK Tidyness ... OK 22 passed; 0 failed.
When I run the old standard test suite from daringfireball,
MarkdownTest.pl
reports:
$ MARKDOWN_FLAGS=0x22004 ./MarkdownTest.pl --tidy --script=/usr/local/bin/markdown Amps and angle encoding ... OK Auto links ... OK Backslash escapes ... OK Blockquotes with code blocks ... OK Hard-wrapped paragraphs with list-like lines ... OK Horizontal rules ... OK Inline HTML (Advanced) ... OK Inline HTML (Simple) ... OK Inline HTML comments ... OK Links, inline style ... OK Links, reference style ... OK Literal quotes in titles ... OK Markdown Documentation - Basics ... OK Markdown Documentation - Syntax ... OK Nested blockquotes ... OK Ordered and unordered lists ... OK Strong and em together ... OK Tabs ... OK Tidyness ... OK 19 passed; 0 failed.
Most of the “how to get standards compliant” changes that went in were cleaning up corner cases and blatant misreading of the spec, but there were two places where I had to do a horrible hack to get compliant:
mkd_compile()
so that it would have top-level
paragraphs absorb adjacent list items, but I had to retain the
old (and, IMO, correct) behavior of a new list forcing a block
break within indented (quoted, inside lists) blocks..MKD_1_COMPAT
(0x2000) turns it on again for testing purposes.By default, yes, it does. The habit of compensating for broken editors that give no way to indent except for tabbing by setting tabstops to 4 is so intertwined with this language that treating tabs properly would be the moral equivalent of dropping nuclear devices into the testsuite.
But if you use a proper tabstop (8 characters), you can configure
markdown with --with-tabstop
and it will expand tabs to 8
spaces. If you’ve configured your markdown like this (markdown -V
will report TAB=
8) and you need to mark up text from other
sources, you can set the input flag MKD_TABSTOP
to revert those
documents back to the icky standard 4-space tab.