diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,6 @@
+# Revision history for djot
+
+## 0.1.0.0 -- 2024-02-14
+
+* Initial release.
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2023 John MacFarlane
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/app/Main.hs b/app/Main.hs
new file mode 100644
--- /dev/null
+++ b/app/Main.hs
@@ -0,0 +1,97 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE Strict #-}
+module Main where
+
+import qualified Data.ByteString as B
+import Data.ByteString.Builder (hPutBuilder)
+import Djot ( ParseOptions(..), RenderOptions(..), SourcePosOption(..),
+              parseDoc, renderHtml, renderDjot )
+import System.Environment (getArgs)
+import System.IO (stderr, stdout, hPutStrLn)
+import System.Exit ( ExitCode(ExitFailure), exitWith, exitSuccess )
+import Text.DocLayout (render)
+import Text.Read (readMaybe)
+import qualified Data.Text.IO as TIO
+
+data OutputFormat = Html | Djot | Ast
+  deriving (Eq, Show)
+
+data WrapOption = Auto | Preserve | NoWrap
+  deriving (Eq, Show)
+
+data Opts =
+      Opts{ format :: OutputFormat
+          , files :: [FilePath]
+          , wrap :: WrapOption
+          , columns :: Int
+          , sourcePos :: SourcePosOption }
+
+parseOpts :: [String] -> IO Opts
+parseOpts = go Opts{ format = Html, files = [], wrap = Preserve, columns = 72,
+                     sourcePos = NoSourcePos }
+ where
+   go opts [] = pure opts
+   go opts ("--wrap" : as) =
+     case as of
+       "auto" : as' -> go opts{ wrap = Auto } as'
+       "preserve" : as' -> go opts{ wrap = Preserve } as'
+       "none" : as' -> go opts{ wrap = NoWrap } as'
+       _ -> err "--wrap must be followed by auto, preserve, or none"
+   go opts ("--columns" : as) =
+     case as of
+       (a:as') | Just n <- readMaybe a
+         -> go opts{ columns = n } as'
+       _ -> err "--columns must be followed by a number"
+   go opts ("--to" : as) =
+     case as of
+       "djot" : as' -> go opts{ format = Djot } as'
+       "html" : as' -> go opts{ format = Html } as'
+       "ast" : as' -> go opts{ format = Ast } as'
+       _ -> err "--to must be followed by djot, html, or ast"
+   go opts ("--sourcepos" : as) =
+     case as of
+       ("none":as') -> go opts{ sourcePos = NoSourcePos } as'
+       ("block":as') -> go opts{ sourcePos = BlockSourcePos } as'
+       ("all":as') -> go opts{ sourcePos = AllSourcePos } as'
+       _ -> err "--sourcepos takes an argument (none|block|all)"
+   go _opts ("--help" : _) = do
+     putStrLn "djoths [options] [files]"
+     putStrLn "  --to djot|html*|ast"
+     putStrLn "  --wrap auto|preserve*|none"
+     putStrLn "  --columns NUMBER"
+     putStrLn "  --sourcepos none*|block|all"
+     putStrLn "  --help"
+     exitSuccess
+   go opts (xs@('-':_) : as) =
+     case break (== '=') xs of  -- support e.g. '--columns=33'
+       (ys, '=':zs) -> go opts (ys : zs : as)
+       _ -> err $ "Unknown option " <> ('-':xs)
+   go opts (f : as) = go opts{ files = files opts ++ [f] } as
+
+err :: String -> IO a
+err msg = do
+  hPutStrLn stderr msg
+  exitWith $ ExitFailure 1
+
+main :: IO ()
+main = do
+  opts <- getArgs >>= parseOpts
+  bs <- case files opts of
+          [] -> B.getContents
+          fs  -> mconcat <$> mapM B.readFile fs
+  let popts = ParseOptions { sourcePositions = sourcePos opts }
+  let ropts = RenderOptions { preserveSoftBreaks = wrap opts == Preserve }
+  case parseDoc popts bs of
+    Right doc -> do
+      case format opts of
+        Html -> hPutBuilder stdout $ renderHtml ropts doc
+        Djot -> TIO.putStr $ render (case wrap opts of
+                                       NoWrap -> Nothing
+                                       Preserve -> Nothing
+                                       Auto -> Just (columns opts))
+                           $ renderDjot ropts doc
+        Ast -> print doc
+      exitSuccess
+    Left e -> do
+      hPutStrLn stderr e
+      exitWith $ ExitFailure 1
diff --git a/benchmark/Main.hs b/benchmark/Main.hs
new file mode 100644
--- /dev/null
+++ b/benchmark/Main.hs
@@ -0,0 +1,49 @@
+{-# LANGUAGE CPP                 #-}
+{-# LANGUAGE OverloadedStrings   #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TupleSections #-}
+{-# LANGUAGE Strict #-}
+import Test.Tasty.Bench
+import Data.Functor.Identity  -- base >= 4.8
+import qualified Data.ByteString as B
+import Djot ( ParseOptions(..), RenderOptions(..), SourcePosOption(..),
+              parseDoc, renderHtml, renderDjot )
+import Data.ByteString.Builder ( toLazyByteString )
+import Text.DocLayout (render)
+import System.Directory
+import System.FilePath (takeExtension, (</>))
+import qualified Data.ByteString.Lazy as BL
+
+main :: IO ()
+main = do
+  fns <- filter ((== ".dj") . takeExtension) <$> listDirectory "benchmark"
+  files <- mapM (\fn -> (fn,) <$> B.readFile ("benchmark" </> fn)) fns
+  defaultMain $
+   map (\(fn, bs) ->
+     bench ("parse " <> fn) $
+       whnf (parseDoc ParseOptions{ sourcePositions = NoSourcePos }) bs)
+     files
+   ++
+   map (\(fn, bs) ->
+     bench ("parse w/ block source positions only " <> fn) $
+       whnf (parseDoc ParseOptions{ sourcePositions = BlockSourcePos }) bs)
+     files
+   ++
+   map (\(fn, bs) ->
+     bench ("parse w/ source positions " <> fn) $
+       whnf (parseDoc ParseOptions{ sourcePositions = AllSourcePos }) bs)
+     files
+   ++
+   map (\(fn, bs) ->
+     let doc = either error id $ parseDoc ParseOptions{ sourcePositions = NoSourcePos } bs
+     in bench ("renderHtml " <> fn) $
+           nf (BL.toStrict . toLazyByteString .
+               renderHtml RenderOptions{preserveSoftBreaks = True}) doc)
+     files
+   ++
+   map (\(fn, bs) ->
+     let doc = either error id $ parseDoc ParseOptions{ sourcePositions = NoSourcePos } bs
+     in bench ("renderDjot " <> fn) $
+          nf (render (Just 72) .
+          renderDjot RenderOptions{preserveSoftBreaks = True}) doc)
+     files
diff --git a/benchmark/m.dj b/benchmark/m.dj
new file mode 100644
--- /dev/null
+++ b/benchmark/m.dj
@@ -0,0 +1,7523 @@
+{#synopsis}
+# Synopsis
+
+`pandoc` \[_options_\] \[_input-file_\]…
+
+{#description}
+# Description
+
+Pandoc is a [Haskell](https://www.haskell.org) library for converting
+from one markup format to another, and a command-line tool that uses
+this library.
+
+Pandoc can convert between numerous markup and word processing formats,
+including, but not limited to, various flavors of
+[Markdown](https://daringfireball.net/projects/markdown/),
+[HTML](https://www.w3.org/html/),
+[LaTeX](https://www.latex-project.org/) and [Word
+docx](https://en.wikipedia.org/wiki/Office_Open_XML). For the full lists
+of input and output formats, see the `--from` and `--to` [options
+below](#general-options). Pandoc can also produce
+[PDF](https://www.adobe.com/pdf/) output: see [creating a
+PDF](#creating-a-pdf), below.
+
+Pandoc’s enhanced version of Markdown includes syntax for
+[tables](#tables), [definition lists](#definition-lists), [metadata
+blocks](#metadata-blocks), [footnotes](#footnotes),
+[citations](#citations), [math](#math), and much more. See below under
+[Pandoc’s Markdown](#pandocs-markdown).
+
+Pandoc has a modular design: it consists of a set of readers, which
+parse text in a given format and produce a native representation of the
+document (an _abstract syntax tree_ or AST), and a set of writers, which
+convert this native representation into a target format. Thus, adding an
+input or output format requires only adding a reader or writer. Users
+can also run custom [pandoc filters](https://pandoc.org/filters.html) to
+modify the intermediate AST.
+
+Because pandoc’s intermediate representation of a document is less
+expressive than many of the formats it converts between, one should not
+expect perfect conversions between every format and every other. Pandoc
+attempts to preserve the structural elements of a document, but not
+formatting details such as margin size. And some document elements, such
+as complex tables, may not fit into pandoc’s simple document model.
+While conversions from pandoc’s Markdown to all formats aspire to be
+perfect, conversions from formats more expressive than pandoc’s Markdown
+can be expected to be lossy.
+
+{#using-pandoc}
+## Using pandoc
+
+If no _input-files_ are specified, input is read from _stdin_. Output
+goes to _stdout_ by default. For output to a file, use the `-o` option:
+
+```
+pandoc -o output.html input.txt
+```
+
+By default, pandoc produces a document fragment. To produce a standalone
+document (e.g. a valid HTML file including `<head>` and `<body>`), use
+the `-s` or `--standalone` flag:
+
+```
+pandoc -s -o output.html input.txt
+```
+
+For more information on how standalone documents are produced, see
+[Templates](#templates) below.
+
+If multiple input files are given, pandoc will concatenate them all
+(with blank lines between them) before parsing. (Use `--file-scope` to
+parse files individually.)
+
+{#specifying-formats}
+## Specifying formats
+
+The format of the input and output can be specified explicitly using
+command-line options. The input format can be specified using the
+`-f/--from` option, the output format using the `-t/--to` option. Thus,
+to convert `hello.txt` from Markdown to LaTeX, you could type:
+
+```
+pandoc -f markdown -t latex hello.txt
+```
+
+To convert `hello.html` from HTML to Markdown:
+
+```
+pandoc -f html -t markdown hello.html
+```
+
+Supported input and output formats are listed below under
+[Options](#options) (see `-f` for input formats and `-t` for output
+formats). You can also use `pandoc --list-input-formats` and
+`pandoc --list-output-formats` to print lists of supported formats.
+
+If the input or output format is not specified explicitly, pandoc will
+attempt to guess it from the extensions of the filenames. Thus, for
+example,
+
+```
+pandoc -o hello.tex hello.txt
+```
+
+will convert `hello.txt` from Markdown to LaTeX. If no output file is
+specified (so that output goes to _stdout_), or if the output file’s
+extension is unknown, the output format will default to HTML. If no
+input file is specified (so that input comes from _stdin_), or if the
+input files’ extensions are unknown, the input format will be assumed to
+be Markdown.
+
+{#character-encoding}
+## Character encoding
+
+Pandoc uses the UTF-8 character encoding for both input and output. If
+your local character encoding is not UTF-8, you should pipe input and
+output through [`iconv`](https://www.gnu.org/software/libiconv/):
+
+```
+iconv -t utf-8 input.txt | pandoc | iconv -f utf-8
+```
+
+Note that in some output formats (such as HTML, LaTeX, ConTeXt, RTF,
+OPML, DocBook, and Texinfo), information about the character encoding is
+included in the document header, which will only be included if you use
+the `-s/--standalone` option.
+
+{#creating-a-pdf}
+## Creating a PDF
+
+To produce a PDF, specify an output file with a `.pdf` extension:
+
+```
+pandoc test.txt -o test.pdf
+```
+
+By default, pandoc will use LaTeX to create the PDF, which requires that
+a LaTeX engine be installed (see `--pdf-engine` below). Alternatively,
+pandoc can use ConTeXt, roff ms, or HTML as an intermediate format. To
+do this, specify an output file with a `.pdf` extension, as before, but
+add the `--pdf-engine` option or `-t context`, `-t html`, or `-t ms` to
+the command line. The tool used to generate the PDF from the
+intermediate format may be specified using `--pdf-engine`.
+
+You can control the PDF style using variables, depending on the
+intermediate format used: see [variables for
+LaTeX](#variables-for-latex), [variables for
+ConTeXt](#variables-for-context), [variables for
+`wkhtmltopdf`](#variables-for-wkhtmltopdf), [variables for
+ms](#variables-for-ms). When HTML is used as an intermediate format, the
+output can be styled using `--css`.
+
+To debug the PDF creation, it can be useful to look at the intermediate
+representation: instead of `-o test.pdf`, use for example
+`-s -o test.tex` to output the generated LaTeX. You can then test it
+with `pdflatex test.tex`.
+
+When using LaTeX, the following packages need to be available (they are
+included with all recent versions of [TeX
+Live](https://www.tug.org/texlive/)):
+[`amsfonts`](https://ctan.org/pkg/amsfonts),
+[`amsmath`](https://ctan.org/pkg/amsmath),
+[`lm`](https://ctan.org/pkg/lm),
+[`unicode-math`](https://ctan.org/pkg/unicode-math),
+[`iftex`](https://ctan.org/pkg/iftex),
+[`listings`](https://ctan.org/pkg/listings) (if the `--listings` option
+is used), [`fancyvrb`](https://ctan.org/pkg/fancyvrb),
+[`longtable`](https://ctan.org/pkg/longtable),
+[`booktabs`](https://ctan.org/pkg/booktabs),
+[`graphicx`](https://ctan.org/pkg/graphicx) (if the document contains
+images), [`hyperref`](https://ctan.org/pkg/hyperref),
+[`xcolor`](https://ctan.org/pkg/xcolor),
+[`soul`](https://ctan.org/pkg/soul),
+[`geometry`](https://ctan.org/pkg/geometry) (with the `geometry`
+variable set), [`setspace`](https://ctan.org/pkg/setspace) (with
+`linestretch`), and [`babel`](https://ctan.org/pkg/babel) (with `lang`).
+If `CJKmainfont` is set, [`xeCJK`](https://ctan.org/pkg/xecjk) is
+needed. The use of `xelatex` or `lualatex` as the PDF engine requires
+[`fontspec`](https://ctan.org/pkg/fontspec). `lualatex` uses
+[`selnolig`](https://ctan.org/pkg/selnolig). `xelatex` uses
+[`bidi`](https://ctan.org/pkg/bidi) (with the `dir` variable set). If
+the `mathspec` variable is set, `xelatex` will use
+[`mathspec`](https://ctan.org/pkg/mathspec) instead of
+[`unicode-math`](https://ctan.org/pkg/unicode-math). The
+[`upquote`](https://ctan.org/pkg/upquote) and
+[`microtype`](https://ctan.org/pkg/microtype) packages are used if
+available, and [`csquotes`](https://ctan.org/pkg/csquotes) will be used
+for [typography](#typography) if the `csquotes` variable or metadata
+field is set to a true value. The
+[`natbib`](https://ctan.org/pkg/natbib),
+[`biblatex`](https://ctan.org/pkg/biblatex),
+[`bibtex`](https://ctan.org/pkg/bibtex), and
+[`biber`](https://ctan.org/pkg/biber) packages can optionally be used
+for [citation rendering](#citation-rendering). The following packages
+will be used to improve output quality if present, but pandoc does not
+require them to be present: [`upquote`](https://ctan.org/pkg/upquote)
+(for straight quotes in verbatim environments),
+[`microtype`](https://ctan.org/pkg/microtype) (for better spacing
+adjustments), [`parskip`](https://ctan.org/pkg/parskip) (for better
+inter-paragraph spaces), [`xurl`](https://ctan.org/pkg/xurl) (for better
+line breaks in URLs), [`bookmark`](https://ctan.org/pkg/bookmark) (for
+better PDF bookmarks), and
+[`footnotehyper`](https://ctan.org/pkg/footnotehyper) or
+[`footnote`](https://ctan.org/pkg/footnote) (to allow footnotes in
+tables).
+
+{#reading-from-the-web}
+## Reading from the Web
+
+Instead of an input file, an absolute URI may be given. In this case
+pandoc will fetch the content using HTTP:
+
+```
+pandoc -f html -t markdown https://www.fsf.org
+```
+
+It is possible to supply a custom User-Agent string or other header when
+requesting a document from a URL:
+
+```
+pandoc -f html -t markdown --request-header User-Agent:"Mozilla/5.0" \
+  https://www.fsf.org
+```
+
+{#options}
+# Options
+
+{#general-options .options}
+## General options
+
+: `-f` _FORMAT_, `-r` _FORMAT_, `--from=`_FORMAT_, `--read=`_FORMAT_
+
+  Specify input format. _FORMAT_ can be:
+
+  {#input-formats}
+  :::
+  - `bibtex` ([BibTeX](https://ctan.org/pkg/bibtex) bibliography)
+  - `biblatex` ([BibLaTeX](https://ctan.org/pkg/biblatex) bibliography)
+  - `commonmark` ([CommonMark](https://commonmark.org) Markdown)
+  - `commonmark_x` ([CommonMark](https://commonmark.org) Markdown with
+    extensions)
+  - `creole` ([Creole 1.0](http://www.wikicreole.org/wiki/Creole1.0))
+  - `csljson` ([CSL
+    JSON](https://citeproc-js.readthedocs.io/en/latest/csl-json/markup.html)
+    bibliography)
+  - `csv` ([CSV](https://tools.ietf.org/html/rfc4180) table)
+  - `tsv`
+    ([TSV](https://www.iana.org/assignments/media-types/text/tab-separated-values)
+    table)
+  - `docbook` ([DocBook](https://docbook.org))
+  - `docx` ([Word docx](https://en.wikipedia.org/wiki/Office_Open_XML))
+  - `dokuwiki` ([DokuWiki markup](https://www.dokuwiki.org/dokuwiki))
+  - `endnotexml` ([EndNote XML
+    bibliography](https://support.clarivate.com/Endnote/s/article/EndNote-XML-Document-Type-Definition))
+  - `epub` ([EPUB](http://idpf.org/epub))
+  - `fb2`
+    ([FictionBook2](http://www.fictionbook.org/index.php/Eng:XML_Schema_Fictionbook_2.1)
+    e-book)
+  - `gfm` ([GitHub-Flavored
+    Markdown](https://help.github.com/articles/github-flavored-markdown/)),
+    or the deprecated and less accurate `markdown_github`; use
+    [`markdown_github`](#markdown-variants) only if you need extensions
+    not supported in [`gfm`](#markdown-variants).
+  - `haddock` ([Haddock
+    markup](https://www.haskell.org/haddock/doc/html/ch03s08.html))
+  - `html` ([HTML](https://www.w3.org/html/))
+  - `ipynb` ([Jupyter
+    notebook](https://nbformat.readthedocs.io/en/latest/))
+  - `jats` ([JATS](https://jats.nlm.nih.gov) XML)
+  - `jira`
+    ([Jira](https://jira.atlassian.com/secure/WikiRendererHelpAction.jspa?section=all)/Confluence
+    wiki markup)
+  - `json` (JSON version of native AST)
+  - `latex` ([LaTeX](https://www.latex-project.org/))
+  - `markdown` ([Pandoc’s Markdown](#pandocs-markdown))
+  - `markdown_mmd`
+    ([MultiMarkdown](https://fletcherpenney.net/multimarkdown/))
+  - `markdown_phpextra` ([PHP Markdown
+    Extra](https://michelf.ca/projects/php-markdown/extra/))
+  - `markdown_strict` (original unextended
+    [Markdown](https://daringfireball.net/projects/markdown/))
+  - `mediawiki` ([MediaWiki
+    markup](https://www.mediawiki.org/wiki/Help:Formatting))
+  - `man` ([roff man](https://man.cx/groff_man(7)))
+  - `muse` ([Muse](https://amusewiki.org/library/manual))
+  - `native` (native Haskell)
+  - `odt` ([ODT](https://en.wikipedia.org/wiki/OpenDocument))
+  - `opml` ([OPML](http://dev.opml.org/spec2.html))
+  - `org` ([Emacs Org mode](https://orgmode.org))
+  - `ris` ([RIS](https://en.wikipedia.org/wiki/RIS_(file_format))
+    bibliography)
+  - `rtf` ([Rich Text
+    Format](https://en.wikipedia.org/wiki/Rich_Text_Format))
+  - `rst`
+    ([reStructuredText](https://docutils.sourceforge.io/docs/ref/rst/introduction.html))
+  - `t2t` ([txt2tags](https://txt2tags.org))
+  - `textile` ([Textile](https://textile-lang.com))
+  - `tikiwiki` ([TikiWiki
+    markup](https://doc.tiki.org/Wiki-Syntax-Text#The_Markup_Language_Wiki-Syntax))
+  - `twiki` ([TWiki
+    markup](https://twiki.org/cgi-bin/view/TWiki/TextFormattingRules))
+  - `typst` ([typst](https://typst.app))
+  - `vimwiki` ([Vimwiki](https://vimwiki.github.io))
+  - the path of a custom Lua reader, see [Custom readers and
+    writers](#custom-readers-and-writers) below
+  :::
+
+  Extensions can be individually enabled or disabled by appending
+  `+EXTENSION` or `-EXTENSION` to the format name. See
+  [Extensions](#extensions) below, for a list of extensions and their
+  names. See `--list-input-formats` and `--list-extensions`, below.
+: `-t` _FORMAT_, `-w` _FORMAT_, `--to=`_FORMAT_, `--write=`_FORMAT_
+
+  Specify output format. _FORMAT_ can be:
+
+  {#output-formats}
+  :::
+  - `asciidoc` (modern [AsciiDoc](https://www.methods.co.nz/asciidoc/)
+    as interpreted by [AsciiDoctor](https://asciidoctor.org/))
+  - `asciidoc_legacy` ([AsciiDoc](https://www.methods.co.nz/asciidoc/)
+    as interpreted by
+    [`asciidoc-py`](https://github.com/asciidoc-py/asciidoc-py)).
+  - `asciidoctor` (deprecated synonym for `asciidoc`)
+  - `beamer` ([LaTeX beamer](https://ctan.org/pkg/beamer) slide show)
+  - `bibtex` ([BibTeX](https://ctan.org/pkg/bibtex) bibliography)
+  - `biblatex` ([BibLaTeX](https://ctan.org/pkg/biblatex) bibliography)
+  - `chunkedhtml` (zip archive of multiple linked HTML files)
+  - `commonmark` ([CommonMark](https://commonmark.org) Markdown)
+  - `commonmark_x` ([CommonMark](https://commonmark.org) Markdown with
+    extensions)
+  - `context` ([ConTeXt](https://www.contextgarden.net/))
+  - `csljson` ([CSL
+    JSON](https://citeproc-js.readthedocs.io/en/latest/csl-json/markup.html)
+    bibliography)
+  - `docbook` or `docbook4` ([DocBook](https://docbook.org) 4)
+  - `docbook5` (DocBook 5)
+  - `docx` ([Word docx](https://en.wikipedia.org/wiki/Office_Open_XML))
+  - `dokuwiki` ([DokuWiki markup](https://www.dokuwiki.org/dokuwiki))
+  - `epub` or `epub3` ([EPUB](http://idpf.org/epub) v3 book)
+  - `epub2` (EPUB v2)
+  - `fb2`
+    ([FictionBook2](http://www.fictionbook.org/index.php/Eng:XML_Schema_Fictionbook_2.1)
+    e-book)
+  - `gfm` ([GitHub-Flavored
+    Markdown](https://help.github.com/articles/github-flavored-markdown/)),
+    or the deprecated and less accurate `markdown_github`; use
+    [`markdown_github`](#markdown-variants) only if you need extensions
+    not supported in [`gfm`](#markdown-variants).
+  - `haddock` ([Haddock
+    markup](https://www.haskell.org/haddock/doc/html/ch03s08.html))
+  - `html` or `html5` ([HTML](https://www.w3.org/html/),
+    i.e. [HTML5](https://html.spec.whatwg.org/)/XHTML [polyglot
+    markup](https://www.w3.org/TR/html-polyglot/))
+  - `html4` ([XHTML](https://www.w3.org/TR/xhtml1/) 1.0 Transitional)
+  - `icml` ([InDesign
+    ICML](https://wwwimages.adobe.com/www.adobe.com/content/dam/acom/en/devnet/indesign/sdk/cs6/idml/idml-cookbook.pdf))
+  - `ipynb` ([Jupyter
+    notebook](https://nbformat.readthedocs.io/en/latest/))
+  - `jats_archiving` ([JATS](https://jats.nlm.nih.gov) XML, Archiving
+    and Interchange Tag Set)
+  - `jats_articleauthoring` ([JATS](https://jats.nlm.nih.gov) XML,
+    Article Authoring Tag Set)
+  - `jats_publishing` ([JATS](https://jats.nlm.nih.gov) XML, Journal
+    Publishing Tag Set)
+  - `jats` (alias for `jats_archiving`)
+  - `jira`
+    ([Jira](https://jira.atlassian.com/secure/WikiRendererHelpAction.jspa?section=all)/Confluence
+    wiki markup)
+  - `json` (JSON version of native AST)
+  - `latex` ([LaTeX](https://www.latex-project.org/))
+  - `man` ([roff man](https://man.cx/groff_man(7)))
+  - `markdown` ([Pandoc’s Markdown](#pandocs-markdown))
+  - `markdown_mmd`
+    ([MultiMarkdown](https://fletcherpenney.net/multimarkdown/))
+  - `markdown_phpextra` ([PHP Markdown
+    Extra](https://michelf.ca/projects/php-markdown/extra/))
+  - `markdown_strict` (original unextended
+    [Markdown](https://daringfireball.net/projects/markdown/))
+  - `markua` ([Markua](https://leanpub.com/markua/read))
+  - `mediawiki` ([MediaWiki
+    markup](https://www.mediawiki.org/wiki/Help:Formatting))
+  - `ms` ([roff ms](https://man.cx/groff_ms(7)))
+  - `muse` ([Muse](https://amusewiki.org/library/manual))
+  - `native` (native Haskell)
+  - `odt` ([OpenOffice text
+    document](https://en.wikipedia.org/wiki/OpenDocument))
+  - `opml` ([OPML](http://dev.opml.org/spec2.html))
+  - `opendocument` ([OpenDocument](http://opendocument.xml.org))
+  - `org` ([Emacs Org mode](https://orgmode.org))
+  - `pdf` ([PDF](https://www.adobe.com/pdf/))
+  - `plain` (plain text)
+  - `pptx`
+    ([PowerPoint](https://en.wikipedia.org/wiki/Microsoft_PowerPoint)
+    slide show)
+  - `rst`
+    ([reStructuredText](https://docutils.sourceforge.io/docs/ref/rst/introduction.html))
+  - `rtf` ([Rich Text
+    Format](https://en.wikipedia.org/wiki/Rich_Text_Format))
+  - `texinfo` ([GNU Texinfo](https://www.gnu.org/software/texinfo/))
+  - `textile` ([Textile](https://textile-lang.com))
+  - `slideous` ([Slideous](https://goessner.net/articles/slideous/) HTML
+    and JavaScript slide show)
+  - `slidy` ([Slidy](https://www.w3.org/Talks/Tools/Slidy2/) HTML and
+    JavaScript slide show)
+  - `dzslides` ([DZSlides](https://paulrouget.com/dzslides/) HTML5 +
+    JavaScript slide show)
+  - `revealjs` ([reveal.js](https://revealjs.com/) HTML5 + JavaScript
+    slide show)
+  - `s5` ([S5](https://meyerweb.com/eric/tools/s5/) HTML and JavaScript
+    slide show)
+  - `tei` ([TEI Simple](https://github.com/TEIC/TEI-Simple))
+  - `typst` ([typst](https://typst.app))
+  - `xwiki` ([XWiki
+    markup](https://www.xwiki.org/xwiki/bin/view/Documentation/UserGuide/Features/XWikiSyntax/))
+  - `zimwiki` ([ZimWiki
+    markup](https://zim-wiki.org/manual/Help/Wiki_Syntax.html))
+  - the path of a custom Lua writer, see [Custom readers and
+    writers](#custom-readers-and-writers) below
+  :::
+
+  Note that `odt`, `docx`, `epub`, and `pdf` output will not be directed
+  to _stdout_ unless forced with `-o -`.
+
+  Extensions can be individually enabled or disabled by appending
+  `+EXTENSION` or `-EXTENSION` to the format name. See
+  [Extensions](#extensions) below, for a list of extensions and their
+  names. See `--list-output-formats` and `--list-extensions`, below.
+: `-o` _FILE_, `--output=`_FILE_
+
+  Write output to _FILE_ instead of _stdout_. If _FILE_ is `-`, output
+  will go to _stdout_, even if a non-textual format (`docx`, `odt`,
+  `epub2`, `epub3`) is specified. If the output format is `chunkedhtml`
+  and _FILE_ has no extension, then instead of producing a `.zip` file
+  pandoc will create a directory _FILE_ and unpack the zip archive there
+  (unless _FILE_ already exists, in which case an error will be raised).
+: `--data-dir=`_DIRECTORY_
+
+  Specify the user data directory to search for pandoc data files. If
+  this option is not specified, the default user data directory will be
+  used. On \*nix and macOS systems this will be the `pandoc`
+  subdirectory of the XDG data directory (by default,
+  `$HOME/.local/share`, overridable by setting the `XDG_DATA_HOME`
+  environment variable). If that directory does not exist and
+  `$HOME/.pandoc` exists, it will be used (for backwards compatibility).
+  On Windows the default user data directory is `%APPDATA%\pandoc`. You
+  can find the default user data directory on your system by looking at
+  the output of `pandoc --version`. Data files placed in this directory
+  (for example, `reference.odt`, `reference.docx`, `epub.css`,
+  `templates`) will override pandoc’s normal defaults. (Note that the
+  user data directory is not created by pandoc, so you will need to
+  create it yourself if you want to make use of it.)
+: `-d` _FILE_, `--defaults=`_FILE_
+
+  Specify a set of default option settings. _FILE_ is a YAML file whose
+  fields correspond to command-line option settings. All options for
+  document conversion, including input and output files, can be set
+  using a defaults file. The file will be searched for first in the
+  working directory, and then in the `defaults` subdirectory of the user
+  data directory (see `--data-dir`). The `.yaml` extension may be
+  omitted. See the section [Defaults files](#defaults-files) for more
+  information on the file format. Settings from the defaults file may be
+  overridden or extended by subsequent options on the command line.
+: `--bash-completion`
+
+  Generate a bash completion script. To enable bash completion with
+  pandoc, add this to your `.bashrc`:
+
+  ```
+  eval "$(pandoc --bash-completion)"
+  ```
+: `--verbose`
+
+  Give verbose debugging output.
+: `--quiet`
+
+  Suppress warning messages.
+: `--fail-if-warnings[=true|false]`
+
+  Exit with error status if there are any warnings.
+: `--log=`_FILE_
+
+  Write log messages in machine-readable JSON format to _FILE_. All
+  messages above DEBUG level will be written, regardless of verbosity
+  settings (`--verbose`, `--quiet`).
+: `--list-input-formats`
+
+  List supported input formats, one per line.
+: `--list-output-formats`
+
+  List supported output formats, one per line.
+: `--list-extensions`\[`=`_FORMAT_\]
+
+  List supported extensions for _FORMAT_, one per line, preceded by a
+  `+` or `-` indicating whether it is enabled by default in _FORMAT_. If
+  _FORMAT_ is not specified, defaults for pandoc’s Markdown are given.
+: `--list-highlight-languages`
+
+  List supported languages for syntax highlighting, one per line.
+: `--list-highlight-styles`
+
+  List supported styles for syntax highlighting, one per line. See
+  `--highlight-style`.
+: `-v`, `--version`
+
+  Print version.
+: `-h`, `--help`
+
+  Show usage message.
+
+{#reader-options .options}
+## Reader options
+
+: `--shift-heading-level-by=`_NUMBER_
+
+  Shift heading levels by a positive or negative integer. For example,
+  with `--shift-heading-level-by=-1`, level 2 headings become level 1
+  headings, and level 3 headings become level 2 headings. Headings
+  cannot have a level less than 1, so a heading that would be shifted
+  below level 1 becomes a regular paragraph. Exception: with a shift of
+  -N, a level-N heading at the beginning of the document replaces the
+  metadata title. `--shift-heading-level-by=-1` is a good choice when
+  converting HTML or Markdown documents that use an initial level-1
+  heading for the document title and level-2+ headings for sections.
+  `--shift-heading-level-by=1` may be a good choice for converting
+  Markdown documents that use level-1 headings for sections to HTML,
+  since pandoc uses a level-1 heading to render the document title.
+: `--base-header-level=`_NUMBER_
+
+  _Deprecated. Use `--shift-heading-level-by`=X instead, where X =
+  NUMBER - 1._ Specify the base level for headings (defaults to 1).
+: `--indented-code-classes=`_CLASSES_
+
+  Specify classes to use for indented code blocks–for example,
+  `perl,numberLines` or `haskell`. Multiple classes may be separated by
+  spaces or commas.
+: `--default-image-extension=`_EXTENSION_
+
+  Specify a default extension to use when image paths/URLs have no
+  extension. This allows you to use the same source for formats that
+  require different kinds of images. Currently this option only affects
+  the Markdown and LaTeX readers.
+: `--file-scope[=true|false]`
+
+  Parse each file individually before combining for multifile documents.
+  This will allow footnotes in different files with the same identifiers
+  to work as expected. If this option is set, footnotes and links will
+  not work across files. Reading binary files (docx, odt, epub) implies
+  `--file-scope`.
+
+  If two or more files are processed using `--file-scope`, prefixes
+  based on the filenames will be added to identifiers in order to
+  disambiguate them, and internal links will be adjusted accordingly.
+  For example, a header with identifier `foo` in `subdir/file1.txt` will
+  have its identifier changed to `subdir__file1.txt__foo`.
+
+  In addition, a Div with an identifier based on the filename will be
+  added around the file’s content, so that internal links to the
+  filename will point to this Div’s identifier.
+: `-F` _PROGRAM_, `--filter=`_PROGRAM_
+
+  Specify an executable to be used as a filter transforming the pandoc
+  AST after the input is parsed and before the output is written. The
+  executable should read JSON from stdin and write JSON to stdout. The
+  JSON must be formatted like pandoc’s own JSON input and output. The
+  name of the output format will be passed to the filter as the first
+  argument. Hence,
+
+  ```
+  pandoc --filter ./caps.py -t latex
+  ```
+
+  is equivalent to
+
+  ```
+  pandoc -t json | ./caps.py latex | pandoc -f json -t latex
+  ```
+
+  The latter form may be useful for debugging filters.
+
+  Filters may be written in any language. `Text.Pandoc.JSON` exports
+  `toJSONFilter` to facilitate writing filters in Haskell. Those who
+  would prefer to write filters in python can use the module
+  [`pandocfilters`](https://github.com/jgm/pandocfilters), installable
+  from PyPI. There are also pandoc filter libraries in
+  [PHP](https://github.com/vinai/pandocfilters-php),
+  [perl](https://metacpan.org/pod/Pandoc::Filter), and
+  [JavaScript/node.js](https://github.com/mvhenderson/pandoc-filter-node).
+
+  In order of preference, pandoc will look for filters in
+
+  1. a specified full or relative path (executable or non-executable),
+
+  2. `$DATADIR/filters` (executable or non-executable) where `$DATADIR`
+     is the user data directory (see `--data-dir`, above),
+
+  3. `$PATH` (executable only).
+
+  Filters, Lua-filters, and citeproc processing are applied in the order
+  specified on the command line.
+: `-L` _SCRIPT_, `--lua-filter=`_SCRIPT_
+
+  Transform the document in a similar fashion as JSON filters (see
+  `--filter`), but use pandoc’s built-in Lua filtering system. The given
+  Lua script is expected to return a list of Lua filters which will be
+  applied in order. Each Lua filter must contain element-transforming
+  functions indexed by the name of the AST element on which the filter
+  function should be applied.
+
+  The `pandoc` Lua module provides helper functions for element
+  creation. It is always loaded into the script’s Lua environment.
+
+  See the [Lua filters
+  documentation](https://pandoc.org/lua-filters.html) for further
+  details.
+
+  In order of preference, pandoc will look for Lua filters in
+
+  1. a specified full or relative path,
+
+  2. `$DATADIR/filters` where `$DATADIR` is the user data directory (see
+     `--data-dir`, above).
+
+  Filters, Lua filters, and citeproc processing are applied in the order
+  specified on the command line.
+: `-M` _KEY_\[`=`_VAL_\], `--metadata=`_KEY_\[`:`_VAL_\]
+
+  Set the metadata field _KEY_ to the value _VAL_. A value specified on
+  the command line overrides a value specified in the document using
+  [YAML metadata blocks](#extension-yaml_metadata_block). Values will be
+  parsed as YAML boolean or string values. If no value is specified, the
+  value will be treated as Boolean true. Like `--variable`, `--metadata`
+  causes template variables to be set. But unlike `--variable`,
+  `--metadata` affects the metadata of the underlying document (which is
+  accessible from filters and may be printed in some output formats) and
+  metadata values will be escaped when inserted into the template.
+: `--metadata-file=`_FILE_
+
+  Read metadata from the supplied YAML (or JSON) file. This option can
+  be used with every input format, but string scalars in the metadata
+  file will always be parsed as Markdown. (If the input format is
+  Markdown or a Markdown variant, then the same variant will be used to
+  parse the metadata file; if it is a non-Markdown format, pandoc’s
+  default Markdown extensions will be used.) This option can be used
+  repeatedly to include multiple metadata files; values in files
+  specified later on the command line will be preferred over those
+  specified in earlier files. Metadata values specified inside the
+  document, or by using `-M`, overwrite values specified with this
+  option. The file will be searched for first in the working directory,
+  and then in the `metadata` subdirectory of the user data directory
+  (see `--data-dir`).
+: `-p`, `--preserve-tabs[=true|false]`
+
+  Preserve tabs instead of converting them to spaces. (By default,
+  pandoc converts tabs to spaces before parsing its input.) Note that
+  this will only affect tabs in literal code spans and code blocks. Tabs
+  in regular text are always treated as spaces.
+: `--tab-stop=`_NUMBER_
+
+  Specify the number of spaces per tab (default is 4).
+: `--track-changes=accept`|`reject`|`all`
+
+  Specifies what to do with insertions, deletions, and comments produced
+  by the MS Word "Track Changes" feature. `accept` (the default)
+  processes all the insertions and deletions. `reject` ignores them.
+  Both `accept` and `reject` ignore comments. `all` includes all
+  insertions, deletions, and comments, wrapped in spans with
+  `insertion`, `deletion`, `comment-start`, and `comment-end` classes,
+  respectively. The author and time of change is included. `all` is
+  useful for scripting: only accepting changes from a certain reviewer,
+  say, or before a certain date. If a paragraph is inserted or deleted,
+  `track-changes=all` produces a span with the class
+  `paragraph-insertion`/`paragraph-deletion` before the affected
+  paragraph break. This option only affects the docx reader.
+: `--extract-media=`_DIR_
+
+  Extract images and other media contained in or linked from the source
+  document to the path _DIR_, creating it if necessary, and adjust the
+  images references in the document so they point to the extracted
+  files. Media are downloaded, read from the file system, or extracted
+  from a binary container (e.g. docx), as needed. The original file
+  paths are used if they are relative paths not containing `..`.
+  Otherwise filenames are constructed from the SHA1 hash of the
+  contents.
+: `--abbreviations=`_FILE_
+
+  Specifies a custom abbreviations file, with abbreviations one to a
+  line. If this option is not specified, pandoc will read the data file
+  `abbreviations` from the user data directory or fall back on a system
+  default. To see the system default, use
+  `pandoc --print-default-data-file=abbreviations`. The only use pandoc
+  makes of this list is in the Markdown reader. Strings found in this
+  list will be followed by a nonbreaking space, and the period will not
+  produce sentence-ending space in formats like LaTeX. The strings may
+  not contain spaces.
+: `--trace[=true|false]`
+
+  Print diagnostic output tracing parser progress to stderr. This option
+  is intended for use by developers in diagnosing performance issues.
+
+{#general-writer-options .options}
+## General writer options
+
+: `-s`, `--standalone`
+
+  Produce output with an appropriate header and footer (e.g. a
+  standalone HTML, LaTeX, TEI, or RTF file, not a fragment). This option
+  is set automatically for `pdf`, `epub`, `epub3`, `fb2`, `docx`, and
+  `odt` output. For `native` output, this option causes metadata to be
+  included; otherwise, metadata is suppressed.
+: `--template=`_FILE_|_URL_
+
+  Use the specified file as a custom template for the generated
+  document. Implies `--standalone`. See [Templates](#templates), below,
+  for a description of template syntax. If no extension is specified, an
+  extension corresponding to the writer will be added, so that
+  `--template=special` looks for `special.html` for HTML output. If the
+  template is not found, pandoc will search for it in the `templates`
+  subdirectory of the user data directory (see `--data-dir`). If this
+  option is not used, a default template appropriate for the output
+  format will be used (see `-D/--print-default-template`).
+: `-V` _KEY_\[`=`_VAL_\], `--variable=`_KEY_\[`:`_VAL_\]
+
+  Set the template variable _KEY_ to the value _VAL_ when rendering the
+  document in standalone mode. If no _VAL_ is specified, the key will be
+  given the value `true`.
+: `--sandbox[=true|false]`
+
+  Run pandoc in a sandbox, limiting IO operations in readers and writers
+  to reading the files specified on the command line. Note that this
+  option does not limit IO operations by filters or in the production of
+  PDF documents. But it does offer security against, for example,
+  disclosure of files through the use of `include` directives. Anyone
+  using pandoc on untrusted user input should use this option.
+
+  Note: some readers and writers (e.g., `docx`) need access to data
+  files. If these are stored on the file system, then pandoc will not be
+  able to find them when run in `--sandbox` mode and will raise an
+  error. For these applications, we recommend using a pandoc binary
+  compiled with the `embed_data_files` option, which causes the data
+  files to be baked into the binary instead of being stored on the file
+  system.
+: `-D` _FORMAT_, `--print-default-template=`_FORMAT_
+
+  Print the system default template for an output _FORMAT_. (See `-t`
+  for a list of possible _FORMAT_s.) Templates in the user data
+  directory are ignored. This option may be used with `-o`/`--output` to
+  redirect output to a file, but `-o`/`--output` must come before
+  `--print-default-template` on the command line.
+
+  Note that some of the default templates use partials, for example
+  `styles.html`. To print the partials, use `--print-default-data-file`:
+  for example, `--print-default-data-file=templates/styles.html`.
+: `--print-default-data-file=`_FILE_
+
+  Print a system default data file. Files in the user data directory are
+  ignored. This option may be used with `-o`/`--output` to redirect
+  output to a file, but `-o`/`--output` must come before
+  `--print-default-data-file` on the command line.
+: `--eol=crlf`|`lf`|`native`
+
+  Manually specify line endings: `crlf` (Windows), `lf`
+  (macOS/Linux/UNIX), or `native` (line endings appropriate to the OS on
+  which pandoc is being run). The default is `native`.
+: `--dpi`=_NUMBER_
+
+  Specify the default dpi (dots per inch) value for conversion from
+  pixels to inch/centimeters and vice versa. (Technically, the correct
+  term would be ppi: pixels per inch.) The default is 96dpi. When images
+  contain information about dpi internally, the encoded value is used
+  instead of the default specified by this option.
+: `--wrap=auto`|`none`|`preserve`
+
+  Determine how text is wrapped in the output (the source code, not the
+  rendered version). With `auto` (the default), pandoc will attempt to
+  wrap lines to the column width specified by `--columns` (default 72).
+  With `none`, pandoc will not wrap lines at all. With `preserve`,
+  pandoc will attempt to preserve the wrapping from the source document
+  (that is, where there are nonsemantic newlines in the source, there
+  will be nonsemantic newlines in the output as well). In `ipynb`
+  output, this option affects wrapping of the contents of markdown
+  cells.
+: `--columns=`_NUMBER_
+
+  Specify length of lines in characters. This affects text wrapping in
+  the generated source code (see `--wrap`). It also affects calculation
+  of column widths for plain text tables (see [Tables](#tables) below).
+: `--toc[=true|false]`, `--table-of-contents[=true|false]`
+
+  Include an automatically generated table of contents (or, in the case
+  of `latex`, `context`, `docx`, `odt`, `opendocument`, `rst`, or `ms`,
+  an instruction to create one) in the output document. This option has
+  no effect unless `-s/--standalone` is used, and it has no effect on
+  `man`, `docbook4`, `docbook5`, or `jats` output.
+
+  Note that if you are producing a PDF via `ms`, the table of contents
+  will appear at the beginning of the document, before the title. If you
+  would prefer it to be at the end of the document, use the option
+  `--pdf-engine-opt=--no-toc-relocation`.
+: `--toc-depth=`_NUMBER_
+
+  Specify the number of section levels to include in the table of
+  contents. The default is 3 (which means that level-1, 2, and 3
+  headings will be listed in the contents).
+: `--strip-comments[=true|false]`
+
+  Strip out HTML comments in the Markdown or Textile source, rather than
+  passing them on to Markdown, Textile or HTML output as raw HTML. This
+  does not apply to HTML comments inside raw HTML blocks when the
+  `markdown_in_html_blocks` extension is not set.
+: `--no-highlight`
+
+  Disables syntax highlighting for code blocks and inlines, even when a
+  language attribute is given.
+: `--highlight-style=`_STYLE_|_FILE_
+
+  Specifies the coloring style to be used in highlighted source code.
+  Options are `pygments` (the default), `kate`, `monochrome`,
+  `breezeDark`, `espresso`, `zenburn`, `haddock`, and `tango`. For more
+  information on syntax highlighting in pandoc, see [Syntax
+  highlighting](#syntax-highlighting), below. See also
+  `--list-highlight-styles`.
+
+  Instead of a _STYLE_ name, a JSON file with extension `.theme` may be
+  supplied. This will be parsed as a KDE syntax highlighting theme and
+  (if valid) used as the highlighting style.
+
+  To generate the JSON version of an existing style, use
+  `--print-highlight-style`.
+: `--print-highlight-style=`_STYLE_|_FILE_
+
+  Prints a JSON version of a highlighting style, which can be modified,
+  saved with a `.theme` extension, and used with `--highlight-style`.
+  This option may be used with `-o`/`--output` to redirect output to a
+  file, but `-o`/`--output` must come before `--print-highlight-style`
+  on the command line.
+: `--syntax-definition=`_FILE_
+
+  Instructs pandoc to load a KDE XML syntax definition file, which will
+  be used for syntax highlighting of appropriately marked code blocks.
+  This can be used to add support for new languages or to use altered
+  syntax definitions for existing languages. This option may be repeated
+  to add multiple syntax definitions.
+: `-H` _FILE_, `--include-in-header=`_FILE_|_URL_
+
+  Include contents of _FILE_, verbatim, at the end of the header. This
+  can be used, for example, to include special CSS or JavaScript in HTML
+  documents. This option can be used repeatedly to include multiple
+  files in the header. They will be included in the order specified.
+  Implies `--standalone`.
+: `-B` _FILE_, `--include-before-body=`_FILE_|_URL_
+
+  Include contents of _FILE_, verbatim, at the beginning of the document
+  body (e.g. after the `<body>` tag in HTML, or the `\begin{document}`
+  command in LaTeX). This can be used to include navigation bars or
+  banners in HTML documents. This option can be used repeatedly to
+  include multiple files. They will be included in the order specified.
+  Implies `--standalone`.
+: `-A` _FILE_, `--include-after-body=`_FILE_|_URL_
+
+  Include contents of _FILE_, verbatim, at the end of the document body
+  (before the `</body>` tag in HTML, or the `\end{document}` command in
+  LaTeX). This option can be used repeatedly to include multiple files.
+  They will be included in the order specified. Implies `--standalone`.
+: `--resource-path=`_SEARCHPATH_
+
+  List of paths to search for images and other resources. The paths
+  should be separated by `:` on Linux, UNIX, and macOS systems, and by
+  `;` on Windows. If `--resource-path` is not specified, the default
+  resource path is the working directory. Note that, if
+  `--resource-path` is specified, the working directory must be
+  explicitly listed or it will not be searched. For example:
+  `--resource-path=.:test` will search the working directory and the
+  `test` subdirectory, in that order. This option can be used
+  repeatedly. Search path components that come later on the command line
+  will be searched before those that come earlier, so
+  `--resource-path foo:bar --resource-path baz:bim` is equivalent to
+  `--resource-path baz:bim:foo:bar`.
+: `--request-header=`_NAME_`:`_VAL_
+
+  Set the request header _NAME_ to the value _VAL_ when making HTTP
+  requests (for example, when a URL is given on the command line, or
+  when resources used in a document must be downloaded). If you’re
+  behind a proxy, you also need to set the environment variable
+  `http_proxy` to `http://...`.
+: `--no-check-certificate[=true|false]`
+
+  Disable the certificate verification to allow access to unsecure HTTP
+  resources (for example when the certificate is no longer valid or self
+  signed).
+
+{#options-affecting-specific-writers .options}
+## Options affecting specific writers
+
+: `--self-contained[=true|false]`
+
+  _Deprecated synonym for `--embed-resources --standalone`._
+: `--embed-resources[=true|false]`
+
+  Produce a standalone HTML file with no external dependencies, using
+  `data:` URIs to incorporate the contents of linked scripts,
+  stylesheets, images, and videos. The resulting file should be
+  "self-contained," in the sense that it needs no external files and no
+  net access to be displayed properly by a browser. This option works
+  only with HTML output formats, including `html4`, `html5`, `html+lhs`,
+  `html5+lhs`, `s5`, `slidy`, `slideous`, `dzslides`, and `revealjs`.
+  Scripts, images, and stylesheets at absolute URLs will be downloaded;
+  those at relative URLs will be sought relative to the working
+  directory (if the first source file is local) or relative to the base
+  URL (if the first source file is remote). Elements with the attribute
+  `data-external="1"` will be left alone; the documents they link to
+  will not be incorporated in the document. Limitation: resources that
+  are loaded dynamically through JavaScript cannot be incorporated; as a
+  result, fonts may be missing when `--mathjax` is used, and some
+  advanced features (e.g. zoom or speaker notes) may not work in an
+  offline "self-contained" `reveal.js` slide show.
+: `--html-q-tags[=true|false]`
+
+  Use `<q>` tags for quotes in HTML. (This option only has an effect if
+  the `smart` extension is enabled for the input format used.)
+: `--ascii[=true|false]`
+
+  Use only ASCII characters in output. Currently supported for XML and
+  HTML formats (which use entities instead of UTF-8 when this option is
+  selected), CommonMark, gfm, and Markdown (which use entities), roff
+  man and ms (which use hexadecimal escapes), and to a limited degree
+  LaTeX (which uses standard commands for accented characters when
+  possible).
+: `--reference-links[=true|false]`
+
+  Use reference-style links, rather than inline links, in writing
+  Markdown or reStructuredText. By default inline links are used. The
+  placement of link references is affected by the `--reference-location`
+  option.
+: `--reference-location=block`|`section`|`document`
+
+  Specify whether footnotes (and references, if `reference-links` is
+  set) are placed at the end of the current (top-level) block, the
+  current section, or the document. The default is `document`. Currently
+  this option only affects the `markdown`, `muse`, `html`, `epub`,
+  `slidy`, `s5`, `slideous`, `dzslides`, and `revealjs` writers. In
+  slide formats, specifying `--reference-location=section` will cause
+  notes to be rendered at the bottom of a slide.
+: `--markdown-headings=setext`|`atx`
+
+  Specify whether to use ATX-style (`#`-prefixed) or Setext-style
+  (underlined) headings for level 1 and 2 headings in Markdown output.
+  (The default is `atx`.) ATX-style headings are always used for levels
+  3+. This option also affects Markdown cells in `ipynb` output.
+: `--list-tables[=true|false]`
+
+  Render tables as list tables in RST output.
+: `--top-level-division=default`|`section`|`chapter`|`part`
+
+  Treat top-level headings as the given division type in LaTeX, ConTeXt,
+  DocBook, and TEI output. The hierarchy order is part, chapter, then
+  section; all headings are shifted such that the top-level heading
+  becomes the specified type. The default behavior is to determine the
+  best division type via heuristics: unless other conditions apply,
+  `section` is chosen. When the `documentclass` variable is set to
+  `report`, `book`, or `memoir` (unless the `article` option is
+  specified), `chapter` is implied as the setting for this option. If
+  `beamer` is the output format, specifying either `chapter` or `part`
+  will cause top-level headings to become `\part{..}`, while
+  second-level headings remain as their default type.
+: `-N`, `--number-sections`
+
+  Number section headings in LaTeX, ConTeXt, HTML, Docx, ms, or EPUB
+  output. By default, sections are not numbered. Sections with class
+  `unnumbered` will never be numbered, even if `--number-sections` is
+  specified.
+: `--number-offset=`_NUMBER_\[`,`_NUMBER_`,`_…_\]
+
+  Offset for section headings in HTML output (ignored in other output
+  formats). The first number is added to the section number for
+  top-level headings, the second for second-level headings, and so on.
+  So, for example, if you want the first top-level heading in your
+  document to be numbered "6", specify `--number-offset=5`. If your
+  document starts with a level-2 heading which you want to be numbered
+  "1.5", specify `--number-offset=1,4`. Offsets are 0 by default.
+  Implies `--number-sections`.
+: `--listings[=true|false]`
+
+  Use the [`listings`](https://ctan.org/pkg/listings) package for LaTeX
+  code blocks. The package does not support multi-byte encoding for
+  source code. To handle UTF-8 you would need to use a custom template.
+  This issue is fully documented here: [Encoding issue with the listings
+  package](https://en.wikibooks.org/wiki/LaTeX/Source_Code_Listings#Encoding_issue).
+: `-i`, `--incremental[=true|false]`
+
+  Make list items in slide shows display incrementally (one by one). The
+  default is for lists to be displayed all at once.
+: `--slide-level=`_NUMBER_
+
+  Specifies that headings with the specified level create slides (for
+  `beamer`, `s5`, `slidy`, `slideous`, `dzslides`). Headings above this
+  level in the hierarchy are used to divide the slide show into
+  sections; headings below this level create subheads within a slide.
+  Valid values are 0-6. If a slide level of 0 is specified, slides will
+  not be split automatically on headings, and horizontal rules must be
+  used to indicate slide boundaries. If a slide level is not specified
+  explicitly, the slide level will be set automatically based on the
+  contents of the document; see [Structuring the slide
+  show](#structuring-the-slide-show).
+: `--section-divs[=true|false]`
+
+  Wrap sections in `<section>` tags (or `<div>` tags for `html4`), and
+  attach identifiers to the enclosing `<section>` (or `<div>`) rather
+  than the heading itself (see [Heading
+  identifiers](#heading-identifiers), below). This option only affects
+  HTML output (and does not affect HTML slide formats).
+: `--email-obfuscation=none`|`javascript`|`references`
+
+  Specify a method for obfuscating `mailto:` links in HTML documents.
+  `none` leaves `mailto:` links as they are. `javascript` obfuscates
+  them using JavaScript. `references` obfuscates them by printing their
+  letters as decimal or hexadecimal character references. The default is
+  `none`.
+: `--id-prefix=`_STRING_
+
+  Specify a prefix to be added to all identifiers and internal links in
+  HTML and DocBook output, and to footnote numbers in Markdown and
+  Haddock output. This is useful for preventing duplicate identifiers
+  when generating fragments to be included in other pages.
+: `-T` _STRING_, `--title-prefix=`_STRING_
+
+  Specify _STRING_ as a prefix at the beginning of the title that
+  appears in the HTML header (but not in the title as it appears at the
+  beginning of the HTML body). Implies `--standalone`.
+: `-c` _URL_, `--css=`_URL_
+
+  Link to a CSS style sheet. This option can be used repeatedly to
+  include multiple files. They will be included in the order specified.
+  This option only affects HTML (including HTML slide shows) and EPUB
+  output. It should be used together with `-s/--standalone`, because the
+  link to the stylesheet goes in the document header.
+
+  A stylesheet is required for generating EPUB. If none is provided
+  using this option (or the `css` or `stylesheet` metadata fields),
+  pandoc will look for a file `epub.css` in the user data directory (see
+  `--data-dir`). If it is not found there, sensible defaults will be
+  used.
+: `--reference-doc=`_FILE_|_URL_
+
+  Use the specified file as a style reference in producing a docx or ODT
+  file.
+
+  : Docx
+
+    For best results, the reference docx should be a modified version of
+    a docx file produced using pandoc. The contents of the reference
+    docx are ignored, but its stylesheets and document properties
+    (including margins, page size, header, and footer) are used in the
+    new docx. If no reference docx is specified on the command line,
+    pandoc will look for a file `reference.docx` in the user data
+    directory (see `--data-dir`). If this is not found either, sensible
+    defaults will be used.
+
+    To produce a custom `reference.docx`, first get a copy of the
+    default `reference.docx`:
+    `pandoc -o custom-reference.docx --print-default-data-file reference.docx`.
+    Then open `custom-reference.docx` in Word, modify the styles as you
+    wish, and save the file. For best results, do not make changes to
+    this file other than modifying the styles used by pandoc:
+
+    Paragraph styles:
+
+    - Normal
+    - Body Text
+    - First Paragraph
+    - Compact
+    - Title
+    - Subtitle
+    - Author
+    - Date
+    - Abstract
+    - AbstractTitle
+    - Bibliography
+    - Heading 1
+    - Heading 2
+    - Heading 3
+    - Heading 4
+    - Heading 5
+    - Heading 6
+    - Heading 7
+    - Heading 8
+    - Heading 9
+    - Block Text
+    - Source Code
+    - Footnote Text
+    - Definition Term
+    - Definition
+    - Caption
+    - Table Caption
+    - Image Caption
+    - Figure
+    - Captioned Figure
+    - TOC Heading
+
+    Character styles:
+
+    - Default Paragraph Font
+    - Body Text Char
+    - Verbatim Char
+    - Footnote Reference
+    - Hyperlink
+    - Section Number
+
+    Table style:
+
+    - Table
+  : ODT
+
+    For best results, the reference ODT should be a modified version of
+    an ODT produced using pandoc. The contents of the reference ODT are
+    ignored, but its stylesheets are used in the new ODT. If no
+    reference ODT is specified on the command line, pandoc will look for
+    a file `reference.odt` in the user data directory (see
+    `--data-dir`). If this is not found either, sensible defaults will
+    be used.
+
+    To produce a custom `reference.odt`, first get a copy of the default
+    `reference.odt`:
+    `pandoc -o custom-reference.odt --print-default-data-file reference.odt`.
+    Then open `custom-reference.odt` in LibreOffice, modify the styles
+    as you wish, and save the file.
+  : PowerPoint
+
+    Templates included with Microsoft PowerPoint 2013 (either with
+    `.pptx` or `.potx` extension) are known to work, as are most
+    templates derived from these.
+
+    The specific requirement is that the template should contain layouts
+    with the following names (as seen within PowerPoint):
+
+    - Title Slide
+    - Title and Content
+    - Section Header
+    - Two Content
+    - Comparison
+    - Content with Caption
+    - Blank
+
+    For each name, the first layout found with that name will be used.
+    If no layout is found with one of the names, pandoc will output a
+    warning and use the layout with that name from the default reference
+    doc instead. (How these layouts are used is described in [PowerPoint
+    layout choice](#powerpoint-layout-choice).)
+
+    All templates included with a recent version of MS PowerPoint will
+    fit these criteria. (You can click on `Layout` under the `Home` menu
+    to check.)
+
+    You can also modify the default `reference.pptx`: first run
+    `pandoc -o custom-reference.pptx --print-default-data-file reference.pptx`,
+    and then modify `custom-reference.pptx` in MS PowerPoint (pandoc
+    will use the layouts with the names listed above).
+: `--split-level=`_NUMBER_
+
+  Specify the heading level at which to split an EPUB or chunked HTML
+  document into separate files. The default is to split into chapters at
+  level-1 headings. In the case of EPUB, this option only affects the
+  internal composition of the EPUB, not the way chapters and sections
+  are displayed to users. Some readers may be slow if the chapter files
+  are too large, so for large documents with few level-1 headings, one
+  might want to use a chapter level of 2 or 3. For chunked HTML, this
+  option determines how much content goes in each "chunk."
+: `--chunk-template=`_PATHTEMPLATE_
+
+  Specify a template for the filenames in a `chunkedhtml` document. In
+  the template, `%n` will be replaced by the chunk number (padded with
+  leading 0s to 3 digits), `%s` with the section number of the chunk,
+  `%h` with the heading text (with formatting removed), `%i` with the
+  section identifier. For example, `%section-%s-%i.html` might be
+  resolved to `section-1.1-introduction.html`. The characters `/` and
+  `\` are not allowed in chunk templates and will be ignored. The
+  default is `%s-%i.html`.
+: `--epub-chapter-level=`_NUMBER_
+
+  _Deprecated synonym for `--split-level`._
+: `--epub-cover-image=`_FILE_
+
+  Use the specified image as the EPUB cover. It is recommended that the
+  image be less than 1000px in width and height. Note that in a Markdown
+  source document you can also specify `cover-image` in a YAML metadata
+  block (see [EPUB Metadata](#epub-metadata), below).
+: `--epub-title-page=true`|`false`
+
+  Determines whether a the title page is included in the EPUB (default
+  is `true`).
+: `--epub-metadata=`_FILE_
+
+  Look in the specified XML file for metadata for the EPUB. The file
+  should contain a series of [Dublin Core
+  elements](https://www.dublincore.org/specifications/dublin-core/dces/).
+  For example:
+
+  ```
+   <dc:rights>Creative Commons</dc:rights>
+   <dc:language>es-AR</dc:language>
+  ```
+
+  By default, pandoc will include the following metadata elements:
+  `<dc:title>` (from the document title), `<dc:creator>` (from the
+  document authors), `<dc:date>` (from the document date, which should
+  be in [ISO 8601 format](https://www.w3.org/TR/NOTE-datetime)),
+  `<dc:language>` (from the `lang` variable, or, if is not set, the
+  locale), and `<dc:identifier id="BookId">` (a randomly generated
+  UUID). Any of these may be overridden by elements in the metadata
+  file.
+
+  Note: if the source document is Markdown, a YAML metadata block in the
+  document can be used instead. See below under [EPUB
+  Metadata](#epub-metadata).
+: `--epub-embed-font=`_FILE_
+
+  Embed the specified font in the EPUB. This option can be repeated to
+  embed multiple fonts. Wildcards can also be used: for example,
+  `DejaVuSans-*.ttf`. However, if you use wildcards on the command line,
+  be sure to escape them or put the whole filename in single quotes, to
+  prevent them from being interpreted by the shell. To use the embedded
+  fonts, you will need to add declarations like the following to your
+  CSS (see `--css`):
+
+  ```
+  @font-face {
+     font-family: DejaVuSans;
+     font-style: normal;
+     font-weight: normal;
+     src:url("../fonts/DejaVuSans-Regular.ttf");
+  }
+  @font-face {
+     font-family: DejaVuSans;
+     font-style: normal;
+     font-weight: bold;
+     src:url("../fonts/DejaVuSans-Bold.ttf");
+  }
+  @font-face {
+     font-family: DejaVuSans;
+     font-style: italic;
+     font-weight: normal;
+     src:url("../fonts/DejaVuSans-Oblique.ttf");
+  }
+  @font-face {
+     font-family: DejaVuSans;
+     font-style: italic;
+     font-weight: bold;
+     src:url("../fonts/DejaVuSans-BoldOblique.ttf");
+  }
+  body { font-family: "DejaVuSans"; }
+  ```
+: `--epub-subdirectory=`_DIRNAME_
+
+  Specify the subdirectory in the OCF container that is to hold the
+  EPUB-specific contents. The default is `EPUB`. To put the EPUB
+  contents in the top level, use an empty string.
+: `--ipynb-output=all|none|best`
+
+  Determines how ipynb output cells are treated. `all` means that all of
+  the data formats included in the original are preserved. `none` means
+  that the contents of data cells are omitted. `best` causes pandoc to
+  try to pick the richest data block in each output cell that is
+  compatible with the output format. The default is `best`.
+: `--pdf-engine=`_PROGRAM_
+
+  Use the specified engine when producing PDF output. Valid values are
+  `pdflatex`, `lualatex`, `xelatex`, `latexmk`, `tectonic`,
+  `wkhtmltopdf`, `weasyprint`, `pagedjs-cli`, `prince`, `context`,
+  `pdfroff`, and `typst`. If the engine is not in your PATH, the full
+  path of the engine may be specified here. If this option is not
+  specified, pandoc uses the following defaults depending on the output
+  format specified using `-t/--to`:
+
+  - `-t latex` or none: `pdflatex` (other options: `xelatex`,
+    `lualatex`, `tectonic`, `latexmk`)
+  - `-t context`: `context`
+  - `-t html`: `wkhtmltopdf` (other options: `prince`, `weasyprint`,
+    `pagedjs-cli`; see [print-css.rocks](https://print-css.rocks) for a
+    good introduction to PDF generation from HTML/CSS)
+  - `-t ms`: `pdfroff`
+  - `-t typst`: `typst`
+: `--pdf-engine-opt=`_STRING_
+
+  Use the given string as a command-line argument to the `pdf-engine`.
+  For example, to use a persistent directory `foo` for `latexmk`’s
+  auxiliary files, use `--pdf-engine-opt=-outdir=foo`. Note that no
+  check for duplicate options is done.
+
+{#citation-rendering .options}
+## Citation rendering
+
+: `-C`, `--citeproc`
+
+  Process the citations in the file, replacing them with rendered
+  citations and adding a bibliography. Citation processing will not take
+  place unless bibliographic data is supplied, either through an
+  external file specified using the `--bibliography` option or the
+  `bibliography` field in metadata, or via a `references` section in
+  metadata containing a list of citations in CSL YAML format with
+  Markdown formatting. The style is controlled by a
+  [CSL](https://docs.citationstyles.org/en/stable/specification.html)
+  stylesheet specified using the `--csl` option or the `csl` field in
+  metadata. (If no stylesheet is specified, the `chicago-author-date`
+  style will be used by default.) The citation processing transformation
+  may be applied before or after filters or Lua filters (see `--filter`,
+  `--lua-filter`): these transformations are applied in the order they
+  appear on the command line. For more information, see the section on
+  [Citations](#citations).
+: `--bibliography=`_FILE_
+
+  Set the `bibliography` field in the document’s metadata to _FILE_,
+  overriding any value set in the metadata. If you supply this argument
+  multiple times, each _FILE_ will be added to bibliography. If _FILE_
+  is a URL, it will be fetched via HTTP. If _FILE_ is not found relative
+  to the working directory, it will be sought in the resource path (see
+  `--resource-path`).
+: `--csl=`_FILE_
+
+  Set the `csl` field in the document’s metadata to _FILE_, overriding
+  any value set in the metadata. (This is equivalent to
+  `--metadata csl=FILE`.) If _FILE_ is a URL, it will be fetched via
+  HTTP. If _FILE_ is not found relative to the working directory, it
+  will be sought in the resource path (see `--resource-path`) and
+  finally in the `csl` subdirectory of the pandoc user data directory.
+: `--citation-abbreviations=`_FILE_
+
+  Set the `citation-abbreviations` field in the document’s metadata to
+  _FILE_, overriding any value set in the metadata. (This is equivalent
+  to `--metadata citation-abbreviations=FILE`.) If _FILE_ is a URL, it
+  will be fetched via HTTP. If _FILE_ is not found relative to the
+  working directory, it will be sought in the resource path (see
+  `--resource-path`) and finally in the `csl` subdirectory of the pandoc
+  user data directory.
+: `--natbib`
+
+  Use [`natbib`](https://ctan.org/pkg/natbib) for citations in LaTeX
+  output. This option is not for use with the `--citeproc` option or
+  with PDF output. It is intended for use in producing a LaTeX file that
+  can be processed with [`bibtex`](https://ctan.org/pkg/bibtex).
+: `--biblatex`
+
+  Use [`biblatex`](https://ctan.org/pkg/biblatex) for citations in LaTeX
+  output. This option is not for use with the `--citeproc` option or
+  with PDF output. It is intended for use in producing a LaTeX file that
+  can be processed with [`bibtex`](https://ctan.org/pkg/bibtex) or
+  [`biber`](https://ctan.org/pkg/biber).
+
+{#math-rendering-in-html .options}
+## Math rendering in HTML
+
+The default is to render TeX math as far as possible using Unicode
+characters. Formulas are put inside a `span` with `class="math"`, so
+that they may be styled differently from the surrounding text if needed.
+However, this gives acceptable results only for basic math, usually you
+will want to use `--mathjax` or another of the following options.
+
+: `--mathjax`\[`=`_URL_\]
+
+  Use [MathJax](https://www.mathjax.org) to display embedded TeX math in
+  HTML output. TeX math will be put between `\(...\)` (for inline math)
+  or `\[...\]` (for display math) and wrapped in `<span>` tags with
+  class `math`. Then the MathJax JavaScript will render it. The _URL_
+  should point to the `MathJax.js` load script. If a _URL_ is not
+  provided, a link to the Cloudflare CDN will be inserted.
+: `--mathml`
+
+  Convert TeX math to [MathML](https://www.w3.org/Math/) (in `epub3`,
+  `docbook4`, `docbook5`, `jats`, `html4` and `html5`). This is the
+  default in `odt` output. MathML is supported natively by the main web
+  browsers and select e-book readers.
+: `--webtex`\[`=`_URL_\]
+
+  Convert TeX formulas to `<img>` tags that link to an external script
+  that converts formulas to images. The formula will be URL-encoded and
+  concatenated with the URL provided. For SVG images you can for example
+  use `--webtex https://latex.codecogs.com/svg.latex?`. If no URL is
+  specified, the CodeCogs URL generating PNGs will be used
+  (`https://latex.codecogs.com/png.latex?`). Note: the `--webtex` option
+  will affect Markdown output as well as HTML, which is useful if you’re
+  targeting a version of Markdown without native math support.
+: `--katex`\[`=`_URL_\]
+
+  Use [KaTeX](https://github.com/Khan/KaTeX) to display embedded TeX
+  math in HTML output. The _URL_ is the base URL for the KaTeX library.
+  That directory should contain a `katex.min.js` and a `katex.min.css`
+  file. If a _URL_ is not provided, a link to the KaTeX CDN will be
+  inserted.
+: `--gladtex`
+
+  Enclose TeX math in `<eq>` tags in HTML output. The resulting HTML can
+  then be processed by [GladTeX](https://humenda.github.io/GladTeX/) to
+  produce SVG images of the typeset formulas and an HTML file with these
+  images embedded.
+
+  ```
+  pandoc -s --gladtex input.md -o myfile.htex
+  gladtex -d image_dir myfile.htex
+  # produces myfile.html and images in image_dir
+  ```
+
+{#options-for-wrapper-scripts .options}
+## Options for wrapper scripts
+
+: `--dump-args[=true|false]`
+
+  Print information about command-line arguments to _stdout_, then exit.
+  This option is intended primarily for use in wrapper scripts. The
+  first line of output contains the name of the output file specified
+  with the `-o` option, or `-` (for _stdout_) if no output file was
+  specified. The remaining lines contain the command-line arguments, one
+  per line, in the order they appear. These do not include regular
+  pandoc options and their arguments, but do include any options
+  appearing after a `--` separator at the end of the line.
+: `--ignore-args[=true|false]`
+
+  Ignore command-line arguments (for use in wrapper scripts). Regular
+  pandoc options are not ignored. Thus, for example,
+
+  ```
+  pandoc --ignore-args -o foo.html -s foo.txt -- -e latin1
+  ```
+
+  is equivalent to
+
+  ```
+  pandoc -o foo.html -s
+  ```
+
+{#exit-codes}
+# Exit codes
+
+If pandoc completes successfully, it will return exit code 0. Nonzero
+exit codes have the following meanings:
+
+|Code|Error|
+|--:|:--|
+|1|PandocIOError|
+|3|PandocFailOnWarningError|
+|4|PandocAppError|
+|5|PandocTemplateError|
+|6|PandocOptionError|
+|21|PandocUnknownReaderError|
+|22|PandocUnknownWriterError|
+|23|PandocUnsupportedExtensionError|
+|24|PandocCiteprocError|
+|25|PandocBibliographyError|
+|31|PandocEpubSubdirectoryError|
+|43|PandocPDFError|
+|44|PandocXMLError|
+|47|PandocPDFProgramNotFoundError|
+|61|PandocHttpError|
+|62|PandocShouldNeverHappenError|
+|63|PandocSomeError|
+|64|PandocParseError|
+|66|PandocMakePDFError|
+|67|PandocSyntaxMapError|
+|83|PandocFilterError|
+|84|PandocLuaError|
+|89|PandocNoScriptingEngine|
+|91|PandocMacroLoop|
+|92|PandocUTF8DecodingError|
+|93|PandocIpynbDecodingError|
+|94|PandocUnsupportedCharsetError|
+|97|PandocCouldNotFindDataFileError|
+|98|PandocCouldNotFindMetadataFileError|
+|99|PandocResourceNotFound|
+
+{#defaults-files}
+# Defaults files
+
+The `--defaults` option may be used to specify a package of options, in
+the form of a YAML file.
+
+Fields that are omitted will just have their regular default values. So
+a defaults file can be as simple as one line:
+
+``` yaml
+verbosity: INFO
+```
+
+In fields that expect a file path (or list of file paths), the following
+syntax may be used to interpolate environment variables:
+
+``` yaml
+csl:  ${HOME}/mycsldir/special.csl
+```
+
+`${USERDATA}` may also be used; this will always resolve to the user
+data directory that is current when the defaults file is parsed,
+regardless of the setting of the environment variable `USERDATA`.
+
+`${.}` will resolve to the directory containing the defaults file
+itself. This allows you to refer to resources contained in that
+directory:
+
+``` yaml
+epub-cover-image: ${.}/cover.jpg
+epub-metadata: ${.}/meta.xml
+resource-path:
+- .             # the working directory from which pandoc is run
+- ${.}/images   # the images subdirectory of the directory
+                # containing this defaults file
+```
+
+This environment variable interpolation syntax _only_ works in fields
+that expect file paths.
+
+Defaults files can be placed in the `defaults` subdirectory of the user
+data directory and used from any directory. For example, one could
+create a file specifying defaults for writing letters, save it as
+`letter.yaml` in the `defaults` subdirectory of the user data directory,
+and then invoke these defaults from any directory using
+`pandoc --defaults letter` or `pandoc -dletter`.
+
+When multiple defaults are used, their contents will be combined.
+
+Note that, where command-line arguments may be repeated
+(`--metadata-file`, `--css`, `--include-in-header`,
+`--include-before-body`, `--include-after-body`, `--variable`,
+`--metadata`, `--syntax-definition`), the values specified on the
+command line will combine with values specified in the defaults file,
+rather than replacing them.
+
+The following tables show the mapping between the command line and
+defaults file entries.
+
+|command line|defaults file|
+|:--|:--|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+
+The value of `input-files` may be left empty to indicate input from
+stdin, and it can be an empty sequence `[]` for no input.
+
+{#general-options-1}
+## General options
+
+|command line|defaults file|
+|:--|:--|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+
+Options specified in a defaults file itself always have priority over
+those in another file included with a `defaults:` entry.
+
+`verbosity` can have the values `ERROR`, `WARNING`, or `INFO`.
+
+{#reader-options-1}
+## Reader options
+
+|command line|defaults file|
+|:--|:--|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+
+Metadata values specified in a defaults file are parsed as literal
+string text, not Markdown.
+
+Filters will be assumed to be Lua filters if they have the `.lua`
+extension, and JSON filters otherwise. But the filter type can also be
+specified explicitly, as shown. Filters are run in the order specified.
+To include the built-in citeproc filter, use either `citeproc` or
+`{type: citeproc}`.
+
+{#general-writer-options-1}
+## General writer options
+
+|command line|defaults file|
+|:--|:--|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+
+{#options-affecting-specific-writers-1}
+## Options affecting specific writers
+
+|command line|defaults file|
+|:--|:--|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+
+{#citation-rendering-1}
+## Citation rendering
+
+|command line|defaults file|
+|:--|:--|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+
+`cite-method` can be `citeproc`, `natbib`, or `biblatex`. This only
+affects LaTeX output. If you want to use citeproc to format citations,
+you should also set 'citeproc: true'.
+
+If you need control over when the citeproc processing is done relative
+to other filters, you should instead use `citeproc` in the list of
+`filters` (see above).
+
+{#math-rendering-in-html-1}
+## Math rendering in HTML
+
+|command line|defaults file|
+|:--|:--|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+
+In addition to the values listed above, `method` can have the value
+`plain`.
+
+If the command line option accepts a URL argument, an `url:` field can
+be added to `html-math-method:`.
+
+{#options-for-wrapper-scripts-1}
+## Options for wrapper scripts
+
+|command line|defaults file|
+|:--|:--|
+|((content omitted))|((content omitted))|
+|((content omitted))|((content omitted))|
+
+{#templates}
+# Templates
+
+When the `-s/--standalone` option is used, pandoc uses a template to add
+header and footer material that is needed for a self-standing document.
+To see the default template that is used, just type
+
+```
+pandoc -D *FORMAT*
+```
+
+where _FORMAT_ is the name of the output format. A custom template can
+be specified using the `--template` option. You can also override the
+system default templates for a given output format _FORMAT_ by putting a
+file `templates/default.*FORMAT*` in the user data directory (see
+`--data-dir`, above). _Exceptions:_
+
+- For `odt` output, customize the `default.opendocument` template.
+- For `pdf` output, customize the `default.latex` template (or the
+  `default.context` template, if you use `-t context`, or the
+  `default.ms` template, if you use `-t ms`, or the `default.html`
+  template, if you use `-t html`).
+- `docx` and `pptx` have no template (however, you can use
+  `--reference-doc` to customize the output).
+
+Templates contain _variables_, which allow for the inclusion of
+arbitrary information at any point in the file. They may be set at the
+command line using the `-V/--variable` option. If a variable is not set,
+pandoc will look for the key in the document’s metadata, which can be
+set using either [YAML metadata blocks](#extension-yaml_metadata_block)
+or with the `-M/--metadata` option. In addition, some variables are
+given default values by pandoc. See [Variables](#variables) below for a
+list of variables used in pandoc’s default templates.
+
+If you use custom templates, you may need to revise them as pandoc
+changes. We recommend tracking the changes in the default templates, and
+modifying your custom templates accordingly. An easy way to do this is
+to fork the [pandoc-templates](https://github.com/jgm/pandoc-templates)
+repository and merge in changes after each pandoc release.
+
+{#template-syntax}
+## Template syntax
+
+{#comments}
+### Comments
+
+Anything between the sequence `$--` and the end of the line will be
+treated as a comment and omitted from the output.
+
+{#delimiters}
+### Delimiters
+
+To mark variables and control structures in the template, either `$`…`$`
+or `${`…`}` may be used as delimiters. The styles may also be mixed in
+the same template, but the opening and closing delimiter must match in
+each case. The opening delimiter may be followed by one or more spaces
+or tabs, which will be ignored. The closing delimiter may be preceded by
+one or more spaces or tabs, which will be ignored.
+
+To include a literal `$` in the document, use `$$`.
+
+{#interpolated-variables}
+### Interpolated variables
+
+A slot for an interpolated variable is a variable name surrounded by
+matched delimiters. Variable names must begin with a letter and can
+contain letters, numbers, `_`, `-`, and `.`. The keywords `it`, `if`,
+`else`, `endif`, `for`, `sep`, and `endfor` may not be used as variable
+names. Examples:
+
+```
+$foo$
+$foo.bar.baz$
+$foo_bar.baz-bim$
+$ foo $
+${foo}
+${foo.bar.baz}
+${foo_bar.baz-bim}
+${ foo }
+```
+
+Variable names with periods are used to get at structured variable
+values. So, for example, `employee.salary` will return the value of the
+`salary` field of the object that is the value of the `employee` field.
+
+- If the value of the variable is a simple value, it will be rendered
+  verbatim. (Note that no escaping is done; the assumption is that the
+  calling program will escape the strings appropriately for the output
+  format.)
+- If the value is a list, the values will be concatenated.
+- If the value is a map, the string `true` will be rendered.
+- Every other value will be rendered as the empty string.
+
+{#conditionals}
+### Conditionals
+
+A conditional begins with `if(variable)` (enclosed in matched
+delimiters) and ends with `endif` (enclosed in matched delimiters). It
+may optionally contain an `else` (enclosed in matched delimiters). The
+`if` section is used if `variable` has a non-empty value, otherwise the
+`else` section is used (if present). Examples:
+
+```
+$if(foo)$bar$endif$
+
+$if(foo)$
+  $foo$
+$endif$
+
+$if(foo)$
+part one
+$else$
+part two
+$endif$
+
+${if(foo)}bar${endif}
+
+${if(foo)}
+  ${foo}
+${endif}
+
+${if(foo)}
+${ foo.bar }
+${else}
+no foo!
+${endif}
+```
+
+The keyword `elseif` may be used to simplify complex nested
+conditionals:
+
+```
+$if(foo)$
+XXX
+$elseif(bar)$
+YYY
+$else$
+ZZZ
+$endif$
+```
+
+{#for-loops}
+### For loops
+
+A for loop begins with `for(variable)` (enclosed in matched delimiters)
+and ends with `endfor` (enclosed in matched delimiters).
+
+- If `variable` is an array, the material inside the loop will be
+  evaluated repeatedly, with `variable` being set to each value of the
+  array in turn, and concatenated.
+- If `variable` is a map, the material inside will be set to the map.
+- If the value of the associated variable is not an array or a map, a
+  single iteration will be performed on its value.
+
+Examples:
+
+```
+$for(foo)$$foo$$sep$, $endfor$
+
+$for(foo)$
+  - $foo.last$, $foo.first$
+$endfor$
+
+${ for(foo.bar) }
+  - ${ foo.bar.last }, ${ foo.bar.first }
+${ endfor }
+
+$for(mymap)$
+$it.name$: $it.office$
+$endfor$
+```
+
+You may optionally specify a separator between consecutive values using
+`sep` (enclosed in matched delimiters). The material between `sep` and
+the `endfor` is the separator.
+
+```
+${ for(foo) }${ foo }${ sep }, ${ endfor }
+```
+
+Instead of using `variable` inside the loop, the special anaphoric
+keyword `it` may be used.
+
+```
+${ for(foo.bar) }
+  - ${ it.last }, ${ it.first }
+${ endfor }
+```
+
+{#partials}
+### Partials
+
+Partials (subtemplates stored in different files) may be included by
+using the name of the partial, followed by `()`, for example:
+
+```
+${ styles() }
+```
+
+Partials will be sought in the directory containing the main template.
+The file name will be assumed to have the same extension as the main
+template if it lacks an extension. When calling the partial, the full
+name including file extension can also be used:
+
+```
+${ styles.html() }
+```
+
+(If a partial is not found in the directory of the template and the
+template path is given as a relative path, it will also be sought in the
+`templates` subdirectory of the user data directory.)
+
+Partials may optionally be applied to variables using a colon:
+
+```
+${ date:fancy() }
+
+${ articles:bibentry() }
+```
+
+If `articles` is an array, this will iterate over its values, applying
+the partial `bibentry()` to each one. So the second example above is
+equivalent to
+
+```
+${ for(articles) }
+${ it:bibentry() }
+${ endfor }
+```
+
+Note that the anaphoric keyword `it` must be used when iterating over
+partials. In the above examples, the `bibentry` partial should contain
+`it.title` (and so on) instead of `articles.title`.
+
+Final newlines are omitted from included partials.
+
+Partials may include other partials.
+
+A separator between values of an array may be specified in square
+brackets, immediately after the variable name or partial:
+
+```
+${months[, ]}$
+
+${articles:bibentry()[; ]$
+```
+
+The separator in this case is literal and (unlike with `sep` in an
+explicit `for` loop) cannot contain interpolated variables or other
+template directives.
+
+{#nesting}
+### Nesting
+
+To ensure that content is "nested," that is, subsequent lines indented,
+use the `^` directive:
+
+```
+$item.number$  $^$$item.description$ ($item.price$)
+```
+
+In this example, if `item.description` has multiple lines, they will all
+be indented to line up with the first line:
+
+```
+00123  A fine bottle of 18-year old
+       Oban whiskey. ($148)
+```
+
+To nest multiple lines to the same level, align them with the `^`
+directive in the template. For example:
+
+```
+$item.number$  $^$$item.description$ ($item.price$)
+               (Available til $item.sellby$.)
+```
+
+will produce
+
+```
+00123  A fine bottle of 18-year old
+       Oban whiskey. ($148)
+       (Available til March 30, 2020.)
+```
+
+If a variable occurs by itself on a line, preceded by whitespace and not
+followed by further text or directives on the same line, and the
+variable’s value contains multiple lines, it will be nested
+automatically.
+
+{#breakable-spaces}
+### Breakable spaces
+
+Normally, spaces in the template itself (as opposed to values of the
+interpolated variables) are not breakable, but they can be made
+breakable in part of the template by using the `~` keyword (ended with
+another `~`).
+
+```
+$~$This long line may break if the document is rendered
+with a short line length.$~$
+```
+
+{#pipes}
+### Pipes
+
+A pipe transforms the value of a variable or partial. Pipes are
+specified using a slash (`/`) between the variable name (or partial) and
+the pipe name. Example:
+
+```
+$for(name)$
+$name/uppercase$
+$endfor$
+
+$for(metadata/pairs)$
+- $it.key$: $it.value$
+$endfor$
+
+$employee:name()/uppercase$
+```
+
+Pipes may be chained:
+
+```
+$for(employees/pairs)$
+$it.key/alpha/uppercase$. $it.name$
+$endfor$
+```
+
+Some pipes take parameters:
+
+```
+|----------------------|------------|
+$for(employee)$
+$it.name.first/uppercase/left 20 "| "$$it.name.salary/right 10 " | " " |"$
+$endfor$
+|----------------------|------------|
+```
+
+Currently the following pipes are predefined:
+
+- `pairs`: Converts a map or array to an array of maps, each with `key`
+  and `value` fields. If the original value was an array, the `key` will
+  be the array index, starting with 1.
+
+- `uppercase`: Converts text to uppercase.
+
+- `lowercase`: Converts text to lowercase.
+
+- `length`: Returns the length of the value: number of characters for a
+  textual value, number of elements for a map or array.
+
+- `reverse`: Reverses a textual value or array, and has no effect on
+  other values.
+
+- `first`: Returns the first value of an array, if applied to a
+  non-empty array; otherwise returns the original value.
+
+- `last`: Returns the last value of an array, if applied to a non-empty
+  array; otherwise returns the original value.
+
+- `rest`: Returns all but the first value of an array, if applied to a
+  non-empty array; otherwise returns the original value.
+
+- `allbutlast`: Returns all but the last value of an array, if applied
+  to a non-empty array; otherwise returns the original value.
+
+- `chomp`: Removes trailing newlines (and breakable space).
+
+- `nowrap`: Disables line wrapping on breakable spaces.
+
+- `alpha`: Converts textual values that can be read as an integer into
+  lowercase alphabetic characters `a..z` (mod 26). This can be used to
+  get lettered enumeration from array indices. To get uppercase letters,
+  chain with `uppercase`.
+
+- `roman`: Converts textual values that can be read as an integer into
+  lowercase roman numerals. This can be used to get lettered enumeration
+  from array indices. To get uppercase roman, chain with `uppercase`.
+
+- `left n "leftborder" "rightborder"`: Renders a textual value in a
+  block of width `n`, aligned to the left, with an optional left and
+  right border. Has no effect on other values. This can be used to align
+  material in tables. Widths are positive integers indicating the number
+  of characters. Borders are strings inside double quotes; literal `"`
+  and `\` characters must be backslash-escaped.
+
+- `right n "leftborder" "rightborder"`: Renders a textual value in a
+  block of width `n`, aligned to the right, and has no effect on other
+  values.
+
+- `center n "leftborder" "rightborder"`: Renders a textual value in a
+  block of width `n`, aligned to the center, and has no effect on other
+  values.
+
+{#variables}
+## Variables
+
+{#metadata-variables}
+### Metadata variables
+
+: `title`, `author`, `date`
+
+  allow identification of basic aspects of the document. Included in PDF
+  metadata through LaTeX and ConTeXt. These can be set through a [pandoc
+  title block](#extension-pandoc_title_block), which allows for multiple
+  authors, or through a [YAML metadata
+  block](#extension-yaml_metadata_block):
+
+  ```
+  ---
+  author:
+  - Aristotle
+  - Peter Abelard
+  ...
+  ```
+
+  Note that if you just want to set PDF or HTML metadata, without
+  including a title block in the document itself, you can set the
+  `title-meta`, `author-meta`, and `date-meta` variables. (By default
+  these are set automatically, based on `title`, `author`, and `date`.)
+  The page title in HTML is set by `pagetitle`, which is equal to
+  `title` by default.
+: `subtitle`
+
+  document subtitle, included in HTML, EPUB, LaTeX, ConTeXt, and docx
+  documents
+: `abstract`
+
+  document summary, included in HTML, LaTeX, ConTeXt, AsciiDoc, and docx
+  documents
+: `abstract-title`
+
+  title of abstract, currently used only in HTML, EPUB, and docx. This
+  will be set automatically to a localized value, depending on `lang`,
+  but can be manually overridden.
+: `keywords`
+
+  list of keywords to be included in HTML, PDF, ODT, pptx, docx and
+  AsciiDoc metadata; repeat as for `author`, above
+: `subject`
+
+  document subject, included in ODT, PDF, docx, EPUB, and pptx metadata
+: `description`
+
+  document description, included in ODT, docx and pptx metadata. Some
+  applications show this as `Comments` metadata.
+: `category`
+
+  document category, included in docx and pptx metadata
+
+Additionally, any root-level string metadata, not included in ODT, docx
+or pptx metadata is added as a _custom property_. The following
+[YAML](https://yaml.org/spec/1.2/spec.html){title="YAML v1.2 Spec"}
+metadata block for instance:
+
+```
+---
+title:  'This is the title'
+subtitle: "This is the subtitle"
+author:
+- Author One
+- Author Two
+description: |
+    This is a long
+    description.
+
+    It consists of two paragraphs
+...
+```
+
+will include `title`, `author` and `description` as standard document
+properties and `subtitle` as a custom property when converting to docx,
+ODT or pptx.
+
+{#language-variables}
+### Language variables
+
+: `lang`
+
+  identifies the main language of the document using IETF language tags
+  (following the [BCP 47](https://tools.ietf.org/html/bcp47) standard),
+  such as `en` or `en-GB`. The [Language subtag
+  lookup](https://r12a.github.io/app-subtags/) tool can look up or
+  verify these tags. This affects most formats, and controls hyphenation
+  in PDF output when using LaTeX (through
+  [`babel`](https://ctan.org/pkg/babel) and
+  [`polyglossia`](https://ctan.org/pkg/polyglossia)) or ConTeXt.
+
+  Use native pandoc [Divs and Spans](#divs-and-spans) with the `lang`
+  attribute to switch the language:
+
+  ```
+  ---
+  lang: en-GB
+  ...
+
+  Text in the main document language (British English).
+
+  ::: {lang=fr-CA}
+  > Cette citation est écrite en français canadien.
+  :::
+
+  More text in English. ['Zitat auf Deutsch.']{lang=de}
+  ```
+: `dir`
+
+  the base script direction, either `rtl` (right-to-left) or `ltr`
+  (left-to-right).
+
+  For bidirectional documents, native pandoc `span`s and `div`s with the
+  `dir` attribute (value `rtl` or `ltr`) can be used to override the
+  base direction in some output formats. This may not always be
+  necessary if the final renderer (e.g. the browser, when generating
+  HTML) supports the [Unicode Bidirectional
+  Algorithm](https://www.w3.org/International/articles/inline-bidi-markup/uba-basics).
+
+  When using LaTeX for bidirectional documents, only the `xelatex`
+  engine is fully supported (use `--pdf-engine=xelatex`).
+
+{#variables-for-html}
+### Variables for HTML
+
+: `document-css`
+
+  Enables inclusion of most of the
+  [CSS](https://developer.mozilla.org/en-US/docs/Learn/CSS) in the
+  `styles.html` [partial](#partials) (have a look with
+  `pandoc --print-default-data-file=templates/styles.html`). Unless you
+  use [`--css`](#option--css), this variable is set to `true` by
+  default. You can disable it with e.g. `pandoc -M document-css=false`.
+: `mainfont`
+
+  sets the CSS `font-family` property on the `html` element.
+: `fontsize`
+
+  sets the base CSS `font-size`, which you’d usually set to e.g. `20px`,
+  but it also accepts `pt` (12pt = 16px in most browsers).
+: `fontcolor`
+
+  sets the CSS `color` property on the `html` element.
+: `linkcolor`
+
+  sets the CSS `color` property on all links.
+: `monofont`
+
+  sets the CSS `font-family` property on `code` elements.
+: `monobackgroundcolor`
+
+  sets the CSS `background-color` property on `code` elements and adds
+  extra padding.
+: `linestretch`
+
+  sets the CSS `line-height` property on the `html` element, which is
+  preferred to be unitless.
+: `maxwidth`
+
+  sets the CSS `max-width` property (default is 32em).
+: `backgroundcolor`
+
+  sets the CSS `background-color` property on the `html` element.
+: `margin-left`, `margin-right`, `margin-top`, `margin-bottom`
+
+  sets the corresponding CSS `padding` properties on the `body` element.
+
+To override or extend some
+[CSS](https://developer.mozilla.org/en-US/docs/Learn/CSS) for just one
+document, include for example:
+
+```
+---
+header-includes: |
+  <style>
+  blockquote {
+    font-style: italic;
+  }
+  tr.even {
+    background-color: #f0f0f0;
+  }
+  td, th {
+    padding: 0.5em 2em 0.5em 0.5em;
+  }
+  tbody {
+    border-bottom: none;
+  }
+  </style>
+---
+```
+
+{#variables-for-html-math}
+### Variables for HTML math
+
+: `classoption`
+
+  when using [KaTeX](#option--katex), you can render display math
+  equations flush left using [YAML metadata](#layout) or with
+  `-M classoption=fleqn`.
+
+{#variables-for-html-slides}
+### Variables for HTML slides
+
+These affect HTML output when [producing slide shows with
+pandoc](#slide-shows).
+
+: `institute`
+
+  author affiliations: can be a list when there are multiple authors
+: `revealjs-url`
+
+  base URL for reveal.js documents (defaults to
+  `https://unpkg.com/reveal.js@^4/`)
+: `s5-url`
+
+  base URL for S5 documents (defaults to `s5/default`)
+: `slidy-url`
+
+  base URL for Slidy documents (defaults to
+  `https://www.w3.org/Talks/Tools/Slidy2`)
+: `slideous-url`
+
+  base URL for Slideous documents (defaults to `slideous`)
+: `title-slide-attributes`
+
+  additional attributes for the title slide of reveal.js slide shows.
+  See [background in reveal.js, beamer, and
+  pptx](#background-in-reveal.js-beamer-and-pptx) for an example.
+
+All [reveal.js configuration options](https://revealjs.com/config/) are
+available as variables. To turn off boolean flags that default to true
+in reveal.js, use `0`.
+
+{#variables-for-beamer-slides}
+### Variables for Beamer slides
+
+These variables change the appearance of PDF slides using
+[`beamer`](https://ctan.org/pkg/beamer).
+
+: `aspectratio`
+
+  slide aspect ratio (`43` for 4:3 \[default\], `169` for 16:9, `1610`
+  for 16:10, `149` for 14:9, `141` for 1.41:1, `54` for 5:4, `32` for
+  3:2)
+: `beameroption`
+
+  add extra beamer option with `\setbeameroption{}`
+: `institute`
+
+  author affiliations: can be a list when there are multiple authors
+: `logo`
+
+  logo image for slides
+: `navigation`
+
+  controls navigation symbols (default is `empty` for no navigation
+  symbols; other valid values are `frame`, `vertical`, and `horizontal`)
+: `section-titles`
+
+  enables "title pages" for new sections (default is true)
+: `theme`, `colortheme`, `fonttheme`, `innertheme`, `outertheme`
+
+  beamer themes
+: `themeoptions`
+
+  options for LaTeX beamer themes (a list).
+: `titlegraphic`
+
+  image for title slide
+
+{#variables-for-powerpoint}
+### Variables for PowerPoint
+
+These variables control the visual aspects of a slide show that are not
+easily controlled via templates.
+
+: `monofont`
+
+  font to use for code.
+
+{#variables-for-latex}
+### Variables for LaTeX
+
+Pandoc uses these variables when [creating a PDF](#creating-a-pdf) with
+a LaTeX engine.
+
+{#layout}
+#### Layout
+
+: `block-headings`
+
+  make `\paragraph` and `\subparagraph` (fourth- and fifth-level
+  headings, or fifth- and sixth-level with book classes) free-standing
+  rather than run-in; requires further formatting to distinguish from
+  `\subsubsection` (third- or fourth-level headings). Instead of using
+  this option, [KOMA-Script](https://ctan.org/pkg/koma-script) can
+  adjust headings more extensively:
+
+  ```
+  ---
+  documentclass: scrartcl
+  header-includes: |
+    \RedeclareSectionCommand[
+      beforeskip=-10pt plus -2pt minus -1pt,
+      afterskip=1sp plus -1sp minus 1sp,
+      font=\normalfont\itshape]{paragraph}
+    \RedeclareSectionCommand[
+      beforeskip=-10pt plus -2pt minus -1pt,
+      afterskip=1sp plus -1sp minus 1sp,
+      font=\normalfont\scshape,
+      indent=0pt]{subparagraph}
+  ...
+  ```
+: `classoption`
+
+  option for document class, e.g. `oneside`; repeat for multiple
+  options:
+
+  ```
+  ---
+  classoption:
+  - twocolumn
+  - landscape
+  ...
+  ```
+: `documentclass`
+
+  document class: usually one of the standard classes,
+  [`article`](https://ctan.org/pkg/article),
+  [`book`](https://ctan.org/pkg/book), and
+  [`report`](https://ctan.org/pkg/report); the
+  [KOMA-Script](https://ctan.org/pkg/koma-script) equivalents,
+  `scrartcl`, `scrbook`, and `scrreprt`, which default to smaller
+  margins; or [`memoir`](https://ctan.org/pkg/memoir)
+: `geometry`
+
+  option for [`geometry`](https://ctan.org/pkg/geometry) package,
+  e.g. `margin=1in`; repeat for multiple options:
+
+  ```
+  ---
+  geometry:
+  - top=30mm
+  - left=20mm
+  - heightrounded
+  ...
+  ```
+: `hyperrefoptions`
+
+  option for [`hyperref`](https://ctan.org/pkg/hyperref) package,
+  e.g. `linktoc=all`; repeat for multiple options:
+
+  ```
+  ---
+  hyperrefoptions:
+  - linktoc=all
+  - pdfwindowui
+  - pdfpagemode=FullScreen
+  ...
+  ```
+: `indent`
+
+  if true, pandoc will use document class settings for indentation (the
+  default LaTeX template otherwise removes indentation and adds space
+  between paragraphs)
+: `linestretch`
+
+  adjusts line spacing using the
+  [`setspace`](https://ctan.org/pkg/setspace) package, e.g. `1.25`,
+  `1.5`
+: `margin-left`, `margin-right`, `margin-top`, `margin-bottom`
+
+  sets margins if `geometry` is not used (otherwise `geometry` overrides
+  these)
+: `pagestyle`
+
+  control `\pagestyle{}`: the default article class supports `plain`
+  (default), `empty` (no running heads or page numbers), and `headings`
+  (section titles in running heads)
+: `papersize`
+
+  paper size, e.g. `letter`, `a4`
+: `secnumdepth`
+
+  numbering depth for sections (with `--number-sections` option or
+  `numbersections` variable)
+: `beamerarticle`
+
+  produce an article from Beamer slides
+
+{#fonts}
+#### Fonts
+
+: `fontenc`
+
+  allows font encoding to be specified through `fontenc` package (with
+  `pdflatex`); default is `T1` (see [LaTeX font encodings
+  guide](https://ctan.org/pkg/encguide))
+: `fontfamily`
+
+  font package for use with `pdflatex`: [TeX
+  Live](https://www.tug.org/texlive/) includes many options, documented
+  in the [LaTeX Font Catalogue](https://tug.org/FontCatalogue/). The
+  default is [Latin Modern](https://ctan.org/pkg/lm).
+: `fontfamilyoptions`
+
+  options for package used as `fontfamily`; repeat for multiple options.
+  For example, to use the Libertine font with proportional lowercase
+  (old-style) figures through the
+  [`libertinus`](https://ctan.org/pkg/libertinus) package:
+
+  ```
+  ---
+  fontfamily: libertinus
+  fontfamilyoptions:
+  - osf
+  - p
+  ...
+  ```
+: `fontsize`
+
+  font size for body text. The standard classes allow 10pt, 11pt, and
+  12pt. To use another size, set `documentclass` to one of the
+  [KOMA-Script](https://ctan.org/pkg/koma-script) classes, such as
+  `scrartcl` or `scrbook`.
+: `mainfont`, `sansfont`, `monofont`, `mathfont`, `CJKmainfont`,
+  `CJKsansfont`, `CJKmonofont`
+
+  font families for use with `xelatex` or `lualatex`: take the name of
+  any system font, using the [`fontspec`](https://ctan.org/pkg/fontspec)
+  package. `CJKmainfont` uses the [`xecjk`](https://ctan.org/pkg/xecjk)
+  package.
+: `mainfontoptions`, `sansfontoptions`, `monofontoptions`,
+  `mathfontoptions`, `CJKoptions`
+
+  options to use with `mainfont`, `sansfont`, `monofont`, `mathfont`,
+  `CJKmainfont` in `xelatex` and `lualatex`. Allow for any choices
+  available through [`fontspec`](https://ctan.org/pkg/fontspec); repeat
+  for multiple options. For example, to use the [TeX
+  Gyre](http://www.gust.org.pl/projects/e-foundry/tex-gyre) version of
+  Palatino with lowercase figures:
+
+  ```
+  ---
+  mainfont: TeX Gyre Pagella
+  mainfontoptions:
+  - Numbers=Lowercase
+  - Numbers=Proportional
+  ...
+  ```
+: `babelfonts`
+
+  a map of Babel language names (e.g. `chinese`) to the font to be used
+  with the language:
+
+  * * * * *
+
+  babelfonts: chinese-hant: "Noto Serif CJK TC" russian: "Noto Serif" …
+: `microtypeoptions`
+
+  options to pass to the microtype package
+
+{#links}
+#### Links
+
+: `colorlinks`
+
+  add color to link text; automatically enabled if any of `linkcolor`,
+  `filecolor`, `citecolor`, `urlcolor`, or `toccolor` are set
+: `boxlinks`
+
+  add visible box around links (has no effect if `colorlinks` is set)
+: `linkcolor`, `filecolor`, `citecolor`, `urlcolor`, `toccolor`
+
+  color for internal links, external links, citation links, linked URLs,
+  and links in table of contents, respectively: uses options allowed by
+  [`xcolor`](https://ctan.org/pkg/xcolor), including the `dvipsnames`,
+  `svgnames`, and `x11names` lists
+: `links-as-notes`
+
+  causes links to be printed as footnotes
+: `urlstyle`
+
+  style for URLs (e.g., `tt`, `rm`, `sf`, and, the default, `same`)
+
+{#front-matter}
+#### Front matter
+
+: `lof`, `lot`
+
+  include list of figures, list of tables
+: `thanks`
+
+  contents of acknowledgments footnote after document title
+: `toc`
+
+  include table of contents (can also be set using
+  `--toc/--table-of-contents`)
+: `toc-depth`
+
+  level of section to include in table of contents
+
+{#biblatex-bibliographies}
+#### BibLaTeX Bibliographies
+
+These variables function when using BibLaTeX for [citation
+rendering](#citation-rendering).
+
+: `biblatexoptions`
+
+  list of options for biblatex
+: `biblio-style`
+
+  bibliography style, when used with `--natbib` and `--biblatex`
+: `biblio-title`
+
+  bibliography title, when used with `--natbib` and `--biblatex`
+: `bibliography`
+
+  bibliography to use for resolving references
+: `natbiboptions`
+
+  list of options for natbib
+
+{#variables-for-context}
+### Variables for ConTeXt
+
+Pandoc uses these variables when [creating a PDF](#creating-a-pdf) with
+ConTeXt.
+
+: `fontsize`
+
+  font size for body text (e.g. `10pt`, `12pt`)
+: `headertext`, `footertext`
+
+  text to be placed in running header or footer (see [ConTeXt Headers
+  and Footers](https://wiki.contextgarden.net/Headers_and_Footers));
+  repeat up to four times for different placement
+: `indenting`
+
+  controls indentation of paragraphs, e.g. `yes,small,next` (see
+  [ConTeXt Indentation](https://wiki.contextgarden.net/Indentation));
+  repeat for multiple options
+: `interlinespace`
+
+  adjusts line spacing, e.g. `4ex` (using
+  [`setupinterlinespace`](https://wiki.contextgarden.net/Command/setupinterlinespace));
+  repeat for multiple options
+: `layout`
+
+  options for page margins and text arrangement (see [ConTeXt
+  Layout](https://wiki.contextgarden.net/Layout)); repeat for multiple
+  options
+: `linkcolor`, `contrastcolor`
+
+  color for links outside and inside a page, e.g. `red`, `blue` (see
+  [ConTeXt Color](https://wiki.contextgarden.net/Color))
+: `linkstyle`
+
+  typeface style for links, e.g. `normal`, `bold`, `slanted`,
+  `boldslanted`, `type`, `cap`, `small`
+: `lof`, `lot`
+
+  include list of figures, list of tables
+: `mainfont`, `sansfont`, `monofont`, `mathfont`
+
+  font families: take the name of any system font (see [ConTeXt Font
+  Switching](https://wiki.contextgarden.net/Font_Switching))
+: `margin-left`, `margin-right`, `margin-top`, `margin-bottom`
+
+  sets margins, if `layout` is not used (otherwise `layout` overrides
+  these)
+: `pagenumbering`
+
+  page number style and location (using
+  [`setuppagenumbering`](https://wiki.contextgarden.net/Command/setuppagenumbering));
+  repeat for multiple options
+: `papersize`
+
+  paper size, e.g. `letter`, `A4`, `landscape` (see [ConTeXt Paper
+  Setup](https://wiki.contextgarden.net/PaperSetup)); repeat for
+  multiple options
+: `pdfa`
+
+  adds to the preamble the setup necessary to generate PDF/A of the type
+  specified, e.g. `1a:2005`, `2a`. If no type is specified (i.e. the
+  value is set to True, by e.g. `--metadata=pdfa` or `pdfa: true` in a
+  YAML metadata block), `1b:2005` will be used as default, for reasons
+  of backwards compatibility. Using `--variable=pdfa` without specified
+  value is not supported. To successfully generate PDF/A the required
+  ICC color profiles have to be available and the content and all
+  included files (such as images) have to be standard-conforming. The
+  ICC profiles and output intent may be specified using the variables
+  `pdfaiccprofile` and `pdfaintent`. See also [ConTeXt
+  PDFA](https://wiki.contextgarden.net/PDF/A) for more details.
+: `pdfaiccprofile`
+
+  when used in conjunction with `pdfa`, specifies the ICC profile to use
+  in the PDF, e.g. `default.cmyk`. If left unspecified, `sRGB.icc` is
+  used as default. May be repeated to include multiple profiles. Note
+  that the profiles have to be available on the system. They can be
+  obtained from [ConTeXt ICC
+  Profiles](https://wiki.contextgarden.net/PDFX#ICC_profiles).
+: `pdfaintent`
+
+  when used in conjunction with `pdfa`, specifies the output intent for
+  the colors, e.g. `ISO coated v2 300\letterpercent\space (ECI)` If left
+  unspecified, `sRGB IEC61966-2.1` is used as default.
+: `toc`
+
+  include table of contents (can also be set using
+  `--toc/--table-of-contents`)
+: `urlstyle`
+
+  typeface style for links without link text, e.g. `normal`, `bold`,
+  `slanted`, `boldslanted`, `type`, `cap`, `small`
+: `whitespace`
+
+  spacing between paragraphs, e.g. `none`, `small` (using
+  [`setupwhitespace`](https://wiki.contextgarden.net/Command/setupwhitespace))
+: `includesource`
+
+  include all source documents as file attachments in the PDF file
+
+{#variables-for-wkhtmltopdf}
+### Variables for `wkhtmltopdf`
+
+Pandoc uses these variables when [creating a PDF](#creating-a-pdf) with
+[`wkhtmltopdf`](https://wkhtmltopdf.org). The `--css` option also
+affects the output.
+
+: `footer-html`, `header-html`
+
+  add information to the header and footer
+: `margin-left`, `margin-right`, `margin-top`, `margin-bottom`
+
+  set the page margins
+: `papersize`
+
+  sets the PDF paper size
+
+{#variables-for-man-pages}
+### Variables for man pages
+
+: `adjusting`
+
+  adjusts text to left (`l`), right (`r`), center (`c`), or both (`b`)
+  margins
+: `footer`
+
+  footer in man pages
+: `header`
+
+  header in man pages
+: `hyphenate`
+
+  if `true` (the default), hyphenation will be used
+: `section`
+
+  section number in man pages
+
+{#variables-for-typst}
+### Variables for Typst
+
+: `margin`
+
+  A dictionary with the fields defined in the Typst documentation: `x`,
+  `y`, `top`, `bottom`, `left`, `right`.
+: `papersize`
+
+  Paper size: `a4`, `us-letter`, etc.
+: `mainfont`
+
+  Name of system font to use for the main font.
+: `fontsize`
+
+  Font size (e.g., `12pt`).
+: `section-numbering`
+
+  Schema to use for numbering sections, e.g. `1.A.1`.
+: `columns`
+
+  Number of columns for body text.
+
+{#variables-for-ms}
+### Variables for ms
+
+: `fontfamily`
+
+  `A` (Avant Garde), `B` (Bookman), `C` (Helvetica), `HN` (Helvetica
+  Narrow), `P` (Palatino), or `T` (Times New Roman). This setting does
+  not affect source code, which is always displayed using monospace
+  Courier. These built-in fonts are limited in their coverage of
+  characters. Additional fonts may be installed using the script
+  [`install-font.sh`](https://www.schaffter.ca/mom/bin/install-font.sh)
+  provided by Peter Schaffter and documented in detail on [his web
+  site](https://www.schaffter.ca/mom/momdoc/appendices.html#steps).
+: `indent`
+
+  paragraph indent (e.g. `2m`)
+: `lineheight`
+
+  line height (e.g. `12p`)
+: `pointsize`
+
+  point size (e.g. `10p`)
+
+{#variables-set-automatically}
+### Variables set automatically
+
+Pandoc sets these variables automatically in response to
+[options](#options) or document contents; users can also modify them.
+These vary depending on the output format, and include the following:
+
+: `body`
+
+  body of document
+: `date-meta`
+
+  the `date` variable converted to ISO 8601 YYYY-MM-DD, included in all
+  HTML based formats (dzslides, epub, html, html4, html5, revealjs, s5,
+  slideous, slidy). The recognized formats for `date` are: `mm/dd/yyyy`,
+  `mm/dd/yy`, `yyyy-mm-dd` (ISO 8601), `dd MM yyyy` (e.g. either
+  `02 Apr 2018` or `02 April 2018`), `MM dd, yyyy` (e.g. `Apr. 02, 2018`
+  or `April 02, 2018),`yyyy\[mm\[dd\]\]`(e.g.`20180402, `201804` or
+  `2018`).
+: `header-includes`
+
+  contents specified by `-H/--include-in-header` (may have multiple
+  values)
+: `include-before`
+
+  contents specified by `-B/--include-before-body` (may have multiple
+  values)
+: `include-after`
+
+  contents specified by `-A/--include-after-body` (may have multiple
+  values)
+: `meta-json`
+
+  JSON representation of all of the document’s metadata. Field values
+  are transformed to the selected output format.
+: `numbersections`
+
+  non-null value if `-N/--number-sections` was specified
+: `sourcefile`, `outputfile`
+
+  source and destination filenames, as given on the command line.
+  `sourcefile` can also be a list if input comes from multiple files, or
+  empty if input is from stdin. You can use the following snippet in
+  your template to distinguish them:
+
+  ```
+  $if(sourcefile)$
+  $for(sourcefile)$
+  $sourcefile$
+  $endfor$
+  $else$
+  (stdin)
+  $endif$
+  ```
+
+  Similarly, `outputfile` can be `-` if output goes to the terminal.
+
+  If you need absolute paths, use e.g. `$curdir$/$sourcefile$`.
+: `curdir`
+
+  working directory from which pandoc is run.
+: `pandoc-version`
+
+  pandoc version.
+: `toc`
+
+  non-null value if `--toc/--table-of-contents` was specified
+: `toc-title`
+
+  title of table of contents (works only with EPUB, HTML, revealjs,
+  opendocument, odt, docx, pptx, beamer, LaTeX)
+
+{#extensions}
+# Extensions
+
+The behavior of some of the readers and writers can be adjusted by
+enabling or disabling various extensions.
+
+An extension can be enabled by adding `+EXTENSION` to the format name
+and disabled by adding `-EXTENSION`. For example,
+`--from markdown_strict+footnotes` is strict Markdown with footnotes
+enabled, while `--from markdown-footnotes-pipe_tables` is pandoc’s
+Markdown without footnotes or pipe tables.
+
+The markdown reader and writer make by far the most use of extensions.
+Extensions only used by them are therefore covered in the section
+[Pandoc’s Markdown](#pandocs-markdown) below (see [Markdown
+variants](#markdown-variants) for `commonmark` and `gfm`). In the
+following, extensions that also work for other formats are covered.
+
+Note that markdown extensions added to the `ipynb` format affect
+Markdown cells in Jupyter notebooks (as do command-line options like
+`--markdown-headings`).
+
+{#typography}
+## Typography
+
+{#extension-smart}
+#### Extension: `smart`
+
+Interpret straight quotes as curly quotes, `---` as em-dashes, `--` as
+en-dashes, and `...` as ellipses. Nonbreaking spaces are inserted after
+certain abbreviations, such as "Mr."
+
+This extension can be enabled/disabled for the following formats:
+
+: input formats
+
+  `markdown`, `commonmark`, `latex`, `mediawiki`, `org`, `rst`, `twiki`,
+  `html`
+: output formats
+
+  `markdown`, `latex`, `context`, `rst`
+: enabled by default in
+
+  `markdown`, `latex`, `context` (both input and output)
+
+Note: If you are _writing_ Markdown, then the `smart` extension has the
+reverse effect: what would have been curly quotes comes out straight.
+
+In LaTeX, `smart` means to use the standard TeX ligatures for quotation
+marks (` `` ` and `''` for double quotes, `` ` `` and `'` for single
+quotes) and dashes (`--` for en-dash and `---` for em-dash). If `smart`
+is disabled, then in reading LaTeX pandoc will parse these characters
+literally. In writing LaTeX, enabling `smart` tells pandoc to use the
+ligatures when possible; if `smart` is disabled pandoc will use unicode
+quotation mark and dash characters.
+
+{#headings-and-sections}
+## Headings and sections
+
+{#extension-auto_identifiers}
+#### Extension: `auto_identifiers`
+
+A heading without an explicitly specified identifier will be
+automatically assigned a unique identifier based on the heading text.
+
+This extension can be enabled/disabled for the following formats:
+
+: input formats
+
+  `markdown`, `latex`, `rst`, `mediawiki`, `textile`
+: output formats
+
+  `markdown`, `muse`
+: enabled by default in
+
+  `markdown`, `muse`
+
+The default algorithm used to derive the identifier from the heading
+text is:
+
+- Remove all formatting, links, etc.
+- Remove all footnotes.
+- Remove all non-alphanumeric characters, except underscores, hyphens,
+  and periods.
+- Replace all spaces and newlines with hyphens.
+- Convert all alphabetic characters to lowercase.
+- Remove everything up to the first letter (identifiers may not begin
+  with a number or punctuation mark).
+- If nothing is left after this, use the identifier `section`.
+
+Thus, for example,
+
+|Heading|Identifier|
+|:--|:--|
+|`Heading identifiers in HTML`|`heading-identifiers-in-html`|
+|`Maître d'hôtel`|`maître-dhôtel`|
+|`*Dogs*?--in *my* house?`|`dogs--in-my-house`|
+|`[HTML], [S5], or [RTF]?`|`html-s5-or-rtf`|
+|`3. Applications`|`applications`|
+|`33`|`section`|
+
+These rules should, in most cases, allow one to determine the identifier
+from the heading text. The exception is when several headings have the
+same text; in this case, the first will get an identifier as described
+above; the second will get the same identifier with `-1` appended; the
+third with `-2`; and so on.
+
+(However, a different algorithm is used if `gfm_auto_identifiers` is
+enabled; see below.)
+
+These identifiers are used to provide link targets in the table of
+contents generated by the `--toc|--table-of-contents` option. They also
+make it easy to provide links from one section of a document to another.
+A link to this section, for example, might look like this:
+
+```
+See the section on
+[heading identifiers](#heading-identifiers-in-html-latex-and-context).
+```
+
+Note, however, that this method of providing links to sections works
+only in HTML, LaTeX, and ConTeXt formats.
+
+If the `--section-divs` option is specified, then each section will be
+wrapped in a `section` (or a `div`, if `html4` was specified), and the
+identifier will be attached to the enclosing `<section>` (or `<div>`)
+tag rather than the heading itself. This allows entire sections to be
+manipulated using JavaScript or treated differently in CSS.
+
+{#extension-ascii_identifiers}
+#### Extension: `ascii_identifiers`
+
+Causes the identifiers produced by `auto_identifiers` to be pure ASCII.
+Accents are stripped off of accented Latin letters, and non-Latin
+letters are omitted.
+
+{#extension-gfm_auto_identifiers}
+#### Extension: `gfm_auto_identifiers`
+
+Changes the algorithm used by `auto_identifiers` to conform to GitHub’s
+method. Spaces are converted to dashes (`-`), uppercase characters to
+lowercase characters, and punctuation characters other than `-` and `_`
+are removed. Emojis are replaced by their names.
+
+{#math-input}
+## Math Input
+
+The extensions [`tex_math_dollars`](#extension-tex_math_dollars),
+[`tex_math_single_backslash`](#extension-tex_math_single_backslash), and
+[`tex_math_double_backslash`](#extension-tex_math_double_backslash) are
+described in the section about Pandoc’s Markdown.
+
+However, they can also be used with HTML input. This is handy for
+reading web pages formatted using MathJax, for example.
+
+{#raw-htmltex}
+## Raw HTML/TeX
+
+The following extensions are described in more detail in their
+respective sections of [Pandoc’s Markdown](#pandocs-markdown):
+
+- [`raw_html`](#extension-raw_html) allows HTML elements which are not
+  representable in pandoc’s AST to be parsed as raw HTML. By default,
+  this is disabled for HTML input.
+
+- [`raw_tex`](#extension-raw_tex) allows raw LaTeX, TeX, and ConTeXt to
+  be included in a document. This extension can be enabled/disabled for
+  the following formats (in addition to `markdown`):
+
+  : input formats
+
+    `latex`, `textile`, `html` (environments, `\ref`, and `\eqref`
+    only), `ipynb`
+  : output formats
+
+    `textile`, `commonmark`
+
+  Note: as applied to `ipynb`, `raw_html` and `raw_tex` affect not only
+  raw TeX in markdown cells, but data with mime type `text/html` in
+  output cells. Since the `ipynb` reader attempts to preserve the
+  richest possible outputs when several options are given, you will get
+  best results if you disable `raw_html` and `raw_tex` when converting
+  to formats like `docx` which don’t allow raw `html` or `tex`.
+
+- [`native_divs`](#extension-native_divs) causes HTML `div` elements to
+  be parsed as native pandoc Div blocks. If you want them to be parsed
+  as raw HTML, use `-f html-native_divs+raw_html`.
+
+- [`native_spans`](#extension-native_spans) causes HTML `span` elements
+  to be parsed as native pandoc Span inlines. If you want them to be
+  parsed as raw HTML, use `-f html-native_spans+raw_html`. If you want
+  to drop all `div`s and `span`s when converting HTML to Markdown, you
+  can use `pandoc -f html-native_divs-native_spans -t markdown`.
+
+{#literate-haskell-support}
+## Literate Haskell support
+
+{#extension-literate_haskell}
+#### Extension: `literate_haskell`
+
+Treat the document as literate Haskell source.
+
+This extension can be enabled/disabled for the following formats:
+
+: input formats
+
+  `markdown`, `rst`, `latex`
+: output formats
+
+  `markdown`, `rst`, `latex`, `html`
+
+If you append `+lhs` (or `+literate_haskell`) to one of the formats
+above, pandoc will treat the document as literate Haskell source. This
+means that
+
+- In Markdown input, "bird track" sections will be parsed as Haskell
+  code rather than block quotations. Text between `\begin{code}` and
+  `\end{code}` will also be treated as Haskell code. For ATX-style
+  headings the character '=' will be used instead of '#'.
+
+- In Markdown output, code blocks with classes `haskell` and `literate`
+  will be rendered using bird tracks, and block quotations will be
+  indented one space, so they will not be treated as Haskell code. In
+  addition, headings will be rendered setext-style (with underlines)
+  rather than ATX-style (with '#' characters). (This is because ghc
+  treats '#' characters in column 1 as introducing line numbers.)
+
+- In restructured text input, "bird track" sections will be parsed as
+  Haskell code.
+
+- In restructured text output, code blocks with class `haskell` will be
+  rendered using bird tracks.
+
+- In LaTeX input, text in `code` environments will be parsed as Haskell
+  code.
+
+- In LaTeX output, code blocks with class `haskell` will be rendered
+  inside `code` environments.
+
+- In HTML output, code blocks with class `haskell` will be rendered with
+  class `literatehaskell` and bird tracks.
+
+Examples:
+
+```
+pandoc -f markdown+lhs -t html
+```
+
+reads literate Haskell source formatted with Markdown conventions and
+writes ordinary HTML (without bird tracks).
+
+```
+pandoc -f markdown+lhs -t html+lhs
+```
+
+writes HTML with the Haskell code in bird tracks, so it can be copied
+and pasted as literate Haskell source.
+
+Note that GHC expects the bird tracks in the first column, so indented
+literate code blocks (e.g. inside an itemized environment) will not be
+picked up by the Haskell compiler.
+
+{#other-extensions}
+## Other extensions
+
+{#extension-empty_paragraphs}
+#### Extension: `empty_paragraphs`
+
+Allows empty paragraphs. By default empty paragraphs are omitted.
+
+This extension can be enabled/disabled for the following formats:
+
+: input formats
+
+  `docx`, `html`
+: output formats
+
+  `docx`, `odt`, `opendocument`, `html`
+
+{#extension-native_numbering}
+#### Extension: `native_numbering`
+
+Enables native numbering of figures and tables. Enumeration starts at 1.
+
+This extension can be enabled/disabled for the following formats:
+
+: output formats
+
+  `odt`, `opendocument`, `docx`
+
+{#extension-xrefs_name}
+#### Extension: `xrefs_name`
+
+Links to headings, figures and tables inside the document are
+substituted with cross-references that will use the name or caption of
+the referenced item. The original link text is replaced once the
+generated document is refreshed. This extension can be combined with
+`xrefs_number` in which case numbers will appear before the name.
+
+Text in cross-references is only made consistent with the referenced
+item once the document has been refreshed.
+
+This extension can be enabled/disabled for the following formats:
+
+: output formats
+
+  `odt`, `opendocument`
+
+{#extension-xrefs_number}
+#### Extension: `xrefs_number`
+
+Links to headings, figures and tables inside the document are
+substituted with cross-references that will use the number of the
+referenced item. The original link text is discarded. This extension can
+be combined with `xrefs_name` in which case the name or caption numbers
+will appear after the number.
+
+For the `xrefs_number` to be useful heading numbers must be enabled in
+the generated document, also table and figure captions must be enabled
+using for example the `native_numbering` extension.
+
+Numbers in cross-references are only visible in the final document once
+it has been refreshed.
+
+This extension can be enabled/disabled for the following formats:
+
+: output formats
+
+  `odt`, `opendocument`
+
+{#ext-styles}
+#### Extension: `styles`
+
+When converting from docx, read all docx styles as divs (for paragraph
+styles) and spans (for character styles) regardless of whether pandoc
+understands the meaning of these styles. This can be used with [docx
+custom styles](#custom-styles). Disabled by default.
+
+: input formats
+
+  `docx`
+
+{#extension-amuse}
+#### Extension: `amuse`
+
+In the `muse` input format, this enables Text::Amuse extensions to Emacs
+Muse markup.
+
+{#extension-raw_markdown}
+#### Extension: `raw_markdown`
+
+In the `ipynb` input format, this causes Markdown cells to be included
+as raw Markdown blocks (allowing lossless round-tripping) rather than
+being parsed. Use this only when you are targeting `ipynb` or a
+markdown-based output format.
+
+{#org-citations}
+#### Extension: `citations`
+
+When the `citations` extension is enabled in `org`, org-cite and org-ref
+style citations will be parsed as native pandoc citations.
+
+When `citations` is enabled in `docx`, citations inserted by Zotero or
+Mendeley or EndNote plugins will be parsed as native pandoc citations.
+(Otherwise, the formatted citations generated by the bibliographic
+software will be parsed as regular text.)
+
+{#org-fancy-lists}
+#### Extension: `fancy_lists`
+
+Some aspects of [Pandoc’s Markdown fancy lists](#extension-fancy_lists)
+are also accepted in `org` input, mimicking the option
+`org-list-allow-alphabetical` in Emacs. As in Org Mode, enabling this
+extension allows lowercase and uppercase alphabetical markers for
+ordered lists to be parsed in addition to arabic ones. Note that for
+Org, this does not include roman numerals or the `#` placeholder that
+are enabled by the extension in Pandoc’s Markdown.
+
+{#extension-element_citations}
+#### Extension: `element_citations`
+
+In the `jats` output formats, this causes reference items to be replaced
+with `<element-citation>` elements. These elements are not influenced by
+CSL styles, but all information on the item is included in tags.
+
+{#extension-ntb}
+#### Extension: `ntb`
+
+In the `context` output format this enables the use of [Natural Tables
+(TABLE)](https://wiki.contextgarden.net/TABLE) instead of the default
+[Extreme Tables (xtables)](https://wiki.contextgarden.net/xtables).
+Natural tables allow more fine-grained global customization but come at
+a performance penalty compared to extreme tables.
+
+{#extension-tagging}
+#### Extension: `tagging`
+
+Enabling this extension with `context` output will produce markup
+suitable for the production of tagged PDFs. This includes additional
+markers for paragraphs and alternative markup for emphasized text. The
+`emphasis-command` template variable is set if the extension is enabled.
+
+{#pandocs-markdown}
+# Pandoc’s Markdown
+
+Pandoc understands an extended and slightly revised version of John
+Gruber’s [Markdown](https://daringfireball.net/projects/markdown/)
+syntax. This document explains the syntax, noting differences from
+original Markdown. Except where noted, these differences can be
+suppressed by using the `markdown_strict` format instead of `markdown`.
+Extensions can be enabled or disabled to specify the behavior more
+granularly. They are described in the following. See also
+[Extensions](#extensions) above, for extensions that work also on other
+formats.
+
+{#philosophy}
+## Philosophy
+
+Markdown is designed to be easy to write, and, even more importantly,
+easy to read:
+
+> A Markdown-formatted document should be publishable as-is, as plain
+> text, without looking like it’s been marked up with tags or formatting
+> instructions. – [John
+> Gruber](https://daringfireball.net/projects/markdown/syntax#philosophy)
+
+This principle has guided pandoc’s decisions in finding syntax for
+tables, footnotes, and other extensions.
+
+There is, however, one respect in which pandoc’s aims are different from
+the original aims of Markdown. Whereas Markdown was originally designed
+with HTML generation in mind, pandoc is designed for multiple output
+formats. Thus, while pandoc allows the embedding of raw HTML, it
+discourages it, and provides other, non-HTMLish ways of representing
+important document elements like definition lists, tables, mathematics,
+and footnotes.
+
+{#paragraphs}
+## Paragraphs
+
+A paragraph is one or more lines of text followed by one or more blank
+lines. Newlines are treated as spaces, so you can reflow your paragraphs
+as you like. If you need a hard line break, put two or more spaces at
+the end of a line.
+
+{#extension-escaped_line_breaks}
+#### Extension: `escaped_line_breaks`
+
+A backslash followed by a newline is also a hard line break. Note: in
+multiline and grid table cells, this is the only way to create a hard
+line break, since trailing spaces in the cells are ignored.
+
+{#headings}
+## Headings
+
+There are two kinds of headings: Setext and ATX.
+
+{#setext-style-headings}
+### Setext-style headings
+
+A setext-style heading is a line of text "underlined" with a row of `=`
+signs (for a level-one heading) or `-` signs (for a level-two heading):
+
+```
+A level-one heading
+===================
+
+A level-two heading
+-------------------
+```
+
+The heading text can contain inline formatting, such as emphasis (see
+[Inline formatting](#inline-formatting), below).
+
+{#atx-style-headings}
+### ATX-style headings
+
+An ATX-style heading consists of one to six `#` signs and a line of
+text, optionally followed by any number of `#` signs. The number of `#`
+signs at the beginning of the line is the heading level:
+
+```
+## A level-two heading
+
+### A level-three heading ###
+```
+
+As with setext-style headings, the heading text can contain formatting:
+
+```
+# A level-one heading with a [link](/url) and *emphasis*
+```
+
+{#extension-blank_before_header}
+#### Extension: `blank_before_header`
+
+Original Markdown syntax does not require a blank line before a heading.
+Pandoc does require this (except, of course, at the beginning of the
+document). The reason for the requirement is that it is all too easy for
+a `#` to end up at the beginning of a line by accident (perhaps through
+line wrapping). Consider, for example:
+
+```
+I like several of their flavors of ice cream:
+#22, for example, and #5.
+```
+
+{#extension-space_in_atx_header}
+#### Extension: `space_in_atx_header`
+
+Many Markdown implementations do not require a space between the opening
+`#`s of an ATX heading and the heading text, so that `#5 bolt` and
+`#hashtag` count as headings. With this extension, pandoc does require
+the space.
+
+{#heading-identifiers}
+### Heading identifiers
+
+See also the [`auto_identifiers` extension](#extension-auto_identifiers)
+above.
+
+{#extension-header_attributes}
+#### Extension: `header_attributes`
+
+Headings can be assigned attributes using this syntax at the end of the
+line containing the heading text:
+
+```
+{#identifier .class .class key=value key=value}
+```
+
+Thus, for example, the following headings will all be assigned the
+identifier `foo`:
+
+```
+# My heading {#foo}
+
+## My heading ##    {#foo}
+
+My other heading   {#foo}
+---------------
+```
+
+(This syntax is compatible with [PHP Markdown
+Extra](https://michelf.ca/projects/php-markdown/extra/).)
+
+Note that although this syntax allows assignment of classes and
+key/value attributes, writers generally don’t use all of this
+information. Identifiers, classes, and key/value attributes are used in
+HTML and HTML-based formats such as EPUB and slidy. Identifiers are used
+for labels and link anchors in the LaTeX, ConTeXt, Textile, Jira markup,
+and AsciiDoc writers.
+
+Headings with the class `unnumbered` will not be numbered, even if
+`--number-sections` is specified. A single hyphen (`-`) in an attribute
+context is equivalent to `.unnumbered`, and preferable in non-English
+documents. So,
+
+```
+# My heading {-}
+```
+
+is just the same as
+
+```
+# My heading {.unnumbered}
+```
+
+If the `unlisted` class is present in addition to `unnumbered`, the
+heading will not be included in a table of contents. (Currently this
+feature is only implemented for certain formats: those based on LaTeX
+and HTML, PowerPoint, and RTF.)
+
+{#extension-implicit_header_references}
+#### Extension: `implicit_header_references`
+
+Pandoc behaves as if reference links have been defined for each heading.
+So, to link to a heading
+
+```
+# Heading identifiers in HTML
+```
+
+you can simply write
+
+```
+[Heading identifiers in HTML]
+```
+
+or
+
+```
+[Heading identifiers in HTML][]
+```
+
+or
+
+```
+[the section on heading identifiers][heading identifiers in
+HTML]
+```
+
+instead of giving the identifier explicitly:
+
+```
+[Heading identifiers in HTML](#heading-identifiers-in-html)
+```
+
+If there are multiple headings with identical text, the corresponding
+reference will link to the first one only, and you will need to use
+explicit links to link to the others, as described above.
+
+Like regular reference links, these references are case-insensitive.
+
+Explicit link reference definitions always take priority over implicit
+heading references. So, in the following example, the link will point to
+`bar`, not to `#foo`:
+
+```
+# Foo
+
+[foo]: bar
+
+See [foo]
+```
+
+{#block-quotations}
+## Block quotations
+
+Markdown uses email conventions for quoting blocks of text. A block
+quotation is one or more paragraphs or other block elements (such as
+lists or headings), with each line preceded by a `>` character and an
+optional space. (The `>` need not start at the left margin, but it
+should not be indented more than three spaces.)
+
+```
+> This is a block quote. This
+> paragraph has two lines.
+>
+> 1. This is a list inside a block quote.
+> 2. Second item.
+```
+
+A "lazy" form, which requires the `>` character only on the first line
+of each block, is also allowed:
+
+```
+> This is a block quote. This
+paragraph has two lines.
+
+> 1. This is a list inside a block quote.
+2. Second item.
+```
+
+Among the block elements that can be contained in a block quote are
+other block quotes. That is, block quotes can be nested:
+
+```
+> This is a block quote.
+>
+> > A block quote within a block quote.
+```
+
+If the `>` character is followed by an optional space, that space will
+be considered part of the block quote marker and not part of the
+indentation of the contents. Thus, to put an indented code block in a
+block quote, you need five spaces after the `>`:
+
+```
+>     code
+```
+
+{#extension-blank_before_blockquote}
+#### Extension: `blank_before_blockquote`
+
+Original Markdown syntax does not require a blank line before a block
+quote. Pandoc does require this (except, of course, at the beginning of
+the document). The reason for the requirement is that it is all too easy
+for a `>` to end up at the beginning of a line by accident (perhaps
+through line wrapping). So, unless the `markdown_strict` format is used,
+the following does not produce a nested block quote in pandoc:
+
+```
+> This is a block quote.
+>> Not nested, since `blank_before_blockquote` is enabled by default
+```
+
+{#verbatim-code-blocks}
+## Verbatim (code) blocks
+
+{#indented-code-blocks}
+### Indented code blocks
+
+A block of text indented four spaces (or one tab) is treated as verbatim
+text: that is, special characters do not trigger special formatting, and
+all spaces and line breaks are preserved. For example,
+
+```
+    if (a > 3) {
+      moveShip(5 * gravity, DOWN);
+    }
+```
+
+The initial (four space or one tab) indentation is not considered part
+of the verbatim text, and is removed in the output.
+
+Note: blank lines in the verbatim text need not begin with four spaces.
+
+{#fenced-code-blocks}
+### Fenced code blocks
+
+{#extension-fenced_code_blocks}
+#### Extension: `fenced_code_blocks`
+
+In addition to standard indented code blocks, pandoc supports _fenced_
+code blocks. These begin with a row of three or more tildes (`~`) and
+end with a row of tildes that must be at least as long as the starting
+row. Everything between these lines is treated as code. No indentation
+is necessary:
+
+```
+~~~~~~~
+if (a > 3) {
+  moveShip(5 * gravity, DOWN);
+}
+~~~~~~~
+```
+
+Like regular code blocks, fenced code blocks must be separated from
+surrounding text by blank lines.
+
+If the code itself contains a row of tildes or backticks, just use a
+longer row of tildes or backticks at the start and end:
+
+```
+~~~~~~~~~~~~~~~~
+~~~~~~~~~~
+code including tildes
+~~~~~~~~~~
+~~~~~~~~~~~~~~~~
+```
+
+{#extension-backtick_code_blocks}
+#### Extension: `backtick_code_blocks`
+
+Same as `fenced_code_blocks`, but uses backticks (`` ` ``) instead of
+tildes (`~`).
+
+{#extension-fenced_code_attributes}
+#### Extension: `fenced_code_attributes`
+
+Optionally, you may attach attributes to fenced or backtick code block
+using this syntax:
+
+```
+~~~~ {#mycode .haskell .numberLines startFrom="100"}
+qsort []     = []
+qsort (x:xs) = qsort (filter (< x) xs) ++ [x] ++
+               qsort (filter (>= x) xs)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+```
+
+Here `mycode` is an identifier, `haskell` and `numberLines` are classes,
+and `startFrom` is an attribute with value `100`. Some output formats
+can use this information to do syntax highlighting. Currently, the only
+output formats that use this information are HTML, LaTeX, Docx, Ms, and
+PowerPoint. If highlighting is supported for your output format and
+language, then the code block above will appear highlighted, with
+numbered lines. (To see which languages are supported, type
+`pandoc --list-highlight-languages`.) Otherwise, the code block above
+will appear as follows:
+
+```
+<pre id="mycode" class="haskell numberLines" startFrom="100">
+  <code>
+  ...
+  </code>
+</pre>
+```
+
+The `numberLines` (or `number-lines`) class will cause the lines of the
+code block to be numbered, starting with `1` or the value of the
+`startFrom` attribute. The `lineAnchors` (or `line-anchors`) class will
+cause the lines to be clickable anchors in HTML output.
+
+A shortcut form can also be used for specifying the language of the code
+block:
+
+````
+```haskell
+qsort [] = []
+```
+````
+
+This is equivalent to:
+
+````
+``` {.haskell}
+qsort [] = []
+```
+````
+
+This shortcut form may be combined with attributes:
+
+````
+```haskell {.numberLines}
+qsort [] = []
+```
+````
+
+Which is equivalent to:
+
+````
+``` {.haskell .numberLines}
+qsort [] = []
+```
+````
+
+If the `fenced_code_attributes` extension is disabled, but input
+contains class attribute(s) for the code block, the first class
+attribute will be printed after the opening fence as a bare word.
+
+To prevent all highlighting, use the `--no-highlight` flag. To set the
+highlighting style, use `--highlight-style`. For more information on
+highlighting, see [Syntax highlighting](#syntax-highlighting), below.
+
+{#line-blocks}
+## Line blocks
+
+{#extension-line_blocks}
+#### Extension: `line_blocks`
+
+A line block is a sequence of lines beginning with a vertical bar (`|`)
+followed by a space. The division into lines will be preserved in the
+output, as will any leading spaces; otherwise, the lines will be
+formatted as Markdown. This is useful for verse and addresses:
+
+```
+| The limerick packs laughs anatomical
+| In space that is quite economical.
+|    But the good ones I've seen
+|    So seldom are clean
+| And the clean ones so seldom are comical
+
+| 200 Main St.
+| Berkeley, CA 94718
+```
+
+The lines can be hard-wrapped if needed, but the continuation line must
+begin with a space.
+
+```
+| The Right Honorable Most Venerable and Righteous Samuel L.
+  Constable, Jr.
+| 200 Main St.
+| Berkeley, CA 94718
+```
+
+Inline formatting (such as emphasis) is allowed in the content, but not
+block-level formatting (such as block quotes or lists).
+
+This syntax is borrowed from
+[reStructuredText](https://docutils.sourceforge.io/docs/ref/rst/introduction.html).
+
+{#lists}
+## Lists
+
+{#bullet-lists}
+### Bullet lists
+
+A bullet list is a list of bulleted list items. A bulleted list item
+begins with a bullet (`*`, `+`, or `-`). Here is a simple example:
+
+```
+* one
+* two
+* three
+```
+
+This will produce a "compact" list. If you want a "loose" list, in which
+each item is formatted as a paragraph, put spaces between the items:
+
+```
+* one
+
+* two
+
+* three
+```
+
+The bullets need not be flush with the left margin; they may be indented
+one, two, or three spaces. The bullet must be followed by whitespace.
+
+List items look best if subsequent lines are flush with the first line
+(after the bullet):
+
+```
+* here is my first
+  list item.
+* and my second.
+```
+
+But Markdown also allows a "lazy" format:
+
+```
+* here is my first
+list item.
+* and my second.
+```
+
+{#block-content-in-list-items}
+### Block content in list items
+
+A list item may contain multiple paragraphs and other block-level
+content. However, subsequent paragraphs must be preceded by a blank line
+and indented to line up with the first non-space content after the list
+marker.
+
+```
+  * First paragraph.
+
+    Continued.
+
+  * Second paragraph. With a code block, which must be indented
+    eight spaces:
+
+        { code }
+```
+
+Exception: if the list marker is followed by an indented code block,
+which must begin 5 spaces after the list marker, then subsequent
+paragraphs must begin two columns after the last character of the list
+marker:
+
+```
+*     code
+
+  continuation paragraph
+```
+
+List items may include other lists. In this case the preceding blank
+line is optional. The nested list must be indented to line up with the
+first non-space character after the list marker of the containing list
+item.
+
+```
+* fruits
+  + apples
+    - macintosh
+    - red delicious
+  + pears
+  + peaches
+* vegetables
+  + broccoli
+  + chard
+```
+
+As noted above, Markdown allows you to write list items "lazily,"
+instead of indenting continuation lines. However, if there are multiple
+paragraphs or other blocks in a list item, the first line of each must
+be indented.
+
+```
++ A lazy, lazy, list
+item.
+
++ Another one; this looks
+bad but is legal.
+
+    Second paragraph of second
+list item.
+```
+
+{#ordered-lists}
+### Ordered lists
+
+Ordered lists work just like bulleted lists, except that the items begin
+with enumerators rather than bullets.
+
+In original Markdown, enumerators are decimal numbers followed by a
+period and a space. The numbers themselves are ignored, so there is no
+difference between this list:
+
+```
+1.  one
+2.  two
+3.  three
+```
+
+and this one:
+
+```
+5.  one
+7.  two
+1.  three
+```
+
+{#extension-fancy_lists}
+#### Extension: `fancy_lists`
+
+Unlike original Markdown, pandoc allows ordered list items to be marked
+with uppercase and lowercase letters and roman numerals, in addition to
+Arabic numerals. List markers may be enclosed in parentheses or followed
+by a single right-parenthesis or period. They must be separated from the
+text that follows by at least one space, and, if the list marker is a
+capital letter with a period, by at least two spaces.[^1]
+
+The `fancy_lists` extension also allows '`#`' to be used as an ordered
+list marker in place of a numeral:
+
+```
+#. one
+#. two
+```
+
+Note: the '`#`' ordered list marker doesn’t work with `commonmark`.
+
+{#extension-startnum}
+#### Extension: `startnum`
+
+Pandoc also pays attention to the type of list marker used, and to the
+starting number, and both of these are preserved where possible in the
+output format. Thus, the following yields a list with numbers followed
+by a single parenthesis, starting with 9, and a sublist with lowercase
+roman numerals:
+
+```
+ 9)  Ninth
+10)  Tenth
+11)  Eleventh
+       i. subone
+      ii. subtwo
+     iii. subthree
+```
+
+Pandoc will start a new list each time a different type of list marker
+is used. So, the following will create three lists:
+
+```
+(2) Two
+(5) Three
+1.  Four
+*   Five
+```
+
+If default list markers are desired, use `#.`:
+
+```
+#.  one
+#.  two
+#.  three
+```
+
+{#extension-task_lists}
+#### Extension: `task_lists`
+
+Pandoc supports task lists, using the syntax of GitHub-Flavored
+Markdown.
+
+```
+- [ ] an unchecked task list item
+- [x] checked item
+```
+
+{#definition-lists}
+### Definition lists
+
+{#extension-definition_lists}
+#### Extension: `definition_lists`
+
+Pandoc supports definition lists, using the syntax of [PHP Markdown
+Extra](https://michelf.ca/projects/php-markdown/extra/) with some
+extensions.[^2]
+
+```
+Term 1
+
+:   Definition 1
+
+Term 2 with *inline markup*
+
+:   Definition 2
+
+        { some code, part of Definition 2 }
+
+    Third paragraph of definition 2.
+```
+
+Each term must fit on one line, which may optionally be followed by a
+blank line, and must be followed by one or more definitions. A
+definition begins with a colon or tilde, which may be indented one or
+two spaces.
+
+A term may have multiple definitions, and each definition may consist of
+one or more block elements (paragraph, code block, list, etc.), each
+indented four spaces or one tab stop. The body of the definition (not
+including the first line) should be indented four spaces. However, as
+with other Markdown lists, you can "lazily" omit indentation except at
+the beginning of a paragraph or other block element:
+
+```
+Term 1
+
+:   Definition
+with lazy continuation.
+
+    Second paragraph of the definition.
+```
+
+If you leave space before the definition (as in the example above), the
+text of the definition will be treated as a paragraph. In some output
+formats, this will mean greater spacing between term/definition pairs.
+For a more compact definition list, omit the space before the
+definition:
+
+```
+Term 1
+  ~ Definition 1
+
+Term 2
+  ~ Definition 2a
+  ~ Definition 2b
+```
+
+Note that space between items in a definition list is required. (A
+variant that loosens this requirement, but disallows "lazy" hard
+wrapping, can be activated with the [`compact_definition_lists`
+extension](#extension-compact_definition_lists).)
+
+{#numbered-example-lists}
+### Numbered example lists
+
+{#extension-example_lists}
+#### Extension: `example_lists`
+
+The special list marker `@` can be used for sequentially numbered
+examples. The first list item with a `@` marker will be numbered '1',
+the next '2', and so on, throughout the document. The numbered examples
+need not occur in a single list; each new list using `@` will take up
+where the last stopped. So, for example:
+
+```
+(@)  My first example will be numbered (1).
+(@)  My second example will be numbered (2).
+
+Explanation of examples.
+
+(@)  My third example will be numbered (3).
+```
+
+Numbered examples can be labeled and referred to elsewhere in the
+document:
+
+```
+(@good)  This is a good example.
+
+As (@good) illustrates, ...
+```
+
+The label can be any string of alphanumeric characters, underscores, or
+hyphens.
+
+Note: continuation paragraphs in example lists must always be indented
+four spaces, regardless of the length of the list marker. That is,
+example lists always behave as if the `four_space_rule` extension is
+set. This is because example labels tend to be long, and indenting
+content to the first non-space character after the label would be
+awkward.
+
+{#ending-a-list}
+### Ending a list
+
+What if you want to put an indented code block after a list?
+
+```
+-   item one
+-   item two
+
+    { my code block }
+```
+
+Trouble! Here pandoc (like other Markdown implementations) will treat
+`{ my code block }` as the second paragraph of item two, and not as a
+code block.
+
+To "cut off" the list after item two, you can insert some non-indented
+content, like an HTML comment, which won’t produce visible output in any
+format:
+
+```
+-   item one
+-   item two
+
+<!-- end of list -->
+
+    { my code block }
+```
+
+You can use the same trick if you want two consecutive lists instead of
+one big list:
+
+```
+1.  one
+2.  two
+3.  three
+
+<!-- -->
+
+1.  uno
+2.  dos
+3.  tres
+```
+
+{#horizontal-rules}
+## Horizontal rules
+
+A line containing a row of three or more `*`, `-`, or `_` characters
+(optionally separated by spaces) produces a horizontal rule:
+
+```
+*  *  *  *
+
+---------------
+```
+
+We strongly recommend that horizontal rules be separated from
+surrounding text by blank lines. If a horizontal rule is not followed by
+a blank line, pandoc may try to interpret the lines that follow as a
+YAML metadata block or a table.
+
+{#tables}
+## Tables
+
+Four kinds of tables may be used. The first three kinds presuppose the
+use of a fixed-width font, such as Courier. The fourth kind can be used
+with proportionally spaced fonts, as it does not require lining up
+columns.
+
+{#extension-table_captions}
+#### Extension: `table_captions`
+
+A caption may optionally be provided with all 4 kinds of tables (as
+illustrated in the examples below). A caption is a paragraph beginning
+with the string `Table:` (or `table:` or just `:`), which will be
+stripped off. It may appear either before or after the table.
+
+{#extension-simple_tables}
+#### Extension: `simple_tables`
+
+Simple tables look like this:
+
+```
+  Right     Left     Center     Default
+-------     ------ ----------   -------
+     12     12        12            12
+    123     123       123          123
+      1     1          1             1
+
+Table:  Demonstration of simple table syntax.
+```
+
+The header and table rows must each fit on one line. Column alignments
+are determined by the position of the header text relative to the dashed
+line below it:[^3]
+
+- If the dashed line is flush with the header text on the right side but
+  extends beyond it on the left, the column is right-aligned.
+- If the dashed line is flush with the header text on the left side but
+  extends beyond it on the right, the column is left-aligned.
+- If the dashed line extends beyond the header text on both sides, the
+  column is centered.
+- If the dashed line is flush with the header text on both sides, the
+  default alignment is used (in most cases, this will be left).
+
+The table must end with a blank line, or a line of dashes followed by a
+blank line.
+
+The column header row may be omitted, provided a dashed line is used to
+end the table. For example:
+
+```
+-------     ------ ----------   -------
+     12     12        12             12
+    123     123       123           123
+      1     1          1              1
+-------     ------ ----------   -------
+```
+
+When the header row is omitted, column alignments are determined on the
+basis of the first line of the table body. So, in the tables above, the
+columns would be right, left, center, and right aligned, respectively.
+
+{#extension-multiline_tables}
+#### Extension: `multiline_tables`
+
+Multiline tables allow header and table rows to span multiple lines of
+text (but cells that span multiple columns or rows of the table are not
+supported). Here is an example:
+
+```
+-------------------------------------------------------------
+ Centered   Default           Right Left
+  Header    Aligned         Aligned Aligned
+----------- ------- --------------- -------------------------
+   First    row                12.0 Example of a row that
+                                    spans multiple lines.
+
+  Second    row                 5.0 Here's another one. Note
+                                    the blank line between
+                                    rows.
+-------------------------------------------------------------
+
+Table: Here's the caption. It, too, may span
+multiple lines.
+```
+
+These work like simple tables, but with the following differences:
+
+- They must begin with a row of dashes, before the header text (unless
+  the header row is omitted).
+- They must end with a row of dashes, then a blank line.
+- The rows must be separated by blank lines.
+
+In multiline tables, the table parser pays attention to the widths of
+the columns, and the writers try to reproduce these relative widths in
+the output. So, if you find that one of the columns is too narrow in the
+output, try widening it in the Markdown source.
+
+The header may be omitted in multiline tables as well as simple tables:
+
+```
+----------- ------- --------------- -------------------------
+   First    row                12.0 Example of a row that
+                                    spans multiple lines.
+
+  Second    row                 5.0 Here's another one. Note
+                                    the blank line between
+                                    rows.
+----------- ------- --------------- -------------------------
+
+: Here's a multiline table without a header.
+```
+
+It is possible for a multiline table to have just one row, but the row
+should be followed by a blank line (and then the row of dashes that ends
+the table), or the table may be interpreted as a simple table.
+
+{#extension-grid_tables}
+#### Extension: `grid_tables`
+
+Grid tables look like this:
+
+```
+: Sample grid table.
+
++---------------+---------------+--------------------+
+| Fruit         | Price         | Advantages         |
++===============+===============+====================+
+| Bananas       | $1.34         | - built-in wrapper |
+|               |               | - bright color     |
++---------------+---------------+--------------------+
+| Oranges       | $2.10         | - cures scurvy     |
+|               |               | - tasty            |
++---------------+---------------+--------------------+
+```
+
+The row of `=`s separates the header from the table body, and can be
+omitted for a headerless table. The cells of grid tables may contain
+arbitrary block elements (multiple paragraphs, code blocks, lists,
+etc.).
+
+Cells can span multiple columns or rows:
+
+```
++---------------------+----------+
+| Property            | Earth    |
++=============+=======+==========+
+|             | min   | -89.2 °C |
+| Temperature +-------+----------+
+| 1961-1990   | mean  | 14 °C    |
+|             +-------+----------+
+|             | max   | 56.7 °C  |
++-------------+-------+----------+
+```
+
+A table header may contain more than one row:
+
+```
++---------------------+-----------------------+
+| Location            | Temperature 1961-1990 |
+|                     | in degree Celsius     |
+|                     +-------+-------+-------+
+|                     | min   | mean  | max   |
++=====================+=======+=======+=======+
+| Antarctica          | -89.2 | N/A   | 19.8  |
++---------------------+-------+-------+-------+
+| Earth               | -89.2 | 14    | 56.7  |
++---------------------+-------+-------+-------+
+```
+
+Alignments can be specified as with pipe tables, by putting colons at
+the boundaries of the separator line after the header:
+
+```
++---------------+---------------+--------------------+
+| Right         | Left          | Centered           |
++==============:+:==============+:==================:+
+| Bananas       | $1.34         | built-in wrapper   |
++---------------+---------------+--------------------+
+```
+
+For headerless tables, the colons go on the top line instead:
+
+```
++--------------:+:--------------+:------------------:+
+| Right         | Left          | Centered           |
++---------------+---------------+--------------------+
+```
+
+A table foot can be defined by enclosing it with separator lines that
+use `=` instead of `-`:
+
+```
+ +---------------+---------------+
+ | Fruit         | Price         |
+ +===============+===============+
+ | Bananas       | $1.34         |
+ +---------------+---------------+
+ | Oranges       | $2.10         |
+ +===============+===============+
+ | Sum           | $3.44         |
+ +===============+===============+
+```
+
+The foot must always be placed at the very bottom of the table.
+
+Grid tables can be created easily using Emacs’ table-mode
+(`M-x table-insert`).
+
+{#extension-pipe_tables}
+#### Extension: `pipe_tables`
+
+Pipe tables look like this:
+
+```
+| Right | Left | Default | Center |
+|------:|:-----|---------|:------:|
+|   12  |  12  |    12   |    12  |
+|  123  |  123 |   123   |   123  |
+|    1  |    1 |     1   |     1  |
+
+  : Demonstration of pipe table syntax.
+```
+
+The syntax is identical to [PHP Markdown Extra
+tables](https://michelf.ca/projects/php-markdown/extra/#table). The
+beginning and ending pipe characters are optional, but pipes are
+required between all columns. The colons indicate column alignment as
+shown. The header cannot be omitted. To simulate a headerless table,
+include a header with blank cells.
+
+Since the pipes indicate column boundaries, columns need not be
+vertically aligned, as they are in the above example. So, this is a
+perfectly legal (though ugly) pipe table:
+
+```
+fruit| price
+-----|-----:
+apple|2.05
+pear|1.37
+orange|3.09
+```
+
+The cells of pipe tables cannot contain block elements like paragraphs
+and lists, and cannot span multiple lines. If any line of the markdown
+source is longer than the column width (see `--columns`), then the table
+will take up the full text width and the cell contents will wrap, with
+the relative cell widths determined by the number of dashes in the line
+separating the table header from the table body. (For example `---|-`
+would make the first column 3/4 and the second column 1/4 of the full
+text width.) On the other hand, if no lines are wider than column width,
+then cell contents will not be wrapped, and the cells will be sized to
+their contents.
+
+Note: pandoc also recognizes pipe tables of the following form, as can
+be produced by Emacs’ orgtbl-mode:
+
+```
+| One | Two   |
+|-----+-------|
+| my  | table |
+| is  | nice  |
+```
+
+The difference is that `+` is used instead of `|`. Other orgtbl features
+are not supported. In particular, to get non-default column alignment,
+you’ll need to add colons as above.
+
+{#metadata-blocks}
+## Metadata blocks
+
+{#extension-pandoc_title_block}
+#### Extension: `pandoc_title_block`
+
+If the file begins with a title block
+
+```
+% title
+% author(s) (separated by semicolons)
+% date
+```
+
+it will be parsed as bibliographic information, not regular text. (It
+will be used, for example, in the title of standalone LaTeX or HTML
+output.) The block may contain just a title, a title and an author, or
+all three elements. If you want to include an author but no title, or a
+title and a date but no author, you need a blank line:
+
+```
+%
+% Author
+```
+
+```
+% My title
+%
+% June 15, 2006
+```
+
+The title may occupy multiple lines, but continuation lines must begin
+with leading space, thus:
+
+```
+% My title
+  on multiple lines
+```
+
+If a document has multiple authors, the authors may be put on separate
+lines with leading space, or separated by semicolons, or both. So, all
+of the following are equivalent:
+
+```
+% Author One
+  Author Two
+```
+
+```
+% Author One; Author Two
+```
+
+```
+% Author One;
+  Author Two
+```
+
+The date must fit on one line.
+
+All three metadata fields may contain standard inline formatting
+(italics, links, footnotes, etc.).
+
+Title blocks will always be parsed, but they will affect the output only
+when the `--standalone` (`-s`) option is chosen. In HTML output, titles
+will appear twice: once in the document head – this is the title that
+will appear at the top of the window in a browser – and once at the
+beginning of the document body. The title in the document head can have
+an optional prefix attached (`--title-prefix` or `-T` option). The title
+in the body appears as an H1 element with class "title", so it can be
+suppressed or reformatted with CSS. If a title prefix is specified with
+`-T` and no title block appears in the document, the title prefix will
+be used by itself as the HTML title.
+
+The man page writer extracts a title, man page section number, and other
+header and footer information from the title line. The title is assumed
+to be the first word on the title line, which may optionally end with a
+(single-digit) section number in parentheses. (There should be no space
+between the title and the parentheses.) Anything after this is assumed
+to be additional footer and header text. A single pipe character (`|`)
+should be used to separate the footer text from the header text. Thus,
+
+```
+% PANDOC(1)
+```
+
+will yield a man page with the title `PANDOC` and section 1.
+
+```
+% PANDOC(1) Pandoc User Manuals
+```
+
+will also have "Pandoc User Manuals" in the footer.
+
+```
+% PANDOC(1) Pandoc User Manuals | Version 4.0
+```
+
+will also have "Version 4.0" in the header.
+
+{#extension-yaml_metadata_block}
+#### Extension: `yaml_metadata_block`
+
+A [YAML](https://yaml.org/spec/1.2/spec.html){title="YAML v1.2 Spec"}
+metadata block is a valid YAML object, delimited by a line of three
+hyphens (`---`) at the top and a line of three hyphens (`---`) or three
+dots (`...`) at the bottom. The initial line `---` must not be followed
+by a blank line. A YAML metadata block may occur anywhere in the
+document, but if it is not at the beginning, it must be preceded by a
+blank line.
+
+Note that, because of the way pandoc concatenates input files when
+several are provided, you may also keep the metadata in a separate YAML
+file and pass it to pandoc as an argument, along with your Markdown
+files:
+
+```
+pandoc chap1.md chap2.md chap3.md metadata.yaml -s -o book.html
+```
+
+Just be sure that the YAML file begins with `---` and ends with `---` or
+`...`. Alternatively, you can use the `--metadata-file` option. Using
+that approach however, you cannot reference content (like footnotes)
+from the main markdown input document.
+
+Metadata will be taken from the fields of the YAML object and added to
+any existing document metadata. Metadata can contain lists and objects
+(nested arbitrarily), but all string scalars will be interpreted as
+Markdown. Fields with names ending in an underscore will be ignored by
+pandoc. (They may be given a role by external processors.) Field names
+must not be interpretable as YAML numbers or boolean values (so, for
+example, `yes`, `True`, and `15` cannot be used as field names).
+
+A document may contain multiple metadata blocks. If two metadata blocks
+attempt to set the same field, the value from the second block will be
+taken.
+
+Each metadata block is handled internally as an independent YAML
+document. This means, for example, that any YAML anchors defined in a
+block cannot be referenced in another block.
+
+When pandoc is used with `-t markdown` to create a Markdown document, a
+YAML metadata block will be produced only if the `-s/--standalone`
+option is used. All of the metadata will appear in a single block at the
+beginning of the document.
+
+Note that
+[YAML](https://yaml.org/spec/1.2/spec.html){title="YAML v1.2 Spec"}
+escaping rules must be followed. Thus, for example, if a title contains
+a colon, it must be quoted, and if it contains a backslash escape, then
+it must be ensured that it is not treated as a [YAML escape
+sequence](https://yaml.org/spec/1.2/spec.html#id2776092). The pipe
+character (`|`) can be used to begin an indented block that will be
+interpreted literally, without need for escaping. This form is necessary
+when the field contains blank lines or block-level formatting:
+
+```
+---
+title:  'This is the title: it contains a colon'
+author:
+- Author One
+- Author Two
+keywords: [nothing, nothingness]
+abstract: |
+  This is the abstract.
+
+  It consists of two paragraphs.
+...
+```
+
+The literal block after the `|` must be indented relative to the line
+containing the `|`. If it is not, the YAML will be invalid and pandoc
+will not interpret it as metadata. For an overview of the complex rules
+governing YAML, see the [Wikipedia entry on YAML
+syntax](https://en.wikipedia.org/wiki/YAML#Syntax).
+
+Template variables will be set automatically from the metadata. Thus,
+for example, in writing HTML, the variable `abstract` will be set to the
+HTML equivalent of the Markdown in the `abstract` field:
+
+```
+<p>This is the abstract.</p>
+<p>It consists of two paragraphs.</p>
+```
+
+Variables can contain arbitrary YAML structures, but the template must
+match this structure. The `author` variable in the default templates
+expects a simple list or string, but can be changed to support more
+complicated structures. The following combination, for example, would
+add an affiliation to the author if one is given:
+
+```
+---
+title: The document title
+author:
+- name: Author One
+  affiliation: University of Somewhere
+- name: Author Two
+  affiliation: University of Nowhere
+...
+```
+
+To use the structured authors in the example above, you would need a
+custom template:
+
+```
+$for(author)$
+$if(author.name)$
+$author.name$$if(author.affiliation)$ ($author.affiliation$)$endif$
+$else$
+$author$
+$endif$
+$endfor$
+```
+
+Raw content to include in the document’s header may be specified using
+`header-includes`; however, it is important to mark up this content as
+raw code for a particular output format, using the [`raw_attribute`
+extension](#extension-raw_attribute), or it will be interpreted as
+markdown. For example:
+
+````
+header-includes:
+- |
+  ```{=latex}
+  \let\oldsection\section
+  \renewcommand{\section}[1]{\clearpage\oldsection{#1}}
+  ```
+````
+
+Note: the `yaml_metadata_block` extension works with `commonmark` as
+well as `markdown` (and it is enabled by default in `gfm` and
+`commonmark_x`). However, in these formats the following restrictions
+apply:
+
+- The YAML metadata block must occur at the beginning of the document
+  (and there can be only one). If multiple files are given as arguments
+  to pandoc, only the first can be a YAML metadata block.
+
+- The leaf nodes of the YAML structure are parsed in isolation from each
+  other and from the rest of the document. So, for example, you can’t
+  use a reference link in these contexts if the link definition is
+  somewhere else in the document.
+
+{#backslash-escapes}
+## Backslash escapes
+
+{#extension-all_symbols_escapable}
+#### Extension: `all_symbols_escapable`
+
+Except inside a code block or inline code, any punctuation or space
+character preceded by a backslash will be treated literally, even if it
+would normally indicate formatting. Thus, for example, if one writes
+
+```
+*\*hello\**
+```
+
+one will get
+
+```
+<em>*hello*</em>
+```
+
+instead of
+
+```
+<strong>hello</strong>
+```
+
+This rule is easier to remember than original Markdown’s rule, which
+allows only the following characters to be backslash-escaped:
+
+```
+\`*_{}[]()>#+-.!
+```
+
+(However, if the `markdown_strict` format is used, the original Markdown
+rule will be used.)
+
+A backslash-escaped space is parsed as a nonbreaking space. In TeX
+output, it will appear as `~`. In HTML and XML output, it will appear as
+a literal unicode nonbreaking space character (note that it will thus
+actually look "invisible" in the generated HTML source; you can still
+use the `--ascii` command-line option to make it appear as an explicit
+entity).
+
+A backslash-escaped newline (i.e. a backslash occurring at the end of a
+line) is parsed as a hard line break. It will appear in TeX output as
+`\\` and in HTML as `<br />`. This is a nice alternative to Markdown’s
+"invisible" way of indicating hard line breaks using two trailing spaces
+on a line.
+
+Backslash escapes do not work in verbatim contexts.
+
+{#inline-formatting}
+## Inline formatting
+
+{#emphasis}
+### Emphasis
+
+To _emphasize_ some text, surround it with `*`s or `_`, like this:
+
+```
+This text is _emphasized with underscores_, and this
+is *emphasized with asterisks*.
+```
+
+Double `*` or `_` produces *strong emphasis*:
+
+```
+This is **strong emphasis** and __with underscores__.
+```
+
+A `*` or `_` character surrounded by spaces, or backslash-escaped, will
+not trigger emphasis:
+
+```
+This is * not emphasized *, and \*neither is this\*.
+```
+
+{#extension-intraword_underscores}
+#### Extension: `intraword_underscores`
+
+Because `_` is sometimes used inside words and identifiers, pandoc does
+not interpret a `_` surrounded by alphanumeric characters as an emphasis
+marker. If you want to emphasize just part of a word, use `*`:
+
+```
+feas*ible*, not feas*able*.
+```
+
+{#strikeout}
+### Strikeout
+
+{#extension-strikeout}
+#### Extension: `strikeout`
+
+To strike out a section of text with a horizontal line, begin and end it
+with `~~`. Thus, for example,
+
+```
+This ~~is deleted text.~~
+```
+
+{#superscripts-and-subscripts}
+### Superscripts and subscripts
+
+{#extension-superscript-subscript}
+#### Extension: `superscript`, `subscript`
+
+Superscripts may be written by surrounding the superscripted text by `^`
+characters; subscripts may be written by surrounding the subscripted
+text by `~` characters. Thus, for example,
+
+```
+H~2~O is a liquid.  2^10^ is 1024.
+```
+
+The text between `^...^` or `~...~` may not contain spaces or newlines.
+If the superscripted or subscripted text contains spaces, these spaces
+must be escaped with backslashes. (This is to prevent accidental
+superscripting and subscripting through the ordinary use of `~` and `^`,
+and also bad interactions with footnotes.) Thus, if you want the letter
+P with 'a cat' in subscripts, use `P~a\ cat~`, not `P~a cat~`.
+
+{#verbatim}
+### Verbatim
+
+To make a short span of text verbatim, put it inside backticks:
+
+```
+What is the difference between `>>=` and `>>`?
+```
+
+If the verbatim text includes a backtick, use double backticks:
+
+```
+Here is a literal backtick `` ` ``.
+```
+
+(The spaces after the opening backticks and before the closing backticks
+will be ignored.)
+
+The general rule is that a verbatim span starts with a string of
+consecutive backticks (optionally followed by a space) and ends with a
+string of the same number of backticks (optionally preceded by a space).
+
+Note that backslash-escapes (and other Markdown constructs) do not work
+in verbatim contexts:
+
+```
+This is a backslash followed by an asterisk: `\*`.
+```
+
+{#extension-inline_code_attributes}
+#### Extension: `inline_code_attributes`
+
+Attributes can be attached to verbatim text, just as with [fenced code
+blocks](#fenced-code-blocks):
+
+```
+`<$>`{.haskell}
+```
+
+{#underline}
+### Underline
+
+To underline text, use the `underline` class:
+
+```
+[Underline]{.underline}
+```
+
+Or, without the `bracketed_spans` extension (but with `native_spans`):
+
+```
+<span class="underline">Underline</span>
+```
+
+This will work in all output formats that support underline.
+
+{#small-caps}
+### Small caps
+
+To write small caps, use the `smallcaps` class:
+
+```
+[Small caps]{.smallcaps}
+```
+
+Or, without the `bracketed_spans` extension:
+
+```
+<span class="smallcaps">Small caps</span>
+```
+
+For compatibility with other Markdown flavors, CSS is also supported:
+
+```
+<span style="font-variant:small-caps;">Small caps</span>
+```
+
+This will work in all output formats that support small caps.
+
+{#highlighting}
+### Highlighting
+
+To highlight text, use the `mark` class:
+
+```
+[Mark]{.mark}
+```
+
+Or, without the `bracketed_spans` extension (but with `native_spans`):
+
+```
+<span class="mark">Mark</span>
+```
+
+This will work in all output formats that support highlighting.
+
+{#math}
+## Math
+
+{#extension-tex_math_dollars}
+#### Extension: `tex_math_dollars`
+
+Anything between two `$` characters will be treated as TeX math. The
+opening `$` must have a non-space character immediately to its right,
+while the closing `$` must have a non-space character immediately to its
+left, and must not be followed immediately by a digit. Thus,
+`$20,000 and $30,000` won’t parse as math. If for some reason you need
+to enclose text in literal `$` characters, backslash-escape them and
+they won’t be treated as math delimiters.
+
+For display math, use `$$` delimiters. (In this case, the delimiters may
+be separated from the formula by whitespace. However, there can be no
+blank lines between the opening and closing `$$` delimiters.)
+
+TeX math will be printed in all output formats. How it is rendered
+depends on the output format:
+
+: LaTeX
+
+  It will appear verbatim surrounded by `\(...\)` (for inline math) or
+  `\[...\]` (for display math).
+: Markdown, Emacs Org mode, ConTeXt, ZimWiki
+
+  It will appear verbatim surrounded by `$...$` (for inline math) or
+  `$$...$$` (for display math).
+: XWiki
+
+  It will appear verbatim surrounded by `{{formula}}..{{/formula}}`.
+: reStructuredText
+
+  It will be rendered using an [interpreted text role
+  `:math:`](https://docutils.sourceforge.io/docs/ref/rst/roles.html#math).
+: AsciiDoc
+
+  For AsciiDoc output math will appear verbatim surrounded by
+  `latexmath:[...]`. For `asciidoc_legacy` the bracketed material will
+  also include inline or display math delimiters.
+: Texinfo
+
+  It will be rendered inside a `@math` command.
+: roff man, Jira markup
+
+  It will be rendered verbatim without `$`’s.
+: MediaWiki, DokuWiki
+
+  It will be rendered inside `<math>` tags.
+: Textile
+
+  It will be rendered inside `<span class="math">` tags.
+: RTF, OpenDocument
+
+  It will be rendered, if possible, using Unicode characters, and will
+  otherwise appear verbatim.
+: ODT
+
+  It will be rendered, if possible, using MathML.
+: DocBook
+
+  If the `--mathml` flag is used, it will be rendered using MathML in an
+  `inlineequation` or `informalequation` tag. Otherwise it will be
+  rendered, if possible, using Unicode characters.
+: Docx and PowerPoint
+
+  It will be rendered using OMML math markup.
+: FictionBook2
+
+  If the `--webtex` option is used, formulas are rendered as images
+  using CodeCogs or other compatible web service, downloaded and
+  embedded in the e-book. Otherwise, they will appear verbatim.
+: HTML, Slidy, DZSlides, S5, EPUB
+
+  The way math is rendered in HTML will depend on the command-line
+  options selected. Therefore see [Math rendering in
+  HTML](#math-rendering-in-html) above.
+
+{#raw-html}
+## Raw HTML
+
+{#extension-raw_html}
+#### Extension: `raw_html`
+
+Markdown allows you to insert raw HTML (or DocBook) anywhere in a
+document (except verbatim contexts, where `<`, `>`, and `&` are
+interpreted literally). (Technically this is not an extension, since
+standard Markdown allows it, but it has been made an extension so that
+it can be disabled if desired.)
+
+The raw HTML is passed through unchanged in HTML, S5, Slidy, Slideous,
+DZSlides, EPUB, Markdown, CommonMark, Emacs Org mode, and Textile
+output, and suppressed in other formats.
+
+For a more explicit way of including raw HTML in a Markdown document,
+see the [`raw_attribute` extension](#extension-raw_attribute).
+
+In the CommonMark format, if `raw_html` is enabled, superscripts,
+subscripts, strikeouts and small capitals will be represented as HTML.
+Otherwise, plain-text fallbacks will be used. Note that even if
+`raw_html` is disabled, tables will be rendered with HTML syntax if they
+cannot use pipe syntax.
+
+{#extension-markdown_in_html_blocks}
+#### Extension: `markdown_in_html_blocks`
+
+Original Markdown allows you to include HTML "blocks": blocks of HTML
+between balanced tags that are separated from the surrounding text with
+blank lines, and start and end at the left margin. Within these blocks,
+everything is interpreted as HTML, not Markdown; so (for example), `*`
+does not signify emphasis.
+
+Pandoc behaves this way when the `markdown_strict` format is used; but
+by default, pandoc interprets material between HTML block tags as
+Markdown. Thus, for example, pandoc will turn
+
+```
+<table>
+<tr>
+<td>*one*</td>
+<td>[a link](https://google.com)</td>
+</tr>
+</table>
+```
+
+into
+
+```
+<table>
+<tr>
+<td><em>one</em></td>
+<td><a href="https://google.com">a link</a></td>
+</tr>
+</table>
+```
+
+whereas `Markdown.pl` will preserve it as is.
+
+There is one exception to this rule: text between `<script>`, `<style>`,
+and `<textarea>` tags is not interpreted as Markdown.
+
+This departure from original Markdown should make it easier to mix
+Markdown with HTML block elements. For example, one can surround a block
+of Markdown text with `<div>` tags without preventing it from being
+interpreted as Markdown.
+
+{#extension-native_divs}
+#### Extension: `native_divs`
+
+Use native pandoc `Div` blocks for content inside `<div>` tags. For the
+most part this should give the same output as `markdown_in_html_blocks`,
+but it makes it easier to write pandoc filters to manipulate groups of
+blocks.
+
+{#extension-native_spans}
+#### Extension: `native_spans`
+
+Use native pandoc `Span` blocks for content inside `<span>` tags. For
+the most part this should give the same output as `raw_html`, but it
+makes it easier to write pandoc filters to manipulate groups of inlines.
+
+{#extension-raw_tex}
+#### Extension: `raw_tex`
+
+In addition to raw HTML, pandoc allows raw LaTeX, TeX, and ConTeXt to be
+included in a document. Inline TeX commands will be preserved and passed
+unchanged to the LaTeX and ConTeXt writers. Thus, for example, you can
+use LaTeX to include BibTeX citations:
+
+```
+This result was proved in \cite{jones.1967}.
+```
+
+Note that in LaTeX environments, like
+
+```
+\begin{tabular}{|l|l|}\hline
+Age & Frequency \\ \hline
+18--25  & 15 \\
+26--35  & 33 \\
+36--45  & 22 \\ \hline
+\end{tabular}
+```
+
+the material between the begin and end tags will be interpreted as raw
+LaTeX, not as Markdown.
+
+For a more explicit and flexible way of including raw TeX in a Markdown
+document, see the [`raw_attribute` extension](#extension-raw_attribute).
+
+Inline LaTeX is ignored in output formats other than Markdown, LaTeX,
+Emacs Org mode, and ConTeXt.
+
+{#generic-raw-attribute}
+### Generic raw attribute
+
+{#extension-raw_attribute}
+#### Extension: `raw_attribute`
+
+Inline spans and fenced code blocks with a special kind of attribute
+will be parsed as raw content with the designated format. For example,
+the following produces a raw roff `ms` block:
+
+````
+```{=ms}
+.MYMACRO
+blah blah
+```
+````
+
+And the following produces a raw `html` inline element:
+
+```
+This is `<a>html</a>`{=html}
+```
+
+This can be useful to insert raw xml into `docx` documents, e.g. a
+pagebreak:
+
+````
+```{=openxml}
+<w:p>
+  <w:r>
+    <w:br w:type="page"/>
+  </w:r>
+</w:p>
+```
+````
+
+The format name should match the target format name (see `-t/--to`,
+above, for a list, or use `pandoc --list-output-formats`). Use `openxml`
+for `docx` output, `opendocument` for `odt` output, `html5` for `epub3`
+output, `html4` for `epub2` output, and `latex`, `beamer`, `ms`, or
+`html5` for `pdf` output (depending on what you use for `--pdf-engine`).
+
+This extension presupposes that the relevant kind of inline code or
+fenced code block is enabled. Thus, for example, to use a raw attribute
+with a backtick code block, `backtick_code_blocks` must be enabled.
+
+The raw attribute cannot be combined with regular attributes.
+
+{#latex-macros}
+## LaTeX macros
+
+{#extension-latex_macros}
+#### Extension: `latex_macros`
+
+When this extension is enabled, pandoc will parse LaTeX macro
+definitions and apply the resulting macros to all LaTeX math and raw
+LaTeX. So, for example, the following will work in all output formats,
+not just LaTeX:
+
+```
+\newcommand{\tuple}[1]{\langle #1 \rangle}
+
+$\tuple{a, b, c}$
+```
+
+Note that LaTeX macros will not be applied if they occur inside a raw
+span or block marked with the [`raw_attribute`
+extension](#extension-raw_attribute).
+
+When `latex_macros` is disabled, the raw LaTeX and math will not have
+macros applied. This is usually a better approach when you are targeting
+LaTeX or PDF.
+
+Macro definitions in LaTeX will be passed through as raw LaTeX only if
+`latex_macros` is not enabled. Macro definitions in Markdown source (or
+other formats allowing `raw_tex`) will be passed through regardless of
+whether `latex_macros` is enabled.
+
+{#links-1}
+## Links
+
+Markdown allows links to be specified in several ways.
+
+{#automatic-links}
+### Automatic links
+
+If you enclose a URL or email address in pointy brackets, it will become
+a link:
+
+```
+<https://google.com>
+<sam@green.eggs.ham>
+```
+
+{#inline-links}
+### Inline links
+
+An inline link consists of the link text in square brackets, followed by
+the URL in parentheses. (Optionally, the URL can be followed by a link
+title, in quotes.)
+
+```
+This is an [inline link](/url), and here's [one with
+a title](https://fsf.org "click here for a good time!").
+```
+
+There can be no space between the bracketed part and the parenthesized
+part. The link text can contain formatting (such as emphasis), but the
+title cannot.
+
+Email addresses in inline links are not autodetected, so they have to be
+prefixed with `mailto`:
+
+```
+[Write me!](mailto:sam@green.eggs.ham)
+```
+
+{#reference-links}
+### Reference links
+
+An _explicit_ reference link has two parts, the link itself and the link
+definition, which may occur elsewhere in the document (either before or
+after the link).
+
+The link consists of link text in square brackets, followed by a label
+in square brackets. (There cannot be space between the two unless the
+`spaced_reference_links` extension is enabled.) The link definition
+consists of the bracketed label, followed by a colon and a space,
+followed by the URL, and optionally (after a space) a link title either
+in quotes or in parentheses. The label must not be parseable as a
+citation (assuming the `citations` extension is enabled): citations take
+precedence over link labels.
+
+Here are some examples:
+
+```
+[my label 1]: /foo/bar.html  "My title, optional"
+[my label 2]: /foo
+[my label 3]: https://fsf.org (The Free Software Foundation)
+[my label 4]: /bar#special  'A title in single quotes'
+```
+
+The URL may optionally be surrounded by angle brackets:
+
+```
+[my label 5]: <http://foo.bar.baz>
+```
+
+The title may go on the next line:
+
+```
+[my label 3]: https://fsf.org
+  "The Free Software Foundation"
+```
+
+Note that link labels are not case sensitive. So, this will work:
+
+```
+Here is [my link][FOO]
+
+[Foo]: /bar/baz
+```
+
+In an _implicit_ reference link, the second pair of brackets is empty:
+
+```
+See [my website][].
+
+[my website]: http://foo.bar.baz
+```
+
+Note: In `Markdown.pl` and most other Markdown implementations,
+reference link definitions cannot occur in nested constructions such as
+list items or block quotes. Pandoc lifts this arbitrary-seeming
+restriction. So the following is fine in pandoc, though not in most
+other implementations:
+
+```
+> My block [quote].
+>
+> [quote]: /foo
+```
+
+{#extension-shortcut_reference_links}
+#### Extension: `shortcut_reference_links`
+
+In a _shortcut_ reference link, the second pair of brackets may be
+omitted entirely:
+
+```
+See [my website].
+
+[my website]: http://foo.bar.baz
+```
+
+{#internal-links}
+### Internal links
+
+To link to another section of the same document, use the automatically
+generated identifier (see [Heading identifiers](#heading-identifiers)).
+For example:
+
+```
+See the [Introduction](#introduction).
+```
+
+or
+
+```
+See the [Introduction].
+
+[Introduction]: #introduction
+```
+
+Internal links are currently supported for HTML formats (including HTML
+slide shows and EPUB), LaTeX, and ConTeXt.
+
+{#images}
+## Images
+
+A link immediately preceded by a `!` will be treated as an image. The
+link text will be used as the image’s alt text:
+
+```
+![la lune](lalune.jpg "Voyage to the moon")
+
+![movie reel]
+
+[movie reel]: movie.gif
+```
+
+{#extension-implicit_figures}
+#### Extension: `implicit_figures`
+
+An image with nonempty alt text, occurring by itself in a paragraph,
+will be rendered as a figure with a caption. The image’s alt text will
+be used as the caption.
+
+```
+![This is the caption](/url/of/image.png)
+```
+
+How this is rendered depends on the output format. Some output formats
+(e.g. RTF) do not yet support figures. In those formats, you’ll just get
+an image in a paragraph by itself, with no caption.
+
+If you just want a regular inline image, just make sure it is not the
+only thing in the paragraph. One way to do this is to insert a
+nonbreaking space after the image:
+
+```
+![This image won't be a figure](/url/of/image.png)\
+```
+
+Note that in reveal.js slide shows, an image in a paragraph by itself
+that has the `r-stretch` class will fill the screen, and the caption and
+figure tags will be omitted.
+
+{#extension-link_attributes}
+#### Extension: `link_attributes`
+
+Attributes can be set on links and images:
+
+```
+An inline ![image](foo.jpg){#id .class width=30 height=20px}
+and a reference ![image][ref] with attributes.
+
+[ref]: foo.jpg "optional title" {#id .class key=val key2="val 2"}
+```
+
+(This syntax is compatible with [PHP Markdown
+Extra](https://michelf.ca/projects/php-markdown/extra/) when only `#id`
+and `.class` are used.)
+
+For HTML and EPUB, all known HTML5 attributes except `width` and
+`height` (but including `srcset` and `sizes`) are passed through as is.
+Unknown attributes are passed through as custom attributes, with `data-`
+prepended. The other writers ignore attributes that are not specifically
+supported by their output format.
+
+The `width` and `height` attributes on images are treated specially.
+When used without a unit, the unit is assumed to be pixels. However, any
+of the following unit identifiers can be used: `px`, `cm`, `mm`, `in`,
+`inch` and `%`. There must not be any spaces between the number and the
+unit. For example:
+
+```
+![](file.jpg){ width=50% }
+```
+
+- Dimensions may be converted to a form that is compatible with the
+  output format (for example, dimensions given in pixels will be
+  converted to inches when converting HTML to LaTeX). Conversion between
+  pixels and physical measurements is affected by the `--dpi` option (by
+  default, 96 dpi is assumed, unless the image itself contains dpi
+  information).
+- The `%` unit is generally relative to some available space. For
+  example the above example will render to the following.
+
+  - HTML: `<img href="file.jpg" style="width: 50%;" />`
+  - LaTeX:
+    `\includegraphics[width=0.5\textwidth,height=\textheight]{file.jpg}`
+    (If you’re using a custom template, you need to configure `graphicx`
+    as in the default template.)
+  - ConTeXt: `\externalfigure[file.jpg][width=0.5\textwidth]`
+- Some output formats have a notion of a class
+  ([ConTeXt](https://wiki.contextgarden.net/Using_Graphics#Multiple_Image_Settings))
+  or a unique identifier (LaTeX `\caption`), or both (HTML).
+- When no `width` or `height` attributes are specified, the fallback is
+  to look at the image resolution and the dpi metadata embedded in the
+  image file.
+
+{#divs-and-spans}
+## Divs and Spans
+
+Using the `native_divs` and `native_spans` extensions (see
+[above](#extension-native_divs)), HTML syntax can be used as part of
+markdown to create native `Div` and `Span` elements in the pandoc AST
+(as opposed to raw HTML). However, there is also nicer syntax available:
+
+{#extension-fenced_divs}
+#### Extension: `fenced_divs`
+
+Allow special fenced syntax for native `Div` blocks. A Div starts with a
+fence containing at least three consecutive colons plus some attributes.
+The attributes may optionally be followed by another string of
+consecutive colons.
+
+Note: the `commonmark` parser doesn’t permit colons after the
+attributes.
+
+The attribute syntax is exactly as in fenced code blocks (see
+[Extension:
+`fenced_code_attributes`](#extension-fenced_code_attributes)). As with
+fenced code blocks, one can use either attributes in curly braces or a
+single unbraced word, which will be treated as a class name. The Div
+ends with another line containing a string of at least three consecutive
+colons. The fenced Div should be separated by blank lines from preceding
+and following blocks.
+
+Example:
+
+```
+::::: {#special .sidebar}
+Here is a paragraph.
+
+And another.
+:::::
+```
+
+Fenced divs can be nested. Opening fences are distinguished because they
+_must_ have attributes:
+
+```
+::: Warning ::::::
+This is a warning.
+
+::: Danger
+This is a warning within a warning.
+:::
+::::::::::::::::::
+```
+
+Fences without attributes are always closing fences. Unlike with fenced
+code blocks, the number of colons in the closing fence need not match
+the number in the opening fence. However, it can be helpful for visual
+clarity to use fences of different lengths to distinguish nested divs
+from their parents.
+
+{#extension-bracketed_spans}
+#### Extension: `bracketed_spans`
+
+A bracketed sequence of inlines, as one would use to begin a link, will
+be treated as a `Span` with attributes if it is followed immediately by
+attributes:
+
+```
+[This is *some text*]{.class key="val"}
+```
+
+{#footnotes}
+## Footnotes
+
+{#extension-footnotes}
+#### Extension: `footnotes`
+
+Pandoc’s Markdown allows footnotes, using the following syntax:
+
+```
+Here is a footnote reference,[^1] and another.[^longnote]
+
+[^1]: Here is the footnote.
+
+[^longnote]: Here's one with multiple blocks.
+
+    Subsequent paragraphs are indented to show that they
+belong to the previous footnote.
+
+        { some.code }
+
+    The whole paragraph can be indented, or just the first
+    line.  In this way, multi-paragraph footnotes work like
+    multi-paragraph list items.
+
+This paragraph won't be part of the note, because it
+isn't indented.
+```
+
+The identifiers in footnote references may not contain spaces, tabs, or
+newlines. These identifiers are used only to correlate the footnote
+reference with the note itself; in the output, footnotes will be
+numbered sequentially.
+
+The footnotes themselves need not be placed at the end of the document.
+They may appear anywhere except inside other block elements (lists,
+block quotes, tables, etc.). Each footnote should be separated from
+surrounding content (including other footnotes) by blank lines.
+
+{#extension-inline_notes}
+#### Extension: `inline_notes`
+
+Inline footnotes are also allowed (though, unlike regular notes, they
+cannot contain multiple paragraphs). The syntax is as follows:
+
+```
+Here is an inline note.^[Inline notes are easier to write, since
+you don't have to pick an identifier and move down to type the
+note.]
+```
+
+Inline and regular footnotes may be mixed freely.
+
+{#citation-syntax}
+## Citation syntax
+
+{#extension-citations}
+#### Extension: `citations`
+
+To cite a bibliographic item with an identifier foo, use the syntax
+`@foo`. Normal citations should be included in square brackets, with
+semicolons separating distinct items:
+
+```
+Blah blah [@doe99; @smith2000; @smith2004].
+```
+
+How this is rendered depends on the citation style. In an author-date
+style, it might render as
+
+```
+Blah blah (Doe 1999, Smith 2000, 2004).
+```
+
+In a footnote style, it might render as
+
+```
+Blah blah.[^1]
+
+[^1]:  John Doe, "Frogs," *Journal of Amphibians* 44 (1999);
+Susan Smith, "Flies," *Journal of Insects* (2000);
+Susan Smith, "Bees," *Journal of Insects* (2004).
+```
+
+See the [CSL user documentation](https://citationstyles.org/authors/)
+for more information about CSL styles and how they affect rendering.
+
+Unless a citation key starts with a letter, digit, or `_`, and contains
+only alphanumerics and single internal punctuation characters
+(`:.#$%&-+?<>~/`), it must be surrounded by curly braces, which are not
+considered part of the key. In `@Foo_bar.baz.`, the key is `Foo_bar.baz`
+because the final period is not _internal_ punctuation, so it is not
+included in the key. In `@{Foo_bar.baz.}`, the key is `Foo_bar.baz.`,
+including the final period. In `@Foo_bar--baz`, the key is `Foo_bar`
+because the repeated internal punctuation characters terminate the key.
+The curly braces are recommended if you use URLs as keys:
+`[@{https://example.com/bib?name=foobar&date=2000}, p.  33]`.
+
+Citation items may optionally include a prefix, a locator, and a suffix.
+In
+
+```
+Blah blah [see @doe99, pp. 33-35 and *passim*; @smith04, chap. 1].
+```
+
+the first item (`doe99`) has prefix `see`, locator `pp.  33-35`, and
+suffix `and *passim*`. The second item (`smith04`) has locator `chap. 1`
+and no prefix or suffix.
+
+Pandoc uses some heuristics to separate the locator from the rest of the
+subject. It is sensitive to the locator terms defined in the [CSL locale
+files](https://github.com/citation-style-language/locales). Either
+abbreviated or unabbreviated forms are accepted. In the `en-US` locale,
+locator terms can be written in either singular or plural forms, as
+`book`, `bk.`/`bks.`; `chapter`, `chap.`/`chaps.`; `column`,
+`col.`/`cols.`; `figure`, `fig.`/`figs.`; `folio`, `fol.`/`fols.`;
+`number`, `no.`/`nos.`; `line`, `l.`/`ll.`; `note`, `n.`/`nn.`; `opus`,
+`op.`/`opp.`; `page`, `p.`/`pp.`; `paragraph`, `para.`/`paras.`; `part`,
+`pt.`/`pts.`; `section`, `sec.`/`secs.`; `sub verbo`, `s.v.`/`s.vv.`;
+`verse`, `v.`/`vv.`; `volume`, `vol.`/`vols.`; `¶`/`¶¶`; `§`/`§§`. If no
+locator term is used, "page" is assumed.
+
+In complex cases, you can force something to be treated as a locator by
+enclosing it in curly braces or prevent parsing the suffix as locator by
+prepending curly braces:
+
+```
+[@smith{ii, A, D-Z}, with a suffix]
+[@smith, {pp. iv, vi-xi, (xv)-(xvii)} with suffix here]
+[@smith{}, 99 years later]
+```
+
+A minus sign (`-`) before the `@` will suppress mention of the author in
+the citation. This can be useful when the author is already mentioned in
+the text:
+
+```
+Smith says blah [-@smith04].
+```
+
+You can also write an author-in-text citation, by omitting the square
+brackets:
+
+```
+@smith04 says blah.
+
+@smith04 [p. 33] says blah.
+```
+
+This will cause the author’s name to be rendered, followed by the
+bibliographical details. Use this form when you want to make the
+citation the subject of a sentence.
+
+When you are using a note style, it is usually better to let citeproc
+create the footnotes from citations rather than writing an explicit
+note. If you do write an explicit note that contains a citation, note
+that normal citations will be put in parentheses, while author-in-text
+citations will not. For this reason, it is sometimes preferable to use
+the author-in-text style inside notes when using a note style.
+
+{#non-default-extensions}
+## Non-default extensions
+
+The following Markdown syntax extensions are not enabled by default in
+pandoc, but may be enabled by adding `+EXTENSION` to the format name,
+where `EXTENSION` is the name of the extension. Thus, for example,
+`markdown+hard_line_breaks` is Markdown with hard line breaks.
+
+{#extension-rebase_relative_paths}
+#### Extension: `rebase_relative_paths`
+
+Rewrite relative paths for Markdown links and images, depending on the
+path of the file containing the link or image link. For each link or
+image, pandoc will compute the directory of the containing file,
+relative to the working directory, and prepend the resulting path to the
+link or image path.
+
+The use of this extension is best understood by example. Suppose you
+have a subdirectory for each chapter of a book, `chap1`, `chap2`,
+`chap3`. Each contains a file `text.md` and a number of images used in
+the chapter. You would like to have `![image](spider.jpg)` in
+`chap1/text.md` refer to `chap1/spider.jpg` and `![image](spider.jpg)`
+in `chap2/text.md` refer to `chap2/spider.jpg`. To do this, use
+
+```
+pandoc chap*/*.md -f markdown+rebase_relative_paths
+```
+
+Without this extension, you would have to use
+`![image](chap1/spider.jpg)` in `chap1/text.md` and
+`![image](chap2/spider.jpg)` in `chap2/text.md`. Links with relative
+paths will be rewritten in the same way as images.
+
+Absolute paths and URLs are not changed. Neither are empty paths or
+paths consisting entirely of a fragment, e.g., `#foo`.
+
+Note that relative paths in reference links and images will be rewritten
+relative to the file containing the link reference definition, not the
+file containing the reference link or image itself, if these differ.
+
+{#extension-mark}
+#### Extension: `mark`
+
+To highlight out a section of text, begin and end it with with `==`.
+Thus, for example,
+
+```
+This ==is deleted text.==
+```
+
+{#extension-attributes}
+#### Extension: `attributes`
+
+Allows attributes to be attached to any inline or block-level element
+when parsing `commonmark`. The syntax for the attributes is the same as
+that used in [`header_attributes`](#extension-header_attributes).
+
+- Attributes that occur immediately after an inline element affect that
+  element. If they follow a space, then they belong to the space.
+  (Hence, this option subsumes `inline_code_attributes` and
+  `link_attributes`.)
+- Attributes that occur immediately before a block element, on a line by
+  themselves, affect that element.
+- Consecutive attribute specifiers may be used, either for blocks or for
+  inlines. Their attributes will be combined.
+- Attributes that occur at the end of the text of a Setext or ATX
+  heading (separated by whitespace from the text) affect the heading
+  element. (Hence, this option subsumes `header_attributes`.)
+- Attributes that occur after the opening fence in a fenced code block
+  affect the code block element. (Hence, this option subsumes
+  `fenced_code_attributes`.)
+- Attributes that occur at the end of a reference link definition affect
+  links that refer to that definition.
+
+Note that pandoc’s AST does not currently allow attributes to be
+attached to arbitrary elements. Hence a Span or Div container will be
+added if needed.
+
+{#extension-old_dashes}
+#### Extension: `old_dashes`
+
+Selects the pandoc \<= 1.8.2.1 behavior for parsing smart dashes: `-`
+before a numeral is an en-dash, and `--` is an em-dash. This option only
+has an effect if `smart` is enabled. It is selected automatically for
+`textile` input.
+
+{#extension-angle_brackets_escapable}
+#### Extension: `angle_brackets_escapable`
+
+Allow `<` and `>` to be backslash-escaped, as they can be in GitHub
+flavored Markdown but not original Markdown. This is implied by pandoc’s
+default `all_symbols_escapable`.
+
+{#extension-lists_without_preceding_blankline}
+#### Extension: `lists_without_preceding_blankline`
+
+Allow a list to occur right after a paragraph, with no intervening blank
+space.
+
+{#extension-four_space_rule}
+#### Extension: `four_space_rule`
+
+Selects the pandoc \<= 2.0 behavior for parsing lists, so that four
+spaces indent are needed for list item continuation paragraphs.
+
+{#extension-spaced_reference_links}
+#### Extension: `spaced_reference_links`
+
+Allow whitespace between the two components of a reference link, for
+example,
+
+```
+[foo] [bar].
+```
+
+{#extension-hard_line_breaks}
+#### Extension: `hard_line_breaks`
+
+Causes all newlines within a paragraph to be interpreted as hard line
+breaks instead of spaces.
+
+{#extension-ignore_line_breaks}
+#### Extension: `ignore_line_breaks`
+
+Causes newlines within a paragraph to be ignored, rather than being
+treated as spaces or as hard line breaks. This option is intended for
+use with East Asian languages where spaces are not used between words,
+but text is divided into lines for readability.
+
+{#extension-east_asian_line_breaks}
+#### Extension: `east_asian_line_breaks`
+
+Causes newlines within a paragraph to be ignored, rather than being
+treated as spaces or as hard line breaks, when they occur between two
+East Asian wide characters. This is a better choice than
+`ignore_line_breaks` for texts that include a mix of East Asian wide
+characters and other characters.
+
+{#extension-emoji}
+#### Extension: `emoji`
+
+Parses textual emojis like `:smile:` as Unicode emoticons.
+
+{#extension-tex_math_single_backslash}
+#### Extension: `tex_math_single_backslash`
+
+Causes anything between `\(` and `\)` to be interpreted as inline TeX
+math, and anything between `\[` and `\]` to be interpreted as display
+TeX math. Note: a drawback of this extension is that it precludes
+escaping `(` and `[`.
+
+{#extension-tex_math_double_backslash}
+#### Extension: `tex_math_double_backslash`
+
+Causes anything between `\\(` and `\\)` to be interpreted as inline TeX
+math, and anything between `\\[` and `\\]` to be interpreted as display
+TeX math.
+
+{#extension-markdown_attribute}
+#### Extension: `markdown_attribute`
+
+By default, pandoc interprets material inside block-level tags as
+Markdown. This extension changes the behavior so that Markdown is only
+parsed inside block-level tags if the tags have the attribute
+`markdown=1`.
+
+{#extension-mmd_title_block}
+#### Extension: `mmd_title_block`
+
+Enables a [MultiMarkdown](https://fletcherpenney.net/multimarkdown/)
+style title block at the top of the document, for example:
+
+```
+Title:   My title
+Author:  John Doe
+Date:    September 1, 2008
+Comment: This is a sample mmd title block, with
+         a field spanning multiple lines.
+```
+
+See the MultiMarkdown documentation for details. If `pandoc_title_block`
+or `yaml_metadata_block` is enabled, it will take precedence over
+`mmd_title_block`.
+
+{#extension-abbreviations}
+#### Extension: `abbreviations`
+
+Parses PHP Markdown Extra abbreviation keys, like
+
+```
+*[HTML]: Hypertext Markup Language
+```
+
+Note that the pandoc document model does not support abbreviations, so
+if this extension is enabled, abbreviation keys are simply skipped (as
+opposed to being parsed as paragraphs).
+
+{#extension-autolink_bare_uris}
+#### Extension: `autolink_bare_uris`
+
+Makes all absolute URIs into links, even when not surrounded by pointy
+braces `<...>`.
+
+{#extension-mmd_link_attributes}
+#### Extension: `mmd_link_attributes`
+
+Parses multimarkdown style key-value attributes on link and image
+references. This extension should not be confused with the
+[`link_attributes`](#extension-link_attributes) extension.
+
+```
+This is a reference ![image][ref] with multimarkdown attributes.
+
+[ref]: https://path.to/image "Image title" width=20px height=30px
+       id=myId class="myClass1 myClass2"
+```
+
+{#extension-mmd_header_identifiers}
+#### Extension: `mmd_header_identifiers`
+
+Parses multimarkdown style heading identifiers (in square brackets,
+after the heading but before any trailing `#`s in an ATX heading).
+
+{#extension-compact_definition_lists}
+#### Extension: `compact_definition_lists`
+
+Activates the definition list syntax of pandoc 1.12.x and earlier. This
+syntax differs from the one described above under [Definition
+lists](#definition-lists) in several respects:
+
+- No blank line is required between consecutive items of the definition
+  list.
+- To get a "tight" or "compact" list, omit space between consecutive
+  items; the space between a term and its definition does not affect
+  anything.
+- Lazy wrapping of paragraphs is not allowed: the entire definition must
+  be indented four spaces.[^4]
+
+{#extension-gutenberg}
+#### Extension: `gutenberg`
+
+Use [Project Gutenberg](https://www.gutenberg.org) conventions for
+`plain` output: all-caps for strong emphasis, surround by underscores
+for regular emphasis, add extra blank space around headings.
+
+{#extension-sourcepos}
+#### Extension: `sourcepos`
+
+Include source position attributes when parsing `commonmark`. For
+elements that accept attributes, a `data-pos` attribute is added; other
+elements are placed in a surrounding Div or Span element with a
+`data-pos` attribute.
+
+{#extension-short_subsuperscripts}
+#### Extension: `short_subsuperscripts`
+
+Parse multimarkdown style subscripts and superscripts, which start with
+a '\~' or '\^' character, respectively, and include the alphanumeric
+sequence that follows. For example:
+
+```
+x^2 = 4
+```
+
+or
+
+```
+Oxygen is O~2.
+```
+
+{#extension-wikilinks_title_after_pipe}
+#### Extension: `wikilinks_title_after_pipe`
+
+Pandoc supports multiple markdown wikilink syntaxes, regardless of
+whether the title is before or after the pipe.
+
+Using `--from=markdown+wikilinks_title_after_pipe` results in
+
+``` [[wiki]]
+[[URL|title]]
+```
+
+while using `--from=markdown+wikilinks_title_before_pipe` results in
+
+``` [[wiki]]
+[[title|URL]]
+```
+
+{#markdown-variants}
+## Markdown variants
+
+In addition to pandoc’s extended Markdown, the following Markdown
+variants are supported:
+
+- `markdown_phpextra` (PHP Markdown Extra)
+- `markdown_github` (deprecated GitHub-Flavored Markdown)
+- `markdown_mmd` (MultiMarkdown)
+- `markdown_strict` (Markdown.pl)
+- `commonmark` (CommonMark)
+- `gfm` (Github-Flavored Markdown)
+- `commonmark_x` (CommonMark with many pandoc extensions)
+
+To see which extensions are supported for a given format, and which are
+enabled by default, you can use the command
+
+```
+pandoc --list-extensions=FORMAT
+```
+
+where `FORMAT` is replaced with the name of the format.
+
+Note that the list of extensions for `commonmark`, `gfm`, and
+`commonmark_x` are defined relative to default commonmark. So, for
+example, `backtick_code_blocks` does not appear as an extension, since
+it is enabled by default and cannot be disabled.
+
+{#citations}
+# Citations
+
+When the `--citeproc` option is used, pandoc can automatically generate
+citations and a bibliography in a number of styles. Basic usage is
+
+```
+pandoc --citeproc myinput.txt
+```
+
+To use this feature, you will need to have
+
+- a document containing citations (see [Citation
+  syntax](#citation-syntax));
+- a source of bibliographic data: either an external bibliography file
+  or a list of `references` in the document’s YAML metadata;
+- optionally, a
+  [CSL](https://docs.citationstyles.org/en/stable/specification.html)
+  citation style.
+
+{#specifying-bibliographic-data}
+## Specifying bibliographic data
+
+You can specify an external bibliography using the `bibliography`
+metadata field in a YAML metadata section or the `--bibliography`
+command line argument. If you want to use multiple bibliography files,
+you can supply multiple `--bibliography` arguments or set `bibliography`
+metadata field to YAML array. A bibliography may have any of these
+formats:
+
+|Format|File extension|
+|:--|---|
+|BibLaTeX|.bib|
+|BibTeX|.bibtex|
+|CSL JSON|.json|
+|CSL YAML|.yaml|
+|RIS|.ris|
+
+Note that `.bib` can be used with both BibTeX and BibLaTeX files; use
+the extension `.bibtex` to force interpretation as BibTeX.
+
+In BibTeX and BibLaTeX databases, pandoc parses LaTeX markup inside
+fields such as `title`; in CSL YAML databases, pandoc Markdown; and in
+CSL JSON databases, an [HTML-like
+markup](https://docs.citationstyles.org/en/1.0/release-notes.html#rich-text-markup-within-fields):
+
+: `<i>...</i>`
+
+  italics
+: `<b>...</b>`
+
+  bold
+: `<span style="font-variant:small-caps;">...</span>` or `<sc>...</sc>`
+
+  small capitals
+: `<sub>...</sub>`
+
+  subscript
+: `<sup>...</sup>`
+
+  superscript
+: `<span class="nocase">...</span>`
+
+  prevent a phrase from being capitalized as title case
+
+As an alternative to specifying a bibliography file using
+`--bibliography` or the YAML metadata field `bibliography`, you can
+include the citation data directly in the `references` field of the
+document’s YAML metadata. The field should contain an array of
+YAML-encoded references, for example:
+
+```
+---
+references:
+- type: article-journal
+  id: WatsonCrick1953
+  author:
+  - family: Watson
+    given: J. D.
+  - family: Crick
+    given: F. H. C.
+  issued:
+    date-parts:
+    - - 1953
+      - 4
+      - 25
+  title: 'Molecular structure of nucleic acids: a structure for
+    deoxyribose nucleic acid'
+  title-short: Molecular structure of nucleic acids
+  container-title: Nature
+  volume: 171
+  issue: 4356
+  page: 737-738
+  DOI: 10.1038/171737a0
+  URL: https://www.nature.com/articles/171737a0
+  language: en-GB
+...
+```
+
+If both an external bibliography and inline (YAML metadata) references
+are provided, both will be used. In case of conflicting `id`s, the
+inline references will take precedence.
+
+Note that pandoc can be used to produce such a YAML metadata section
+from a BibTeX, BibLaTeX, or CSL JSON bibliography:
+
+```
+pandoc chem.bib -s -f biblatex -t markdown
+pandoc chem.json -s -f csljson -t markdown
+```
+
+Indeed, pandoc can convert between any of these citation formats:
+
+```
+pandoc chem.bib -s -f biblatex -t csljson
+pandoc chem.yaml -s -f markdown -t biblatex
+```
+
+Running pandoc on a bibliography file with the `--citeproc` option will
+create a formatted bibliography in the format of your choice:
+
+```
+pandoc chem.bib -s --citeproc -o chem.html
+pandoc chem.bib -s --citeproc -o chem.pdf
+```
+
+{#capitalization-in-titles}
+### Capitalization in titles
+
+If you are using a bibtex or biblatex bibliography, then observe the
+following rules:
+
+- English titles should be in title case. Non-English titles should be
+  in sentence case, and the `langid` field in biblatex should be set to
+  the relevant language. (The following values are treated as English:
+  `american`, `british`, `canadian`, `english`, `australian`,
+  `newzealand`, `USenglish`, or `UKenglish`.)
+
+- As is standard with bibtex/biblatex, proper names should be protected
+  with curly braces so that they won’t be lowercased in styles that call
+  for sentence case. For example:
+
+  ```
+  title = {My Dinner with {Andre}}
+  ```
+
+- In addition, words that should remain lowercase (or camelCase) should
+  be protected:
+
+  ```
+  title = {Spin Wave Dispersion on the {nm} Scale}
+  ```
+
+  Though this is not necessary in bibtex/biblatex, it is necessary with
+  citeproc, which stores titles internally in sentence case, and
+  converts to title case in styles that require it. Here we protect "nm"
+  so that it doesn’t get converted to "Nm" at this stage.
+
+If you are using a CSL bibliography (either JSON or YAML), then observe
+the following rules:
+
+- All titles should be in sentence case.
+
+- Use the `language` field for non-English titles to prevent their
+  conversion to title case in styles that call for this. (Conversion
+  happens only if `language` begins with `en` or is left empty.)
+
+- Protect words that should not be converted to title case using this
+  syntax:
+
+  ```
+  Spin wave dispersion on the <span class="nocase">nm</span> scale
+  ```
+
+{#conference-papers-published-vs.-unpublished}
+### Conference Papers, Published vs. Unpublished
+
+For a formally published conference paper, use the biblatex entry type
+`inproceedings` (which will be mapped to CSL `paper-conference`).
+
+For an unpublished manuscript, use the biblatex entry type `unpublished`
+without an `eventtitle` field (this entry type will be mapped to CSL
+`manuscript`).
+
+For a talk, an unpublished conference paper, or a poster presentation,
+use the biblatex entry type `unpublished` with an `eventtitle` field
+(this entry type will be mapped to CSL `speech`). Use the biblatex
+`type` field to indicate the type, e.g. "Paper", or "Poster". `venue`
+and `eventdate` may be useful too, though `eventdate` will not be
+rendered by most CSL styles. Note that `venue` is for the event’s venue,
+unlike `location` which describes the publisher’s location; do not use
+the latter for an unpublished conference paper.
+
+{#specifying-a-citation-style}
+## Specifying a citation style
+
+Citations and references can be formatted using any style supported by
+the [Citation Style Language](https://citationstyles.org), listed in the
+[Zotero Style Repository](https://www.zotero.org/styles). These files
+are specified using the `--csl` option or the `csl` (or
+`citation-style`) metadata field. By default, pandoc will use the
+[Chicago Manual of Style](https://chicagomanualofstyle.org) author-date
+format. (You can override this default by copying a CSL style of your
+choice to `default.csl` in your user data directory.) The CSL project
+provides further information on [finding and editing
+styles](https://citationstyles.org/authors/).
+
+The `--citation-abbreviations` option (or the `citation-abbreviations`
+metadata field) may be used to specify a JSON file containing
+abbreviations of journals that should be used in formatted
+bibliographies when `form="short"` is specified. The format of the file
+can be illustrated with an example:
+
+```
+{ "default": {
+    "container-title": {
+            "Lloyd's Law Reports": "Lloyd's Rep",
+            "Estates Gazette": "EG",
+            "Scots Law Times": "SLT"
+    }
+  }
+}
+```
+
+{#citations-in-note-styles}
+## Citations in note styles
+
+Pandoc’s citation processing is designed to allow you to move between
+author-date, numerical, and note styles without modifying the markdown
+source. When you’re using a note style, avoid inserting footnotes
+manually. Instead, insert citations just as you would in an author-date
+style—for example,
+
+```
+Blah blah [@foo, p. 33].
+```
+
+The footnote will be created automatically. Pandoc will take care of
+removing the space and moving the note before or after the period,
+depending on the setting of `notes-after-punctuation`, as described
+below in [Other relevant metadata
+fields](#other-relevant-metadata-fields).
+
+In some cases you may need to put a citation inside a regular footnote.
+Normal citations in footnotes (such as `[@foo, p. 33]`) will be rendered
+in parentheses. In-text citations (such as `@foo [p. 33]`) will be
+rendered without parentheses. (A comma will be added if appropriate.)
+Thus:
+
+```
+[^1]:  Some studies [@foo; @bar, p. 33] show that
+frubulicious zoosnaps are quantical.  For a survey
+of the literature, see @baz [chap. 1].
+```
+
+{#placement-of-the-bibliography}
+## Placement of the bibliography
+
+If the style calls for a list of works cited, it will be placed in a div
+with id `refs`, if one exists:
+
+```
+::: {#refs}
+:::
+```
+
+Otherwise, it will be placed at the end of the document. Generation of
+the bibliography can be suppressed by setting
+`suppress-bibliography: true` in the YAML metadata.
+
+If you wish the bibliography to have a section heading, you can set
+`reference-section-title` in the metadata, or put the heading at the
+beginning of the div with id `refs` (if you are using it) or at the end
+of your document:
+
+```
+last paragraph...
+
+# References
+```
+
+The bibliography will be inserted after this heading. Note that the
+`unnumbered` class will be added to this heading, so that the section
+will not be numbered.
+
+If you want to put the bibliography into a variable in your template,
+one way to do that is to put the div with id `refs` into a metadata
+field, e.g.
+
+```
+---
+refs: |
+   ::: {#refs}
+   :::
+...
+```
+
+You can then put the variable `$refs$` into your template where you want
+the bibliography to be placed.
+
+{#including-uncited-items-in-the-bibliography}
+## Including uncited items in the bibliography
+
+If you want to include items in the bibliography without actually citing
+them in the body text, you can define a dummy `nocite` metadata field
+and put the citations there:
+
+```
+---
+nocite: |
+  @item1, @item2
+...
+
+@item3
+```
+
+In this example, the document will contain a citation for `item3` only,
+but the bibliography will contain entries for `item1`, `item2`, and
+`item3`.
+
+It is possible to create a bibliography with all the citations, whether
+or not they appear in the document, by using a wildcard:
+
+```
+---
+nocite: |
+  @*
+...
+```
+
+For LaTeX output, you can also use
+[`natbib`](https://ctan.org/pkg/natbib) or
+[`biblatex`](https://ctan.org/pkg/biblatex) to render the bibliography.
+In order to do so, specify bibliography files as outlined above, and add
+`--natbib` or `--biblatex` argument to pandoc invocation. Bear in mind
+that bibliography files have to be in either BibTeX (for `--natbib`) or
+BibLaTeX (for `--biblatex`) format.
+
+{#other-relevant-metadata-fields}
+## Other relevant metadata fields
+
+A few other metadata fields affect bibliography formatting:
+
+: `link-citations`
+
+  If true, citations will be hyperlinked to the corresponding
+  bibliography entries (for author-date and numerical styles only).
+  Defaults to false.
+: `link-bibliography`
+
+  If true, DOIs, PMCIDs, PMID, and URLs in bibliographies will be
+  rendered as hyperlinks. (If an entry contains a DOI, PMCID, PMID, or
+  URL, but none of these fields are rendered by the style, then the
+  title, or in the absence of a title the whole entry, will be
+  hyperlinked.) Defaults to true.
+: `lang`
+
+  The `lang` field will affect how the style is localized, for example
+  in the translation of labels, the use of quotation marks, and the way
+  items are sorted. (For backwards compatibility, `locale` may be used
+  instead of `lang`, but this use is deprecated.)
+
+  A BCP 47 language tag is expected: for example, `en`, `de`, `en-US`,
+  `fr-CA`, `ug-Cyrl`. The unicode extension syntax (after `-u-`) may be
+  used to specify options for collation (sorting) more precisely. Here
+  are some examples:
+
+  - `zh-u-co-pinyin` – Chinese with the Pinyin collation.
+  - `es-u-co-trad` – Spanish with the traditional collation (with `Ch`
+    sorting after `C`).
+  - `fr-u-kb` – French with "backwards" accent sorting (with `coté`
+    sorting after `côte`).
+  - `en-US-u-kf-upper` – English with uppercase letters sorting before
+    lower (default is lower before upper).
+: `notes-after-punctuation`
+
+  If true (the default for note styles), pandoc will put footnote
+  references or superscripted numerical citations after following
+  punctuation. For example, if the source contains
+  `blah blah [@jones99].`, the result will look like `blah blah.[^1]`,
+  with the note moved after the period and the space collapsed. If
+  false, the space will still be collapsed, but the footnote will not be
+  moved after the punctuation. The option may also be used in numerical
+  styles that use superscripts for citation numbers (but for these
+  styles the default is not to move the citation).
+
+{#slide-shows}
+# Slide shows
+
+You can use pandoc to produce an HTML + JavaScript slide presentation
+that can be viewed via a web browser. There are five ways to do this,
+using [S5](https://meyerweb.com/eric/tools/s5/),
+[DZSlides](https://paulrouget.com/dzslides/),
+[Slidy](https://www.w3.org/Talks/Tools/Slidy2/),
+[Slideous](https://goessner.net/articles/slideous/), or
+[reveal.js](https://revealjs.com/). You can also produce a PDF slide
+show using LaTeX [`beamer`](https://ctan.org/pkg/beamer), or slide shows
+in Microsoft
+[PowerPoint](https://en.wikipedia.org/wiki/Microsoft_PowerPoint) format.
+
+Here’s the Markdown source for a simple slide show, `habits.txt`:
+
+```
+% Habits
+% John Doe
+% March 22, 2005
+
+# In the morning
+
+## Getting up
+
+- Turn off alarm
+- Get out of bed
+
+## Breakfast
+
+- Eat eggs
+- Drink coffee
+
+# In the evening
+
+## Dinner
+
+- Eat spaghetti
+- Drink wine
+
+------------------
+
+![picture of spaghetti](images/spaghetti.jpg)
+
+## Going to sleep
+
+- Get in bed
+- Count sheep
+```
+
+To produce an HTML/JavaScript slide show, simply type
+
+```
+pandoc -t FORMAT -s habits.txt -o habits.html
+```
+
+where `FORMAT` is either `s5`, `slidy`, `slideous`, `dzslides`, or
+`revealjs`.
+
+For Slidy, Slideous, reveal.js, and S5, the file produced by pandoc with
+the `-s/--standalone` option embeds a link to JavaScript and CSS files,
+which are assumed to be available at the relative path `s5/default` (for
+S5), `slideous` (for Slideous), `reveal.js` (for reveal.js), or at the
+Slidy website at `w3.org` (for Slidy). (These paths can be changed by
+setting the `slidy-url`, `slideous-url`, `revealjs-url`, or `s5-url`
+variables; see [Variables for HTML slides](#variables-for-html-slides),
+above.) For DZSlides, the (relatively short) JavaScript and CSS are
+included in the file by default.
+
+With all HTML slide formats, the `--self-contained` option can be used
+to produce a single file that contains all of the data necessary to
+display the slide show, including linked scripts, stylesheets, images,
+and videos.
+
+To produce a PDF slide show using beamer, type
+
+```
+pandoc -t beamer habits.txt -o habits.pdf
+```
+
+Note that a reveal.js slide show can also be converted to a PDF by
+printing it to a file from the browser.
+
+To produce a PowerPoint slide show, type
+
+```
+pandoc habits.txt -o habits.pptx
+```
+
+{#structuring-the-slide-show}
+## Structuring the slide show
+
+By default, the _slide level_ is the highest heading level in the
+hierarchy that is followed immediately by content, and not another
+heading, somewhere in the document. In the example above, level-1
+headings are always followed by level-2 headings, which are followed by
+content, so the slide level is 2. This default can be overridden using
+the `--slide-level` option.
+
+The document is carved up into slides according to the following rules:
+
+- A horizontal rule always starts a new slide.
+
+- A heading at the slide level always starts a new slide.
+
+- Headings _below_ the slide level in the hierarchy create headings
+  _within_ a slide. (In beamer, a "block" will be created. If the
+  heading has the class `example`, an `exampleblock` environment will be
+  used; if it has the class `alert`, an `alertblock` will be used;
+  otherwise a regular `block` will be used.)
+
+- Headings _above_ the slide level in the hierarchy create "title
+  slides," which just contain the section title and help to break the
+  slide show into sections. Non-slide content under these headings will
+  be included on the title slide (for HTML slide shows) or in a
+  subsequent slide with the same title (for beamer).
+
+- A title page is constructed automatically from the document’s title
+  block, if present. (In the case of beamer, this can be disabled by
+  commenting out some lines in the default template.)
+
+These rules are designed to support many different styles of slide show.
+If you don’t care about structuring your slides into sections and
+subsections, you can either just use level-1 headings for all slides (in
+that case, level 1 will be the slide level) or you can set
+`--slide-level=0`.
+
+Note: in reveal.js slide shows, if slide level is 2, a two-dimensional
+layout will be produced, with level-1 headings building horizontally and
+level-2 headings building vertically. It is not recommended that you use
+deeper nesting of section levels with reveal.js unless you set
+`--slide-level=0` (which lets reveal.js produce a one-dimensional layout
+and only interprets horizontal rules as slide boundaries).
+
+{#powerpoint-layout-choice}
+### PowerPoint layout choice
+
+When creating slides, the pptx writer chooses from a number of
+pre-defined layouts, based on the content of the slide:
+
+: Title Slide
+
+  This layout is used for the initial slide, which is generated and
+  filled from the metadata fields `date`, `author`, and `title`, if they
+  are present.
+: Section Header
+
+  This layout is used for what pandoc calls "title slides", i.e. slides
+  which start with a header which is above the slide level in the
+  hierarchy.
+: Two Content
+
+  This layout is used for two-column slides, i.e. slides containing a
+  div with class `columns` which contains at least two divs with class
+  `column`.
+: Comparison
+
+  This layout is used instead of "Two Content" for any two-column slides
+  in which at least one column contains text followed by non-text
+  (e.g. an image or a table).
+: Content with Caption
+
+  This layout is used for any non-two-column slides which contain text
+  followed by non-text (e.g. an image or a table).
+: Blank
+
+  This layout is used for any slides which only contain blank content,
+  e.g. a slide containing only speaker notes, or a slide containing only
+  a non-breaking space.
+: Title and Content
+
+  This layout is used for all slides which do not match the criteria for
+  another layout.
+
+These layouts are chosen from the default pptx reference doc included
+with pandoc, unless an alternative reference doc is specified using
+`--reference-doc`.
+
+{#incremental-lists}
+## Incremental lists
+
+By default, these writers produce lists that display "all at once." If
+you want your lists to display incrementally (one item at a time), use
+the `-i` option. If you want a particular list to depart from the
+default, put it in a `div` block with class `incremental` or
+`nonincremental`. So, for example, using the `fenced div` syntax, the
+following would be incremental regardless of the document default:
+
+```
+::: incremental
+
+- Eat spaghetti
+- Drink wine
+
+:::
+```
+
+or
+
+```
+::: nonincremental
+
+- Eat spaghetti
+- Drink wine
+
+:::
+```
+
+While using `incremental` and `nonincremental` divs is the recommended
+method of setting incremental lists on a per-case basis, an older method
+is also supported: putting lists inside a blockquote will depart from
+the document default (that is, it will display incrementally without the
+`-i` option and all at once with the `-i` option):
+
+```
+> - Eat spaghetti
+> - Drink wine
+```
+
+Both methods allow incremental and nonincremental lists to be mixed in a
+single document.
+
+If you want to include a block-quoted list, you can work around this
+behavior by putting the list inside a fenced div, so that it is not the
+direct child of the block quote:
+
+```
+> ::: wrapper
+> - a
+> - list in a quote
+> :::
+```
+
+{#inserting-pauses}
+## Inserting pauses
+
+You can add "pauses" within a slide by including a paragraph containing
+three dots, separated by spaces:
+
+```
+# Slide with a pause
+
+content before the pause
+
+. . .
+
+content after the pause
+```
+
+Note: this feature is not yet implemented for PowerPoint output.
+
+{#styling-the-slides}
+## Styling the slides
+
+You can change the style of HTML slides by putting customized CSS files
+in `$DATADIR/s5/default` (for S5), `$DATADIR/slidy` (for Slidy), or
+`$DATADIR/slideous` (for Slideous), where `$DATADIR` is the user data
+directory (see `--data-dir`, above). The originals may be found in
+pandoc’s system data directory (generally
+`$CABALDIR/pandoc-VERSION/s5/default`). Pandoc will look there for any
+files it does not find in the user data directory.
+
+For dzslides, the CSS is included in the HTML file itself, and may be
+modified there.
+
+All [reveal.js configuration options](https://revealjs.com/config/) can
+be set through variables. For example, themes can be used by setting the
+`theme` variable:
+
+```
+-V theme=moon
+```
+
+Or you can specify a custom stylesheet using the `--css` option.
+
+To style beamer slides, you can specify a `theme`, `colortheme`,
+`fonttheme`, `innertheme`, and `outertheme`, using the `-V` option:
+
+```
+pandoc -t beamer habits.txt -V theme:Warsaw -o habits.pdf
+```
+
+Note that heading attributes will turn into slide attributes (on a
+`<div>` or `<section>`) in HTML slide formats, allowing you to style
+individual slides. In beamer, a number of heading classes and attributes
+are recognized as frame options and will be passed through as options to
+the frame: see [Frame attributes in
+beamer](#frame-attributes-in-beamer), below.
+
+{#speaker-notes}
+## Speaker notes
+
+Speaker notes are supported in reveal.js, PowerPoint (pptx), and beamer
+output. You can add notes to your Markdown document thus:
+
+```
+::: notes
+
+This is my note.
+
+- It can contain Markdown
+- like this list
+
+:::
+```
+
+To show the notes window in reveal.js, press `s` while viewing the
+presentation. Speaker notes in PowerPoint will be available, as usual,
+in handouts and presenter view.
+
+Notes are not yet supported for other slide formats, but the notes will
+not appear on the slides themselves.
+
+{#columns}
+## Columns
+
+To put material in side by side columns, you can use a native div
+container with class `columns`, containing two or more div containers
+with class `column` and a `width` attribute:
+
+```
+:::::::::::::: {.columns}
+::: {.column width="40%"}
+contents...
+:::
+::: {.column width="60%"}
+contents...
+:::
+::::::::::::::
+```
+
+{#additional-columns-attributes-in-beamer}
+### Additional columns attributes in beamer
+
+The div containers with classes `columns` and `column` can optionally
+have an `align` attribute. The class `columns` can optionally have a
+`totalwidth` attribute or an `onlytextwidth` class.
+
+```
+:::::::::::::: {.columns align=center totalwidth=8em}
+::: {.column width="40%"}
+contents...
+:::
+::: {.column width="60%" align=bottom}
+contents...
+:::
+::::::::::::::
+```
+
+The `align` attributes on `columns` and `column` can be used with the
+values `top`, `top-baseline`, `center` and `bottom` to vertically align
+the columns. It defaults to `top` in `columns`.
+
+The `totalwidth` attribute limits the width of the columns to the given
+value.
+
+```
+:::::::::::::: {.columns align=top .onlytextwidth}
+::: {.column width="40%" align=center}
+contents...
+:::
+::: {.column width="60%"}
+contents...
+:::
+::::::::::::::
+```
+
+The class `onlytextwidth` sets the `totalwidth` to `\textwidth`.
+
+See Section 12.7 of the [Beamer User’s
+Guide](http://mirrors.ctan.org/macros/latex/contrib/beamer/doc/beameruserguide.pdf)
+for more details.
+
+{#frame-attributes-in-beamer}
+## Frame attributes in beamer
+
+Sometimes it is necessary to add the LaTeX `[fragile]` option to a frame
+in beamer (for example, when using the `minted` environment). This can
+be forced by adding the `fragile` class to the heading introducing the
+slide:
+
+```
+# Fragile slide {.fragile}
+```
+
+All of the other frame attributes described in Section 8.1 of the
+[Beamer User’s
+Guide](http://mirrors.ctan.org/macros/latex/contrib/beamer/doc/beameruserguide.pdf)
+may also be used: `allowdisplaybreaks`, `allowframebreaks`, `b`, `c`,
+`s`, `t`, `environment`, `label`, `plain`, `shrink`, `standout`,
+`noframenumbering`, `squeeze`. `allowframebreaks` is recommended
+especially for bibliographies, as it allows multiple slides to be
+created if the content overfills the frame:
+
+```
+# References {.allowframebreaks}
+```
+
+In addition, the `frameoptions` attribute may be used to pass arbitrary
+frame options to a beamer slide:
+
+```
+# Heading {frameoptions="squeeze,shrink,customoption=foobar"}
+```
+
+{#background-in-reveal.js-beamer-and-pptx}
+## Background in reveal.js, beamer, and pptx
+
+Background images can be added to self-contained reveal.js slide shows,
+beamer slide shows, and pptx slide shows.
+
+{#on-all-slides-beamer-reveal.js-pptx}
+### On all slides (beamer, reveal.js, pptx)
+
+With beamer and reveal.js, the configuration option `background-image`
+can be used either in the YAML metadata block or as a command-line
+variable to get the same image on every slide.
+
+Note that for reveal.js, the `background-image` will be used as a
+`parallaxBackgroundImage` (see below).
+
+For pptx, you can use a [reference doc](#option--reference-doc) in which
+background images have been set on the [relevant
+layouts](#powerpoint-layout-choice).
+
+{#parallaxbackgroundimage-reveal.js}
+#### `parallaxBackgroundImage` (reveal.js)
+
+For reveal.js, there is also the reveal.js-native option
+`parallaxBackgroundImage`, which produces a parallax scrolling
+background. You must also set `parallaxBackgroundSize`, and can
+optionally set `parallaxBackgroundHorizontal` and
+`parallaxBackgroundVertical` to configure the scrolling behaviour. See
+the [reveal.js
+documentation](https://revealjs.com/backgrounds/#parallax-background)
+for more details about the meaning of these options.
+
+In reveal.js’s overview mode, the parallaxBackgroundImage will show up
+only on the first slide.
+
+{#on-individual-slides-reveal.js-pptx}
+### On individual slides (reveal.js, pptx)
+
+To set an image for a particular reveal.js or pptx slide, add
+`{background-image="/path/to/image"}` to the first slide-level heading
+on the slide (which may even be empty).
+
+As the [HTML writers pass unknown attributes
+through](#extension-link_attributes), other reveal.js background
+settings also work on individual slides, including `background-size`,
+`background-repeat`, `background-color`, `transition`, and
+`transition-speed`. (The `data-` prefix will automatically be added.)
+
+Note: `data-background-image` is also supported in pptx for consistency
+with reveal.js – if `background-image` isn’t found,
+`data-background-image` will be checked.
+
+{#on-the-title-slide-reveal.js-pptx}
+### On the title slide (reveal.js, pptx)
+
+To add a background image to the automatically generated title slide for
+reveal.js, use the `title-slide-attributes` variable in the YAML
+metadata block. It must contain a map of attribute names and values.
+(Note that the `data-` prefix is required here, as it isn’t added
+automatically.)
+
+For pptx, pass a [reference doc](#option--reference-doc) with the
+background image set on the "Title Slide" layout.
+
+{#example-reveal.js}
+### Example (reveal.js)
+
+```
+---
+title: My Slide Show
+parallaxBackgroundImage: /path/to/my/background_image.png
+title-slide-attributes:
+    data-background-image: /path/to/title_image.png
+    data-background-size: contain
+---
+
+## Slide One
+
+Slide 1 has background_image.png as its background.
+
+## {background-image="/path/to/special_image.jpg"}
+
+Slide 2 has a special image for its background, even though the heading has no content.
+```
+
+{#epubs}
+# EPUBs
+
+{#epub-metadata}
+## EPUB Metadata
+
+EPUB metadata may be specified using the `--epub-metadata` option, but
+if the source document is Markdown, it is better to use a [YAML metadata
+block](#extension-yaml_metadata_block). Here is an example:
+
+```
+---
+title:
+- type: main
+  text: My Book
+- type: subtitle
+  text: An investigation of metadata
+creator:
+- role: author
+  text: John Smith
+- role: editor
+  text: Sarah Jones
+identifier:
+- scheme: DOI
+  text: doi:10.234234.234/33
+publisher:  My Press
+rights: © 2007 John Smith, CC BY-NC
+ibooks:
+  version: 1.3.4
+...
+```
+
+The following fields are recognized:
+
+: `identifier`
+
+  Either a string value or an object with fields `text` and `scheme`.
+  Valid values for `scheme` are `ISBN-10`, `GTIN-13`, `UPC`, `ISMN-10`,
+  `DOI`, `LCCN`, `GTIN-14`, `ISBN-13`, `Legal deposit number`, `URN`,
+  `OCLC`, `ISMN-13`, `ISBN-A`, `JP`, `OLCC`.
+: `title`
+
+  Either a string value, or an object with fields `file-as` and `type`,
+  or a list of such objects. Valid values for `type` are `main`,
+  `subtitle`, `short`, `collection`, `edition`, `extended`.
+: `creator`
+
+  Either a string value, or an object with fields `role`, `file-as`, and
+  `text`, or a list of such objects. Valid values for `role` are [MARC
+  relators](https://loc.gov/marc/relators/relaterm.html), but pandoc
+  will attempt to translate the human-readable versions (like "author"
+  and "editor") to the appropriate marc relators.
+: `contributor`
+
+  Same format as `creator`.
+: `date`
+
+  A string value in `YYYY-MM-DD` format. (Only the year is necessary.)
+  Pandoc will attempt to convert other common date formats.
+: `lang` (or legacy: `language`)
+
+  A string value in [BCP 47](https://tools.ietf.org/html/bcp47) format.
+  Pandoc will default to the local language if nothing is specified.
+: `subject`
+
+  Either a string value, or an object with fields `text`, `authority`,
+  and `term`, or a list of such objects. Valid values for `authority`
+  are either a [reserved authority
+  value](https://idpf.github.io/epub-registries/authorities/) (currently
+  `AAT`, `BIC`, `BISAC`, `CLC`, `DDC`, `CLIL`, `EuroVoc`, `MEDTOP`,
+  `LCSH`, `NDC`, `Thema`, `UDC`, and `WGS`) or an absolute IRI
+  identifying a custom scheme. Valid values for `term` are defined by
+  the scheme.
+: `description`
+
+  A string value.
+: `type`
+
+  A string value.
+: `format`
+
+  A string value.
+: `relation`
+
+  A string value.
+: `coverage`
+
+  A string value.
+: `rights`
+
+  A string value.
+: `belongs-to-collection`
+
+  A string value. Identifies the name of a collection to which the EPUB
+  Publication belongs.
+: `group-position`
+
+  The `group-position` field indicates the numeric position in which the
+  EPUB Publication belongs relative to other works belonging to the same
+  `belongs-to-collection` field.
+: `cover-image`
+
+  A string value (path to cover image).
+: `css` (or legacy: `stylesheet`)
+
+  A string value (path to CSS stylesheet).
+: `page-progression-direction`
+
+  Either `ltr` or `rtl`. Specifies the `page-progression-direction`
+  attribute for the [`spine`
+  element](http://idpf.org/epub/301/spec/epub-publications.html#sec-spine-elem).
+: `ibooks`
+
+  iBooks-specific metadata, with the following fields:
+
+  - `version`: (string)
+  - `specified-fonts`: `true`|`false` (default `false`)
+  - `ipad-orientation-lock`: `portrait-only`|`landscape-only`
+  - `iphone-orientation-lock`: `portrait-only`|`landscape-only`
+  - `binding`: `true`|`false` (default `true`)
+  - `scroll-axis`: `vertical`|`horizontal`|`default`
+
+{#the-epubtype-attribute}
+## The `epub:type` attribute
+
+For `epub3` output, you can mark up the heading that corresponds to an
+EPUB chapter using the [`epub:type`
+attribute](http://www.idpf.org/epub/31/spec/epub-contentdocs.html#sec-epub-type-attribute).
+For example, to set the attribute to the value `prologue`, use this
+markdown:
+
+```
+# My chapter {epub:type=prologue}
+```
+
+Which will result in:
+
+```
+<body epub:type="frontmatter">
+  <section epub:type="prologue">
+    <h1>My chapter</h1>
+```
+
+Pandoc will output `<body epub:type="bodymatter">`, unless you use one
+of the following values, in which case either `frontmatter` or
+`backmatter` will be output.
+
+|`epub:type` of first section|`epub:type` of body|
+|---|---|
+|prologue|frontmatter|
+|abstract|frontmatter|
+|acknowledgments|frontmatter|
+|copyright-page|frontmatter|
+|dedication|frontmatter|
+|credits|frontmatter|
+|keywords|frontmatter|
+|imprint|frontmatter|
+|contributors|frontmatter|
+|other-credits|frontmatter|
+|errata|frontmatter|
+|revision-history|frontmatter|
+|titlepage|frontmatter|
+|halftitlepage|frontmatter|
+|seriespage|frontmatter|
+|foreword|frontmatter|
+|preface|frontmatter|
+|frontispiece|frontmatter|
+|appendix|backmatter|
+|colophon|backmatter|
+|bibliography|backmatter|
+|index|backmatter|
+
+{#linked-media}
+## Linked media
+
+By default, pandoc will download media referenced from any `<img>`,
+`<audio>`, `<video>` or `<source>` element present in the generated
+EPUB, and include it in the EPUB container, yielding a completely
+self-contained EPUB. If you want to link to external media resources
+instead, use raw HTML in your source and add `data-external="1"` to the
+tag with the `src` attribute. For example:
+
+```
+<audio controls="1">
+  <source src="https://example.com/music/toccata.mp3"
+          data-external="1" type="audio/mpeg">
+  </source>
+</audio>
+```
+
+If the input format already is HTML then `data-external="1"` will work
+as expected for `<img>` elements. Similarly, for Markdown, external
+images can be declared with `![img](url){external=1}`. Note that this
+only works for images; the other media elements have no native
+representation in pandoc’s AST and require the use of raw HTML.
+
+{#epub-styling}
+## EPUB styling
+
+By default, pandoc will include some basic styling contained in its
+`epub.css` data file. (To see this, use
+`pandoc --print-default-data-file epub.css`.) To use a different CSS
+file, just use the `--css` command line option. A few inline styles are
+defined in addition; these are essential for correct formatting of
+pandoc’s HTML output.
+
+The `document-css` variable may be set if the more opinionated styling
+of pandoc’s default HTML templates is desired (and in that case the
+variables defined in [Variables for HTML](#variables-for-html) may be
+used to fine-tune the style).
+
+{#chunked-html}
+# Chunked HTML
+
+`pandoc -t chunkedhtml` will produce a zip archive of linked HTML files,
+one for each section of the original document. Internal links will
+automatically be adjusted to point to the right place, images linked to
+under the working directory will be incorporated, and navigation links
+will be added. In addition, a JSON file `sitemap.json` will be included
+describing the hierarchical structure of the files.
+
+If an output file without an extension is specified, then it will be
+interpreted as a directory and the zip archive will be automatically
+unpacked into it (unless it already exists, in which case an error will
+be raised). Otherwise a `.zip` file will be produced.
+
+The navigation links can be customized by adjusting the template. By
+default, a table of contents is included only on the top page. To
+include it on every page, set the `toc` variable manually.
+
+{#jupyter-notebooks}
+# Jupyter notebooks
+
+When creating a [Jupyter
+notebook](https://nbformat.readthedocs.io/en/latest/), pandoc will try
+to infer the notebook structure. Code blocks with the class `code` will
+be taken as code cells, and intervening content will be taken as
+Markdown cells. Attachments will automatically be created for images in
+Markdown cells. Metadata will be taken from the `jupyter` metadata
+field. For example:
+
+````
+---
+title: My notebook
+jupyter:
+  nbformat: 4
+  nbformat_minor: 5
+  kernelspec:
+     display_name: Python 2
+     language: python
+     name: python2
+  language_info:
+     codemirror_mode:
+       name: ipython
+       version: 2
+     file_extension: ".py"
+     mimetype: "text/x-python"
+     name: "python"
+     nbconvert_exporter: "python"
+     pygments_lexer: "ipython2"
+     version: "2.7.15"
+---
+
+# Lorem ipsum
+
+**Lorem ipsum** dolor sit amet, consectetur adipiscing elit. Nunc luctus
+bibendum felis dictum sodales.
+
+``` code
+print("hello")
+```
+
+## Pyout
+
+``` code
+from IPython.display import HTML
+HTML("""
+<script>
+console.log("hello");
+</script>
+<b>HTML</b>
+""")
+```
+
+## Image
+
+This image ![image](myimage.png) will be
+included as a cell attachment.
+````
+
+If you want to add cell attributes, group cells differently, or add
+output to code cells, then you need to include divs to indicate the
+structure. You can use either [fenced divs](#extension-fenced_divs) or
+[native divs](#extension-native_divs) for this. Here is an example:
+
+````
+:::::: {.cell .markdown}
+# Lorem
+
+**Lorem ipsum** dolor sit amet, consectetur adipiscing elit. Nunc luctus
+bibendum felis dictum sodales.
+::::::
+
+:::::: {.cell .code execution_count=1}
+``` {.python}
+print("hello")
+```
+
+::: {.output .stream .stdout}
+```
+hello
+```
+:::
+::::::
+
+:::::: {.cell .code execution_count=2}
+``` {.python}
+from IPython.display import HTML
+HTML("""
+<script>
+console.log("hello");
+</script>
+<b>HTML</b>
+""")
+```
+
+::: {.output .execute_result execution_count=2}
+```{=html}
+<script>
+console.log("hello");
+</script>
+<b>HTML</b>
+hello
+```
+:::
+::::::
+````
+
+If you include raw HTML or TeX in an output cell, use the [raw
+attribute](#extension-raw_attribute), as shown in the last cell of the
+example above. Although pandoc can process "bare" raw HTML and TeX, the
+result is often interspersed raw elements and normal textual elements,
+and in an output cell pandoc expects a single, connected raw block. To
+avoid using raw HTML or TeX except when marked explicitly using raw
+attributes, we recommend specifying the extensions
+`-raw_html-raw_tex+raw_attribute` when translating between Markdown and
+ipynb notebooks.
+
+Note that options and extensions that affect reading and writing of
+Markdown will also affect Markdown cells in ipynb notebooks. For
+example, `--wrap=preserve` will preserve soft line breaks in Markdown
+cells; `--markdown-headings=setext` will cause Setext-style headings to
+be used; and `--preserve-tabs` will prevent tabs from being turned to
+spaces.
+
+{#syntax-highlighting}
+# Syntax highlighting
+
+Pandoc will automatically highlight syntax in [fenced code
+blocks](#fenced-code-blocks) that are marked with a language name. The
+Haskell library [skylighting](https://github.com/jgm/skylighting) is
+used for highlighting. Currently highlighting is supported only for
+HTML, EPUB, Docx, Ms, and LaTeX/PDF output. To see a list of language
+names that pandoc will recognize, type
+`pandoc --list-highlight-languages`.
+
+The color scheme can be selected using the `--highlight-style` option.
+The default color scheme is `pygments`, which imitates the default color
+scheme used by the Python library pygments (though pygments is not
+actually used to do the highlighting). To see a list of highlight
+styles, type `pandoc --list-highlight-styles`.
+
+If you are not satisfied with the predefined styles, you can use
+`--print-highlight-style` to generate a JSON `.theme` file which can be
+modified and used as the argument to `--highlight-style`. To get a JSON
+version of the `pygments` style, for example:
+
+```
+pandoc --print-highlight-style pygments > my.theme
+```
+
+Then edit `my.theme` and use it like this:
+
+```
+pandoc --highlight-style my.theme
+```
+
+If you are not satisfied with the built-in highlighting, or you want to
+highlight a language that isn’t supported, you can use the
+`--syntax-definition` option to load a [KDE-style XML syntax definition
+file](https://docs.kde.org/stable5/en/kate/katepart/highlight.html).
+Before writing your own, have a look at KDE’s [repository of syntax
+definitions](https://github.com/KDE/syntax-highlighting/tree/master/data/syntax).
+
+To disable highlighting, use the `--no-highlight` option.
+
+{#custom-styles}
+# Custom Styles
+
+Custom styles can be used in the docx and ICML formats.
+
+{#output}
+## Output
+
+By default, pandoc’s docx and ICML output applies a predefined set of
+styles for blocks such as paragraphs and block quotes, and uses largely
+default formatting (italics, bold) for inlines. This will work for most
+purposes, especially alongside a `reference.docx` file. However, if you
+need to apply your own styles to blocks, or match a preexisting set of
+styles, pandoc allows you to define custom styles for blocks and text
+using `div`s and `span`s, respectively.
+
+If you define a `div` or `span` with the attribute `custom-style`,
+pandoc will apply your specified style to the contained elements (with
+the exception of elements whose function depends on a style, like
+headings, code blocks, block quotes, or links). So, for example, using
+the `bracketed_spans` syntax,
+
+```
+[Get out]{custom-style="Emphatically"}, he said.
+```
+
+would produce a docx file with "Get out" styled with character style
+`Emphatically`. Similarly, using the `fenced_divs` syntax,
+
+```
+Dickinson starts the poem simply:
+
+::: {custom-style="Poetry"}
+| A Bird came down the Walk---
+| He did not know I saw---
+:::
+```
+
+would style the two contained lines with the `Poetry` paragraph style.
+
+For docx output, styles will be defined in the output file as inheriting
+from normal text, if the styles are not yet in your reference.docx. If
+they are already defined, pandoc will not alter the definition.
+
+This feature allows for greatest customization in conjunction with
+[pandoc filters](https://pandoc.org/filters.html). If you want all
+paragraphs after block quotes to be indented, you can write a filter to
+apply the styles necessary. If you want all italics to be transformed to
+the `Emphasis` character style (perhaps to change their color), you can
+write a filter which will transform all italicized inlines to inlines
+within an `Emphasis` custom-style `span`.
+
+For docx output, you don’t need to enable any extensions for custom
+styles to work.
+
+{#input}
+## Input
+
+The docx reader, by default, only reads those styles that it can convert
+into pandoc elements, either by direct conversion or interpreting the
+derivation of the input document’s styles.
+
+By enabling the [`styles` extension](#ext-styles) in the docx reader
+(`-f docx+styles`), you can produce output that maintains the styles of
+the input document, using the `custom-style` class. Paragraph styles are
+interpreted as divs, while character styles are interpreted as spans.
+
+For example, using the `custom-style-reference.docx` file in the test
+directory, we have the following different outputs:
+
+Without the `+styles` extension:
+
+```
+$ pandoc test/docx/custom-style-reference.docx -f docx -t markdown
+This is some text.
+
+This is text with an *emphasized* text style. And this is text with a
+**strengthened** text style.
+
+> Here is a styled paragraph that inherits from Block Text.
+```
+
+And with the extension:
+
+```
+$ pandoc test/docx/custom-style-reference.docx -f docx+styles -t markdown
+
+::: {custom-style="First Paragraph"}
+This is some text.
+:::
+
+::: {custom-style="Body Text"}
+This is text with an [emphasized]{custom-style="Emphatic"} text style.
+And this is text with a [strengthened]{custom-style="Strengthened"}
+text style.
+:::
+
+::: {custom-style="My Block Style"}
+> Here is a styled paragraph that inherits from Block Text.
+:::
+```
+
+With these custom styles, you can use your input document as a
+reference-doc while creating docx output (see below), and maintain the
+same styles in your input and output files.
+
+{#custom-readers-and-writers}
+# Custom readers and writers
+
+Pandoc can be extended with custom readers and writers written in
+[Lua](https://www.lua.org). (Pandoc includes a Lua interpreter, so Lua
+need not be installed separately.)
+
+To use a custom reader or writer, simply specify the path to the Lua
+script in place of the input or output format. For example:
+
+```
+pandoc -t data/sample.lua
+pandoc -f my_custom_markup_language.lua -t latex -s
+```
+
+If the script is not found relative to the working directory, it will be
+sought in the `custom` subdirectory of the user data directory (see
+`--data-dir`).
+
+A custom reader is a Lua script that defines one function, Reader, which
+takes a string as input and returns a Pandoc AST. See the [Lua filters
+documentation](https://pandoc.org/lua-filters.html) for documentation of
+the functions that are available for creating pandoc AST elements. For
+parsing, the [lpeg](http://www.inf.puc-rio.br/~roberto/lpeg/) parsing
+library is available by default. To see a sample custom reader:
+
+```
+pandoc --print-default-data-file creole.lua
+```
+
+If you want your custom reader to have access to reader options
+(e.g. the tab stop setting), you give your Reader function a second
+`options` parameter.
+
+A custom writer is a Lua script that defines a function that specifies
+how to render each element in a Pandoc AST. See the
+[djot-writer.lua](https://github.com/jgm/djot.lua/blob/main/djot-writer.lua)
+for a full-featured example.
+
+Note that custom writers have no default template. If you want to use
+`--standalone` with a custom writer, you will need to specify a template
+manually using `--template` or add a new default template with the name
+`default.NAME_OF_CUSTOM_WRITER.lua` to the `templates` subdirectory of
+your user data directory (see [Templates](#templates)).
+
+{#reproducible-builds}
+# Reproducible builds
+
+Some of the document formats pandoc targets (such as EPUB, docx, and
+ODT) include build timestamps in the generated document. That means that
+the files generated on successive builds will differ, even if the source
+does not. To avoid this, set the `SOURCE_DATE_EPOCH` environment
+variable, and the timestamp will be taken from it instead of the current
+time. `SOURCE_DATE_EPOCH` should contain an integer unix timestamp
+(specifying the number of seconds since midnight UTC January 1, 1970).
+
+Some document formats also include a unique identifier. For EPUB, this
+can be set explicitly by setting the `identifier` metadata field (see
+[EPUB Metadata](#epub-metadata), above).
+
+{#accessible-pdfs-and-pdf-archiving-standards}
+# Accessible PDFs and PDF archiving standards
+
+PDF is a flexible format, and using PDF in certain contexts requires
+additional conventions. For example, PDFs are not accessible by default;
+they define how characters are placed on a page but do not contain
+semantic information on the content. However, it is possible to generate
+accessible PDFs, which use tagging to add semantic information to the
+document.
+
+Pandoc defaults to LaTeX to generate PDF. Tagging support in LaTeX is in
+development and not readily available, so PDFs generated in this way
+will always be untagged and not accessible. This means that alternative
+engines must be used to generate accessible PDFs.
+
+The PDF standards PDF/A and PDF/UA define further restrictions intended
+to optimize PDFs for archiving and accessibility. Tagging is commonly
+used in combination with these standards to ensure best results.
+
+Note, however, that standard compliance depends on many things,
+including the colorspace of embedded images. Pandoc cannot check this,
+and external programs must be used to ensure that generated PDFs are in
+compliance.
+
+{#context}
+## ConTeXt
+
+ConTeXt always produces tagged PDFs, but the quality depends on the
+input. The default ConTeXt markup generated by pandoc is optimized for
+readability and reuse, not tagging. Enable the
+[`tagging`](#extension--tagging) format extension to force markup that
+is optimized for tagging. This can be combined with the `pdfa` variable
+to generate standard-compliant PDFs. E.g.:
+
+```
+pandoc --to=context+tagging -V pdfa=3a
+```
+
+A recent `context` version should be used, as older versions contained a
+bug that lead to invalid PDF metadata.
+
+{#weasyprint}
+## WeasyPrint
+
+The HTML-based engine WeasyPrint includes experimental support for PDF/A
+and PDF/UA since version 57. Tagged PDFs can created with
+
+```
+pandoc --pdf-engine=weasyprint \
+       --pdf-engine-opt=--pdf-variant=pdf/ua-1 ...
+```
+
+The feature is experimental and standard compliance should not be
+assumed.
+
+{#prince-xml}
+## Prince XML
+
+The non-free HTML-to-PDf converter `prince` has extensive support for
+various PDF standards as well as tagging. E.g.:
+
+```
+pandoc --pdf-engine=prince \
+       --pdf-engine-opt=--tagged-pdf ...
+```
+
+See the prince documentation for more info.
+
+{#word-processors}
+## Word Processors
+
+Word processors like LibreOffice and MS Word can also be used to
+generate standardized and tagged PDF output. Pandoc does not support
+direct conversions via these tools. However, pandoc can convert a
+document to a `docx` or `odt` file, which can then be opened and
+converted to PDF with the respective word processor. See the
+documentation for
+[Word](https://support.microsoft.com/en-us/office/create-accessible-pdfs-064625e0-56ea-4e16-ad71-3aa33bb4b7ed)
+and
+[LibreOffice](https://help.libreoffice.org/7.1/en-US/text/shared/01/ref_pdf_export_general.html).
+
+{#running-pandoc-as-a-web-server}
+# Running pandoc as a web server
+
+If you rename (or symlink) the pandoc executable to `pandoc-server`, or
+if you call pandoc with `server` as the first argument, it will start up
+a web server with a JSON API. This server exposes most of the conversion
+functionality of pandoc. For full documentation, see the
+[pandoc-server](https://github.com/jgm/pandoc/blob/master/doc/pandoc-server.md)
+man page.
+
+If you rename (or symlink) the pandoc executable to `pandoc-server.cgi`,
+it will function as a CGI program exposing the same API as
+`pandoc-server`.
+
+`pandoc-server` is designed to be maximally secure; it uses Haskell’s
+type system to provide strong guarantees that no I/O will be performed
+on the server during pandoc conversions.
+
+{#running-pandoc-as-a-lua-interpreter}
+# Running pandoc as a Lua interpreter
+
+Calling the pandoc executable under the name `pandoc-lua` or with `lua`
+as the first argument will make it function as a standalone Lua
+interpreter. The behavior is mostly identical to that of the [standalone
+`lua` executable](https://www.lua.org/manual/5.4/manual.html#7), version
+5.4. However, there is no REPL yet, and the `-i` option has no effect.
+For full documentation, see the
+[pandoc-lua](https://github.com/jgm/pandoc/blob/master/doc/pandoc-lua.md)
+man page.
+
+{#a-note-on-security}
+# A note on security
+
+1. Although pandoc itself will not create or modify any files other than
+   those you explicitly ask it create (with the exception of temporary
+   files used in producing PDFs), a filter or custom writer could in
+   principle do anything on your file system. Please audit filters and
+   custom writers very carefully before using them.
+
+2. Several input formats (including HTML, Org, and RST) support
+   `include` directives that allow the contents of a file to be included
+   in the output. An untrusted attacker could use these to view the
+   contents of files on the file system. (Using the `--sandbox` option
+   can protect against this threat.)
+
+3. Several output formats (including RTF, FB2, HTML with
+   `--self-contained`, EPUB, Docx, and ODT) will embed encoded or raw
+   images into the output file. An untrusted attacker could exploit this
+   to view the contents of non-image files on the file system. (Using
+   the `--sandbox` option can protect against this threat, but will also
+   prevent including images in these formats.)
+
+4. If your application uses pandoc as a Haskell library (rather than
+   shelling out to the executable), it is possible to use it in a mode
+   that fully isolates pandoc from your file system, by running the
+   pandoc operations in the `PandocPure` monad. See the document [Using
+   the pandoc API](https://pandoc.org/using-the-pandoc-api.html) for
+   more details. (This corresponds to the use of the `--sandbox` option
+   on the command line.)
+
+5. Pandoc’s parsers can exhibit pathological performance on some corner
+   cases. It is wise to put any pandoc operations under a timeout, to
+   avoid DOS attacks that exploit these issues. If you are using the
+   pandoc executable, you can add the command line options
+   `+RTS -M512M -RTS` (for example) to limit the heap size to 512MB.
+   Note that the `commonmark` parser (including `commonmark_x` and
+   `gfm`) is much less vulnerable to pathological performance than the
+   `markdown` parser, so it is a better choice when processing untrusted
+   input.
+
+6. The HTML generated by pandoc is not guaranteed to be safe. If
+   `raw_html` is enabled for the Markdown input, users can inject
+   arbitrary HTML. Even if `raw_html` is disabled, users can include
+   dangerous content in URLs and attributes. To be safe, you should run
+   all HTML generated from untrusted user input through an HTML
+   sanitizer.
+
+{#authors}
+# Authors
+
+Copyright 2006–2022 John MacFarlane (jgm@berkeley.edu). Released under
+the
+[GPL](https://www.gnu.org/copyleft/gpl.html){title="GNU General Public License"},
+version 2 or greater. This software carries no warranty of any kind.
+(See COPYRIGHT for full copyright and warranty notices.) For a full list
+of contributors, see the file AUTHORS.md in the pandoc source code.
+
+[^1]: The point of this rule is to ensure that normal paragraphs
+  starting with people’s initials, like
+
+  ```
+  B. Russell won a Nobel Prize (but not for "On Denoting").
+  ```
+
+  do not get treated as list items.
+
+  This rule will not prevent
+
+  ```
+  (C) 2007 Joe Smith
+  ```
+
+  from being interpreted as a list item. In this case, a backslash
+  escape can be used:
+
+  ```
+  (C\) 2007 Joe Smith
+  ```
+
+[^2]: I have been influenced by the suggestions of [David
+  Wheeler](https://justatheory.com/2009/02/modest-markdown-proposal/).
+
+[^3]: This scheme is due to Michel Fortin, who proposed it on the
+  [Markdown discussion
+  list](http://six.pairlist.net/pipermail/markdown-discuss/2005-March/001097.html).
+
+[^4]: To see why laziness is incompatible with relaxing the requirement
+  of a blank line between items, consider the following example:
+
+  ```
+  bar
+  :    definition
+  foo
+  :    definition
+  ```
+
+  Is this a single list item with two definitions of "bar," the first of
+  which is lazily wrapped, or two list items? To remove the ambiguity we
+  must either disallow lazy wrapping or require a blank line between
+  list items.
diff --git a/djot.cabal b/djot.cabal
new file mode 100644
--- /dev/null
+++ b/djot.cabal
@@ -0,0 +1,88 @@
+cabal-version:      3.0
+name:               djot
+version:            0.1.0.0
+synopsis:           Parser and renderer for djot light markup syntax.
+description:        Djot (<https://djot.net>) is a light markup language.
+                    This package provides a data structure to represent
+                    djot documents, a very fast parser, and functions
+                    to render a parsed document as HTML and as djot.
+license:            MIT
+license-file:       LICENSE
+author:             John MacFarlane
+maintainer:         jgm@berkeley.edu
+copyright:          Copyright (C) 2024 John MacFarlane
+category:           Text
+build-type:         Simple
+extra-doc-files:    CHANGELOG.md
+extra-source-files: test/*.test
+                    benchmark/m.dj
+
+Library
+    build-depends:    base >= 4.12 && < 5,
+                      bytestring >= 0.11.3,
+                      containers,
+                      mtl,
+                      text,
+                      doclayout
+    hs-source-dirs:   src
+    default-language: Haskell2010
+    exposed-modules:  Djot
+                      Djot.AST
+                      Djot.Parse
+                      Djot.Options
+                      Djot.Attributes
+                      Djot.Inlines
+                      Djot.Blocks
+                      Djot.Html
+                      Djot.Djot
+    ghc-options: -Wall -O2
+
+executable djoths
+    main-is:          Main.hs
+    build-depends:    base >= 4.12 && < 5,
+                      djot,
+                      bytestring,
+                      containers,
+                      text,
+                      doclayout
+    hs-source-dirs:   app
+    default-language: Haskell2010
+    ghc-options: -Wall -O2 -rtsopts -threaded
+
+test-suite test-djot
+  type: exitcode-stdio-1.0
+  main-is: Main.hs
+  hs-source-dirs: test
+  ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-K40K -with-rtsopts=-kc40K
+  if impl(ghc >= 8.10)
+    ghc-options:      -Wunused-packages
+  build-depends:
+      base >= 4.12 && <5
+    , djot
+    , text
+    , bytestring
+    , directory
+    , filepath
+    , doclayout
+    , tasty
+    , tasty-hunit
+    , tasty-quickcheck
+  default-language: Haskell2010
+
+benchmark benchmark-djot
+  type:            exitcode-stdio-1.0
+  main-is:         Main.hs
+  hs-source-dirs:  benchmark
+  build-depends:
+       djot
+     , base >= 4.9 && < 5
+     , bytestring
+     , directory
+     , filepath
+     , doclayout
+     , tasty-bench
+  ghc-options: -O2 -threaded -rtsopts -with-rtsopts=-K10K -with-rtsopts=-kc10K
+  if impl(ghc >= 8.10)
+    ghc-options:      -Wunused-packages
+  default-language: Haskell2010
+
diff --git a/src/Djot.hs b/src/Djot.hs
new file mode 100644
--- /dev/null
+++ b/src/Djot.hs
@@ -0,0 +1,17 @@
+module Djot
+  ( parseDoc
+  , renderHtml
+  , renderDjot
+  , toIdentifier
+  , ParseOptions(..)
+  , SourcePosOption(..)
+  , RenderOptions(..)
+  , module Djot.AST
+  )
+where
+
+import Djot.Options (ParseOptions(..), RenderOptions(..), SourcePosOption(..))
+import Djot.Blocks (parseDoc, toIdentifier)
+import Djot.Html (renderHtml)
+import Djot.Djot (renderDjot)
+import Djot.AST
diff --git a/src/Djot/AST.hs b/src/Djot/AST.hs
new file mode 100644
--- /dev/null
+++ b/src/Djot/AST.hs
@@ -0,0 +1,445 @@
+{-# LANGUAGE Strict #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE DeriveTraversable #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE DeriveGeneric #-}
+module Djot.AST
+( Inline(..),
+  Many(..),
+  Inlines,
+  MathStyle(..),
+  Format(..),
+  Node(Node),
+  Pos(..),
+  addAttr,
+  addPos,
+  Block(..),
+  Blocks,
+  Doc(..),
+  NoteMap(..),
+  insertNote,
+  lookupNote,
+  ReferenceMap(..),
+  insertReference,
+  lookupReference,
+  normalizeLabel,
+  Attr(..),
+  Target(..),
+  TaskStatus(..),
+  Align(..),
+  Cell(..),
+  CellType(..),
+  Caption(..),
+  ListSpacing(..),
+  OrderedListAttributes(..),
+  OrderedListDelim(..),
+  OrderedListStyle(..),
+  QuoteType(..),
+  delete,
+  displayMath,
+  insert,
+  emailLink,
+  emph,
+  footnoteReference,
+  hardBreak,
+  highlight,
+  image,
+  inlineMath,
+  link,
+  nonBreakingSpace,
+  rawInline,
+  softBreak,
+  span_,
+  str,
+  strong,
+  subscript,
+  superscript,
+  singleQuoted,
+  doubleQuoted,
+  symbol,
+  verbatim,
+  urlLink,
+  para,
+  section,
+  heading,
+  blockQuote,
+  codeBlock,
+  div,
+  bulletList,
+  orderedList,
+  definitionList,
+  taskList,
+  thematicBreak,
+  table,
+  rawBlock,
+  inlinesToByteString
+  )
+where
+
+import Prelude hiding (div)
+import Data.ByteString (ByteString)
+import Data.Sequence (Seq)
+import qualified Data.Sequence as Seq
+import qualified Data.Map.Strict as M
+import Data.Set (Set)
+import Data.Data (Data, Typeable)
+import qualified Data.ByteString.Char8 as B8
+import GHC.Generics (Generic)
+
+-- import Debug.Trace
+
+newtype Attr = Attr [(ByteString, ByteString)]
+  deriving (Show, Eq, Ord, Typeable, Generic, Data)
+
+instance Semigroup Attr where
+  Attr as <> Attr bs =
+    Attr $ foldr integrate bs as
+
+instance Monoid Attr where
+  mappend = (<>)
+  mempty = Attr mempty
+
+integrate :: (ByteString, ByteString)
+          -> [(ByteString, ByteString)] -> [(ByteString, ByteString)]
+integrate (k,v) kvs =
+  case lookup k kvs of
+    Nothing -> (k,v) : kvs
+    Just v'
+      | k == "class" ->
+        (k, v <> " " <> v') : filter (\(k',_) -> k' /= "class") kvs
+      | otherwise -> kvs
+
+data Pos = NoPos | Pos Int Int Int Int -- start line, start col, end line, end col
+  deriving (Show, Eq, Ord, Typeable, Generic)
+
+instance Semigroup Pos where
+  Pos sl1 sc1 _ _ <> Pos _ _ el2 ec2 =
+    Pos sl1 sc1 el2 ec2
+  NoPos <> _ = NoPos
+  _ <> NoPos = NoPos
+
+instance Monoid Pos where
+  mappend = (<>)
+  mempty = NoPos
+
+data Node a = Node Pos Attr a
+  deriving (Show, Eq, Ord, Functor, Traversable, Foldable, Typeable, Generic)
+
+{-# INLINE addAttr #-}
+addAttr :: Attr -> Node a -> Node a
+addAttr attr (Node pos attr' bs) = Node pos (attr' <> attr) bs
+
+{-# INLINE addPos #-}
+addPos :: Pos -> Node a -> Node a
+addPos pos (Node _ attr bs) = Node pos attr bs
+
+newtype Format = Format { unFormat :: ByteString }
+  deriving (Show, Eq, Ord, Typeable, Generic)
+
+data MathStyle = DisplayMath | InlineMath
+  deriving (Show, Ord, Eq, Typeable, Generic)
+
+data Target =
+    Direct ByteString
+  | Reference ByteString
+  deriving (Show, Ord, Eq, Typeable, Generic)
+
+data QuoteType = SingleQuotes | DoubleQuotes
+  deriving (Show, Ord, Eq, Typeable, Generic)
+
+data Inline =
+      Str ByteString
+    | Emph Inlines
+    | Strong Inlines
+    | Highlight Inlines
+    | Insert Inlines
+    | Delete Inlines
+    | Superscript Inlines
+    | Subscript Inlines
+    | Verbatim ByteString
+    | Symbol ByteString
+    | Math MathStyle ByteString
+    | Link Inlines Target
+    | Image Inlines Target
+    | Span Inlines
+    | FootnoteReference ByteString
+    | UrlLink ByteString
+    | EmailLink ByteString
+    | RawInline Format ByteString
+    | NonBreakingSpace
+    | Quoted QuoteType Inlines
+    | SoftBreak
+    | HardBreak
+    deriving (Show, Ord, Eq, Typeable, Generic)
+
+newtype Many a = Many { unMany :: Seq a }
+  deriving (Show, Ord, Eq, Functor, Traversable, Foldable, Typeable, Generic)
+
+type Inlines = Many (Node Inline)
+
+instance Semigroup Inlines where
+  Many as <> Many bs =
+    case (Seq.viewr as, Seq.viewl bs) of
+      (as' Seq.:> Node pos1 attr (Str s), Node pos2 attr' (Str t) Seq.:< bs')
+        | attr == mempty && attr' /= mempty
+        , (sa, sb) <- B8.spanEnd (not . isSpaceOrTab) s
+        , not (B8.null sb)
+          -> if B8.null sa
+                then
+                  Many (as' <> (Node (pos1 <> pos2) attr' (Str (s <> t)) Seq.<| bs'))
+                else
+                  let sblen = B8.length (B8.filter (\c -> c < '\128' || c >= '\192') sb)
+                      (pos1', pos2') =
+                        case pos1 <> pos2 of
+                            NoPos -> (NoPos, NoPos)
+                            Pos sl sc el ec ->
+                              (Pos sl sc el (ec - sblen),
+                               Pos sl (sc + sblen + 1) el ec)
+                  in  Many ((as' Seq.|> Node pos1' mempty (Str sa)
+                        Seq.|> Node pos2' attr (Str (sb <> t))) <> bs')
+        | attr == attr'
+          -> Many (as' <> (Node (pos1 <> pos2) attr (Str (s <> t)) Seq.<| bs'))
+      (as' Seq.:> Node pos attr (Str s), Node _ _ HardBreak Seq.:< _)
+        | B8.all isSpaceOrTab (B8.takeEnd 1 s)
+          -> Many (as' <> (Node pos attr (Str (B8.dropWhileEnd isSpaceOrTab s))
+                            Seq.<| bs))
+      _ -> Many (as <> bs)
+    where
+      isSpaceOrTab ' ' = True
+      isSpaceOrTab '\t' = True
+      isSpaceOrTab _ = False
+
+instance Monoid Inlines where
+  mappend = (<>)
+  mempty = Many mempty
+
+data ListSpacing = Tight | Loose
+  deriving (Show, Ord, Eq, Typeable, Generic)
+
+data OrderedListStyle =
+  Decimal | LetterUpper | LetterLower | RomanUpper | RomanLower
+  deriving (Show, Ord, Eq, Typeable, Generic)
+
+data OrderedListDelim =
+  RightPeriod | RightParen | LeftRightParen
+  deriving (Show, Ord, Eq, Typeable, Generic)
+
+data OrderedListAttributes =
+  OrderedListAttributes
+  { orderedListStyle :: OrderedListStyle
+  , orderedListDelim :: OrderedListDelim
+  , orderedListStart :: Int }
+  deriving (Show, Ord, Eq, Typeable, Generic)
+
+data TaskStatus = Complete | Incomplete
+  deriving (Show, Ord, Eq, Typeable, Generic)
+
+newtype Caption = Caption Blocks
+  deriving (Show, Ord, Eq, Typeable, Generic)
+
+data Align = AlignLeft | AlignRight | AlignCenter | AlignDefault
+  deriving (Show, Ord, Eq, Typeable, Generic)
+
+data CellType = HeadCell | BodyCell
+  deriving (Show, Ord, Eq, Typeable, Generic)
+
+data Cell = Cell CellType Align Inlines
+  deriving (Show, Ord, Eq, Typeable, Generic)
+
+data Block =
+    Para Inlines
+  | Section Blocks
+  | Heading Int Inlines
+  | BlockQuote Blocks
+  | CodeBlock ByteString ByteString
+  | Div Blocks
+  | OrderedList OrderedListAttributes ListSpacing [Blocks]
+  | BulletList ListSpacing [Blocks]
+  | TaskList ListSpacing [(TaskStatus, Blocks)]
+  | DefinitionList ListSpacing [(Inlines, Blocks)]
+  | ThematicBreak
+  | Table (Maybe Caption) [[Cell]]
+  | RawBlock Format ByteString
+  deriving (Show, Ord, Eq, Typeable, Generic)
+
+type Blocks = Many (Node Block)
+
+instance Semigroup Blocks where
+  Many as <> Many bs = Many (as <> bs)
+
+instance Monoid Blocks where
+  mappend = (<>)
+  mempty = Many mempty
+
+data Doc =
+  Doc{ docBlocks :: Blocks
+     , docFootnotes :: NoteMap
+     , docReferences :: ReferenceMap
+     , docAutoReferences :: ReferenceMap
+     , docAutoIdentifiers :: Set ByteString
+     } deriving (Show, Ord, Eq, Typeable, Generic)
+
+instance Semigroup Doc where
+  Doc bs ns rs ar ai <> Doc bs' ns' rs' ar' ai' =
+    Doc (bs <> bs') (ns <> ns') (rs <> rs') (ar <> ar') (ai <> ai')
+
+instance Monoid Doc where
+  mappend = (<>)
+  mempty = Doc mempty mempty mempty mempty mempty
+
+-- | A map from labels to contents.
+newtype NoteMap = NoteMap { unNoteMap :: M.Map ByteString Blocks }
+  deriving (Show, Ord, Eq, Semigroup, Monoid, Typeable, Generic)
+
+insertNote :: ByteString -> Blocks -> NoteMap -> NoteMap
+insertNote label ref (NoteMap m) =
+  NoteMap (M.insert (normalizeLabel label) ref m)
+
+lookupNote :: ByteString -> NoteMap -> Maybe Blocks
+lookupNote label (NoteMap m) =
+  M.lookup (normalizeLabel label) m
+
+newtype ReferenceMap =
+  ReferenceMap { unReferenceMap :: M.Map ByteString (ByteString, Attr) }
+  deriving (Show, Ord, Eq, Semigroup, Monoid, Typeable, Generic)
+
+normalizeLabel :: ByteString -> ByteString
+normalizeLabel = B8.unwords . B8.words
+
+insertReference :: ByteString -> (ByteString, Attr) -> ReferenceMap
+                -> ReferenceMap
+insertReference label ref (ReferenceMap rm) =
+  ReferenceMap (M.insert (normalizeLabel label) ref rm)
+
+lookupReference :: ByteString -> ReferenceMap -> Maybe (ByteString, Attr)
+lookupReference label (ReferenceMap rm) =
+  M.lookup (normalizeLabel label) rm
+
+{-# INLINE inline #-}
+inline :: Inline -> Inlines
+inline = Many . Seq.singleton . Node NoPos mempty
+
+str, verbatim, symbol :: ByteString -> Inlines
+str = inline . Str
+verbatim = inline . Verbatim
+symbol = inline . Symbol
+
+emph, strong, superscript, subscript :: Inlines -> Inlines
+emph = inline . Emph
+strong = inline . Strong
+superscript = inline . Superscript
+subscript = inline . Subscript
+
+highlight, insert, delete :: Inlines -> Inlines
+highlight = inline . Highlight
+insert = inline . Insert
+delete = inline . Delete
+
+link, image :: Inlines -> Target -> Inlines
+link ils url = inline $ Link ils url
+image ils url = inline $ Image ils url
+
+span_ :: Inlines -> Inlines
+span_ = inline . Span
+
+softBreak, hardBreak, nonBreakingSpace :: Inlines
+softBreak = inline SoftBreak
+hardBreak = inline HardBreak
+nonBreakingSpace = inline NonBreakingSpace
+
+inlineMath, displayMath :: ByteString -> Inlines
+inlineMath = inline . Math InlineMath
+displayMath = inline . Math DisplayMath
+
+singleQuoted, doubleQuoted :: Inlines -> Inlines
+singleQuoted = inline . Quoted SingleQuotes
+doubleQuoted = inline . Quoted DoubleQuotes
+
+footnoteReference :: ByteString -> Inlines
+footnoteReference = inline . FootnoteReference
+
+urlLink, emailLink :: ByteString -> Inlines
+urlLink = inline . UrlLink
+emailLink = inline . EmailLink
+
+rawInline :: Format -> ByteString -> Inlines
+rawInline f = inline . RawInline f
+
+
+--
+
+block :: Block -> Blocks
+block = Many . Seq.singleton . Node NoPos mempty
+
+para :: Inlines -> Blocks
+para = block . Para
+
+section :: Blocks -> Blocks
+section = block . Section
+
+heading :: Int -> Inlines -> Blocks
+heading lev = block . Heading lev
+
+blockQuote :: Blocks -> Blocks
+blockQuote = block . BlockQuote
+
+codeBlock :: ByteString -> ByteString -> Blocks
+codeBlock lang bs = block $ CodeBlock lang bs
+
+bulletList :: ListSpacing -> [Blocks] -> Blocks
+bulletList tightness = block . BulletList tightness
+
+orderedList :: OrderedListAttributes -> ListSpacing -> [Blocks] -> Blocks
+orderedList attr tightness = block . OrderedList attr tightness
+
+definitionList :: ListSpacing -> [(Inlines, Blocks)] -> Blocks
+definitionList tightness = block . DefinitionList tightness
+
+taskList :: ListSpacing -> [(TaskStatus, Blocks)] -> Blocks
+taskList tightness = block . TaskList tightness
+
+div :: Blocks -> Blocks
+div = block . Div
+
+thematicBreak :: Blocks
+thematicBreak = block ThematicBreak
+
+table :: Maybe Caption -> [[Cell]] -> Blocks
+table mbCaption = block . Table mbCaption
+
+rawBlock :: Format -> ByteString -> Blocks
+rawBlock f = block . RawBlock f
+
+inlinesToByteString :: Inlines -> ByteString
+inlinesToByteString = foldMap go . unMany
+ where
+  go (Node _pos _attr x) =
+      case x of
+        Str bs -> bs
+        Emph ils -> inlinesToByteString ils
+        Strong ils -> inlinesToByteString ils
+        Highlight ils -> inlinesToByteString ils
+        Insert ils -> inlinesToByteString ils
+        Delete ils -> inlinesToByteString ils
+        Superscript ils -> inlinesToByteString ils
+        Subscript ils -> inlinesToByteString ils
+        Quoted SingleQuotes ils ->
+          "\x2018" <> inlinesToByteString ils <> "\x2019"
+        Quoted DoubleQuotes ils ->
+          "\x201C" <> inlinesToByteString ils <> "\x201D"
+        Verbatim bs -> bs
+        Math DisplayMath bs -> "$$" <> bs <> "$$"
+        Math InlineMath bs -> "$" <> bs <> "$"
+        Symbol bs -> ":" <> bs <> ":"
+        Link ils _url -> inlinesToByteString ils
+        Image ils _url -> inlinesToByteString ils
+        Span ils -> inlinesToByteString ils
+        UrlLink url -> url
+        EmailLink email -> email
+        RawInline _ _ -> mempty
+        FootnoteReference bs -> "[" <> bs <> "]"
+        SoftBreak -> "\n"
+        HardBreak -> "\n"
+        NonBreakingSpace -> "\160"
diff --git a/src/Djot/Attributes.hs b/src/Djot/Attributes.hs
new file mode 100644
--- /dev/null
+++ b/src/Djot/Attributes.hs
@@ -0,0 +1,187 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE Strict #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+module Djot.Attributes
+  ( pAttributes
+  , parseAttributes
+  , AttrParserState  -- opaque
+  , AttrParseResult(..)
+  )
+where
+import Data.Char (isAlphaNum, isSpace, isPunctuation)
+import Djot.AST (Attr(..))
+import Djot.Parse
+import Data.ByteString (ByteString)
+import qualified Data.ByteString.Char8 as B8
+import Data.ByteString.Char8 ( (!?) )
+import Data.Typeable (Typeable)
+import Data.Maybe (fromMaybe)
+-- import Debug.Trace
+
+
+--  attributes { id = "foo", class = "bar baz",
+--               key1 = "val1", key2 = "val2" }
+--  syntax:
+--
+--  attributes <- '{' (ignorable attribute)* ignorable* '}'
+--  attribute <- identifier | class | keyval
+--  identifier <- '#' name
+--  class <- '.' name
+--  name <- (nonspace, nonpunctuation other than ':', '_', '-')+
+--  keyval <- key '=' val
+--  key <- (ASCII_ALPHANUM | ':' | '_' | '-')+
+--  val <- bareval | quotedval
+--  bareval <- (ASCII_ALPHANUM | ':' | '_' | '-')+
+--  quotedval <- '"' ([^"] | '\"') '"'
+--  ignorable <- whitespace | comment
+--  comment <- '%' [^%}]* '%'
+
+pAttributes :: Parser s Attr
+pAttributes = lookahead (asciiChar '{') >> getSlice >>= go Nothing
+ where
+   getSlice = byteStringOf $
+               branch
+                (skipSome (skipSatisfyByte (/= '}')))
+                (optional_ (asciiChar '}'))
+                (asciiChar '}')
+   go mbst bs = do
+     case parseAttributes mbst bs of
+       Done (attr, _off) -> pure attr
+       Partial st -> getSlice >>= go (Just st)
+       Failed _off -> failed
+
+data AttrParseResult =
+    Done (Attr, Int) -- result and byte offset
+  | Failed Int -- byte offset of failure
+  | Partial AttrParserState -- entire bytestring consumed
+  deriving (Show, Typeable)
+
+data AttrParserState =
+  AttrParserState
+  { aState :: AState
+  , subject :: ByteString
+  , offset :: Int
+  , parts :: [AttrPart] }
+  deriving (Show, Typeable)
+
+data AState =
+    SCANNING
+  | AFTER_KEY
+  | SCANNING_VALUE
+  | SCANNING_QUOTED_VALUE
+  | SCANNING_ESCAPE
+  | SCANNING_COMMENT
+  | FAIL
+  | DONE
+  | START
+  deriving (Eq, Ord, Show, Typeable)
+
+data AttrPart =
+    AttrId ByteString
+  | AttrClass ByteString
+  | AttrKey ByteString
+  | AttrValue ByteString
+  deriving (Eq, Ord, Show, Typeable)
+
+-- | Resumable parser, returning parts in reverse order.
+parseAttributes :: Maybe AttrParserState -> ByteString -> AttrParseResult
+parseAttributes mbState bs =
+  case go (fromMaybe AttrParserState{ aState = START
+                                    , subject = bs
+                                    , offset = 0
+                                    , parts = [] } mbState) of
+    AttrParserState{ aState = DONE, parts = attparts, offset = off } ->
+      Done (attrPartsToAttr attparts, off)
+    AttrParserState{ aState = FAIL, offset = off } -> Failed off
+    st -> Partial st
+ where
+  go :: AttrParserState -> AttrParserState
+  go st@(AttrParserState _ subj off _) = -- trace (show st) $
+     case subj !? off of
+       Nothing -> st
+       Just nextc ->
+        case aState st of
+         SCANNING ->
+           case nextc of
+             '}' -> go st{ aState = DONE, offset = off + 1 }
+             '%' -> go st{ aState = SCANNING_COMMENT, offset = off + 1 }
+             '#' -> go $ takePart isNameChar AttrId SCANNING st{ offset = off + 1 }
+             '.' -> go $ takePart isNameChar AttrClass SCANNING st{ offset = off + 1 }
+             c | isKeyChar c -> go $ takePart isKeyChar AttrKey AFTER_KEY st
+             c | isWs c -> go $ skipWhile isWs st
+             _ -> st{ aState = FAIL }
+         AFTER_KEY ->
+           case nextc of
+             '=' -> go st{ aState = SCANNING_VALUE, offset = off + 1 }
+             _ -> st{ aState = FAIL }
+         SCANNING_VALUE ->
+           case nextc of
+             '"' -> go st{ aState = SCANNING_QUOTED_VALUE, offset = off + 1 }
+             c | isBareValChar c -> go $ takePart isBareValChar AttrValue SCANNING st
+             _ -> st{ aState = FAIL }
+         SCANNING_QUOTED_VALUE ->
+           case nextc of
+             '"' -> go st{ aState = SCANNING, offset = off + 1 }
+             '\\' -> go st{ aState = SCANNING_ESCAPE, offset = off + 1 }
+             c | isWs c ->
+                 let st' = skipWhile isWs st
+                   in go st'{ parts = AttrValue " " : parts st' }
+             _ -> go $ takePart (\c -> not (isWs c || c == '"' || c == '\\'))
+                        AttrValue SCANNING_QUOTED_VALUE st
+         SCANNING_ESCAPE ->
+           go st{ aState = SCANNING_QUOTED_VALUE, offset = off + 1,
+                  parts = AttrValue (B8.singleton nextc) : parts st }
+         SCANNING_COMMENT ->
+           case nextc of
+             '%' -> go st{ aState = SCANNING, offset = off + 1 }
+             '}' -> st{ aState = DONE, offset = off + 1 }
+             _ -> go $ skipWhile (\c -> not (c == '%' || c == '}')) st
+         FAIL -> st
+         DONE -> st
+         START ->
+           case nextc of
+             '{' -> go st{ aState = SCANNING, offset = off + 1 }
+             _ -> st{ aState = FAIL }
+
+takePart :: (Char -> Bool) -> (ByteString -> AttrPart) -> AState ->
+            AttrParserState -> AttrParserState
+takePart charP partConstructor nextstate st =
+  case subject st !? offset st of
+    Just c | charP c ->
+      let val = B8.takeWhile charP (B8.drop (offset st) (subject st))
+      in  st{ aState = nextstate,
+              offset = offset st + B8.length val,
+              parts = partConstructor val : parts st }
+    _ -> st{ aState = FAIL }
+
+skipWhile :: (Char -> Bool) -> AttrParserState -> AttrParserState
+skipWhile charP st =
+  case B8.findIndex (not . charP) (B8.drop (offset st) (subject st)) of
+    Nothing -> st{ offset = B8.length (subject st) }
+    Just i -> st{ offset = offset st + i }
+
+attrPartsToAttr :: [AttrPart] -> Attr
+attrPartsToAttr = go
+ where
+   go [] = Attr []
+   go (AttrId bs : xs) = (<> Attr [("id",bs)]) $ go xs
+   go (AttrClass bs : xs) = (<> Attr [("class",bs)]) $ go xs
+   go zs =
+     case break isAttrKey zs of
+       (vs, AttrKey bs : xs) ->
+         (<> Attr [(bs, mconcat (reverse $ map getAttrVal vs))]) $ go xs
+       _ -> Attr [] -- should not happen
+   isAttrKey (AttrKey _) = True
+   isAttrKey _ = False
+   getAttrVal (AttrValue bs) = bs
+   getAttrVal _ = mempty
+
+isNameChar :: Char -> Bool
+isNameChar c = not (isSpace c)
+    && (not (isPunctuation c) || c == ':' || c == '_' || c == '-')
+
+isKeyChar :: Char -> Bool
+isKeyChar c = isAlphaNum c || c == ':' || c == '_' || c == '-'
+
+isBareValChar :: Char -> Bool
+isBareValChar c = isAlphaNum c || c == ':' || c == '_' || c == '-'
diff --git a/src/Djot/Blocks.hs b/src/Djot/Blocks.hs
new file mode 100644
--- /dev/null
+++ b/src/Djot/Blocks.hs
@@ -0,0 +1,1138 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE Strict #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+module Djot.Blocks
+( parseDoc
+, toIdentifier
+)
+where
+
+import Prelude hiding (div)
+import Text.Read (readMaybe)
+import Data.Maybe (fromMaybe)
+import Data.Char (ord, isAsciiLower, isAsciiUpper, isAscii, isAlphaNum, isDigit)
+import Data.Foldable as F
+import Djot.Parse
+import Djot.AST
+import Djot.Inlines (parseInlines, parseTableCells)
+import Djot.Options (ParseOptions(..), SourcePosOption(..))
+import Djot.Attributes (parseAttributes, AttrParserState, AttrParseResult(..))
+import Data.Sequence (Seq)
+import qualified Data.Sequence as Seq
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Char8 as B8
+import Data.ByteString (ByteString)
+import Control.Monad (replicateM_, void, mzero, unless, when, guard, foldM)
+import Data.List.NonEmpty (NonEmpty(..))
+import Data.List (intercalate)
+import qualified Data.List.NonEmpty as NonEmpty
+import Data.Set (Set)
+import qualified Data.Set as Set
+import Control.Applicative
+import Data.Typeable (Typeable)
+-- import Debug.Trace
+
+parseDoc :: ParseOptions -> ByteString -> Either String Doc
+parseDoc opts bs = do
+  case parse pDoc PState{ psParseOptions = opts
+                        , psContainerStack =
+                            NonEmpty.fromList
+                             [emptyContainer{ containerSpec = docSpec }]
+                        , psReferenceMap = mempty
+                        , psAutoReferenceMap = mempty
+                        , psNoteMap = mempty
+                        , psAttributes = mempty
+                        , psAttrParserState = Nothing
+                        , psIds = mempty
+                        , psAutoIds = mempty
+                        , psLastColumnPrevLine = 0
+                        , psLastLine = 1
+                        } [Chunk{ chunkLine = 1, chunkColumn = 1, chunkBytes = bs }] of
+    Just doc -> Right doc
+    Nothing -> Left "Parse failure."
+
+data BlockType =
+  Normal | ListItem | CaptionBlock | Document
+  deriving (Show, Eq)
+
+data BlockSpec =
+  BlockSpec
+  { -- | Descriptive name
+    blockName :: String
+  , -- | Type of block
+    blockType :: BlockType
+    -- | Parser for start of this block type
+  , blockStart :: P ()
+    -- | Parser that must return True if this block is to continue
+  , blockContinue :: Container -> P Bool
+    -- | Just blockType if it can contain that type of block
+  , blockContainsBlock :: Maybe BlockType
+    -- | True if it can accept text lines
+  , blockContainsLines :: Bool
+    -- | Parser that runs when block is closed, possibly
+    -- updating the container.
+  , blockClose :: Container -> P Container
+    -- | Parser that runs when the document is closed, creating the
+    -- block AST element.
+  , blockFinalize :: Container -> Blocks
+  }
+
+docSpec :: BlockSpec
+docSpec =
+  BlockSpec
+  { blockName = "Doc"
+  , blockType = Document
+  , blockStart = mzero
+  , blockContinue = \_ -> pure True
+  , blockContainsBlock = Just Normal
+  , blockContainsLines = False
+  , blockClose = pure
+  , blockFinalize = finalizeChildren
+  }
+
+listItemSpec :: BlockSpec
+listItemSpec =
+  BlockSpec
+  { blockName = "ListItem"
+  , blockType = ListItem
+  , blockStart = do
+      ind <- sourceColumn
+      ltypes <- pListStart
+      skipMany spaceOrTab
+      tip :| _ <- psContainerStack <$> getState
+      case blockContainsBlock (containerSpec tip) of
+        Just ListItem -> pure ()
+        _ -> addContainer listSpec ind NoData
+      addContainer listItemSpec ind (ListItemData ind ltypes False)
+  , blockContinue = \container -> do
+          True <$ fails
+            (do skipMany spaceOrTab
+                curind <- sourceColumn
+                let liIndent = case containerData container of
+                                 ListItemData i _ _ -> i
+                                 _ -> error "Missing ListItemData"
+                tip :| _ <- psContainerStack <$> getState
+                guard (curind <= liIndent)
+                case blockName (containerSpec tip) of
+                  "Para" -> void pListStart
+                  _ -> pure ())
+        <|> pure False
+  , blockContainsBlock = Just Normal
+  , blockContainsLines = False
+  , blockClose = pure
+  , blockFinalize = finalizeChildren
+  }
+
+pListStart :: P [ListType]
+pListStart = pBulletListStart <|> pDefinitionListStart <|> pOrderedListStart
+
+pBulletListStart :: P [ListType]
+pBulletListStart = do
+  bulletchar <- satisfyByte (\c -> c == '-' || c == '+' || c == '*')
+  followedByWhitespace
+  (do skipMany spaceOrTab
+      asciiChar '['
+      status <- (Complete <$ byteString "x]")
+            <|> (Complete <$ byteString "X]")
+            <|> (Incomplete <$ byteString " ]")
+      followedByWhitespace
+      pure [Task status])
+   <|> pure [Bullet bulletchar]
+
+pDefinitionListStart :: P [ListType]
+pDefinitionListStart = do
+  asciiChar ':'
+  followedByWhitespace
+  pure [Definition]
+
+groupLists :: Seq Container -> Seq ([ListType], Seq Container)
+groupLists = snd . foldl' go ([], mempty)
+ where
+   go :: ([ListType], Seq ([ListType], Seq Container))
+      -> Container
+      -> ([ListType], Seq ([ListType], Seq Container))
+   go (curtypes, lists) cont =
+     case Seq.viewr lists of
+       Seq.EmptyR -> (getListTypes cont,
+                       Seq.singleton (getListTypes cont, Seq.singleton cont))
+       rest Seq.:> (_, cur) ->
+         let lt = getListTypes cont
+             matchedTypes = [ty | ty <- curtypes, any (ty `matches`) lt]
+         in if null matchedTypes
+               then (getListTypes cont, lists Seq.|> (getListTypes cont, Seq.singleton cont)) -- new list
+               else (matchedTypes, rest Seq.|> (matchedTypes, cur Seq.|> cont))
+
+   matches :: ListType -> ListType -> Bool
+   matches (Bullet b1) (Bullet b2) = b1 == b2
+   matches (Ordered o1) (Ordered o2) =
+     orderedListStyle o1 == orderedListStyle o2 &&
+     orderedListDelim o1 == orderedListDelim o2
+   matches Definition Definition = True
+   matches Task{} Task{} = True
+   matches _ _ = False
+
+
+getListTypes :: Container -> [ListType]
+getListTypes cont = case containerData cont of
+                      ListItemData _ tys _ -> tys
+                      _ -> error "Missing ListItemData"
+
+pOrderedListStart :: P [ListType]
+pOrderedListStart = do
+  openParen <- (True <$ asciiChar '(') <|> pure False
+  lookahead $ do
+    skipSome $ skipSatisfyByte (\c -> isAscii c && isAlphaNum c)
+    skipSatisfyByte (\c -> c == '.' || c == ')')
+  stylesAndStarts <- decimalStart <|> romanStart <|> letterStart
+  delimType <-
+    if openParen
+       then LeftRightParen <$ asciiChar ')'
+       else (RightParen <$ asciiChar ')') <|> (RightPeriod <$ asciiChar '.')
+  followedByWhitespace
+  pure $ map
+    (\(style, start) -> Ordered
+        OrderedListAttributes
+        { orderedListStyle = style
+        , orderedListDelim = delimType
+        , orderedListStart = start }) stylesAndStarts
+ where
+  decimalStart = do
+    digits <- some (satisfyByte isDigit)
+    case readMaybe digits of
+      Just n -> pure [(Decimal, n)]
+      Nothing -> mzero
+  letterStart = do
+    c <- satisfyByte (\c -> isAsciiLower c || isAsciiUpper c)
+    if isAsciiLower c
+       then pure [(LetterLower, 1 + (ord c - ord 'a'))]
+       else pure [(LetterUpper, 1 + (ord c - ord 'A'))]
+  romanStart = do
+    (n, lettercase) <- pRomanNumeral
+    let sty = if lettercase == Uppercase then RomanUpper else RomanLower
+    let altsty = if lettercase == Uppercase then LetterUpper else LetterLower
+    pure $ (sty, n) :
+      case n of
+        1 -> [(altsty, 9)]
+        5 -> [(altsty, 22)]
+        10 -> [(altsty, 24)]
+        50 -> [(altsty, 12)]
+        100 -> [(altsty, 3)]
+        500 -> [(altsty, 4)]
+        1000 -> [(altsty, 13)]
+        _ -> []
+
+data Case = Uppercase | Lowercase
+  deriving (Eq)
+
+pRomanNumeral :: P (Int, Case)
+pRomanNumeral = do
+  let isUpperRomanChar c = c == 'I' || c == 'V' || c == 'X' ||
+                           c == 'L' || c == 'C' || c == 'D' || c == 'M'
+  let isLowerRomanChar c = c == 'i' || c == 'v' || c == 'x' ||
+                           c == 'l' || c == 'c' || c == 'd' || c == 'm'
+  let isRomanChar c = isUpperRomanChar c || isLowerRomanChar c
+  lettercase <- lookahead $ do
+    c <- satisfyByte isRomanChar
+    let lettercase = if isUpperRomanChar c then Uppercase else Lowercase
+    skipMany $ skipSatisfyByte $
+      case lettercase of
+        Uppercase -> isUpperRomanChar
+        Lowercase -> isLowerRomanChar
+    skipSatisfyByte (\d -> d == ')' || d == '.')
+    pure lettercase
+  let rchar uc lc = satisfyByte $ if lettercase == Uppercase
+                                       then (== uc)
+                                       else (== lc)
+  let one         = rchar 'I' 'i'
+  let five        = rchar 'V' 'v'
+  let ten         = rchar 'X' 'x'
+  let fifty       = rchar 'L' 'l'
+  let hundred     = rchar 'C' 'c'
+  let fivehundred = rchar 'D' 'd'
+  let thousand    = rchar 'M' 'm'
+  thousands <- (1000 *) . length <$> many thousand
+  ninehundreds <- option 0 $ hundred >> thousand >> return 900
+  fivehundreds <- option 0 $ 500 <$ fivehundred
+  fourhundreds <- option 0 $ hundred >> fivehundred >> return 400
+  hundreds <- (100 *) . length <$> many hundred
+  nineties <- option 0 $ ten >> hundred >> return 90
+  fifties <- option 0 (50 <$ fifty)
+  forties <- option 0 $ ten >> fifty >> return 40
+  tens <- (10 *) . length <$> many ten
+  nines <- option 0 $ one >> ten >> return 9
+  fives <- option 0 (5 <$ five)
+  fours <- option 0 $ one >> five >> return 4
+  ones <- length <$> many one
+  let total = thousands + ninehundreds + fivehundreds + fourhundreds +
+              hundreds + nineties + fifties + forties + tens + nines +
+              fives + fours + ones
+  if total == 0
+     then mzero
+     else return (total, lettercase)
+ where
+   option defval p = p <|> pure defval
+
+listSpec :: BlockSpec
+listSpec =
+  BlockSpec
+  { blockName = "List"
+  , blockType = Normal
+  , blockStart = mzero -- added in listItemSpec
+  , blockContinue = \_ -> pure True
+  , blockContainsBlock = Just ListItem
+  , blockContainsLines = False
+  , blockClose = pure
+  , blockFinalize = foldMap itemsToList . groupLists . containerChildren
+  }
+
+itemsToList :: ([ListType], Seq Container) -> Blocks
+itemsToList (ltypes, containers) =
+  case containers of
+    Seq.Empty -> mempty
+    _ ->
+       let spacing =
+             case Seq.viewr containers of
+               Seq.EmptyR -> Tight
+               as Seq.:> _ | any itemEndsWithBlank as ||
+                             any hasChildrenSeparatedWithBlank containers
+                          -> Loose
+               _ -> Tight
+           items' = toList items
+           taskListStatus = map getTaskStatus (toList containers)
+           pos = case (Seq.viewl containers, Seq.viewr containers) of
+                    (s Seq.:< _, _ Seq.:> e) | containerSourcePos s ->
+                      Pos (containerStartLine s) (containerStartColumn s)
+                          (containerEndLine e) (containerEndColumn e)
+                    _ -> NoPos
+       in addPos pos <$>
+          case ltypes of
+            Bullet{} : _-> bulletList spacing items'
+            Ordered _ : _->
+              orderedList (chooseOrderedAttr ltypes) spacing items'
+            Definition : _ -> definitionList spacing $ map toDefinition items'
+            Task _ : _ -> taskList spacing $ zip taskListStatus items'
+            [] -> mempty
+ where
+   items = map finalize $ toList containers
+   getTaskStatus cont = case getListTypes cont of
+                           ([Task stat] :: [ListType]) -> stat
+                           _ -> error "getTaskStatus: wrong shape"
+   -- when ambiguous between roman and lettered list, choose roman if start number is 1,
+   -- otherwise lettered
+   chooseOrderedAttr os =
+     case [at | Ordered at <- os, isRomanStartOne at] of
+       (a:_) -> a
+       _ -> case [at | Ordered at <- os, isLettered at] of
+         (a:_)  -> a
+         _ -> case [at | Ordered at <- os] of
+           (a:_) -> a
+           [] -> error "chooseOrderedAttr on empty list"
+   isRomanStartOne at = (orderedListStyle at == RomanUpper ||
+                         orderedListStyle at == RomanLower) &&
+                         orderedListStart at == 1
+   isLettered at = orderedListStyle at == LetterUpper ||
+                   orderedListStyle at == LetterLower
+
+-- | We determine whether a list item ends with a blank line by
+-- comparing its end line with the end line of its last child.
+itemEndsWithBlank :: Container -> Bool
+itemEndsWithBlank li =
+  case Seq.viewr (containerChildren li) of
+    Seq.EmptyR -> False
+    _ Seq.:> lastChild -> containerEndLine li > containerEndLine lastChild
+
+-- | We don't count blanks before lists, because
+-- otherwise it would be impossible to have nested tight lists.
+hasChildrenSeparatedWithBlank :: Container -> Bool
+hasChildrenSeparatedWithBlank cont =
+  or $ Seq.zipWith check children (Seq.drop 1 children)
+ where
+   children = (if Definition `elem` liTypes then Seq.drop 1 else id) $
+                  containerChildren cont
+   check x y = (blockName (containerSpec y) /= "List") &&
+               (containerStartLine y > containerEndLine x)
+   liTypes = getListTypes cont
+
+toDefinition :: Blocks -> (Inlines, Blocks)
+toDefinition bs =
+  case Seq.viewl bs' of
+    Node _ _ (Para ils) Seq.:< _ -> (ils, Many (Seq.drop 1 bs'))
+    _ -> (mempty, bs)
+  where
+   bs' = unMany bs
+
+sectionSpec :: BlockSpec
+sectionSpec =
+  BlockSpec
+  { blockName = "Section"
+  , blockType = Normal
+  , blockStart = mzero  -- these containers are added by headingSpec
+  , blockContinue = \_ -> pure True  -- these are closed by headingSpec
+  , blockContainsBlock = Just Normal
+  , blockContainsLines = False
+  , blockClose = \container -> do
+      case containerChildren container of
+        h Seq.:<| _
+          | blockName (containerSpec h) == "Heading" -> do
+             let lev = case containerData container of
+                         SectionData n _ -> n
+                         _ -> error "Missing SectionData"
+             let ils = case containerData h of
+                         HeadingData _ xs -> xs
+                         _ -> error "Missing HeadingData"
+             (secid, attr, label) <- do
+               let bs = inlinesToByteString ils
+               let Attr ats = containerAttr container
+               case lookup "id" ats of
+                 Just id' -> pure (id', mempty, normalizeLabel bs)
+                 Nothing -> do -- generate id from title
+                   let generateId (n :: Int) base = do
+                         let candidate
+                               | n == 0 = base
+                               | otherwise = base <> "-" <> B8.pack (show n)
+                         ids <- psIds <$> getState
+                         if candidate `Set.member` ids
+                            then generateId (n+1) base
+                            else do
+                              updateState $ \st ->
+                                st{ psIds = Set.insert candidate (psIds st)
+                                  , psAutoIds = Set.insert candidate
+                                                   (psAutoIds st) }
+                              pure candidate
+                   ident <- generateId 0 (toIdentifier bs)
+                   pure (ident, mempty, normalizeLabel bs)
+             -- add implicit reference
+             let dest = "#" <> secid
+             updateState $ \st -> st{ psAutoReferenceMap = insertReference label
+                                     (dest, Attr []) (psAutoReferenceMap st) }
+
+             pure container{ containerData =
+                               SectionData lev (Just secid)
+                           , containerAttr = containerAttr container <> attr }
+        _ -> pure container
+  , blockFinalize = \container ->
+      let blocks = finalizeChildren container
+          secid = case containerData container of
+                    SectionData _ ident -> ident
+                    _ -> error "Missing SectionData"
+      in addSourcePos container $
+          maybe id (\ident -> addAttr (Attr [("id", ident)])) secid
+           <$> section blocks
+  }
+
+blockQuoteSpec :: BlockSpec
+blockQuoteSpec =
+  BlockSpec
+  { blockName = "BlockQuote"
+  , blockType = Normal
+  , blockStart = do
+      ind <- sourceColumn
+      asciiChar '>'
+      followedByWhitespace
+      skipMany spaceOrTab
+      addContainer blockQuoteSpec ind NoData
+  , blockContinue = \_ -> do
+      skipMany spaceOrTab
+      asciiChar '>'
+      followedByWhitespace
+      skipMany spaceOrTab
+      pure True
+  , blockContainsBlock = Just Normal
+  , blockContainsLines = False
+  , blockClose = pure
+  , blockFinalize = \container ->
+      addSourcePos container $ blockQuote $ finalizeChildren container
+  }
+
+tableSpec :: BlockSpec
+tableSpec =
+  BlockSpec
+  { blockName = "Table"
+  , blockType = Normal
+  , blockStart = do
+      lookahead pRawTableRow
+      ind <- sourceColumn
+      addContainer tableSpec ind (TableData mempty)
+  , blockContinue = \container ->
+      -- TODO: this is inefficient; we parse the inline contents
+      -- twice. Find a better way.
+      (True <$ lookahead pRawTableRow)
+      <|> (True <$ followedByBlankLine)
+      <|> (True <$ lookahead
+                      (skipMany spaceOrTab *> asciiChar '^' *> spaceOrTab))
+      <|> (True <$ guard (not (null (containerChildren container))))
+  , blockContainsBlock = Just CaptionBlock
+  , blockContainsLines = True
+  , blockClose = \container -> do
+      let lns = containerText container
+      rows <- reverse . snd <$> foldM parseTableRow ([], []) lns
+      pure $ container{ containerData = TableData rows }
+  , blockFinalize = \container ->
+      let rows = case containerData container of
+                   TableData rs -> rs
+                   _ -> error "Missing TableData"
+          mbCaption =
+            case Seq.viewr (containerChildren container) of
+              Seq.EmptyR -> Nothing
+              _ Seq.:> x -> Just . Caption $ blockFinalize (containerSpec x) x
+      in  addSourcePos container $ table mbCaption rows
+  }
+
+parseTableRow :: ([Align], [[Cell]])
+              -> Chunk
+              -> P ([Align], [[Cell]])
+parseTableRow (aligns, rows) chunk =
+  case B8.uncons (B8.strip $ chunkBytes chunk) of
+    Just ('|',_) -> do
+      res <- pTableCells aligns chunk
+      case res of
+        Left aligns' -> pure (aligns',
+                               case rows of
+                                 r:rs -> zipWith toHeadCell aligns' r : rs
+                                 [] -> [] )
+        Right cells -> pure (aligns, cells : rows)
+    Nothing -> pure (aligns, rows)
+    Just (_,_) -> mzero
+ where
+   toHeadCell align' (Cell _ _ ils) = Cell HeadCell align' ils
+
+pTableCells :: [Align] -> Chunk -> P (Either [Align] [Cell])
+pTableCells aligns chunk =
+  case parse pTableSeps () [chunk] of
+    Just aligns' -> pure $ Left aligns'
+    Nothing -> do
+      opts <- psParseOptions <$> getState
+      case parseTableCells opts chunk of
+        Right cs ->
+          pure $ Right $
+            zipWith (Cell BodyCell) (aligns ++ repeat AlignDefault) cs
+        Left _ -> mzero
+
+pTableSeps :: Parser () [Align]
+pTableSeps = do
+  skipMany spaceOrTab
+  asciiChar '|'
+  many pTableSep <* skipMany ws <* eof
+ where
+   pTableSep = do
+     skipMany spaceOrTab
+     start <- (True <$ asciiChar ':') <|> pure False
+     skipSome (asciiChar '-')
+     end <- (True <$ asciiChar ':') <|> pure False
+     skipMany spaceOrTab
+     asciiChar '|'
+     pure $ case (start, end) of
+              (True, True) -> AlignCenter
+              (True, False) -> AlignLeft
+              (False, True) -> AlignRight
+              (False, False) -> AlignDefault
+
+pRawTableRow :: P ()
+pRawTableRow = do
+  lookahead $ asciiChar '|'
+  curline <- sourceLine
+  curcolumn <- sourceColumn
+  bs <- restOfLine
+  void $ parseTableRow ([],[]) Chunk{ chunkLine = curline
+                                    , chunkColumn = curcolumn
+                                    , chunkBytes = bs }
+
+captionSpec :: BlockSpec
+captionSpec =
+  BlockSpec
+  { blockName = "Caption"
+  , blockType = CaptionBlock
+  , blockStart = do
+      ind <- sourceColumn
+      asciiChar '^'
+      void spaceOrTab
+      addContainer captionSpec ind $ CaptionData ind
+  , blockContinue = \container -> (do
+      skipMany spaceOrTab
+      curind <- sourceColumn
+      let ind = case containerData container of
+                  CaptionData i -> i
+                  _ -> error "Missing CaptionData"
+      guard (curind > ind) <|> followedByBlankLine
+      pure True) <|> pure False
+  , blockContainsBlock = Just Normal
+  , blockContainsLines = False
+  , blockClose = pure
+  , blockFinalize = finalizeChildren
+  }
+
+
+thematicBreakSpec :: BlockSpec
+thematicBreakSpec =
+  BlockSpec
+  { blockName = "ThematicBreak"
+  , blockType = Normal
+  , blockStart = do
+      let breakChar = skipSatisfyByte (\c -> c == '-' || c == '*')
+                        *> skipMany spaceOrTab
+      ind <- sourceColumn
+      breakChar *> breakChar *> breakChar *> skipMany breakChar
+      lookahead endline
+      addContainer thematicBreakSpec ind NoData
+  , blockContinue = \_ -> pure False
+  , blockContainsBlock = Nothing
+  , blockContainsLines = True
+  , blockClose = pure
+  , blockFinalize = \container -> addSourcePos container thematicBreak
+  }
+
+headingSpec :: BlockSpec
+headingSpec =
+  BlockSpec
+  { blockName = "Heading"
+  , blockType = Normal
+  , blockStart = do
+      ind <- sourceColumn
+      lev <- length <$> some (asciiChar '#')
+      followedByWhitespace
+      skipMany spaceOrTab
+      closeContainingSections lev
+      addContainer sectionSpec ind $ SectionData lev Nothing
+      addContainer headingSpec ind $ HeadingData lev mempty
+  , blockContinue = \container -> do
+       do skipMany spaceOrTab
+          let lev = case containerData container of
+                      HeadingData n _ -> n
+                      _ -> error "Missing HeadingData"
+          (True <$ (do lev' <- length <$> some (asciiChar '#')
+                       guard (lev' == lev)
+                       skipMany spaceOrTab))
+            <|> (False <$ do
+                    lookahead (asciiChar '#' <|> endline <|> eof))
+            <|> pure True
+  , blockContainsBlock = Nothing
+  , blockContainsLines = True
+  , blockClose = \container -> do
+      ils <- parseTextLines container
+      let lev = case containerData container of
+                  HeadingData n _ -> n
+                  _ -> error "Missing HeadingData"
+      pure $ container{ containerData = HeadingData lev ils }
+  , blockFinalize = \container ->
+      let (lev, title) =
+            case containerData container of
+              HeadingData l t -> (l, t)
+              _ -> error "Missing HeadingData"
+      in  addSourcePos container $ heading lev title
+  }
+
+codeBlockSpec :: BlockSpec
+codeBlockSpec =
+  BlockSpec
+  { blockName = "CodeBlock"
+  , blockType = Normal
+  , blockStart = do
+      indent <- sourceColumn
+      ticks <- byteStringOf $ asciiChar '`' *> asciiChar '`' *> skipSome (asciiChar '`')
+      skipMany spaceOrTab
+      lang <- (byteStringOf
+                 (skipSome $ skipSatisfyByte (\c -> c /= '`' && not (isWs c)))
+                  <* skipMany spaceOrTab)
+             <|> pure ""
+      lookahead endline
+      addContainer codeBlockSpec indent (CodeBlockData ticks lang indent)
+  , blockContinue = \container -> do
+      let (ticks, indent) = case containerData container of
+                              CodeBlockData t _ i -> (t, i)
+                              _ -> error "Missing CodeBlockData"
+      gobbleSpaceToIndent indent
+      (do skipMany spaceOrTab
+          byteString ticks
+          skipMany (asciiChar '`')
+          skipMany spaceOrTab
+          lookahead endline
+          pure False)
+        <|> pure True
+  , blockContainsBlock = Nothing
+  , blockContainsLines = True
+  , blockClose = pure
+  , blockFinalize = \container ->
+      let lang = case containerData container of
+                   CodeBlockData _ l _ -> l
+                   _ -> error "Missing CodeBlockData"
+      -- drop first line which should be empty
+          bs = foldMap chunkBytes (Seq.drop 1 $ containerText container)
+      in  addSourcePos container $
+          case B8.uncons lang of
+            Just ('=', fmt) -> rawBlock (Format fmt) bs
+            _ -> codeBlock lang bs
+  }
+
+divSpec :: BlockSpec
+divSpec =
+  BlockSpec
+  { blockName = "Div"
+  , blockType = Normal
+  , blockStart = do
+      ind <- sourceColumn
+      colons <- byteStringOf $
+        asciiChar ':' *> asciiChar ':' *> skipSome (asciiChar ':')
+      skipMany spaceOrTab
+      label <- byteStringOf $ skipMany $ skipSatisfyByte (not . isWs)
+      skipMany spaceOrTab
+      lookahead endline
+      addContainer divSpec ind (DivData colons label)
+  , blockContinue = \container -> (do
+      tip <- getTip
+      -- see jgm/djot.js#109
+      guard $ blockName (containerSpec tip) /= "CodeBlock"
+      skipMany spaceOrTab
+      let colons = case containerData container of
+                     DivData c _ -> c
+                     _ -> error "Missing DivData"
+      byteString colons
+      skipMany (asciiChar ':')
+      skipMany spaceOrTab
+      lookahead endline
+      pure False) <|> pure True
+  , blockContainsBlock = Just Normal
+  , blockContainsLines = False
+  , blockClose = pure
+  , blockFinalize = \container ->
+      let label = case containerData container of
+                     DivData _ l -> l
+                     _ -> error "Missing DivData"
+      -- drop first line which should be empty
+          bls = finalizeChildren container
+      in  (if B.null label
+              then id
+              else addAttr (Attr [("class", label)])) <$>
+          addSourcePos container (div bls)
+  }
+
+attrSpec :: BlockSpec
+attrSpec =
+  BlockSpec
+  { blockName = "Attributes"
+  , blockType = Normal
+  , blockStart = do
+      ind <- sourceColumn
+      lookahead $ asciiChar '{'
+      addContainer attrSpec ind $ AttributeData ind
+  , blockContinue = \container -> do
+      let ind = case containerData container of
+                  AttributeData i -> i
+                  _ -> error "Missing AttributeData"
+      skipMany spaceOrTab
+      curind <- sourceColumn
+      mbapstate <- psAttrParserState <$> getState
+      if curind <= ind
+         then pure False
+         else do
+           let lastLine = case Seq.viewr (containerText container) of
+                             _ Seq.:> ll -> chunkBytes ll
+                             _ -> mempty
+           case parseAttributes mbapstate lastLine of
+             Done _ -> pure False
+             Partial apstate' -> do
+               updateState $ \st -> st{ psAttrParserState = Just apstate' }
+               pure True
+             Failed _ -> pure True -- not yet: keep going!
+  , blockContainsBlock = Nothing
+  , blockContainsLines = True
+  , blockClose = \container -> do
+      let bs = foldMap chunkBytes $ containerText container
+      case parseAttributes Nothing bs of
+        Done (attr, off)
+          | B8.all isWs (B8.drop off bs) -> do
+             updateState $ \st -> st{ psAttributes = psAttributes st <> attr }
+             pure container
+          | otherwise -> do
+             ils <- parseTextLines container
+             pure $ container{ containerSpec = paraSpec
+                             , containerInlines = ils }
+        _ -> do  -- could not parse lines as attribute, treat as Para
+          ils <- parseTextLines container
+          pure $ container{ containerSpec = paraSpec
+                          , containerInlines = ils }
+  , blockFinalize = const mempty
+  }
+
+referenceDefinitionSpec :: BlockSpec
+referenceDefinitionSpec =
+  BlockSpec
+  { blockName = "ReferenceDefinition"
+  , blockType = Normal
+  , blockStart = do
+      ind <- sourceColumn
+      asciiChar '['
+      fails (asciiChar '^') -- footnote
+      label <- byteStringOf
+                (some (skipSatisfyByte (\c -> c /= ']' && c /= '\n')))
+      asciiChar ']'
+      asciiChar ':'
+      skipMany spaceOrTab
+      addContainer referenceDefinitionSpec ind
+        (ReferenceData (normalizeLabel label))
+  , blockContinue = \_ ->
+      True <$ skipSome spaceOrTab `notFollowedBy` endline
+  , blockContainsBlock = Nothing
+  , blockContainsLines = True
+  , blockClose = \container -> do
+      let label = case containerData container of
+                    ReferenceData l -> l
+                    _ -> error "Missing ReferenceData"
+      let attr = containerAttr container
+      let dest = B.filter (> 32) . foldMap chunkBytes $ containerText container
+      updateState $ \st ->
+        st{ psReferenceMap = insertReference label (dest, attr)
+                                 (psReferenceMap st) }
+      pure container
+  , blockFinalize = const mempty
+  }
+
+footnoteSpec :: BlockSpec
+footnoteSpec =
+  BlockSpec
+  { blockName = "Footnote"
+  , blockType = Normal
+  , blockStart = do
+      ind <- sourceColumn
+      asciiChar '['
+      asciiChar '^'
+      label <- byteStringOf
+                (some (skipSatisfyByte (\c -> c /= ']' && c /= '\n')))
+      asciiChar ']'
+      asciiChar ':'
+      skipMany spaceOrTab
+      addContainer footnoteSpec ind $ FootnoteData ind (normalizeLabel label)
+  , blockContinue = \container -> (do
+      skipMany spaceOrTab
+      curind <- sourceColumn
+      let ind = case containerData container of
+                  FootnoteData i _ -> i
+                  _ -> error "Missing FootnoteData"
+      guard (curind > ind) <|> followedByBlankLine
+      pure True) <|> pure False
+  , blockContainsBlock = Just Normal
+  , blockContainsLines = True
+  , blockClose = \container -> do
+      let label = case containerData container of
+                     FootnoteData _ l -> l
+                     _ -> error "Missing FootnoteData"
+      let bls = finalizeChildren container
+      updateState $ \st -> st{ psNoteMap = insertNote label bls (psNoteMap st) }
+      pure container
+  , blockFinalize = const mempty
+  }
+
+
+paraSpec :: BlockSpec
+paraSpec =
+  BlockSpec
+  { blockName = "Para"
+  , blockType = Normal
+  , blockStart = do
+      fails followedByBlankLine
+      ind <- sourceColumn
+      addContainer paraSpec ind NoData
+  , blockContinue = \_ -> do
+      skipMany spaceOrTab
+      (False <$ lookahead (endline <|> eof)) <|> pure True
+  , blockContainsBlock = Nothing
+  , blockContainsLines = True
+  , blockClose = \container -> do
+      ils <- parseTextLines container
+      pure $ container{ containerInlines = ils }
+  , blockFinalize = \container ->
+      addSourcePos container . para $ containerInlines container
+  }
+
+parseTextLines :: Container -> P Inlines
+parseTextLines cont = do
+  opts <- psParseOptions <$> getState
+  either error pure . parseInlines opts $ containerText cont
+
+emptyContainer :: Container
+emptyContainer =
+  Container { containerSpec = docSpec
+            , containerChildren = mempty
+            , containerText = mempty
+            , containerInlines = mempty
+            , containerStartLine = 1
+            , containerStartColumn = 0
+            , containerEndLine = 1
+            , containerEndColumn = 0
+            , containerData = NoData
+            , containerAttr = mempty
+            , containerSourcePos = False
+            }
+
+data Container =
+  Container
+  { containerSpec      :: BlockSpec
+  , containerChildren  :: Seq Container
+  , containerText :: Seq Chunk
+  , containerInlines :: Inlines
+  , containerStartLine :: Int
+  , containerStartColumn :: Int
+  , containerEndLine :: Int
+  , containerEndColumn :: Int
+  , containerData :: ContainerData
+  , containerAttr :: Attr
+  , containerSourcePos :: Bool
+  }
+
+data ContainerData =
+    NoData
+  | ListItemData Int [ListType] Bool
+  | SectionData Int (Maybe ByteString)
+  | HeadingData Int Inlines
+  | CodeBlockData ByteString ByteString Int
+  | DivData ByteString ByteString
+  | FootnoteData Int ByteString
+  | TableData [[Cell]]
+  | CaptionData Int
+  | AttributeData Int
+  | ReferenceData ByteString
+  deriving (Show, Eq, Ord, Typeable)
+
+data ListType =
+    Bullet Char
+  | Ordered OrderedListAttributes
+  | Definition
+  | Task TaskStatus
+  deriving (Show, Ord, Eq)
+
+data PState =
+  PState
+  { psParseOptions :: ParseOptions
+  , psContainerStack :: NonEmpty Container
+  , psReferenceMap :: ReferenceMap
+  , psAutoReferenceMap :: ReferenceMap
+  , psNoteMap :: NoteMap
+  , psAttributes :: Attr
+  , psAttrParserState :: Maybe AttrParserState
+  , psIds :: Set ByteString
+  , psAutoIds :: Set ByteString
+  , psLastColumnPrevLine :: Int
+  , psLastLine :: Int
+  }
+
+type P = Parser PState
+
+pDoc :: P Doc
+pDoc = do
+  bls <- pBlocks <* eof
+  st <- getState
+  pure $ Doc{ docBlocks = bls
+            , docFootnotes = psNoteMap st
+            , docReferences = psReferenceMap st
+            , docAutoReferences = psAutoReferenceMap st
+            , docAutoIdentifiers = psAutoIds st }
+
+pBlocks :: P Blocks
+pBlocks = processLines >> finalizeDocument
+
+-- | Return value is True if all continuations match.
+checkContinuations :: NonEmpty Container -> P Bool
+checkContinuations = go . reverse . NonEmpty.toList
+ where
+   go [] = return True
+   go (c:cs) = do continue <- (Just <$> blockContinue (containerSpec c) c)
+                                  <|> pure Nothing
+                  when (continue == Just False) $ do -- early exit
+                     curline <- sourceLine
+                     curcol <- sourceColumn
+                     updateState $ \st ->
+                                     st{ psLastLine = curline
+                                       , psLastColumnPrevLine = curcol - 1 }
+                  if fromMaybe False continue
+                     then go cs
+                     else False <$ -- close len (c:cs) containers
+                          replicateM_ (length (c:cs)) closeCurrentContainer
+
+
+{-# INLINE processLines #-}
+processLines :: P ()
+processLines = do
+  -- check continuations for open containers and close any that don't match
+  containers <- psContainerStack <$> getState
+  allContainersMatch <- checkContinuations containers
+
+  -- check for new container starts and open if needed
+  newContainersAdded <- tryContainerStarts
+
+  followedByBlankLine <|> do
+    -- determine if we have a lazy line
+    let isLazy = not (allContainersMatch || newContainersAdded) &&
+                 blockName (containerSpec (NonEmpty.head containers)) == "Para"
+
+    when isLazy $ -- restore original containers
+       updateState (\st -> st{ psContainerStack = containers })
+
+    tip <- getTip
+
+    when (blockContainsBlock (containerSpec tip) == Just Normal) $ do
+      -- add a paragraph container
+      skipMany spaceOrTab
+      blockStart paraSpec
+
+  !curline <- sourceLine
+  !curcolumn <- sourceColumn
+  restline <- byteStringOf $ do
+     skipMany (skipSatisfyByte (\c -> c /= '\n' && c /= '\r'))
+     !lastcolumn <- sourceColumn
+     optional_ endline
+     updateState $ \st -> st{ psLastColumnPrevLine = lastcolumn - 1
+                            , psLastLine = curline }
+
+  -- if current container is a line container, add remainder of line
+  modifyContainers $
+    \(c :| rest) ->
+       if blockContainsLines (containerSpec c)
+          then c{ containerText = containerText c Seq.|>
+                    Chunk{ chunkLine = curline
+                         , chunkColumn = curcolumn
+                         , chunkBytes = restline } } :| rest
+          else c :| rest
+
+  eof <|> processLines
+
+-- True if new container was started
+tryContainerStarts :: P Bool
+tryContainerStarts = do
+  (c :| _) <- psContainerStack <$> getState
+  case blockContainsBlock (containerSpec c) of
+    Just bt -> (do
+      nextc <- lookahead (satisfyByte isAscii)
+      next <- if nextc == ' ' || nextc == '\t'
+                 then skipMany spaceOrTab *> lookahead (satisfyByte isAscii)
+                 else pure nextc
+      case next of
+        '>' -> blockStart blockQuoteSpec
+        '#' -> blockStart headingSpec
+        ':' -> blockStart divSpec <|> blockStart listItemSpec
+        '*' -> blockStart thematicBreakSpec <|> blockStart listItemSpec
+        '-' -> blockStart thematicBreakSpec <|> blockStart listItemSpec
+        '`' -> blockStart codeBlockSpec
+        '{' -> blockStart attrSpec
+        '[' -> blockStart referenceDefinitionSpec <|> blockStart footnoteSpec
+        '|' | bt == Normal -> blockStart tableSpec
+        '^' | bt == CaptionBlock -> blockStart captionSpec
+        _ -> blockStart listItemSpec
+      True <$ tryContainerStarts) <|> pure False
+    _ -> pure False
+
+-- | Close and finalize containers, returning Blocks.
+finalizeDocument :: P Blocks
+finalizeDocument = do
+  cs <- psContainerStack <$> getState
+  case cs of
+    _ :| [] -> closeCurrentContainer >> finalize <$> getTip
+    _ -> closeCurrentContainer >> finalizeDocument
+
+{-# INLINE closeCurrentContainer #-}
+-- | Close container and add to parent container.
+closeCurrentContainer :: P ()
+closeCurrentContainer = do
+  cs <- psContainerStack <$> getState
+  cs' <- case cs of
+           _ :| [] -> pure cs
+           c :| rest -> do
+             case containerAttr c of
+               Attr as | Just ident <- lookup "id" as
+                 -> updateState $ \st -> st{ psIds = Set.insert ident (psIds st) }
+               _ -> pure ()
+             c' <- blockClose (containerSpec c) c
+             pure (c':|rest)
+  case cs' of
+    c :| (d:rest) -> updateState $
+        \st -> st{ psContainerStack =
+                   d{ containerChildren = containerChildren d Seq.|>
+                        c{ containerEndLine = psLastLine st
+                         , containerEndColumn = psLastColumnPrevLine st } }
+                   :| rest }
+    c :| [] -> updateState $
+        \st -> st{ psContainerStack =
+                     c{ containerEndLine = psLastLine st
+                      , containerEndColumn = psLastColumnPrevLine st } :| [] }
+
+{-# INLINE modifyContainers #-}
+modifyContainers :: (NonEmpty Container -> NonEmpty Container) -> P ()
+modifyContainers f =
+  updateState $ \st -> st{ psContainerStack = f (psContainerStack st) }
+
+{-# INLINE addContainer #-}
+addContainer :: BlockSpec -> Int -> ContainerData -> P ()
+addContainer bspec curcol bdata = do
+  curline <- sourceLine
+  attr <- psAttributes <$> getState
+  opts <- psParseOptions <$> getState
+  let newcontainer = emptyContainer { containerSpec = bspec
+                                    , containerStartLine = curline
+                                    , containerStartColumn = curcol
+                                    , containerEndLine = curline
+                                    , containerEndColumn = curcol
+                                    , containerData = bdata
+                                    , containerAttr = attr
+                                    , containerSourcePos = sourcePositions opts /= NoSourcePos }
+  unless (blockName bspec == "Attributes") $
+    updateState $ \st -> st{ psAttributes = mempty }
+  closeInappropriateContainers bspec
+  modifyContainers (newcontainer NonEmpty.<|)
+
+closeInappropriateContainers :: BlockSpec -> P ()
+closeInappropriateContainers spec = do
+  -- close containers until we get one that can accept this type of container
+  cs <- psContainerStack <$> getState
+  case cs of
+    c :| _
+      | blockContainsBlock (containerSpec c) == Just (blockType spec) ->
+              pure ()
+      | otherwise -> closeCurrentContainer *> closeInappropriateContainers spec
+
+
+finalize :: Container -> Blocks
+finalize cont =
+  addAttr (containerAttr cont)
+    <$> blockFinalize (containerSpec cont) cont
+
+addSourcePos :: Container -> Blocks -> Blocks
+addSourcePos cont =
+   if containerSourcePos cont
+      then fmap
+           (addPos (Pos (containerStartLine cont) (containerStartColumn cont)
+                         (containerEndLine cont) (containerEndColumn cont)))
+      else id
+
+finalizeChildren :: Container -> Blocks
+finalizeChildren = foldMap finalize . containerChildren
+
+-- Gobble as much space as possible up to indent.
+gobbleSpaceToIndent :: Int -> P ()
+gobbleSpaceToIndent indent = do
+  curindent <- sourceColumn
+  when (curindent < indent) $
+     optional_ (spaceOrTab *> gobbleSpaceToIndent indent)
+
+{-# INLINE getTip #-}
+-- Get tip of container stack.
+getTip :: P Container
+getTip = NonEmpty.head . psContainerStack <$> getState
+
+closeContainingSections :: Int -> P ()
+closeContainingSections lev = do
+  tip <- getTip
+  case containerData tip of
+    SectionData lev' _ | lev' >= lev ->
+      closeCurrentContainer >>
+      closeContainingSections lev
+    _ -> pure ()
+
+-- TODO avoid detour through String
+toIdentifier :: ByteString -> ByteString
+toIdentifier bs =
+  if null parts
+     then "sec"
+     else strToUtf8 $ intercalate "-" parts
+ where
+   isSym = (`elem` ("][~!@#$%^&*(){}`,.<>\\|=+/" :: [Char]))
+   parts = words $ map (\c -> if isSym c then ' ' else c) $ utf8ToStr bs
diff --git a/src/Djot/Djot.hs b/src/Djot/Djot.hs
new file mode 100644
--- /dev/null
+++ b/src/Djot/Djot.hs
@@ -0,0 +1,477 @@
+{-# LANGUAGE TupleSections #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE OverloadedLists #-}
+{-# LANGUAGE StrictData #-}
+module Djot.Djot
+  ( renderDjot
+  , RenderOptions(..)
+  )
+where
+
+import Djot.AST
+import Djot.Options (RenderOptions(..))
+import Data.Char (ord, chr)
+import Djot.Parse (utf8ToStr)
+import Data.ByteString (ByteString)
+import qualified Data.ByteString.Char8 as B8
+import qualified Data.Set as Set
+import Data.Set (Set)
+import qualified Data.Sequence as Seq
+import qualified Data.Map.Strict as M
+import Data.Maybe (fromMaybe)
+import Data.List (sortOn, intersperse, transpose)
+import Control.Monad
+import Control.Monad.State
+import qualified Data.Foldable as F
+import Text.DocLayout hiding (Doc)
+import qualified Text.DocLayout as Layout
+import Data.Text (Text)
+import qualified Data.Text as T
+import Data.Text.Encoding (decodeUtf8With)
+import Data.Text.Encoding.Error (lenientDecode)
+import qualified Data.IntMap.Strict as IntMap
+
+renderDjot :: RenderOptions -> Doc -> Layout.Doc Text
+renderDjot opts doc = evalState
+                           (do body <- toLayout (docBlocks doc)
+                               refs <- gets referenceMap >>= toReferences
+                               notes <- toNotes
+                               pure $ body $$ refs $$ notes <> cr)
+                         BState{ noteMap = docFootnotes doc
+                               , noteOrder = mempty
+                               , referenceMap = docReferences doc
+                               , autoIds = docAutoIdentifiers doc
+                               , afterSpace = True
+                               , nestings = IntMap.fromList
+                                  -- anything not in this list
+                                  -- will ALWAYS get {}:
+                                  [(ord '_', 0)
+                                  ,(ord '*', 0)
+                                  ,(ord '~', 0)
+                                  ,(ord '\'', 0)
+                                  ,(ord '"', 0)
+                                  ,(ord '^', 0)]
+                               , lastBullet = Nothing
+                               , options = opts
+                               }
+
+data BState =
+  BState { noteMap :: NoteMap
+         , noteOrder :: M.Map ByteString Int
+         , referenceMap :: ReferenceMap
+         , autoIds :: Set ByteString
+         , afterSpace :: Bool
+         , nestings :: IntMap.IntMap Int
+         , lastBullet :: Maybe Char
+         , options :: RenderOptions
+         }
+
+toReferences :: ReferenceMap -> State BState (Layout.Doc Text)
+toReferences (ReferenceMap refs) =
+  (<> cr) . vcat <$> mapM toReference (M.toList refs)
+
+toReference :: (ByteString, (ByteString, Attr)) -> State BState (Layout.Doc Text)
+toReference (label, (url, attr)) = do
+  attr' <- toLayout attr
+  let ref = "[" <> literal (fromUtf8 label) <> "]:" <+> literal (fromUtf8 url)
+  pure $ attr' $$ ref
+
+toNotes :: State BState (Layout.Doc Text)
+toNotes = do
+  noterefs <- gets noteOrder
+  allLabels <- gets (M.keys . unNoteMap . noteMap)
+  let sortedLabels = sortOn (`M.lookup` noterefs) allLabels
+  (<> cr) . vsep <$> mapM toNote sortedLabels
+
+toNote :: ByteString -> State BState (Layout.Doc Text)
+toNote label = do
+  notes <- gets noteMap
+  case lookupNote label notes of
+    Nothing -> pure mempty
+    Just bls ->
+      hang 4 (toNoteRef label <> ":" <> space) <$> toLayout bls
+
+fromUtf8 :: ByteString -> Text
+fromUtf8 = decodeUtf8With lenientDecode
+
+data EscapeContext = Normal
+
+{-# INLINE escapeDjot #-}
+escapeDjot :: EscapeContext -> ByteString -> Text
+escapeDjot Normal bs
+  | B8.any escapable bs = T.pack. go . utf8ToStr $ bs
+  | otherwise = fromUtf8 bs
+ where
+  escapable c = c == '[' || c == ']' || c == '<' || c == '>' ||
+                c == '$' || c == '!' || c == '{' || c == '}' || c == ':' ||
+                c == '-' || c == '^' || c == '~' ||
+                c == '*' || c == '_' || c == '\''|| c == '"' || c == '.' ||
+                c == '|' || c == '`' || c == '\\'
+  go [] = []
+  go ('$':c:cs)
+    | c == '`' = '\\' : '$' : c : go cs
+    | otherwise = '$' : go (c : cs)
+  go ('-':cs) =
+    case cs of
+      '-':_ -> '\\' : '-' : go cs
+      _ -> '-' : go cs
+  go ('.':cs) =
+    case cs of
+      '.':'.':_ -> '\\' : '.' : go cs
+      _ -> '.' : go cs
+  go (c:':':cs)
+    | c /= ']'
+    , case cs of
+        [] -> True
+        (' ':_) -> True
+        _ -> False
+       = (if escapable c then ('\\' :) else id) $ c : ':' : go cs
+  go (c:cs)
+    | escapable c = '\\' : c : go cs
+    | otherwise = c : go cs
+
+{-# SPECIALIZE toLayout :: Blocks -> State BState (Layout.Doc Text) #-}
+{-# SPECIALIZE toLayout :: Inlines -> State BState (Layout.Doc Text) #-}
+{-# SPECIALIZE toLayout :: Attr -> State BState (Layout.Doc Text) #-}
+class ToLayout a where
+  toLayout :: a -> State BState (Layout.Doc Text)
+
+instance ToLayout Inlines where
+  toLayout = fmap F.fold . mapM toLayout . unMany
+
+instance ToLayout Blocks where
+  toLayout = fmap F.fold . mapM toLayout . unMany
+
+instance ToLayout Attr where
+  toLayout (Attr kvs)
+    = pure $ if isEmpty contents
+                then mempty
+                else "{" <> contents <> "}"
+       where
+         contents = hsep (map go kvs)
+         go ("id",ident) = literal ("#" <> fromUtf8 ident)
+         go ("class", classes') = hsep $ map (("." <>) . literal)
+                                       $ T.words $ fromUtf8 classes'
+         go (k,v) = literal (fromUtf8 k) <> "=" <>
+                       doubleQuotes (literal (escapeDjot Normal v))
+
+instance ToLayout (Node Block) where
+  toLayout (Node _pos attr bl) =
+    ($$) <$> (case bl of
+                -- don't print an id that was generated implicitly
+                Heading{} -> do
+                  autoids <- gets autoIds
+                  let Attr as = attr
+                  toLayout $ Attr [(k,v) | (k,v) <- as
+                                  , not (k == "id" && v `Set.member` autoids)]
+                Section{} -> do
+                  autoids <- gets autoIds
+                  let Attr as = attr
+                  toLayout $ Attr [(k,v) | (k,v) <- as
+                                  , not (k == "id" &&
+                                         v `Set.member` autoids)]
+                _ -> toLayout attr)
+         <*> (($$ blankline) <$> case bl of
+               Para ils -> toLayout ils
+               Heading lev ils -> do
+                 contents <- toLayout ils
+                 pure $ literal (T.replicate lev "#") <+> contents
+               Section bls -> ($$ blankline) <$> toLayout bls
+               ThematicBreak -> pure $ literal "* * * *"
+               BulletList listSpacing items -> do
+                 lastb <- gets lastBullet
+                 let bullet = case lastb of
+                                Just '+' -> "-"
+                                Just '-' -> "+"
+                                _ -> "-"
+                 (case listSpacing of
+                    Tight -> vcat . map chomp
+                    Loose -> vsep) <$>
+                   mapM (fmap (hang 2 (bullet <> space)) . toLayout) items
+               OrderedList listAttr listSpacing items ->
+                 (case listSpacing of
+                    Tight -> vcat . map chomp
+                    Loose -> vsep) <$>
+                 zipWithM (toOrderedListItem listAttr)
+                          [(orderedListStart listAttr)..]
+                          items
+               DefinitionList listSpacing items ->
+                 (case listSpacing of
+                    Tight -> vcat . map chomp
+                    Loose -> vsep) <$>
+                 mapM toDefinitionListItem items
+               TaskList listSpacing items ->
+                 (case listSpacing of
+                    Tight -> vcat . map chomp
+                    Loose -> vsep) <$>
+                 mapM toTaskListItem items
+               Div bls -> do
+                 let nestedDivs = computeDivNestingLevel bls
+                 contents <- toLayout bls
+                 let colons = literal (T.replicate (nestedDivs + 3) ":")
+                 pure $ colons $$ contents $$ colons
+               BlockQuote bls ->
+                 if bls == mempty
+                    then pure ">"
+                    else prefixed "> " <$> toLayout bls
+               CodeBlock lang bs -> do
+                 let longesttickline =
+                       case B8.lines bs of
+                         [] -> 0
+                         ls -> maximum $ map (B8.length . B8.takeWhile (=='`')) ls
+                 let numticks = max 3 longesttickline
+                 let ticks = literal $ T.replicate numticks "`"
+                 let lang' = if lang == mempty
+                                then mempty
+                                else literal (fromUtf8 lang)
+                 pure $ ticks <+> lang'
+                      $$ literal (fromUtf8 bs)
+                      $$ ticks
+               Table mbCaption rows -> do
+                 caption <- case mbCaption of
+                               Nothing -> pure mempty
+                               Just (Caption bls)
+                                       -> hang 2 ("^" <> space) <$> toLayout bls
+                 body <- toTable rows
+                 pure $ body $+$ caption
+               RawBlock (Format "djot") bs ->
+                 pure $ literal (fromUtf8 bs)
+               RawBlock _ _ -> pure mempty)
+         <* modify' (\st -> st{ afterSpace = True
+                              -- Handle case of one bullet list right after
+                              -- another; we need to change the bullet to
+                              -- start a new list:
+                              , lastBullet = case bl of
+                                               BulletList{} ->
+                                                 case lastBullet st of
+                                                   Just '-' -> Just '+'
+                                                   Just '+' -> Just '-'
+                                                   _ -> Just '-'
+                                               _ -> Nothing })
+
+toTable :: [[Cell]] -> State BState (Layout.Doc Text)
+toTable [] = pure "|--|" -- minimal empty table
+toTable rows = do
+  let getCellContents (Cell hd al ils) = ((hd, al),) <$> toLayout ils
+  rowContents <- mapM (mapM getCellContents) rows
+  let colwidths = map (maximum . map (offset . snd))
+                      (transpose rowContents)
+  let toCell width ((_,align), d) =
+        (case align of
+          AlignLeft -> lblock
+          AlignRight -> rblock
+          AlignCenter -> cblock
+          AlignDefault -> lblock) width d
+  let mkRow ds = hcat $ vfill "| " : intersperse (vfill " | ") ds ++
+                           [vfill " |"]
+  let mkLines ds = hcat $ vfill "|" : intersperse (vfill "|") ds ++
+                             [vfill "|"]
+  let toUnderline width ((_,al),_) = literal $
+        case al of
+           AlignLeft -> ":" <> T.replicate (width + 1) "-"
+           AlignRight -> T.replicate (width + 1) "-" <> ":"
+           AlignCenter -> ":" <> T.replicate width "-" <> ":"
+           AlignDefault -> T.replicate width "-"
+  let toRow cells =
+         let isHeader = case cells of
+                          ((HeadCell,_),_) : _ -> True
+                          _ -> False
+         in mkRow (zipWith toCell colwidths cells)
+            $$
+            if isHeader
+               then mkLines (zipWith toUnderline colwidths cells)
+               else mempty
+  pure $ vcat $ map toRow rowContents
+
+toDefinitionListItem :: (Inlines, Blocks) -> State BState (Layout.Doc Text)
+toDefinitionListItem (term, def) = do
+  term' <- toLayout term
+  def' <- toLayout def
+  pure $ hang 2 (":" <> space) $ term' $+$ def'
+
+toTaskListItem :: (TaskStatus, Blocks) -> State BState (Layout.Doc Text)
+toTaskListItem (status, bls) = do
+  contents <- toLayout bls
+  let marker = case status of
+                  Incomplete -> "- [ ]" <> space
+                  Complete -> "- [X]" <> space
+  pure $ hang 2 marker contents
+
+toOrderedListItem :: OrderedListAttributes -> Int -> Blocks
+                  -> State BState (Layout.Doc Text)
+toOrderedListItem listAttr num bs = do
+  contents <- toLayout bs
+  let marker = formatOrderedListMarker listAttr num
+  pure $ hang (offset marker + 1) (marker <> space) contents
+
+formatOrderedListMarker :: OrderedListAttributes -> Int -> Layout.Doc Text
+formatOrderedListMarker listAttr =
+  addDelims (orderedListDelim listAttr) .
+    formatNumber (orderedListStyle listAttr)
+
+addDelims :: OrderedListDelim -> Layout.Doc Text -> Layout.Doc Text
+addDelims RightPeriod d = d <> "."
+addDelims RightParen d = d <> ")"
+addDelims LeftRightParen d = "(" <> d <> ")"
+
+formatNumber :: OrderedListStyle -> Int -> Layout.Doc Text
+formatNumber Decimal n = literal (T.pack (show n))
+formatNumber LetterUpper n = literal (T.singleton (chr (ord 'A' + n - 1)))
+formatNumber LetterLower n = literal (T.singleton (chr (ord 'a' + n - 1)))
+formatNumber RomanUpper n = literal $ toRomanNumeral n
+formatNumber RomanLower n = literal $ T.toLower (toRomanNumeral n)
+
+-- | Convert number < 4000 to uppercase roman numeral. (from pandoc)
+toRomanNumeral :: Int -> T.Text
+toRomanNumeral x
+  | x >= 4000 || x < 0 = "?"
+  | x >= 1000 = "M" <> toRomanNumeral (x - 1000)
+  | x >= 900  = "CM" <> toRomanNumeral (x - 900)
+  | x >= 500  = "D" <> toRomanNumeral (x - 500)
+  | x >= 400  = "CD" <> toRomanNumeral (x - 400)
+  | x >= 100  = "C" <> toRomanNumeral (x - 100)
+  | x >= 90   = "XC" <> toRomanNumeral (x - 90)
+  | x >= 50   = "L"  <> toRomanNumeral (x - 50)
+  | x >= 40   = "XL" <> toRomanNumeral (x - 40)
+  | x >= 10   = "X" <> toRomanNumeral (x - 10)
+  | x == 9    = "IX"
+  | x >= 5    = "V" <> toRomanNumeral (x - 5)
+  | x == 4    = "IV"
+  | x >= 1    = "I" <> toRomanNumeral (x - 1)
+  | otherwise = ""
+
+
+
+instance ToLayout (Node Inline) where
+  toLayout (Node _pos attr il) = (<>)
+    <$> case il of
+          Str bs -> do
+            let fixSmart = T.replace "\x2014" "---" .
+                           T.replace "\x2013" "--" .
+                           T.replace "\x2026" "..." .
+                           T.replace "\x2019" "'" .
+                           T.replace "\x201C" "\""
+            let chunks =
+                  T.groupBy
+                   (\c d -> (c /= ' ' && d /= ' ') || (c == ' ' && d == ' '))
+                   (fixSmart $ escapeDjot Normal bs)
+            let toChunk ch
+                  = case T.uncons ch of
+                      Just (' ', rest)
+                        -> afterBreak "{}" <> space <> literal rest
+                      _ -> literal ch
+            pure $ hcat $ map toChunk chunks
+          SoftBreak -> do
+            opts <- gets options
+            pure $ if preserveSoftBreaks opts then cr else space
+          HardBreak -> pure (literal "\\" <> cr)
+          NonBreakingSpace -> pure "\\ "
+          Emph ils -> surround '_' ils
+          Strong ils -> surround '*' ils
+          Highlight ils -> surround '=' ils
+          Insert ils -> surround '+' ils
+          Delete ils -> surround '-' ils
+          Superscript ils -> surround '^' ils
+          Subscript ils -> surround '~' ils
+          Quoted SingleQuotes ils -> surround '\'' ils
+          Quoted DoubleQuotes ils -> surround '"' ils
+          Verbatim bs -> pure $ toVerbatimSpan bs
+          Math mt bs -> do
+            let suffix = toVerbatimSpan bs
+            let prefix = case mt of
+                            DisplayMath -> "$$"
+                            InlineMath -> "$"
+            pure $ prefix <> suffix
+          Symbol bs -> pure $ ":" <> literal (fromUtf8 bs) <> ":"
+          Span ils -> do
+            contents <- toLayout ils
+            pure $ "[" <> contents <> "]" <>
+                    case attr of  -- there must be attributes for it to be a span
+                      Attr [] -> "{}"
+                      _ -> mempty
+          Link ils target -> do
+            contents <- toLayout ils
+            let suffix = toLinkSuffix target contents
+            pure $ "[" <> contents <> "]" <> suffix
+          Image ils target -> do
+            contents <- toLayout ils
+            let suffix = toLinkSuffix target contents
+            pure $ "![" <> contents <> "]" <> suffix
+          EmailLink email -> pure $ "<" <> literal (fromUtf8 email) <> ">"
+          UrlLink url -> pure $ "<" <> literal (fromUtf8 url) <> ">"
+          RawInline (Format "djot") bs -> pure $ literal (fromUtf8 bs)
+          RawInline _ _ -> pure mempty
+          FootnoteReference label -> do
+            order <- gets noteOrder
+            case M.lookup label order of
+              Nothing -> modify' $ \st ->
+                            st{ noteOrder =
+                                  M.insert label (M.size order + 1) order }
+              Just _ -> pure ()
+            pure $ toNoteRef label
+   <*> toLayout attr
+    <* modify' (\st ->
+                 st{ afterSpace =
+                      case il of
+                         Str bs | isWhite (B8.takeEnd 1 bs) -> True
+                         SoftBreak -> True
+                         HardBreak -> True
+                         NonBreakingSpace -> True
+                         _ -> False })
+
+toLinkSuffix :: Target -> Layout.Doc Text -> Layout.Doc Text
+toLinkSuffix (Direct url) _ = literal $ "(" <> fromUtf8 url <> ")"
+toLinkSuffix (Reference label) d
+  | render Nothing d == fromUtf8 label = literal "[]"
+  | otherwise = literal $ "[" <> fromUtf8 label <> "]"
+
+toVerbatimSpan :: ByteString -> Layout.Doc Text
+toVerbatimSpan bs =
+  ticks <> (if startsWithTick then " " else mempty) <>
+    literal (fromUtf8 bs) <>
+    (if endsWithTick then " " else mempty) <> ticks
+ where
+  startsWithTick = B8.take 1 bs == "`"
+  endsWithTick = B8.takeEnd 1 bs == "`"
+  ticks = literal $ T.replicate (maxticks + 1) "`"
+  maxticks = fst $ B8.foldl' scanTicks (0,0) bs
+  scanTicks (longest, theseticks) '`' =
+     (max (theseticks + 1) longest, theseticks + 1)
+  scanTicks (longest, _) _ = (longest, 0)
+
+isWhite :: ByteString -> Bool
+isWhite " " = True
+isWhite "\t" = True
+isWhite _ = False
+
+surround :: Char -> Inlines -> State BState (Layout.Doc Text)
+surround c ils = do
+  let startBeforeSpace =
+        case Seq.viewl (unMany ils) of
+                Node _pos _ (Str bs) Seq.:< _ ->
+                    isWhite (B8.take 1 bs)
+                _ -> False
+  modify' $ \st -> st{ nestings = IntMap.adjust (+ 1) (ord c) (nestings st)}
+  contents <- toLayout ils
+  modify' $ \st -> st{ nestings = IntMap.adjust (\x -> x - 1)
+                        (ord c) (nestings st)}
+  endAfterSpace <- gets afterSpace
+  nestingLevel <- gets (fromMaybe 1 . IntMap.lookup (ord c) . nestings)
+  let core = char c <> contents <> char c
+  pure $
+    if nestingLevel == 0 && not (startBeforeSpace || endAfterSpace) &&
+         not (null ils)
+       then core
+       else char '{' <> core <> char '}'
+
+toNoteRef :: ByteString -> Layout.Doc Text
+toNoteRef bs = literal ("[^" <> fromUtf8 bs <> "]")
+
+computeDivNestingLevel :: Blocks -> Int
+computeDivNestingLevel =
+  foldr go 0 . unMany
+ where
+   go (Node _pos _ (Div bls')) n =
+     max (n + 1) (foldr go (n + 1) (unMany bls'))
+   go _ n = n
diff --git a/src/Djot/Html.hs b/src/Djot/Html.hs
new file mode 100644
--- /dev/null
+++ b/src/Djot/Html.hs
@@ -0,0 +1,350 @@
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE OverloadedLists #-}
+{-# LANGUAGE Strict #-}
+module Djot.Html
+  ( inlinesToByteString
+  , renderHtml
+  , RenderOptions(..)
+  )
+where
+
+import Djot.AST
+import Data.Tuple (swap)
+import Djot.Parse (strToUtf8)
+import Djot.Options (RenderOptions(..))
+import Data.ByteString (ByteString)
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Char8 as B8
+import Data.ByteString.Builder (Builder, byteString, word8, intDec)
+import qualified Data.Sequence as Seq
+import qualified Data.Map.Strict as M
+import Data.Maybe (fromMaybe)
+import Data.List (sort)
+import Control.Monad.State
+import qualified Data.Foldable as F
+
+renderHtml :: RenderOptions -> Doc -> Builder
+renderHtml opts doc = evalState
+                          ( (<>) <$> toBuilder (docBlocks doc)
+                                 <*> toNotes )
+                         BState{ noteMap = docFootnotes doc
+                               , noteRefs = mempty
+                               , renderedNotes = mempty
+                               , referenceMap = docReferences doc
+                                             <> docAutoReferences doc
+                               , options = opts
+                               }
+
+toNotes :: State BState Builder
+toNotes = do
+  st <- get
+  let noterefs = noteRefs st
+  let numnotes = M.size noterefs
+  let revnoterefs = sort $ map swap $ M.toList noterefs
+  let toNote (num, lab) =
+        let num' = B8.pack (show num)
+        in  inTags "li" NoPos (Attr [("id", "fn" <> num')])
+              ("\n" <> fromMaybe mempty (M.lookup lab (renderedNotes st)))
+             <> "\n"
+  if numnotes < 1
+     then pure mempty
+     else pure $
+           inTags "section" NoPos (Attr [("role", "doc-endnotes")])
+            ("\n" <> singleTag "hr" NoPos mempty <> "\n" <>
+              inTags "ol" NoPos mempty ("\n" <> foldMap toNote revnoterefs) <> "\n")
+             <> "\n"
+
+addBackref :: ByteString -> Blocks -> Blocks
+addBackref num (Many bls) =
+    Many $
+      case Seq.viewr bls of
+          rest Seq.:> Node pos attr (Para ils) ->
+            rest Seq.|> Node pos attr (Para (ils <> backlink))
+          _ -> bls Seq.|> Node NoPos mempty (Para backlink)
+ where
+   backlink = Many $ Seq.singleton $
+               Node NoPos (Attr [("role", "doc-backlink")])
+                 (Link (str (strToUtf8 "\8617\65038"))
+                 (Direct ("#fnref" <> num)))
+
+{-# INLINE escapeHtml #-}
+escapeHtml :: ByteString -> Builder
+escapeHtml bs =
+  if hasEscapable bs
+     then B.foldl' go mempty bs
+     else byteString bs
+ where
+  hasEscapable = B.any (\w -> w == 38 || w == 60 || w == 62)
+  go b 38 = b <> byteString "&amp;"
+  go b 60 = b <> byteString "&lt;"
+  go b 62 = b <> byteString "&gt;"
+  go b c  = b <> word8 c
+
+{-# INLINE escapeHtmlAttribute #-}
+escapeHtmlAttribute :: ByteString -> Builder
+escapeHtmlAttribute bs =
+  if hasEscapable bs
+     then B.foldl' go mempty bs
+     else byteString bs
+ where
+  hasEscapable = B.any (\w -> w == 38 || w == 60 || w == 62 || w == 34)
+  go b 38 = b <> byteString "&amp;"
+  go b 60 = b <> byteString "&lt;"
+  go b 62 = b <> byteString "&gt;"
+  go b 34 = b <> byteString "&quot;"
+  go b c  = b <> word8 c
+
+data BState =
+  BState { noteMap :: NoteMap
+         , noteRefs :: M.Map ByteString Int
+         , renderedNotes :: M.Map ByteString Builder
+         , referenceMap :: ReferenceMap
+         , options :: RenderOptions
+         }
+
+{-# SPECIALIZE toBuilder :: Blocks -> State BState Builder #-}
+{-# SPECIALIZE toBuilder :: Inlines -> State BState Builder #-}
+class ToBuilder a where
+  toBuilder :: a -> State BState Builder
+
+instance ToBuilder Inlines where
+  toBuilder = fmap F.fold . mapM toBuilder . unMany
+
+instance ToBuilder Blocks where
+  toBuilder = fmap F.fold . mapM toBuilder . unMany
+
+instance ToBuilder (Node Block) where
+  toBuilder (Node pos attr bl) =
+    let addNl = (<> "\n") in
+    case bl of
+      Para ils -> addNl . inTags "p" pos attr <$> toBuilder ils
+      Heading lev ils ->
+        let tag = case lev of
+                    1 -> "h1"
+                    2 -> "h2"
+                    3 -> "h3"
+                    4 -> "h4"
+                    5 -> "h5"
+                    6 -> "h6"
+                    _ -> "p"
+        in  addNl . inTags tag pos attr <$> toBuilder ils
+      Section bls -> do
+        contents <- toBuilder bls
+        pure $ addNl $ inTags "section" pos attr $ "\n" <> contents
+      ThematicBreak -> pure $ addNl $ singleTag "hr" pos attr
+      BulletList listSpacing items ->
+        addNl . inTags "ul" pos attr . ("\n" <>) . mconcat <$> mapM toLi items
+          where
+            toLi bls = addNl . inTags "li" NoPos mempty . ("\n" <>) <$>
+                          toItemContents listSpacing bls
+      OrderedList listAttr listSpacing items ->
+        addNl . inTags "ol" pos (Attr [("start", strToUtf8 (show start))
+                                  | start /= 1]
+                              <> Attr [("type", typ) | typ /= "1"] <> attr)
+         . ("\n" <>) . mconcat <$> mapM toLi items
+          where
+            typ = case orderedListStyle listAttr of
+                    Decimal -> "1"
+                    LetterUpper -> "A"
+                    LetterLower -> "a"
+                    RomanUpper -> "I"
+                    RomanLower -> "i"
+            start = orderedListStart listAttr
+            toLi bls = addNl . inTags "li" NoPos mempty . ("\n" <>)
+                          <$> toItemContents listSpacing bls
+      DefinitionList listSpacing defs ->
+        addNl . inTags "dl" pos attr . ("\n" <>) . mconcat
+          <$> mapM (toDefinition listSpacing) defs
+      TaskList listSpacing items ->
+        addNl . inTags "ul" pos (Attr [("class", "task-list")] <> attr)
+          . ("\n" <>) . mconcat <$> mapM (toTaskListItem listSpacing) items
+      Div bls -> addNl . inTags "div" pos attr . ("\n" <>) <$> toBuilder bls
+      BlockQuote bls ->
+        addNl . inTags "blockquote" pos attr . ("\n" <>) <$> toBuilder bls
+      CodeBlock lang bs -> pure $
+        inTags "pre" pos attr (inTags "code" NoPos codeattr (escapeHtml bs))
+          <> "\n"
+         where
+           codeattr = if B.null lang
+                         then mempty
+                         else Attr [("class", "language-" <> lang)]
+      Table mbCaption rows -> do
+        rows' <- mapM toRow rows
+        capt <- case mbCaption of
+                   Nothing -> pure mempty
+                   Just (Caption bs) ->
+                     addNl . inTags "caption" NoPos mempty
+                       <$> case F.toList (unMany bs) of
+                              [Node _pos at (Para ils)] | at == mempty
+                                 -> toBuilder ils
+                              _ -> ("\n" <>) <$> toBuilder bs
+        pure $ addNl . inTags "table" pos attr . ("\n" <>) $ capt <> mconcat rows'
+      RawBlock (Format "html") bs -> pure $ byteString bs
+      RawBlock _ _ -> pure mempty
+
+toRow :: [Cell] -> State BState Builder
+toRow cells = (<> "\n") . inTags "tr" NoPos mempty . ("\n" <>) . mconcat
+                <$> mapM toCell cells
+
+toCell :: Cell -> State BState Builder
+toCell (Cell cellType align ils) =
+  (<> "\n") . inTags (if cellType == HeadCell
+                         then "th"
+                         else "td") NoPos attr <$> toBuilder ils
+ where
+   attr = Attr $ case align of
+                   AlignDefault -> []
+                   AlignLeft -> [("style", "text-align: left;")]
+                   AlignRight -> [("style", "text-align: right;")]
+                   AlignCenter -> [("style", "text-align: center;")]
+
+
+toItemContents :: ListSpacing -> Blocks -> State BState Builder
+toItemContents listSpacing = fmap F.fold . mapM go . unMany
+ where
+   go (Node pos attr bl) =
+    case bl of
+      Para ils
+        | listSpacing == Tight ->
+            if attr == mempty
+               then (<> "\n") <$> toBuilder ils
+               else (<> "\n") . inTags "span" pos attr <$> toBuilder ils
+        | otherwise -> toBuilder (Node pos attr bl)
+      _ -> toBuilder (Node pos attr bl)
+
+toTaskListItem :: ListSpacing -> (TaskStatus, Blocks) -> State BState Builder
+toTaskListItem listSpacing (status, bs) = do
+  body <- case Seq.viewl $ unMany bs of
+            Node pos attr (Para ils) Seq.:< rest ->
+              toItemContents listSpacing (Many
+                (Node pos attr
+                 (Para (rawInline (Format "html") ("<label>" <> input) <>
+                         ils <>
+                         rawInline (Format "html") "</label>")) Seq.<| rest))
+            _ -> toBuilder $ rawBlock (Format "html") input <> bs
+  pure $ inTags "li" NoPos (Attr [("class", if status == Complete
+                                             then "checked"
+                                             else "unchecked")])  ("\n" <> body) <> "\n"
+ where
+   inputattr = " type=\"checkbox\"" <>
+               if status == Complete then " checked=\"\"" else ""
+   input = "<input" <> inputattr <> " />"
+
+toDefinition :: ListSpacing -> (Inlines, Blocks) -> State BState Builder
+toDefinition listSpacing (term, defn) = (<>) <$>
+  ((<> "\n") . inTags "dt" NoPos mempty <$> toBuilder term) <*>
+  ((<> "\n") . inTags "dd" NoPos mempty . ("\n" <>) <$> toItemContents listSpacing defn)
+
+instance ToBuilder (Node Inline) where
+  toBuilder (Node pos attr il) =
+    case il of
+      Str bs -> case attr of
+                   Attr [] | pos == NoPos -> pure $ escapeHtml bs
+                   _ -> pure $ inTags "span" pos attr $ escapeHtml bs
+      SoftBreak -> do
+        opts <- gets options
+        pure $ word8 $ if preserveSoftBreaks opts then 10 else 32
+      HardBreak -> pure $ singleTag "br" NoPos attr <> "\n"
+      NonBreakingSpace -> pure $ byteString "&nbsp;"
+      Emph ils -> inTags "em" pos attr <$> toBuilder ils
+      Strong ils -> inTags "strong" pos attr <$> toBuilder ils
+      Highlight ils -> inTags "mark" pos attr <$> toBuilder ils
+      Insert ils -> inTags "ins" pos attr <$> toBuilder ils
+      Delete ils -> inTags "del" pos attr <$> toBuilder ils
+      Superscript ils -> inTags "sup" pos attr <$> toBuilder ils
+      Subscript ils -> inTags "sub" pos attr <$> toBuilder ils
+      Quoted SingleQuotes ils -> inSingleQuotes <$> toBuilder ils
+      Quoted DoubleQuotes ils -> inDoubleQuotes <$> toBuilder ils
+      Verbatim bs -> pure $ inTags "code" pos attr (escapeHtml bs)
+      Math DisplayMath bs -> pure $
+        inTags "span" pos (Attr [("class", "math display")] <> attr)
+          ("\\[" <> escapeHtml bs <> "\\]")
+      Math InlineMath bs -> pure $
+        inTags "span" pos (Attr [("class", "math inline")] <> attr)
+          ("\\(" <> escapeHtml bs <> "\\)")
+      Symbol bs -> pure $
+        inTags "span" pos (Attr [("class", "symbol")] <> attr)
+          (":" <> escapeHtml bs <> ":")
+      Span ils -> inTags "span" pos attr <$> toBuilder ils
+      Link ils target -> do
+        attr' <- case target of
+                   Direct u -> pure $ Attr [("href", u)]
+                   Reference label -> do
+                     rm <- gets referenceMap
+                     case lookupReference label rm of
+                       Nothing -> pure $ Attr [("href", "")]
+                       Just (u, Attr as) -> pure $ Attr (("href",u):as)
+        inTags "a" pos (attr' <> attr) <$> toBuilder ils
+      Image ils target -> do
+        attr' <- case target of
+                   Direct u -> pure $ Attr [("src", u)]
+                   Reference label -> do
+                     rm <- gets referenceMap
+                     case lookupReference label rm of
+                       Nothing -> pure $ Attr [("src", "")]
+                       Just (u, Attr as) -> pure $ Attr (("src",u):as)
+        pure $ singleTag "img" pos
+                 (Attr [("alt", inlinesToByteString ils)] <> attr' <> attr)
+      EmailLink email ->
+        toBuilder (Node pos attr (Link (str email) (Direct ("mailto:" <> email))))
+      UrlLink url -> toBuilder (Node pos attr (Link (str url) (Direct url)))
+      RawInline (Format "html") bs -> pure $ byteString bs
+      RawInline _ _ -> pure mempty
+      FootnoteReference label -> do
+        noterefs <- gets noteRefs
+        notemap <- gets noteMap
+        num <- case M.lookup label noterefs of
+                 Just num -> pure num
+                 Nothing -> do
+                   let num = M.size noterefs + 1
+                   modify $ \st -> st{ noteRefs = M.insert label num noterefs }
+                   renderedNotesMap <- gets renderedNotes
+                   case M.lookup label renderedNotesMap of
+                     Just _ -> pure ()
+                     Nothing -> do -- render the note and add to renderedNotes
+                       let num' = B8.pack (show num)
+                       rendered <- maybe (toBuilder $ addBackref num' (mempty :: Blocks))
+                                    (toBuilder . addBackref num') (lookupNote label notemap)
+                       modify $ \st -> st{ renderedNotes =
+                                             M.insert label rendered (renderedNotes st) }
+                   pure num
+        let num' = B8.pack $ show num
+        pure $ inTags "a" pos (Attr [("id", "fnref" <> num'),
+                                     ("href", "#fn" <> num'),
+                                     ("role", "doc-noteref")] <> attr) $
+                 inTags "sup" pos mempty (escapeHtml num')
+
+{-# INLINE inTags #-}
+inTags :: ByteString -> Pos -> Attr -> Builder -> Builder
+inTags tag pos attr contents =
+  "<" <> byteString tag <> posToBuilder pos
+      <> attrToBuilder attr <> ">" <> contents
+      <> "</" <> byteString tag <> ">"
+
+
+{-# INLINE singleTag #-}
+singleTag :: ByteString -> Pos -> Attr -> Builder
+singleTag tag pos attr =
+  "<" <> byteString tag <> posToBuilder pos <> attrToBuilder attr <> ">"
+
+{-# INLINE attrToBuilder #-}
+attrToBuilder :: Attr -> Builder
+attrToBuilder (Attr pairs) = foldMap go pairs
+ where
+   go (k,v) = " " <> byteString k <> "=\"" <> escapeHtmlAttribute v <> "\""
+
+{-# INLINE posToBuilder #-}
+posToBuilder :: Pos -> Builder
+posToBuilder NoPos = mempty
+posToBuilder (Pos sl sc el ec) =
+  " data-pos=\"" <> intDec sl <> ":" <> intDec sc <> "-" <>
+     intDec el <> ":" <> intDec ec <> "\""
+
+inSingleQuotes :: Builder -> Builder
+inSingleQuotes x =
+  byteString (strToUtf8 "\x2018") <> x <> byteString (strToUtf8 "\x2019")
+
+inDoubleQuotes :: Builder -> Builder
+inDoubleQuotes x =
+  byteString (strToUtf8 "\x201C") <> x <> byteString (strToUtf8 "\x201D")
diff --git a/src/Djot/Inlines.hs b/src/Djot/Inlines.hs
new file mode 100644
--- /dev/null
+++ b/src/Djot/Inlines.hs
@@ -0,0 +1,525 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE OverloadedLists #-}
+{-# LANGUAGE Strict #-}
+{-# LANGUAGE BinaryLiterals #-}
+module Djot.Inlines
+  ( parseInlines
+  , parseTableCells
+  )
+where
+
+import Data.Char (isAscii, isLetter, isAlphaNum, isSymbol, isPunctuation)
+import Control.Monad (guard, when, mzero)
+import Data.Sequence (Seq)
+import qualified Data.Sequence as Seq
+import Data.Set (Set)
+import qualified Data.Set as Set
+import Djot.Parse
+import Djot.Options (ParseOptions(..), SourcePosOption(..))
+import Djot.Attributes (pAttributes)
+import Djot.AST
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Char8 as B8
+import Data.ByteString (ByteString)
+import Data.Foldable as F
+import Control.Applicative
+import Data.Maybe (fromMaybe)
+
+-- import Debug.Trace
+
+{-# INLINE isSpecial #-}
+isSpecial :: Char -> Bool
+isSpecial c = c == '[' || c == ']' || c == '<' || c == '>' ||
+              c == '$' || c == '!' || c == '{' || c == '}' || c == ':' ||
+              c == '=' || c == '+' || c == '-' || c == '^' || c == '~' ||
+              c == '*' || c == '_' || c == '\''|| c == '"' || c == '.' ||
+              c == '|' || c == '`' || c == '\\'|| c == '\n' || c == '\r'
+
+parseInlines :: ParseOptions -> Seq Chunk -> Either String Inlines
+parseInlines opts chunks = do
+  case parse (pInlines <* eof) ParserState{ mode = NormalMode
+                                          , activeDelims = mempty
+                                          , options = opts }
+       (toList (stripEndChunks chunks)) of
+    Just ils -> Right ils
+    Nothing -> Left $ "parseInlines failed on input: "
+                     <> show (foldMap chunkBytes chunks)
+
+parseTableCells :: ParseOptions -> Chunk -> Either String [Inlines]
+parseTableCells opts chunk = do
+  case parse (asciiChar '|'
+                 *> some (removeFinalWs <$> pInlines <* asciiChar '|')
+                 <* skipMany ws
+                 <* eof)
+        ParserState{ mode = TableCellMode
+                   , activeDelims = mempty
+                   , options = opts }
+       [chunk] of
+    Just cells -> Right cells
+    Nothing -> Left $ "parseTableCells failed on input: " <> show chunk
+
+removeFinalWs :: Inlines -> Inlines
+removeFinalWs (Many ils) = Many $
+  case Seq.viewr ils of
+    rest Seq.:> Node pos attr (Str bs)
+      | B8.takeEnd 1 bs == " "
+        -> case B8.dropWhileEnd (== ' ') bs of
+             "" -> rest
+             bs' -> rest Seq.|> Node pos attr (Str bs')
+    _ -> ils
+
+data InlineParseMode =
+  NormalMode | TableCellMode
+  deriving (Show, Ord, Eq)
+
+data ParserState =
+  ParserState
+  { mode :: InlineParseMode
+  , activeDelims :: Set Delim
+  , options :: ParseOptions }
+  deriving (Show)
+
+data Delim = Delim Bool Char
+  deriving (Show, Ord, Eq)
+
+type P = Parser ParserState
+
+pInlines :: P Inlines
+pInlines = skipMany ws *> (mconcat <$> many pInline)
+
+pInline :: P Inlines
+pInline = do
+  sline <- sourceLine
+  scol <- sourceColumn
+  res <- pInline'
+  opts <- options <$> getState
+  (case sourcePositions opts of
+     AllSourcePos -> do
+       eline <- sourceLine
+       ecol <- sourceColumn
+       pure $ addPos (Pos sline scol eline (ecol - 1)) <$> res
+     _ -> pure res) >>= pOptionalAttributes
+
+pOptionalAttributes :: Inlines -> P Inlines
+pOptionalAttributes (Many ils) = pAddAttributes (Many ils) <|> pure (Many ils)
+
+pAddAttributes :: Inlines -> P Inlines
+pAddAttributes (Many ils) = do
+  attr <- mconcat <$> some pAttributes
+  pure $
+    case attr of
+      Attr [] -> Many ils
+      _ -> case Seq.viewr ils of
+             Seq.EmptyR -> mempty
+             ils' Seq.:> Node pos attr' (Str bs)
+               | B8.any isWs bs ->
+               -- attach attribute to last word
+               let (front, lastword) = B8.breakEnd isWs bs
+               in if B.null lastword
+                     then Many ils  -- ignore attr after whitespace
+                     else
+                       let (pos1, pos2) =
+                               case pos of
+                                 NoPos -> (NoPos, NoPos)
+                                 Pos sl sc el ec ->
+                                   let frontlen = B8.length
+                                        (B8.filter (\c -> c < '\128' || c >= '\192') front)
+                                   in (Pos sl sc sl (sc + frontlen),
+                                       Pos sl (sc + frontlen + 1) el ec)
+                       in Many (ils' Seq.|>
+                                     Node pos1 attr' (Str front) Seq.|>
+                                     Node pos2 attr (Str lastword))
+             ils' Seq.:> Node pos attr' il ->
+               Many (ils' Seq.|> Node pos (attr' <> attr) il)
+
+pInline' :: P Inlines
+pInline' = do
+  (do c <- lookahead (satisfyByte isSpecial)
+      fails pCloser
+      (case c of
+          '\\' -> pEscaped
+          '[' -> pFootnoteReference
+             <|> pLinkOrSpan
+          '<' -> pAutolink
+          '!' -> pImage
+          '_' -> pEmph
+          '*' -> pStrong
+          '^' -> pSuperscript
+          '~' -> pSubscript
+          '{' -> pEmph
+             <|> pStrong
+             <|> pHighlight
+             <|> pInsert
+             <|> pDelete
+             <|> pSuperscript
+             <|> pSubscript
+             <|> pDoubleQuote
+             <|> pSingleQuote
+             <|> (mempty <$ pAttributes)
+          '`' -> pVerbatim
+          ':' -> pSymbol
+          '$' -> pMath
+          '"' -> pDoubleQuote
+          '\'' -> pSingleQuote
+          '-' -> pHyphens
+          '.' -> pEllipses
+          '\n' -> pSoftBreak
+          _ -> mzero)
+        <|> pSpecial
+       ) <|> pWords
+
+pSpecial :: P Inlines
+pSpecial = do
+  st <- getState
+  c <- satisfyByte (case mode st of
+                       TableCellMode -> \d -> isSpecial d && d /= '|'
+                       _ -> isSpecial)
+  if c == '\r'
+     then pure mempty
+     else pure $ str $ B8.singleton c
+
+pWords :: P Inlines
+pWords = str <$> byteStringOf (skipSome (skipSatisfyByte (not . isSpecial)))
+
+pEscaped :: P Inlines
+pEscaped = do
+  asciiChar '\\'
+  c <- satisfyByte (\d ->
+          isAscii d &&
+            (isSymbol d || isPunctuation d || d == ' ' || d == '\t'))
+         <|> ('\n' <$ endline)
+         <|> pure '\\'
+  case c of
+    '\n' -> hardBreak <$ skipMany spaceOrTab
+    _ | c == ' ' || c == '\t' -> pHardBreak
+                             <|> if c == ' '
+                                    then pure nonBreakingSpace
+                                    else pure $ str "\\\t"
+    _ -> pure $ str $ B8.singleton c
+
+pHardBreak :: P Inlines
+pHardBreak = do -- assumes we've parsed \ already
+  skipMany spaceOrTab
+  endline
+  skipMany spaceOrTab
+  pure hardBreak
+
+pSoftBreak :: P Inlines
+pSoftBreak = do
+  endline
+  skipMany spaceOrTab
+  (mempty <$ eof) <|> pure softBreak
+
+pSymbol :: P Inlines
+pSymbol = do
+  asciiChar ':'
+  bs <- byteStringOf $ skipSome (skipSatisfyByte
+                                    (\c -> c == '+' || c == '-' ||
+                                         (isAscii c && isAlphaNum c)))
+  asciiChar ':'
+  pure $ symbol bs
+
+pMath :: P Inlines
+pMath = do
+  asciiChar '$'
+  mathStyle <- (DisplayMath <$ asciiChar '$') <|> pure InlineMath
+  verb <- pVerbatim
+  case unMany verb of
+         [Node pos attr (Verbatim bs)] ->
+              pure $ Many $ Seq.singleton $ Node pos attr (Math mathStyle bs)
+         _ -> pure $ (case mathStyle of
+                        DisplayMath -> str "$$"
+                        _ -> str "$") <> verb
+
+{-# INLINE bracesRequired #-}
+bracesRequired :: Char -> Bool
+bracesRequired '=' = True
+bracesRequired '+' = True
+bracesRequired '-' = True
+bracesRequired _ = False
+
+pCloser :: P ()
+pCloser = do
+  delims <- activeDelims <$> getState
+  if Set.null delims
+     then mzero
+     else do
+       openerHadBrace <- asum $
+         map (\(Delim hadBrace c) -> hadBrace <$ asciiChar c) (F.toList delims)
+       mblastc <- peekBack
+       let afterws = maybe True isWs mblastc
+       when ( afterws || openerHadBrace ) $ asciiChar '}'
+
+pEmph, pStrong, pSuperscript, pSubscript :: P Inlines
+pEmph = pBetween '_' emph
+pStrong = pBetween '*' strong
+pSuperscript = pBetween '^' superscript
+pSubscript = pBetween '~' subscript
+
+pHighlight, pInsert, pDelete :: P Inlines
+pHighlight = pBetween '=' highlight
+pInsert = pBetween '+' insert
+pDelete = pBetween '-' delete
+
+pBetween :: Char -> (Inlines -> Inlines) -> P Inlines
+pBetween c constructor = do
+  let starter leftBrace = do
+        case leftBrace of
+          False
+            | bracesRequired c -> mzero
+            | otherwise -> asciiChar c `notFollowedBy`
+                                (ws <|> asciiChar '}')
+          True -> asciiChar c `notFollowedBy` asciiChar '}'
+  let ender leftBrace = do
+        mblastc <- peekBack
+        let afterws = maybe True isWs mblastc
+        asciiChar c
+        if leftBrace
+           then asciiChar '}'
+           else guard (not afterws) `notFollowedBy` asciiChar '}'
+  leftBrace <- (True <$ asciiChar '{') <|> pure False
+  starterBs <- (if leftBrace then ("{" <>) else id) <$>
+                 byteStringOf (starter leftBrace) `notFollowedBy` pAttributes
+                 -- don't let *{.foo} start emphasis, for example
+  oldActiveDelims <- activeDelims <$> getState
+  updateState $ \st -> st{ activeDelims = Set.insert (Delim leftBrace c)
+                                             (activeDelims st) }
+  firstIl <- pInline <|> pBetween c constructor -- to allow stacked cases like '**hi**'
+  restIls <- many pInline
+  let ils = mconcat (firstIl:restIls)
+  updateState $ \st -> st{ activeDelims = oldActiveDelims }
+  (constructor ils <$ ender leftBrace) <|> pure (str starterBs <> ils)
+
+pTicks :: P Int
+pTicks = do
+  sp <- getOffset
+  skipSome (asciiChar '`')
+  ep <- getOffset
+  pure (ep - sp)
+
+pVerbatim :: P Inlines
+pVerbatim = do
+  numticks <- pTicks
+  let ender = pTicks >>= guard . (== numticks)
+  let content = skipSome (skipSatisfyByte (\c -> c /= '`' && c /= '\\')) <|>
+                 (asciiChar '\\' <* anyChar) <|>
+                 (fails ender *> skipSome (asciiChar '`'))
+  bs <- trimSpaces <$> byteStringOf (skipMany content) <* (ender <|> eof)
+  (rawInline <$> pRawAttribute <*> pure bs) <|> pure (verbatim bs)
+
+-- Trim a leading space if first non-space character is `,
+-- and similarly for trailing space/last non-space.
+trimSpaces :: ByteString -> ByteString
+trimSpaces = trimSpaceFront . trimSpaceBack
+ where
+   trimSpaceFront bs =
+        case B8.span (== ' ') bs of
+          (a, b) | B8.take 1 b == "`"
+                 , not (B8.null a)
+            -> B8.drop 1 bs
+          _ -> bs
+   trimSpaceBack bs =
+        case B8.spanEnd (== ' ') bs of
+          (a, b) | B8.takeEnd 1 a == "`"
+                 , not (B8.null b)
+            -> B8.dropEnd 1 bs
+          _ -> bs
+
+pRawAttribute :: P Format
+pRawAttribute = do
+  byteString "{="
+  fmt <- Format <$>
+             byteStringOf (skipMany (skipSatisfyByte (\c -> c /= '}' &&
+                                                 not (isWs c))))
+  asciiChar '}'
+  pure fmt
+
+pFootnoteReference :: P Inlines
+pFootnoteReference = do
+  asciiChar '['
+  asciiChar '^'
+  label <- byteStringOf $ skipMany $
+             skipSatisfyByte (\c -> c /= ']' && not (isWs c))
+  asciiChar ']'
+  pure $ footnoteReference label
+
+-- returns Left with parsed content if no ] has been reached, otherwise Right
+-- with inner contents.
+pBracketed :: P (Either Inlines Inlines)
+pBracketed = do
+  let starter = asciiChar '['
+  let ender = asciiChar ']'
+  starterBs <- byteStringOf starter
+  oldActiveDelims <- activeDelims <$> getState
+  updateState $ \st -> st{ activeDelims = Set.insert (Delim False ']') (activeDelims st) }
+  ils <- mconcat <$> many pInline
+  updateState $ \st -> st{ activeDelims = oldActiveDelims }
+  (Right ils <$ ender) <|> pure (Left (str starterBs <> ils))
+
+pImage :: P Inlines
+pImage = do
+  asciiChar '!'
+  (res, raw) <- withByteString pBracketed
+  case res of
+    Left ils -> pure (str "!" <> ils)
+    Right ils ->
+            ((str "!" <>) <$> pAddAttributes (span_ ils))
+        <|> (image ils <$> (pDestination <|> pReference raw))
+        <|> pure (str "![" <> ils <> str "]")
+
+pAutolink :: P Inlines
+pAutolink = do
+  asciiChar '<'
+  res <- byteStringOf $ skipSome $ skipSatisfyByte (\c -> c /= '>' && c /= '<')
+  asciiChar '>'
+  let url = B8.filter (\c -> c /= '\n' && c /= '\r') res
+  case B8.find (\c -> c == '@' || c == ':' || c == '.') url of
+    Just '@' -> pure $ emailLink url
+    Just _ -> pure $ urlLink url
+    Nothing -> mzero
+
+pLinkOrSpan :: P Inlines
+pLinkOrSpan = do
+  (res, raw) <- withByteString pBracketed
+  case res of
+    Left ils -> pure ils
+    Right ils ->
+            (span_ ils <$ lookahead (asciiChar '{'))
+        <|> (link ils <$> (pDestination <|> pReference raw))
+        <|> pure (str "[" <> ils <> str "]")
+
+-- We allow balanced pairs of parens inside.
+pDestination :: P Target
+pDestination = do
+  asciiChar '('
+  res <- byteStringOf $ pInBalancedParens 0
+  asciiChar ')'
+  pure $ Direct (snd (handleEscapesAndNewlines res))
+ where
+  handleEscapesAndNewlines = B8.foldl' go (False, mempty)
+  go (esc, bs) '\n' = (esc, bs)
+  go (esc, bs) '\r' = (esc, bs)
+  go (True, bs) c = (False, bs `B8.snoc` c)
+  go (False, bs) '\\' = (True, bs)
+  go (False, bs) c = (False, bs `B8.snoc` c)
+
+pInBalancedParens :: Int -> P ()
+pInBalancedParens nestlevel =
+  (guard (nestlevel == 0) <* lookahead (asciiChar ')')) <|>
+    do lev <-   (nestlevel <$ (fails pCloser *>
+                               -- but see https://github.com/jgm/djot/discussions/247
+                               skipSatisfyByte
+                                 (\c -> c /= '(' && c /= ')' && c /= '\\')))
+            <|> (nestlevel <$ (asciiChar '\\' <* anyChar))
+            <|> ((nestlevel + 1) <$ asciiChar '(')
+            <|> ((nestlevel - 1) <$ asciiChar ')')
+       pInBalancedParens lev
+
+pReference :: ByteString -> P Target
+pReference rawDescription = do
+  asciiChar '['
+  bs <- byteStringOf $ pAtMost 400 $ skipSatisfyByte
+           (\c -> c /= '[' && c /= ']')
+  asciiChar ']'
+  let label = normalizeLabel $
+              if B.null bs
+                 then B.drop 1 $ B.dropEnd 1
+                               $ B8.filter (/= '\n') rawDescription
+                 else bs
+  pure $ Reference label
+
+pAtMost :: Int -> P () -> P ()
+pAtMost n pa = optional_ (pa *> when (n > 0) (pAtMost ( n - 1 ) pa))
+
+pOpenDoubleQuote :: P ()
+pOpenDoubleQuote = do
+  lbrace <- (True <$ asciiChar '{') <|> pure False
+  asciiChar '"'
+  rbrace <- (True <$ asciiChar '}') <|> pure False
+  guard $ lbrace || not rbrace
+
+pCloseDoubleQuote :: P ()
+pCloseDoubleQuote = do
+  mblastc <- peekBack
+  let whitespaceBefore = maybe True isWs mblastc
+  lbrace <- (True <$ asciiChar '{') <|> pure False
+  asciiChar '"'
+  rbrace <- (True <$ asciiChar '}') <|> pure False
+  whitespaceAfter <- (True <$ lookahead (skipSatisfyByte isWs)) <|> pure False
+  guard $ not lbrace && (rbrace || not whitespaceBefore || whitespaceAfter)
+
+pDoubleQuote :: P Inlines
+pDoubleQuote = (do
+  pOpenDoubleQuote
+  contents <- mconcat <$> many (fails pCloseDoubleQuote *> pInline)
+  (doubleQuoted contents <$ pCloseDoubleQuote)
+    <|> pure (openDoubleQuote <> contents))
+ <|> (closeDoubleQuote <$ asciiChar '"')
+
+openDoubleQuote, closeDoubleQuote :: Inlines
+openDoubleQuote = str "\226\128\156" -- utf8 0x201C
+closeDoubleQuote = str "\226\128\157" -- utf8 0x201D
+
+pOpenSingleQuote :: P ()
+pOpenSingleQuote = do
+  lastc <- fromMaybe '\n' <$> peekBack
+  let openContext = lastc == '\t' || lastc == '\r' ||
+                    lastc == '\n' || lastc == ' ' ||
+                    lastc == '"' || lastc == '\'' ||
+                    lastc == '(' || lastc == '[' || lastc == '\0'
+  lbrace <- (True <$ asciiChar '{') <|> pure False
+  asciiChar '\''
+  rbrace <- (True <$ asciiChar '}') <|> pure False
+  guard $ lbrace || (openContext && not rbrace)
+
+pCloseSingleQuote :: P ()
+pCloseSingleQuote = do
+  mblastc <- peekBack
+  let whitespaceBefore = maybe True isWs mblastc
+  lbrace <- (True <$ asciiChar '{') <|> pure False
+  asciiChar '\''
+  rbrace <- (True <$ asciiChar '}') <|> pure False
+  letterAfter <- (True <$ lookahead (satisfy isLetter)) <|> pure False
+  guard $ not lbrace && (rbrace || not (whitespaceBefore || letterAfter))
+
+pSingleQuote :: P Inlines
+pSingleQuote = (do
+  pOpenSingleQuote
+  contents <- mconcat <$> many (fails pCloseSingleQuote *> pInline)
+  (singleQuoted contents <$ pCloseSingleQuote)
+    <|> pure (closeSingleQuote <> contents))
+ <|> (closeSingleQuote <$ asciiChar '\'')
+
+closeSingleQuote :: Inlines
+closeSingleQuote = str "\226\128\153" -- utf8 0x2019
+
+pHyphens :: P Inlines
+pHyphens = do
+  numHyphens <- length <$> some hyphen
+  pure $ str $ go numHyphens
+    where
+     emdash = "\226\128\148" -- utf8 0x2014
+     endash = "\226\128\147" -- utf8 0x2013
+     hyphen = asciiChar '-' `notFollowedBy` asciiChar '}'
+     go 1 = "-"
+     go n | n `mod` 3 == 0
+          = mconcat (replicate (n `Prelude.div` 3) emdash)
+          | n `mod` 2 == 0
+          = mconcat (replicate (n `Prelude.div` 2) endash)
+          | n `mod` 3 == 2
+          = mconcat (replicate (n `Prelude.div` 3) emdash) <> endash
+          | n `mod` 3 == 1
+          = mconcat (replicate (n `Prelude.div` 3 - 1) emdash) <>
+              endash <> endash
+          | otherwise
+          = emdash <> go (n - 3)
+
+pEllipses :: P Inlines
+pEllipses = str "\226\128\166" {- utf8 0x2026 -} <$ byteString "..."
+
+stripEndChunks :: Seq Chunk -> Seq Chunk
+stripEndChunks cs =
+  case Seq.viewr cs of
+    initial Seq.:> c ->
+      initial Seq.|> c{ chunkBytes = B8.dropWhileEnd isWs (chunkBytes c) }
+    _ -> cs
diff --git a/src/Djot/Options.hs b/src/Djot/Options.hs
new file mode 100644
--- /dev/null
+++ b/src/Djot/Options.hs
@@ -0,0 +1,25 @@
+{-# LANGUAGE StrictData #-}
+module Djot.Options
+( ParseOptions(..)
+, RenderOptions(..)
+, SourcePosOption(..) )
+where
+
+newtype ParseOptions =
+  ParseOptions
+  { sourcePositions :: SourcePosOption -- ^ Add attributes for source lines
+  }
+  deriving (Show)
+
+newtype RenderOptions =
+  RenderOptions
+  { preserveSoftBreaks :: Bool -- ^ Preserve soft breaks as in the source
+  }
+  deriving (Show)
+
+-- | Adding source positions for blocks adds almost no overhead to parsing.
+-- Adding source positions for inlines has a small penalty.  For many purposes
+-- it is enough to have source lines for blocks, so we offer the option.
+data SourcePosOption =
+  NoSourcePos | BlockSourcePos | AllSourcePos
+  deriving (Show, Eq, Ord)
diff --git a/src/Djot/Parse.hs b/src/Djot/Parse.hs
new file mode 100644
--- /dev/null
+++ b/src/Djot/Parse.hs
@@ -0,0 +1,400 @@
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE BinaryLiterals #-}
+module Djot.Parse
+(   Parser
+  , Chunk(..)
+  , parse
+  , asciiChar
+  , satisfyByte
+  , skipSatisfyByte
+  , satisfy
+  , anyChar
+  , skipMany
+  , skipSome
+  , eof
+  , getState
+  , updateState
+  , lookahead
+  , peek
+  , peekBack
+  , fails
+  , failed
+  , withByteString
+  , byteStringOf
+  , notFollowedBy
+  , optional_
+  , byteString
+  , getOffset
+  , sourceLine
+  , sourceColumn
+  , branch
+  , endline
+  , restOfLine
+  , ws
+  , followedByWhitespace
+  , followedByBlankLine
+  , spaceOrTab
+  , isWs
+  , strToUtf8
+  , utf8ToStr
+)
+where
+
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Char8 as B8
+import Data.ByteString (ByteString)
+import Control.Applicative
+import Control.Monad (void, MonadPlus(..))
+import Data.Char (chr)
+import Data.Bits
+import Data.Maybe (fromMaybe)
+import Data.Text.Encoding (decodeUtf8With, encodeUtf8)
+import Data.Text.Encoding.Error (lenientDecode)
+import qualified Data.Text as T
+-- import Text.Printf
+-- import Debug.Trace
+
+newtype Parser s a =
+  Parser{ runParser :: ParserState s -> Maybe (ParserState s, a) }
+
+instance Functor (Parser s) where
+  fmap f g = Parser $ \s -> case runParser g s of
+                                 Nothing -> Nothing
+                                 Just (s', !x) -> Just (s', f x)
+
+instance Applicative (Parser s) where
+  pure x = Parser (\s -> Just (s, x))
+  liftA2 f g h = Parser $ \s ->
+    case runParser g s of
+      Nothing -> Nothing
+      Just (s', x) ->
+        case runParser h s' of
+          Nothing -> Nothing
+          Just (s'', y) -> Just (s'', f x y)
+
+instance Monad (Parser s) where
+  return = pure
+  f >>= g = Parser $ \s ->
+    case runParser f s of
+      Nothing -> Nothing
+      Just (s', x) -> runParser (g x) s'
+
+instance Alternative (Parser s) where
+  empty = Parser (const Nothing)
+  f <|> g = Parser $ \s ->
+    case runParser f s of
+      Just (s', x) -> Just (s', x)
+      Nothing -> runParser g s
+
+instance MonadPlus (Parser s) where
+  mzero = empty
+  mplus = (<|>)
+
+data Chunk =
+  Chunk{ chunkLine :: Int
+       , chunkColumn :: Int
+       , chunkBytes :: ByteString }
+  deriving (Show, Eq, Ord)
+
+
+data ParserState a =
+  ParserState
+  { chunks :: [Chunk]
+  , subject :: !ByteString
+  , offset :: !Int
+  , line :: !Int
+  , column :: !Int
+  , userState :: !a
+  }
+  deriving (Show)
+
+-- | Apply a parser to a bytestring with a given user state.
+-- Returns @Nothing@ on failure, @Just result@ on success.
+parse :: Parser s a -> s -> [Chunk] -> Maybe a
+parse parser ustate chunks'' =
+  snd <$>
+    runParser parser ParserState { chunks = chunks'
+                                 , subject = bs
+                                 , offset = 0
+                                 , line = startline
+                                 , column = startcol
+                                 , userState = ustate }
+
+ where
+   (chunks', bs, startline, startcol) =
+     case chunks'' of
+       [] -> ([], mempty, 1, 0)
+       (c:cs) -> (cs, chunkBytes c, chunkLine c, chunkColumn c)
+
+-- | Given a number of bytes, advances the offset and updates line/column.
+unsafeAdvance :: Int -> ParserState s -> ParserState s
+unsafeAdvance 0 = id
+unsafeAdvance !n = unsafeAdvance (n - 1) . unsafeAdvanceByte
+
+-- | Advance the offset and line/column for consuming a given byte.
+unsafeAdvanceByte :: ParserState s -> ParserState s
+unsafeAdvanceByte st
+  | offset st + 1 >= B.length (subject st)
+  , c:cs <- chunks st
+   = st{ chunks = cs
+       , subject = chunkBytes c
+       , offset = 0
+       , line = chunkLine c
+       , column = chunkColumn c }
+  | otherwise
+     = case B.index (subject st) (offset st) of
+         10 -> st{ offset = offset st + 1
+                 , line = line st + 1
+                 , column = 1 }
+         9 -> st{ offset = offset st + 1
+                , column = column st + (4 - (column st `mod` 4)) }
+         !w | w < 0x80 -> st{ offset = offset st + 1
+                            , column = column st + 1 }
+            -- utf8 multibyte: only count byte 1:
+            | w >= 0b11000000 -> st{ offset = offset st + 1
+                                   , column = column st + 1 }
+            | otherwise -> st{ offset = offset st + 1 }
+
+-- | Returns current byte as Char.
+current :: ParserState s -> Maybe Char
+current st = subject st B8.!? offset st
+
+-- | Returns current byte as Char.
+peek :: Parser s (Maybe Char)
+peek = Parser $ \st -> Just (st, current st)
+
+-- | Returns previous byte as Char.  Doesn't cross chunk boundaries.
+peekBack :: Parser s (Maybe Char)
+peekBack = Parser $ \st -> Just (st, subject st B8.!? (offset st - 1))
+
+-- | Parse a byte satisfying a predicate.
+satisfyByte :: (Char -> Bool) -> Parser s Char
+satisfyByte f = Parser $ \st ->
+  case current st of
+    Just c | f c -> Just (unsafeAdvanceByte st, c)
+    _ -> Nothing
+
+-- | Skip byte satisfying a predicate.
+skipSatisfyByte :: (Char -> Bool) -> Parser s ()
+skipSatisfyByte f = Parser $ \st ->
+  case current st of
+    Just c | f c -> Just (unsafeAdvanceByte st, ())
+    _ -> Nothing
+
+-- | Parse a (possibly multibyte) Char satisfying a predicate.
+-- Assumes UTF-8 encoding.
+satisfy :: (Char -> Bool) -> Parser s Char
+satisfy f = Parser $ \st ->
+  let peekWord !n = subject st B.!? (offset st + n)
+      b2 = fromMaybe 0 $ peekWord 1
+      b3 = fromMaybe 0 $ peekWord 2
+      b4 = fromMaybe 0 $ peekWord 3
+  in case peekWord 0 of
+    Nothing -> Nothing
+    Just b1
+      | b1 < 0b10000000
+      , !c <- chr (fromIntegral b1)
+      , f c -> Just (unsafeAdvanceByte st, c)
+      | b1 .&. 0b11100000 == 0b11000000
+      , b2 >= 0b10000000
+      , !c <- chr (toCodePoint2 b1 b2)
+      , f c -> Just (unsafeAdvance 2 st, c)
+      | b1 .&. 0b11110000 == 0b11100000
+      , b2 >= 0b10000000
+      , b3 >= 0b10000000
+      , !c <- chr (toCodePoint3 b1 b2 b3)
+      , f c -> Just (unsafeAdvance 3 st, c)
+      | b1 .&. 0b11111000 == 0b11110000
+      , b2 >= 0b10000000
+      , b3 >= 0b10000000
+      , b4 >= 0b10000000
+      , !c <- chr (toCodePoint4 b1 b2 b3 b4)
+      , f c -> Just (unsafeAdvance 4 st, c)
+      | otherwise -> Nothing
+ where
+  toCodePoint2 a b =
+    (fromIntegral (a .&. 0b00011111) `shiftL` 6) +
+     fromIntegral (b .&. 0b00111111)
+  toCodePoint3 a b c =
+    (fromIntegral (a .&. 0b00001111) `shiftL` 12) +
+    (fromIntegral (b .&. 0b00111111) `shiftL` 6) +
+     fromIntegral (c .&. 0b00111111)
+  toCodePoint4 a b c d =
+    (fromIntegral (a .&. 0b00000111) `shiftL` 18) +
+    (fromIntegral (b .&. 0b00111111) `shiftL` 12) +
+    (fromIntegral (c .&. 0b00111111) `shiftL` 6) +
+     fromIntegral (d .&. 0b00111111)
+
+-- | Parse any character. Assumes UTF-8 encoding.
+anyChar :: Parser s Char
+anyChar = satisfy (const True)
+
+-- | Parse an ASCII character.
+asciiChar :: Char -> Parser s ()
+asciiChar !c = Parser $ \st ->
+  case current st of
+    Just d | d == c -> Just (unsafeAdvanceByte st, ())
+    _ -> Nothing
+
+-- | Apply parser 0 or more times, discarding result.
+skipMany :: Parser s a -> Parser s ()
+skipMany parser = Parser go
+ where
+   go st = case runParser parser st of
+             Nothing -> Just (st, ())
+             Just (st',_) -> go st'
+
+-- | Apply parser 1 or more times, discarding result.
+skipSome :: Parser s a -> Parser s ()
+skipSome parser = parser *> skipMany parser
+
+-- | Succeeds if no more input.
+eof :: Parser s ()
+eof = Parser $ \st ->
+  case current st of
+    Nothing -> Just (st, ())
+    Just _ -> Nothing
+
+-- | Returns current user state.
+getState :: Parser s s
+getState = Parser $ \st -> Just (st, userState st)
+
+-- | Updates user state.
+updateState :: (s -> s) -> Parser s ()
+updateState f = Parser $ \st ->
+  Just (st{ userState = f (userState st) }, ())
+
+-- | Apply a parser, returning its result but not changing state
+-- or advancing.
+lookahead :: Parser s a -> Parser s a
+lookahead pa = Parser $ \st ->
+  case runParser pa st of
+    Just (_, x) -> Just (st, x)
+    Nothing -> Nothing
+
+-- | Succeeds if parser fails.
+fails :: Parser s a -> Parser s ()
+fails pa = Parser $ \st ->
+  case runParser pa st of
+    Just _ -> Nothing
+    Nothing -> Just (st, ())
+
+-- | Always fails.
+failed :: Parser s a
+failed = Parser $ const Nothing
+
+-- | Returns result of parse together with the bytestring
+-- consumed.
+withByteString :: Parser s a -> Parser s (a, ByteString)
+withByteString pa = Parser $ \st ->
+  case runParser pa st of
+    Just (st', x) -> Just (st', (x, B8.take (offset st' - offset st)
+                                    (B8.drop (offset st) (subject st))))
+    Nothing -> Nothing
+
+-- | Returns bytestring consumed by parse.
+byteStringOf :: Parser s a -> Parser s ByteString
+byteStringOf pa = Parser $ \st ->
+  case runParser pa st of
+    Just (st', _) -> Just (st',
+       case length (chunks st) - length (chunks st') of
+         0 -> B8.take (offset st' - offset st) (B8.drop (offset st) (subject st))
+         n ->
+           B8.drop (offset st) (subject st) <>
+            foldMap chunkBytes (take (n - 1) (chunks st)) <>
+            B8.take (offset st') (subject st'))
+    Nothing -> Nothing
+
+-- | Succeeds if first parser succeeds and second fails, returning
+-- first parser's value.
+notFollowedBy :: Parser s a -> Parser s b -> Parser s a
+notFollowedBy pa pb = pa <* fails pb
+
+-- | Apply parser but still succeed if it doesn't succeed.
+optional_ :: Parser s a -> Parser s ()
+optional_ pa = void pa <|> pure ()
+
+-- | Parse a bytestring.
+byteString :: ByteString -> Parser s ()
+byteString bs = Parser $ \st ->
+  if bs `B8.isPrefixOf` B8.drop (offset st) (subject st)
+     then Just (unsafeAdvance (B.length bs) st, ())
+     else Nothing
+
+-- | Returns byte offset in input.
+getOffset :: Parser s Int
+getOffset = Parser $ \st -> Just (st, offset st)
+
+-- | Returns the line number.
+sourceLine :: Parser s Int
+sourceLine = Parser $ \st -> Just (st, line st)
+
+-- | Returns the source column number. (Tab stop is computed at 4.)
+sourceColumn :: Parser st Int
+sourceColumn = Parser $ \st -> Just (st, column st)
+
+-- | Try the first parser: if it succeeds, apply the second,
+-- returning its result, otherwise the third.
+branch :: Parser s b -> Parser s a -> Parser s a -> Parser s a
+branch pa pb pc = Parser $ \st ->
+  case runParser pa st of
+    Just (st',_) -> runParser pb st'
+    Nothing -> runParser pc st
+
+-- | Parse an end of line sequence.
+endline :: Parser s ()
+endline = branch (asciiChar '\r') (optional_ (asciiChar '\n')) (asciiChar '\n')
+
+-- | Return the rest of line (including the end of line).
+restOfLine :: Parser s ByteString
+restOfLine =
+  byteStringOf $
+    skipMany (skipSatisfyByte (\c -> c /= '\n' && c /= '\r'))
+      <* optional_ endline
+
+{-# INLINE isWs #-}
+-- | Is space, tab, `\r`, or `\n`.
+isWs :: Char -> Bool
+isWs c = c == ' ' || c == '\t' || c == '\r' || c == '\n'
+
+-- | Skip one space or tab.
+spaceOrTab :: Parser s ()
+spaceOrTab = Parser $ \st ->
+  case current st of
+    Just ' ' -> Just (unsafeAdvanceByte st, ())
+    Just '\t' -> Just (unsafeAdvanceByte st, ())
+    _ -> Nothing
+
+-- | Skip 1 or more ASCII whitespace.
+ws :: Parser s ()
+ws = skipSome (satisfyByte isWs)
+
+-- | Next character is ASCII whitespace.
+followedByWhitespace :: Parser s ()
+followedByWhitespace = Parser $ \st ->
+  case current st of
+    Just c | isWs c -> Just (st, ())
+    _ -> Nothing
+
+-- | Followed by 0 or more spaces/tabs and endline or eof.
+followedByBlankLine :: Parser s ()
+followedByBlankLine = Parser $ \st ->
+  let subj = subject st
+      !len = B8.length subj
+      go !off
+        | off >= len
+          = Just (st, ())
+        | otherwise
+          = case B8.index subj off of
+              ' ' -> go (off + 1)
+              '\r' -> go (off + 1)
+              '\t' -> go (off + 1)
+              '\n' -> Just (st, ())
+              _ -> Nothing
+  in go (offset st)
+
+strToUtf8 :: String -> ByteString
+strToUtf8 = encodeUtf8 . T.pack
+
+utf8ToStr :: ByteString -> String
+utf8ToStr = T.unpack . decodeUtf8With lenientDecode
diff --git a/test/Main.hs b/test/Main.hs
new file mode 100644
--- /dev/null
+++ b/test/Main.hs
@@ -0,0 +1,163 @@
+{-# LANGUAGE TupleSections       #-}
+{-# LANGUAGE OverloadedStrings   #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+import Test.Tasty
+import Test.Tasty.QuickCheck
+import Test.Tasty.HUnit
+import qualified Data.Text.Lazy as TL
+import Data.Text.Lazy.Encoding (decodeUtf8With, encodeUtf8)
+import Data.Text.Encoding.Error (lenientDecode)
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Lazy.Char8 as BL
+import Data.ByteString.Builder ( toLazyByteString )
+import Djot ( ParseOptions(..), RenderOptions(..), SourcePosOption(..),
+              parseDoc, renderHtml, renderDjot )
+import Djot.Parse ( parse, satisfy, strToUtf8, utf8ToStr, Chunk(..) )
+import Djot.AST
+import System.FilePath ((</>), takeExtension, takeFileName)
+import System.Directory (getDirectoryContents)
+import Text.DocLayout (render)
+
+main :: IO ()
+main = do
+  specTests <- filter ((== ".test") . takeExtension) <$>
+                  getDirectoryContents "test"
+  tests <- mapM (\fp -> (fp,) <$> getSpecTests ("test" </> fp)) specTests
+  let parser = parseDoc ParseOptions{ sourcePositions = NoSourcePos } . BL.toStrict
+  defaultMain $ testGroup "Tests" $
+    [ testGroup "djot -> html"
+        (map (\(fp, ts) ->
+                testGroup fp
+                 (map (toSpecTest parser) ts)) tests)
+    , testGroup "native -> djot -> native"
+       [testGroup fp (map (toRoundTripTest parser) ts)
+          | (fp, ts) <- tests
+          , takeFileName fp /= "raw.test"]
+    , testGroup "Djot.Parse" parserTests
+    , testGroup "sourcepos" sourcePosTests
+    , testGroup "Fuzz"
+       [testProperty "parses all inputs"
+         (\s -> case parseDoc ParseOptions{ sourcePositions = NoSourcePos }
+                 (strToUtf8 s) of
+                    Left _ -> False
+                    Right _ -> True)
+       ]
+    ]
+
+parserTests :: [TestTree]
+parserTests =
+  [ testCase "satisfy multibyte"
+      (parse (satisfy (=='ǎ') *> satisfy (=='老')) ()
+         (toChunks $ strToUtf8 "ǎ老bc") @?= Just '老')
+  , testProperty "UTF8 conversion round-trips"
+      (\s -> utf8ToStr (strToUtf8 s) == s)
+  ]
+
+sourcePosTests :: [TestTree]
+sourcePosTests =
+  let convert = either mempty (fromUtf8 . toLazyByteString .
+                      renderHtml RenderOptions{ preserveSoftBreaks = True })
+                 . parseDoc ParseOptions{ sourcePositions = AllSourcePos }
+  in [ testCase "period at end" $
+        convert "the `goo` option.\n" @?=
+        "<p data-pos=\"1:1-1:17\"><span data-pos=\"1:1-1:4\">the </span><code data-pos=\"1:5-1:9\">goo</code><span data-pos=\"1:10-1:17\"> option.</span></p>\n"
+     , testCase "attr after *" $
+        convert "*{.foo}\n" @?=
+        "<p data-pos=\"1:1-1:7\"><span data-pos=\"1:1-1:1\" class=\"foo\">*</span></p>\n"
+     , testCase "no newline at end" $
+        convert "foo" @?=
+        "<p data-pos=\"1:1-1:3\"><span data-pos=\"1:1-1:3\">foo</span></p>\n"
+     , testCase "list" $
+        convert "1. > hello\nthere\n\n2.  ok" @?=
+        "<ol data-pos=\"1:1-4:6\">\n<li>\n<blockquote data-pos=\"1:4-2:5\">\n<p data-pos=\"1:6-2:5\"><span data-pos=\"1:6-1:10\">hello</span>\n<span data-pos=\"2:1-2:5\">there</span></p>\n</blockquote>\n</li>\n<li>\n<p data-pos=\"4:5-4:6\"><span data-pos=\"4:5-4:6\">ok</span></p>\n</li>\n</ol>\n"
+     , testCase "code block" $
+        convert "``` ruby\nhi\n```\n" @?=
+        "<pre data-pos=\"1:1-3:3\"><code class=\"language-ruby\">hi\n</code></pre>\n"
+     , testCase "nested " $
+        convert "*_hi_*" @?=
+        "<p data-pos=\"1:1-1:6\"><strong data-pos=\"1:1-1:6\"><em data-pos=\"1:2-1:5\"><span data-pos=\"1:3-1:4\">hi</span></em></strong></p>\n"
+     , testCase "hr " $
+        convert "----\n" @?=
+        "<hr data-pos=\"1:1-1:4\">\n"
+     ]
+
+toChunks :: B.ByteString -> [Chunk]
+toChunks bs = [Chunk{ chunkBytes = bs, chunkLine = 1, chunkColumn = 0 }]
+
+toSpecTest :: (BL.ByteString -> Either String Doc)
+           -> SpecTest -> TestTree
+toSpecTest parser st =
+  testCase name (actual @?= expected)
+    where name = "lines " ++ show (start_line st) ++ "-" ++ show (end_line st)
+          expected = fromUtf8 $ html st
+          ropts = RenderOptions{ preserveSoftBreaks = True }
+          actual = either mempty (fromUtf8 . toLazyByteString . renderHtml ropts)
+                     . parser $ djot st
+
+toRoundTripTest :: (BL.ByteString -> Either String Doc)
+                -> SpecTest -> TestTree
+toRoundTripTest parser st =
+  testCase name ((actual == expected) @? rtlog)
+    where name = "lines " ++ show (start_line st) ++ "-" ++ show (end_line st)
+          native = either (\_ -> mempty) id $ parser (djot st)
+          expected = native
+          ropts = RenderOptions{ preserveSoftBreaks = True }
+          renderedDjot = encodeUtf8 . TL.fromStrict $ render (Just 62) $
+                           renderDjot ropts native
+          actual = either (\_ -> mempty) id $ parser renderedDjot
+          lbsToStr = TL.unpack . fromUtf8
+          rtlog = lbsToStr (djot st) <>
+                  "↓\n" <>
+                  show native <> "\n" <>
+                  "↓\n" <>
+                  lbsToStr renderedDjot <>
+                  "↓\n" <>
+                  show actual <> "\n"
+
+data SpecTest = SpecTest
+     { djot       :: BL.ByteString
+     , source     :: FilePath
+     , end_line   :: Int
+     , start_line :: Int
+     , html       :: BL.ByteString }
+  deriving (Show)
+
+getSpecTests :: FilePath -> IO [SpecTest]
+getSpecTests fp = do
+  speclines <- zip [1..] . BL.lines <$> BL.readFile fp
+  pure $ parseSpecTests fp speclines
+
+--- state machine parser for spec test cases
+
+data ParseState =
+     Scanning
+   | ParsingDjot (SpecTest, BL.ByteString)
+   | ParsingHtml (SpecTest, BL.ByteString)
+   deriving (Show)
+
+parseSpecTests :: FilePath -> [(Int, BL.ByteString)] -> [SpecTest]
+parseSpecTests fp = go Scanning
+ where
+   go _ [] = []
+   go Scanning ((ln, bs) : xs)
+     | BL.length bs > 0 && BL.all (== '`') bs =
+          go (ParsingDjot (SpecTest { djot = mempty
+                                    , source = fp
+                                    , end_line = ln
+                                    , start_line = ln
+                                    , html = mempty }, bs)) xs
+     | otherwise = go Scanning xs
+   go (ParsingDjot (st,fence)) ((_,bs) : xs)
+     | bs == "." =
+          go (ParsingHtml (st, fence)) xs
+     | otherwise =
+          go (ParsingDjot (st{ djot = djot st <> bs <> "\n" }, fence)) xs
+   go (ParsingHtml (st,fence)) ((ln,bs) : xs)
+     | bs == fence =
+          st{ end_line = ln } : go Scanning xs
+     | otherwise =
+          go (ParsingHtml (st{ html = html st <> bs <> "\n" }, fence)) xs
+
+fromUtf8 :: BL.ByteString -> TL.Text
+fromUtf8 = decodeUtf8With lenientDecode
diff --git a/test/attributes.test b/test/attributes.test
new file mode 100644
--- /dev/null
+++ b/test/attributes.test
@@ -0,0 +1,322 @@
+An inline attribute attaches to the preceding element, which might
+be complex (span, emphasis, link) or a simple word (defined as a
+sequence of non-ASCII-whitespace characters).
+```
+foo привет{.ru}
+.
+<p>foo <span class="ru">привет</span></p>
+```
+
+```
+(some text){.attr}
+.
+<p>(some <span class="attr">text)</span></p>
+```
+
+```
+[some text]{.attr}
+.
+<p><span class="attr">some text</span></p>
+```
+
+Ensure that emphasis that starts before the attribute can still close,
+even if the attribute contains a potential closer.
+
+```
+a *b{#id key="*"}*
+.
+<p>a <strong><span id="id" key="*">b</span></strong></p>
+```
+
+```
+a *b{#id key="*"}o
+.
+<p>a <span id="id" key="*">*b</span>o</p>
+```
+
+Don't mind braces in quotes:
+
+```
+hi{key="{#hi"}
+.
+<p><span key="{#hi">hi</span></p>
+```
+
+Process escapes correctly:
+
+```
+hi{key="\\\\\*"}
+
+{key="\\\\\*"}
+foo
+.
+<p><span key="\\*">hi</span></p>
+<p key="\\*">foo</p>
+```
+
+Don't allow attributes to start when we're parsing a potential
+attribute.
+
+```
+hi\{key="abc{#hi}"
+.
+<p>hi{key=“<span id="hi">abc</span>”</p>
+```
+
+```
+hi{key="\"#hi"}
+.
+<p><span key="&quot;#hi">hi</span></p>
+```
+
+```
+hi{key="hi\"#hi"}
+.
+<p><span key="hi&quot;#hi">hi</span></p>
+```
+
+Line break:
+
+```
+hi{#id .class
+key="value"}
+.
+<p><span id="id" class="class" key="value">hi</span></p>
+```
+
+Here there is nothing for the attribute to attach to:
+
+```
+{#id} at beginning
+.
+<p> at beginning</p>
+```
+
+```
+After {#id} space
+{.class}
+.
+<p>After  space
+</p>
+```
+
+Block attributes come before the block, on a line by themselves.
+
+```
+{#id .class}
+A paragraph
+.
+<p id="id" class="class">A paragraph</p>
+```
+
+Use indentation if you need to continue the attributes over a line break.
+
+```
+{#id .class
+  style="color:red"}
+A paragraph
+.
+<p id="id" class="class" style="color:red">A paragraph</p>
+```
+
+If the attribute block can't be parsed as attributes, it will be
+parsed as a regular paragraph:
+
+```
+{#id .cla*ss*
+.
+<p>{#id .cla<strong>ss</strong></p>
+```
+
+You can use consecutive attribute blocks.
+In case of conflict, later values take precedence over earlier ones,
+but classes accumulate:
+
+```
+{#id}
+{key=val}
+{.foo .bar}
+{key=val2}
+{.baz}
+{#id2}
+Okay
+.
+<p class="foo bar baz" key="val2" id="id2">Okay</p>
+```
+
+Attributes on different kinds of blocks:
+
+```
+{#id}
+> Block quote
+.
+<blockquote id="id">
+<p>Block quote</p>
+</blockquote>
+```
+
+```
+{#id}
+# Heading
+.
+<section id="id">
+<h1>Heading</h1>
+</section>
+```
+
+```
+{.blue}
+- - - - -
+.
+<hr class="blue">
+```
+
+````
+{highlight=3}
+``` ruby
+x = 3
+```
+.
+<pre highlight="3"><code class="language-ruby">x = 3
+</code></pre>
+````
+
+```
+{.special}
+1. one
+2. two
+.
+<ol class="special">
+<li>
+one
+</li>
+<li>
+two
+</li>
+</ol>
+```
+
+```
+> {.foo}
+> > {.bar}
+> > nested
+.
+<blockquote>
+<blockquote class="foo">
+<p class="bar">nested</p>
+</blockquote>
+</blockquote>
+```
+
+Comments start at a `%` character
+(not in quotes) and end with another `%` or
+the end of the attribute (`}`).
+These can be used to comment up an attribute
+list or without any real attributes.
+
+```
+foo{#ident % this is a comment % .class}
+.
+<p><span id="ident" class="class">foo</span></p>
+```
+
+```
+foo{#ident % this is a comment}
+.
+<p><span id="ident">foo</span></p>
+```
+
+In block-level comment, subsequent lines must
+be indented, as with attributes:
+
+```
+{% This is  a comment before a
+  block-level item. %}
+Paragraph.
+.
+<p>Paragraph.</p>
+```
+
+Inline attributes can be empty:
+
+```
+hi{}
+.
+<p>hi</p>
+```
+
+Block attributes can be empty:
+
+```
+{}
+hi
+.
+<p>hi</p>
+```
+
+Non-attributes:
+
+```
+text{a=x
+
+hello
+.
+<p>text{a=x</p>
+<p>hello</p>
+```
+
+skip ```
+skip {a=x
+skip hello
+skip .
+skip <p>{a=x
+skip hello</p>
+skip ```
+
+```
+text{a=x
+# non-heading
+.
+<p>text{a=x
+# non-heading</p>
+```
+
+skip ```
+skip {a=x
+skip # non-heading
+skip .
+skip <p>{a=x
+skip # non-heading</p>
+skip ```
+
+```
+{a=" inline text
+.
+<p>{a=“ inline text</p>
+```
+
+
+```
+{
+ attr="long
+ value
+ spanning
+ multiple
+ lines"
+ }
+> a
+.
+<blockquote attr="long value spanning multiple lines">
+<p>a</p>
+</blockquote>
+```
+
+```
+> {key="bar
+>    a\$bim"}
+> ou
+.
+<blockquote>
+<p key="bar a$bim">ou</p>
+</blockquote>
+```
diff --git a/test/block_quote.test b/test/block_quote.test
new file mode 100644
--- /dev/null
+++ b/test/block_quote.test
@@ -0,0 +1,154 @@
+```
+> Basic
+> block _quote_.
+.
+<blockquote>
+<p>Basic
+block <em>quote</em>.</p>
+</blockquote>
+```
+
+```
+> Lazy
+block _quote_.
+.
+<blockquote>
+<p>Lazy
+block <em>quote</em>.</p>
+</blockquote>
+```
+
+```
+> block
+>
+> quote
+.
+<blockquote>
+<p>block</p>
+<p>quote</p>
+</blockquote>
+```
+
+```
+> block
+
+> quote
+.
+<blockquote>
+<p>block</p>
+</blockquote>
+<blockquote>
+<p>quote</p>
+</blockquote>
+```
+
+```
+> > > nested
+.
+<blockquote>
+<blockquote>
+<blockquote>
+<p>nested</p>
+</blockquote>
+</blockquote>
+</blockquote>
+```
+
+```
+> > > nested
+lazy
+.
+<blockquote>
+<blockquote>
+<blockquote>
+<p>nested
+lazy</p>
+</blockquote>
+</blockquote>
+</blockquote>
+```
+
+```
+> > > nested
+> lazy
+.
+<blockquote>
+<blockquote>
+<blockquote>
+<p>nested
+lazy</p>
+</blockquote>
+</blockquote>
+</blockquote>
+```
+
+```
+> nested
+>
+> > more
+.
+<blockquote>
+<p>nested</p>
+<blockquote>
+<p>more</p>
+</blockquote>
+</blockquote>
+```
+
+```
+>not blockquote
+.
+<p>&gt;not blockquote</p>
+```
+
+```
+>> not blockquote
+.
+<p>&gt;&gt; not blockquote</p>
+```
+
+```
+>
+.
+<blockquote>
+</blockquote>
+```
+
+```
+> # Heading
+.
+<blockquote>
+<section id="Heading">
+<h1>Heading</h1>
+</section>
+</blockquote>
+```
+
+```
+> hi
+>there
+.
+<blockquote>
+<p>hi
+&gt;there</p>
+</blockquote>
+```
+
+```
+aaa
+> bbb
+.
+<p>aaa
+&gt; bbb</p>
+```
+
+```
+aaa
+
+> bbb
+.
+<p>aaa</p>
+<blockquote>
+<p>bbb</p>
+</blockquote>
+```
diff --git a/test/code_blocks.test b/test/code_blocks.test
new file mode 100644
--- /dev/null
+++ b/test/code_blocks.test
@@ -0,0 +1,65 @@
+```
+```
+code
+  block
+```
+.
+<pre><code>code
+  block
+</code></pre>
+```
+
+````
+``` python
+x = y + 3
+```
+.
+<pre><code class="language-python">x = y + 3
+</code></pre>
+````
+
+````
+  ``` python
+  if true:
+    x = 3
+  ```
+.
+<pre><code class="language-python">if true:
+  x = 3
+</code></pre>
+````
+
+````
+``` not a code block ```
+.
+<p><code> not a code block </code></p>
+````
+
+````
+``` not a code block
+.
+<p><code> not a code block</code></p>
+````
+
+````
+```
+hi
+```
+```
+two
+```
+.
+<pre><code>hi
+</code></pre>
+<pre><code>two
+</code></pre>
+````
+
+Empty code block:
+
+````
+```
+```
+.
+<pre><code></code></pre>
+````
diff --git a/test/definition_lists.test b/test/definition_lists.test
new file mode 100644
--- /dev/null
+++ b/test/definition_lists.test
@@ -0,0 +1,95 @@
+Definition lists are just like ordinary bullet lists, but with
+`:` as the marker instead of `-`, `+`, or `*`.  The first
+paragraph of the list item is interpreted as the term, and
+the rest as the definition.
+
+```
+: apple
+
+  red fruit
+: banana
+
+  yellow fruit
+.
+<dl>
+<dt>apple</dt>
+<dd>
+red fruit
+</dd>
+<dt>banana</dt>
+<dd>
+yellow fruit
+</dd>
+</dl>
+```
+
+Loose:
+
+```
+: apple
+
+  red fruit
+
+: banana
+
+  yellow fruit
+.
+<dl>
+<dt>apple</dt>
+<dd>
+<p>red fruit</p>
+</dd>
+<dt>banana</dt>
+<dd>
+<p>yellow fruit</p>
+</dd>
+</dl>
+```
+
+```
+: apple
+ fruit
+
+  Paragraph one
+
+  Paragraph two
+
+  - sub
+  - list
+
+: orange
+.
+<dl>
+<dt>apple
+fruit</dt>
+<dd>
+<p>Paragraph one</p>
+<p>Paragraph two</p>
+<ul>
+<li>
+sub
+</li>
+<li>
+list
+</li>
+</ul>
+</dd>
+<dt>orange</dt>
+<dd>
+</dd>
+</dl>
+```
+
+````
+: ```
+  ok
+  ```
+.
+<dl>
+<dt></dt>
+<dd>
+<pre><code>ok
+</code></pre>
+</dd>
+</dl>
+````
diff --git a/test/emphasis.test b/test/emphasis.test
new file mode 100644
--- /dev/null
+++ b/test/emphasis.test
@@ -0,0 +1,239 @@
+```
+*foo bar*
+.
+<p><strong>foo bar</strong></p>
+```
+
+```
+a* foo bar*
+.
+<p>a* foo bar*</p>
+```
+
+```
+*foo bar *
+.
+<p>*foo bar *</p>
+```
+
+Unicode spaces don't block emphasis.
+
+```
+* a *
+.
+<p><strong> a </strong></p>
+```
+
+Intraword:
+
+```
+foo*bar*baz
+.
+<p>foo<strong>bar</strong>baz</p>
+```
+
+```
+_foo bar_
+.
+<p><em>foo bar</em></p>
+```
+
+```
+_ foo bar_
+.
+<p>_ foo bar_</p>
+```
+
+```
+_foo bar _
+.
+<p>_foo bar _</p>
+```
+
+Unicode spaces don't block emphasis.
+
+```
+_ a _
+.
+<p><em> a </em></p>
+```
+
+Intraword:
+
+```
+foo_bar_baz
+.
+<p>foo<em>bar</em>baz</p>
+```
+
+```
+aa_"bb"_cc
+.
+<p>aa<em>“bb”</em>cc</p>
+```
+
+```
+*foo_
+.
+<p>*foo_</p>
+```
+
+```
+_foo*
+.
+<p>_foo*</p>
+```
+
+A line ending counts as whitespace:
+
+```
+_foo bar
+_
+.
+<p>_foo bar
+_</p>
+```
+
+So does a tab:
+
+```
+_	a_
+.
+<p>_	a_</p>
+```
+
+This one is different from commonmark:
+
+```
+_(_foo_)_
+.
+<p><em>(</em>foo<em>)</em></p>
+```
+
+But you can force the second `_` to be an opener
+using the marker `{`.
+
+```
+_({_foo_})_
+.
+<p><em>(<em>foo</em>)</em></p>
+```
+
+Note that an explicitly marked opener can only be closed
+by an explicitly marked closer, and a non-marked opener
+can only be closed by a non-marked closer:
+
+```
+{_ x_ _} _x_}
+.
+<p><em> x_ </em> _x_}</p>
+```
+
+
+```
+_(*foo*)_
+.
+<p><em>(<strong>foo</strong>)</em></p>
+```
+
+Overlapping scopes (first to close wins):
+
+```
+_foo *bar_ baz*
+.
+<p><em>foo *bar</em> baz*</p>
+```
+
+Over line break:
+
+```
+_foo
+bar_
+.
+<p><em>foo
+bar</em></p>
+```
+
+Inline content allowed:
+
+```
+*foo [link](url) `*`*
+.
+<p><strong>foo <a href="url">link</a> <code>*</code></strong></p>
+```
+
+Can't emph an underscore:
+
+```
+___
+.
+<p>___</p>
+```
+
+Unless you escape it:
+
+```
+_\__
+.
+<p><em>_</em></p>
+```
+
+No empty emph:
+
+```
+__
+.
+<p>__</p>
+```
+
+```
+_}b_
+.
+<p>_}b_</p>
+```
+
+```
+_\}b_
+.
+<p><em>}b</em></p>
+```
+
+```
+_ab\_c_
+.
+<p><em>ab_c</em></p>
+```
+
+```
+*****a*****
+.
+<p><strong><strong><strong><strong><strong>a</strong></strong></strong></strong></strong></p>
+```
+
+```
+_[bar_](url)
+.
+<p><em>[bar</em>](url)</p>
+```
+
+```
+\_[bar_](url)
+.
+<p>_<a href="url">bar_</a></p>
+```
+
+Code takes precedence:
+
+```
+_`a_`b
+.
+<p>_<code>a_</code>b</p>
+```
+
+Autolinks take precedence:
+
+```
+_<http://example.com/a_b>
+.
+<p>_<a href="http://example.com/a_b">http://example.com/a_b</a></p>
+```
diff --git a/test/escapes.test b/test/escapes.test
new file mode 100644
--- /dev/null
+++ b/test/escapes.test
@@ -0,0 +1,54 @@
+ASCII punctuation characters can be escaped:
+
+```
+\`\*\_\[\#
+.
+<p>`*_[#</p>
+```
+
+Non-ASCII punctuation characters can't be escaped:
+
+```
+\a\«
+.
+<p>\a\«</p>
+```
+
+An escaped newline is a hard break:
+
+```
+ab\
+c
+.
+<p>ab<br>
+c</p>
+```
+
+There can be spaces and tabs between the backslash and the newline:
+
+```
+ab\	  
+c
+.
+<p>ab<br>
+c</p>
+```
+
+There can also be spaces and tabs before the backslash, which are ignored:
+
+```
+ab 	 \  	
+c
+.
+<p>ab<br>
+c</p>
+```
+
+An escaped space is a non-breaking space:
+
+```
+a\ b
+.
+<p>a&nbsp;b</p>
+```
+
diff --git a/test/fenced_divs.test b/test/fenced_divs.test
new file mode 100644
--- /dev/null
+++ b/test/fenced_divs.test
@@ -0,0 +1,134 @@
+Fenced divs are containers for sequences of blocks, to
+which an attribute can be attached.
+
+A fenced div begins with an opening fence: a line with
+three or more consecutive `:` characters, followed optionally by
+a class name and optionally whitespace.
+
+It ends with a closing fence: a line beginning with three
+or more consecutive `:` characters, followed by optional
+whitespace and the end of the line. The number of `:` characters
+in the closing fence must be at least the number in the opening fence.
+
+If the end of the input (or enclosing block) is encountered
+before a closing fence, the fenced div is implicitly closed.
+
+```
+:::::::::: foo
+Hi
+
+> A block quote.
+:::::::::::
+.
+<div class="foo">
+<p>Hi</p>
+<blockquote>
+<p>A block quote.</p>
+</blockquote>
+</div>
+```
+
+```
+{#bar .foo}
+:::
+Hi
+
+> A block quote.
+:::::::::::::
+.
+<div id="bar" class="foo">
+<p>Hi</p>
+<blockquote>
+<p>A block quote.</p>
+</blockquote>
+</div>
+```
+
+Fenced divs may be nested.
+
+```
+{#bar .foo}
+::::
+Hi
+
+::: baz
+> A block quote.
+:::
+::::
+.
+<div id="bar" class="foo">
+<p>Hi</p>
+<div class="baz">
+<blockquote>
+<p>A block quote.</p>
+</blockquote>
+</div>
+</div>
+```
+
+A fenced div cannot interrupt a paragraph, without
+an intervening blank line.
+
+```
+Paragraph text
+::::
+Hi
+::::
+.
+<p>Paragraph text
+::::
+Hi
+::::</p>
+```
+
+A fenced div need not have attributes or a class name.
+
+```
+::::
+Hi
+::::
+.
+<div>
+<p>Hi</p>
+</div>
+```
+
+The closing fence must be at least as long as the opening fence.
+
+```
+::::::::: foo
+Hi
+::::
+.
+<div class="foo">
+<p>Hi
+::::</p>
+</div>
+```
+
+If the end of the input (or enclosing block) is encountered
+before a closing fence, the fenced div is implicitly closed.
+
+```
+> :::: foo
+> Hi
+.
+<blockquote>
+<div class="foo">
+<p>Hi</p>
+</div>
+</blockquote>
+```
+
+````
+::: outer
+```
+:::
+```
+:::
+.
+<div class="outer">
+<pre><code>:::
+</code></pre>
+</div>
+````
diff --git a/test/footnotes.test b/test/footnotes.test
new file mode 100644
--- /dev/null
+++ b/test/footnotes.test
@@ -0,0 +1,92 @@
+````
+test[^a] and another[^foo_bar].
+
+[^a]: This is a note.
+
+  Second paragraph.
+
+[^foo_bar]:
+  ```
+  code
+  ```
+
+another ref to the first note[^a].
+.
+<p>test<a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a> and another<a id="fnref2" href="#fn2" role="doc-noteref"><sup>2</sup></a>.</p>
+<p>another ref to the first note<a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a>.</p>
+<section role="doc-endnotes">
+<hr>
+<ol>
+<li id="fn1">
+<p>This is a note.</p>
+<p>Second paragraph.<a href="#fnref1" role="doc-backlink">↩︎</a></p>
+</li>
+<li id="fn2">
+<pre><code>code
+</code></pre>
+<p><a href="#fnref2" role="doc-backlink">↩︎</a></p>
+</li>
+</ol>
+</section>
+````
+
+```
+test[^nonexistent]
+
+[^unused]: note
+
+  more
+.
+<p>test<a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a></p>
+<section role="doc-endnotes">
+<hr>
+<ol>
+<li id="fn1">
+<p><a href="#fnref1" role="doc-backlink">↩︎</a></p>
+</li>
+</ol>
+</section>
+```
+
+```
+[^a]
+[^b]
+
+[^b]:
+.
+<p><a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a>
+<a id="fnref2" href="#fn2" role="doc-noteref"><sup>2</sup></a></p>
+<section role="doc-endnotes">
+<hr>
+<ol>
+<li id="fn1">
+<p><a href="#fnref1" role="doc-backlink">↩︎</a></p>
+</li>
+<li id="fn2">
+<p><a href="#fnref2" role="doc-backlink">↩︎</a></p>
+</li>
+</ol>
+</section>
+```
+
+Issue #37:
+
+```
+text[^footnote].
+
+[^footnote]: very long footnote[^another-footnote]
+[^another-footnote]: bla bla[^another-footnote]
+.
+<p>text<a id="fnref1" href="#fn1" role="doc-noteref"><sup>1</sup></a>.</p>
+<section role="doc-endnotes">
+<hr>
+<ol>
+<li id="fn1">
+<p>very long footnote<a id="fnref2" href="#fn2" role="doc-noteref"><sup>2</sup></a><a href="#fnref1" role="doc-backlink">↩︎</a></p>
+</li>
+<li id="fn2">
+<p>bla bla<a id="fnref2" href="#fn2" role="doc-noteref"><sup>2</sup></a><a href="#fnref2" role="doc-backlink">↩︎</a></p>
+</li>
+</ol>
+</section>
+```
diff --git a/test/headings.test b/test/headings.test
new file mode 100644
--- /dev/null
+++ b/test/headings.test
@@ -0,0 +1,182 @@
+```
+## Heading
+.
+<section id="Heading">
+<h2>Heading</h2>
+</section>
+```
+
+```
+# Heading
+
+# another
+.
+<section id="Heading">
+<h1>Heading</h1>
+</section>
+<section id="another">
+<h1>another</h1>
+</section>
+```
+
+```
+# Heading
+# continued
+.
+<section id="Heading-continued">
+<h1>Heading
+continued</h1>
+</section>
+```
+
+```
+##
+heading
+
+para
+.
+<section id="heading">
+<h2>heading</h2>
+<p>para</p>
+</section>
+```
+
+```
+##
+.
+<section id="sec">
+<h2></h2>
+</section>
+```
+
+```
+## Heading
+### Next level
+.
+<section id="Heading">
+<h2>Heading</h2>
+<section id="Next-level">
+<h3>Next level</h3>
+</section>
+</section>
+```
+
+```
+# Heading
+lazy
+.
+<section id="Heading-lazy">
+<h1>Heading
+lazy</h1>
+</section>
+```
+
+```
+# Heading
+lazy
+# more
+lazy
+
+text
+.
+<section id="Heading-lazy-more-lazy">
+<h1>Heading
+lazy
+more
+lazy</h1>
+<p>text</p>
+</section>
+```
+
+```
+##Notheading
+.
+<p>##Notheading</p>
+```
+
+```
+   ##    Heading
+.
+<section id="Heading">
+<h2>Heading</h2>
+</section>
+```
+
+```
+## heading ##
+.
+<section id="heading">
+<h2>heading ##</h2>
+</section>
+```
+
+```
+# # heading
+.
+<section id="heading">
+<h1># heading</h1>
+</section>
+```
+
+Auto-identifiers:
+
+```
+{#Foo-bar}
+Paragraph
+
+# Foo bar
+
+## Foo  bar
+
+{#baz}
+# Foo bar
+.
+<p id="Foo-bar">Paragraph</p>
+<section id="Foo-bar-2">
+<h1>Foo bar</h1>
+<section id="Foo-bar-1">
+<h2>Foo  bar</h2>
+</section>
+</section>
+<section id="baz">
+<h1>Foo bar</h1>
+</section>
+```
+
+Implicit header references:
+
+```
+See [Introduction][].
+
+# Introduction
+.
+<p>See <a href="#Introduction">Introduction</a>.</p>
+<section id="Introduction">
+<h1>Introduction</h1>
+</section>
+```
+
+```
+See [Introduction][].
+
+{#foo}
+# Introduction
+.
+<p>See <a href="#foo">Introduction</a>.</p>
+<section id="foo">
+<h1>Introduction</h1>
+</section>
+```
+
+```
+See [Introduction][].
+
+# Introduction
+
+[Introduction]: #bar
+.
+<p>See <a href="#bar">Introduction</a>.</p>
+<section id="Introduction">
+<h1>Introduction</h1>
+</section>
+```
diff --git a/test/insert_delete_mark.test b/test/insert_delete_mark.test
new file mode 100644
--- /dev/null
+++ b/test/insert_delete_mark.test
@@ -0,0 +1,29 @@
+```
+This is {-deleted
+_text_-}. The braces are -required-.
+And they must be in the -}right order{-.
+.
+<p>This is <del>deleted
+<em>text</em></del>. The braces are -required-.
+And they must be in the -}right order{-.</p>
+```
+
+```
+{+ Inserted text +}
+.
+<p><ins> Inserted text </ins></p>
+```
+
+Interaction with smart:
+
+```
+{--hello--}
+.
+<p><del>-hello-</del></p>
+```
+
+```
+This is {=marked *text*=}.
+.
+<p>This is <mark>marked <strong>text</strong></mark>.</p>
+```
diff --git a/test/links_and_images.test b/test/links_and_images.test
new file mode 100644
--- /dev/null
+++ b/test/links_and_images.test
@@ -0,0 +1,242 @@
+```
+[basic _link_][a_b_]
+
+[a_b_]: url
+.
+<p><a href="url">basic <em>link</em></a></p>
+```
+
+```
+![basic _image_][a_b_]
+
+[a_b_]: url
+.
+<p><img alt="basic image" src="url"></p>
+```
+
+```
+[link][]
+
+[link]: url
+.
+<p><a href="url">link</a></p>
+```
+
+```
+[link][]
+
+[link]:
+ url
+.
+<p><a href="url">link</a></p>
+```
+
+The URL can be split over multiple lines:
+
+```
+[link][]
+
+[link]:
+ url
+  andurl
+.
+<p><a href="urlandurl">link</a></p>
+```
+
+```
+[link](url
+andurl)
+.
+<p><a href="urlandurl">link</a></p>
+```
+
+```
+[link][]
+
+[link]:
+[link2]: url
+.
+<p><a href="">link</a></p>
+```
+
+```
+[link][]
+[link][link2]
+
+[link2]:
+  url2
+[link]:
+ url
+.
+<p><a href="url">link</a>
+<a href="url2">link</a></p>
+```
+
+```
+[link][a and
+b]
+
+[a and b]: url
+.
+<p><a href="url">link</a></p>
+```
+
+If the reference isn't found, we get an empty link.
+
+```
+[link][a and
+b]
+.
+<p><a href="">link</a></p>
+```
+
+Reference definitions can't have line breaks in the key:
+
+```
+[link][a and
+b]
+
+[a and
+b]: url
+.
+<p><a href="">link</a></p>
+<p>[a and
+b]: url</p>
+```
+
+No case normalization is done on reference definitions:
+
+```
+[Link][]
+
+[link]: /url
+.
+<p><a href="">Link</a></p>
+```
+
+Attributes on reference definitions get transferred to
+the link:
+
+```
+{title=foo}
+[ref]: /url
+
+[ref][]
+.
+<p><a href="/url" title="foo">ref</a></p>
+```
+
+Attributes on the link override those on references:
+
+```
+{title=foo}
+[ref]: /url
+
+[ref][]{title=bar}
+.
+<p><a href="/url" title="bar">ref</a></p>
+```
+
+```
+[link _and_ link][]
+
+[link _and_ link]: url
+.
+<p><a href="url">link <em>and</em> link</a></p>
+```
+
+```
+![basic _image_](url)
+.
+<p><img alt="basic image" src="url"></p>
+```
+
+```
+[![image](img.jpg)](url)
+.
+<p><a href="url"><img alt="image" src="img.jpg"></a></p>
+```
+
+```
+[unclosed](hello *a
+b*
+.
+<p>[unclosed](hello <strong>a
+b</strong></p>
+```
+
+Note that soft breaks are ignored, so long URLs
+can be split over multiple lines:
+```
+[closed](hello *a
+b*)
+.
+<p><a href="hello *ab*">closed</a></p>
+```
+
+Here the strong takes precedence over the link because it
+starts first:
+```
+*[closed](hello*)
+.
+<p><strong>[closed](hello</strong>)</p>
+```
+
+Avoid this with a backslash escape:
+```
+*[closed](hello\*)
+.
+<p>*<a href="hello*">closed</a></p>
+```
+
+Link in link?
+```
+[[foo](bar)](baz)
+.
+<p><a href="baz"><a href="bar">foo</a></a></p>
+```
+
+Link in image?
+```
+![[link](url)](img)
+.
+<p><img alt="link" src="img"></p>
+```
+
+Image in link?
+```
+[![image](img)](url)
+.
+<p><a href="url"><img alt="image" src="img"></a></p>
+```
+
+Autolinks:
+```
+<http://example.com/foo>
+<me@example.com>
+.
+<p><a href="http://example.com/foo">http://example.com/foo</a>
+<a href="mailto:me@example.com">me@example.com</a></p>
+```
+
+Openers inside `[..](` or `[..][` or `[..]{` can't match
+outside them, even if the construction doesn't turn out to be
+a link or span or image.
+
+```
+[x_y](x_y)
+.
+<p><a href="x_y">x_y</a></p>
+```
+
+```
+[x_y](x_
+.
+<p>[x_y](x_</p>
+```
+
+```
+[x_y]{.bar_}
+.
+<p><span class="bar_">x_y</span></p>
+```
diff --git a/test/lists.test b/test/lists.test
new file mode 100644
--- /dev/null
+++ b/test/lists.test
@@ -0,0 +1,510 @@
+```
+- one
+- two
+.
+<ul>
+<li>
+one
+</li>
+<li>
+two
+</li>
+</ul>
+```
+
+```
+- one
+ - two
+  - three
+.
+<ul>
+<li>
+one
+- two
+- three
+</li>
+</ul>
+```
+
+```
+- one
+
+ - two
+
+  - three
+.
+<ul>
+<li>
+one
+<ul>
+<li>
+two
+<ul>
+<li>
+three
+</li>
+</ul>
+</li>
+</ul>
+</li>
+</ul>
+```
+
+```
+- one
+  and
+
+  another paragraph
+
+  - a list
+
+- two
+.
+<ul>
+<li>
+<p>one
+and</p>
+<p>another paragraph</p>
+<ul>
+<li>
+a list
+</li>
+</ul>
+</li>
+<li>
+<p>two</p>
+</li>
+</ul>
+```
+
+```
+- one
+lazy
+- two
+.
+<ul>
+<li>
+one
+lazy
+</li>
+<li>
+two
+</li>
+</ul>
+```
+
+```
+- a
+- b
++ c
+.
+<ul>
+<li>
+a
+</li>
+<li>
+b
+</li>
+</ul>
+<ul>
+<li>
+c
+</li>
+</ul>
+```
+
+```
+- a
+
+- b
+.
+<ul>
+<li>
+<p>a</p>
+</li>
+<li>
+<p>b</p>
+</li>
+</ul>
+```
+
+```
+- a
+  - b
+
+  - c
+- d
+.
+<ul>
+<li>
+a
+- b
+<ul>
+<li>
+c
+</li>
+</ul>
+</li>
+<li>
+d
+</li>
+</ul>
+```
+
+```
+- a
+  - b
+
+  - c
+
+- d
+.
+<ul>
+<li>
+a
+- b
+<ul>
+<li>
+c
+</li>
+</ul>
+</li>
+<li>
+d
+</li>
+</ul>
+```
+
+```
+- a
+
+  b
+- c
+.
+<ul>
+<li>
+<p>a</p>
+<p>b</p>
+</li>
+<li>
+<p>c</p>
+</li>
+</ul>
+```
+
+```
+- a
+
+  - b
+  - c
+- d
+.
+<ul>
+<li>
+a
+<ul>
+<li>
+b
+</li>
+<li>
+c
+</li>
+</ul>
+</li>
+<li>
+d
+</li>
+</ul>
+```
+
+```
+- a
+
+  - b
+  - c
+
+- d
+.
+<ul>
+<li>
+a
+<ul>
+<li>
+b
+</li>
+<li>
+c
+</li>
+</ul>
+</li>
+<li>
+d
+</li>
+</ul>
+```
+
+```
+- a
+
+  * b
+cd
+.
+<ul>
+<li>
+a
+<ul>
+<li>
+b
+cd
+</li>
+</ul>
+</li>
+</ul>
+```
+
+```
+- - - a
+.
+<ul>
+<li>
+<ul>
+<li>
+<ul>
+<li>
+a
+</li>
+</ul>
+</li>
+</ul>
+</li>
+</ul>
+```
+
+```
+1. one
+1. two
+.
+<ol>
+<li>
+one
+</li>
+<li>
+two
+</li>
+</ol>
+```
+
+```
+1. one
+
+ 1. two
+.
+<ol>
+<li>
+one
+<ol>
+<li>
+two
+</li>
+</ol>
+</li>
+</ol>
+```
+
+```
+4. one
+5. two
+.
+<ol start="4">
+<li>
+one
+</li>
+<li>
+two
+</li>
+</ol>
+```
+
+```
+1) one
+2) two
+.
+<ol>
+<li>
+one
+</li>
+<li>
+two
+</li>
+</ol>
+```
+
+```
+(1) one
+(2) two
+.
+<ol>
+<li>
+one
+</li>
+<li>
+two
+</li>
+</ol>
+```
+
+```
+(a) one
+(b) two
+.
+<ol type="a">
+<li>
+one
+</li>
+<li>
+two
+</li>
+</ol>
+```
+
+```
+(D) one
+(E) two
+.
+<ol start="4" type="A">
+<li>
+one
+</li>
+<li>
+two
+</li>
+</ol>
+```
+
+```
+a. one
+b. two
+.
+<ol type="a">
+<li>
+one
+</li>
+<li>
+two
+</li>
+</ol>
+```
+
+```
+i. one
+ii. two
+.
+<ol type="i">
+<li>
+one
+</li>
+<li>
+two
+</li>
+</ol>
+```
+
+```
+xli) one
+xlii) two
+.
+<ol start="41" type="i">
+<li>
+one
+</li>
+<li>
+two
+</li>
+</ol>
+```
+
+```
+(IV) one
+(V) two
+.
+<ol start="4" type="I">
+<li>
+one
+</li>
+<li>
+two
+</li>
+</ol>
+```
+
+```
+i. a
+ii. b
+.
+<ol type="i">
+<li>
+a
+</li>
+<li>
+b
+</li>
+</ol>
+```
+
+```
+i. a
+j. b
+.
+<ol start="9" type="a">
+<li>
+a
+</li>
+<li>
+b
+</li>
+</ol>
+```
+
+When ambiguous, prioritize roman numerals.
+
+```
+i. a
+i. b
+.
+<ol type="i">
+<li>
+a
+</li>
+<li>
+b
+</li>
+</ol>
+```
+
+```
+I. a
+II. b
+E. d
+.
+<ol type="I">
+<li>
+a
+</li>
+<li>
+b
+</li>
+</ol>
+<ol start="5" type="A">
+<li>
+d
+</li>
+</ol>
+```
+
+```
+The civil war ended in
+1865. And this should not start a list.
+.
+<p>The civil war ended in
+1865. And this should not start a list.</p>
+```
diff --git a/test/math.test b/test/math.test
new file mode 100644
--- /dev/null
+++ b/test/math.test
@@ -0,0 +1,44 @@
+Math goes in verbatim spans prefixed with either `$` (for
+inline math) or `$$` (for display math).
+
+```
+$`e=mc^2`
+.
+<p><span class="math inline">\(e=mc^2\)</span></p>
+```
+
+```
+My equation: $`e=mc^2`
+.
+<p>My equation: <span class="math inline">\(e=mc^2\)</span></p>
+```
+
+```
+$$`e=mc^2`
+.
+<p><span class="math display">\[e=mc^2\]</span></p>
+```
+
+```
+My equation: $$`e=mc^2`
+.
+<p>My equation: <span class="math display">\[e=mc^2\]</span></p>
+```
+
+Newlines are allowed, just as in verbatim:
+
+```
+$`e=
+mc^2`
+.
+<p><span class="math inline">\(e=
+mc^2\)</span></p>
+```
+
+`$` characters are allowed inside:
+
+```
+$`e=\text{the number $\pi$}`
+.
+<p><span class="math inline">\(e=\text{the number $\pi$}\)</span></p>
+```
diff --git a/test/para.test b/test/para.test
new file mode 100644
--- /dev/null
+++ b/test/para.test
@@ -0,0 +1,7 @@
+```
+hi  
+there  
+.
+<p>hi  
+there</p>
+```
diff --git a/test/raw.test b/test/raw.test
new file mode 100644
--- /dev/null
+++ b/test/raw.test
@@ -0,0 +1,38 @@
+Raw inline content:
+
+```
+`<a>`{=html}
+.
+<p><a></p>
+```
+
+Raw block-level content:
+
+````
+``` =html
+<table>
+```
+.
+<table>
+````
+
+You can't mix regular attributes and raw syntax:
+
+````
+`<b>foo</b>`{=html #id}
+```
+.
+<p><code>&lt;b&gt;foo&lt;/b&gt;</code>{=html #id}
+<code></code></p>
+````
+
+Attributes attached to raw content will just be ignored:
+
+````
+{.foo}
+``` =html
+<table>
+```
+.
+<table>
+````
diff --git a/test/regression.test b/test/regression.test
new file mode 100644
--- /dev/null
+++ b/test/regression.test
@@ -0,0 +1,93 @@
+Issue #104:
+
+```
+{1--}
+
+{1-}
+.
+<p>{1--}</p>
+<p>{1-}</p>
+```
+
+Issue #106:
+
+```
+
+|`|
+.
+<p>|<code>|</code></p>
+```
+
+```
+
+|`|x
+.
+<p>|<code>|x</code></p>
+```
+
+Issue #127:
+
+```
+\$$`a`
+.
+<p>$<span class="math inline">\(a\)</span></p>
+```
+
+```
+{
+ .`
+.
+<p>{
+.<code></code></p>
+```
+
+Issue #57:
+
+```
+| 1 | 2 |
+
+ ^ cap1
+
+ ^ cap2
+.
+<table>
+<caption>cap2</caption>
+<tr>
+<td>1</td>
+<td>2</td>
+</tr>
+</table>
+```
+
+Section start after list:
+
+```
+: term
+
+  def
+
+# New heading
+.
+<dl>
+<dt>term</dt>
+<dd>
+def
+</dd>
+</dl>
+<section id="New-heading">
+<h1>New heading</h1>
+</section>
+```
+
+Block quotes with markers unaligned:
+
+```
+> foo
+ > bar
+.
+<blockquote>
+<p>foo
+bar</p>
+</blockquote>
+```
+
diff --git a/test/smart.test b/test/smart.test
new file mode 100644
--- /dev/null
+++ b/test/smart.test
@@ -0,0 +1,192 @@
+Open quotes are matched with closed quotes.
+The same method is used for matching openers and closers
+as is used in emphasis parsing:
+
+```
+"Hello," said the spider.
+"'Shelob' is my name."
+.
+<p>“Hello,” said the spider.
+“‘Shelob’ is my name.”</p>
+```
+
+```
+'A', 'B', and 'C' are letters.
+.
+<p>‘A’, ‘B’, and ‘C’ are letters.</p>
+```
+
+```
+'Oak,' 'elm,' and 'beech' are names of trees.
+So is 'pine.'
+.
+<p>‘Oak,’ ‘elm,’ and ‘beech’ are names of trees.
+So is ‘pine.’</p>
+```
+
+```
+'He said, "I want to go."'
+.
+<p>‘He said, “I want to go.”’</p>
+```
+
+A single quote that isn't an open quote matched
+with a close quote will be treated as an
+apostrophe:
+
+```
+Were you alive in the '70s?
+.
+<p>Were you alive in the ’70s?</p>
+```
+
+```
+Here is some quoted '`code`' and a "[quoted link](url)".
+.
+<p>Here is some quoted ‘<code>code</code>’ and a “<a href="url">quoted link</a>”.</p>
+```
+
+Here the first `'` is treated as an apostrophe, not
+an open quote, because the final single quote is matched
+by the single quote before `jolly`:
+
+```
+'tis the season to be 'jolly'
+.
+<p>’tis the season to be ‘jolly’</p>
+```
+
+Multiple apostrophes should not be marked as open/closing quotes.
+
+```
+'We'll use Jane's boat and John's truck,' Jenna said.
+.
+<p>‘We’ll use Jane’s boat and John’s truck,’ Jenna said.</p>
+```
+
+An unmatched double quote will be interpreted as a
+left double quote, to facilitate this style:
+
+```
+"A paragraph with no closing quote.
+
+"Second paragraph by same speaker, in fiction."
+.
+<p>“A paragraph with no closing quote.</p>
+<p>“Second paragraph by same speaker, in fiction.”</p>
+```
+
+A quote following a `]` or `)` character cannot
+be an open quote:
+
+```
+[a]'s b'
+.
+<p>[a]’s b’</p>
+```
+
+Quotes that are escaped come out as literal straight
+quotes:
+
+```
+\"This is not smart.\"
+This isn\'t either.
+5\'8\"
+.
+<p>"This is not smart."
+This isn't either.
+5'8"</p>
+```
+
+Doubled quotes are treated as nested:
+
+```
+''hi''
+.
+<p>‘‘hi’’</p>
+```
+
+Heuristics for determining openers and closers can
+be overridden using `{` and `}`:
+
+```
+{''}hi{''}
+.
+<p>‘’hi‘’</p>
+```
+
+Two hyphens form an en-dash, three an em-dash.
+
+```
+Some dashes:  em---em
+en--en
+em --- em
+en -- en
+2--3
+.
+<p>Some dashes:  em—em
+en–en
+em — em
+en – en
+2–3</p>
+```
+
+A sequence of more than three hyphens is
+parsed as a sequence of em and/or en dashes,
+with no hyphens. If possible, a homogeneous
+sequence of dashes is used (so, 10 hyphens
+= 5 en dashes, and 9 hyphens = 3 em dashes).
+When a heterogeneous sequence must be used,
+the em dashes come first, followed by the en
+dashes, and as few en dashes as possible are
+used (so, 7 hyphens = 2 em dashes an 1 en
+dash).
+
+```
+one-
+two--
+three---
+four----
+five-----
+six------
+seven-------
+eight--------
+nine---------
+thirteen-------------.
+.
+<p>one-
+two–
+three—
+four––
+five—–
+six——
+seven—––
+eight––––
+nine———
+thirteen———––.</p>
+```
+
+Hyphens can be escaped:
+
+```
+Escaped hyphens: \-- \-\-\-.
+.
+<p>Escaped hyphens: -- ---.</p>
+```
+
+Three periods form an ellipsis:
+
+```
+Ellipses...and...and....
+.
+<p>Ellipses…and…and….</p>
+```
+
+Periods can be escaped if ellipsis-formation
+is not wanted:
+
+```
+No ellipses\.\.\.
+.
+<p>No ellipses...</p>
+```
diff --git a/test/spans.test b/test/spans.test
new file mode 100644
--- /dev/null
+++ b/test/spans.test
@@ -0,0 +1,19 @@
+```
+This is a [test of
+*color*]{.blue}.
+.
+<p>This is a <span class="blue">test of
+<strong>color</strong></span>.</p>
+```
+
+```
+not a [span] {#id}.
+.
+<p>not a [span] .</p>
+```
+
+```
+[nested [span]{.blue}]{#ident}
+.
+<p><span id="ident">nested <span class="blue">span</span></span></p>
+```
diff --git a/test/super_subscript.test b/test/super_subscript.test
new file mode 100644
--- /dev/null
+++ b/test/super_subscript.test
@@ -0,0 +1,23 @@
+```
+H~2~O
+.
+<p>H<sub>2</sub>O</p>
+```
+
+```
+mc^2^
+.
+<p>mc<sup>2</sup></p>
+```
+
+```
+test^of superscript ~with subscript~^
+.
+<p>test<sup>of superscript <sub>with subscript</sub></sup></p>
+```
+
+```
+H{~2 ~}O
+.
+<p>H<sub>2 </sub>O</p>
+```
diff --git a/test/symb.test b/test/symb.test
new file mode 100644
--- /dev/null
+++ b/test/symb.test
@@ -0,0 +1,11 @@
+```
+:+1: :scream:
+.
+<p><span class="symbol">:+1:</span> <span class="symbol">:scream:</span></p>
+```
+
+```
+:ice:scream:
+.
+<p><span class="symbol">:ice:</span>scream:</p>
+```
diff --git a/test/tables.test b/test/tables.test
new file mode 100644
--- /dev/null
+++ b/test/tables.test
@@ -0,0 +1,125 @@
+Simplest table:
+
+```
+| a |
+.
+<table>
+<tr>
+<td>a</td>
+</tr>
+</table>
+```
+
+```
+|a|   *b*|
+|*c| d* |
+.
+<table>
+<tr>
+<td>a</td>
+<td><strong>b</strong></td>
+</tr>
+<tr>
+<td>*c</td>
+<td>d*</td>
+</tr>
+</table>
+```
+
+```
+| `a |`
+.
+<p>| <code>a |</code></p>
+```
+
+```
+| a | b |
+
+^ With a _caption_
+and another line.
+.
+<table>
+<caption>With a <em>caption</em>
+and another line.</caption>
+<tr>
+<td>a</td>
+<td>b</td>
+</tr>
+</table>
+```
+
+Table headers:  note that we can have multiple headers; each
+determines the alignment for following cells, until the next header.
+
+```
+|a|b|
+|:-|---:|
+|c|d|
+|cc|dd|
+|-:|:-:|
+|e|f|
+|g|h|
+.
+<table>
+<tr>
+<th style="text-align: left;">a</th>
+<th style="text-align: right;">b</th>
+</tr>
+<tr>
+<td style="text-align: left;">c</td>
+<td style="text-align: right;">d</td>
+</tr>
+<tr>
+<th style="text-align: right;">cc</th>
+<th style="text-align: center;">dd</th>
+</tr>
+<tr>
+<td style="text-align: right;">e</td>
+<td style="text-align: center;">f</td>
+</tr>
+<tr>
+<td style="text-align: right;">g</td>
+<td style="text-align: center;">h</td>
+</tr>
+</table>
+```
+
+```
+|--|--|
+.
+<table>
+</table>
+```
+
+```
+|---|---|
+| a | b |
+.
+<table>
+<tr>
+<td>a</td>
+<td>b</td>
+</tr>
+</table>
+```
+
+```
+| |
+.
+<table>
+<tr>
+<td></td>
+</tr>
+</table>
+```
+
+```
+| just two \| `|` | cells in this table |
+.
+<table>
+<tr>
+<td>just two | <code>|</code></td>
+<td>cells in this table</td>
+</tr>
+</table>
+```
diff --git a/test/task_lists.test b/test/task_lists.test
new file mode 100644
--- /dev/null
+++ b/test/task_lists.test
@@ -0,0 +1,31 @@
+```
+- [ ] an unchecked task list item
+- [x] checked item
+.
+<ul class="task-list">
+<li class="unchecked">
+<label><input type="checkbox" />an unchecked task list item</label>
+</li>
+<li class="checked">
+<label><input type="checkbox" checked="" />checked item</label>
+</li>
+</ul>
+```
+
+```
+* [ ] an unchecked task list item
+
+  with two paragraphs
+
+* [x] checked item
+.
+<ul class="task-list">
+<li class="unchecked">
+<p><label><input type="checkbox" />an unchecked task list item</label></p>
+<p>with two paragraphs</p>
+</li>
+<li class="checked">
+<p><label><input type="checkbox" checked="" />checked item</label></p>
+</li>
+</ul>
+```
diff --git a/test/thematic_breaks.test b/test/thematic_breaks.test
new file mode 100644
--- /dev/null
+++ b/test/thematic_breaks.test
@@ -0,0 +1,45 @@
+```
+hello
+
+- - -
+
+there
+.
+<p>hello</p>
+<hr>
+<p>there</p>
+```
+
+```
+hello
+
+   **   **
+
+there
+.
+<p>hello</p>
+<hr>
+<p>there</p>
+```
+
+```
+hello
+
+   *-*-*-*
+
+there
+.
+<p>hello</p>
+<hr>
+<p>there</p>
+```
+
+```
+hello
+   *-*-*-*
+there
+.
+<p>hello
+<strong>-</strong>-<strong>-</strong>
+there</p>
+```
diff --git a/test/verbatim.test b/test/verbatim.test
new file mode 100644
--- /dev/null
+++ b/test/verbatim.test
@@ -0,0 +1,47 @@
+```
+Some `code`
+.
+<p>Some <code>code</code></p>
+```
+
+```
+Some `code
+with a line break`
+.
+<p>Some <code>code
+with a line break</code></p>
+```
+
+```
+Special characters: `*hi*`
+.
+<p>Special characters: <code>*hi*</code></p>
+```
+
+```
+*foo`*`
+.
+<p>*foo<code>*</code></p>
+```
+
+```````
+`````a`a``a```a````a``````a`````
+.
+<p><code>a`a``a```a````a``````a</code></p>
+```````
+
+```
+` ``a`` `
+.
+<p><code>``a``</code></p>
+```
+
+Implicitly closed by end of paragraph:
+
+```
+` a
+c
+.
+<p><code> a
+c</code></p>
+```
