diff --git a/AUTHORS.md b/AUTHORS.md
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -6,6 +6,7 @@
 - Akash Patel
 - Akos Marton
 - Albert Krewinkel
+- Albert Lei
 - Alex Ivkin
 - Alex Toldaiev
 - Alex Vong
@@ -33,6 +34,7 @@
 - Anti-Distinctlyminty
 - Antoine Latter
 - Anton Antich
+- Anton Melnikov
 - Antonio Terceiro
 - Arata Mizuki
 - Arata Mizuki
@@ -74,6 +76,7 @@
 - Charles Tapley Hoyt
 - Charlotte Koch
 - Chris Black
+- Chris Callison-Burch
 - Christian Conkle
 - Christian Christiansen
 - Christian Despres
@@ -176,6 +179,7 @@
 - Ivar de Bruin
 - Ivo Clarysse
 - Jaap de Jong
+- Jacob Larkin
 - James J Balamuta
 - J. B. Rainsberger
 - J. Lewis Muir
@@ -441,15 +445,18 @@
 - Wout Gevaert
 - Xavier Olive
 - Yan Pashkovsky
+- Yann Trividic
 - Yehuda Katz
 - YI
 - Yoan Blanc
+- You Jiangbin
 - Yuchen Pei
 - Zihang Chen
 - 3w36zj6
 - arcnmx
 - a-vrma
 - andrebauer
+- benniekiss
 - black-desk
 - blmage
 - bucklereed
@@ -466,6 +473,7 @@
 - etclub
 - favonia
 - guqicun
+- har7an
 - harabat
 - hseg
 - infinity0x
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -215,9 +215,9 @@
     external C libraries should especially be avoided.
 
 10. We aim for compatibility with at least the last three
-    released ghc versions, and sometimes more. Currently pandoc
-    can be compiled on ghc 8.6 an higher. All pull requests and
-    commits are tested automatically on GitHub Actions.
+    released ghc versions, and sometimes more.  Currently we
+    support ghc versions 9.6 and higher. All pull requests
+    and commits are tested automatically on GitHub Actions.
 
 Tests
 -----
@@ -448,7 +448,7 @@
 Lua filters
 -----------
 
-If you've written a useful pandoc [lua filter](lua-filters.html),
+If you've written a useful pandoc [lua filter](./doc/lua-filters.md),
 you may want to consider submitting a pull request to the
 [lua-filters repository](https://github.com/pandoc/lua-filters).
 
diff --git a/COPYRIGHT b/COPYRIGHT
--- a/COPYRIGHT
+++ b/COPYRIGHT
@@ -196,3 +196,30 @@
 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.
+
+------------------------------------------------------------------------
+Some of the code in wasm/pandoc.js and the patches in wasm/patches is
+from https://github.com/haskell-wasm/pandoc-wasm. It is released under
+this license:
+
+MIT License
+
+Copyright (c) Tweag I/O Limited.
+
+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/MANUAL.txt b/MANUAL.txt
--- a/MANUAL.txt
+++ b/MANUAL.txt
@@ -1,7 +1,7 @@
 ---
 title: Pandoc User's Guide
 author: John MacFarlane
-date: 2025-12-01
+date: 2025-02-02
 ---
 
 # Synopsis
@@ -307,7 +307,7 @@
     - `bbcode` [BBCode]
     - `bbcode_fluxbb` [BBCode (FluxBB)]
     - `bbcode_phpbb` [BBCode (phpBB)]
-    - `bbcode_steam` [BBCode (Hubzilla)]
+    - `bbcode_steam` [BBCode (Steam)]
     - `bbcode_hubzilla` [BBCode (Hubzilla)]
     - `bbcode_xenforo` [BBCode (xenForo)]
     - `beamer` ([LaTeX beamer][`beamer`] slide show)
@@ -416,17 +416,17 @@
 
 `-d` *FILE*, `--defaults=`*FILE*
 
-:   Specify a set of default option settings.  *FILE* is a YAML
+:   Specify a set of default option settings.  *FILE* is a YAML or JSON
     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] for more information on the
-    file format.  Settings from the defaults file may be
-    overridden or extended by subsequent options on the command
-    line.
+    (see `--data-dir`).  The `.yaml` extension will be added if
+    *FILE* lacs an extension. See the section [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`
 
@@ -736,7 +736,7 @@
     affected paragraph break. This option only affects the docx
     reader.
 
-`--extract-media=`*DIR*
+`--extract-media=`*DIR*|*FILE*`.zip`
 
 :   Extract images and other media contained in or linked from
     the source document to the path *DIR*, creating it if
@@ -748,6 +748,10 @@
     Otherwise filenames are constructed from the SHA1 hash of
     the contents.
 
+    If the path given ends in `.zip`, then instead of creating
+    a directory, pandoc will create a zip archive containing the
+    media files.
+
 `--abbreviations=`*FILE*
 
 :   Specifies a custom abbreviations file, with abbreviations
@@ -945,27 +949,20 @@
     inside raw HTML blocks when the `markdown_in_html_blocks`
     extension is not set.
 
-`--syntax-highlighting="default"|"none"|"idiomatic"|`*STYLE*`|`*FILE*
+`--syntax-highlighting=default|none|idiomatic|`*STYLE*`|`*FILE*
 
 :   The method to use for code syntax highlighting. Setting a
     specific *STYLE* causes highlighting to be performed with the
     internal highlighting engine, using KDE syntax definitions and
-    styles. The `"idiomatic"` method uses a format-specific
+    styles. The `idiomatic` method uses a format-specific
     highlighter if one is available, or the default style if the
     target format has no idiomatic highlighting method. Setting this
     option to `none` disables all syntax highlighting. The
-    `"default"` method uses a format-specific default.
+    `default` method uses a format-specific default.
 
     The default for HTML, EPUB, Docx, Ms, Man, and LaTeX output is
-    to use the internal highlighter with the default style; Typst
-    output relies on Typst's own syntax highlighting system by
-    default.
-
-    The [`listings`][] LaTeX package is used for idiomatic
-    highlighting in LaTeX. 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][].
+    to use the internal highlighter with the default style; for Typst
+    it is to use Typst's own syntax highlighting system.
 
     Style options are `pygments` (the default), `kate`,
     `monochrome`, `breezeDark`, `espresso`, `zenburn`, `haddock`,
@@ -1431,7 +1428,7 @@
     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
+    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`.
@@ -1762,7 +1759,9 @@
 # Defaults files
 
 The `--defaults` option may be used to specify a package
-of options, in the form of a YAML file.
+of options, in the form of a YAML or JSON file. Examples in
+this section will be given in YAML, but the equivalent forms in
+JSON will also work.
 
 Fields that are omitted will just have their regular
 default values.  So a defaults file can be as simple as
@@ -1772,6 +1771,12 @@
 verbosity: INFO
 ```
 
+or in JSON:
+
+``` json
+{ "verbosity": "INFO" }
+```
+
 In fields that expect a file path (or list of file paths), the
 following syntax may be used to interpolate environment variables:
 
@@ -2005,7 +2010,7 @@
 | ```                              | ```                               |
 +----------------------------------+-----------------------------------+
 | ```                              | ``` yaml                          |
-| --wrap 60                        | wrap: 60                          |
+| --wrap preserve                  | wrap: "preserve"                  |
 | ```                              | ```                               |
 +----------------------------------+-----------------------------------+
 | ```                              | ``` yaml                          |
@@ -2964,6 +2969,13 @@
 :   additional attributes for the title slide of reveal.js slide shows.
     See [background in reveal.js, beamer, and pptx] for an example.
 
+`highlightjs-theme`
+:   highlight.js theme for code highlighting when using
+    `--syntax-highlighting=idiomatic` with reveal.js (defaults to
+    `monokai`). See the [highlight.js demo page] for available themes.
+
+[highlight.js demo page]: https://highlightjs.org/demo
+
 All [reveal.js configuration options] are available as variables.
 To turn off boolean flags that default to true in reveal.js, use `0`.
 
@@ -3276,6 +3288,17 @@
 
     See the section on [reproducible builds].
 
+`pdfstandard`
+:   PDF standard(s) for the document, e.g. `ua-2`, `a-4f`.
+    Supports PDF/A, PDF/X, and PDF/UA variants.
+    Requires LuaLaTeX and LaTeX 2023+. Repeat for multiple standards:
+
+        ---
+        pdfstandard:
+        - ua-2
+        - a-4f
+        ...
+
 [KOMA-Script]: https://ctan.org/pkg/koma-script
 [LaTeX Font Catalogue]: https://tug.org/FontCatalogue/
 [LaTeX font encodings guide]: https://ctan.org/pkg/encguide
@@ -5992,6 +6015,17 @@
 this reason, it is sometimes preferable to use the
 author-in-text style inside notes when using a note style.
 
+Many CSL styles will format citations differently when the
+same source has been cited earlier.  In documents with chapters,
+it is usually desirable to reset this position information
+at the beginning of every chapter. To do this, add the class
+`reset-citation-positions` to the heading for each chapter:
+
+    # The Beginning {.reset-citation-positions}
+
+Note that this class only has an effect when placed on
+top-level headings; it is ignored in nested blocks.
+
 [CSL user documentation]: https://citationstyles.org/authors/
 [CSL]: https://docs.citationstyles.org/en/stable/specification.html
 [CSL markup specs]: https://citeproc-js.readthedocs.io/en/latest/csl-json/markup.html#html-like-formatting-tags
@@ -6199,9 +6233,6 @@
 
   [GitHub-style Markdown alerts]: https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts
 
-Note: This extension currently only works with commonmark:
-`commonmark`, `gfm`, `commonmark_x`.
-
 ### Extension: `autolink_bare_uris` ###
 
 Makes all absolute URIs into links, even when not surrounded by
@@ -6927,6 +6958,23 @@
 Notes are not yet supported for other slide formats, but the notes
 will not appear on the slides themselves.
 
+### Speaker notes on the title slide (PowerPoint)
+
+For PowerPoint output, the title slide is generated from the document's
+YAML metadata block. To add speaker notes to this slide, use a `notes`
+field in the metadata:
+
+    ---
+    title: My Presentation
+    author: Jane Doe
+    notes: |
+      Welcome everyone to this presentation.
+
+      Remember to introduce yourself and mention the key topics.
+    ---
+
+The `notes` field can contain multiple paragraphs and Markdown formatting.
+
 ## Columns
 
 To put material in side by side columns, you can use a native
@@ -7594,6 +7642,24 @@
 
 To disable highlighting, use `--syntax-highlighting=none`.
 
+To use a format's idiomatic syntax highlighting instead of
+pandoc's built-in highlighting, use `--syntax-highlighting=idiomatic`.
+Currently, `idiomatic` only affects the following formats:
+
+- In reveal.js, it causes reveal.js's highlighting plugin to be
+  used for source code highlighting.  The style may be customized
+  by setting the `highlightjs-theme` variable.
+
+- In Typst, it causes Typst's built-in highlighting to be used.
+  (This is also the default for Typst.)
+
+- In LaTeX, it causes the [`listings`][] package  to be used. Note that
+  `listings` 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][].
+
+- In other formats, `idiomatic` will have the same result as `default`.
+
 [skylighting]: https://github.com/jgm/skylighting
 
 # Custom Styles
@@ -7769,10 +7835,10 @@
 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.
+Pandoc defaults to LaTeX to generate PDF. LaTeX's `\DocumentMetadata`
+interface supports PDF standards and tagging when using LuaLaTeX;
+set the `pdfstandard` variable to enable this (see below). For older
+LaTeX installations, alternative engines must be used.
 
 The PDF standards PDF/A and PDF/UA define further restrictions
 intended to optimize PDFs for archiving and accessibility. Tagging
@@ -7783,6 +7849,25 @@
 including the colorspace of embedded images. Pandoc cannot check
 this, and external programs must be used to ensure that generated
 PDFs are in compliance.
+
+## LaTeX
+
+Set the `pdfstandard` variable to produce tagged PDFs conforming
+to PDF/A, PDF/X, or PDF/UA standards. For example:
+
+    pandoc -V pdfstandard=ua-2 --pdf-engine=lualatex doc.md -o doc.pdf
+
+Multiple standards can be combined:
+
+    ---
+    pdfstandard:
+      - ua-2
+      - a-4f
+    ---
+
+The required PDF version is inferred automatically. This feature
+requires LuaLaTeX in TeX Live 2025 with LaTeX kernel 2025-06-01
+or newer.
 
 ## ConTeXt
 
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -135,7 +135,7 @@
 - `bbcode_phpbb` [BBCode
   (phpBB)](https://www.phpbb.com/community/help/bbcode)
 - `bbcode_steam` [BBCode
-  (Hubzilla)](https://hubzilla.org/help/member/bbcode)
+  (Steam)](https://steamcommunity.com/comment/ForumTopic/formattinghelp)
 - `bbcode_hubzilla` [BBCode
   (Hubzilla)](https://hubzilla.org/help/member/bbcode)
 - `bbcode_xenforo` [BBCode
diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,336 @@
 # Revision history for pandoc
 
+## pandoc 3.9 (2026-02-03)
+
+  * Add support for compiling pandoc to WASM. (To build, `make
+    pandoc.wasm`.) A stanza in `cabal.project` contains the
+    necessary build modifications. We owe this almost entirely to
+    TerrorJack, who created the original proof of context and
+    came up with the necessary build flags and patches to some
+    dependencies.
+
+    `pandoc.wasm` has almost all the power of regular pandoc. The
+    main limitations is that it operates in a WASM sandbox and thus
+    cannot use HTTP to fetch resources and cannot run system commands.
+    As a result, JSON filters cannot be used, `--embed-resources`
+    works only with resources that have been explicitly provided in
+    the WASM sandbox, and PDFs cannot be produced (since that requires
+    running external commands). However, Lua filters can be used,
+    as long as they do not run system commands.
+
+    A JavaScript bridge module, `wasm/pandoc.js`, is provided; this
+    handles the setup necessary to run `pandoc.wasm` in a browser.
+
+    In addition, a full-featured GUI interface is provided in the
+    `wasm` subdirectory. `make serve` from that directory and it
+    will run locally, or visit <https://pandoc.org/app>.  Note that
+    once the relevant code has been downloaded by the browser, it runs
+    entirely in the browser, and the conversions never touch a server.
+    The GUI was developed in interaction with Claude Code.  This app
+    includes a WASM version of Typst and can produce PDF output via
+    Typst.
+
+  * Defaults files may now be either JSON or YAML (though a `.yaml`
+    extension will still be assumed if the file has no extension).
+    The structure of a JSON defaults file is isomorphic to that of
+    a YAML defaults file.
+
+  * Variable expansion now works even for the `defaults` field of
+    defaults files (#8024, Jacob Larkin).
+
+  * `--extract-media` will now create a zip archive containing the media
+    (instead of a directory) if the path provided has a `.zip` extension.
+
+  * Processing with `--citeproc` is now affected by a
+    `reset-citation-positions` class on headings. When the
+    `reset-citation-positions` class is added to a top-level
+    heading, `--citeproc` will reset position information at that
+    point in the document. This is needed in order to ensure that
+    the first citation in a chapter to a work that has been cited
+    in a previous chapter will not be in abbreviated form.
+
+  * RST reader:
+
+    + Fix definition lists where term ends with `-` (#11323).
+      This reverts some old code giving special treatment to
+      lines ending in hyphens; I don't understand why it was
+      there, because rst2html does not seem to do this.
+
+  * HTML reader:
+
+    + Revert an earlier change that caused style tags in the body
+      to be ignored (#10643, #11246).
+    + Parse inline style elements as RawInline (#10643, #11246).
+
+  * Markdown reader:
+
+    + Fix parsing of inline math (`$...$`) (#11311, benniekiss).
+      Do not allow blank lines before closing `$` delimiter. This brings the
+      parser in line with the documentation.
+    + Allow superscripted spans (#11409). These were being parsed as inline
+      notes. Now we disallow an inline note followed by
+      attributes, as this is almost certainly meant to be a span.
+    + Support `alerts` extension for pandoc markdown (#9716). It is not
+      enabled by default.
+
+  * MediaWiki reader:
+
+    + Add behavior switches support (#11354, Anton Melnikov).
+      They add a field to metadata without producing any text.
+    + Handle non-recognized tags as plain text (#11299).
+    + Better handling of inline tags (#11299). `<mark>`, `<var>`,
+      `<samp>`, and `<kbd>` now produce Code or Span elements with
+      classes, which can be handled by multiple output formats,
+      instead of simply being parsed as raw HTML tags.
+
+  * RTF reader:
+
+    + Ensure a new paragraph on `\pard` (#11361, Tuong Nguyen Manh). New
+      paragraphs may start with `\pard` alone without an explicit paragraph
+      break with `\par` preceding it.
+    + Improve hyperlink parsing more (#10942, Tuong Nguyen Manh).
+      Both the field instruction and its result may be ungrouped.
+    + Fix bug where list items were incorporated into a following table
+      (#11364).
+
+  * ODT reader:
+
+    + Add table row and column spans (#11366, Tuong Nguyen Manh).
+      Parse the number-rows-spanned and number-columns-spanned attributes to
+      create Cells for the Table.
+
+  * DocBook reader:
+
+    + Support "role" attribute (#11255, Yann Trividic). The `role`
+      attribute is parsed and added to Pandoc AST elements, using a
+      wrapper Div if needed.
+    + Omit empty title when not required (#11422).
+      This affects example and sidebar elements.
+    + Fix adding wrong metadata (#11300, Tuong Nguyen Manh). Now
+      keep track of the current element stack to only add metadata
+      if inside an appropriate parent element.
+
+  * DocBook/JATS reader:
+
+    + Don't export surrounding space from inline elements (#11398). Previously
+      we would export leading and trailing space inside elements like
+      emphasis or ulink so they appeared outside the resulting pandoc Inline
+      (Emph or Link). This is not really motivated; DocBook and XML in general
+      treats leading and trailing whitespace in this context as significant.
+
+  * Docx reader:
+
+    + Handle tables without `tblGrid` (#11380).
+    + Look inside v:rect as well as v:shape (#11412).
+
+  * LaTeX reader:
+
+    + Handle more quote macros from fontspec and ngerman babel (#6120).
+
+  * Org reader:
+
+    + Don't include 'example' class when parsing org example blocks (#11339).
+      These are just unmarked code blocks.
+
+  * Texinfo writer:
+
+    + Improve handling of certain code blocks (#11312).
+      MediaWiki, for example, will parse a code block containing
+      formatting as a sequence of Code elements separated by
+      LineBreaks. For this we now use a texinfo example block.
+
+  * Typst writer:
+
+    + Escape hyphens when needed (#11334).
+
+  * HTML writer:
+
+    + Include all classes on highlighted code elements (#11423). Previously,
+      only the language class was included, and the others were dropped.
+    + Slide formats: Make `. . .` pause work in nested blocks (#7201, #7582).
+    + For revealjs, add idiomatic highlight.js support (#11420, Claude Opus 4.5).
+      When using `--syntax-highlighting=idiomatic` with reveal.js output, pandoc
+      now generates HTML compatible with reveal.js's built-in highlight.js plugin:
+      Code blocks use `<pre><code class="language-X">` format.
+      The template loads highlight.js CSS, JS, and RevealHighlight plugin.
+
+  * Markdown writer:
+
+    + Ensure that `\ ` line breaks are used for commonmark.
+    + Use setext for headers containing line breaks for commonmark (#11341).
+    + Allow display math to start/end with space (#11384).
+      This reverts to earlier < 3.7 behavior.
+    + Properly handle tables with footers (#11416).
+
+  * JATS writer:
+
+    + Fix XML output for nested figures (#11362, Albert Krewinkel).
+      Subfigures are now wrapped inside a `<fig-group>` element. Furthermore,
+      figure content that isn't allowed as children of `<fig>` elements, such as
+      raw text, gets wrapped in `<p>` elements to ensure schema-conforming XML.
+
+  * AsciiDoc writer:
+
+    + Use doubled delims in more contexts (#11362). Also escape the `#` character.
+    + Use a span with role for SmallCaps (#11374).
+    + Export spaces inside delimited constructs like emph.
+
+  * Docx writer:
+
+    + Skip directory entries when building media overrides (#11379, You Jiangbin).
+      Pandoc's docx writer was previously adding an `<Override>` for
+      `/word/media/` in `[Content_Types].xml` when the reference doc contains
+      media, which violates OPC rules and causes Word to report corruption.
+    + Refactor the monolithic `writeDocx` into a number of smaller functions
+      (Claude Opus 4.5).
+    + Replace generic XML traversal with direct path navigation (Claude
+      Opus 4.5). Instead of using Data.Generics `everywhere` to traverse the
+      entire XML tree when setting language attributes, navigate directly to
+      the known path `w:docDefaults/w:rPr/w:lang`. This is more efficient and
+      removes the dependency on `Data.Generics`.
+
+  * EPUB writer:
+
+    + Don't use footnote backlinks for EPUBv3. Here we use aside elements,
+      which are popups, and the backlinks are not needed; in some readers
+      they cause a redundant number to appear, since the reader adds a note number.
+
+  * MediaWiki writer:
+
+    + Use Doc Text instead of Text for document construction (with Claude Open 4.5).
+      This refactors the writer to use Text.DocLayout combinators (vcat, hcat,
+      literal, blankline, cr, chomp) for building output, following the pattern
+      used by other text format writers (RST, Markdown, Man). This enables
+      better control over line spacing and paragraph separation.
+    + Improve blank space around div elements (#11417). This is merely cosmetic.
+
+  * PPTX writer:
+
+    + Support notes field in metadata for title slide (#5844, Chris Callison-Burch).
+      This adds support for a `notes` field in the YAML metadata block that will
+      be used as speaker notes for the title slide in PowerPoint output.
+      Previously, there was no way to add speaker notes to the title slide since
+      it is generated from metadata rather than from content blocks.
+
+  * LaTeX writer:
+
+    + Add PDF standard support via DocumentMetadata (#11407, Gordon Woodhull
+      with Claude Opus 4.5). The `pdfstandard` variable can be
+      used to specify PDF standards (PDF/A, PDF/X, PDF/UA) in
+      LaTeX output. This uses LaTeX's `\DocumentMetadata`
+      command, which requires LuaLaTeX. PDF version requirements
+      are automatically inferred, but can be explicitly overridden.
+      Automatic tagging is added for standards that require it.
+
+  * Typst template:
+
+    + Fix keywords usage. (#11317, har7an).
+    + Disable hyphenation for title, subtitle (#11375).
+    + Improve accessibility of definition lists (#11436). Before this
+      change, our show rule produced an error when typst was run with
+      `--pdf-standard=ua1`.
+
+  * HTML5 template:
+
+    + Conditionally include lang attribute, instead of providing
+      it with an empty value.
+
+  * JATS template:
+
+    + Fix author prefix placeholder (#11381, Christophe Dervieux).
+
+  * Text.Pandoc.Citeproc:
+
+    + Fix biblatex parsing of `@commentary` entries (#11322).
+    + Fix typo affecting `jurisdiction` biblatex type (#11321).
+    + Avoid adding an extra space at the beginning of a Cite.
+
+  * Text.Pandoc.Shared:
+
+    + Export `hasLineBreaks` [API change]. This was formerly
+      defined in the DocBook writer but more generally useful.
+
+  * Text.Pandoc.PDF:
+
+    + Add `SOURCE_DATE_EPOCH` to verbose environment variable info.
+
+  * Text.Pandoc.Class:
+
+    + Factor out `openURL` into Text.Pandoc.Class.IO.HTTP (unexported module).
+
+  * Lua subsystem (Albert Krewinkel):
+
+    + Mark readers and writers with their types (#11367). The
+      `pandoc.readers` and `pandoc.writers` maps now have string values
+      instead of boolean values. The string signals the type of the
+      reader/writer, `"text"` for TextReader/TextWriter and `"bytestring"`
+      for ByteStringReader/ByteStringWriter.
+    + Support equallity checks of LogMessage objects.
+    + Add function `pandoc.with_state` (#10859). The function allows
+      to run a callback with a modified pandoc state. This provides
+      the ability to temporarily modify the resource path, the user
+      data directory, and the HTTP request headers.
+    + Let `pandoc.with_state` error on unknown options (#11376).
+    + Add function `pandoc.utils.documentation` (#10999).
+      This is now used to generate much of the Lua API documentation.
+
+  * Text.Pandoc.App:
+
+    + Remove redundant check for asciidoc in UnknownReader.
+
+  * Text.Pandoc.Logging:
+
+    + Add `pretty` field to ToJSON instance for LogMessage.
+      This just reproduces the output of `logMessage`, for convenience for those
+      who are using the JSON output outside of Haskell.
+
+  * Text.Pandoc.Error:
+
+    + Change PandocHttpError constructor to take a Text instead of an
+      HttpException as the second argument (see #10980) [API change]
+      Motivation: exposing HttpException in the public API makes it
+      difficult to provide a version of pandoc that can be compiled
+      to wasm, which currently can't handle the network libraries.
+    + Define `displayException` for PandocError. This is a behavior change,
+      not an API change, since there was already a definition that defaulted
+      to using Show. The change here is that we use `renderError` for
+      a more human-readable version.
+
+  * Drop support for compilation with GHC versions < 9.6.
+
+  * Use released citeproc 0.13, djot 0.1.3, skylighting-format-blaze-html
+    0.1.2 (see #11423), texmath 0.13.1, asciidoc 0.1.0.1, typst-0.9,
+    hslua 2.5.
+
+  * Add `-Wno-deriving-typeble` to cabal ghc-options for ghc >= 9.12.
+
+  * Add `http` cabal flag (#10980). This allows pandoc to be
+    compiled without support for making HTTP requests, which is
+    useful when WASM is the target.
+
+  * pandoc-lua-engine has a new build flag `repl`, allowing support
+    for the Lua repl to be disabled for the wasm build.
+
+  * We now sign Windows artifacts with a code signing
+    certificate provided by SignPath.
+
+  * Mention Excel in cabal description.
+
+  * Makefile: remove some obsolete targets.
+
+  * MANUAL.txt:
+
+    + Fix typo about `--chunk-template` (#11358, Albert Lei).
+    + Fix link for bbcode_steam (#11389).
+    + Small rewrite of syntax-highlighting info.
+    + Fix defaults.yaml example for `wrap`.
+
+  * Fix a few mistakes in the contributing docs. (#11318, har7an).
+
+  * Fix a couple small errors in `doc/lua-filters.md`,
+    `doc/custom-writers.md`, and `doc/custom-readers.md`
+    (#11408, #11388).
+
 ## pandoc 3.8.3 (2025-12-01)
 
   * Add `asciidoc` as an input format (#1456).
diff --git a/data/templates/default.html5 b/data/templates/default.html5
--- a/data/templates/default.html5
+++ b/data/templates/default.html5
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<html xmlns="http://www.w3.org/1999/xhtml" lang="$lang$" xml:lang="$lang$"$if(dir)$ dir="$dir$"$endif$>
+<html xmlns="http://www.w3.org/1999/xhtml"$if(lang)$ lang="$lang$" xml:lang="$lang$"$endif$$if(dir)$ dir="$dir$"$endif$>
 <head>
   <meta charset="utf-8" />
   <meta name="generator" content="pandoc" />
diff --git a/data/templates/default.jats_articleauthoring b/data/templates/default.jats_articleauthoring
--- a/data/templates/default.jats_articleauthoring
+++ b/data/templates/default.jats_articleauthoring
@@ -31,7 +31,7 @@
 <surname>$if(author.non-dropping-particle)$${author.non-dropping-particle} $endif$${author.surname}</surname>
 <given-names>${author.given-names}$if(author.dropping-particle)$ ${author.dropping-particle}$endif$</given-names>
 $if(author.prefix)$
-<prefix>${author.suffix}</prefix>
+<prefix>${author.prefix}</prefix>
 $endif$
 $if(author.suffix)$
 <suffix>${author.suffix}</suffix>
diff --git a/data/templates/default.latex b/data/templates/default.latex
--- a/data/templates/default.latex
+++ b/data/templates/default.latex
@@ -1,3 +1,4 @@
+$document-metadata.latex()$
 $passoptions.latex()$
 \documentclass[
 $for(babel-otherlangs)$
diff --git a/data/templates/default.revealjs b/data/templates/default.revealjs
--- a/data/templates/default.revealjs
+++ b/data/templates/default.revealjs
@@ -29,6 +29,9 @@
 $else$
   <link rel="stylesheet" href="$revealjs-url$/dist/theme/black.css" id="theme">
 $endif$
+$if(highlight-js)$
+  <link rel="stylesheet" href="$revealjs-url$/plugin/highlight/$highlightjs-theme$.css">
+$endif$
 $for(css)$
   <link rel="stylesheet" href="$css$"/>
 $endfor$
@@ -87,6 +90,9 @@
 $if(mathjax)$
   <script src="$revealjs-url$/plugin/math/math.js"></script>
 $endif$
+$if(highlight-js)$
+  <script src="$revealjs-url$/plugin/highlight/highlight.js"></script>
+$endif$
 
   <script>
 
@@ -320,6 +326,9 @@
         plugins: [
 $if(mathjax)$
           RevealMath,
+$endif$
+$if(highlight-js)$
+          RevealHighlight,
 $endif$
           RevealNotes,
           RevealSearch,
diff --git a/data/templates/default.typst b/data/templates/default.typst
--- a/data/templates/default.typst
+++ b/data/templates/default.typst
@@ -1,13 +1,9 @@
 #let horizontalrule = line(start: (25%,0%), end: (75%,0%))
 
-#show terms: it => {
-  it.children
-    .map(child => [
-      #strong[#child.term]
-      #block(inset: (left: 1.5em, top: -0.4em))[#child.description]
-      ])
-    .join()
-}
+#show terms.item: it => block(breakable: false)[
+  #text(weight: "bold")[#it.term]
+  #block(inset: (left: 1.5em, top: -0.4em))[#it.description]
+]
 
 #set table(
   inset: 6pt,
@@ -65,7 +61,7 @@
     ),
 $endif$
 $if(keywords)$
-  keywords: ($for(keywords)$$keyword$$sep$,$endfor$),
+  keywords: ($for(keywords)$$keywords$$sep$,$endfor$),
 $endif$
 $if(date)$
   date: [$date$],
diff --git a/data/templates/document-metadata.latex b/data/templates/document-metadata.latex
new file mode 100644
--- /dev/null
+++ b/data/templates/document-metadata.latex
@@ -0,0 +1,20 @@
+$--
+$-- PDF standard support (PDF/A, PDF/UA, PDF/X)
+$-- Requires LuaLaTeX and recent LaTeX (2023+)
+$--
+$if(pdfstandard)$
+\DocumentMetadata{
+$if(pdfstandard.version)$
+  pdfversion=$pdfstandard.version$,
+$endif$
+$if(pdfstandard.standards)$
+  pdfstandard={$for(pdfstandard.standards)$$it$$sep$,$endfor$},
+$endif$
+$if(pdfstandard.tagging)$
+  tagging=on,
+$endif$
+$if(lang)$
+  lang=$lang$,
+$endif$
+  xmp=true}
+$endif$
diff --git a/data/templates/template.typst b/data/templates/template.typst
--- a/data/templates/template.typst
+++ b/data/templates/template.typst
@@ -76,14 +76,14 @@
   block(below: 1em, width: 100%)[
     #if title != none {
       align(center, block[
-          #text(weight: "bold", size: 1.5em)[#title #if thanks != none {
+          #text(weight: "bold", size: 1.5em, hyphenate: false)[#title #if thanks != none {
               footnote(thanks, numbering: "*")
               counter(footnote).update(n => n - 1)
             }]
           #(
             if subtitle != none {
               parbreak()
-              text(weight: "bold", size: 1.25em)[#subtitle]
+              text(weight: "bold", size: 1.25em, hyphenate: false)[#subtitle]
             }
            )])
     }
diff --git a/pandoc.cabal b/pandoc.cabal
--- a/pandoc.cabal
+++ b/pandoc.cabal
@@ -1,6 +1,6 @@
 cabal-version:   2.4
 name:            pandoc
-version:         3.8.3
+version:         3.9
 build-type:      Simple
 license:         GPL-2.0-or-later
 license-file:    COPYING.md
@@ -11,9 +11,7 @@
 stability:       alpha
 homepage:        https://pandoc.org
 category:        Text
-tested-with:     GHC == 8.10.7, GHC == 9.0.2,
-                 GHC == 9.2.5, GHC == 9.4.4, GHC == 9.6.3,
-                 GHC == 9.8.2, GHC == 9.10.1
+tested-with:     GHC == 9.6.7, GHC == 9.8.4, GHC == 9.10.3, GHC == 9.12.2
 synopsis:        Conversion between markup formats
 description:     Pandoc is a Haskell library for converting from one markup
                  format to another.  The formats it can handle include
@@ -38,7 +36,7 @@
                    Vimwiki, XWiki, ZimWiki, Jira wiki, Creole)
                  - Slide show formats (LaTeX Beamer, PowerPoint, Slidy,
                    reveal.js, Slideous, S5, DZSlides)
-                 - Data formats (CSV and TSV tables)
+                 - Data formats (CSV and TSV tables, Excel spreadsheets)
                  - PDF (via external programs such as pdflatex or wkhtmltopdf)
                  .
                  Pandoc can convert mathematical content in documents
@@ -103,6 +101,7 @@
                  data/templates/affiliations.jats
                  data/templates/default.markua
                  data/templates/default.typst
+                 data/templates/document-metadata.latex
                  data/templates/template.typst
                  data/templates/common.latex
                  data/templates/hypersetup.latex
@@ -250,6 +249,8 @@
                  test/command/defaults7.yaml
                  test/command/defaults8.yaml
                  test/command/defaults9.yaml
+                 test/command/8024a.yaml
+                 test/command/8024b.yaml
                  test/command/3533-rst-csv-tables.csv
                  test/command/3880.txt
                  test/command/5182.txt
@@ -457,9 +458,13 @@
   Description:   Embed data files in binary for relocatable executable.
   Default:       False
 
+flag http
+  Description:   Support for fetching resources using HTTP.
+  Default:       True
+
 common common-options
   default-language: Haskell2010
-  build-depends:    base         >= 4.12 && < 5
+  build-depends:    base         >= 4.18 && < 5
   ghc-options:      -Wall -fno-warn-unused-do-bind
                     -Wincomplete-record-updates
                     -Wnoncanonical-monad-instances
@@ -470,12 +475,11 @@
                     -Wmissing-signatures
                     -fhide-source-paths
                     -Wmissing-export-lists
-
-  if impl(ghc >= 8.10)
-    ghc-options:    -Wunused-packages
+                    -Wunused-packages
+                    -Winvalid-haddock
 
-  if impl(ghc >= 9.0)
-    ghc-options:    -Winvalid-haddock
+  if impl(ghc >= 9.12)
+    ghc-options:    -Wno-deriving-typeable
 
   if os(windows)
     cpp-options:      -D_WINDOWS
@@ -514,13 +518,12 @@
                  blaze-markup          >= 0.8      && < 0.9,
                  bytestring            >= 0.9      && < 0.13,
                  case-insensitive      >= 1.2      && < 1.3,
-                 citeproc              >= 0.12     && < 0.13,
+                 citeproc              >= 0.13     && < 0.14,
                  commonmark            >= 0.2.6.1  && < 0.3,
                  commonmark-extensions >= 0.2.6    && < 0.3,
                  commonmark-pandoc     >= 0.2.3    && < 0.3,
                  containers            >= 0.6.0.1  && < 0.9,
                  crypton               >= 0.30     && < 1.1,
-                 crypton-connection    >= 0.3.1    && < 0.5,
                  data-default          >= 0.4      && < 0.9,
                  deepseq               >= 1.3      && < 1.6,
                  directory             >= 1.2.3    && < 1.4,
@@ -532,14 +535,11 @@
                  filepath              >= 1.1      && < 1.6,
                  gridtables            >= 0.1      && < 0.2,
                  haddock-library       >= 1.10     && < 1.12,
-                 http-client           >= 0.4.30   && < 0.8,
-                 http-client-tls       >= 0.2.4    && < 0.4,
                  http-types            >= 0.8      && < 0.13,
                  ipynb                 >= 0.2      && < 0.3,
                  jira-wiki-markup      >= 1.5.1    && < 1.6,
                  mime-types            >= 0.1.1    && < 0.2,
                  mtl                   >= 2.2      && < 2.4,
-                 network               >= 2.6      && < 3.3,
                  network-uri           >= 2.6      && < 2.8,
                  pandoc-types          >= 1.23.1   && < 1.24,
                  parsec                >= 3.1      && < 3.2,
@@ -555,7 +555,7 @@
                  syb                   >= 0.1      && < 0.8,
                  tagsoup               >= 0.14.6   && < 0.15,
                  temporary             >= 1.1      && < 1.4,
-                 texmath               >= 0.13.0.2 && < 0.14,
+                 texmath               >= 0.13.1   && < 0.14,
                  text                  >= 1.1.1.0  && < 2.2,
                  text-conversions      >= 0.3      && < 0.4,
                  time                  >= 1.5      && < 1.16,
@@ -567,18 +567,25 @@
                  zip-archive           >= 0.4.3.1  && < 0.5,
                  zlib                  >= 0.5      && < 0.8,
                  xml                   >= 1.3.12   && < 1.4,
-                 typst                 >= 0.8.1    && < 0.9,
+                 typst                 >= 0.9      && < 0.10,
                  vector                >= 0.12     && < 0.14,
-                 djot                  >= 0.1.2.4  && < 0.2,
-                 asciidoc              >= 0.1      && < 0.2,
-                 tls                   >= 2.0.1    && < 2.2,
-                 crypton-x509-system   >= 1.6.7    && < 1.7
+                 djot                  >= 0.1.3    && < 0.2,
+                 asciidoc              >= 0.1.0.1  && < 0.2
 
   if !os(windows)
     build-depends:  unix >= 2.4 && < 2.9
   if flag(embed_data_files)
      cpp-options:   -DEMBED_DATA_FILES
      other-modules: Text.Pandoc.Data.BakedIn
+  if flag(http)
+     cpp-options:   -DPANDOC_HTTP_SUPPORT
+     build-depends:
+                 crypton-connection    >= 0.3.1    && < 0.5,
+                 crypton-x509-system   >= 1.6.7    && < 1.9,
+                 http-client           >= 0.4.30   && < 0.8,
+                 http-client-tls       >= 0.2.4    && < 0.4,
+                 network               >= 2.6      && < 3.3,
+                 tls                   >= 2.0.1    && < 2.2
   hs-source-dirs:  src
 
   exposed-modules: Text.Pandoc,
diff --git a/src/Text/Pandoc/App/CommandLineOptions.hs b/src/Text/Pandoc/App/CommandLineOptions.hs
--- a/src/Text/Pandoc/App/CommandLineOptions.hs
+++ b/src/Text/Pandoc/App/CommandLineOptions.hs
@@ -31,7 +31,8 @@
          defConfig, Indent(..), NumberFormat(..))
 import Data.Bifunctor (second)
 import Data.Char (toLower)
-import Data.List (intercalate, sort, foldl')
+import Data.List (intercalate, sort)
+import qualified Data.List as L
 #ifdef _WINDOWS
 import Data.List (isPrefixOf)
 #endif
@@ -100,7 +101,7 @@
 
   if (null errors && null unknownOptionErrors)
      then -- thread option data structure through all supplied option actions
-       runExceptT $ adjustOpts <$> (foldl' (>>=) (return defaults) actions)
+       runExceptT $ adjustOpts <$> (L.foldl' (>>=) (return defaults) actions)
      else return $ Left $ OptError $ PandocOptionError $ T.pack $
              concat errors ++ unlines unknownOptionErrors ++
              ("Try " ++ prg ++ " --help for more information.")
diff --git a/src/Text/Pandoc/App/Opt.hs b/src/Text/Pandoc/App/Opt.hs
--- a/src/Text/Pandoc/App/Opt.hs
+++ b/src/Text/Pandoc/App/Opt.hs
@@ -28,12 +28,12 @@
           , fullDefaultsPath
           ) where
 import Control.Monad.Except (throwError)
-import Control.Monad.Trans (MonadIO, liftIO)
+import Control.Monad.Trans (MonadIO, liftIO, lift)
 import Control.Monad ((>=>), foldM)
 import Control.Monad.State.Strict (StateT, modify, gets)
 import System.FilePath ( addExtension, (</>), takeExtension, takeDirectory )
 import System.Directory ( canonicalizePath )
-import Data.Char (toLower)
+import Data.Char (toLower, isSpace)
 import Data.Maybe (fromMaybe)
 import GHC.Generics hiding (Meta)
 import Text.Pandoc.Filter (Filter (..))
@@ -62,7 +62,7 @@
 import qualified Data.ByteString.Char8 as B8
 import Text.Pandoc.Definition (Meta(..), MetaValue(..))
 import Data.Aeson (defaultOptions, Options(..), Result(..),
-                   genericToJSON, fromJSON, camelTo2)
+                   genericToJSON, fromJSON, camelTo2, eitherDecodeStrict)
 import Data.Aeson.TH (deriveJSON)
 import Control.Applicative ((<|>))
 import Data.Yaml
@@ -395,39 +395,49 @@
                 }
 
  where
-  resolveVars :: FilePath -> StateT DefaultsState m FilePath
-  resolveVars [] = return []
-  resolveVars ('$':'{':xs) =
+  resolveVars = expandVars (optDataDir opt)
+  resolveVarsInFilter (JSONFilter fp) =
+    JSONFilter <$> resolveVars fp
+  resolveVarsInFilter (LuaFilter fp) =
+    LuaFilter <$> resolveVars fp
+  resolveVarsInFilter CiteprocFilter = return CiteprocFilter
+
+
+expandVars :: (PandocMonad m, MonadIO m)
+           => Maybe FilePath
+           -> FilePath
+           -> StateT DefaultsState m FilePath
+expandVars _ [] = return []
+expandVars mbDataDir ('$':'{':xs) =
     let (ys, zs) = break (=='}') xs
      in if null zs
            then return $ '$':'{':xs
            else do
-             val <- lookupEnv' ys
-             (val ++) <$> resolveVars (drop 1 zs)
-  resolveVars (c:cs) = (c:) <$> resolveVars cs
-  lookupEnv' :: String -> StateT DefaultsState m String
-  lookupEnv' "." = do
+             val <- expandEnv mbDataDir ys
+             (val ++) <$> expandVars mbDataDir (drop 1 zs)
+expandVars mbDataDir (c:cs) = (c:) <$> expandVars mbDataDir cs
+
+expandEnv :: (PandocMonad m, MonadIO m)
+          => Maybe FilePath
+          -> String
+          -> StateT DefaultsState m String
+expandEnv _ "." = do
     mbCurDefaults <- gets curDefaults
     maybe (return "")
           (fmap takeDirectory . liftIO . canonicalizePath)
           mbCurDefaults
-  lookupEnv' "USERDATA" = do
-    mbodatadir <- mapM resolveVars oDataDir
-    mbdatadir  <- getUserDataDir
+expandEnv mbDataDir "USERDATA" = do
+    mbodatadir <- mapM (expandVars mbDataDir) mbDataDir
+    mbdatadir'  <- getUserDataDir
     defdatadir <- liftIO defaultUserDataDir
-    return $ fromMaybe defdatadir (mbodatadir <|> mbdatadir)
-  lookupEnv' v = do
+    return $ fromMaybe defdatadir (mbodatadir <|> mbdatadir')
+expandEnv _ v = do
     mbval <- fmap T.unpack <$> lookupEnv (T.pack v)
     case mbval of
       Nothing -> do
         report $ EnvironmentVariableUndefined (T.pack v)
         return mempty
       Just x  -> return x
-  resolveVarsInFilter (JSONFilter fp) =
-    JSONFilter <$> resolveVars fp
-  resolveVarsInFilter (LuaFilter fp) =
-    LuaFilter <$> resolveVars fp
-  resolveVarsInFilter CiteprocFilter = return CiteprocFilter
 
 
 
@@ -438,8 +448,9 @@
 parseDefaults n dataDir = parseDefsNames n >>= \ds -> return $ \o -> do
   -- get parent defaults:
   defsParent <- gets $ fromMaybe "" . curDefaults
+  expandedDataDir <- mapM (expandVars dataDir) dataDir
   -- get child defaults:
-  defsChildren <- mapM (fullDefaultsPath dataDir) ds
+  defsChildren <- mapM (\d -> expandVars expandedDataDir d >>= lift . fullDefaultsPath expandedDataDir) ds
   -- expand parent in defaults inheritance graph by children:
   defsGraph <- gets inheritanceGraph
   let defsGraphExp = expand defsGraph defsChildren defsParent
@@ -858,11 +869,19 @@
   setVerbosity $ optVerbosity opt
   modify $ \defsState -> defsState{ curDefaults = Just file }
   inp <- readFileStrict file
-  case decodeEither' (B8.unlines . takeWhile (/= "...") . B8.lines $ inp) of
-      Right f -> f opt
-      Left err'  -> throwError $
-         PandocParseError
-             $ T.pack $ Data.Yaml.prettyPrintParseException err'
+  let isJSON = B8.take 1 (B8.dropWhile isSpace inp) == "{"
+  if isJSON
+     then
+       case eitherDecodeStrict inp of
+         Right f -> f opt
+         Left err' -> throwError $ PandocParseError $ T.pack $
+            "Error parsing " <> file <> ":\n" <> err'
+     else
+       case decodeEither' (B8.unlines . takeWhile (/= "...") . B8.lines $ inp) of
+           Right f -> f opt
+           Left err'  -> throwError $ PandocParseError $ T.pack $
+                  "Error parsing " <> file <> ":\n" <>
+                  Data.Yaml.prettyPrintParseException err'
 
 fullDefaultsPath :: (PandocMonad m, MonadIO m)
                  => Maybe FilePath
diff --git a/src/Text/Pandoc/Citeproc.hs b/src/Text/Pandoc/Citeproc.hs
--- a/src/Text/Pandoc/Citeproc.hs
+++ b/src/Text/Pandoc/Citeproc.hs
@@ -30,7 +30,7 @@
 import Text.Pandoc.Extensions (pandocExtensions)
 import Text.Pandoc.Logging (LogMessage(..))
 import Text.Pandoc.Options (ReaderOptions(..))
-import Text.Pandoc.Shared (stringify, tshow)
+import Text.Pandoc.Shared (stringify, tshow, makeSections)
 import Data.Containers.ListUtils (nubOrd)
 import Text.Pandoc.Walk (query, walk, walkM)
 import Control.Applicative ((<|>))
@@ -295,10 +295,22 @@
              -> M.Map Text ItemId
              -> Pandoc
              -> [Citeproc.Citation Inlines]
-getCitations locale otherIdsMap = Foldable.toList . query getCitation
+getCitations locale otherIdsMap (Pandoc meta blocks) =
+  Foldable.toList (query getCitation meta <>
+                   foldMap handleBlock (makeSections False Nothing blocks))
  where
+  handleBlock :: Block -> Seq.Seq (Citeproc.Citation Inlines)
+  handleBlock b@(Div (_,cls,_) _)
+    | "section" `elem` cls
+    , "reset-citation-positions" `elem` cls =
+      case Seq.viewl (query getCitation b) of
+        x Seq.:< xs -> addResetTo x Seq.<| xs
+        Seq.EmptyL -> mempty
+  handleBlock b = query getCitation b
+  addResetTo citation = citation{ Citeproc.citationResetPosition = True }
   getCitation (Cite cs _fallback) = Seq.singleton $
     Citeproc.Citation { Citeproc.citationId = Nothing
+                      , Citeproc.citationResetPosition = False
                       , Citeproc.citationPrefix = Nothing
                       , Citeproc.citationSuffix = Nothing
                       , Citeproc.citationNoteNumber =
@@ -580,7 +592,8 @@
       = Cite (c:cs) (addCommas (needsPeriod zs) ils) :
         addParens zs
     | otherwise
-      = Cite (c:cs) (concatMap noteInParens ils) : addParens zs
+      = Cite (c:cs) (dropWhile (== Space) (concatMap noteInParens ils))
+         : addParens zs
   addParens (x:xs) = x : addParens xs
 
   removeNotes (Span ("",["csl-note"],[]) ils) = Span ("",[],[]) ils
diff --git a/src/Text/Pandoc/Citeproc/BibTeX.hs b/src/Text/Pandoc/Citeproc/BibTeX.hs
--- a/src/Text/Pandoc/Citeproc/BibTeX.hs
+++ b/src/Text/Pandoc/Citeproc/BibTeX.hs
@@ -52,7 +52,8 @@
 import           Data.Char              (isAlphaNum, isDigit, isLetter,
                                          isUpper, toLower, toUpper,
                                          isLower, isPunctuation, isSpace)
-import           Data.List              (foldl', intercalate, intersperse)
+import           Data.List              (intercalate, intersperse)
+import qualified Data.List as L
 import           Safe                   (readMay)
 import           Text.Printf            (printf)
 import           Text.DocLayout         (literal, hsep, nest, hang, Doc(..),
@@ -117,7 +118,7 @@
       "manuscript"        -> "unpublished"
       "graphic"           | variant == Biblatex -> "artwork"
       "song"              | variant == Biblatex -> "music"
-      "legal_case"        | variant == Biblatex -> "jurisdictionN"
+      "legal_case"        | variant == Biblatex -> "jurisdiction"
       "legislation"       | variant == Biblatex -> "legislation"
       "treaty"            | variant == Biblatex -> "legal"
       "personal_communication" | variant == Biblatex -> "letter"
@@ -821,7 +822,7 @@
 bibComment = do
   cistring "comment"
   spaces'
-  void inBraces <|> bibSkip <|> return ()
+  void inBraces
 
 bibPreamble :: BibParser ()
 bibPreamble = do
@@ -1115,7 +1116,7 @@
 toLiteralList _ = mzero
 
 concatWith :: Char -> [Inlines] -> Inlines
-concatWith sep = foldl' go mempty
+concatWith sep = L.foldl' go mempty
   where go :: Inlines -> Inlines -> Inlines
         go accum s
           | s == mempty = accum
diff --git a/src/Text/Pandoc/Citeproc/Locator.hs b/src/Text/Pandoc/Citeproc/Locator.hs
--- a/src/Text/Pandoc/Citeproc/Locator.hs
+++ b/src/Text/Pandoc/Citeproc/Locator.hs
@@ -11,7 +11,7 @@
 import Text.Pandoc.Citeproc.Util (splitStrWhen)
 import Data.Text (Text)
 import qualified Data.Text as T
-import Data.List (foldl')
+import qualified Data.List as L
 import Text.Pandoc.Definition
 import Text.Pandoc.Parsing
 import Text.Pandoc.Shared (stringify)
@@ -196,7 +196,7 @@
   where
       except = notFollowedBy pBraces >> p
       -- outer and inner
-      surround = foldl' (\a (open, close) -> sur open close except <|> a)
+      surround = L.foldl' (\a (open, close) -> sur open close except <|> a)
                        except
                        braces
 
diff --git a/src/Text/Pandoc/Citeproc/Name.hs b/src/Text/Pandoc/Citeproc/Name.hs
--- a/src/Text/Pandoc/Citeproc/Name.hs
+++ b/src/Text/Pandoc/Citeproc/Name.hs
@@ -30,7 +30,7 @@
 import qualified Data.Text              as T
 import           Data.List.Split        (splitWhen, wordsBy)
 import Data.Char (isUpper, isDigit)
-import Data.List (foldl')
+import qualified Data.List as L
 
 emptyName :: Name
 emptyName =
@@ -83,7 +83,7 @@
         ag{ nameSuffix = Just $ stringify xs }
       addPart ag (Space : xs) = addPart ag xs
       addPart ag _ = ag
-  return $ foldl' addPart emptyName commaParts
+  return $ L.foldl' addPart emptyName commaParts
 -- First von Last
 -- von Last, First
 -- von Last, Jr ,First
diff --git a/src/Text/Pandoc/Class/CommonState.hs b/src/Text/Pandoc/Class/CommonState.hs
--- a/src/Text/Pandoc/Class/CommonState.hs
+++ b/src/Text/Pandoc/Class/CommonState.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE CPP #-}
 {- |
 Module      : Text.Pandoc.Class.CommonState
 Copyright   : Copyright (C) 2016-2020 Jesse Rosenthal, John MacFarlane
@@ -23,7 +24,9 @@
 import Text.Pandoc.MediaBag (MediaBag)
 import Text.Pandoc.Logging (LogMessage, Verbosity (WARNING))
 import Text.Pandoc.Translations.Types (Translations)
+#ifdef PANDOC_HTTP_SUPPORT
 import Network.HTTP.Client (Manager)
+#endif
 
 -- | 'CommonState' represents state that is used by all
 -- instances of 'PandocMonad'.  Normally users should not
@@ -51,9 +54,11 @@
   , stResourcePath :: [FilePath]
     -- ^ Path to search for resources like
     -- included images
+#ifdef PANDOC_HTTP_SUPPORT
   , stManager      :: Maybe Manager
     -- ^ Manager for HTTP client; this needs to persist across many requests
     -- for efficiency.
+#endif
   , stVerbosity    :: Verbosity
     -- ^ Verbosity level
   , stTrace        :: Bool
@@ -79,7 +84,9 @@
   , stInputFiles = []
   , stOutputFile = Nothing
   , stResourcePath = ["."]
+#ifdef PANDOC_HTTP_SUPPORT
   , stManager = Nothing
+#endif
   , stVerbosity = WARNING
   , stTrace = False
   }
diff --git a/src/Text/Pandoc/Class/IO.hs b/src/Text/Pandoc/Class/IO.hs
--- a/src/Text/Pandoc/Class/IO.hs
+++ b/src/Text/Pandoc/Class/IO.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE CPP #-}
+{-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE OverloadedStrings #-}
 {- |
 Module      : Text.Pandoc.Class.IO
@@ -37,31 +38,37 @@
 
 import Control.Monad.Except (throwError)
 import Control.Monad.IO.Class (MonadIO, liftIO)
-import Data.ByteString.Lazy (toChunks)
 import Data.Text (Text, pack, unpack)
 import Data.Time (TimeZone, UTCTime)
 import Data.Unique (hashUnique)
+#ifdef PANDOC_HTTP_SUPPORT
+import Data.ByteString.Lazy (toChunks)
+import System.Environment (getEnv)
+import Data.Default (def)
 import Network.Connection (TLSSettings(..))
 import qualified Network.TLS as TLS
 import qualified Network.TLS.Extra as TLS
+import System.X509 (getSystemCertificateStore)
 import Network.HTTP.Client
        (httpLbs, Manager, responseBody, responseHeaders,
-        Request(port, host, requestHeaders), parseUrlThrow, newManager)
+        Request(port, host, requestHeaders), parseUrlThrow, newManager, HttpException)
 import Network.HTTP.Client.Internal (addProxy)
 import Network.HTTP.Client.TLS (mkManagerSettings)
 import Network.HTTP.Types.Header ( hContentType )
 import Network.Socket (withSocketsDo)
+import Text.Pandoc.Class.CommonState (CommonState (..))
+import Text.Pandoc.Class.PandocMonad ( getsCommonState, modifyCommonState )
+import qualified Data.CaseInsensitive as CI
+#endif
 import Network.URI (URI(..), parseURI, unEscapeString)
 import System.Directory (createDirectoryIfMissing)
-import System.Environment (getEnv)
-import System.FilePath ((</>), takeDirectory, normalise)
+import System.FilePath ((</>), takeDirectory, takeFileName, normalise, takeExtension)
 import qualified System.FilePath.Posix as Posix
 import System.IO (stderr)
 import System.IO.Error
 import System.Random (StdGen)
-import Text.Pandoc.Class.CommonState (CommonState (..))
 import Text.Pandoc.Class.PandocMonad
-       (PandocMonad, getsCommonState, modifyCommonState,
+       (PandocMonad,
         getMediaBag, report, extractURIData)
 import Text.Pandoc.Definition (Pandoc, Inline (Image))
 import Text.Pandoc.Error (PandocError (..))
@@ -72,7 +79,6 @@
 import qualified Control.Exception as E
 import qualified Data.ByteString as B
 import qualified Data.ByteString.Lazy as BL
-import qualified Data.CaseInsensitive as CI
 import qualified Data.Text as T
 import qualified Data.Time
 import qualified Data.Time.LocalTime
@@ -82,8 +88,7 @@
 import qualified System.FilePath.Glob
 import qualified System.Random
 import qualified Text.Pandoc.UTF8 as UTF8
-import Data.Default (def)
-import System.X509 (getSystemCertificateStore)
+import Codec.Archive.Zip
 #ifndef EMBED_DATA_FILES
 import qualified Paths_pandoc as Paths
 #endif
@@ -124,6 +129,7 @@
 newUniqueHash :: MonadIO m => m Int
 newUniqueHash = hashUnique <$> liftIO Data.Unique.newUnique
 
+#ifdef PANDOC_HTTP_SUPPORT
 getManager :: (PandocMonad m, MonadIO m) => m Manager
 getManager = do
   mbManager <- getsCommonState stManager
@@ -153,12 +159,14 @@
         newManager tlsManagerSettings
       modifyCommonState $ \st -> st{ stManager = Just manager }
       pure manager
+#endif
 
 openURL :: (PandocMonad m, MonadIO m) => Text -> m (B.ByteString, Maybe MimeType)
 openURL u
  | Just (URI{ uriScheme = "data:",
               uriPath = upath }) <- parseURI (T.unpack u)
      = pure $ extractURIData upath
+#ifdef PANDOC_HTTP_SUPPORT
  | otherwise = do
      let toReqHeader (n, v) = (CI.mk (UTF8.fromText n), UTF8.fromText v)
      customHeaders <- map toReqHeader <$> getsCommonState stRequestHeaders
@@ -178,7 +186,12 @@
 
      case res of
           Right r -> return r
-          Left e  -> throwError $ PandocHttpError u e
+          Left (e :: HttpException)
+                  -> throwError $ PandocHttpError u (T.pack (show e))
+#else
+ | otherwise =
+     throwError $ PandocHttpError u "pandoc was compiled without HTTP support"
+#endif
 
 -- | Read the lazy ByteString contents from a file path, raising an error on
 -- failure.
@@ -233,16 +246,26 @@
   where go l' = do UTF8.hPutStr stderr "  "
                    UTF8.hPutStrLn stderr l'
 
--- | Extract media from the mediabag into a directory.
+-- | Extract media from the mediabag into a directory (or a zip archive if the
+-- path supplied ends in @.zip@.
 extractMedia :: (PandocMonad m, MonadIO m) => FilePath -> Pandoc -> m Pandoc
-extractMedia dir d = do
+extractMedia path d = do
   media <- getMediaBag
   let items = mediaItems media
+  let (dir, mbZip) = case takeExtension path of
+                       ".zip" -> (takeDirectory path, Just (takeFileName path))
+                       _ -> (path, Nothing)
   if null items
     then return d
     else do
-      mapM_ (writeMedia dir) items
+      let archive = foldr addEntry emptyArchive items
+      case mbZip of
+        Just fname -> writeMedia dir
+                        (fname, "application/zip", fromArchive archive)
+        Nothing -> mapM_ (writeMedia dir) items
       return $ walk (adjustImagePath dir media) d
+ where
+  addEntry (fp, _mime, content) = addEntryToArchive (toEntry fp 0 content)
 
 -- | Write the contents of a media bag to a path.
 -- If the path contains URI escape sequences (percent-encoding),
diff --git a/src/Text/Pandoc/Class/PandocMonad.hs b/src/Text/Pandoc/Class/PandocMonad.hs
--- a/src/Text/Pandoc/Class/PandocMonad.hs
+++ b/src/Text/Pandoc/Class/PandocMonad.hs
@@ -534,7 +534,7 @@
                   return $ replacementSpan attr src tit lab
                 PandocHttpError u er -> do
                   report $ CouldNotFetchResource u
-                            (T.pack $ show er ++ "\rReplacing image with description.")
+                            (er <> "\nReplacing image with description.")
                   -- emit alt text
                   return $ replacementSpan attr src tit lab
                 _ -> throwError e)
diff --git a/src/Text/Pandoc/Error.hs b/src/Text/Pandoc/Error.hs
--- a/src/Text/Pandoc/Error.hs
+++ b/src/Text/Pandoc/Error.hs
@@ -25,7 +25,6 @@
 import Data.Text (Text)
 import qualified Data.Text as T
 import GHC.Generics (Generic)
-import Network.HTTP.Client (HttpException)
 import System.Exit (ExitCode (..), exitWith)
 import System.IO (stderr)
 import qualified Text.Pandoc.UTF8 as UTF8
@@ -34,7 +33,7 @@
 import Citeproc (CiteprocError, prettyCiteprocError)
 
 data PandocError = PandocIOError Text IOError
-                 | PandocHttpError Text HttpException
+                 | PandocHttpError Text Text
                  | PandocShouldNeverHappenError Text
                  | PandocSomeError Text
                  | PandocParseError Text
@@ -69,13 +68,14 @@
                  deriving (Show, Typeable, Generic)
 
 instance Exception PandocError
+ where
+   displayException = T.unpack . renderError
 
 renderError :: PandocError -> Text
 renderError e =
   case e of
     PandocIOError _ err' -> T.pack $ displayException err'
-    PandocHttpError u err' ->
-      "Could not fetch " <> u <> "\n" <> tshow err'
+    PandocHttpError u err' -> "Could not fetch " <> u <> "\n" <> err'
     PandocShouldNeverHappenError s ->
       "Something we thought was impossible happened!\n" <>
       "Please report this to pandoc's developers: " <> s
@@ -125,7 +125,6 @@
                  "\nTry using Word to save your DOC file as DOCX," <>
                  " and convert that with pandoc."
         "pdf" -> "\nPandoc can convert to PDF, but not from PDF."
-        "asciidoc" -> "\nPandoc can convert to asciidoc, but not from asciidoc."
         _     -> ""
     PandocUnknownWriterError w ->
        "Unknown output format " <> w <>
diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs
--- a/src/Text/Pandoc/Extensions.hs
+++ b/src/Text/Pandoc/Extensions.hs
@@ -521,6 +521,7 @@
        , Ext_rebase_relative_paths
        , Ext_wikilinks_title_after_pipe
        , Ext_wikilinks_title_before_pipe
+       , Ext_alerts
        ]
   getAll "markdown_strict"   = allMarkdownExtensions
   getAll "markdown_phpextra" = allMarkdownExtensions
diff --git a/src/Text/Pandoc/Format.hs b/src/Text/Pandoc/Format.hs
--- a/src/Text/Pandoc/Format.hs
+++ b/src/Text/Pandoc/Format.hs
@@ -22,7 +22,7 @@
 import Control.Monad.Except (throwError)
 import Data.Char (toLower)
 import Data.Foldable (asum)
-import Data.List (foldl')
+import qualified Data.List as L
 import System.FilePath (splitExtension, takeExtension)
 import Text.Pandoc.Class (PandocMonad)
 import Text.Pandoc.Error (PandocError (..))
@@ -142,7 +142,7 @@
 
 pExtensionsDiff :: (UpdateSourcePos s Char, Stream s m Char)
                 => ParsecT s u m ExtensionsDiff
-pExtensionsDiff = foldl' (flip ($)) mempty <$> many extMod
+pExtensionsDiff = L.foldl' (flip ($)) mempty <$> many extMod
   where
     extMod = do
       polarity <- oneOf "-+"
diff --git a/src/Text/Pandoc/Logging.hs b/src/Text/Pandoc/Logging.hs
--- a/src/Text/Pandoc/Logging.hs
+++ b/src/Text/Pandoc/Logging.hs
@@ -105,12 +105,14 @@
   | UnclosedDiv SourcePos SourcePos
   | UnsupportedCodePage Int
   | YamlWarning SourcePos Text
+  | UnsupportedPdfStandard Text
   deriving (Show, Eq, Data, Ord, Typeable, Generic)
 
 instance ToJSON LogMessage where
   toJSON x = object $
     "verbosity" .= toJSON (messageVerbosity x) :
     "type" .= toJSON (show $ toConstr x) :
+    "pretty" .= toJSON (showLogMessage x) :
     case x of
       SkippedContent s pos ->
            ["contents" .= s,
@@ -290,6 +292,8 @@
            , "column" .= toJSON (sourceColumn pos)
            , "message" .= msg
            ]
+      UnsupportedPdfStandard s ->
+           ["contents" .= s]
 
 showPos :: SourcePos -> Text
 showPos pos = Text.pack $ sn ++ "line " ++
@@ -437,6 +441,8 @@
        UnsupportedCodePage cpg -> "Unsupported code page " <> tshow cpg <>
           ". Text will likely be garbled."
        YamlWarning pos m -> "YAML warning (" <> showPos pos <> "): " <> m
+       UnsupportedPdfStandard s ->
+         "PDF standard '" <> s <> "' is not supported by LaTeX and will be ignored."
 
 messageVerbosity :: LogMessage -> Verbosity
 messageVerbosity msg =
@@ -496,3 +502,4 @@
        UnclosedDiv{}                 -> WARNING
        UnsupportedCodePage{}         -> WARNING
        YamlWarning{}                 -> WARNING
+       UnsupportedPdfStandard{}      -> WARNING
diff --git a/src/Text/Pandoc/PDF.hs b/src/Text/Pandoc/PDF.hs
--- a/src/Text/Pandoc/PDF.hs
+++ b/src/Text/Pandoc/PDF.hs
@@ -218,7 +218,7 @@
   verbosity <- getVerbosity
   liftIO $ toPdfViaTempFile verbosity program args (:[]) ".html" source
 
--- convert SVG to PDF, and pixel formatst to PNG or JPEG, for TeX formats
+-- convert SVG to PDF, and pixel formats to PNG or JPEG, for TeX formats
 convertImages :: (PandocMonad m, MonadIO m)
               => WriterOptions -> FilePath -> Inline -> m Inline
 convertImages opts tmpdir (Image attr ils (src, tit)) = do
@@ -628,6 +628,7 @@
                                , "SELFAUTOPARENT"
                                , "SFDFONTS"
                                , "SHELL"
+                               , "SOURCE_DATE_EPOCH"
                                , "T1FONTS"
                                , "T1INPUTS"
                                , "T42FONTS"
diff --git a/src/Text/Pandoc/Parsing/Math.hs b/src/Text/Pandoc/Parsing/Math.hs
--- a/src/Text/Pandoc/Parsing/Math.hs
+++ b/src/Text/Pandoc/Parsing/Math.hs
@@ -42,7 +42,7 @@
                            (try (string "text" >>
                                  (("\\text" <>) <$> inBalancedBraces 0 ""))
                             <|>  (\c -> T.pack ['\\',c]) <$> anyChar))
-                   <|> ("\n" <$ blankline <* notFollowedBy' blankline)
+                   <|> ("\n" <$ blankline <* notFollowedBy' blankline <* notFollowedBy (char '$'))
                    <|> (T.pack <$> many1 spaceChar <* notFollowedBy (char '$'))
                     ) (try $ textStr cl)
   notFollowedBy digit  -- to prevent capture of $5
diff --git a/src/Text/Pandoc/Readers/AsciiDoc.hs b/src/Text/Pandoc/Readers/AsciiDoc.hs
--- a/src/Text/Pandoc/Readers/AsciiDoc.hs
+++ b/src/Text/Pandoc/Readers/AsciiDoc.hs
@@ -37,7 +37,7 @@
 import Text.Pandoc.Logging
 import Text.Pandoc.Sources
 import Control.Monad.State
-import Data.List (intersperse, foldl')
+import qualified Data.List as L
 import Data.Char (chr, ord)
 import qualified Data.Text as T
 import qualified Data.Map as M
@@ -148,7 +148,7 @@
          B.setMeta "version" vers .
          maybe id (B.setMeta "date") mbdate .
          maybe id (B.setMeta "remark") mbremark) .
-    flip (foldl' (\m (k,v) ->
+    flip (L.foldl' (\m (k,v) ->
                     -- leave out flags that are set just for processing
                     if k == "sectids" || k == "stem"
                        then m
@@ -363,7 +363,7 @@
                          (B.str ("[" <> t <> "]"))
     A.Button t -> pure $ B.spanWith ("",["button"],[])
                          (B.strong $ B.str ("[" <> t <> "]"))
-    A.Kbd ts -> pure $ mconcat $ intersperse (B.str "+") $
+    A.Kbd ts -> pure $ mconcat $ L.intersperse (B.str "+") $
          map (B.spanWith ("",["kbd"],[]) . B.strong . B.str) ts
     A.Menu ts -> pure $ B.spanWith ("",["menu"],[]) $
         B.strong $ B.text $ T.intercalate " › " ts
diff --git a/src/Text/Pandoc/Readers/Djot.hs b/src/Text/Pandoc/Readers/Djot.hs
--- a/src/Text/Pandoc/Readers/Djot.hs
+++ b/src/Text/Pandoc/Readers/Djot.hs
@@ -37,7 +37,6 @@
 import Text.Pandoc.Emoji (emojiToInline)
 import Control.Monad.Reader
 import qualified Data.Foldable as F
-import Data.List (foldl')
 import Data.ByteString (ByteString)
 -- import Debug.Trace
 
@@ -141,7 +140,7 @@
                      (hs,bs):rs -> (hs,row:bs):rs
                      [] -> [([],[row])]
       let reverseSnd (as,bs) = (as,reverse bs)
-      let bodies = reverse $ map reverseSnd $ foldl' getBody [] rest
+      let bodies = reverse $ map reverseSnd $ F.foldl' getBody [] rest
       let toCell (D.Cell _ al ils) =
             Cell nullAttr (toAlign al) (RowSpan 1) (ColSpan 1)
                    . (\is -> [Para $ toList is]) <$> convertInlines ils
diff --git a/src/Text/Pandoc/Readers/DocBook.hs b/src/Text/Pandoc/Readers/DocBook.hs
--- a/src/Text/Pandoc/Readers/DocBook.hs
+++ b/src/Text/Pandoc/Readers/DocBook.hs
@@ -46,7 +46,7 @@
 import Text.Pandoc.Class.PandocMonad (PandocMonad, report)
 import Text.Pandoc.Options
 import Text.Pandoc.Logging (LogMessage(..))
-import Text.Pandoc.Shared (safeRead, extractSpaces)
+import Text.Pandoc.Shared (safeRead, addPandocAttributes)
 import Text.Pandoc.Sources (ToSources(..), sourcesToText)
 import Text.Pandoc.Transforms (headerShift)
 import Text.TeXMath (readMathML, writeTeX)
@@ -173,7 +173,7 @@
 [ ] errorname - An error name
 [ ] errortext - An error message.
 [ ] errortype - The classification of an error message
-[ ] example - A formal example, with a title
+[x] example - A formal example, with a title
 [ ] exceptionname - The name of an exception
 [ ] fax - A fax number
 [ ] fieldsynopsis - The name of a field in a class definition
@@ -438,7 +438,7 @@
 [ ] shortaffil - A brief description of an affiliation
 [ ] shortcut - A key combination for an action that is also accessible through
     a menu
-[ ] sidebar - A portion of a document that is isolated from the main
+[x] sidebar - A portion of a document that is isolated from the main
     narrative flow
 [ ] sidebarinfo - Meta-information for a Sidebar
 [x] simpara - A paragraph that contains only text and inline markup, no block
@@ -548,6 +548,7 @@
                       , dbBook          :: Bool
                       , dbContent       :: [Content]
                       , dbLiteralLayout :: Bool
+                      , dbElementStack  :: [Text]
                       } deriving Show
 
 instance Default DBState where
@@ -556,7 +557,9 @@
                , dbMeta = mempty
                , dbBook = False
                , dbContent = []
-               , dbLiteralLayout = False }
+               , dbLiteralLayout = False
+               , dbElementStack = []
+               }
 
 
 readDocBook :: (PandocMonad m, ToSources a)
@@ -621,9 +624,13 @@
 --
 
 addMetadataFromElement :: PandocMonad m => Element -> DB m Blocks
-addMetadataFromElement e =
-  mempty <$ mapM_ handleMetadataElement
+addMetadataFromElement e = do
+  -- Add metadata if at root or appropriate parent element
+  elementStack <- gets dbElementStack
+  if take 1 elementStack `elem` [[], ["book"], ["article"]]
+    then mempty <$ mapM_ handleMetadataElement
                   (filterChildren ((isMetadataField . qName . elName)) e)
+    else return mempty
  where
   handleMetadataElement elt =
     case qName (elName elt) of
@@ -724,9 +731,7 @@
   , "articleinfo"
   , "attribution"
   , "authorinitials"
-  , "bibliodiv"
   , "biblioentry"
-  , "bibliography"
   , "bibliomisc"
   , "bibliomixed"
   , "blockquote"
@@ -765,31 +770,19 @@
   , "preface"
   , "procedure"
   , "programlisting"
-  , "qandadiv"
   , "question"
-  , "refsect1"
   , "refsect1info"
-  , "refsect2"
   , "refsect2info"
-  , "refsect3"
   , "refsect3info"
-  , "refsection"
   , "refsectioninfo"
   , "screen"
-  , "sect1"
   , "sect1info"
-  , "sect2"
   , "sect2info"
-  , "sect3"
   , "sect3info"
-  , "sect4"
   , "sect4info"
-  , "sect5"
   , "sect5info"
-  , "section"
   , "sectioninfo"
   , "simpara"
-  , "simplesect"
   , "substeps"
   , "subtitle"
   , "table"
@@ -797,8 +790,14 @@
   , "titleabbrev"
   , "toc"
   , "variablelist"
-  ] ++ admonitionTags
+  ] ++ sectionTags ++ admonitionTags
 
+sectionTags :: [Text]
+sectionTags = ["bibliography", "bibliodiv"
+              , "sect1", "sect2", "sect3", "sect4", "sect5", "section", "simplesect"
+              , "refsect1", "refsect2", "refsect3", "refsection", "qandadiv"
+              ]
+
 admonitionTags :: [Text]
 admonitionTags = ["caution","danger","important","note","tip","warning"]
 
@@ -853,9 +852,16 @@
   fmap (imageWith attr imageUrl tit) capt
 
 getBlocks :: PandocMonad m => Element -> DB m Blocks
-getBlocks e =  mconcat <$>
-                 mapM parseBlock (elContent e)
+getBlocks e = do
+  modify (\st -> st{ dbElementStack = qName (elName e) : dbElementStack st })
+  blocks <- mconcat <$> mapM parseBlock (elContent e)
+  modify (\st -> st{ dbElementStack = drop 1 $ dbElementStack st })
+  return blocks
 
+getRoleAttr :: Element -> [(Text, Text)] -- extract role attribute and add it to the attribute list
+getRoleAttr e = case attrValue "role" e of
+                  "" -> []
+                  r  -> [("role", r)]
 
 parseBlock :: PandocMonad m => Content -> DB m Blocks
 parseBlock (Text (CData CDataRaw _ _)) = return mempty -- DOCTYPE
@@ -863,8 +869,8 @@
                                      then return mempty
                                      else return $ plain $ trimInlines $ text s
 parseBlock (CRef x) = return $ plain $ str $ T.toUpper x
-parseBlock (Elem e) =
-  case qName (elName e) of
+parseBlock (Elem e) = do
+  parsedBlock <- case qName (elName e) of
         "toc"   -> skip -- skip TOC, since in pandoc it's autogenerated
         "index" -> skip -- skip index, since page numbers meaningless
         "para"  -> parseMixed para (elContent e)
@@ -913,8 +919,8 @@
         "refsect2" -> sect 2
         "refsect3" -> sect 3
         "refsection" -> gets dbSectionLevel >>= sect . (+1)
-        l | l `elem` titledBlockElements -> parseAdmonition l
-        l | l `elem` admonitionTags -> parseAdmonition l
+        l | l `elem` titledBlockElements -> parseAdmonition False l
+        l | l `elem` admonitionTags -> parseAdmonition True l
         "area" -> skip
         "areaset" -> skip
         "areaspec" -> skip
@@ -976,6 +982,9 @@
         "title" -> return mempty     -- handled in parent element
         "subtitle" -> return mempty  -- handled in parent element
         _ -> skip >> getBlocks e
+  if qName (elName e) `elem` sectionTags
+     then return parsedBlock
+     else return $ addPandocAttributes (getRoleAttr e) parsedBlock
    where skip = do
            let qn = qName $ elName e
            let name = if "pi-" `T.isPrefixOf` qn
@@ -1111,7 +1120,10 @@
            modify $ \st -> st{ dbSectionLevel = n }
            b <- getBlocks e
            modify $ \st -> st{ dbSectionLevel = n - 1 }
-           return $ headerWith (elId, classes, maybeToList titleabbrevElAsAttr++attrs) n' headerText <> b
+           let hdr = addPandocAttributes (getRoleAttr e)
+                        $ headerWith (elId, classes, maybeToList titleabbrevElAsAttr ++ attrs)
+                                      n' headerText
+           return $ hdr <> b
          titleabbrevElAsAttr =
            case filterChild (named "titleabbrev") e `mplus`
                 (filterChild (named "info") e >>=
@@ -1134,17 +1146,20 @@
            b <- p
            case mbt of
              Nothing -> return b
-             Just t -> return $ divWith (attrValue "id" e,[],[])
+             Just t -> return $ divWith (attrValue "id" e, [], getRoleAttr e)
                          (divWith ("", ["title"], []) (plain t) <> b)
 
          -- Admonitions are parsed into a div. Following other Docbook tools that output HTML,
          -- we parse the optional title as a div with the @title@ class, and give the
          -- block itself a class corresponding to the admonition name.
-         parseAdmonition label = do
+         parseAdmonition alwaysIncludeTitle label = do
            mbt <- getTitle
            -- this will ignore the title element if it is present:
            b <- getBlocks e
-           let t = divWith ("", ["title"], []) (plain $ fromMaybe mempty mbt)
+           let t = maybe mempty (divWith ("", ["title"], []) . plain)
+                        (case mbt of
+                           Nothing | alwaysIncludeTitle -> Just mempty
+                           _ -> mbt)
            -- we also attach the label as a class, so it can be styled properly
            return $ divWith (attrValue "id" e,[label],[]) (t <> b)
 
@@ -1234,8 +1249,8 @@
      else return $ text s
 parseInline (CRef ref) =
   return $ text $ fromMaybe (T.toUpper ref) $ lookupEntity ref
-parseInline (Elem e) =
-  case qName (elName e) of
+parseInline (Elem e) = do
+  parsedInline <- case qName (elName e) of
         "anchor" -> do
            return $ spanWith (attrValue "id" e, [], []) mempty
         "phrase" -> do
@@ -1357,6 +1372,9 @@
         -- <?asciidor-br?> to in handleInstructions, above.
         "pi-asciidoc-br" -> return linebreak
         _          -> skip >> innerInlines id
+  return $ case qName (elName e) of
+    "emphasis" -> parsedInline
+    _ -> addPandocAttributes (getRoleAttr e) parsedInline
    where skip = do
            let qn = qName $ elName e
            let name = if "pi-" `T.isPrefixOf` qn
@@ -1365,8 +1383,7 @@
            lift $ report $ IgnoredElement name
            return mempty
 
-         innerInlines f = extractSpaces f . mconcat <$>
-                          mapM parseInline (elContent e)
+         innerInlines f = f . mconcat <$> mapM parseInline (elContent e)
          codeWithLang = do
            let classes' = case attrValue "language" e of
                                "" -> []
diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs
--- a/src/Text/Pandoc/Readers/Docx.hs
+++ b/src/Text/Pandoc/Readers/Docx.hs
@@ -74,7 +74,8 @@
 import Data.Bifunctor (bimap, first)
 import qualified Data.ByteString.Lazy as B
 import Data.Default (Default)
-import Data.List (delete, intersect, foldl')
+import Data.List (delete, intersect)
+import qualified Data.List as L
 import Data.Char (isSpace)
 import qualified Data.Map as M
 import qualified Data.Text as T
@@ -625,8 +626,8 @@
 splitHeaderRows :: Bool -> [Docx.Row] -> ([Docx.Row], [Docx.Row])
 splitHeaderRows hasFirstRowFormatting rs = bimap reverse reverse $ fst
   $ if hasFirstRowFormatting
-    then foldl' f ((take 1 rs, []), True) (drop 1 rs)
-    else foldl' f (([], []), False) rs
+    then L.foldl' f ((take 1 rs, []), True) (drop 1 rs)
+    else L.foldl' f (([], []), False) rs
   where
     f ((headerRows, bodyRows), previousRowWasHeader) r@(Docx.Row h cs)
       | h == HasTblHeader || (previousRowWasHeader && any isContinuationCell cs)
@@ -836,7 +837,9 @@
       alignments = case rows of
                      [] -> replicate width Pandoc.AlignDefault
                      Docx.Row _ cs : _ -> concatMap getAlignment cs
-      widths = map ColWidth grid
+      widths = map (\n -> if n == 0
+                             then ColWidthDefault
+                             else ColWidth n) grid
 
   extStylesEnabled <- asks (isEnabled Ext_styles . docxOptions)
   let attr = case mbsty of
diff --git a/src/Text/Pandoc/Readers/Docx/Combine.hs b/src/Text/Pandoc/Readers/Docx/Combine.hs
--- a/src/Text/Pandoc/Readers/Docx/Combine.hs
+++ b/src/Text/Pandoc/Readers/Docx/Combine.hs
@@ -57,7 +57,7 @@
                                         )
        where
 
-import Data.List
+import qualified Data.List as L
 import Data.Bifunctor
 import Data.Sequence ( ViewL (..), ViewR (..), viewl, viewr, spanr, spanl
                      , (><), (|>) )
@@ -135,9 +135,9 @@
 combineSingletonInlines x y =
   let (xfs, xs) = unstackInlines x
       (yfs, ys) = unstackInlines y
-      shared = xfs `intersect` yfs
-      x_remaining = xfs \\ shared
-      y_remaining = yfs \\ shared
+      shared = xfs `L.intersect` yfs
+      x_remaining = xfs L.\\ shared
+      y_remaining = yfs L.\\ shared
       x_rem_attr = filter isAttrModifier x_remaining
       y_rem_attr = filter isAttrModifier y_remaining
   in
@@ -182,7 +182,7 @@
 
 smushInlines :: [Inlines] -> Inlines
 smushInlines xs = combineInlines xs' mempty
-  where xs' = foldl' combineInlines mempty xs
+  where xs' = L.foldl' combineInlines mempty xs
 
 smushBlocks :: [Blocks] -> Blocks
-smushBlocks xs = foldl' combineBlocks mempty xs
+smushBlocks xs = L.foldl' combineBlocks mempty xs
diff --git a/src/Text/Pandoc/Readers/Docx/Parse.hs b/src/Text/Pandoc/Readers/Docx/Parse.hs
--- a/src/Text/Pandoc/Readers/Docx/Parse.hs
+++ b/src/Text/Pandoc/Readers/Docx/Parse.hs
@@ -164,7 +164,8 @@
                        findChildrenByName ns "mc" "AlternateContent" >>=
                        findChildrenByName ns "mc" "Fallback" >>=
                        findChildrenByName ns "w" "pict" >>=
-                       findChildrenByName ns "v" "shape" >>=
+                       (\e -> findChildrenByName ns "v" "shape" e <>
+                              findChildrenByName ns "v" "rect" e) >>=
                        findChildrenByName ns "v" "textbox" >>=
                        findChildrenByName ns "w" "txbxContent"
   = concatMap (unwrapElement ns) (concatMap elChildren textboxes) -- handle #9214
@@ -875,9 +876,15 @@
                        Just l  -> elemToTblLook ns l
                        Nothing -> return defaultTblLook
 
-    grid <- grid'
+    grid'' <- grid'
     tblLook <- tblLook'
     rows <- mapD (elemToRow ns) (elChildren element)
+    let grid = if null grid''
+                  then let numcols = case rowsToRowspans rows of
+                                       (cs@(_:_):_) -> sum (map fst cs)
+                                       _ -> 0
+                       in replicate numcols 0.0
+                  else grid''
     return $ Tbl mbstyle (caption <> description) grid tblLook rows
 elemToBodyPart _ _ = throwError WrongElem
 
diff --git a/src/Text/Pandoc/Readers/EndNote.hs b/src/Text/Pandoc/Readers/EndNote.hs
--- a/src/Text/Pandoc/Readers/EndNote.hs
+++ b/src/Text/Pandoc/Readers/EndNote.hs
@@ -81,6 +81,7 @@
   let items = map toCitationItem $ filterElementsName (name "Cite") tree
   return $ Citeproc.Citation{
                      Citeproc.citationId = Nothing
+                   , Citeproc.citationResetPosition = False
                    , Citeproc.citationPrefix = Nothing
                    , Citeproc.citationSuffix = Nothing
                    , Citeproc.citationNoteNumber = Nothing
diff --git a/src/Text/Pandoc/Readers/HTML.hs b/src/Text/Pandoc/Readers/HTML.hs
--- a/src/Text/Pandoc/Readers/HTML.hs
+++ b/src/Text/Pandoc/Readers/HTML.hs
@@ -33,7 +33,7 @@
 import Data.Default (Default (..), def)
 import Data.Foldable (for_)
 import Data.List.Split (splitWhen)
-import Data.List (foldl')
+import qualified Data.List as L
 import qualified Data.Map as M
 import Data.Maybe (fromMaybe, isJust, isNothing)
 import Data.Either (partitionEithers)
@@ -242,7 +242,7 @@
         "main" -> pDiv
         "figure" -> pFigure
         "iframe" -> pIframe
-        "style" -> mempty <$ pHtmlBlock "style" -- see #10643
+        "style" -> pRawHtmlBlock
         "textarea" -> pRawHtmlBlock
         "switch"
           | epubExts
@@ -444,9 +444,9 @@
   terms <- many1 (try $ skipMany nonItem >> pInTags "dt" inline)
   defs  <- many1 (try $ skipMany nonItem >> pInTags "dd" block)
   skipMany nonItem
-  let term = foldl' (\x y -> if null x
-                                then trimInlines y
-                                else x <> B.linebreak <> trimInlines y)
+  let term = L.foldl' (\x y -> if null x
+                                  then trimInlines y
+                                  else x <> B.linebreak <> trimInlines y)
                     mempty terms
   return (term, map (fixPlains True) defs)
 
@@ -538,7 +538,7 @@
 
 pRawHtmlBlock :: PandocMonad m => TagParser m Blocks
 pRawHtmlBlock = do
-  raw <- pHtmlBlock "script" <|> pHtmlBlock "textarea"
+  raw <- pHtmlBlock "script" <|> pHtmlBlock "style" <|> pHtmlBlock "textarea"
           <|> pRawTag
   exts <- getOption readerExtensions
   if extensionEnabled Ext_raw_html exts && not (T.null raw)
@@ -713,7 +713,7 @@
         "input"
           | lookup "type" attr == Just "checkbox"
           -> asks inListItem >>= guard >> pCheckbox
-        "style" -> mempty <$ pHtmlBlock "style" -- see #10643
+        "style" -> B.rawInline "html" <$> pHtmlBlock "style"
         "script"
           | Just x <- lookup "type" attr
           , "math/tex" `T.isPrefixOf` x -> pScriptMath
diff --git a/src/Text/Pandoc/Readers/HTML/Table.hs b/src/Text/Pandoc/Readers/HTML/Table.hs
--- a/src/Text/Pandoc/Readers/HTML/Table.hs
+++ b/src/Text/Pandoc/Readers/HTML/Table.hs
@@ -20,7 +20,7 @@
 import Data.Maybe (fromMaybe, isJust)
 import Data.Either (lefts, rights)
 import Data.List.NonEmpty (nonEmpty)
-import Data.List (foldl')
+import qualified Data.List as L
 import Data.Text (Text)
 import Text.HTML.TagSoup
 import Text.Pandoc.Builder (Blocks)
@@ -204,9 +204,9 @@
   let adjustRowHeadCols
         headcolsv
         (currentrow, (numheads, Row _ cells)) =
-          foldl' (adjustRowHeadColsForCell currentrow) headcolsv
+          L.foldl' (adjustRowHeadColsForCell currentrow) headcolsv
             (take numheads cells)
-  let headcols = foldl' adjustRowHeadCols
+  let headcols = L.foldl' adjustRowHeadCols
                          (V.replicate numrows (0 :: Int))
                          (zip [(0 :: Int)..] rows)
   let rowHeadCols = case V.uncons headcols of
diff --git a/src/Text/Pandoc/Readers/JATS.hs b/src/Text/Pandoc/Readers/JATS.hs
--- a/src/Text/Pandoc/Readers/JATS.hs
+++ b/src/Text/Pandoc/Readers/JATS.hs
@@ -21,7 +21,7 @@
 import Data.Char (isDigit, isSpace)
 import Data.Default
 import Data.Generics
-import Data.List (foldl', intersperse)
+import qualified Data.List as L
 import qualified Data.Map as Map
 import Data.Maybe (maybeToList, fromMaybe, catMaybes)
 import Data.Text (Text)
@@ -31,7 +31,7 @@
 import Text.Pandoc.Builder
 import Text.Pandoc.Class.PandocMonad (PandocMonad)
 import Text.Pandoc.Options
-import Text.Pandoc.Shared (safeRead, extractSpaces)
+import Text.Pandoc.Shared (safeRead)
 import Text.Pandoc.Walk (walk)
 import Text.Pandoc.XML.Light
 import Text.TeXMath (readMathML, writeTeX)
@@ -290,11 +290,11 @@
                      let items = filterChildren (named "def") e'
                      terms' <- mapM getInlines terms
                      items' <- mapM getBlocks items
-                     return (mconcat $ intersperse (str "; ") terms', items')
+                     return (mconcat $ L.intersperse (str "; ") terms', items')
          parseFigure = do
            modify $ \st -> st{ jatsInFigure = True }
            capt <- case filterChild (named "caption") e of
-                     Just t  -> mconcat . intersperse linebreak <$>
+                     Just t  -> mconcat . L.intersperse linebreak <$>
                                 mapM getInlines (filterChildren (const True) t)
                      Nothing -> return mempty
            contents <- getBlocks e
@@ -350,7 +350,7 @@
                             n <- safeRead $ "0" <> T.filter (\x -> isDigit x || x == '.') w
                             if n > 0 then Just n else Nothing
                       let firstBody = fromMaybe [] (headMay multipleBodyRowElements)
-                      let numrows = foldl' max 0 $ map length firstBody
+                      let numrows = L.foldl' max 0 $ map length firstBody
                       let aligns = case colspecs of
                                      [] -> replicate numrows AlignDefault
                                      cs -> map toAlignment cs
@@ -733,8 +733,7 @@
         "fn" -> note . mconcat <$>
                          mapM parseBlock (elContent e)
         _          -> innerInlines id
-   where innerInlines f = extractSpaces f . mconcat <$>
-                          mapM parseInline (elContent e)
+   where innerInlines f = f . mconcat <$> mapM parseInline (elContent e)
          codeWithLang = do
            let classes' = case attrValue "language" e of
                                "" -> []
diff --git a/src/Text/Pandoc/Readers/LaTeX/Inline.hs b/src/Text/Pandoc/Readers/LaTeX/Inline.hs
--- a/src/Text/Pandoc/Readers/LaTeX/Inline.hs
+++ b/src/Text/Pandoc/Readers/LaTeX/Inline.hs
@@ -315,6 +315,26 @@
   , ("dothyp", lit ".\173")
   , ("colonhyp", lit ":\173")
   , ("hyp", lit "-")
+  -- ngerman (babel)
+  , ("glq", lit "‚")
+  , ("grq", lit "‘")
+  , ("glqq", lit "„")
+  , ("grqq", lit "“")
+  , ("flq", lit "‹")
+  , ("frq", lit "›")
+  , ("flqq", lit "«")
+  , ("frqq", lit "»")
+  , ("dq", lit "\"")
+  -- fontspec
+  , ("guillemetleft", lit "«")
+  , ("guillemotleft", lit "«")
+  , ("guillemetright", lit "»")
+  , ("guillemotright", lit "»")
+  , ("guilsinglleft", lit "‹")
+  , ("guilsinglright", lit "›")
+  , ("quotedblbase", lit "„")
+  , ("quotesinglbase", lit ",")
+  , ("textquotedbl", lit "\"")
   ]
 
 biblatexInlineCommands :: PandocMonad m
diff --git a/src/Text/Pandoc/Readers/LaTeX/Math.hs b/src/Text/Pandoc/Readers/LaTeX/Math.hs
--- a/src/Text/Pandoc/Readers/LaTeX/Math.hs
+++ b/src/Text/Pandoc/Readers/LaTeX/Math.hs
@@ -14,7 +14,7 @@
   )
 where
 import Data.Maybe (fromMaybe, mapMaybe, listToMaybe)
-import Data.List (foldl')
+import qualified Data.List as L
 import Text.Pandoc.Walk (walk)
 import Text.Pandoc.Builder as B
 import qualified Data.Sequence as Seq
@@ -160,7 +160,7 @@
 extractLabelFromBlock :: Block -> Maybe Text
 extractLabelFromBlock (Para inlines) = extractLabel Nothing inlines
   where
-    extractLabel = foldl' go
+    extractLabel = L.foldl' go
     go :: Maybe Text -> Inline -> Maybe Text
     go (Just t) _ = Just t
     go Nothing (Span (_, _, attrs) _) = lookup "label" attrs
diff --git a/src/Text/Pandoc/Readers/Markdown.hs b/src/Text/Pandoc/Readers/Markdown.hs
--- a/src/Text/Pandoc/Readers/Markdown.hs
+++ b/src/Text/Pandoc/Readers/Markdown.hs
@@ -24,7 +24,8 @@
 import Control.Monad.Except (throwError)
 import qualified Data.Bifunctor as Bifunctor
 import Data.Char (isAlphaNum, isPunctuation, isSpace)
-import Data.List (transpose, elemIndex, sortOn, foldl')
+import Data.List (transpose, elemIndex, sortOn)
+import qualified Data.List as L
 import qualified Data.Map as M
 import Data.Maybe
 import qualified Data.Set as Set
@@ -388,7 +389,7 @@
   addKvs <- option [] $ guardEnabled Ext_mmd_link_attributes
                           >> many (try $ spnl >> keyValAttr)
   blanklines
-  let attr'  = extractIdClass $ foldl' (\x f -> f x) attr addKvs
+  let attr'  = extractIdClass $ L.foldl' (\x f -> f x) attr addKvs
       target = (escapeURI $ trimr src, tit)
   st <- getState
   let oldkeys = stateKeys st
@@ -648,7 +649,7 @@
   spnl
   attrs <- many (attribute <* spnl)
   char '}'
-  return $ foldl' (\x f -> f x) nullAttr attrs
+  return $ L.foldl' (\x f -> f x) nullAttr attrs
 
 attribute :: PandocMonad m => MarkdownParser m (Attr -> Attr)
 attribute = identifierAttr <|> classAttr <|> keyValAttr <|> specialAttr
@@ -817,9 +818,28 @@
 blockQuote :: PandocMonad m => MarkdownParser m (F Blocks)
 blockQuote = do
   raw <- emailBlockQuote
+  (mbAlert, raw') <-
+    (do guardEnabled Ext_alerts
+        case raw of
+          (t:ts) | "[!" `T.isPrefixOf` t ->
+              case T.strip t of
+                "[!TIP]" -> pure (Just "tip", ts)
+                "[!WARNING]" -> pure (Just "warning", ts)
+                "[!IMPORTANT]" -> pure (Just "important", ts)
+                "[!CAUTION]" -> pure (Just "caution", ts)
+                "[!NOTE]" -> pure (Just "note", ts)
+                _ -> pure (Nothing, raw)
+          _ -> pure (Nothing, raw))
+      <|> pure (Nothing, raw)
   -- parse the extracted block, which may contain various block elements:
-  contents <- parseFromString' parseBlocks $ T.intercalate "\n" raw <> "\n\n"
-  return $ B.blockQuote <$> contents
+  contents <- parseFromString' parseBlocks $ T.intercalate "\n" raw' <> "\n\n"
+  return $
+    case mbAlert of
+      Nothing -> B.blockQuote <$> contents
+      Just alert ->
+        (B.divWith ("", [alert], [])
+          . (B.divWith ("", ["title"], []) (B.para (B.str (T.toTitle alert))) <>))
+           <$> contents
 
 --
 -- list blocks
@@ -2072,7 +2092,8 @@
     updateState $ \st -> st{ stateInNote = True
                            , stateNoteNumber = stateNoteNumber st + 1 }
     contents <- inBalancedBrackets inlines
-    notFollowedBy (char '(' <|> char '[') -- ^[link](foo)^ is superscript
+    notFollowedBy (char '(' <|> char '[' <|> ('{' <$ attributes))
+      -- ^[link](foo)^ is superscript
     updateState $ \st -> st{ stateInNote = False }
     return $ B.note . B.para <$> contents
 
diff --git a/src/Text/Pandoc/Readers/MediaWiki.hs b/src/Text/Pandoc/Readers/MediaWiki.hs
--- a/src/Text/Pandoc/Readers/MediaWiki.hs
+++ b/src/Text/Pandoc/Readers/MediaWiki.hs
@@ -13,7 +13,7 @@
 {-
 TODO:
 _ correctly handle tables within tables
-_ parse templates?
+_ parse templates(?) and built-in magic words
 -}
 module Text.Pandoc.Readers.MediaWiki ( readMediaWiki ) where
 
@@ -28,17 +28,17 @@
 import Data.Text (Text)
 import qualified Data.Text as T
 import Text.HTML.TagSoup
-import Text.Pandoc.Builder (Blocks, Inlines, trimInlines)
 import qualified Text.Pandoc.Builder as B
+import Text.Pandoc.Builder (Blocks, Inlines, trimInlines)
+import Text.Pandoc.Char (isCJK)
 import Text.Pandoc.Class.PandocMonad (PandocMonad (..))
 import Text.Pandoc.Definition
 import Text.Pandoc.Logging
 import Text.Pandoc.Options
 import Text.Pandoc.Parsing hiding (tableCaption)
-import Text.Pandoc.Readers.HTML (htmlTag, isBlockTag, isCommentTag, toAttr)
-import Text.Pandoc.Shared (safeRead, stringify, stripTrailingNewlines,
-                           trim, splitTextBy, tshow, formatCode)
-import Text.Pandoc.Char (isCJK)
+import Text.Pandoc.Readers.HTML (htmlTag, isCommentTag, toAttr)
+import Text.Pandoc.Shared (formatCode, safeRead, splitTextBy, stringify,
+                           stripTrailingNewlines, trim, tshow)
 import Text.Pandoc.XML (fromEntities)
 
 -- | Read mediawiki from an input string and return a Pandoc document.
@@ -56,6 +56,7 @@
                                             , mwLogMessages = []
                                             , mwInTT = False
                                             , mwAllowNewlines = True
+                                            , mwMeta = nullMeta
                                             }
             sources
   case parsed of
@@ -70,6 +71,7 @@
                        , mwLogMessages     :: [LogMessage]
                        , mwInTT            :: Bool
                        , mwAllowNewlines   :: Bool
+                       , mwMeta            :: Meta
                        }
 
 type MWParser m = ParsecT Sources MWState m
@@ -90,7 +92,7 @@
 --
 
 specialChars :: [Char]
-specialChars = "'[]<=&*{}|\":\\"
+specialChars = "'[]<=&*{}|\":\\_"
 
 spaceChars :: [Char]
 spaceChars = " \n\t"
@@ -102,21 +104,86 @@
 newBlockTags = ["haskell","syntaxhighlight","source","gallery","references"]
 
 isBlockTag' :: Tag Text -> Bool
-isBlockTag' tag@(TagOpen t _) = (isBlockTag tag || t `elem` newBlockTags) &&
-  t `notElem` eitherBlockOrInline
-isBlockTag' (TagClose "ref") = True -- needed so 'special' doesn't parse it
-isBlockTag' tag@(TagClose t) = (isBlockTag tag || t `elem` newBlockTags) &&
-  t `notElem` eitherBlockOrInline
-isBlockTag' tag = isBlockTag tag
+isBlockTag' (TagOpen t _) = isBlockTagName t
+isBlockTag' (TagClose t) = isBlockTagName t
+isBlockTag' _ = False
 
+isBlockTagName :: Text -> Bool
+isBlockTagName t =
+  t `elem` [ "blockquote"
+           , "caption"
+           , "col"
+           , "colgroup"
+           , "dd"
+           , "div"
+           , "dl"
+           , "dt"
+           , "h1"
+           , "h2"
+           , "h3"
+           , "h4"
+           , "h5"
+           , "h6"
+           , "hr"
+           , "li"
+           , "meta"
+           , "ol"
+           , "p"
+           , "pre"
+           , "rp"
+           , "table"
+           , "td"
+           , "th"
+           , "time"
+           , "tr"
+           , "ul"
+           , "center"
+           ] || t `elem` newBlockTags
+
 isInlineTag' :: Tag Text -> Bool
 isInlineTag' (TagComment _) = True
-isInlineTag' (TagClose "ref") = False -- see below inlineTag
-isInlineTag' t              = not (isBlockTag' t)
+isInlineTag' (TagOpen t _) = isInlineTagName t
+isInlineTag' (TagClose t) = isInlineTagName t
+isInlineTag' _ = False
 
-eitherBlockOrInline :: [Text]
-eitherBlockOrInline = ["applet", "button", "del", "iframe", "ins",
-                               "map", "area", "object"]
+isInlineTagName :: Text -> Bool
+isInlineTagName t =
+  t `elem` [ "abbr"
+           , "b"
+           , "bdi"
+           , "bdo"
+           , "big"
+           , "br"
+           , "cite"
+           , "code"
+           , "data"
+           , "del"
+           , "dfn"
+           , "em"
+           , "i"
+           , "ins"
+           , "kbd"
+           , "link"
+           , "mark"
+           , "q"
+           , "rt"
+           , "ruby"
+           , "s"
+           , "samp"
+           , "small"
+           , "span"
+           , "strong"
+           , "sub"
+           , "sup"
+           , "u"
+           , "var"
+           , "wbr"
+           , "font"
+           , "rb"
+           , "rtc"
+           , "strike"
+           , "tt"
+           ]
 
 htmlComment :: PandocMonad m => MWParser m ()
 htmlComment = () <$ htmlTag isCommentTag
@@ -160,11 +227,12 @@
   spaces
   eof
   categoryLinks <- reverse . mwCategoryLinks <$> getState
+  meta <- mwMeta <$> getState
   let categories = if null categoryLinks
                       then mempty
                       else B.para $ mconcat $ intersperse B.space categoryLinks
   reportLogMessages
-  return $ B.doc $ bs <> categories
+  return $ Pandoc meta (B.toList bs <> B.toList categories)
 
 --
 -- block parsers
@@ -533,6 +601,7 @@
       <|> doubleQuotes
       <|> strong
       <|> emph
+      <|> behaviorSwitch
       <|> image
       <|> internalLink
       <|> externalLink
@@ -571,7 +640,11 @@
 
 inlineTag :: PandocMonad m => MWParser m Inlines
 inlineTag = do
-  (tag, _) <- lookAhead $ htmlTag isInlineTag'
+  (tag, _) <- lookAhead $ htmlTag (\tag -> case tag of
+                                      TagOpen "hask" _ -> True
+                                      TagOpen "ref" _ -> True
+                                      TagOpen "nowiki" _ -> True
+                                      _ -> isInlineTag' tag)
   case tag of
        TagOpen "ref" _ -> B.note . singleParaToPlain <$> blocksInTags "ref"
        TagOpen "nowiki" _ -> try $ do
@@ -586,6 +659,10 @@
        TagOpen "del" _ -> B.strikeout <$> inlinesInTags "del"
        TagOpen "sub" _ -> B.subscript <$> inlinesInTags "sub"
        TagOpen "sup" _ -> B.superscript <$> inlinesInTags "sup"
+       TagOpen "var" _ -> B.codeWith ("",["variable"],[]) <$> textInTags "var"
+       TagOpen "samp" _ -> B.codeWith ("",["sample"],[]) <$> textInTags "samp"
+       TagOpen "kbd" _ -> B.spanWith ("",["kbd"],[]) <$> inlinesInTags "kbd"
+       TagOpen "mark" _ -> B.spanWith ("",["mark"],[]) <$> inlinesInTags "mark"
        TagOpen "code" _ -> encode <$> inlinesInTags "code"
        TagOpen "tt" _ -> do
          inTT <- mwInTT <$> getState
@@ -597,8 +674,15 @@
        _ -> B.rawInline "html" . snd <$> htmlTag (~== tag)
 
 special :: PandocMonad m => MWParser m Inlines
-special = B.str <$> countChar 1 (notFollowedBy' (htmlTag isBlockTag') *>
-                                  oneOf specialChars)
+special = B.str . T.singleton <$>
+  (notFollowedBy' (htmlTag (\t -> isInlineTag' t ||
+                                  isBlockTag' t ||
+                                  case t of
+                                    TagClose "ref" -> True
+                                    TagClose "hask" -> True
+                                    TagClose "nowiki" -> True
+                                    _ -> False)
+                                  ) *> oneOf specialChars)
 
 inlineHtml :: PandocMonad m => MWParser m Inlines
 inlineHtml = B.rawInline "html" . snd <$> htmlTag isInlineTag'
@@ -716,3 +800,37 @@
   B.doubleQuoted <$> inlinesBetween openDoubleQuote closeDoubleQuote
     where openDoubleQuote = sym "\"" >> lookAhead nonspaceChar
           closeDoubleQuote = try $ sym "\""
+
+behaviorSwitch :: PandocMonad m => MWParser m Inlines
+behaviorSwitch = try $ do
+  let reservedMagicWords = [ "NOTOC"
+              , "FORCETOC"
+              , "TOC"
+              , "NOEDITSECTION"
+              , "NEWSECTIONLINK"
+              , "NONEWSECTIONLINK"
+              , "NOGALLERY"
+              , "HIDDENCAT"
+              , "EXPECTUNUSEDCATEGORY"
+              , "NOCONTENTCONVERT"
+              , "NOCC"
+              , "NOTITLECONVERT"
+              , "NOTC"
+              , "INDEX"
+              , "NOINDEX"
+              , "STATICREDIRECT"
+              , "EXPECTUNUSEDTEMPLATE"
+              -- From popular extensions
+              , "NOGLOBAL"
+              , "DISAMBIG"
+              , "ARCHIVEDTALK"
+              , "NOTALK"
+              ]
+  string "__"
+  name <- many1 alphaNum
+  string "__"
+  case name `elem` reservedMagicWords of
+    True -> do
+      updateState $ \st -> st{ mwMeta = B.setMeta (T.toLower $ T.pack name) True (mwMeta st) }
+      return mempty
+    False -> return $ B.str $ "__" <> T.pack name <> "__"
diff --git a/src/Text/Pandoc/Readers/ODT/Arrows/State.hs b/src/Text/Pandoc/Readers/ODT/Arrows/State.hs
--- a/src/Text/Pandoc/Readers/ODT/Arrows/State.hs
+++ b/src/Text/Pandoc/Readers/ODT/Arrows/State.hs
@@ -33,10 +33,10 @@
   , iterateS'
   ) where
 
+import qualified Data.List as L
 import Control.Arrow
 import qualified Control.Category as Cat
 import Control.Monad
-import Data.List (foldl')
 import Text.Pandoc.Readers.ODT.Arrows.Utils
 import Text.Pandoc.Readers.ODT.Generic.Fallible
 
@@ -136,7 +136,7 @@
 iterateSL :: (Foldable f, MonadPlus m)
           => ArrowState s    x     y
           -> ArrowState s (f x) (m y)
-iterateSL a = ArrowState $ \(s,f) -> foldl' a' (s,mzero) f
+iterateSL a = ArrowState $ \(s,f) -> L.foldl' a' (s,mzero) f
   where a' (s',m) x = second (mplus m.return) $ runArrowState a (s',x)
 
 
diff --git a/src/Text/Pandoc/Readers/ODT/ContentReader.hs b/src/Text/Pandoc/Readers/ODT/ContentReader.hs
--- a/src/Text/Pandoc/Readers/ODT/ContentReader.hs
+++ b/src/Text/Pandoc/Readers/ODT/ContentReader.hs
@@ -794,42 +794,46 @@
 --
 read_table        :: BlockMatcher
 read_table         = matchingElement NsTable "table"
-                     $ liftA simpleTable'
+                     $ liftA table'
                      $ (matchChildContent' [read_table_header]) &&&
-                       (matchChildContent'  [read_table_row])
+                       (matchChildContent' [read_table_row])
 
--- | A simple table without a caption.
-simpleTable' :: ([[Blocks]], [[Blocks]]) -> Blocks
-simpleTable' (headers, rows) =
+-- | A table without a caption.
+table' :: ([[Cell]], [[Cell]]) -> Blocks
+table' (headers, rows) =
   table emptyCaption (replicate numcols defaults) th [tb] tf
   where
     defaults = (AlignDefault, ColWidthDefault)
     numcols = maximum $ map length $ headers ++ rows
-    toRow = Row nullAttr . map simpleCell
+    toRow = Row nullAttr
     th = TableHead nullAttr $ map toRow headers
     tb = TableBody nullAttr 0 [] $ map toRow rows
     tf = TableFoot nullAttr []
 
 --
-read_table_header :: ElementMatcher [[Blocks]]
+read_table_header :: ElementMatcher [[Cell]]
 read_table_header = matchingElement NsTable "table-header-rows"
                       $ matchChildContent' [ read_table_row
                                            ]
 
 --
-read_table_row    :: ElementMatcher [[Blocks]]
+read_table_row    :: ElementMatcher [[Cell]]
 read_table_row     = matchingElement NsTable "table-row"
                      $ liftA (:[])
                      $ matchChildContent'  [ read_table_cell
                                            ]
 
 --
-read_table_cell   :: ElementMatcher [Blocks]
+read_table_cell   :: ElementMatcher [Cell]
 read_table_cell    = matchingElement NsTable "table-cell"
-                     $ liftA (compactify.(:[]))
+                     $ liftA3 cell'
+                       (readAttrWithDefault NsTable "number-rows-spanned" 1 >>^ RowSpan)
+                       (readAttrWithDefault NsTable "number-columns-spanned" 1 >>^ ColSpan)
                      $ matchChildContent' [ read_paragraph
                                           , read_list
                                           ]
+  where
+    cell' rowSpan colSpan blocks = map (cell AlignDefault rowSpan colSpan) $ compactify [blocks]
 
 ----------------------
 -- Frames
diff --git a/src/Text/Pandoc/Readers/ODT/Generic/XMLConverter.hs b/src/Text/Pandoc/Readers/ODT/Generic/XMLConverter.hs
--- a/src/Text/Pandoc/Readers/ODT/Generic/XMLConverter.hs
+++ b/src/Text/Pandoc/Readers/ODT/Generic/XMLConverter.hs
@@ -64,7 +64,7 @@
 import           Data.Text (Text)
 import           Data.Default
 import           Data.Maybe
-import           Data.List (foldl')
+import qualified Data.List as L
 import qualified Data.List.NonEmpty as NonEmpty
 import           Data.List.NonEmpty (NonEmpty(..))
 
@@ -298,7 +298,7 @@
                          => XMLConverterState nsID extraState
                          -> Maybe (XMLConverterState nsID extraState)
     extractNSAttrs startState
-                         = foldl' (\state d -> state >>= addNS d)
+                         = L.foldl' (\state d -> state >>= addNS d)
                                  (Just startState)
                                  nsAttribs
       where nsAttribs    = mapMaybe readNSattr (XML.elAttribs element)
diff --git a/src/Text/Pandoc/Readers/ODT/StyleReader.hs b/src/Text/Pandoc/Readers/ODT/StyleReader.hs
--- a/src/Text/Pandoc/Readers/ODT/StyleReader.hs
+++ b/src/Text/Pandoc/Readers/ODT/StyleReader.hs
@@ -45,7 +45,7 @@
 
 import Data.Default
 import qualified Data.Foldable as F
-import Data.List (unfoldr, foldl')
+import qualified Data.List as L
 import qualified Data.Map as M
 import Data.Maybe
 import Data.Text (Text)
@@ -121,7 +121,7 @@
                               &&&
                               lookupDefaultingAttr NsStyle "font-pitch"
                             ))
-                    >>?^ ( M.fromList . foldl' accumLegalPitches [] )
+                    >>?^ ( M.fromList . L.foldl' accumLegalPitches [] )
                   ) `ifFailedDo` returnV (Right M.empty)
   where accumLegalPitches ls (Nothing,_) = ls
         accumLegalPitches ls (Just n,p)  = (n,p):ls
@@ -603,7 +603,7 @@
 -- be the first element of the list, followed by its parent and so on.
 -- The current style is not in the list.
 parents               :: Style       -> Styles ->      [Style]
-parents style styles = unfoldr findNextParent style -- Ha!
+parents style styles = L.unfoldr findNextParent style -- Ha!
   where findNextParent Style{..}
           = fmap duplicate $ (`lookupStyle` styles) =<< styleParentName
 
diff --git a/src/Text/Pandoc/Readers/Org/Blocks.hs b/src/Text/Pandoc/Readers/Org/Blocks.hs
--- a/src/Text/Pandoc/Readers/Org/Blocks.hs
+++ b/src/Text/Pandoc/Readers/Org/Blocks.hs
@@ -38,7 +38,7 @@
 import Data.Char (isSpace)
 import Data.Default (Default)
 import Data.Functor (($>))
-import Data.List (find, foldl')
+import qualified Data.List as L
 import Data.Maybe (fromMaybe, isJust, isNothing)
 import Data.Text (Text)
 import Data.List.NonEmpty (nonEmpty)
@@ -130,9 +130,9 @@
 blockAttributes = try $ do
   kv <- many stringyMetaAttribute
   guard $ all (isBlockAttr . fst) kv
-  let caption = foldl' (appendValues "caption") Nothing kv
-  let kvAttrs = foldl' (appendValues "attr_html") Nothing kv
-  let name    = snd <$> find ((`elem` ["name", "label"]) . fst) (reverse kv)
+  let caption = L.foldl' (appendValues "caption") Nothing kv
+  let kvAttrs = L.foldl' (appendValues "attr_html") Nothing kv
+  let name    = snd <$> L.find ((`elem` ["name", "label"]) . fst) (reverse kv)
   caption' <- traverse (parseFromString inlines . (<> "\n")) caption
   kvAttrs' <- parseFromString keyValues . (<> "\n") $ fromMaybe mempty kvAttrs
   return BlockAttributes
@@ -231,7 +231,7 @@
   newline
   content <- rawBlockContent "example"
   let id' = fromMaybe mempty $ blockAttrName blockAttrs
-  let codeBlck = B.codeBlockWith (id', "example":classes, kv) content
+  let codeBlck = B.codeBlockWith (id', classes, kv) content
   return . return $ codeBlck
 
 rawBlockLines :: Monad m => (Text   -> F Blocks) -> Text -> OrgParser m (F Blocks)
@@ -558,7 +558,7 @@
    exampleLine = try $ exampleLineStart *> anyLine
 
 exampleCode :: Text -> Blocks
-exampleCode = B.codeBlockWith ("", ["example"], [])
+exampleCode = B.codeBlockWith ("", [], [])
 
 
 --
diff --git a/src/Text/Pandoc/Readers/RIS.hs b/src/Text/Pandoc/Readers/RIS.hs
--- a/src/Text/Pandoc/Readers/RIS.hs
+++ b/src/Text/Pandoc/Readers/RIS.hs
@@ -23,7 +23,7 @@
 import Text.Pandoc.Definition
 import Text.Pandoc.Parsing
 import Data.Char (isAsciiUpper, isDigit, isSpace, ord, chr)
-import Data.List (foldl')
+import qualified Data.List as L
 import Citeproc (Reference(..), ItemId(..), Val(..), Date(..), DateParts(..),
                  toVariable)
 import Text.Pandoc.Builder as B
@@ -185,7 +185,7 @@
   return $ fixDuplicateIds $ map risRecordToReference recs
 
 fixDuplicateIds :: [Reference Text] -> [Reference Text]
-fixDuplicateIds = reverse . snd . foldl' go (mempty, [])
+fixDuplicateIds = reverse . snd . L.foldl' go (mempty, [])
  where
    go (ids_seen, refs) ref =
      case M.lookup (referenceId ref) ids_seen of
diff --git a/src/Text/Pandoc/Readers/RST.hs b/src/Text/Pandoc/Readers/RST.hs
--- a/src/Text/Pandoc/Readers/RST.hs
+++ b/src/Text/Pandoc/Readers/RST.hs
@@ -1520,19 +1520,11 @@
                        , str
                        , endline
                        , smart
-                       , hyphens
                        , escapedChar
                        , symbol ] <?> "inline content"
 
 parseInlineFromText :: PandocMonad m => Text -> RSTParser m Inlines
 parseInlineFromText = parseFromString' (trimInlines . mconcat <$> many inline)
-
-hyphens :: Monad m => RSTParser m Inlines
-hyphens = do
-  result <- many1Char (char '-')
-  optional endline
-  -- don't want to treat endline after hyphen or dash as a space
-  return $ B.str result
 
 escapedChar :: Monad m => RSTParser m Inlines
 escapedChar = do c <- escaped anyChar
diff --git a/src/Text/Pandoc/Readers/RTF.hs b/src/Text/Pandoc/Readers/RTF.hs
--- a/src/Text/Pandoc/Readers/RTF.hs
+++ b/src/Text/Pandoc/Readers/RTF.hs
@@ -20,7 +20,7 @@
 import Control.Monad
 import Control.Monad.Except (throwError)
 import Crypto.Hash (hashWith, SHA1(SHA1))
-import Data.List (find, foldl')
+import qualified Data.List as L
 import Data.Word (Word8, Word16)
 import Data.Default
 import Data.Text (Text)
@@ -558,8 +558,9 @@
                                     TableRow (curCell : cs) : rs
                                   [] -> [TableRow [curCell]] -- shouldn't happen
                            , sCurrentCell = mempty }
-    ControlWord "intbl" _ ->
-      emitBlocks bs <* modifyGroup (\g -> g{ gInTable = True })
+    ControlWord "intbl" _ -> do
+      ls <- closeLists 0 -- see #11364
+      ((ls <>) <$> emitBlocks bs) <* modifyGroup (\g -> g{ gInTable = True })
     ControlWord "plain" _ -> bs <$ modifyGroup (const def)
     ControlWord "lquote" _ -> bs <$ addText "\x2018"
     ControlWord "rquote" _ -> bs <$ addText "\x2019"
@@ -627,9 +628,10 @@
       modifyGroup (\g -> g{ gUnderline = boolParam mbp })
     ControlWord "ulnone" _ -> bs <$
       modifyGroup (\g -> g{ gUnderline = False })
-    ControlWord "pard" _ -> bs <$ do
+    ControlWord "pard" _ -> do
+      newbs <- emitBlocks bs
       modifyGroup (const def)
-      getStyleFormatting 0 >>= foldM processTok bs
+      getStyleFormatting 0 >>= foldM processTok newbs
     ControlWord "par" _ -> emitBlocks bs
     _ -> pure bs
 
@@ -788,22 +790,29 @@
   let isFieldMod (Tok _ (ControlWord w _)) =
         w `elem` ["flddirty", "fldedit", "fldlock", "fldpriv"]
       isFieldMod _ = False
+
+  let instructionTokens (Tok _ (Grouped toks)) = Just toks
+      instructionTokens unformattedTok@(Tok _ (UnformattedText _)) = Just [unformattedTok]
+      instructionTokens _ = Nothing
   case dropWhile isFieldMod ts of
     [Tok _ (Grouped
        (Tok _ (ControlSymbol '*')
        :Tok _ (ControlWord "fldinst" Nothing)
-       :Tok _ (Grouped instrtoks)
+       :instrtoks
        :_)),
      Tok _ (Grouped
        (Tok _ (ControlWord "fldrslt" Nothing)
-       :Tok _ (Grouped resulttoks) : _))] -> do
-         case getHyperlink instrtoks of
-           Just linkdest -> do
-             modifyGroup $ \g -> g{ gHyperlink = Just linkdest }
-             result <- foldM processTok bs resulttoks
-             modifyGroup $ \g -> g{ gHyperlink = Nothing }
-             return result
-           Nothing -> foldM processTok bs resulttoks
+       :resulttoks))] -> do
+         case instructionTokens instrtoks of
+           Nothing -> pure bs
+           Just instrtoks' ->
+             case getHyperlink instrtoks' of
+               Just linkdest -> do
+                 modifyGroup $ \g -> g{ gHyperlink = Just linkdest }
+                 result <- foldM processTok bs resulttoks
+                 modifyGroup $ \g -> g{ gHyperlink = Nothing }
+                 return result
+               Nothing -> foldM processTok bs resulttoks
     _ -> pure bs
 
 getHyperlink :: [Tok] -> Maybe Text
@@ -895,7 +904,7 @@
                   _ -> mempty
   let isBasedOn (Tok _ (ControlWord "sbasedon" (Just _))) = True
       isBasedOn _ = False
-  let styBasedOn = case find isBasedOn toks of
+  let styBasedOn = case L.find isBasedOn toks of
                      Just (Tok _ (ControlWord "sbasedon" (Just i))) -> Just i
                      _ -> Nothing
   let isStyleControl (Tok _ (ControlWord x _)) =
@@ -920,7 +929,7 @@
 
 handlePict :: PandocMonad m => [Tok] -> RTFParser m ()
 handlePict toks = do
-  let pict = foldl' getPictData def toks
+  let pict = L.foldl' getPictData def toks
   let altText = "image"
   let bytes =
         if picBinary pict
@@ -959,7 +968,7 @@
 
 
 processFontTable :: [Tok] -> FontTable
-processFontTable = snd . foldl' go (0, mempty)
+processFontTable = snd . L.foldl' go (0, mempty)
  where
   go (fontnum, tbl) (Tok _ tok') =
     case tok' of
@@ -972,7 +981,7 @@
      (ControlWord "fdecor" _) -> (fontnum, IntMap.insert fontnum Decor tbl)
      (ControlWord "ftech" _) -> (fontnum, IntMap.insert fontnum Tech tbl)
      (ControlWord "fbidi" _) -> (fontnum, IntMap.insert fontnum Bidi tbl)
-     (Grouped ts) -> foldl' go (fontnum, tbl) ts
+     (Grouped ts) -> L.foldl' go (fontnum, tbl) ts
      _ -> (fontnum, tbl)
 
 defaultAnsiWordToChar :: Word8 -> Char
diff --git a/src/Text/Pandoc/Readers/Textile.hs b/src/Text/Pandoc/Readers/Textile.hs
--- a/src/Text/Pandoc/Readers/Textile.hs
+++ b/src/Text/Pandoc/Readers/Textile.hs
@@ -38,7 +38,8 @@
 import Control.Monad (guard, liftM)
 import Control.Monad.Except (throwError)
 import Data.Char (digitToInt, isUpper)
-import Data.List (intersperse, transpose, foldl')
+import Data.List (intersperse, transpose)
+import qualified Data.List as L
 import Data.List.NonEmpty (NonEmpty(..), nonEmpty)
 import qualified Data.Map as M
 import Data.Text (Text)
@@ -732,7 +733,7 @@
 
 -- | Html / CSS attributes
 attributes :: PandocMonad m => TextileParser m Attr
-attributes = foldl' (flip ($)) ("",[],[]) <$>
+attributes = L.foldl' (flip ($)) ("",[],[]) <$>
   try (do special <- option id specialAttribute
           attrs <- many attribute
           return (special : attrs))
diff --git a/src/Text/Pandoc/SelfContained.hs b/src/Text/Pandoc/SelfContained.hs
--- a/src/Text/Pandoc/SelfContained.hs
+++ b/src/Text/Pandoc/SelfContained.hs
@@ -431,7 +431,7 @@
             report $ CouldNotFetchResource r ""
             return $ CouldNotFetch e
           PandocHttpError u er -> do
-            report $ CouldNotFetchResource u (tshow er)
+            report $ CouldNotFetchResource u er
             return $ CouldNotFetch e
           _ -> throwError e
    removeQueryAndFragment = T.takeWhile (\c -> c /= '#' && c /= '?')
diff --git a/src/Text/Pandoc/Shared.hs b/src/Text/Pandoc/Shared.hs
--- a/src/Text/Pandoc/Shared.hs
+++ b/src/Text/Pandoc/Shared.hs
@@ -56,6 +56,7 @@
                      inlineListToIdentifier,
                      textToIdentifier,
                      isHeaderBlock,
+                     hasLineBreaks,
                      onlySimpleTableCells,
                      isTightList,
                      taskListItemFromAscii,
@@ -90,7 +91,8 @@
 import Data.Char (isAlpha, isLower, isSpace, isUpper, toLower, isAlphaNum,
                   generalCategory, GeneralCategory(NonSpacingMark,
                   SpacingCombiningMark, EnclosingMark, ConnectorPunctuation))
-import Data.List (find, foldl', groupBy, intercalate, intersperse, union)
+import Data.List (find, groupBy, intercalate, intersperse, union)
+import qualified Data.List as L
 import qualified Data.Map as M
 import Data.Maybe (mapMaybe)
 import Data.Monoid (Any (..) )
@@ -607,6 +609,18 @@
                      x  -> x
     numIdent n = baseIdent <> "-" <> tshow n
 
+-- | True if inlines include a LineBreak (possibly embedded), with the exception
+-- of line breaks in Notes.
+hasLineBreaks :: [Inline] -> Bool
+hasLineBreaks = getAny . query isLineBreak . walk removeNote
+  where
+    removeNote :: Inline -> Inline
+    removeNote (Note _) = Str ""
+    removeNote x        = x
+    isLineBreak :: Inline -> Any
+    isLineBreak LineBreak = Any True
+    isLineBreak _         = Any False
+
 -- | True if block is a Header block.
 isHeaderBlock :: Block -> Bool
 isHeaderBlock Header{} = True
@@ -733,7 +747,7 @@
 -- | Canonicalizes a file path by removing redundant @.@ and @..@.
 makeCanonical :: FilePath -> FilePath
 makeCanonical = Posix.joinPath . transformPathParts . splitDirectories
- where  transformPathParts = reverse . foldl' go []
+ where  transformPathParts = reverse . L.foldl' go []
         go as        "."  = as
         go ("..":as) ".." = ["..", ".."] <> as
         go (_:as)    ".." = as
@@ -749,7 +763,7 @@
 -- > collapseFilePath "parent/foo/.." ==  "parent"
 -- > collapseFilePath "/parent/foo/../../bar" ==  "/bar"
 collapseFilePath :: FilePath -> FilePath
-collapseFilePath = Posix.joinPath . reverse . foldl' go [] . splitDirectories
+collapseFilePath = Posix.joinPath . reverse . L.foldl' go [] . splitDirectories
   where
     go rs "." = rs
     go r@(p:rs) ".." = case p of
diff --git a/src/Text/Pandoc/UTF8.hs b/src/Text/Pandoc/UTF8.hs
--- a/src/Text/Pandoc/UTF8.hs
+++ b/src/Text/Pandoc/UTF8.hs
@@ -8,7 +8,7 @@
    Stability   : alpha
    Portability : portable
 
-UTF-8 aware string IO functions that will work with GHC 6.10, 6.12, or 7.
+UTF-8 aware string IO functions.
 -}
 module Text.Pandoc.UTF8 ( readFile
                         , getContents
diff --git a/src/Text/Pandoc/Writers/AsciiDoc.hs b/src/Text/Pandoc/Writers/AsciiDoc.hs
--- a/src/Text/Pandoc/Writers/AsciiDoc.hs
+++ b/src/Text/Pandoc/Writers/AsciiDoc.hs
@@ -163,6 +163,7 @@
   needsEscape '+' = True
   needsEscape '`' = True
   needsEscape '*' = True
+  needsEscape '#' = True
   needsEscape '_' = True
   needsEscape '<' = True
   needsEscape '>' = True
@@ -593,7 +594,9 @@
                                    _           -> False
        isSpacy Start (Str xs)
          | Just (c, _) <- T.uncons xs = isPunctuation c || isSpace c
-       isSpacy _ _ = True
+       isSpacy End (Link{}) = True
+       isSpacy End (Image{}) = True
+       isSpacy _ _ = False
 
 setIntraword :: PandocMonad m => Bool -> ADW m ()
 setIntraword b = modify $ \st -> st{ intraword = b }
@@ -609,25 +612,27 @@
   contents <- inlineListToAsciiDoc opts lst
   isIntraword <- gets intraword
   let marker = if isIntraword then "__" else "_"
-  return $ marker <> contents <> marker
+  return $ delimited marker marker contents
 inlineToAsciiDoc opts (Underline lst) = do
   contents <- inlineListToAsciiDoc opts lst
-  return $ "[.underline]#" <> contents <> "#"
+  return $ delimited "[.underline]#" "#" contents
 inlineToAsciiDoc opts (Strong lst) = do
   contents <- inlineListToAsciiDoc opts lst
   isIntraword <- gets intraword
   let marker = if isIntraword then "**" else "*"
-  return $ marker <> contents <> marker
+  return $ delimited marker marker contents
 inlineToAsciiDoc opts (Strikeout lst) = do
   contents <- inlineListToAsciiDoc opts lst
-  return $ "[line-through]#" <> contents <> "#"
+  return $ delimited "[line-through]#" "#" contents
 inlineToAsciiDoc opts (Superscript lst) = do
   contents <- inlineListToAsciiDoc opts lst
-  return $ "^" <> contents <> "^"
+  return $ delimited "^" "^" contents
 inlineToAsciiDoc opts (Subscript lst) = do
   contents <- inlineListToAsciiDoc opts lst
-  return $ "~" <> contents <> "~"
-inlineToAsciiDoc opts (SmallCaps lst) = inlineListToAsciiDoc opts lst
+  return $ delimited "~" "~" contents
+inlineToAsciiDoc opts (SmallCaps lst) = do
+  contents <- inlineListToAsciiDoc opts lst
+  return $ delimited "[smallcaps]#" "#" contents
 inlineToAsciiDoc opts (Quoted qt lst) = do
   isLegacy <- gets legacy
   contents <- inlineListToAsciiDoc opts lst
diff --git a/src/Text/Pandoc/Writers/DocBook.hs b/src/Text/Pandoc/Writers/DocBook.hs
--- a/src/Text/Pandoc/Writers/DocBook.hs
+++ b/src/Text/Pandoc/Writers/DocBook.hs
@@ -17,7 +17,7 @@
 import Data.Generics (everywhere, mkT)
 import Data.List (nub, partition)
 import Data.Maybe (isNothing)
-import Data.Monoid (All (..), Any (..))
+import Data.Monoid (All (..))
 import Data.Text (Text)
 import qualified Data.Text as T
 import qualified Text.Pandoc.Builder as B
@@ -343,16 +343,6 @@
         else inTagsIndented "figure" $
              inTagsSimple "title" title $$
              mconcat mediaobjects
-
-hasLineBreaks :: [Inline] -> Bool
-hasLineBreaks = getAny . query isLineBreak . walk removeNote
-  where
-    removeNote :: Inline -> Inline
-    removeNote (Note _) = Str ""
-    removeNote x        = x
-    isLineBreak :: Inline -> Any
-    isLineBreak LineBreak = Any True
-    isLineBreak _         = Any False
 
 alignmentToString :: Alignment -> Text
 alignmentToString alignment = case alignment of
diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs
--- a/src/Text/Pandoc/Writers/Docx.hs
+++ b/src/Text/Pandoc/Writers/Docx.hs
@@ -29,726 +29,816 @@
 import Control.Monad.Except (throwError)
 import Control.Monad.Reader ( ReaderT(runReaderT) )
 import Control.Monad.State.Strict ( StateT(runStateT) )
-import qualified Data.ByteString.Lazy as BL
-import Data.Containers.ListUtils (nubOrd)
-import Data.Char (isSpace)
-import Data.List (isPrefixOf, isSuffixOf)
-import Data.String (fromString)
-import qualified Data.Map as M
-import Data.Maybe (fromMaybe, mapMaybe, maybeToList)
-import qualified Data.Set as Set
-import qualified Data.Text as T
-import Data.Text (Text)
-import Data.Time.Clock.POSIX
-import Skylighting
-import Text.Pandoc.Class (PandocMonad, toLang)
-import qualified Text.Pandoc.Class.PandocMonad as P
-import Text.Pandoc.Data (readDataFile, readDefaultDataFile)
-import Data.Time
-import qualified Text.Pandoc.UTF8 as UTF8
-import Text.Pandoc.Definition
-import Text.Pandoc.Error
-import Text.Pandoc.Highlighting (defaultStyle)
-import Text.Pandoc.MIME (getMimeTypeDef)
-import Text.Pandoc.Options
-import Text.Pandoc.Readers.Docx.Parse (extractTarget)
-import Text.Pandoc.Writers.Docx.StyleMap
-import Text.Pandoc.Writers.Docx.Types
-import Text.Pandoc.Writers.Docx.OpenXML (writeOpenXML, maxListLevel)
-import Text.Pandoc.Shared
-import Text.Pandoc.Walk
-import Text.Pandoc.Writers.Shared
-import Text.Pandoc.Writers.OOXML
-import Text.Pandoc.XML.Light as XML
-import Data.Generics (mkT, everywhere)
-import Text.Collate.Lang (renderLang, Lang(..))
-
-writeDocx :: (PandocMonad m)
-          => WriterOptions  -- ^ Writer options
-          -> Pandoc         -- ^ Document to convert
-          -> m BL.ByteString
-writeDocx opts doc = do
-  let Pandoc meta blocks = walk fixDisplayMath doc
-  setupTranslations meta
-  let blocks' = makeSectionsWithOffsets (writerNumberOffset opts)
-                   True Nothing blocks
-  let doc' = Pandoc meta blocks'
-
-  username <- P.lookupEnv "USERNAME"
-  utctime <- P.getTimestamp
-  oldUserDataDir <- P.getUserDataDir
-  P.setUserDataDir Nothing
-  res <- readDefaultDataFile "reference.docx"
-  P.setUserDataDir oldUserDataDir
-  let distArchive = toArchive $ BL.fromStrict res
-  refArchive <- case writerReferenceDoc opts of
-                     Just f  -> toArchive . BL.fromStrict . fst
-                                   <$> P.fetchItem (T.pack f)
-                     Nothing -> toArchive . BL.fromStrict <$>
-                          readDataFile "reference.docx"
-
-  parsedDoc <- parseXml refArchive distArchive "word/document.xml"
-  let wname f qn = qPrefix qn == Just "w" && f (qName qn)
-  let mbsectpr = filterElementName (wname (=="sectPr")) parsedDoc
-
-  -- Gets the template size
-  let mbpgsz = mbsectpr >>= filterElementName (wname (=="pgSz"))
-  let mbAttrSzWidth = mbpgsz >>= lookupAttrBy ((=="w") . qName) . elAttribs
-
-  let mbpgmar = mbsectpr >>= filterElementName (wname (=="pgMar"))
-  let mbAttrMarLeft = mbpgmar >>= lookupAttrBy ((=="left") . qName) . elAttribs
-  let mbAttrMarRight = mbpgmar >>= lookupAttrBy ((=="right") . qName) . elAttribs
-
-  -- Get the available area (converting the size and the margins to int and
-  -- doing the difference
-  let pgContentWidth = do
-                         w <- mbAttrSzWidth >>= safeRead
-                         r <- mbAttrMarRight >>= safeRead
-                         l <- mbAttrMarLeft >>= safeRead
-                         pure $ w - r - l
-
-  -- styles
-  mblang <- toLang $ getLang opts meta
-  -- TODO FIXME avoid this generic traversal!
-  -- lang is in w:docDefaults /  w:rPr  /  w:lang
-  let addLang :: Element -> Element
-      addLang = case mblang of
-                  Nothing -> id
-                  Just l  -> everywhere (mkT (go l))
-        where
-          go :: Lang -> Element -> Element
-          go lang e'
-           | qName (elName e') == "lang"
-             = if isEastAsianLang lang
-                  then e'{ elAttribs =
-                             map (setattr "eastAsia" (renderLang lang)) $
-                             elAttribs e' }
-                  else
-                    if isBidiLang lang
-                       then e'{ elAttribs =
-                                 map (setattr "bidi" (renderLang lang)) $
-                                 elAttribs e' }
-                       else e'{ elAttribs =
-                                 map (setattr "val" (renderLang lang)) $
-                                 elAttribs e' }
-           | otherwise = e'
-
-          setattr attrname l (XML.Attr qn@(QName s _ _) _)
-            | s == attrname  = XML.Attr qn l
-          setattr _ _ x      = x
-
-          isEastAsianLang Lang{ langLanguage = lang } =
-             lang == "zh" || lang == "ja" || lang == "ko"
-          isBidiLang Lang{ langLanguage = lang } =
-             lang == "he" || lang == "ar"
-
-  let stylepath = "word/styles.xml"
-  styledoc <- addLang <$> parseXml refArchive distArchive stylepath
-
-  -- parse styledoc for heading styles
-  let styleMaps = getStyleMaps refArchive
-
-  let tocTitle = case lookupMetaInlines "toc-title" meta of
-                   [] -> stTocTitle defaultWriterState
-                   ls -> ls
-
-  let isRTLmeta = case lookupMeta "dir" meta of
-        Just (MetaString "rtl")        -> True
-        Just (MetaInlines [Str "rtl"]) -> True
-        _                              -> False
-
-  let env = defaultWriterEnv {
-          envRTL = isRTLmeta
-        , envChangesAuthor = fromMaybe "unknown" username
-        , envChangesDate   = T.pack $ formatTime defaultTimeLocale "%FT%XZ" utctime
-        , envPrintWidth = maybe 420 (`quot` 20) pgContentWidth
-        }
-
-  let isImageNode e = findAttr (QName "Type" Nothing Nothing) e == Just "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"
-  let isHeaderNode e = findAttr (QName "Type" Nothing Nothing) e == Just "http://schemas.openxmlformats.org/officeDocument/2006/relationships/header"
-  let isFooterNode e = findAttr (QName "Type" Nothing Nothing) e == Just "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer"
-  parsedRels <- filterElements
-                  (\e -> isImageNode e || isHeaderNode e || isFooterNode e)
-              <$> parseXml refArchive distArchive "word/_rels/document.xml.rels"
-  let getRelId e =
-        case findAttr (QName "Id" Nothing Nothing) e of
-          Just ident -> T.stripPrefix "rId" ident >>= safeRead
-          Nothing -> Nothing
-  let relIds = mapMaybe getRelId parsedRels
-  let maxRelId = if null relIds then 0 else maximum relIds
-
-  let headers = filter isHeaderNode parsedRels
-  let footers = filter isFooterNode parsedRels
-  -- word/_rels/document.xml.rels
-  let addBaseRel (url', target') (maxId, rels) =
-        case [e | e <- rels
-                , findAttr (QName "Target" Nothing Nothing) e ==
-                   Just target'] of
-          [] -> (maxId + 1, mknode "Relationship"
-                            [("Type",url')
-                            ,("Id","rId" <> tshow (maxId + 1))
-                            ,("Target",target')] () : rels)
-          _ -> (maxId, rels)
-
-  let (newMaxRelId, baserels) = foldr addBaseRel (maxRelId, parsedRels)
-                    [("http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering",
-                      "numbering.xml")
-                    ,("http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles",
-                      "styles.xml")
-                    ,("http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings",
-                      "settings.xml")
-                    ,("http://schemas.openxmlformats.org/officeDocument/2006/relationships/webSettings",
-                      "webSettings.xml")
-                    ,("http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable",
-                      "fontTable.xml")
-                    ,("http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme",
-                      "theme/theme1.xml")
-                    ,("http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes",
-                      "footnotes.xml")
-                    ,("http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments",
-                      "comments.xml")
-                    ]
-
-  let initialSt = defaultWriterState {
-          stStyleMaps  = styleMaps
-        , stTocTitle   = tocTitle
-        , stCurId      = newMaxRelId + 1
-        }
-
-  -- adjust contents to add sectPr from reference.docx
-  let sectpr = case mbsectpr of
-        Just sectpr' -> add_attrs (elAttribs sectpr') $ mknode "w:sectPr" []
-                             (elChildren sectpr')
-        Nothing      -> mknode "w:sectPr" []
-                          [ mknode "w:footnotePr" []
-                            [ mknode "w:numRestart" [("w:val","eachSect")] () ]
-                          ]
-
-  ((contents, footnotes, comments), st) <- runStateT
-                         (runReaderT
-                          (writeOpenXML opts{ writerWrapText = WrapNone }
-                                        doc')
-                          env{ envSectPr = Just sectpr })
-                         initialSt
-  let epochtime = floor $ utcTimeToPOSIXSeconds utctime
-  let imgs = M.elems $ stImages st
-
-  -- create entries for images in word/media/...
-  let toImageEntry (_,path,_,img) = toEntry ("word/" ++ path) epochtime $ toLazy img
-  let imageEntries = map toImageEntry imgs
-
-  let stdAttributes =
-            [("xmlns:w","http://schemas.openxmlformats.org/wordprocessingml/2006/main")
-            ,("xmlns:m","http://schemas.openxmlformats.org/officeDocument/2006/math")
-            ,("xmlns:r","http://schemas.openxmlformats.org/officeDocument/2006/relationships")
-            ,("xmlns:o","urn:schemas-microsoft-com:office:office")
-            ,("xmlns:v","urn:schemas-microsoft-com:vml")
-            ,("xmlns:w10","urn:schemas-microsoft-com:office:word")
-            ,("xmlns:a","http://schemas.openxmlformats.org/drawingml/2006/main")
-            ,("xmlns:pic","http://schemas.openxmlformats.org/drawingml/2006/picture")
-            ,("xmlns:wp","http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing")]
-
-
- -- we create [Content_Types].xml and word/_rels/document.xml.rels
-  -- from scratch rather than reading from reference.docx,
-  -- because Word sometimes changes these files when a reference.docx is modified,
-  -- e.g. deleting the reference to footnotes.xml or removing default entries
-  -- for image content types.
-
-  -- [Content_Types].xml
-  let mkOverrideNode (part', contentType') = mknode "Override"
-               [("PartName", T.pack part')
-               ,("ContentType", contentType')] ()
-  let mkImageOverride (_, imgpath, mbMimeType, _) =
-          mkOverrideNode ("/word/" <> imgpath,
-                          fromMaybe "application/octet-stream" mbMimeType)
-  let mkMediaOverride imgpath =
-          mkOverrideNode ("/" <> imgpath, getMimeTypeDef imgpath)
-  let overrides = map mkOverrideNode (
-                  [("/word/webSettings.xml",
-                    "application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml")
-                  ,("/word/numbering.xml",
-                    "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml")
-                  ,("/word/settings.xml",
-                    "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml")
-                  ,("/word/theme/theme1.xml",
-                    "application/vnd.openxmlformats-officedocument.theme+xml")
-                  ,("/word/fontTable.xml",
-                    "application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml")
-                  ,("/docProps/app.xml",
-                    "application/vnd.openxmlformats-officedocument.extended-properties+xml")
-                  ,("/docProps/core.xml",
-                    "application/vnd.openxmlformats-package.core-properties+xml")
-                  ,("/docProps/custom.xml",
-                    "application/vnd.openxmlformats-officedocument.custom-properties+xml")
-                  ,("/word/styles.xml",
-                    "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml")
-                  ,("/word/document.xml",
-                    "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml")
-                  ,("/word/comments.xml",
-                    "application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml")
-                  ,("/word/footnotes.xml",
-                    "application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml")
-                  ] ++
-                  map (\x -> (maybe "" (T.unpack . ("/word/" <>)) (extractTarget x),
-                       "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml")) headers ++
-                  map (\x -> (maybe "" (T.unpack . ("/word/" <>)) (extractTarget x),
-                       "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml")) footers) ++
-                    map mkImageOverride imgs ++
-                    [ mkMediaOverride (eRelativePath e)
-                        | e <- zEntries refArchive
-                        , "word/media/" `isPrefixOf` eRelativePath e ]
-
-  let mkDefaultNode (ext, mt) =
-        mknode "Default" [("Extension",ext),("ContentType",mt)] ()
-  let defaultnodes = map mkDefaultNode
-        [("xml", "application/xml"),
-         ("rels", "application/vnd.openxmlformats-package.relationships+xml"),
-         ("odttf",
-           "application/vnd.openxmlformats-officedocument.obfuscatedFont")]
-  let contentTypesDoc = mknode "Types" [("xmlns","http://schemas.openxmlformats.org/package/2006/content-types")] $ defaultnodes ++ overrides
-  let contentTypesEntry = toEntry "[Content_Types].xml" epochtime
-        $ renderXml contentTypesDoc
-
-  let toImgRel (ident,path,_,_) =  mknode "Relationship" [("Type","http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"),("Id",T.pack ident),("Target",T.pack path)] ()
-  let imgrels = map toImgRel imgs
-  let toLinkRel (src,ident) =  mknode "Relationship" [("Type","http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"),("Id",ident),("Target",src),("TargetMode","External") ] ()
-  let linkrels = map toLinkRel $ M.toList $ stExternalLinks st
-  let reldoc = mknode "Relationships" [("xmlns","http://schemas.openxmlformats.org/package/2006/relationships")] $ baserels ++ imgrels ++ linkrels
-  let relEntry = toEntry "word/_rels/document.xml.rels" epochtime
-        $ renderXml reldoc
-
-  let contents' = BL.fromStrict $ UTF8.fromText contents
-
-  -- word/document.xml
-  let contentEntry = toEntry "word/document.xml" epochtime contents'
-
-  -- footnotes
-  let notes = mknode "w:footnotes" stdAttributes footnotes
-  let footnotesEntry = toEntry "word/footnotes.xml" epochtime $ renderXml notes
-
-  -- footnote rels
-  let footnoteRelEntry = toEntry "word/_rels/footnotes.xml.rels" epochtime
-        $ renderXml $ mknode "Relationships" [("xmlns","http://schemas.openxmlformats.org/package/2006/relationships")]
-        linkrels
-
-  -- comments
-  let commentsEntry = toEntry "word/comments.xml" epochtime
-        $ renderXml $ mknode "w:comments" stdAttributes comments
-
-  -- styles
-
-  -- We only want to inject paragraph and text properties that
-  -- are not already in the style map. Note that keys in the stylemap
-  -- are normalized as lowercase.
-  let newDynamicParaProps = filter
-        (\sty -> not $ hasStyleName sty $ smParaStyle styleMaps)
-        (Set.toList $ stDynamicParaProps st)
-
-      newDynamicTextProps = filter
-        (\sty -> not $ hasStyleName sty $ smCharStyle styleMaps)
-        (Set.toList $ stDynamicTextProps st)
-
-  let newstyles = map newParaPropToOpenXml newDynamicParaProps ++
-                  map newTextPropToOpenXml newDynamicTextProps ++
-                  (case writerHighlightMethod opts of
-                     Skylighting sty -> styleToOpenXml styleMaps sty
-                     DefaultHighlighting -> styleToOpenXml styleMaps
-                                              defaultStyle
-                     _ -> [])
-  let styledoc' = styledoc{ elContent = elContent styledoc ++
-                                           map Elem newstyles }
-  let styleEntry = toEntry stylepath epochtime $ renderXml styledoc'
-
-  -- construct word/numbering.xml
-  let numpath = "word/numbering.xml"
-  numbering <- parseXml refArchive distArchive numpath
-  let newNumElts = mkNumbering (stLists st)
-  let pandocAdded e =
-       case findAttrBy ((== "abstractNumId") . qName) e >>= safeRead of
-         Just numid -> numid >= (990 :: Int)
-         Nothing    ->
-           case findAttrBy ((== "numId") . qName) e >>= safeRead of
-             Just numid -> numid >= (1000 :: Int)
-             Nothing    -> False
-  let oldElts = filter (not . pandocAdded) $ onlyElems (elContent numbering)
-  let allElts = oldElts ++ newNumElts
-  let numEntry = toEntry numpath epochtime $ renderXml numbering{ elContent =
-                       -- we want all the abstractNums first, then the nums,
-                       -- otherwise things break:
-                       [Elem e | e <- allElts
-                               , qName (elName e) == "abstractNum" ] ++
-                       [Elem e | e <- allElts
-                               , qName (elName e) == "num" ] }
-
-  let keywords = case lookupMeta "keywords" meta of
-                       Just (MetaList xs) -> map stringify xs
-                       _                  -> []
-
-  -- docProps/core.xml
-  let docPropsPath = "docProps/core.xml"
-  let extraCoreProps = ["subject","lang","category","description"]
-  let extraCorePropsMap = M.fromList $ zip extraCoreProps
-                       ["dc:subject","dc:language","cp:category","dc:description"]
-  let lookupMetaString' :: Text -> Meta -> Text
-      lookupMetaString' key' meta' =
-        case key' of
-             "description"    -> T.intercalate "_x000d_\n" (map stringify $ lookupMetaBlocks "description" meta')
-             key''            -> lookupMetaString key'' meta'
-
-  let docProps = mknode "cp:coreProperties"
-          [("xmlns:cp","http://schemas.openxmlformats.org/package/2006/metadata/core-properties")
-          ,("xmlns:dc","http://purl.org/dc/elements/1.1/")
-          ,("xmlns:dcterms","http://purl.org/dc/terms/")
-          ,("xmlns:dcmitype","http://purl.org/dc/dcmitype/")
-          ,("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance")]
-          $ mktnode "dc:title" [] (stringify $ docTitle meta)
-          : mktnode "dc:creator" [] (T.intercalate "; " (map stringify $ docAuthors meta))
-          : [ mktnode (M.findWithDefault "" k extraCorePropsMap) [] (lookupMetaString' k meta)
-            | k <- M.keys (unMeta meta), k `elem` extraCoreProps]
-          ++ mknode "cp:keywords" [] (T.intercalate ", " keywords)
-          : (\x -> [ mknode "dcterms:created" [("xsi:type","dcterms:W3CDTF")] x
-                   , mknode "dcterms:modified" [("xsi:type","dcterms:W3CDTF")] x
-                   ]) (T.pack $ formatTime defaultTimeLocale "%FT%XZ" utctime)
-  let docPropsEntry = toEntry docPropsPath epochtime $ renderXml docProps
-
-  -- docProps/custom.xml
-  let customProperties :: [(Text, Text)]
-      customProperties = [ (k, lookupMetaString k meta)
-                         | k <- M.keys (unMeta meta)
-                         , k `notElem` (["title", "author", "keywords"]
-                                       ++ extraCoreProps)]
-  let mkCustomProp (k, v) pid = mknode "property"
-         [("fmtid","{D5CDD505-2E9C-101B-9397-08002B2CF9AE}")
-         ,("pid", tshow pid)
-         ,("name", k)] $ mknode "vt:lpwstr" [] v
-  let customPropsPath = "docProps/custom.xml"
-  let customProps = mknode "Properties"
-          [("xmlns","http://schemas.openxmlformats.org/officeDocument/2006/custom-properties")
-          ,("xmlns:vt","http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes")
-          ] $ zipWith mkCustomProp customProperties [(2 :: Int)..]
-  let customPropsEntry = toEntry customPropsPath epochtime $ renderXml customProps
-
-  let relsPath = "_rels/.rels"
-  let rels = mknode "Relationships" [("xmlns", "http://schemas.openxmlformats.org/package/2006/relationships")]
-        $ map (\attrs -> mknode "Relationship" attrs ())
-        [ [("Id","rId1")
-          ,("Type","http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument")
-          ,("Target","word/document.xml")]
-        , [("Id","rId4")
-          ,("Type","http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties")
-          ,("Target","docProps/app.xml")]
-        , [("Id","rId3")
-          ,("Type","http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties")
-          ,("Target","docProps/core.xml")]
-        , [("Id","rId5")
-          ,("Type","http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties")
-          ,("Target","docProps/custom.xml")]
-        ]
-  let relsEntry = toEntry relsPath epochtime $ renderXml rels
-
-  -- we use dist archive for settings.xml, because Word sometimes
-  -- adds references to footnotes or endnotes we don't have...
-  -- we do, however, copy some settings over from reference
-  let settingsPath = "word/settings.xml"
-
-  settingsEntry <- copyChildren refArchive distArchive settingsPath epochtime
-                      -- note: these must go in the following order:
-                     [ "writeProtection"
-                     , "view"
-                     , "zoom"
-                     , "removePersonalInformation"
-                     , "removeDateAndTime"
-                     , "doNotDisplayPageBoundaries"
-                     , "displayBackgroundShape"
-                     , "printPostScriptOverText"
-                     , "printFractionalCharacterWidth"
-                     , "printFormsData"
-                     , "embedTrueTypeFonts"
-                     , "embedSystemFonts"
-                     , "saveSubsetFonts"
-                     , "saveFormsData"
-                     , "mirrorMargins"
-                     , "alignBordersAndEdges"
-                     , "bordersDoNotSurroundHeader"
-                     , "bordersDoNotSurroundFooter"
-                     , "gutterAtTop"
-                     , "hideSpellingErrors"
-                     , "hideGrammaticalErrors"
-                     , "activeWritingStyle"
-                     , "proofState"
-                     , "formsDesign"
-                     , "attachedTemplate"
-                     , "linkStyles"
-                     , "stylePaneFormatFilter"
-                     , "stylePaneSortMethod"
-                     , "documentType"
-                     , "mailMerge"
-                     , "revisionView"
-                     , "trackRevisions"
-                     , "doNotTrackMoves"
-                     , "doNotTrackFormatting"
-                     , "documentProtection"
-                     , "autoFormatOverride"
-                     , "styleLockTheme"
-                     , "styleLockQFSet"
-                     , "defaultTabStop"
-                     , "autoHyphenation"
-                     , "consecutiveHyphenLimit"
-                     , "hyphenationZone"
-                     , "doNotHyphenateCaps"
-                     , "showEnvelope"
-                     , "summaryLength"
-                     , "clickAndTypeStyle"
-                     , "defaultTableStyle"
-                     , "evenAndOddHeaders"
-                     , "bookFoldRevPrinting"
-                     , "bookFoldPrinting"
-                     , "bookFoldPrintingSheets"
-                     , "drawingGridHorizontalSpacing"
-                     , "drawingGridVerticalSpacing"
-                     , "displayHorizontalDrawingGridEvery"
-                     , "displayVerticalDrawingGridEvery"
-                     , "doNotUseMarginsForDrawingGridOrigin"
-                     , "drawingGridHorizontalOrigin"
-                     , "drawingGridVerticalOrigin"
-                     , "doNotShadeFormData"
-                     , "noPunctuationKerning"
-                     , "characterSpacingControl"
-                     , "printTwoOnOne"
-                     , "strictFirstAndLastChars"
-                     , "noLineBreaksAfter"
-                     , "noLineBreaksBefore"
-                     , "savePreviewPicture"
-                     , "doNotValidateAgainstSchema"
-                     , "saveInvalidXml"
-                     , "ignoreMixedContent"
-                     , "alwaysShowPlaceholderText"
-                     , "doNotDemarcateInvalidXml"
-                     , "saveXmlDataOnly"
-                     , "useXSLTWhenSaving"
-                     , "saveThroughXslt"
-                     , "showXMLTags"
-                     , "alwaysMergeEmptyNamespace"
-                     , "updateFields"
-                     , "hdrShapeDefaults"
-                     -- , "footnotePr" -- this can cause problems, see #9522
-                     -- , "endnotePr"
-                     , "compat"
-                     , "docVars"
-                     , "rsids"
-                     , "attachedSchema"
-                     , "themeFontLang"
-                     , "clrSchemeMapping"
-                     , "doNotIncludeSubdocsInStats"
-                     , "doNotAutoCompressPictures"
-                     , "forceUpgrade"
-                     , "captions"
-                     , "readModeInkLockDown"
-                     , "smartTagType"
-                     , "shapeDefaults"
-                     , "doNotEmbedSmartTags"
-                     , "decimalSymbol"
-                     , "listSeparator" ]
-
-  let entryFromArchive arch path =
-         maybe (throwError $ PandocSomeError
-                           $ T.pack $ path ++ " missing in reference docx")
-               return
-               (findEntryByPath path arch `mplus` findEntryByPath path distArchive)
-  docPropsAppEntry <- entryFromArchive refArchive "docProps/app.xml"
-  themeEntry <- entryFromArchive refArchive "word/theme/theme1.xml"
-  fontTableEntry <- entryFromArchive refArchive "word/fontTable.xml"
-  let fontTableRelsEntries = maybeToList $
-       findEntryByPath "word/_rels/fontTable.xml.rels" refArchive
-  let fontEntries = [entry | entry <- zEntries refArchive
-                           , "word/fonts/" `isPrefixOf` (eRelativePath entry)]
-                        -- or parse fontTable.xml.rels?
-  webSettingsEntry <- entryFromArchive refArchive "word/webSettings.xml"
-  headerFooterEntries <- mapM (entryFromArchive refArchive . ("word/" ++)) $
-                         mapMaybe (fmap T.unpack . extractTarget)
-                         (headers ++ footers)
-  let miscRelEntries = [ e | e <- zEntries refArchive
-                       , "word/_rels/" `isPrefixOf` eRelativePath e
-                       , ".xml.rels" `isSuffixOf` eRelativePath e
-                       , eRelativePath e /= "word/_rels/document.xml.rels"
-                       , eRelativePath e /= "word/_rels/footnotes.xml.rels" ]
-  let otherMediaEntries = [ e | e <- zEntries refArchive
-                          , "word/media/" `isPrefixOf` eRelativePath e ]
-
-  -- Create archive
-  let archive = foldr addEntryToArchive emptyArchive $
-                  contentTypesEntry : relsEntry : contentEntry : relEntry :
-                  footnoteRelEntry : numEntry : styleEntry : footnotesEntry :
-                  commentsEntry :
-                  docPropsEntry : docPropsAppEntry : customPropsEntry :
-                  themeEntry :
-                  settingsEntry : webSettingsEntry :
-                  fontTableEntry :
-                  fontTableRelsEntries ++ fontEntries ++
-                  imageEntries ++ headerFooterEntries ++
-                  miscRelEntries ++ otherMediaEntries
-  return $ fromArchive archive
-
-newParaPropToOpenXml :: ParaStyleName -> Element
-newParaPropToOpenXml (fromStyleName -> s) =
-  let styleId = T.filter (not . isSpace) s
-  in mknode "w:style" [ ("w:type", "paragraph")
-                      , ("w:customStyle", "1")
-                      , ("w:styleId", styleId)]
-     [ mknode "w:name" [("w:val", s)] ()
-     , mknode "w:basedOn" [("w:val","BodyText")] ()
-     , mknode "w:qFormat" [] ()
-     ]
-
-newTextPropToOpenXml :: CharStyleName -> Element
-newTextPropToOpenXml (fromStyleName -> s) =
-  let styleId = T.filter (not . isSpace) s
-  in mknode "w:style" [ ("w:type", "character")
-                      , ("w:customStyle", "1")
-                      , ("w:styleId", styleId)]
-     [ mknode "w:name" [("w:val", s)] ()
-     , mknode "w:basedOn" [("w:val","BodyTextChar")] ()
-     ]
-
-styleToOpenXml :: StyleMaps -> Style -> [Element]
-styleToOpenXml sm style =
-  maybeToList parStyle ++ mapMaybe toStyle alltoktypes
-  where alltoktypes = enumFromTo KeywordTok NormalTok
-        toStyle toktype | hasStyleName (fromString $ show toktype) (smCharStyle sm) = Nothing
-                        | otherwise = Just $
-                          mknode "w:style" [("w:type","character"),
-                           ("w:customStyle","1"),("w:styleId", tshow toktype)]
-                             [ mknode "w:name" [("w:val", tshow toktype)] ()
-                             , mknode "w:basedOn" [("w:val","VerbatimChar")] ()
-                             , mknode "w:rPr" [] $
-                               [ mknode "w:b" [] () | tokFeature tokenBold toktype ] ++
-                               [ mknode "w:i" [] () | tokFeature tokenItalic toktype ] ++
-                               [ mknode "w:color" [("w:val", tokCol toktype)] ()
-                                 | tokCol toktype /= "auto" ] ++
-                               [ mknode "w:u" [] () | tokFeature tokenUnderline toktype ] ++
-                               [ mknode "w:shd" [("w:val","clear")
-                                                ,("w:fill",tokBg toktype)] ()
-                                 | tokBg toktype /= "auto" ]
-                             ]
-        tokStyles = tokenStyles style
-        tokFeature f toktype = maybe False f $ M.lookup toktype tokStyles
-        tokCol toktype = maybe "auto" (T.pack . drop 1 . fromColor)
-                         $ (tokenColor =<< M.lookup toktype tokStyles)
-                           `mplus` defaultColor style
-        tokBg toktype = maybe "auto" (T.pack . drop 1 . fromColor)
-                         $ (tokenBackground =<< M.lookup toktype tokStyles)
-                           `mplus` backgroundColor style
-        parStyle | hasStyleName "Source Code" (smParaStyle sm) = Nothing
-                 | otherwise = Just $
-                   mknode "w:style" [("w:type","paragraph"),
-                           ("w:customStyle","1"),("w:styleId","SourceCode")]
-                             [ mknode "w:name" [("w:val","Source Code")] ()
-                             , mknode "w:basedOn" [("w:val","Normal")] ()
-                             , mknode "w:link" [("w:val","VerbatimChar")] ()
-                             , mknode "w:pPr" []
-                               $ mknode "w:wordWrap" [("w:val","off")] ()
-                               :
-                         maybe [] (\col -> [mknode "w:shd" [("w:val","clear"),("w:fill", T.pack $ drop 1 $ fromColor col)] ()]) (backgroundColor style)
-                             ]
-
-copyChildren :: (PandocMonad m)
-             => Archive -> Archive -> String -> Integer -> [Text] -> m Entry
-copyChildren refArchive distArchive path timestamp elNames = do
-  ref  <- parseXml refArchive distArchive path
-  dist <- parseXml distArchive distArchive path
-  els <- foldM (addEl ref dist) [] (reverse elNames)
-  return $ toEntry path timestamp
-         $ renderXml dist{ elContent = map cleanElem els }
-  where
-    addEl ref dist els name =
-      case filterChildName (hasName name) ref `mplus`
-             filterChildName (hasName name) dist of
-        Just el -> pure (el : els)
-        Nothing -> pure els
-    hasName name = (== name) . qName
-    cleanElem el@Element{elName=name} = Elem el{elName=name{qURI=Nothing}}
-
--- this is the lowest number used for a list numId
-baseListId :: Int
-baseListId = 1000
-
-mkNumbering :: [ListMarker] -> [Element]
-mkNumbering lists =
-  elts ++ zipWith mkNum lists [baseListId..(baseListId + length lists - 1)]
-    where elts = map mkAbstractNum (nubOrd lists)
-
-mkNum :: ListMarker -> Int -> Element
-mkNum marker numid =
-  mknode "w:num" [("w:numId",tshow numid)]
-   $ mknode "w:abstractNumId" [("w:val",listMarkerToId marker)] ()
-   : case marker of
-       NoMarker     -> []
-       BulletMarker -> []
-       CheckboxMarker _ -> []
-       NumberMarker _ _ start ->
-          map (\lvl -> mknode "w:lvlOverride" [("w:ilvl",tshow (lvl :: Int))]
-              $ mknode "w:startOverride" [("w:val",tshow start)] ())
-                [0..maxListLevel]
-
-mkAbstractNum :: ListMarker -> Element
-mkAbstractNum marker =
-  mknode "w:abstractNum" [("w:abstractNumId",listMarkerToId marker)]
-    $ mknode "w:nsid" [("w:val", T.justifyRight 8 '0' ("A" <> listMarkerToId marker))] ()
-    : mknode "w:multiLevelType" [("w:val","multilevel")] ()
-    : map (mkLvl marker)
-      [0..maxListLevel]
-
-mkLvl :: ListMarker -> Int -> Element
-mkLvl marker lvl =
-  mknode "w:lvl" [("w:ilvl",tshow lvl)] $
-    (case marker of
-        NumberMarker{} -> [mknode "w:start" [("w:val",start)] ()]
-        _ -> []) ++
-    [ mknode "w:numFmt" [("w:val",fmt)] ()
-    , mknode "w:lvlText" [("w:val", lvltxt)] ()
-    , mknode "w:lvlJc" [("w:val","left")] ()
-    , mknode "w:pPr" [] $
-        mknode "w:ind" [ ("w:left",tshow $ lvl * step + step)
-                       , ("w:hanging",tshow hang)
-                       ] ()
-    ] ++
-    maybe [] (\font ->
-                [ mknode "w:rPr" []
-                  [ mknode "w:rFonts" [ ("w:ascii", font)
-                                      , ("w:hAnsi", font)
-                                      , ("w:cs", font)
-                                      , ("w:hint", "default") ] () ]]) mbfont
-    where (fmt, lvltxt, mbfont, start) =
-            case marker of
-                 NoMarker             -> ("bullet"," ", Nothing, "1")
-                 BulletMarker         -> bulletFor lvl
-                 CheckboxMarker False -> ("bullet","\9744", Nothing, "1")
-                 CheckboxMarker True  -> ("bullet","\9746", Nothing, "1")
-                 NumberMarker st de n -> (styleFor st lvl
-                                         ,patternFor de ("%" <> tshow (lvl + 1))
-                                         ,Nothing
-                                         ,tshow n)
-          step = 720
-          hang :: Int
-          hang = 360
-          bulletFor 0 = ("bullet", "\xf0b7", Just "Symbol", "1") -- filled circle
-          bulletFor 1 = ("bullet", "o", Just "Courier New", "1") -- open o
-          bulletFor 2 = ("bullet", "\xf0a7", Just "Wingdings", "1")  -- closed box
-          bulletFor x = bulletFor (x `mod` 3)
-          styleFor UpperAlpha _   = "upperLetter"
-          styleFor LowerAlpha _   = "lowerLetter"
-          styleFor UpperRoman _   = "upperRoman"
-          styleFor LowerRoman _   = "lowerRoman"
-          styleFor Decimal _      = "decimal"
-          styleFor DefaultStyle 0 = "decimal"
-          styleFor DefaultStyle 1 = "lowerLetter"
-          styleFor DefaultStyle 2 = "lowerRoman"
-          styleFor DefaultStyle 3 = "decimal"
-          styleFor DefaultStyle 4 = "lowerLetter"
-          styleFor DefaultStyle 5 = "lowerRoman"
-          styleFor DefaultStyle x = styleFor DefaultStyle (x `mod` 6)
-          styleFor _ _            = "decimal"
-          patternFor OneParen s  = s <> ")"
-          patternFor TwoParens s = "(" <> s <> ")"
-          patternFor _ s         = s <> "."
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Lazy as BL
+import Data.Containers.ListUtils (nubOrd)
+import Data.Char (isSpace)
+import Data.List (isPrefixOf, isSuffixOf)
+import Data.String (fromString)
+import qualified Data.Map as M
+import Data.Maybe (fromMaybe, mapMaybe, maybeToList)
+import qualified Data.Set as Set
+import qualified Data.Text as T
+import Data.Text (Text)
+import Data.Time.Clock.POSIX
+import Skylighting
+import Text.Pandoc.Class (PandocMonad, toLang)
+import qualified Text.Pandoc.Class.PandocMonad as P
+import Text.Pandoc.Data (readDataFile, readDefaultDataFile)
+import Data.Time
+import qualified Text.Pandoc.UTF8 as UTF8
+import Text.Pandoc.Definition
+import Text.Pandoc.Error
+import Text.Pandoc.Highlighting (defaultStyle)
+import Text.Pandoc.MIME (MimeType, getMimeTypeDef)
+import Text.Pandoc.Options
+import Text.Pandoc.Readers.Docx.Parse (extractTarget)
+import Text.Pandoc.Writers.Docx.StyleMap
+import Text.Pandoc.Writers.Docx.Types
+import Text.Pandoc.Writers.Docx.OpenXML (writeOpenXML, maxListLevel)
+import Text.Pandoc.Shared
+import Text.Pandoc.Walk
+import Text.Pandoc.Writers.Shared
+import Text.Pandoc.Writers.OOXML
+import Text.Pandoc.XML.Light as XML
+import Text.Collate.Lang (renderLang, Lang(..))
+
+writeDocx :: (PandocMonad m)
+          => WriterOptions  -- ^ Writer options
+          -> Pandoc         -- ^ Document to convert
+          -> m BL.ByteString
+writeDocx opts doc = do
+  -- Phase 1: Document preprocessing
+  let Pandoc meta blocks = walk fixDisplayMath doc
+  setupTranslations meta
+  let blocks' = makeSectionsWithOffsets (writerNumberOffset opts)
+                   True Nothing blocks
+  let doc' = Pandoc meta blocks'
+
+  -- Phase 2: Archive loading
+  (refArchive, distArchive, username, utctime) <- loadArchives opts
+  let epochtime = floor $ utcTimeToPOSIXSeconds utctime
+
+  -- Phase 3: Page layout extraction
+  (mbsectpr, pgContentWidth) <- extractPageLayout refArchive distArchive
+
+  -- Phase 4: Language & style setup
+  mblang <- toLang $ getLang opts meta
+  let addLang = mkLangTransformer mblang
+  styledoc <- addLang <$> parseXml refArchive distArchive "word/styles.xml"
+  let styleMaps = getStyleMaps refArchive
+
+  let tocTitle = case lookupMetaInlines "toc-title" meta of
+                   [] -> stTocTitle defaultWriterState
+                   ls -> ls
+
+  let isRTLmeta = case lookupMeta "dir" meta of
+        Just (MetaString "rtl")        -> True
+        Just (MetaInlines [Str "rtl"]) -> True
+        _                              -> False
+
+  let env = defaultWriterEnv {
+          envRTL = isRTLmeta
+        , envChangesAuthor = fromMaybe "unknown" username
+        , envChangesDate   = T.pack $ formatTime defaultTimeLocale "%FT%XZ" utctime
+        , envPrintWidth = maybe 420 (`quot` 20) pgContentWidth
+        }
+
+  -- Phase 5: Relationship extraction
+  (baserels, headers, footers, newMaxRelId) <- extractRelationships refArchive distArchive
+
+  let initialSt = defaultWriterState {
+          stStyleMaps  = styleMaps
+        , stTocTitle   = tocTitle
+        , stCurId      = newMaxRelId + 1
+        }
+
+  -- Phase 6: Core content generation
+  -- adjust contents to add sectPr from reference.docx
+  let sectpr = case mbsectpr of
+        Just sectpr' -> add_attrs (elAttribs sectpr') $ mknode "w:sectPr" []
+                             (elChildren sectpr')
+        Nothing      -> mknode "w:sectPr" []
+                          [ mknode "w:footnotePr" []
+                            [ mknode "w:numRestart" [("w:val","eachSect")] () ]
+                          ]
+
+  ((contents, footnotes, comments), st) <- runStateT
+                         (runReaderT
+                          (writeOpenXML opts{ writerWrapText = WrapNone }
+                                        doc')
+                          env{ envSectPr = Just sectpr })
+                         initialSt
+  let imgs = M.elems $ stImages st
+
+  -- Phase 7: XML document construction
+  -- We create [Content_Types].xml and word/_rels/document.xml.rels
+  -- from scratch rather than reading from reference.docx,
+  -- because Word sometimes changes these files when a reference.docx is modified,
+  -- e.g. deleting the reference to footnotes.xml or removing default entries
+  -- for image content types.
+  let contentTypesEntry = mkContentTypesEntry epochtime imgs headers footers refArchive
+  let relEntry = mkDocumentRelsEntry epochtime baserels imgs (stExternalLinks st)
+  let contentEntry = toEntry "word/document.xml" epochtime
+                       (BL.fromStrict $ UTF8.fromText contents)
+  let footnotesEntry = mkFootnotesEntry epochtime footnotes
+  let footnoteRelEntry = mkFootnoteRelsEntry epochtime (stExternalLinks st)
+  let commentsEntry = mkCommentsEntry epochtime comments
+  let styleEntry = mkStylesEntry epochtime styledoc styleMaps st opts
+  numEntry <- mkNumberingEntry refArchive distArchive epochtime (stLists st)
+  let docPropsEntry = mkCorePropsEntry epochtime utctime meta
+  let customPropsEntry = mkCustomPropsEntry epochtime meta
+  let relsEntry = mkPackageRelsEntry epochtime
+
+  -- we use dist archive for settings.xml, because Word sometimes
+  -- adds references to footnotes or endnotes we don't have...
+  -- we do, however, copy some settings over from reference
+  settingsEntry <- copyChildren refArchive distArchive "word/settings.xml"
+                     epochtime settingsElementNames
+
+  -- Phase 8: Archive assembly
+  let toImageEntry (_, path, _, img) = toEntry ("word/" ++ path) epochtime $ toLazy img
+  let imageEntries = map toImageEntry imgs
+
+  refEntries <- collectReferenceEntries refArchive distArchive headers footers
+
+  let archive = foldr addEntryToArchive emptyArchive $
+                  contentTypesEntry : relsEntry : contentEntry : relEntry :
+                  footnoteRelEntry : numEntry : styleEntry : footnotesEntry :
+                  commentsEntry :
+                  docPropsEntry : customPropsEntry :
+                  settingsEntry :
+                  imageEntries ++ refEntries
+  return $ fromArchive archive
+
+newParaPropToOpenXml :: ParaStyleName -> Element
+newParaPropToOpenXml (fromStyleName -> s) =
+  let styleId = T.filter (not . isSpace) s
+  in mknode "w:style" [ ("w:type", "paragraph")
+                      , ("w:customStyle", "1")
+                      , ("w:styleId", styleId)]
+     [ mknode "w:name" [("w:val", s)] ()
+     , mknode "w:basedOn" [("w:val","BodyText")] ()
+     , mknode "w:qFormat" [] ()
+     ]
+
+newTextPropToOpenXml :: CharStyleName -> Element
+newTextPropToOpenXml (fromStyleName -> s) =
+  let styleId = T.filter (not . isSpace) s
+  in mknode "w:style" [ ("w:type", "character")
+                      , ("w:customStyle", "1")
+                      , ("w:styleId", styleId)]
+     [ mknode "w:name" [("w:val", s)] ()
+     , mknode "w:basedOn" [("w:val","BodyTextChar")] ()
+     ]
+
+styleToOpenXml :: StyleMaps -> Style -> [Element]
+styleToOpenXml sm style =
+  maybeToList parStyle ++ mapMaybe toStyle alltoktypes
+  where alltoktypes = enumFromTo KeywordTok NormalTok
+        toStyle toktype | hasStyleName (fromString $ show toktype) (smCharStyle sm) = Nothing
+                        | otherwise = Just $
+                          mknode "w:style" [("w:type","character"),
+                           ("w:customStyle","1"),("w:styleId", tshow toktype)]
+                             [ mknode "w:name" [("w:val", tshow toktype)] ()
+                             , mknode "w:basedOn" [("w:val","VerbatimChar")] ()
+                             , mknode "w:rPr" [] $
+                               [ mknode "w:b" [] () | tokFeature tokenBold toktype ] ++
+                               [ mknode "w:i" [] () | tokFeature tokenItalic toktype ] ++
+                               [ mknode "w:color" [("w:val", tokCol toktype)] ()
+                                 | tokCol toktype /= "auto" ] ++
+                               [ mknode "w:u" [] () | tokFeature tokenUnderline toktype ] ++
+                               [ mknode "w:shd" [("w:val","clear")
+                                                ,("w:fill",tokBg toktype)] ()
+                                 | tokBg toktype /= "auto" ]
+                             ]
+        tokStyles = tokenStyles style
+        tokFeature f toktype = maybe False f $ M.lookup toktype tokStyles
+        tokCol toktype = maybe "auto" (T.pack . drop 1 . fromColor)
+                         $ (tokenColor =<< M.lookup toktype tokStyles)
+                           `mplus` defaultColor style
+        tokBg toktype = maybe "auto" (T.pack . drop 1 . fromColor)
+                         $ (tokenBackground =<< M.lookup toktype tokStyles)
+                           `mplus` backgroundColor style
+        parStyle | hasStyleName "Source Code" (smParaStyle sm) = Nothing
+                 | otherwise = Just $
+                   mknode "w:style" [("w:type","paragraph"),
+                           ("w:customStyle","1"),("w:styleId","SourceCode")]
+                             [ mknode "w:name" [("w:val","Source Code")] ()
+                             , mknode "w:basedOn" [("w:val","Normal")] ()
+                             , mknode "w:link" [("w:val","VerbatimChar")] ()
+                             , mknode "w:pPr" []
+                               $ mknode "w:wordWrap" [("w:val","off")] ()
+                               :
+                         maybe [] (\col -> [mknode "w:shd" [("w:val","clear"),("w:fill", T.pack $ drop 1 $ fromColor col)] ()]) (backgroundColor style)
+                             ]
+
+copyChildren :: (PandocMonad m)
+             => Archive -> Archive -> String -> Integer -> [Text] -> m Entry
+copyChildren refArchive distArchive path timestamp elNames = do
+  ref  <- parseXml refArchive distArchive path
+  dist <- parseXml distArchive distArchive path
+  els <- foldM (addEl ref dist) [] (reverse elNames)
+  return $ toEntry path timestamp
+         $ renderXml dist{ elContent = map cleanElem els }
+  where
+    addEl ref dist els name =
+      case filterChildName (hasName name) ref `mplus`
+             filterChildName (hasName name) dist of
+        Just el -> pure (el : els)
+        Nothing -> pure els
+    hasName name = (== name) . qName
+    cleanElem el@Element{elName=name} = Elem el{elName=name{qURI=Nothing}}
+
+-- this is the lowest number used for a list numId
+baseListId :: Int
+baseListId = 1000
+
+-- | Standard XML namespace attributes for docx elements
+stdAttributes :: [(Text, Text)]
+stdAttributes =
+  [("xmlns:w","http://schemas.openxmlformats.org/wordprocessingml/2006/main")
+  ,("xmlns:m","http://schemas.openxmlformats.org/officeDocument/2006/math")
+  ,("xmlns:r","http://schemas.openxmlformats.org/officeDocument/2006/relationships")
+  ,("xmlns:o","urn:schemas-microsoft-com:office:office")
+  ,("xmlns:v","urn:schemas-microsoft-com:vml")
+  ,("xmlns:w10","urn:schemas-microsoft-com:office:word")
+  ,("xmlns:a","http://schemas.openxmlformats.org/drawingml/2006/main")
+  ,("xmlns:pic","http://schemas.openxmlformats.org/drawingml/2006/picture")
+  ,("xmlns:wp","http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing")]
+
+-- | Settings elements to copy from reference.docx (order matters)
+settingsElementNames :: [Text]
+settingsElementNames =
+  [ "writeProtection"
+  , "view"
+  , "zoom"
+  , "removePersonalInformation"
+  , "removeDateAndTime"
+  , "doNotDisplayPageBoundaries"
+  , "displayBackgroundShape"
+  , "printPostScriptOverText"
+  , "printFractionalCharacterWidth"
+  , "printFormsData"
+  , "embedTrueTypeFonts"
+  , "embedSystemFonts"
+  , "saveSubsetFonts"
+  , "saveFormsData"
+  , "mirrorMargins"
+  , "alignBordersAndEdges"
+  , "bordersDoNotSurroundHeader"
+  , "bordersDoNotSurroundFooter"
+  , "gutterAtTop"
+  , "hideSpellingErrors"
+  , "hideGrammaticalErrors"
+  , "activeWritingStyle"
+  , "proofState"
+  , "formsDesign"
+  , "attachedTemplate"
+  , "linkStyles"
+  , "stylePaneFormatFilter"
+  , "stylePaneSortMethod"
+  , "documentType"
+  , "mailMerge"
+  , "revisionView"
+  , "trackRevisions"
+  , "doNotTrackMoves"
+  , "doNotTrackFormatting"
+  , "documentProtection"
+  , "autoFormatOverride"
+  , "styleLockTheme"
+  , "styleLockQFSet"
+  , "defaultTabStop"
+  , "autoHyphenation"
+  , "consecutiveHyphenLimit"
+  , "hyphenationZone"
+  , "doNotHyphenateCaps"
+  , "showEnvelope"
+  , "summaryLength"
+  , "clickAndTypeStyle"
+  , "defaultTableStyle"
+  , "evenAndOddHeaders"
+  , "bookFoldRevPrinting"
+  , "bookFoldPrinting"
+  , "bookFoldPrintingSheets"
+  , "drawingGridHorizontalSpacing"
+  , "drawingGridVerticalSpacing"
+  , "displayHorizontalDrawingGridEvery"
+  , "displayVerticalDrawingGridEvery"
+  , "doNotUseMarginsForDrawingGridOrigin"
+  , "drawingGridHorizontalOrigin"
+  , "drawingGridVerticalOrigin"
+  , "doNotShadeFormData"
+  , "noPunctuationKerning"
+  , "characterSpacingControl"
+  , "printTwoOnOne"
+  , "strictFirstAndLastChars"
+  , "noLineBreaksAfter"
+  , "noLineBreaksBefore"
+  , "savePreviewPicture"
+  , "doNotValidateAgainstSchema"
+  , "saveInvalidXml"
+  , "ignoreMixedContent"
+  , "alwaysShowPlaceholderText"
+  , "doNotDemarcateInvalidXml"
+  , "saveXmlDataOnly"
+  , "useXSLTWhenSaving"
+  , "saveThroughXslt"
+  , "showXMLTags"
+  , "alwaysMergeEmptyNamespace"
+  , "updateFields"
+  , "hdrShapeDefaults"
+  -- , "footnotePr" -- this can cause problems, see #9522
+  -- , "endnotePr"
+  , "compat"
+  , "docVars"
+  , "rsids"
+  , "attachedSchema"
+  , "themeFontLang"
+  , "clrSchemeMapping"
+  , "doNotIncludeSubdocsInStats"
+  , "doNotAutoCompressPictures"
+  , "forceUpgrade"
+  , "captions"
+  , "readModeInkLockDown"
+  , "smartTagType"
+  , "shapeDefaults"
+  , "doNotEmbedSmartTags"
+  , "decimalSymbol"
+  , "listSeparator" ]
+
+mkNumbering :: [ListMarker] -> [Element]
+mkNumbering lists =
+  elts ++ zipWith mkNum lists [baseListId..(baseListId + length lists - 1)]
+    where elts = map mkAbstractNum (nubOrd lists)
+
+mkNum :: ListMarker -> Int -> Element
+mkNum marker numid =
+  mknode "w:num" [("w:numId",tshow numid)]
+   $ mknode "w:abstractNumId" [("w:val",listMarkerToId marker)] ()
+   : case marker of
+       NoMarker     -> []
+       BulletMarker -> []
+       CheckboxMarker _ -> []
+       NumberMarker _ _ start ->
+          map (\lvl -> mknode "w:lvlOverride" [("w:ilvl",tshow (lvl :: Int))]
+              $ mknode "w:startOverride" [("w:val",tshow start)] ())
+                [0..maxListLevel]
+
+mkAbstractNum :: ListMarker -> Element
+mkAbstractNum marker =
+  mknode "w:abstractNum" [("w:abstractNumId",listMarkerToId marker)]
+    $ mknode "w:nsid" [("w:val", T.justifyRight 8 '0' ("A" <> listMarkerToId marker))] ()
+    : mknode "w:multiLevelType" [("w:val","multilevel")] ()
+    : map (mkLvl marker)
+      [0..maxListLevel]
+
+mkLvl :: ListMarker -> Int -> Element
+mkLvl marker lvl =
+  mknode "w:lvl" [("w:ilvl",tshow lvl)] $
+    (case marker of
+        NumberMarker{} -> [mknode "w:start" [("w:val",start)] ()]
+        _ -> []) ++
+    [ mknode "w:numFmt" [("w:val",fmt)] ()
+    , mknode "w:lvlText" [("w:val", lvltxt)] ()
+    , mknode "w:lvlJc" [("w:val","left")] ()
+    , mknode "w:pPr" [] $
+        mknode "w:ind" [ ("w:left",tshow $ lvl * step + step)
+                       , ("w:hanging",tshow hang)
+                       ] ()
+    ] ++
+    maybe [] (\font ->
+                [ mknode "w:rPr" []
+                  [ mknode "w:rFonts" [ ("w:ascii", font)
+                                      , ("w:hAnsi", font)
+                                      , ("w:cs", font)
+                                      , ("w:hint", "default") ] () ]]) mbfont
+    where (fmt, lvltxt, mbfont, start) =
+            case marker of
+                 NoMarker             -> ("bullet"," ", Nothing, "1")
+                 BulletMarker         -> bulletFor lvl
+                 CheckboxMarker False -> ("bullet","\9744", Nothing, "1")
+                 CheckboxMarker True  -> ("bullet","\9746", Nothing, "1")
+                 NumberMarker st de n -> (styleFor st lvl
+                                         ,patternFor de ("%" <> tshow (lvl + 1))
+                                         ,Nothing
+                                         ,tshow n)
+          step = 720
+          hang :: Int
+          hang = 360
+          bulletFor 0 = ("bullet", "\xf0b7", Just "Symbol", "1") -- filled circle
+          bulletFor 1 = ("bullet", "o", Just "Courier New", "1") -- open o
+          bulletFor 2 = ("bullet", "\xf0a7", Just "Wingdings", "1")  -- closed box
+          bulletFor x = bulletFor (x `mod` 3)
+          styleFor UpperAlpha _   = "upperLetter"
+          styleFor LowerAlpha _   = "lowerLetter"
+          styleFor UpperRoman _   = "upperRoman"
+          styleFor LowerRoman _   = "lowerRoman"
+          styleFor Decimal _      = "decimal"
+          styleFor DefaultStyle 0 = "decimal"
+          styleFor DefaultStyle 1 = "lowerLetter"
+          styleFor DefaultStyle 2 = "lowerRoman"
+          styleFor DefaultStyle 3 = "decimal"
+          styleFor DefaultStyle 4 = "lowerLetter"
+          styleFor DefaultStyle 5 = "lowerRoman"
+          styleFor DefaultStyle x = styleFor DefaultStyle (x `mod` 6)
+          styleFor _ _            = "decimal"
+          patternFor OneParen s  = s <> ")"
+          patternFor TwoParens s = "(" <> s <> ")"
+          patternFor _ s         = s <> "."
+
+-- | Build language transformer function for modifying XML elements.
+-- Navigates directly to w:docDefaults/w:rPr/w:lang instead of generic traversal.
+mkLangTransformer :: Maybe Lang -> (Element -> Element)
+mkLangTransformer Nothing  = id
+mkLangTransformer (Just lang) = modifyAtPath path updateLangAttrs
+  where
+    -- Path is: w:docDefaults / w:rPrDefault / w:rPr / w:lang
+    path = [named "docDefaults", named "rPrDefault", named "rPr", named "lang"]
+    named n = (== n) . qName
+
+    updateLangAttrs e
+      | isEastAsianLang lang = e{ elAttribs = map (setattr "eastAsia") $ elAttribs e }
+      | isBidiLang lang      = e{ elAttribs = map (setattr "bidi") $ elAttribs e }
+      | otherwise            = e{ elAttribs = map (setattr "val") $ elAttribs e }
+
+    setattr attrname (XML.Attr qn@(QName s _ _) _)
+      | s == attrname  = XML.Attr qn (renderLang lang)
+    setattr _ x        = x
+
+    isEastAsianLang Lang{ langLanguage = l } = l == "zh" || l == "ja" || l == "ko"
+    isBidiLang Lang{ langLanguage = l } = l == "he" || l == "ar"
+
+-- | Modify an element at a specific path in the XML tree.
+-- The path is a list of predicates that match element names at each level.
+modifyAtPath :: [(QName -> Bool)] -> (Element -> Element) -> Element -> Element
+modifyAtPath [] f e = f e
+modifyAtPath (p:ps) f e = e{ elContent = map go (elContent e) }
+  where
+    go (Elem el) | p (elName el) = Elem (modifyAtPath ps f el)
+    go c = c
+
+-- | Load reference and distribution archives
+loadArchives :: PandocMonad m
+             => WriterOptions
+             -> m (Archive, Archive, Maybe Text, UTCTime)
+loadArchives opts = do
+  username <- P.lookupEnv "USERNAME"
+  utctime <- P.getTimestamp
+  oldUserDataDir <- P.getUserDataDir
+  P.setUserDataDir Nothing
+  res <- readDefaultDataFile "reference.docx"
+  P.setUserDataDir oldUserDataDir
+  let distArchive = toArchive $ BL.fromStrict res
+  refArchive <- case writerReferenceDoc opts of
+                   Just f  -> toArchive . BL.fromStrict . fst
+                                 <$> P.fetchItem (T.pack f)
+                   Nothing -> toArchive . BL.fromStrict <$>
+                        readDataFile "reference.docx"
+  return (refArchive, distArchive, username, utctime)
+
+-- | Extract page dimensions from template
+extractPageLayout :: PandocMonad m
+                  => Archive -> Archive -> m (Maybe Element, Maybe Integer)
+extractPageLayout refArchive distArchive = do
+  parsedDoc <- parseXml refArchive distArchive "word/document.xml"
+  let wname f qn = qPrefix qn == Just "w" && f (qName qn)
+  let mbsectpr = filterElementName (wname (=="sectPr")) parsedDoc
+
+  -- Gets the template size
+  let mbpgsz = mbsectpr >>= filterElementName (wname (=="pgSz"))
+  let mbAttrSzWidth = mbpgsz >>= lookupAttrBy ((=="w") . qName) . elAttribs
+
+  let mbpgmar = mbsectpr >>= filterElementName (wname (=="pgMar"))
+  let mbAttrMarLeft = mbpgmar >>= lookupAttrBy ((=="left") . qName) . elAttribs
+  let mbAttrMarRight = mbpgmar >>= lookupAttrBy ((=="right") . qName) . elAttribs
+
+  -- Get the available area (converting the size and the margins to int and
+  -- doing the difference
+  let pgContentWidth = do
+                         w <- mbAttrSzWidth >>= safeRead
+                         r <- mbAttrMarRight >>= safeRead
+                         l <- mbAttrMarLeft >>= safeRead
+                         pure $ w - r - l
+
+  return (mbsectpr, pgContentWidth)
+
+-- | Parse and augment relationships from reference.docx
+extractRelationships :: PandocMonad m
+                     => Archive -> Archive
+                     -> m ([Element], [Element], [Element], Int)
+extractRelationships refArchive distArchive = do
+  let isImageNode e = findAttr (QName "Type" Nothing Nothing) e == Just "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"
+  let isHeaderNode e = findAttr (QName "Type" Nothing Nothing) e == Just "http://schemas.openxmlformats.org/officeDocument/2006/relationships/header"
+  let isFooterNode e = findAttr (QName "Type" Nothing Nothing) e == Just "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer"
+  parsedRels <- filterElements
+                  (\e -> isImageNode e || isHeaderNode e || isFooterNode e)
+              <$> parseXml refArchive distArchive "word/_rels/document.xml.rels"
+  let getRelId e =
+        case findAttr (QName "Id" Nothing Nothing) e of
+          Just ident -> T.stripPrefix "rId" ident >>= safeRead
+          Nothing -> Nothing
+  let relIds = mapMaybe getRelId parsedRels
+  let maxRelId = if null relIds then 0 else maximum relIds
+
+  let headers = filter isHeaderNode parsedRels
+  let footers = filter isFooterNode parsedRels
+  -- word/_rels/document.xml.rels
+  let addBaseRel (url', target') (maxId, rels) =
+        case [e | e <- rels
+                , findAttr (QName "Target" Nothing Nothing) e ==
+                   Just target'] of
+          [] -> (maxId + 1, mknode "Relationship"
+                            [("Type",url')
+                            ,("Id","rId" <> tshow (maxId + 1))
+                            ,("Target",target')] () : rels)
+          _ -> (maxId, rels)
+
+  let (newMaxRelId, baserels) = foldr addBaseRel (maxRelId, parsedRels)
+                    [("http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering",
+                      "numbering.xml")
+                    ,("http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles",
+                      "styles.xml")
+                    ,("http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings",
+                      "settings.xml")
+                    ,("http://schemas.openxmlformats.org/officeDocument/2006/relationships/webSettings",
+                      "webSettings.xml")
+                    ,("http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable",
+                      "fontTable.xml")
+                    ,("http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme",
+                      "theme/theme1.xml")
+                    ,("http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes",
+                      "footnotes.xml")
+                    ,("http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments",
+                      "comments.xml")
+                    ]
+
+  return (baserels, headers, footers, newMaxRelId)
+
+-- | Create footnotes XML entry
+mkFootnotesEntry :: Integer -> [Element] -> Entry
+mkFootnotesEntry epochtime footnotes =
+  let notes = mknode "w:footnotes" stdAttributes footnotes
+  in toEntry "word/footnotes.xml" epochtime $ renderXml notes
+
+-- | Create footnote relationships entry
+mkFootnoteRelsEntry :: Integer -> M.Map Text Text -> Entry
+mkFootnoteRelsEntry epochtime externalLinks =
+  let linkrels = map toLinkRel $ M.toList externalLinks
+      toLinkRel (src, ident) = mknode "Relationship"
+        [("Type","http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink")
+        ,("Id",ident)
+        ,("Target",src)
+        ,("TargetMode","External")] ()
+  in toEntry "word/_rels/footnotes.xml.rels" epochtime
+       $ renderXml $ mknode "Relationships"
+           [("xmlns","http://schemas.openxmlformats.org/package/2006/relationships")]
+           linkrels
+
+-- | Create comments XML entry
+mkCommentsEntry :: Integer -> [Element] -> Entry
+mkCommentsEntry epochtime comments =
+  toEntry "word/comments.xml" epochtime
+    $ renderXml $ mknode "w:comments" stdAttributes comments
+
+-- | Create package-level relationships entry
+mkPackageRelsEntry :: Integer -> Entry
+mkPackageRelsEntry epochtime =
+  let rels = mknode "Relationships"
+        [("xmlns", "http://schemas.openxmlformats.org/package/2006/relationships")]
+        $ map (\attrs -> mknode "Relationship" attrs ())
+        [ [("Id","rId1")
+          ,("Type","http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument")
+          ,("Target","word/document.xml")]
+        , [("Id","rId4")
+          ,("Type","http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties")
+          ,("Target","docProps/app.xml")]
+        , [("Id","rId3")
+          ,("Type","http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties")
+          ,("Target","docProps/core.xml")]
+        , [("Id","rId5")
+          ,("Type","http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties")
+          ,("Target","docProps/custom.xml")]
+        ]
+  in toEntry "_rels/.rels" epochtime $ renderXml rels
+
+-- | Create content types manifest entry
+mkContentTypesEntry :: Integer
+                    -> [(String, String, Maybe MimeType, B.ByteString)]  -- imgs
+                    -> [Element]  -- headers
+                    -> [Element]  -- footers
+                    -> Archive    -- refArchive
+                    -> Entry
+mkContentTypesEntry epochtime imgs headers footers refArchive =
+  let mkOverrideNode (part', contentType') = mknode "Override"
+           [("PartName", T.pack part')
+           ,("ContentType", contentType')] ()
+      mkImageOverride (_, imgpath, mbMimeType, _) =
+          mkOverrideNode ("/word/" <> imgpath,
+                          fromMaybe "application/octet-stream" mbMimeType)
+      mkMediaOverride imgpath =
+          mkOverrideNode ("/" <> imgpath, getMimeTypeDef imgpath)
+      overrides = map mkOverrideNode (
+                  [("/word/webSettings.xml",
+                    "application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml")
+                  ,("/word/numbering.xml",
+                    "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml")
+                  ,("/word/settings.xml",
+                    "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml")
+                  ,("/word/theme/theme1.xml",
+                    "application/vnd.openxmlformats-officedocument.theme+xml")
+                  ,("/word/fontTable.xml",
+                    "application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml")
+                  ,("/docProps/app.xml",
+                    "application/vnd.openxmlformats-officedocument.extended-properties+xml")
+                  ,("/docProps/core.xml",
+                    "application/vnd.openxmlformats-package.core-properties+xml")
+                  ,("/docProps/custom.xml",
+                    "application/vnd.openxmlformats-officedocument.custom-properties+xml")
+                  ,("/word/styles.xml",
+                    "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml")
+                  ,("/word/document.xml",
+                    "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml")
+                  ,("/word/comments.xml",
+                    "application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml")
+                  ,("/word/footnotes.xml",
+                    "application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml")
+                  ] ++
+                  map (\x -> (maybe "" (T.unpack . ("/word/" <>)) (extractTarget x),
+                       "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml")) headers ++
+                  map (\x -> (maybe "" (T.unpack . ("/word/" <>)) (extractTarget x),
+                       "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml")) footers) ++
+                    map mkImageOverride imgs ++
+                    [ mkMediaOverride (eRelativePath e)
+                        | e <- zEntries refArchive
+                        , "word/media/" `isPrefixOf` eRelativePath e
+                        , not ("/" `isSuffixOf` eRelativePath e) ]
+      mkDefaultNode (ext, mt) =
+        mknode "Default" [("Extension",ext),("ContentType",mt)] ()
+      defaultnodes = map mkDefaultNode
+        [("xml", "application/xml"),
+         ("rels", "application/vnd.openxmlformats-package.relationships+xml"),
+         ("odttf",
+           "application/vnd.openxmlformats-officedocument.obfuscatedFont")]
+      contentTypesDoc = mknode "Types"
+        [("xmlns","http://schemas.openxmlformats.org/package/2006/content-types")]
+        $ defaultnodes ++ overrides
+  in toEntry "[Content_Types].xml" epochtime $ renderXml contentTypesDoc
+
+-- | Create document relationships entry
+mkDocumentRelsEntry :: Integer
+                    -> [Element]  -- baserels
+                    -> [(String, String, Maybe MimeType, B.ByteString)]  -- imgs
+                    -> M.Map Text Text  -- externalLinks
+                    -> Entry
+mkDocumentRelsEntry epochtime baserels imgs externalLinks =
+  let toImgRel (ident, path, _, _) = mknode "Relationship"
+        [("Type","http://schemas.openxmlformats.org/officeDocument/2006/relationships/image")
+        ,("Id",T.pack ident)
+        ,("Target",T.pack path)] ()
+      imgrels = map toImgRel imgs
+      toLinkRel (src, ident) = mknode "Relationship"
+        [("Type","http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink")
+        ,("Id",ident)
+        ,("Target",src)
+        ,("TargetMode","External")] ()
+      linkrels = map toLinkRel $ M.toList externalLinks
+      reldoc = mknode "Relationships"
+        [("xmlns","http://schemas.openxmlformats.org/package/2006/relationships")]
+        $ baserels ++ imgrels ++ linkrels
+  in toEntry "word/_rels/document.xml.rels" epochtime $ renderXml reldoc
+
+-- | Create styles entry with dynamic additions
+mkStylesEntry :: Integer -> Element -> StyleMaps -> WriterState -> WriterOptions -> Entry
+mkStylesEntry epochtime styledoc styleMaps st opts =
+  let stylepath = "word/styles.xml"
+      -- We only want to inject paragraph and text properties that
+      -- are not already in the style map. Note that keys in the stylemap
+      -- are normalized as lowercase.
+      newDynamicParaProps = filter
+        (\sty -> not $ hasStyleName sty $ smParaStyle styleMaps)
+        (Set.toList $ stDynamicParaProps st)
+
+      newDynamicTextProps = filter
+        (\sty -> not $ hasStyleName sty $ smCharStyle styleMaps)
+        (Set.toList $ stDynamicTextProps st)
+
+      newstyles = map newParaPropToOpenXml newDynamicParaProps ++
+                  map newTextPropToOpenXml newDynamicTextProps ++
+                  (case writerHighlightMethod opts of
+                     Skylighting sty -> styleToOpenXml styleMaps sty
+                     DefaultHighlighting -> styleToOpenXml styleMaps
+                                              defaultStyle
+                     _ -> [])
+      styledoc' = styledoc{ elContent = elContent styledoc ++
+                                           map Elem newstyles }
+  in toEntry stylepath epochtime $ renderXml styledoc'
+
+-- | Create core document properties entry
+mkCorePropsEntry :: Integer -> UTCTime -> Meta -> Entry
+mkCorePropsEntry epochtime utctime meta =
+  let keywords = case lookupMeta "keywords" meta of
+                       Just (MetaList xs) -> map stringify xs
+                       _                  -> []
+      docPropsPath = "docProps/core.xml"
+      extraCoreProps = ["subject","lang","category","description"]
+      extraCorePropsMap = M.fromList $ zip extraCoreProps
+                       ["dc:subject","dc:language","cp:category","dc:description"]
+      lookupMetaString' :: Text -> Meta -> Text
+      lookupMetaString' key' meta' =
+        case key' of
+             "description" -> T.intercalate "_x000d_\n"
+                                (map stringify $ lookupMetaBlocks "description" meta')
+             key''         -> lookupMetaString key'' meta'
+
+      docProps = mknode "cp:coreProperties"
+          [("xmlns:cp","http://schemas.openxmlformats.org/package/2006/metadata/core-properties")
+          ,("xmlns:dc","http://purl.org/dc/elements/1.1/")
+          ,("xmlns:dcterms","http://purl.org/dc/terms/")
+          ,("xmlns:dcmitype","http://purl.org/dc/dcmitype/")
+          ,("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance")]
+          $ mktnode "dc:title" [] (stringify $ docTitle meta)
+          : mktnode "dc:creator" [] (T.intercalate "; " (map stringify $ docAuthors meta))
+          : [ mktnode (M.findWithDefault "" k extraCorePropsMap) [] (lookupMetaString' k meta)
+            | k <- M.keys (unMeta meta), k `elem` extraCoreProps]
+          ++ mknode "cp:keywords" [] (T.intercalate ", " keywords)
+          : (\x -> [ mknode "dcterms:created" [("xsi:type","dcterms:W3CDTF")] x
+                   , mknode "dcterms:modified" [("xsi:type","dcterms:W3CDTF")] x
+                   ]) (T.pack $ formatTime defaultTimeLocale "%FT%XZ" utctime)
+  in toEntry docPropsPath epochtime $ renderXml docProps
+
+-- | Create custom document properties entry
+mkCustomPropsEntry :: Integer -> Meta -> Entry
+mkCustomPropsEntry epochtime meta =
+  let extraCoreProps = ["subject","lang","category","description"]
+      customProperties :: [(Text, Text)]
+      customProperties = [ (k, lookupMetaString k meta)
+                         | k <- M.keys (unMeta meta)
+                         , k `notElem` (["title", "author", "keywords"]
+                                       ++ extraCoreProps)]
+      mkCustomProp (k, v) pid = mknode "property"
+         [("fmtid","{D5CDD505-2E9C-101B-9397-08002B2CF9AE}")
+         ,("pid", tshow pid)
+         ,("name", k)] $ mknode "vt:lpwstr" [] v
+      customPropsPath = "docProps/custom.xml"
+      customProps = mknode "Properties"
+          [("xmlns","http://schemas.openxmlformats.org/officeDocument/2006/custom-properties")
+          ,("xmlns:vt","http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes")
+          ] $ zipWith mkCustomProp customProperties [(2 :: Int)..]
+  in toEntry customPropsPath epochtime $ renderXml customProps
+
+-- | Create numbering entry
+mkNumberingEntry :: PandocMonad m
+                 => Archive -> Archive -> Integer -> [ListMarker] -> m Entry
+mkNumberingEntry refArchive distArchive epochtime lists = do
+  let numpath = "word/numbering.xml"
+  numbering <- parseXml refArchive distArchive numpath
+  let newNumElts = mkNumbering lists
+  let pandocAdded e =
+       case findAttrBy ((== "abstractNumId") . qName) e >>= safeRead of
+         Just numid -> numid >= (990 :: Int)
+         Nothing    ->
+           case findAttrBy ((== "numId") . qName) e >>= safeRead of
+             Just numid -> numid >= (1000 :: Int)
+             Nothing    -> False
+  let oldElts = filter (not . pandocAdded) $ onlyElems (elContent numbering)
+  let allElts = oldElts ++ newNumElts
+  return $ toEntry numpath epochtime $ renderXml numbering{ elContent =
+                       -- we want all the abstractNums first, then the nums,
+                       -- otherwise things break:
+                       [Elem e | e <- allElts
+                               , qName (elName e) == "abstractNum" ] ++
+                       [Elem e | e <- allElts
+                               , qName (elName e) == "num" ] }
+
+-- | Collect auxiliary entries from reference archive
+collectReferenceEntries :: PandocMonad m
+                        => Archive -> Archive -> [Element] -> [Element]
+                        -> m [Entry]
+collectReferenceEntries refArchive distArchive headers footers = do
+  let entryFromArchive arch path =
+         maybe (throwError $ PandocSomeError
+                           $ T.pack $ path ++ " missing in reference docx")
+               return
+               (findEntryByPath path arch `mplus` findEntryByPath path distArchive)
+  docPropsAppEntry <- entryFromArchive refArchive "docProps/app.xml"
+  themeEntry <- entryFromArchive refArchive "word/theme/theme1.xml"
+  fontTableEntry <- entryFromArchive refArchive "word/fontTable.xml"
+  let fontTableRelsEntries = maybeToList $
+       findEntryByPath "word/_rels/fontTable.xml.rels" refArchive
+  let fontEntries = [entry | entry <- zEntries refArchive
+                           , "word/fonts/" `isPrefixOf` (eRelativePath entry)]
+  webSettingsEntry <- entryFromArchive refArchive "word/webSettings.xml"
+  headerFooterEntries <- mapM (entryFromArchive refArchive . ("word/" ++)) $
+                         mapMaybe (fmap T.unpack . extractTarget)
+                         (headers ++ footers)
+  let miscRelEntries = [ e | e <- zEntries refArchive
+                       , "word/_rels/" `isPrefixOf` eRelativePath e
+                       , ".xml.rels" `isSuffixOf` eRelativePath e
+                       , eRelativePath e /= "word/_rels/document.xml.rels"
+                       , eRelativePath e /= "word/_rels/footnotes.xml.rels" ]
+  let otherMediaEntries = [ e | e <- zEntries refArchive
+                          , "word/media/" `isPrefixOf` eRelativePath e ]
+  return $ docPropsAppEntry : themeEntry : fontTableEntry : webSettingsEntry
+         : fontTableRelsEntries ++ fontEntries ++ headerFooterEntries
+         ++ miscRelEntries ++ otherMediaEntries
diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs
--- a/src/Text/Pandoc/Writers/HTML.hs
+++ b/src/Text/Pandoc/Writers/HTML.hs
@@ -35,7 +35,8 @@
 import Control.Monad ( liftM, when, foldM, unless )
 import Control.Monad.Trans ( MonadTrans(lift) )
 import Data.Char (ord, isSpace, isAscii)
-import Data.List (intercalate, intersperse, partition, delete, (\\), foldl')
+import Data.List (intercalate, intersperse, partition, delete, (\\))
+import qualified Data.List as L
 import Data.List.NonEmpty (NonEmpty((:|)))
 import Data.Containers.ListUtils (nubOrd)
 import Data.Maybe (fromMaybe, isJust, isNothing)
@@ -125,7 +126,7 @@
 strToHtml :: Text -> Html
 strToHtml t
     | T.any isSpecial t =
-       let !x = foldl' go mempty $ T.groupBy samegroup t
+       let !x = L.foldl' go mempty $ T.groupBy samegroup t
         in x
     | otherwise = toHtml t
   where
@@ -436,7 +437,13 @@
                          defField "scrollProgress" True .
                          defField "scrollActivationWidth" ("0" :: Doc Text) .
                          defField "scrollSnap" ("mandatory" :: Doc Text) .
-                         defField "scrollLayout" ("full" :: Doc Text)
+                         defField "scrollLayout" ("full" :: Doc Text) .
+                         (case writerHighlightMethod opts of
+                            IdiomaticHighlighting
+                             | slideVariant == RevealJsSlides ->
+                              defField "highlight-js" True .
+                              defField "highlightjs-theme" ("monokai" :: Doc Text)
+                            _ -> id)
                       else id) .
                   defField "document-css" (isNothing mCss && slideVariant == NoSlides) .
                   defField "quotes" (stQuotes st) .
@@ -674,7 +681,7 @@
 
 addAttrs :: PandocMonad m
          => WriterOptions -> Attr -> Html -> StateT WriterState m Html
-addAttrs opts attr h = foldl' (!) h <$> attrsToHtml opts attr
+addAttrs opts attr h = L.foldl' (!) h <$> attrsToHtml opts attr
 
 toAttrs :: PandocMonad m
         => [(Text, Text)] -> StateT WriterState m [Attribute]
@@ -794,8 +801,8 @@
         if titleSlide
            -- title slides have no content of their own
            then let (as, bs) = break isSec xs
-                in  (breakOnPauses as, bs)
-           else ([], breakOnPauses xs)
+                in  (walk breakOnPauses as, bs)
+           else ([], walk breakOnPauses xs)
   let secttag  = if html5
                     then H5.section
                     else H.div
@@ -940,6 +947,7 @@
   return $ if html5 then H5.hr else H.hr
 blockToHtmlInner opts (CodeBlock (id',classes,keyvals) rawCode) = do
   html5 <- gets stHtml5
+  slideVariant <- gets stSlideVariant
   id'' <- if T.null id'
              then do
                modify $ \st -> st{ stCodeBlockNum = stCodeBlockNum st + 1 }
@@ -957,23 +965,37 @@
       adjCode  = if tolhs
                     then T.unlines . map ("> " <>) . T.lines $ rawCode
                     else rawCode
-      highlighted = highlight (writerSyntaxMap opts)
-                      (if html5 then formatHtmlBlock else formatHtml4Block)
-                      (id'',classes',keyvals) adjCode
-      hlCode   = case writerHighlightMethod opts of
-                   Skylighting _ -> highlighted
-                   DefaultHighlighting -> highlighted
-                   _ -> Left ""
-  case hlCode of
-         Left msg -> do
-           unless (T.null msg) $
-             report $ CouldNotHighlight msg
-           addAttrs opts (id',classes,keyvals)
-             $ H.pre $ H.code $ toHtml adjCode
-         Right h -> modify (\st -> st{ stHighlighting = True }) >>
-                    -- we set writerIdentifierPrefix to "" since id'' already
-                    -- includes it:
-                    addAttrs opts{writerIdentifierPrefix = ""} (id'',[],keyvals) h
+      isIdiomaticRevealJs = slideVariant == RevealJsSlides &&
+                            writerHighlightMethod opts == IdiomaticHighlighting
+  if isIdiomaticRevealJs
+     then do
+       -- For idiomatic reveal.js highlighting, put attributes on <code>
+       -- with language- prefix, and let highlight.js do the highlighting.
+       modify (\st -> st{ stHighlighting = True })
+       let (langClasses, otherClasses) = case classes' of
+             (lang:rest) -> (["language-" <> lang], rest)
+             []          -> ([], [])
+           codeAttrs = (id', langClasses ++ otherClasses, keyvals)
+       codeTag <- addAttrs opts codeAttrs $ H.code $ toHtml adjCode
+       return $ H.pre codeTag
+     else do
+       let highlighted = highlight (writerSyntaxMap opts)
+                           (if html5 then formatHtmlBlock else formatHtml4Block)
+                           (id'',classes',keyvals) adjCode
+           hlCode   = case writerHighlightMethod opts of
+                        Skylighting _ -> highlighted
+                        DefaultHighlighting -> highlighted
+                        _ -> Left ""
+       case hlCode of
+              Left msg -> do
+                unless (T.null msg) $
+                  report $ CouldNotHighlight msg
+                addAttrs opts (id',classes,keyvals)
+                  $ H.pre $ H.code $ toHtml adjCode
+              Right h -> modify (\st -> st{ stHighlighting = True }) >>
+                         -- we set writerIdentifierPrefix to "" since id'' already
+                         -- includes it:
+                         addAttrs opts{writerIdentifierPrefix = ""} (id'',[],keyvals) h
 blockToHtmlInner opts (BlockQuote blocks) = do
   -- in S5, treat list in blockquote specially
   -- if default is incremental, make it nonincremental;
@@ -1046,7 +1068,7 @@
                                    numstyle']
                    else [])
   l <- ordList opts contents
-  return $ foldl' (!) l attribs
+  return $ L.foldl' (!) l attribs
 blockToHtmlInner opts (DefinitionList lst) = do
   contents <- mapM (\(term, defs) ->
                   do term' <- liftM H.dt $ inlineListToHtml opts term
@@ -1413,7 +1435,7 @@
                                = Just (t . H.u, cs)
                              | otherwise
                                = Just (t, c:cs)
-                            spanLikeTags = foldl' go Nothing
+                            spanLikeTags = L.foldl' go Nothing
                         in case spanLikeTags classes of
                             Just (tag, cs) -> do
                               h <- inlineListToHtml opts ils
@@ -1610,7 +1632,7 @@
                               Just "audio" -> mediaTag H5.audio "Audio"
                               Just _       -> (H5.embed, [])
                               _            -> imageTag
-                        return $ foldl' (!) tag $ attributes ++ specAttrs
+                        return $ L.foldl' (!) tag $ attributes ++ specAttrs
                         -- note:  null title included, as in Markdown.pl
     (Note contents) -> do
                         notes <- gets stNotes
@@ -1688,14 +1710,16 @@
       let kvs = [("role","doc-backlink") | html5]
       let backlink = Link ("",["footnote-back"],kvs)
                         [Str ref] ("#" <> "fnref" <> ref,"")
-      let blocks' =
-           case blocks of
-             (Para ils : rest) ->
-                Para (backlink : Str "." : Space : ils) : rest
-             (Plain ils : rest) ->
-                Plain (backlink : Str "." : Space : ils) : rest
-             _ -> Para [backlink , Str "."] : blocks
-      contents <- blockListToHtml opts blocks'
+      let addBacklinkInlines bs
+             | epubv == EPUB3 = bs
+             | otherwise =
+                 case bs of
+                   (Para ils : rest) ->
+                     Para (backlink : Str "." : Space : ils) : rest
+                   (Plain ils : rest) ->
+                     Plain (backlink : Str "." : Space : ils) : rest
+                   _ -> Para [backlink , Str "."] : blocks
+      contents <- blockListToHtml opts (addBacklinkInlines blocks)
       let noteItem = (if epubv == EPUB3
                          then H5.aside ! customAttribute "epub:type" "footnote" ! customAttribute "role" "doc-footnote"
                          else H.div) ! prefixedId opts ("fn" <> ref)
diff --git a/src/Text/Pandoc/Writers/JATS.hs b/src/Text/Pandoc/Writers/JATS.hs
--- a/src/Text/Pandoc/Writers/JATS.hs
+++ b/src/Text/Pandoc/Writers/JATS.hs
@@ -439,12 +439,36 @@
   capt <- if null longcapt
           then pure empty
           else inTagsSimple "caption" <$> blocksToJATS opts longcapt
-  figbod <- blocksToJATS opts $ walk unsetAltIfDupl body
-  let figattr = [("id", escapeNCName ident) | not (T.null ident)] ++
-                [(k,v) | (k,v) <- kvs
-                       , k `elem` [ "fig-type", "orientation"
-                                  , "position", "specific-use"]]
-  return $ inTags True "fig" figattr $ capt $$ figbod
+
+  -- We handle the element specially if it's a figure with subfigures, i.e., if
+  -- all immediate children are figures themselves.
+  let hasSubfigures = all (\case { Figure{} -> True; _ -> False}) body
+      needsWrapping = if hasSubfigures
+                      then (const False)
+                      else \case
+                        -- Wrap all figure content elements, except for those
+                        -- allowed as direct subelements.
+                        BlockQuote{}    -> False
+                        CodeBlock{}     -> False
+                        Para{}          -> False
+                        Plain [Image{}] -> False
+                        Plain [Math{}]  -> False
+                        Table{}         -> False
+                        _               -> True
+
+  children <- wrappedBlocksToJATS needsWrapping opts $ walk unsetAltIfDupl body
+  let (tag, allowedAttributes) =
+        if hasSubfigures
+        then ( "fig-group"
+             , ["content-type", "orientation", "position", "specific-use"]
+             )
+        else ("fig"
+             , ["fig-type", "orientation", "position", "specific-use"]
+             )
+  let xmlattr =  [("id", escapeNCName ident) | not (T.null ident)] ++
+                 [(k,v) | (k,v) <- kvs
+                        , k `elem` allowedAttributes]
+  return $ inTags True tag xmlattr $ capt $$ children
 
 -- | Convert a list of inline elements to JATS.
 inlinesToJATS :: PandocMonad m => WriterOptions -> [Inline] -> JATS m (Doc Text)
diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs
--- a/src/Text/Pandoc/Writers/LaTeX.hs
+++ b/src/Text/Pandoc/Writers/LaTeX.hs
@@ -33,13 +33,15 @@
 import Crypto.Hash (hashWith, MD5(MD5))
 import Data.Containers.ListUtils (nubOrd)
 import Data.Char (isDigit, isAscii, isLetter)
-import Data.List (intersperse, (\\))
-import Data.Maybe (catMaybes, fromMaybe, isJust, mapMaybe, isNothing)
+import Data.List (intersperse, partition, (\\))
+import qualified Data.Set as Set
+import Data.Maybe (catMaybes, fromMaybe, isJust, listToMaybe, mapMaybe, isNothing)
 import Data.Monoid (Any (..))
 import Data.Text (Text)
 import qualified Data.Text as T
 import Network.URI (unEscapeString)
-import Text.DocTemplates (FromContext(lookupContext), Val(..), renderTemplate)
+import Text.DocTemplates (Context(..), FromContext(lookupContext), Val(..), renderTemplate)
+import qualified Data.Map as M
 import Text.Collate.Lang (renderLang)
 import Text.Pandoc.Class.PandocMonad (PandocMonad, getPOSIXTime, lookupEnv,
                                       report, toLang)
@@ -58,7 +60,8 @@
 import Text.Pandoc.Writers.LaTeX.Table (tableToLaTeX)
 import Text.Pandoc.Writers.LaTeX.Citation (citationsToNatbib,
                                            citationsToBiblatex)
-import Text.Pandoc.Writers.LaTeX.Types (LW, WriterState (..), startingState)
+import Text.Pandoc.Writers.LaTeX.Types (LW, WriterState (..), startingState,
+                                        PdfStandard (..))
 import Text.Pandoc.Writers.LaTeX.Lang (toBabel)
 import Text.Pandoc.Writers.LaTeX.Util (stringToLaTeX, StringContext(..),
                                        toLabel, inCmd,
@@ -221,6 +224,9 @@
   let bibliography' = map unescapeUnderscore <$>
                         getField "bibliography" metadata
 
+  -- Process PDF standard metadata for DocumentMetadata
+  pdfStd <- processPdfStandard metadata
+
   let context  =  (case bibliography' of
                      Nothing -> id
                      Just xs -> resetField "bibliography" xs) $
@@ -293,6 +299,13 @@
                   (if reproduciblePDF
                     then defField "pdf-trailer-id" trailerID
                     else id) $
+                  (if not (null (pdfStandards pdfStd)) || isJust (pdfVersion pdfStd)
+                    then resetField "pdfstandard" $ MapVal $ Context $ M.fromList
+                           [ ("standards", ListVal $ map (SimpleVal . literal) (pdfStandards pdfStd))
+                           , ("version", maybe NullVal (SimpleVal . literal) (pdfVersion pdfStd))
+                           , ("tagging", BoolVal (pdfTagging pdfStd))
+                           ]
+                    else id) $
                   metadata
   let babelLang = mblang >>= toBabel
   let context' =
@@ -1414,3 +1427,85 @@
            then return True
            else pCancel
       _ -> pCancel
+
+-- PDF standard support for DocumentMetadata
+
+-- | PDF standards supported by LaTeX's DocumentMetadata
+-- See: https://github.com/latex3/latex2e documentmetadata-support.dtx
+latexSupportedStandards :: Set.Set Text
+latexSupportedStandards = Set.fromList
+  [ -- PDF/A standards (note: a-1a is NOT supported by LaTeX, only a-1b)
+    "a-1b", "a-2a", "a-2b", "a-2u", "a-3a", "a-3b", "a-3u"
+  , "a-4", "a-4e", "a-4f"
+    -- PDF/X standards
+  , "x-4", "x-4p", "x-5g", "x-5n", "x-5pg", "x-6", "x-6n", "x-6p"
+    -- PDF/UA standards
+  , "ua-1", "ua-2"
+  ]
+
+-- | Standards that require PDF tagging (document structure)
+-- PDF/A level "a" variants and PDF/UA require tagged structure
+taggingRequiredStandards :: Set.Set Text
+taggingRequiredStandards = Set.fromList
+  ["a-2a", "a-3a", "ua-1", "ua-2"]
+
+-- | Valid PDF versions for DocumentMetadata
+validPdfVersions :: Set.Set Text
+validPdfVersions = Set.fromList
+  ["1.4", "1.5", "1.6", "1.7", "2.0"]
+
+-- | PDF version required by each standard
+-- LaTeX defaults to PDF 2.0 with \DocumentMetadata, but some standards
+-- have maximum version requirements that are incompatible with 2.0
+standardRequiredVersion :: M.Map Text Text
+standardRequiredVersion = M.fromList
+  [ ("a-1b", "1.4")  -- PDF/A-1 requires exactly PDF 1.4
+    -- PDF/A-2 and PDF/A-3 require 1.7; must set explicitly since LaTeX defaults to 2.0
+  , ("a-2a", "1.7"), ("a-2b", "1.7"), ("a-2u", "1.7")
+  , ("a-3a", "1.7"), ("a-3b", "1.7"), ("a-3u", "1.7")
+    -- PDF/A-4, PDF/UA-1, PDF/UA-2 work with PDF 2.0 (the default)
+  ]
+
+-- | Normalize a PDF standard string: lowercase, strip "pdf" prefix
+normalizePdfStandard :: Text -> Text
+normalizePdfStandard t =
+  let lower = T.toLower t
+  in case T.stripPrefix "pdf" lower of
+       Just rest -> T.dropWhile (`elem` ['-', '/']) rest
+       Nothing   -> lower
+
+-- | Normalize a PDF version string
+-- Handles YAML parsing quirk where 2.0 becomes integer 2, then string "2"
+normalizeVersion :: Text -> Text
+normalizeVersion t
+  | t == "2" = "2.0"
+  | otherwise = t
+
+-- | Check if text is a valid PDF version (after normalization)
+isPdfVersion :: Text -> Bool
+isPdfVersion t = Set.member (normalizeVersion t) validPdfVersions
+
+-- | Process pdfstandard metadata, returning PDF standard settings
+processPdfStandard :: PandocMonad m
+                   => Context Text
+                   -> m PdfStandard
+processPdfStandard ctx = do
+  let standards = fromMaybe [] $ getField "pdfstandard" ctx
+      normalized = map normalizePdfStandard standards
+      (versions, pdfStds) = partition isPdfVersion normalized
+      validStandards = filter (`Set.member` latexSupportedStandards) pdfStds
+      invalidStandards = filter (\s -> not (Set.member s latexSupportedStandards)
+                                    && not (isPdfVersion s)) pdfStds
+      needsTagging = any (`Set.member` taggingRequiredStandards) validStandards
+      -- Use explicit version if provided, otherwise infer from standards
+      -- Apply normalizeVersion to handle YAML parsing "2" -> "2.0"
+      explicitVersion = normalizeVersion <$> listToMaybe versions
+      inferredVersion = listToMaybe $ mapMaybe (`M.lookup` standardRequiredVersion) validStandards
+      version = explicitVersion <|> inferredVersion
+  -- Warn about unsupported standards
+  mapM_ (report . UnsupportedPdfStandard) invalidStandards
+  return PdfStandard
+    { pdfStandards = validStandards
+    , pdfVersion = version
+    , pdfTagging = needsTagging
+    }
diff --git a/src/Text/Pandoc/Writers/LaTeX/Citation.hs b/src/Text/Pandoc/Writers/LaTeX/Citation.hs
--- a/src/Text/Pandoc/Writers/LaTeX/Citation.hs
+++ b/src/Text/Pandoc/Writers/LaTeX/Citation.hs
@@ -21,7 +21,7 @@
 import Text.Pandoc.Options
 import Text.Pandoc.Class.PandocMonad (PandocMonad)
 import Text.Pandoc.Definition
-import Data.List (foldl')
+import qualified Data.List as L
 import Text.DocLayout (Doc, brackets, empty, (<+>), text, isEmpty, literal,
                        braces)
 import Text.Pandoc.Walk
@@ -73,7 +73,7 @@
 
 citationsToNatbib inlineListToLaTeX cits = do
   cits' <- mapM convertOne cits
-  return $ text "\\citetext{" <> foldl' combineTwo empty cits' <> text "}"
+  return $ text "\\citetext{" <> L.foldl' combineTwo empty cits' <> text "}"
   where
     citeCommand' = citeCommand inlineListToLaTeX
     combineTwo a b | isEmpty a = b
@@ -182,7 +182,7 @@
                     NormalCitation -> "\\autocites"
 
       groups <- mapM (citeArgumentsList inlineListToLaTeX)
-                     (reverse (foldl' grouper [] (c:cs)))
+                     (reverse (L.foldl' grouper [] (c:cs)))
 
       return $ text cmd <> mconcat groups
 
diff --git a/src/Text/Pandoc/Writers/LaTeX/Types.hs b/src/Text/Pandoc/Writers/LaTeX/Types.hs
--- a/src/Text/Pandoc/Writers/LaTeX/Types.hs
+++ b/src/Text/Pandoc/Writers/LaTeX/Types.hs
@@ -2,6 +2,7 @@
   ( LW
   , WriterState (..)
   , startingState
+  , PdfStandard (..)
   ) where
 
 import Control.Monad.State.Strict (StateT)
@@ -55,6 +56,13 @@
   , stInSoulCommand :: Bool          -- ^ in a soul command like ul
   , stCancel        :: Bool          -- ^ true if document uses \cancel
   , stInCaption     :: Bool          -- ^ true if in a caption
+  }
+
+-- | PDF standard settings for DocumentMetadata
+data PdfStandard = PdfStandard
+  { pdfStandards :: [Text]     -- ^ list of standards (e.g., ["a-2b", "ua-1"])
+  , pdfVersion   :: Maybe Text -- ^ PDF version (e.g., "1.7", "2.0")
+  , pdfTagging   :: Bool       -- ^ whether tagging is required
   }
 
 startingState :: WriterOptions -> WriterState
diff --git a/src/Text/Pandoc/Writers/Markdown.hs b/src/Text/Pandoc/Writers/Markdown.hs
--- a/src/Text/Pandoc/Writers/Markdown.hs
+++ b/src/Text/Pandoc/Writers/Markdown.hs
@@ -529,15 +529,19 @@
                        isEnabled Ext_attributes opts ->
                                     space <> attrsToMarkdown opts attr
                      | otherwise -> empty
+  let setext = level <= 2 && writerSetextHeaders opts ||
+                              (variant == Commonmark &&
+                               hasLineBreaks inlines) -- #11341
   contents <- inlineListToMarkdown opts $
-                 -- ensure no newlines; see #3736
-                 walk lineBreakToSpace $
+                 (if variant == Commonmark && setext
+                     then id
+                     else -- ensure no newlines; see #3736
+                          walk lineBreakToSpace) $
                    if level == 1 && variant == PlainText &&
                       isEnabled Ext_gutenberg opts
                       then capitalize inlines
                       else inlines
 
-  let setext = writerSetextHeaders opts
   when (not setext && isEnabled Ext_literate_haskell opts) $
     report $ ATXHeadingInLHS level (render Nothing contents)
 
@@ -611,6 +615,9 @@
   let (caption, aligns, widths, headers, rows) = toLegacyTable blkCapt specs thead tbody tfoot
   let isColRowSpans (Cell _ _ rs cs _) = rs > 1 || cs > 1
   let rowHasColRowSpans (Row _ cs) = any isColRowSpans cs
+  let hasFooter = case tfoot of
+                    TableFoot _ [] -> False
+                    _ -> True
   let tbodyHasColRowSpans (TableBody _ _ rhs rs) =
         any rowHasColRowSpans rhs || any rowHasColRowSpans rs
   let theadHasColRowSpans (TableHead _ rs) = any rowHasColRowSpans rs
@@ -630,7 +637,8 @@
         = blankline $$ (": " <> caption'') $$ blankline
         | otherwise = blankline $$ caption'' $$ blankline
   let hasSimpleCells = onlySimpleTableCells $ headers : rows
-  let isSimple = hasSimpleCells && all (==0) widths && not hasColRowSpans
+  let isSimple = hasSimpleCells && not hasFooter &&
+                 all (==0) widths && not hasColRowSpans
   let isPlainBlock (Plain _) = True
       isPlainBlock _         = False
   let hasBlocks = not (all (all (all isPlainBlock)) $ headers:rows)
@@ -656,12 +664,13 @@
          isEnabled Ext_pipe_tables opts -> do
            tbl <- mkTable (pipeTable opts)
            return $ (tbl $$ caption''') $$ blankline
-       | not (hasBlocks || hasColRowSpans) &&
+       | not (hasBlocks || hasColRowSpans || hasFooter) &&
          isEnabled Ext_multiline_tables opts -> do
            tbl <- mkTable (pandocTable opts True)
            return $ nest 2 (tbl $$ caption''') $$ blankline
        | isEnabled Ext_grid_tables opts &&
-          (hasColRowSpans || writerColumns opts >= 8 * numcols) -> do
+          (hasColRowSpans || writerColumns opts >= 8 * numcols
+                          || hasFooter) -> do
            tbl <- gridTable opts blockListToMarkdown
                      specs thead tbody tfoot
            return $ (tbl $$ caption''') $$ blankline
diff --git a/src/Text/Pandoc/Writers/Markdown/Inline.hs b/src/Text/Pandoc/Writers/Markdown/Inline.hs
--- a/src/Text/Pandoc/Writers/Markdown/Inline.hs
+++ b/src/Text/Pandoc/Writers/Markdown/Inline.hs
@@ -500,24 +500,24 @@
                         else escapeText opts) $ str
   return $ literal str'
 inlineToMarkdown opts (Math InlineMath str) = do
+  let str' = T.strip str
   variant <- asks envVariant
   case () of
     _ | variant == Markua -> return $ "`" <> literal str <> "`" <> "$"
       | otherwise -> case writerHTMLMathMethod opts of
           WebTeX url ->
-            let str' = T.strip str
-             in inlineToMarkdown opts
+             inlineToMarkdown opts
                   (Image nullAttr [Str str'] (url <> urlEncode str', str'))
           _ | isEnabled Ext_tex_math_gfm opts ->
-                return $ "$`" <> literal str <> "`$"
+                return $ "$`" <> literal str' <> "`$"
             | isEnabled Ext_tex_math_dollars opts ->
-                return $ delimited "$" "$" (literal str)
+                return $ "$" <> literal str' <> "$"
             | isEnabled Ext_tex_math_single_backslash opts ->
-                return $ "\\(" <> literal str <> "\\)"
+                return $ "\\(" <> literal str' <> "\\)"
             | isEnabled Ext_tex_math_double_backslash opts ->
-                return $ "\\\\(" <> literal str <> "\\\\)"
+                return $ "\\\\(" <> literal str' <> "\\\\)"
             | otherwise ->
-                texMathToInlines InlineMath str >>=
+                texMathToInlines InlineMath str' >>=
                   inlineListToMarkdown opts .
                     (if variant == PlainText then makeMathPlainer else id)
 
@@ -540,7 +540,7 @@
                              $$ literal (T.dropAround (=='\n') str)
                              $$ literal "```") <> cr
             | isEnabled Ext_tex_math_dollars opts ->
-                return $ delimited "$$" "$$" (literal str)
+                return $ "$$" <> literal str <> "$$"
             | isEnabled Ext_tex_math_single_backslash opts ->
                 return $ "\\[" <> literal str <> "\\]"
             | isEnabled Ext_tex_math_double_backslash opts ->
@@ -583,7 +583,7 @@
   if variant == PlainText || isEnabled Ext_hard_line_breaks opts
      then return cr
      else return $
-          if isEnabled Ext_escaped_line_breaks opts
+          if variant == Commonmark || isEnabled Ext_escaped_line_breaks opts
              then "\\" <> cr
              else "  " <> cr
 inlineToMarkdown _ Space = do
diff --git a/src/Text/Pandoc/Writers/MediaWiki.hs b/src/Text/Pandoc/Writers/MediaWiki.hs
--- a/src/Text/Pandoc/Writers/MediaWiki.hs
+++ b/src/Text/Pandoc/Writers/MediaWiki.hs
@@ -26,7 +26,7 @@
 import Text.Pandoc.ImageSize
 import Text.Pandoc.Logging
 import Text.Pandoc.Options
-import Text.DocLayout (render, literal)
+import Text.DocLayout
 import Text.Pandoc.Shared
 import Text.Pandoc.URI
 import Text.Pandoc.Templates (renderTemplate)
@@ -61,21 +61,21 @@
 pandocToMediaWiki (Pandoc meta blocks) = do
   opts <- asks options
   metadata <- metaToContext opts
-              (fmap (literal . trimr) . blockListToMediaWiki)
-              (fmap (literal . trimr) . inlineListToMediaWiki)
+              (fmap chomp . blockListToMediaWiki)
+              (fmap chomp . inlineListToMediaWiki)
               meta
   body <- blockListToMediaWiki blocks
   notesExist <- gets stNotes
   let notes = if notesExist
-                 then "\n<references />"
-                 else ""
+                 then cr <> literal "<references />"
+                 else mempty
   let main = body <> notes
   let context = defField "body" main
                 $ defField "toc" (writerTableOfContents opts) metadata
-  return $
+  return $ render Nothing $
     case writerTemplate opts of
          Nothing  -> main
-         Just tpl -> render Nothing $ renderTemplate tpl context
+         Just tpl -> renderTemplate tpl context
 
 -- | Escape special characters for MediaWiki.
 escapeText :: Text -> Text
@@ -84,12 +84,14 @@
 -- | Convert Pandoc block element to MediaWiki.
 blockToMediaWiki :: PandocMonad m
                  => Block         -- ^ Block element
-                 -> MediaWikiWriter m Text
+                 -> MediaWikiWriter m (Doc Text)
 
 blockToMediaWiki (Div attrs bs) = do
   contents <- blockListToMediaWiki bs
-  return $ render Nothing (tagWithAttrs "div" attrs) <> "\n\n" <>
-                     contents <> "\n\n" <> "</div>"
+  return $ tagWithAttrs "div" attrs $$
+            contents $$
+            literal "</div>" $$
+            blankline
 
 blockToMediaWiki (Plain inlines) =
   inlineListToMediaWiki inlines
@@ -97,60 +99,63 @@
 blockToMediaWiki (SimpleFigure attr txt (src, tit)) = do
   capt <- inlineListToMediaWiki txt
   img  <- imageToMediaWiki attr
+  let capt' = render Nothing capt
   let opt = if T.null tit
                then
-                 if T.null capt
+                 if T.null capt'
                     then ""
-                    else "alt=" <> capt
+                    else "alt=" <> capt'
                else "alt=" <> tit
-  return $ "[[" <>
+  return $ literal ("[[" <>
             T.intercalate "|"
             (filter (not . T.null) ["File:" <> src
                                  , "thumb"
                                  , "none"
                                  , img
                                  , opt
-                                 , capt
+                                 , capt'
                                  ]) <>
-            "]]\n"
+            "]]") <> cr
 
 blockToMediaWiki (Para inlines) = do
   tags <- asks useTags
   lev <- asks listLevel
   contents <- inlineListToMediaWiki inlines
-  let initEsc = if startsWithListMarker contents -- #9700
-                   then "\\"
-                   else ""
+  let contents' = render Nothing contents
+  let initEsc = if startsWithListMarker contents'
+                   then literal "\\"
+                   else mempty
   return $ if tags
-              then  "<p>" <> contents <> "</p>"
-              else initEsc <> contents <> if null lev then "\n" else ""
+              then  literal "<p>" <> contents <> literal "</p>"
+              else initEsc <> contents $$
+                   if null lev then blankline else mempty
 
 blockToMediaWiki (LineBlock lns) =
   blockToMediaWiki $ linesToPara lns
 
 blockToMediaWiki b@(RawBlock f str)
-  | f == Format "mediawiki" = return str
-  | f == Format "html"      = return str
-  | otherwise               = "" <$ report (BlockNotRendered b)
+  | f == Format "mediawiki" = return $ literal str
+  | f == Format "html"      = return $ literal str
+  | otherwise               = mempty <$ report (BlockNotRendered b)
 
-blockToMediaWiki HorizontalRule = return "\n-----\n"
+blockToMediaWiki HorizontalRule = return $ blankline <> literal "-----" <> blankline
 
 blockToMediaWiki (Header level (ident,_,_) inlines) = do
   let autoId = T.replace " " "_" $ stringify inlines
   contents <- inlineListToMediaWiki inlines
-  let eqs = T.replicate level "="
+  let eqs = literal $ T.replicate level "="
   return $
     (if T.null ident || autoId == ident
-        then ""
-        else "<span id=\"" <> ident <> "\"></span>\n")
-    <> eqs <> " " <> contents <> " " <> eqs <> "\n"
+        then mempty
+        else literal ("<span id=\"" <> ident <> "\"></span>") <> cr)
+    <> eqs <> space <> contents <> space <> eqs <> blankline
 
 blockToMediaWiki (CodeBlock (_,classes,keyvals) str) = do
   let at  = Set.fromList classes `Set.intersection` highlightingLangs
   let numberLines = any (`elem` ["number","numberLines", "number-lines"])
                     classes
   let start = lookup "startFrom" keyvals
-  return $
+  return $ literal $
     case Set.toList at of
        [] -> "<pre" <> (if null classes
                            then ">"
@@ -165,7 +170,7 @@
 
 blockToMediaWiki (BlockQuote blocks) = do
   contents <- blockListToMediaWiki blocks
-  return $ "<blockquote>" <> contents <> "</blockquote>"
+  return $ literal "<blockquote>" <> chomp contents <> cr <> literal "</blockquote>"
 
 blockToMediaWiki (Table attr capt colSpecs thead tbody tfoot) = do
     tableToMediaWiki (Ann.toTable attr capt colSpecs thead tbody tfoot)
@@ -176,11 +181,11 @@
   if tags
      then do
         contents <- local (\ s -> s { useTags = True }) $ mapM listItemToMediaWiki items
-        return $ "<ul>\n" <> vcat contents <> "</ul>\n"
+        return $ literal "<ul>" <> cr <> vcat contents <> literal "</ul>" <> blankline
      else do
         lev <- asks listLevel
         contents <- local (\s -> s { listLevel = listLevel s <> "*" }) $ mapM listItemToMediaWiki items
-        return $ vcat contents <> if null lev then "\n" else ""
+        return $ vcat contents <> if null lev then blankline else mempty
 
 blockToMediaWiki x@(OrderedList attribs items) = do
   tags <-
@@ -188,11 +193,11 @@
   if tags
      then do
         contents <- local (\s -> s { useTags = True }) $ mapM listItemToMediaWiki items
-        return $ "<ol" <> listAttribsToText attribs <> ">\n" <> vcat contents <> "</ol>\n"
+        return $ literal ("<ol" <> listAttribsToText attribs <> ">") <> cr <> vcat contents <> literal "</ol>" <> blankline
      else do
         lev <- asks listLevel
         contents <- local (\s -> s { listLevel = listLevel s <> "#" }) $ mapM listItemToMediaWiki items
-        return $ vcat contents <> if null lev then "\n" else ""
+        return $ vcat contents <> if null lev then blankline else mempty
 
 blockToMediaWiki x@(DefinitionList items) = do
   tags <-
@@ -200,11 +205,11 @@
   if tags
      then do
         contents <- local (\s -> s { useTags = True }) $ mapM definitionListItemToMediaWiki items
-        return $ "<dl>\n" <> vcat contents <> "</dl>\n"
+        return $ literal "<dl>" <> cr <> vcat contents <> literal "</dl>" <> blankline
      else do
         lev <- asks listLevel
         contents <- local (\s -> s { listLevel = listLevel s <> ";" }) $ mapM definitionListItemToMediaWiki items
-        return $ vcat contents <> if null lev then "\n" else ""
+        return $ vcat contents <> if null lev then blankline else mempty
 
 blockToMediaWiki (Figure (ident, classes, kvs) _ body) =
   blockToMediaWiki (Div (ident, ["figure"] `DL.union` classes, kvs) body)
@@ -223,20 +228,20 @@
           else "")
 
 -- | Convert bullet or ordered list item (list of blocks) to MediaWiki.
-listItemToMediaWiki :: PandocMonad m => [Block] -> MediaWikiWriter m Text
+listItemToMediaWiki :: PandocMonad m => [Block] -> MediaWikiWriter m (Doc Text)
 listItemToMediaWiki items = do
   contents <- blockListToMediaWiki items
   tags <- asks useTags
   if tags
-     then return $ "<li>" <> contents <> "</li>"
+     then return $ literal "<li>" <> chomp contents <> literal "</li>"
      else do
        marker <- asks listLevel
-       return $ T.pack marker <> " " <> contents
+       return $ literal (T.pack marker) <> space <> chomp contents
 
 -- | Convert definition list item (label, list of blocks) to MediaWiki.
 definitionListItemToMediaWiki :: PandocMonad m
                               => ([Inline],[[Block]])
-                              -> MediaWikiWriter m Text
+                              -> MediaWikiWriter m (Doc Text)
 definitionListItemToMediaWiki (label, items) = do
   modify $ \st -> st{ stInDefLabel = True }
   labelText <- inlineListToMediaWiki label
@@ -244,12 +249,12 @@
   contents <- mapM blockListToMediaWiki items
   tags <- asks useTags
   if tags
-     then return $ "<dt>" <> labelText <> "</dt>\n" <>
-           T.intercalate "\n" (map (\d -> "<dd>" <> d <> "</dd>") contents)
+     then return $ literal "<dt>" <> chomp labelText <> literal "</dt>" <> cr <>
+           vcat (map (\d -> literal "<dd>" <> chomp d <> literal "</dd>") contents)
      else do
        marker <- asks listLevel
-       return $ T.pack marker <> " " <> labelText <> "\n" <>
-           T.intercalate "\n" (map (\d -> T.pack (init marker) <> ": " <> d) contents)
+       return $ literal (T.pack marker) <> space <> chomp labelText <> cr <>
+           vcat (map (\d -> literal (T.pack (init marker)) <> literal ": " <> chomp d) contents)
 
 -- | True if the list can be handled by simple wiki markup, False if HTML tags will be needed.
 isSimpleList :: Block -> Bool
@@ -286,59 +291,53 @@
 isPlainOrPara (Para  _) = True
 isPlainOrPara _         = False
 
--- | Concatenates strings with line breaks between them.
-vcat :: [Text] -> Text
-vcat = T.intercalate "\n"
-
 -- Auxiliary functions for tables:
 
-tableToMediaWiki :: PandocMonad m => Ann.Table -> MediaWikiWriter m Text
+tableToMediaWiki :: PandocMonad m => Ann.Table -> MediaWikiWriter m (Doc Text)
 tableToMediaWiki (Ann.Table attr capt _ thead tbodies tfoot) = do
     let (ident,classes,kvs) = attr
     caption <- case capt of
         Caption _ [] -> return mempty
         Caption _ longCapt -> do
             c <- blockListToMediaWiki longCapt
-            return [ "|+ " <> trimr c ]
+            return $ literal "|+ " <> chomp c <> cr
     head' <- tableHeadToMW thead
-    bodies' <- concat <$> mapM tableBodyToMW tbodies
+    bodies' <- mconcat <$> mapM tableBodyToMW tbodies
     foot' <- tableFootToMW tfoot
-    return $ T.unlines $ [
-            "{|" <> (render Nothing (htmlAttrs (ident, "wikitable":classes, kvs)))
-        ] <> caption <> head' <> bodies' <> foot' <> [
-            "|}"
-        ]
+    return $ literal "{|" <> htmlAttrs (ident, "wikitable":classes, kvs) <> cr
+             <> caption <> head' <> bodies' <> foot'
+             <> literal "|}" <> blankline
 
-tableHeadToMW :: PandocMonad m => Ann.TableHead -> MediaWikiWriter m [Text]
+tableHeadToMW :: PandocMonad m => Ann.TableHead -> MediaWikiWriter m (Doc Text)
 tableHeadToMW (Ann.TableHead _ rows) = headerRowsToMW rows
 
-tableFootToMW :: PandocMonad m => Ann.TableFoot -> MediaWikiWriter m [Text]
+tableFootToMW :: PandocMonad m => Ann.TableFoot -> MediaWikiWriter m (Doc Text)
 tableFootToMW (Ann.TableFoot _ rows) = headerRowsToMW rows
 
-tableBodyToMW :: PandocMonad m => Ann.TableBody -> MediaWikiWriter m [Text]
+tableBodyToMW :: PandocMonad m => Ann.TableBody -> MediaWikiWriter m (Doc Text)
 tableBodyToMW (Ann.TableBody _ _ headerRows bodyRows) = do
   headerRows' <- headerRowsToMW headerRows
   bodyRows' <- bodyRowsToMW bodyRows
   return $ headerRows' <> bodyRows'
 
-headerRowsToMW :: PandocMonad m => [Ann.HeaderRow] -> MediaWikiWriter m [Text]
-headerRowsToMW rows = (\x -> mconcat x) <$> mapM headerRowToMW rows
+headerRowsToMW :: PandocMonad m => [Ann.HeaderRow] -> MediaWikiWriter m (Doc Text)
+headerRowsToMW rows = mconcat <$> mapM headerRowToMW rows
 
-headerRowToMW :: PandocMonad m => Ann.HeaderRow -> MediaWikiWriter m [Text]
+headerRowToMW :: PandocMonad m => Ann.HeaderRow -> MediaWikiWriter m (Doc Text)
 headerRowToMW (Ann.HeaderRow attr _ cells) = do
-  cells' <- (\x -> mconcat x) <$> mapM (cellToMW "!") cells
-  return $ ["|-" <> (render Nothing (htmlAttrs attr))] <> cells'
+  cells' <- mconcat <$> mapM (cellToMW "!") cells
+  return $ literal "|-" <> htmlAttrs attr <> cr <> cells'
 
-bodyRowsToMW :: PandocMonad m => [Ann.BodyRow] -> MediaWikiWriter m [Text]
-bodyRowsToMW rows = (\x -> mconcat x) <$> mapM bodyRowToMW rows
+bodyRowsToMW :: PandocMonad m => [Ann.BodyRow] -> MediaWikiWriter m (Doc Text)
+bodyRowsToMW rows = mconcat <$> mapM bodyRowToMW rows
 
-bodyRowToMW :: PandocMonad m => Ann.BodyRow -> MediaWikiWriter m [Text]
+bodyRowToMW :: PandocMonad m => Ann.BodyRow -> MediaWikiWriter m (Doc Text)
 bodyRowToMW (Ann.BodyRow attr _ headCells bodyCells) = do
-  headCells' <- (\x -> mconcat x) <$> mapM (cellToMW "!") headCells
-  bodyCells' <- (\x -> mconcat x) <$> mapM (cellToMW "|") bodyCells
-  return $ ["|-" <> (render Nothing (htmlAttrs attr))] <> headCells' <> bodyCells'
+  headCells' <- mconcat <$> mapM (cellToMW "!") headCells
+  bodyCells' <- mconcat <$> mapM (cellToMW "|") bodyCells
+  return $ literal "|-" <> htmlAttrs attr <> cr <> headCells' <> bodyCells'
 
-cellToMW :: PandocMonad m => Text -> Ann.Cell -> MediaWikiWriter m [Text]
+cellToMW :: PandocMonad m => Text -> Ann.Cell -> MediaWikiWriter m (Doc Text)
 cellToMW marker (Ann.Cell (colSpec :| _) _ (Cell attr align rowspan colspan content)) = do
   content' <- blockListToMediaWiki content
   let (ident,classes,keyVals) = attr
@@ -355,14 +354,12 @@
   let colspan' = case colspan of
                   ColSpan 1 -> mempty
                   ColSpan n -> [("colspan", T.pack(show n))]
-  let attrs' = addPipeIfNotEmpty (render Nothing (htmlAttrs (ident, classes, rowspan' <> colspan' <> keyVals')))
-  return [marker <> attrs' <> addSpaceIfNotEmpty(content')]
-
-addPipeIfNotEmpty :: Text -> Text
-addPipeIfNotEmpty f = if T.null f then f else f <> "|"
-
-addSpaceIfNotEmpty :: Text -> Text
-addSpaceIfNotEmpty f = if T.null f then f else " " <> f
+  let attrs' = htmlAttrs (ident, classes, rowspan' <> colspan' <> keyVals')
+  let attrsRendered = render Nothing attrs'
+  let pipeAttr = if T.null attrsRendered then mempty else attrs' <> literal "|"
+  let contentRendered = render Nothing content'
+  let spaceContent = if T.null contentRendered then mempty else space <> chomp content'
+  return $ literal marker <> pipeAttr <> spaceContent <> cr
 
 imageToMediaWiki :: PandocMonad m => Attr -> MediaWikiWriter m Text
 imageToMediaWiki attr = do
@@ -384,14 +381,14 @@
 -- | Convert list of Pandoc block elements to MediaWiki.
 blockListToMediaWiki :: PandocMonad m
                      => [Block]       -- ^ List of block elements
-                     -> MediaWikiWriter m Text
+                     -> MediaWikiWriter m (Doc Text)
 blockListToMediaWiki blocks =
   vcat <$> mapM blockToMediaWiki blocks
 
 -- | Convert list of Pandoc inline elements to MediaWiki.
-inlineListToMediaWiki :: PandocMonad m => [Inline] -> MediaWikiWriter m Text
+inlineListToMediaWiki :: PandocMonad m => [Inline] -> MediaWikiWriter m (Doc Text)
 inlineListToMediaWiki lst =
-  fmap T.concat $ mapM inlineToMediaWiki $ fixup lst
+  hcat <$> mapM inlineToMediaWiki (fixup lst)
     where
      fixup [] = []
      fixup (Str t : x : xs)
@@ -404,108 +401,110 @@
      isLinkOrImage _         = False
 
 -- | Convert Pandoc inline element to MediaWiki.
-inlineToMediaWiki :: PandocMonad m => Inline -> MediaWikiWriter m Text
+inlineToMediaWiki :: PandocMonad m => Inline -> MediaWikiWriter m (Doc Text)
 
 inlineToMediaWiki (Span attrs ils) = do
   contents <- inlineListToMediaWiki ils
-  return $ render Nothing (tagWithAttrs "span" attrs) <> contents <> "</span>"
+  return $ tagWithAttrs "span" attrs <> contents <> literal "</span>"
 
 inlineToMediaWiki (Emph lst) = do
   contents <- inlineListToMediaWiki lst
-  return $ "''" <> contents <> "''"
+  return $ literal "''" <> contents <> literal "''"
 
 inlineToMediaWiki (Underline lst) = do
   contents <- inlineListToMediaWiki lst
-  return $ "<u>" <> contents <> "</u>"
+  return $ literal "<u>" <> contents <> literal "</u>"
 
 inlineToMediaWiki (Strong lst) = do
   contents <- inlineListToMediaWiki lst
-  return $ "'''" <> contents <> "'''"
+  return $ literal "'''" <> contents <> literal "'''"
 
 inlineToMediaWiki (Strikeout lst) = do
   contents <- inlineListToMediaWiki lst
-  return $ "<s>" <> contents <> "</s>"
+  return $ literal "<s>" <> contents <> literal "</s>"
 
 inlineToMediaWiki (Superscript lst) = do
   contents <- inlineListToMediaWiki lst
-  return $ "<sup>" <> contents <> "</sup>"
+  return $ literal "<sup>" <> contents <> literal "</sup>"
 
 inlineToMediaWiki (Subscript lst) = do
   contents <- inlineListToMediaWiki lst
-  return $ "<sub>" <> contents <> "</sub>"
+  return $ literal "<sub>" <> contents <> literal "</sub>"
 
 inlineToMediaWiki (SmallCaps lst) = inlineListToMediaWiki lst
 
 inlineToMediaWiki (Quoted SingleQuote lst) = do
   contents <- inlineListToMediaWiki lst
-  return $ "\8216" <> contents <> "\8217"
+  return $ literal "\8216" <> contents <> literal "\8217"
 
 inlineToMediaWiki (Quoted DoubleQuote lst) = do
   contents <- inlineListToMediaWiki lst
-  return $ "\8220" <> contents <> "\8221"
+  return $ literal "\8220" <> contents <> literal "\8221"
 
 inlineToMediaWiki (Cite _  lst) = inlineListToMediaWiki lst
 
 inlineToMediaWiki (Code _ str) =
-  return $ "<code>" <> escapeText str <> "</code>"
+  return $ literal $ "<code>" <> escapeText str <> "</code>"
 
 inlineToMediaWiki (Str str) = do
   inDefLabel <- gets stInDefLabel
-  return $
+  return $ literal $
     if inDefLabel
        then T.intercalate "<nowiki>:</nowiki>" $
               map escapeText $ T.splitOn ":" str
        else escapeText str
 
-inlineToMediaWiki (Math mt str) = return $
+inlineToMediaWiki (Math mt str) = return $ literal $
   "<math display=\"" <>
   (if mt == DisplayMath then "block" else "inline") <>
   "\">" <> str <> "</math>"
   -- note:  str should NOT be escaped
 
 inlineToMediaWiki il@(RawInline f str)
-  | f == Format "mediawiki" = return str
-  | f == Format "html"      = return str
-  | otherwise               = "" <$ report (InlineNotRendered il)
+  | f == Format "mediawiki" = return $ literal str
+  | f == Format "html"      = return $ literal str
+  | otherwise               = mempty <$ report (InlineNotRendered il)
 
-inlineToMediaWiki LineBreak = return "<br />\n"
+inlineToMediaWiki LineBreak = return $ literal "<br />" <> cr
 
 inlineToMediaWiki SoftBreak = do
   wrapText <- gets (writerWrapText . stOptions)
   listlevel <- asks listLevel
   case wrapText of
-       WrapAuto     -> return " "
-       WrapNone     -> return " "
+       WrapAuto     -> return space
+       WrapNone     -> return space
        WrapPreserve -> if null listlevel
-                          then return "\n"
-                          else return " "
+                          then return cr
+                          else return space
 
-inlineToMediaWiki Space = return " "
+inlineToMediaWiki Space = return space
 
 inlineToMediaWiki (Link _ txt (src, _)) = do
   -- We need to remove links from link text, because an <a> element is
   -- not allowed inside another <a> element.
   label <- inlineListToMediaWiki (removeLinks txt)
+  let label' = render Nothing label
   case txt of
-     [Str s] | isURI src && escapeURI s == src -> return src
-     _  -> return $ if isURI src
-       then "[" <> src <> " " <> label <> "]"
+     [Str s] | isURI src && escapeURI s == src -> return $ literal src
+     _  -> return $ literal $ if isURI src
+       then "[" <> src <> " " <> label' <> "]"
        else
-         if src == label
+         if src == label'
            then "[[" <> src' <> "]]"
-           else "[[" <> src' <> "|" <> label <> "]]"
+           else "[[" <> src' <> "|" <> label' <> "]]"
        -- with leading / it's a link to a help page
        where src' = fromMaybe src $ T.stripPrefix "/" src
 
 inlineToMediaWiki (Image attr alt (source, tit)) = do
   img  <- imageToMediaWiki attr
   alt' <- inlineListToMediaWiki alt
-  let txt = if T.null alt'
+  let altText = render Nothing alt'
+  let txt = if T.null altText
                then if T.null tit
                        then ""
                        else tit
-               else alt'
-  return $ "[[" <>
+               else altText
+  return $ literal $ "[[" <>
            T.intercalate "|"
            (filter (not . T.null)
             [ "File:" <> source
@@ -516,7 +515,8 @@
 inlineToMediaWiki (Note contents) = do
   contents' <- blockListToMediaWiki contents
   modify (\s -> s { stNotes = True })
-  return $ "<ref>" <> stripTrailingNewlines contents' <> "</ref>"
+  let rendered = render Nothing contents'
+  return $ literal $ "<ref>" <> stripTrailingNewlines rendered <> "</ref>"
   -- note - does not work for notes with multiple blocks
 
 highlightingLangs :: Set.Set Text
diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs
--- a/src/Text/Pandoc/Writers/OpenDocument.hs
+++ b/src/Text/Pandoc/Writers/OpenDocument.hs
@@ -19,7 +19,8 @@
 import Control.Monad.State.Strict ( StateT(..), modify, gets, lift )
 import Data.Char (chr)
 import Data.Foldable (find)
-import Data.List (sortOn, sortBy, foldl')
+import Data.List (sortOn, sortBy)
+import qualified Data.List as L
 import qualified Data.Map as Map
 import Data.Ord (comparing, Down (Down))
 import qualified Data.Set as Set
@@ -175,7 +176,7 @@
                        ,("style:family", "text")]
                        $ selfClosingTag "style:text-properties"
                           (sortOn fst . Map.toList
-                                $ foldl' textStyleAttr mempty (Set.toList at)))
+                                $ L.foldl' textStyleAttr mempty (Set.toList at)))
               return $ inTags False
                   "text:span" [("text:style-name",styleName)] d
 
diff --git a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs
--- a/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs
+++ b/src/Text/Pandoc/Writers/Powerpoint/Presentation.hs
@@ -176,11 +176,6 @@
 runPres env st p = (pres, reverse $ stLog finalSt)
   where (pres, finalSt) = runState (runReaderT p env) st
 
--- GHC 7.8 will still complain about concat <$> mapM unless we specify
--- Functor. We can get rid of this when we stop supporting GHC 7.8.
-concatMapM        :: (Monad m) => (a -> m [b]) -> [a] -> m [b]
-concatMapM f xs   =  liftM concat (mapM f xs)
-
 type Pixels = Integer
 
 data Presentation = Presentation DocProps [Slide]
@@ -348,7 +343,7 @@
 --------------------------------------------------
 
 inlinesToParElems :: [Inline] -> Pres [ParaElem]
-inlinesToParElems = concatMapM inlineToParElems
+inlinesToParElems = fmap mconcat . mapM inlineToParElems
 
 inlineToParElems :: Inline -> Pres [ParaElem]
 inlineToParElems (Str s) = do
@@ -484,7 +479,7 @@
                                                  , pPropIndent = Just 0
                                                  }
                 , envRunProps = (envRunProps r){rPropForceSize = Just blockQuoteSize}})$
-  concatMapM blockToParagraphs blks
+  mconcat <$> mapM blockToParagraphs blks
 -- TODO: work out the format
 blockToParagraphs blk@(RawBlock _ _) = do addLogMessage $ BlockNotRendered blk
                                           return []
@@ -508,7 +503,7 @@
                                            , pPropIndent = Nothing
                                            , pPropIncremental = incremental
                                            }}) $
-    concatMapM multiParList blksLst
+    mconcat <$> mapM multiParList blksLst
 blockToParagraphs (OrderedList listAttr blksLst) = do
   pProps <- asks envParaProps
   incremental <- listShouldBeIncremental
@@ -518,7 +513,7 @@
                                            , pPropIndent = Nothing
                                            , pPropIncremental = incremental
                                            }}) $
-    concatMapM multiParList blksLst
+    mconcat <$> mapM multiParList blksLst
 blockToParagraphs (DefinitionList entries) = do
   incremental <- listShouldBeIncremental
   let go :: ([Inline], [[Block]]) -> Pres [Paragraph]
@@ -526,11 +521,12 @@
         term <-blockToParagraphs $ Para [Strong ils]
         -- For now, we'll treat each definition term as a
         -- blockquote. We can extend this further later.
-        definition <- concatMapM (blockToParagraphs . BlockQuote) blksLst
+        definition <-
+          mconcat <$> mapM (blockToParagraphs . BlockQuote) blksLst
         return $ term ++ definition
   local (\env -> env {envParaProps =
                        (envParaProps env) {pPropIncremental = incremental}})
-    $ concatMapM go entries
+    $ mconcat <$> mapM go entries
 blockToParagraphs (Div (_, classes, _) blks) = let
   hasIncremental = "incremental" `elem` classes
   hasNonIncremental = "nonincremental" `elem` classes
@@ -538,7 +534,7 @@
                    | hasNonIncremental -> Just InNonIncremental
                    | otherwise -> Nothing
   addIncremental env = env { envInIncrementalDiv = incremental }
-  in local addIncremental (concatMapM blockToParagraphs blks)
+  in local addIncremental (mconcat <$> mapM blockToParagraphs blks)
 blockToParagraphs (Figure attr capt blks) = -- This never seems to be used:
   blockToParagraphs (Shared.figureDiv attr capt blks)
 blockToParagraphs hr@HorizontalRule = notRendered hr
@@ -563,7 +559,7 @@
                   , pPropLevel = level + 1
                   }
                 })
-        $ concatMapM blockToParagraphs bs
+        $ mconcat <$> mapM blockToParagraphs bs
   return $ p ++ ps
 
 cellToParagraphs :: Alignment -> SimpleCell -> Pres [Paragraph]
@@ -824,7 +820,7 @@
 blockToSpeakerNotes :: Block -> Pres SpeakerNotes
 blockToSpeakerNotes (Div (_, ["notes"], _) blks) =
   local (\env -> env{envInSpeakerNotes=True}) $
-  SpeakerNotes <$> concatMapM blockToParagraphs blks
+  SpeakerNotes . mconcat <$> mapM blockToParagraphs blks
 blockToSpeakerNotes _ = return mempty
 
 handleSpeakerNotes :: Block -> Pres ()
@@ -891,6 +887,12 @@
   subtitle <- inlinesToParElems $ lookupMetaInlines "subtitle" meta
   authors <- mapM inlinesToParElems $ docAuthors meta
   date <- inlinesToParElems $ docDate meta
+  -- Get speaker notes from metadata "notes" field
+  let notesBlocks = lookupMetaBlocks "notes" meta
+  speakerNotes <- if null notesBlocks
+                  then return mempty
+                  else local (\env -> env{envInSpeakerNotes=True}) $
+                       SpeakerNotes . mconcat <$> mapM blockToParagraphs notesBlocks
   if null title && null subtitle && null authors && null date
     then return Nothing
     else return $
@@ -898,7 +900,7 @@
          Slide
          metadataSlideId
          (MetadataSlide title subtitle authors date)
-         mempty
+         speakerNotes
          Nothing
 
 addSpeakerNotesToMetaSlide :: Slide -> [Block] -> Pres (Slide, [Block])
diff --git a/src/Text/Pandoc/Writers/RST.hs b/src/Text/Pandoc/Writers/RST.hs
--- a/src/Text/Pandoc/Writers/RST.hs
+++ b/src/Text/Pandoc/Writers/RST.hs
@@ -20,7 +20,8 @@
                   GeneralCategory(
                         ClosePunctuation, OpenPunctuation, InitialQuote,
                          FinalQuote, DashPunctuation, OtherPunctuation))
-import Data.List (transpose, intersperse, foldl')
+import Data.List (transpose, intersperse)
+import qualified Data.List as L
 import qualified Data.List.NonEmpty as NE
 import Data.Maybe (fromMaybe)
 import qualified Data.Text as T
@@ -674,7 +675,7 @@
   | null contents = [outer]
   | otherwise     = combineAll contents
   where contents = dropInlineParent outer
-        combineAll = foldl' combine []
+        combineAll = L.foldl' combine []
 
         combine :: [Inline] -> Inline -> [Inline]
         combine f i =
diff --git a/src/Text/Pandoc/Writers/Shared.hs b/src/Text/Pandoc/Writers/Shared.hs
--- a/src/Text/Pandoc/Writers/Shared.hs
+++ b/src/Text/Pandoc/Writers/Shared.hs
@@ -63,7 +63,8 @@
 import Data.Either (isRight)
 import Data.Aeson (ToJSON (..), encode)
 import Data.Char (chr, ord, isSpace, isLetter, isUpper)
-import Data.List (groupBy, intersperse, foldl', transpose)
+import Data.List (groupBy, intersperse, transpose)
+import qualified Data.List as L
 import Data.List.NonEmpty (NonEmpty((:|)))
 import Data.Text.Conversions (FromText(..))
 import qualified Data.Map as M
@@ -381,7 +382,7 @@
                   cellTopBorder = NoLine }
 
 addDummies :: [[RenderedCell Text]] -> [[RenderedCell Text]]
-addDummies = reverse . foldl' go []
+addDummies = reverse . L.foldl' go []
  where
    go [] cs = [cs]
    go (prevRow:rs) cs = addDummiesToRow prevRow cs : prevRow : rs
@@ -468,7 +469,7 @@
 
 formatHeaderLine :: Show a => LineStyle -> [[RenderedCell a]] -> Doc Text
 formatHeaderLine lineStyle rows =
-  literal $ foldl'
+  literal $ L.foldl'
     (\t row -> combineBorders t (render Nothing $ formatBorder (const lineStyle) True row))
     mempty rows
 
@@ -479,7 +480,7 @@
                             then char '|'
                             else char '+'
  where
-   (lastBorderStyle, borderParts) = foldl' addBorder (NoLine, mempty) cs
+   (lastBorderStyle, borderParts) = L.foldl' addBorder (NoLine, mempty) cs
    addBorder (prevBorderStyle, accum) c =
      (borderStyle c, accum <> char junctionChar <> toBorderSection c)
       where junctionChar = case (borderStyle c, prevBorderStyle) of
diff --git a/src/Text/Pandoc/Writers/Texinfo.hs b/src/Text/Pandoc/Writers/Texinfo.hs
--- a/src/Text/Pandoc/Writers/Texinfo.hs
+++ b/src/Text/Pandoc/Writers/Texinfo.hs
@@ -17,7 +17,8 @@
 import Control.Monad.State.Strict
     ( StateT, MonadState(get), gets, modify, evalStateT )
 import Data.Char (chr, ord, isAlphaNum)
-import Data.List (maximumBy, transpose, foldl')
+import Data.List (maximumBy, transpose)
+import qualified Data.List as L
 import Data.List.NonEmpty (nonEmpty)
 import Data.Ord (comparing)
 import qualified Data.Map as M
@@ -148,6 +149,19 @@
 inCmd :: Text -> Doc Text -> Doc Text
 inCmd cmd contents = char '@' <> literal cmd <> braces contents
 
+isCodeOrBreak :: Inline -> Bool
+isCodeOrBreak (Code{}) = True
+isCodeOrBreak LineBreak = True
+isCodeOrBreak _ = False
+
+isCode :: Inline -> Bool
+isCode (Code{}) = True
+isCode _ = False
+
+codeToStr :: Inline -> Inline
+codeToStr (Code _ s) = Str s
+codeToStr x = x
+
 -- | Convert Pandoc block element to Texinfo.
 blockToTexinfo :: PandocMonad m
                => Block     -- ^ Block to convert
@@ -158,8 +172,12 @@
 blockToTexinfo (Plain lst) =
   inlineListToTexinfo lst
 
-blockToTexinfo (Para lst) =
-  inlineListToTexinfo lst    -- this is handled differently from Plain in blockListToTexinfo
+-- this is handled differently from Plain in blockListToTexinfo
+blockToTexinfo (Para lst)
+  | all isCodeOrBreak lst
+      = (\xs -> "@example" $$ vcat xs $$ "@end example")
+          <$> mapM (inlineToTexinfo . codeToStr) (filter isCode lst)
+  | otherwise = inlineListToTexinfo lst
 
 blockToTexinfo (LineBlock lns) =
   blockToTexinfo $ linesToPara lns
@@ -330,7 +348,7 @@
                      -> [[Block]]
                      -> TI m (Doc Text)
 tableAnyRowToTexinfo itemtype aligns cols =
-  (literal itemtype $$) . foldl' (\row item -> row $$
+  (literal itemtype $$) . L.foldl' (\row item -> row $$
   (if isEmpty row then empty else text " @tab ") <> item) empty <$> zipWithM alignedBlock aligns cols
 
 alignedBlock :: PandocMonad m
diff --git a/src/Text/Pandoc/Writers/Typst.hs b/src/Text/Pandoc/Writers/Typst.hs
--- a/src/Text/Pandoc/Writers/Typst.hs
+++ b/src/Text/Pandoc/Writers/Typst.hs
@@ -584,11 +584,13 @@
       | needsEscapeAtLineStart c || isOrderedListMarker t
         -> afterBreak "\\"
     _ -> mempty) <>
-  (literal (T.replace "//" "\\/\\/"
-    (if T.any needsEscape t
-        then T.concatMap escapeChar t
-        else t)))
+   literal (snd $ T.foldl' go ('\n', mempty) t)
   where
+    go (lastc, t') c
+      | needsEscape c = (c, t' <> escapeChar c)
+      | c == '-', lastc == '-', smart = (c, t' <> T.pack ['\\',c])
+      | c == '/', lastc == '/' = (c, t' <> T.pack ['\\',c])
+      | otherwise = (c, T.snoc t' c)
     escapeChar c
       | c == '\160' = "~"
       | c == '\8217', smart = "'" -- apostrophe
diff --git a/test/Tests/Readers/ODT.hs b/test/Tests/Readers/ODT.hs
--- a/test/Tests/Readers/ODT.hs
+++ b/test/Tests/Readers/ODT.hs
@@ -186,6 +186,7 @@
 --                                , "table"
                                   , "textMixedStyles"
                                   , "tableWithContents"
+                                  , "tableWithSpans"
                                   , "unicode"
                                   , "unorderedList"
                                   , "unorderedListHeader"
diff --git a/test/Tests/Readers/Org/Block.hs b/test/Tests/Readers/Org/Block.hs
--- a/test/Tests/Readers/Org/Block.hs
+++ b/test/Tests/Readers/Org/Block.hs
@@ -43,7 +43,7 @@
       T.unlines [ ": echo hello"
                 , ": echo dear tester"
                 ] =?>
-      codeBlockWith ("", ["example"], []) "echo hello\necho dear tester\n"
+      codeBlockWith ("", [], []) "echo hello\necho dear tester\n"
 
   , "Example block surrounded by text" =:
       T.unlines [ "Greetings"
@@ -52,7 +52,7 @@
                 , "Bye"
                 ] =?>
       mconcat [ para "Greetings"
-              , codeBlockWith ("", ["example"], [])
+              , codeBlockWith ("", [], [])
                               "echo hello\necho dear tester\n"
               , para "Bye"
               ]
diff --git a/test/Tests/Readers/Org/Block/CodeBlock.hs b/test/Tests/Readers/Org/Block/CodeBlock.hs
--- a/test/Tests/Readers/Org/Block/CodeBlock.hs
+++ b/test/Tests/Readers/Org/Block/CodeBlock.hs
@@ -99,7 +99,7 @@
            results' = "65\n"
        in codeBlockWith ("", classes, params) code'
           <>
-          codeBlockWith ("", ["example"], []) results'
+          codeBlockWith ("", [], []) results'
 
   , "Source block with results and :exports code" =:
        T.unlines [ "#+begin_src emacs-lisp :exports code"
@@ -126,7 +126,7 @@
                  , "#+RESULTS:"
                  , ": 65" ] =?>
        let results' = "65\n"
-       in codeBlockWith ("", ["example"], []) results'
+       in codeBlockWith ("", [], []) results'
 
   , "Source block with results and :exports none" =:
        T.unlines [ "#+begin_src emacs-lisp :exports none"
@@ -171,7 +171,7 @@
                  , "a rule."
                  , "#+eND_exAMPle"
                  ] =?>
-       codeBlockWith ("", ["example"], [])
+       codeBlockWith ("", [], [])
                      "A chosen representation of\na rule.\n"
 
   , "Code block with caption" =:
diff --git a/test/Tests/Writers/Docx.hs b/test/Tests/Writers/Docx.hs
--- a/test/Tests/Writers/Docx.hs
+++ b/test/Tests/Writers/Docx.hs
@@ -1,10 +1,18 @@
+{-# LANGUAGE OverloadedStrings #-}
 module Tests.Writers.Docx (tests) where
 
-import Text.Pandoc
+import Codec.Archive.Zip (findEntryByPath, fromEntry, toArchive)
+import qualified Data.ByteString.Lazy as BL
+import Data.List (isInfixOf, isPrefixOf)
+import qualified Data.Map as M
+import Data.Text (Text)
+import qualified Data.Text as Text
+import qualified Data.Text.IO as T
 import Test.Tasty
-import Tests.Writers.OOXML
 import Test.Tasty.HUnit
-import Data.List (isPrefixOf)
+import Tests.Writers.OOXML
+import Text.Pandoc
+import Text.XML.Light (QName(QName), findAttr, findElements, parseXMLDoc)
 
 -- we add an extra check to make sure that we're not writing in the
 -- toplevel docx directory. We don't want to accidentally overwrite an
@@ -213,5 +221,75 @@
             def
             "docx/document-properties-short-desc.native"
             "docx/golden/document-properties-short-desc.docx"
+          ]
+        , testGroup "reference docx"
+          [ testCase "no media directory override in content types" $ do
+              let opts = def{ writerReferenceDoc = Just "docx/inline_images.docx" }
+              txt <- T.readFile "docx/inline_formatting.native"
+              bs <- runIOorExplode $ do
+                mblang <- toLang (Just (Text.pack "en-US") :: Maybe Text)
+                maybe (return ()) setTranslations mblang
+                setVerbosity ERROR
+                readNative def txt >>= writeDocx opts
+              let archive = toArchive bs
+              entry <- case findEntryByPath "[Content_Types].xml" archive of
+                Nothing -> assertFailure "Missing [Content_Types].xml in output docx"
+                Just e -> return e
+              doc <- case parseXMLDoc (fromEntry entry) of
+                Nothing -> assertFailure "Failed to parse [Content_Types].xml"
+                Just d -> return d
+              let partNameAttr = QName "PartName" Nothing Nothing
+              let overrideName = QName "Override" Nothing Nothing
+              let overrides = findElements overrideName doc
+              let hasBadOverride =
+                    any (\el -> findAttr partNameAttr el == Just "/word/media/")
+                        overrides
+              assertBool "Found invalid /word/media/ Override in [Content_Types].xml"
+                (not hasBadOverride)
+          , testCase "language from reference docx is preserved" $ do
+              -- First, verify that the german-reference.docx actually has de-DE
+              refBs <- BL.readFile "docx/german-reference.docx"
+              let refArchive = toArchive refBs
+              refEntry <- case findEntryByPath "word/styles.xml" refArchive of
+                Nothing -> assertFailure "Missing word/styles.xml in german-reference.docx"
+                Just e -> return e
+              let refStylesXml = show (fromEntry refEntry)
+              let getLangLines = filter ("w:lang" `isInfixOf`) . lines
+              assertBool ("german-reference.docx w:lang line: " ++
+                  unlines (getLangLines refStylesXml))
+                (any ("de-DE" `isInfixOf`) (getLangLines refStylesXml))
+              -- Now test that using this reference preserves the language
+              let opts = def{ writerReferenceDoc = Just "docx/german-reference.docx" }
+              txt <- T.readFile "docx/inline_formatting.native"
+              bs <- runIOorExplode $ do
+                setVerbosity ERROR
+                readNative def txt >>= writeDocx opts
+              let archive = toArchive bs
+              entry <- case findEntryByPath "word/styles.xml" archive of
+                Nothing -> assertFailure "Missing word/styles.xml in output docx"
+                Just e -> return e
+              let stylesXml = show (fromEntry entry)
+              -- Find the w:lang line for debugging
+              -- Check that the styles.xml contains the German language
+              assertBool ("Language from reference docx not preserved. w:lang lines: " ++ unlines (getLangLines stylesXml))
+                (any ("de-DE" `isInfixOf`) (getLangLines stylesXml))
+          , testCase "language from metadata overrides reference docx" $ do
+              -- Use a reference docx with German language, but specify French in metadata
+              let opts = def{ writerReferenceDoc = Just "docx/german-reference.docx" }
+              bs <- runIOorExplode $ do
+                setVerbosity ERROR
+                -- Create a document with French language metadata
+                let doc = Pandoc (Meta $ M.fromList [("lang", MetaString "fr-FR")])
+                                 [Para [Str "Test"]]
+                writeDocx opts doc
+              let archive = toArchive bs
+              entry <- case findEntryByPath "word/styles.xml" archive of
+                Nothing -> assertFailure "Missing word/styles.xml in output docx"
+                Just e -> return e
+              let stylesXml = show (fromEntry entry)
+              -- Check that the styles.xml contains the French language (not German)
+              let getLangLines = filter ("w:lang" `isInfixOf`) . lines
+              assertBool "Language from metadata did not override reference docx (expected fr-FR)"
+                (any ("fr-FR" `isInfixOf`) (getLangLines stylesXml))
           ]
         ]
diff --git a/test/Tests/Writers/HTML.hs b/test/Tests/Writers/HTML.hs
--- a/test/Tests/Writers/HTML.hs
+++ b/test/Tests/Writers/HTML.hs
@@ -102,13 +102,13 @@
   , testGroup "sample with style"
     [ "samp should wrap highlighted code" =:
       codeWith ("",["sample","haskell"],[]) ">>="
-      =?> ("<samp><code class=\"sourceCode haskell\">" ++
+      =?> ("<samp><code class=\"sourceCode haskell sample\">" ++
           "<span class=\"op\">&gt;&gt;=</span></code></samp>")
     ]
   , testGroup "variable with style"
     [ "var should wrap highlighted code" =:
       codeWith ("",["haskell","variable"],[]) ">>="
-      =?> ("<var><code class=\"sourceCode haskell\">" ++
+      =?> ("<var><code class=\"sourceCode haskell variable\">" ++
           "<span class=\"op\">&gt;&gt;=</span></code></var>")
     ]
   , testGroup "footnotes"
diff --git a/test/Tests/Writers/Powerpoint.hs b/test/Tests/Writers/Powerpoint.hs
--- a/test/Tests/Writers/Powerpoint.hs
+++ b/test/Tests/Writers/Powerpoint.hs
@@ -126,6 +126,10 @@
       def
       "pptx/speaker-notes-after-metadata/input.native"
       "pptx/speaker-notes-after-metadata/output.pptx"
+    , pptxTests "speaker notes from metadata field"
+      def
+      "pptx/metadata-speaker-notes/input.native"
+      "pptx/metadata-speaker-notes/output.pptx"
     , pptxTests "remove empty slides"
       def
       "pptx/remove-empty-slides/input.native"
diff --git a/test/command/10271.md b/test/command/10271.md
--- a/test/command/10271.md
+++ b/test/command/10271.md
@@ -16,7 +16,7 @@
 % pandoc -f markdown-smart -t typst+smart
 "don't do it---"
 ^D
-\"don\'t do it---\"
+\"don\'t do it-\-\-\"
 
 ```
 ```
diff --git a/test/command/10643.md b/test/command/10643.md
new file mode 100644
--- /dev/null
+++ b/test/command/10643.md
@@ -0,0 +1,11 @@
+```
+% pandoc -f html -t native
+<p>A<style></style>B</p>
+^D
+[ Para
+    [ Str "A"
+    , RawInline (Format "html") "<style></style>"
+    , Str "B"
+    ]
+]
+```
diff --git a/test/command/10942.md b/test/command/10942.md
new file mode 100644
--- /dev/null
+++ b/test/command/10942.md
@@ -0,0 +1,97 @@
+Ungrouped field instruction:
+```
+% pandoc -f rtf -t native
+{\rtf1\ansi\deff0
+{\fonttbl{\f0\froman Times New Roman;}}
+{\colortbl;\red0\green0\blue0;}
+\deftab720
+
+\trowd\trleft0\cellx2000\cellx4000
+\pard\intbl
+{{\field{\*\fldinst HYPERLINK "https://example.com" }{\fldrslt {\hich\af0\loch\hich\af0\loch\cf0\f0\cf0\f0\loch
+{\*\bkmkstart _dx_frag_StartFragment}{\*\bkmkend _dx_frag_StartFragment}Problem Text}}}}\cell
+\pard\intbl Normal Text\cell
+\row
+
+\trowd\trleft0\cellx2000\cellx4000
+\pard\intbl Simple Text\cell
+\pard\intbl More Text\cell
+\row
+}
+^D
+[ Table
+    ( "" , [] , [] )
+    (Caption Nothing [])
+    [ ( AlignDefault , ColWidthDefault )
+    , ( AlignDefault , ColWidthDefault )
+    ]
+    (TableHead ( "" , [] , [] ) [])
+    [ TableBody
+        ( "" , [] , [] )
+        (RowHeadColumns 0)
+        []
+        [ Row
+            ( "" , [] , [] )
+            [ Cell
+                ( "" , [] , [] )
+                AlignDefault
+                (RowSpan 1)
+                (ColSpan 1)
+                [ Para
+                    [ Link
+                        ( "" , [] , [] )
+                        [ Str "Problem" , Space , Str "Text" ]
+                        ( "https://example.com" , "" )
+                    ]
+                ]
+            , Cell
+                ( "" , [] , [] )
+                AlignDefault
+                (RowSpan 1)
+                (ColSpan 1)
+                [ Para [ Str "Normal" , Space , Str "Text" ] ]
+            ]
+        , Row
+            ( "" , [] , [] )
+            [ Cell
+                ( "" , [] , [] )
+                AlignDefault
+                (RowSpan 1)
+                (ColSpan 1)
+                [ Para [ Str "Simple" , Space , Str "Text" ] ]
+            , Cell
+                ( "" , [] , [] )
+                AlignDefault
+                (RowSpan 1)
+                (ColSpan 1)
+                [ Para [ Str "More" , Space , Str "Text" ] ]
+            ]
+        ]
+    ]
+    (TableFoot ( "" , [] , [] ) [])
+]
+```
+
+Additionally, ungrouped field result:
+```
+% pandoc -f rtf -t native
+{\rtf1\ansi\ansicpg1252\cocoartf2867
+\cocoatextscaling0\cocoaplatform1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue;}
+{\colortbl;\red255\green255\blue255;\red0\green0\blue0;}
+{\*\expandedcolortbl;;\cssrgb\c0\c0\c0;}
+\paperw11905\paperh16837\margl1133\margr1133\margb1133\margt1133
+\deftab720
+\pard\pardeftab720\partightenfactor0
+{\field{\*\fldinst{HYPERLINK "https://example.com"}}{\fldrslt 
+\f0\fs22 \cf2 \up0 \nosupersub \ul \ulc2 link}}}
+^D
+[ Para
+    [ Underline
+        [ Link
+            ( "" , [] , [] )
+            [ Str "link" ]
+            ( "https://example.com" , "" )
+        ]
+    ]
+]
+```
diff --git a/test/command/11299.md b/test/command/11299.md
new file mode 100644
--- /dev/null
+++ b/test/command/11299.md
@@ -0,0 +1,28 @@
+```
+% pandoc -f mediawiki -t native
+<foo>
+
+<samp>x</samp>
+
+<kbd>x</kbd>
+
+<var>x</var>
+
+<mark>This is ''highlighted''</mark>
+^D
+[ Para [ Str "<foo>" ]
+, Para [ Code ( "" , [ "sample" ] , [] ) "x" ]
+, Para [ Span ( "" , [ "kbd" ] , [] ) [ Str "x" ] ]
+, Para [ Code ( "" , [ "variable" ] , [] ) "x" ]
+, Para
+    [ Span
+        ( "" , [ "mark" ] , [] )
+        [ Str "This"
+        , Space
+        , Str "is"
+        , Space
+        , Emph [ Str "highlighted" ]
+        ]
+    ]
+]
+```
diff --git a/test/command/11300.md b/test/command/11300.md
new file mode 100644
--- /dev/null
+++ b/test/command/11300.md
@@ -0,0 +1,67 @@
+```
+% pandoc -f docbook -t native -s
+<?xml version="1.0"?>
+<book xmlns="http://docbook.org/ns/docbook" version="5.0" dir="ltr">
+  <info>
+    <title>Book title</title>
+    <subtitle>Book subtitle</subtitle>
+  </info>
+  <chapter>
+    <info>
+      <title>Chapter title</title>
+    </info>
+    <para>My sentence</para>
+  </chapter>
+</book>
+^D
+Pandoc
+  Meta
+    { unMeta =
+        fromList
+          [ ( "subtitle"
+            , MetaInlines [ Str "Book" , Space , Str "subtitle" ]
+            )
+          , ( "title"
+            , MetaInlines [ Str "Book" , Space , Str "title" ]
+            )
+          ]
+    }
+  [ Header
+      1 ( "" , [] , [] ) [ Str "Chapter" , Space , Str "title" ]
+  , Para [ Str "My" , Space , Str "sentence" ]
+  ]
+```
+
+```
+% pandoc -f docbook -t native -s
+<?xml version="1.0"?>
+<book xmlns="http://docbook.org/ns/docbook" version="5.0" dir="ltr">
+  <chapter>
+    <info>
+      <title>Chapter title</title>
+    </info>
+    <para>My sentence</para>
+  </chapter>
+  <info>
+    <title>Book title</title>
+    <subtitle>Book subtitle</subtitle>
+  </info>
+</book>
+^D
+Pandoc
+  Meta
+    { unMeta =
+        fromList
+          [ ( "subtitle"
+            , MetaInlines [ Str "Book" , Space , Str "subtitle" ]
+            )
+          , ( "title"
+            , MetaInlines [ Str "Book" , Space , Str "title" ]
+            )
+          ]
+    }
+  [ Header
+      1 ( "" , [] , [] ) [ Str "Chapter" , Space , Str "title" ]
+  , Para [ Str "My" , Space , Str "sentence" ]
+  ]
+```
diff --git a/test/command/11312.md b/test/command/11312.md
new file mode 100644
--- /dev/null
+++ b/test/command/11312.md
@@ -0,0 +1,21 @@
+```
+% pandoc -f mediawiki -t texinfo
+foofoo
+
+ bar
+ baz
+
+foofoo
+^D
+@node Top
+@top Top
+
+foofoo
+
+@example
+bar
+baz
+@end example
+
+foofoo
+```
diff --git a/test/command/11323.md b/test/command/11323.md
new file mode 100644
--- /dev/null
+++ b/test/command/11323.md
@@ -0,0 +1,12 @@
+```
+% pandoc -f rst
+foo-
+   bar
+^D
+<dl>
+<dt>foo-</dt>
+<dd>
+<p>bar</p>
+</dd>
+</dl>
+```
diff --git a/test/command/11341.md b/test/command/11341.md
new file mode 100644
--- /dev/null
+++ b/test/command/11341.md
@@ -0,0 +1,9 @@
+```
+% pandoc -t commonmark -f html
+<h1>The Hobbit<br>or<br>There and Back Again</h1>
+^D
+The Hobbit\
+or\
+There and Back Again
+====================
+```
diff --git a/test/command/11342.md b/test/command/11342.md
new file mode 100644
--- /dev/null
+++ b/test/command/11342.md
@@ -0,0 +1,33 @@
+Subfigures should be converted to *fig-group* elements.
+
+```
+% pandoc -f latex -t jats
+\begin{figure}[H]
+  \begin{subfigure}
+    \centering
+    \includegraphics[height=1.5in]{assets/A.png}
+    \caption{Graph for function *A*.}
+    \label{fig:A}
+  \end{subfigure}
+  \begin{subfigure}
+    \centering
+    \includegraphics[height=1.5in]{assets/B.png}
+    \caption{Graph for function *B*.}
+    \label{fig:B}
+  \end{subfigure}
+  \label{fig:graphs}
+  \caption{Function graphs.}
+\end{figure}
+^D
+<fig-group id="figU003Agraphs">
+  <caption><p>Function graphs.</p></caption>
+  <fig id="figU003AA">
+    <caption><p>Graph for function *A*.</p></caption>
+    <graphic mimetype="image" mime-subtype="png" xlink:href="assets/A.png" />
+  </fig>
+  <fig id="figU003AB">
+    <caption><p>Graph for function *B*.</p></caption>
+    <graphic mimetype="image" mime-subtype="png" xlink:href="assets/B.png" />
+  </fig>
+</fig-group>
+```
diff --git a/test/command/11348.md b/test/command/11348.md
new file mode 100644
--- /dev/null
+++ b/test/command/11348.md
@@ -0,0 +1,61 @@
+```
+% pandoc -f markdown -t native
+$ invalid opening inline math$
+
+$
+invalid opening inline math$
+
+$invalid closing inline math $
+
+$invalid closing inline math
+$
+
+$valid inline math$
+^D
+[ Para
+    [ Str "$"
+    , Space
+    , Str "invalid"
+    , Space
+    , Str "opening"
+    , Space
+    , Str "inline"
+    , Space
+    , Str "math$"
+    ]
+, Para
+    [ Str "$"
+    , SoftBreak
+    , Str "invalid"
+    , Space
+    , Str "opening"
+    , Space
+    , Str "inline"
+    , Space
+    , Str "math$"
+    ]
+, Para
+    [ Str "$invalid"
+    , Space
+    , Str "closing"
+    , Space
+    , Str "inline"
+    , Space
+    , Str "math"
+    , Space
+    , Str "$"
+    ]
+, Para
+    [ Str "$invalid"
+    , Space
+    , Str "closing"
+    , Space
+    , Str "inline"
+    , Space
+    , Str "math"
+    , SoftBreak
+    , Str "$"
+    ]
+, Para [ Math InlineMath "valid inline math" ]
+]
+```
diff --git a/test/command/11362.md b/test/command/11362.md
new file mode 100644
--- /dev/null
+++ b/test/command/11362.md
@@ -0,0 +1,17 @@
+```
+% pandoc -f markdown -t asciidoc
+This is a _test_[^1].
+
+And yet another **one**[^1].
+
+We can also try to write #hashtags [this]{.test}.
+
+[^1]: Test footnote.
+^D
+This is a __test__footnote:[Test footnote.].
+
+And yet another **one**footnote:[Test footnote.].
+
+We can also try to write ++#++hashtags [.test]#this#.
+
+```
diff --git a/test/command/11364.md b/test/command/11364.md
new file mode 100644
--- /dev/null
+++ b/test/command/11364.md
@@ -0,0 +1,75 @@
+```
+% pandoc -f rtf -t native
+{\rtf1\ansi\deff3\adeflang1025
+{\fonttbl{\f0\froman\fprq2\fcharset0 Times New Roman;}{\f1\froman\fprq2\fcharset2 Symbol;}{\f2\fswiss\fprq2\fcharset0 Arial;}{\f3\froman\fprq2\fcharset0 Liberation Serif{\*\falt Times New Roman};}{\f4\fnil\fprq0\fcharset2 OpenSymbol{\*\falt Arial Unicode MS};}{\f5\fswiss\fprq2\fcharset0 Liberation Sans{\*\falt Arial};}{\f6\fnil\fprq2\fcharset0 Noto Sans CJK SC;}{\f7\fnil\fprq2\fcharset0 Matangi Light;}{\f8\fnil\fprq0\fcharset128 Matangi Light;}{\f9\fnil\fprq0\fcharset128 OpenSymbol{\*\falt Arial Unicode MS};}}
+{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;}
+{\stylesheet{\s0\snext0\widctlpar\hyphpar0\ltrpar\kerning1\cf0\rtlch\af7\afs24\alang1081\ltrch\hich\af3\afs24\alang1033\dbch\af10\langfe2052\loch\f3\fs24\lang1033 Normal;}
+{\*\cs15\snext15\rtlch\af4\ltrch\hich\af4\dbch\af4\loch\f4 Bullets;}
+{\s16\sbasedon0\snext17\sb240\sa120\keepn\rtlch\af7\afs28\ltrch\hich\af5\afs28\dbch\af6\loch\f5\fs28 Heading;}
+{\s17\sbasedon0\snext17\sl276\slmult1\sb0\sa140 Body Text;}
+{\s18\sbasedon17\snext18\rtlch\af8\ltrch List;}
+{\s19\sbasedon0\snext19\sb120\sa120\noline\rtlch\af8\afs24\ai\ltrch\fs24\i caption;}
+{\s20\sbasedon0\snext20\noline\rtlch\af8\ltrch Index;}
+{\s21\sbasedon0\snext21\nowidctlpar\noline Table Contents;}
+}{\*\listtable{\list\listtemplateid1
+{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f9\rtlch\af4\ltrch\fi-360\li720}
+{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u9702 ?;}{\levelnumbers;}\f9\rtlch\af4\ltrch\fi-360\li1080}
+{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u9642 ?;}{\levelnumbers;}\f9\rtlch\af4\ltrch\fi-360\li1440}
+{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f9\rtlch\af4\ltrch\fi-360\li1800}
+{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u9702 ?;}{\levelnumbers;}\f9\rtlch\af4\ltrch\fi-360\li2160}
+{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u9642 ?;}{\levelnumbers;}\f9\rtlch\af4\ltrch\fi-360\li2520}
+{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u8226 ?;}{\levelnumbers;}\f9\rtlch\af4\ltrch\fi-360\li2880}
+{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u9702 ?;}{\levelnumbers;}\f9\rtlch\af4\ltrch\fi-360\li3240}
+{\listlevel\levelnfc23\leveljc0\levelstartat1\levelfollow0{\leveltext \'01\u9642 ?;}{\levelnumbers;}\f9\rtlch\af4\ltrch\fi-360\li3600}\listid1}
+{\list\listtemplateid2
+{\listlevel\levelnfc255\leveljc0\levelstartat1\levelfollow2{\leveltext \'00;}{\levelnumbers;}\fi0\li0}
+{\listlevel\levelnfc255\leveljc0\levelstartat1\levelfollow2{\leveltext \'00;}{\levelnumbers;}\fi0\li0}
+{\listlevel\levelnfc255\leveljc0\levelstartat1\levelfollow2{\leveltext \'00;}{\levelnumbers;}\fi0\li0}
+{\listlevel\levelnfc255\leveljc0\levelstartat1\levelfollow2{\leveltext \'00;}{\levelnumbers;}\fi0\li0}
+{\listlevel\levelnfc255\leveljc0\levelstartat1\levelfollow2{\leveltext \'00;}{\levelnumbers;}\fi0\li0}
+{\listlevel\levelnfc255\leveljc0\levelstartat1\levelfollow2{\leveltext \'00;}{\levelnumbers;}\fi0\li0}
+{\listlevel\levelnfc255\leveljc0\levelstartat1\levelfollow2{\leveltext \'00;}{\levelnumbers;}\fi0\li0}
+{\listlevel\levelnfc255\leveljc0\levelstartat1\levelfollow2{\leveltext \'00;}{\levelnumbers;}\fi0\li0}
+{\listlevel\levelnfc255\leveljc0\levelstartat1\levelfollow2{\leveltext \'00;}{\levelnumbers;}\fi0\li0}\listid2}
+}{\listoverridetable{\listoverride\listid1\listoverridecount0\ls1}{\listoverride\listid2\listoverridecount0\ls2}}{\*\generator LibreOffice/25.8.4.2$Linux_X86_64 LibreOffice_project/580$Build-2}{\info{\creatim\yr2025\mo12\dy28\hr10\min51}{\revtim\yr2025\mo12\dy28\hr10\min53}{\printim\yr0\mo0\dy0\hr0\min0}}{\*\userprops}\deftab709
+\hyphauto1\viewscale140\formshade\nobrkwrptbl\paperh15840\paperw12240\margl1134\margr1134\margt1134\margb1134\sectd\sbknone\sftnnar\saftnnrlc\sectunlocked1\pgwsxn12240\pghsxn15840\marglsxn1134\margrsxn1134\margtsxn1134\margbsxn1134\ftnbj\ftnstart1\ftnrstcont\ftnnar\fet\aftnrstcont\aftnstart1\aftnnrlc
+{\*\ftnsep\chftnsep}\pgndec\pard\plain \s0\widctlpar\hyphpar0\ltrpar\kerning1\cf0\rtlch\af7\afs24\alang1081\ltrch\hich\af3\afs24\alang1033\dbch\af10\langfe2052\loch\f3\fs24\lang1033{\listtext\pard\plain \rtlch\af4\ltrch\hich\af4\dbch\af4\loch\f4 \u8226\'95\tab}\ilvl0\ls1 \fi-360\li720\lin720\ql\ltrpar{
+List item}
+\par \trowd\trql\ltrrow\trpaddft3\trpaddt0\trpaddfl3\trpaddl0\trpaddfb3\trpaddb0\trpaddfr3\trpaddr0\clbrdrt\brdrs\brdrw10\brdrcf1\clpadfl3\clpadl55\clbrdrl\brdrs\brdrw10\brdrcf1\clpadft3\clpadt55\clbrdrb\brdrs\brdrw10\brdrcf1\clpadfb3\clpadb55\clpadfr3\clpadr55\cellx4986\clbrdrt\brdrs\brdrw10\brdrcf1\clpadfl3\clpadl55\clbrdrl\brdrs\brdrw10\brdrcf1\clpadft3\clpadt55\clbrdrb\brdrs\brdrw10\brdrcf1\clpadfb3\clpadb55\clbrdrr\brdrs\brdrw10\brdrcf1\clpadfr3\clpadr55\cellx9972\pard\plain \s21\nowidctlpar\noline\intbl\ql\ltrpar{
+A}\cell\pard\plain \s21\nowidctlpar\noline\intbl\ql\ltrpar{
+B}\cell\row\pard \pard\plain \s0\widctlpar\hyphpar0\ltrpar\kerning1\cf0\rtlch\af7\afs24\alang1081\ltrch\hich\af3\afs24\alang1033\dbch\af10\langfe2052\loch\f3\fs24\lang1033\ql\ltrpar
+\par }
+^D
+[ BulletList
+    [ [ Para [ Str "List" , Space , Str "item" ] ] ]
+, Table
+    ( "" , [] , [] )
+    (Caption Nothing [])
+    [ ( AlignDefault , ColWidthDefault )
+    , ( AlignDefault , ColWidthDefault )
+    ]
+    (TableHead ( "" , [] , [] ) [])
+    [ TableBody
+        ( "" , [] , [] )
+        (RowHeadColumns 0)
+        []
+        [ Row
+            ( "" , [] , [] )
+            [ Cell
+                ( "" , [] , [] )
+                AlignDefault
+                (RowSpan 1)
+                (ColSpan 1)
+                [ Para [ Str "A" ] ]
+            , Cell
+                ( "" , [] , [] )
+                AlignDefault
+                (RowSpan 1)
+                (ColSpan 1)
+                [ Para [ Str "B" ] ]
+            ]
+        ]
+    ]
+    (TableFoot ( "" , [] , [] ) [])
+]
+```
diff --git a/test/command/11374.md b/test/command/11374.md
new file mode 100644
--- /dev/null
+++ b/test/command/11374.md
@@ -0,0 +1,6 @@
+```
+% pandoc -f native -t asciidoc
+[ Para [ SmallCaps [ Str "foo" ] ] ]
+^D
+[smallcaps]#foo#
+```
diff --git a/test/command/11384.md b/test/command/11384.md
new file mode 100644
--- /dev/null
+++ b/test/command/11384.md
@@ -0,0 +1,17 @@
+```
+% pandoc -t markdown -f native
+[ Para [ Math DisplayMath "\ne = mc^2\n" ] ]
+^D
+$$
+e = mc^2
+$$
+```
+
+But we need to collapse spaces around inline math:
+
+```
+% pandoc -t markdown -f native
+[ Para [ Math InlineMath "\ne=mc\n" ]]
+^D
+$e=mc$
+```
diff --git a/test/command/11409.md b/test/command/11409.md
new file mode 100644
--- /dev/null
+++ b/test/command/11409.md
@@ -0,0 +1,11 @@
+```
+% pandoc -t native
+text^[sup]{.class}^
+^D
+[ Para
+    [ Str "text"
+    , Superscript
+        [ Span ( "" , [ "class" ] , [] ) [ Str "sup" ] ]
+    ]
+]
+```
diff --git a/test/command/11420.md b/test/command/11420.md
new file mode 100644
--- /dev/null
+++ b/test/command/11420.md
@@ -0,0 +1,15 @@
+````
+% pandoc -t revealjs --syntax-highlighting=idiomatic
+# Slide
+
+```python
+def hello():
+    print("Hello")
+```
+^D
+<section id="slide" class="slide level1">
+<h1>Slide</h1>
+<pre><code class="language-python">def hello():
+    print(&quot;Hello&quot;)</code></pre>
+</section>
+````
diff --git a/test/command/11422.md b/test/command/11422.md
new file mode 100644
--- /dev/null
+++ b/test/command/11422.md
@@ -0,0 +1,12 @@
+```
+% pandoc -f docbook -t markdown
+<example>
+    <para>
+      Example without a title.
+    </para>
+</example>
+^D
+::: example
+Example without a title.
+:::
+```
diff --git a/test/command/4186.md b/test/command/4186.md
--- a/test/command/4186.md
+++ b/test/command/4186.md
@@ -5,7 +5,7 @@
 #+end_example
 ^D
 [ CodeBlock
-    ( "" , [ "example" ] , [] )
+    ( "" , [] , [] )
     "    This should retain the four leading spaces\n"
 ]
 ```
@@ -40,13 +40,13 @@
 ^D
 <ul>
 <li><p>depth 1</p>
-<pre id="bob" class="example"><code>    Vertical alignment is four spaces beyond the appearance of the word &quot;depth&quot;.
+<pre id="bob"><code>    Vertical alignment is four spaces beyond the appearance of the word &quot;depth&quot;.
 </code></pre>
 <ul>
 <li><p>depth 2</p>
-<pre class="example"><code>Vertically aligned with the second appearance of the word &quot;depth&quot;.
+<pre><code>Vertically aligned with the second appearance of the word &quot;depth&quot;.
 </code></pre>
-<pre class="example"><code>    Vertical alignment is four spaces beyond the second
+<pre><code>    Vertical alignment is four spaces beyond the second
     appearance of the word &quot;depth&quot;.
     The &quot;begin&quot; portion is a component of
     this deeper list element, so that guarantees
@@ -55,7 +55,7 @@
 </code></pre>
 <p>Still inside the inner list element</p></li>
 </ul>
-<pre id="carrie" class="example"><code>This belongs to the outer list element, and is aligned accordingly, since the NAME attribute is not indented deeply enough. It is not enough for the BEGIN alone to be aligned deeply if the block is meant to have a NAME.
+<pre id="carrie"><code>This belongs to the outer list element, and is aligned accordingly, since the NAME attribute is not indented deeply enough. It is not enough for the BEGIN alone to be aligned deeply if the block is meant to have a NAME.
 </code></pre>
 <p>Still in the shallower list element since the preceding example block
 forced the deeper list element to terminate.</p></li>
diff --git a/test/command/6719.md b/test/command/6719.md
--- a/test/command/6719.md
+++ b/test/command/6719.md
@@ -4,7 +4,7 @@
 <emphasis>emphasized </emphasis>text
 </para>
 ^D
-[ Para [ Emph [ Str "emphasized" ] , Space , Str "text" ] ]
+[ Para [ Emph [ Str "emphasized" , Space ] , Str "text" ] ]
 ```
 
 ```
@@ -13,6 +13,6 @@
 <italic> hi  </italic>there
 </p>
 ^D
-[ Para [ Emph [ Str "hi" ] , Space , Str "there" ] ]
+[ Para [ Emph [ Space , Str "hi" , Space ] , Str "there" ] ]
 ```
 
diff --git a/test/command/7201.md b/test/command/7201.md
new file mode 100644
--- /dev/null
+++ b/test/command/7201.md
@@ -0,0 +1,47 @@
+```
+% pandoc -t revealjs --slide-level=1
+# Overview
+
+:::::::::::::: {.columns}
+::: {.column width="40%"}
+
+Left column
+
+:::
+::: {.column width="60%"}
+
+* First list
+    * subitem
+
+. . .
+
+* Second list
+    * subitem
+:::
+::::::::::::::
+^D
+<section id="overview" class="slide level1">
+<h1>Overview</h1>
+<div class="columns">
+<div class="column" style="width:40%;">
+<p>Left column</p>
+</div><div class="column" style="width:60%;">
+<ul>
+<li>First list
+<ul>
+<li>subitem</li>
+</ul></li>
+</ul>
+<div class="fragment">
+<ul>
+<li>Second list
+<ul>
+<li>subitem</li>
+</ul></li>
+</ul>
+</div>
+</div>
+</div>
+</section>
+
+```
diff --git a/test/command/7582.md b/test/command/7582.md
new file mode 100644
--- /dev/null
+++ b/test/command/7582.md
@@ -0,0 +1,28 @@
+```
+% pandoc -t revealjs
+# Slide
+
+::: columns
+:::: column
+Left column
+::::
+
+. . .
+
+:::: column
+Right column
+::::
+:::
+^D
+<section id="slide" class="slide level1">
+<h1>Slide</h1>
+<div class="columns">
+<div class="column">
+<p>Left column</p>
+</div><div class="fragment"><div class="column">
+<p>Right column</p>
+</div></div>
+</div>
+</section>
+
+```
diff --git a/test/command/8024.md b/test/command/8024.md
new file mode 100644
--- /dev/null
+++ b/test/command/8024.md
@@ -0,0 +1,10 @@
+```
+% pandoc -d command/8024a.yaml -t markdown
+Hello, if this works the text should be wrapped.
+^D
+Hello, if this
+works the text
+should be
+wrapped.
+
+```
diff --git a/test/command/8024a.yaml b/test/command/8024a.yaml
new file mode 100644
--- /dev/null
+++ b/test/command/8024a.yaml
@@ -0,0 +1,2 @@
+defaults: ${.}/8024b.yaml
+
diff --git a/test/command/8024b.yaml b/test/command/8024b.yaml
new file mode 100644
--- /dev/null
+++ b/test/command/8024b.yaml
@@ -0,0 +1,3 @@
+wrap: auto
+columns: 15
+
diff --git a/test/command/8665.md b/test/command/8665.md
--- a/test/command/8665.md
+++ b/test/command/8665.md
@@ -24,6 +24,6 @@
 [cols=",",options="header",]
 |===
 <|h1 <|h2
-<|!@#$%^&++*++()++{++}{vbar}~?{plus}-',."++<>[]\`++ <|col 2
+<|!@++#++$%^&++*++()++{++}{vbar}~?{plus}-',."++<>[]\`++ <|col 2
 |===
 ```
diff --git a/test/command/8783.md b/test/command/8783.md
new file mode 100644
--- /dev/null
+++ b/test/command/8783.md
@@ -0,0 +1,33 @@
+```
+% pandoc -f rtf -t native
+{\rtf1\ansi\ansicpg1252\cocoartf2867
+\cocoatextscaling0\cocoaplatform1{\fonttbl\f0\fnil\fcharset0 HelveticaNeue;}
+{\colortbl;\red255\green255\blue255;\red0\green0\blue0;}
+{\*\expandedcolortbl;;\cssrgb\c0\c0\c0;}
+{\*\listtable{\list\listtemplateid1\listhybrid{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{decimal\}.}{\leveltext\leveltemplateid1\'02\'00.;}{\levelnumbers\'01;}\fi-360\li720\lin720 }{\listname ;}\listid1}
+{\list\listtemplateid2\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{disc\}}{\leveltext\leveltemplateid101\'01\uc0\u8226 ;}{\levelnumbers;}\fi-360\li720\lin720 }{\listname ;}\listid2}}
+{\*\listoverridetable{\listoverride\listid1\listoverridecount0\ls1}{\listoverride\listid2\listoverridecount0\ls2}}
+\paperw11905\paperh16837\margl1133\margr1133\margb1133\margt1133
+\deftab720
+\pard\pardeftab720\partightenfactor0
+
+\f0\fs22 \cf2 \up0 \nosupersub \ulnone First paragraph\
+\pard\tx20\tx360\pardeftab720\li360\fi-360\partightenfactor0
+\ls1\ilvl0\cf2 \up0 \nosupersub \ulnone {\listtext	1.	}\cf2 \up0 \nosupersub \ulnone Numbered item\
+\pard\pardeftab720\partightenfactor0
+\cf2 Second paragraph\
+\pard\tx20\tx180\pardeftab720\li180\fi-180\partightenfactor0
+\ls2\ilvl0\cf2 \up0 \nosupersub \ulnone {\listtext	\uc0\u8226 	}\cf2 \up0 \nosupersub \ulnone Bullet item\
+\pard\pardeftab720\partightenfactor0
+\cf2 New paragraph}
+^D
+[ Para [ Str "First" , Space , Str "paragraph" ]
+, OrderedList
+    ( 1 , Decimal , Period )
+    [ [ Para [ Str "Numbered" , Space , Str "item" ] ] ]
+, Para [ Str "Second" , Space , Str "paragraph" ]
+, BulletList
+    [ [ Para [ Str "Bullet" , Space , Str "item" ] ] ]
+, Para [ Str "New" , Space , Str "paragraph" ]
+]
+```
diff --git a/test/command/8966.md b/test/command/8966.md
--- a/test/command/8966.md
+++ b/test/command/8966.md
@@ -3,7 +3,7 @@
 **Samsonov T.E.** Shape-Adaptive Geometric Simplification of Heterogeneous Line Datasets / T. E. Samsonov, O. P. Yakimova // International Journal of Geographical Information Science. — 2017. — Vol. 31. — № 8. — pp. 1485-1520.
 ^D
 #strong[Samsonov T.E.] Shape-Adaptive Geometric Simplification of
-Heterogeneous Line Datasets / T. E. Samsonov, O. P. Yakimova \/\/
+Heterogeneous Line Datasets / T. E. Samsonov, O. P. Yakimova /\/
 International Journal of Geographical Information Science. --- 2017. ---
 Vol. 31. --- № 8. --- pp.~1485-1520.
 ```
diff --git a/test/command/8997.md b/test/command/8997.md
--- a/test/command/8997.md
+++ b/test/command/8997.md
@@ -24,7 +24,7 @@
     , Space
     , Str "colon"
     ]
-, CodeBlock ( "" , [ "example" ] , [] ) "aaa\n\nbbb\n"
+, CodeBlock ( "" , [] , [] ) "aaa\n\nbbb\n"
 , Header
     1
     ( "has-no-space-after-second-colon" , [] , [] )
@@ -40,7 +40,7 @@
     , Space
     , Str "colon"
     ]
-, CodeBlock ( "" , [ "example" ] , [] ) "aaa\n\nbbb\n"
+, CodeBlock ( "" , [] , [] ) "aaa\n\nbbb\n"
 ]
 ```
 
@@ -56,7 +56,7 @@
     1
     ( "only-the-colon" , [] , [] )
     [ Str "only" , Space , Str "the" , Space , Str "colon" ]
-, CodeBlock ( "" , [ "example" ] , [] ) "\n"
+, CodeBlock ( "" , [] , [] ) "\n"
 , Header
     1
     ( "only-the-colon-and-a-space" , [] , [] )
@@ -72,6 +72,6 @@
     , Space
     , Str "space"
     ]
-, CodeBlock ( "" , [ "example" ] , [] ) "\n"
+, CodeBlock ( "" , [] , [] ) "\n"
 ]
 ```
diff --git a/test/command/9716.md b/test/command/9716.md
new file mode 100644
--- /dev/null
+++ b/test/command/9716.md
@@ -0,0 +1,13 @@
+```
+% pandoc -f markdown+alerts -t native
+> [!TIP]
+> A suggestion.
+^D
+[ Div
+    ( "" , [ "tip" ] , [] )
+    [ Div ( "" , [ "title" ] , [] ) [ Para [ Str "Tip" ] ]
+    , Para [ Str "A" , Space , Str "suggestion." ]
+    ]
+]
+
+```
diff --git a/test/command/figures-mediawiki.md b/test/command/figures-mediawiki.md
--- a/test/command/figures-mediawiki.md
+++ b/test/command/figures-mediawiki.md
@@ -6,9 +6,6 @@
 
 ^D
 <div id="fig-id" class="figure">
-
 [[File:foo.png|thumb|none]]
-
-
 </div>
 ```
diff --git a/test/command/mediawiki_behavior_switches.md b/test/command/mediawiki_behavior_switches.md
new file mode 100644
--- /dev/null
+++ b/test/command/mediawiki_behavior_switches.md
@@ -0,0 +1,16 @@
+```
+% pandoc -f mediawiki -t native
+____INDEX____
+__NOINDEX__
+__noindex__
+__FOOBAR__
+^D
+[ Para
+    [ Str "____"
+    , SoftBreak
+    , Str "__noindex__"
+    , SoftBreak
+    , Str "__FOOBAR__"
+    ]
+]
+```
diff --git a/test/command/pdfstandard.md b/test/command/pdfstandard.md
new file mode 100644
--- /dev/null
+++ b/test/command/pdfstandard.md
@@ -0,0 +1,588 @@
+PDF standard support: basic PDF/A-2b test (infers version 1.7)
+```
+% pandoc -t latex -s
+---
+pdfstandard: a-2b
+lang: en-US
+---
+
+Test document.
+^D
+\DocumentMetadata{
+  pdfversion=1.7,
+  pdfstandard={a-2b},
+  lang=en-US,
+  xmp=true}
+% Options for packages loaded elsewhere
+\PassOptionsToPackage{unicode}{hyperref}
+\PassOptionsToPackage{hyphens}{url}
+\documentclass[
+  american,
+]{article}
+\usepackage{xcolor}
+\usepackage{amsmath,amssymb}
+\setcounter{secnumdepth}{-\maxdimen} % remove section numbering
+\usepackage{iftex}
+\ifPDFTeX
+  \usepackage[T1]{fontenc}
+  \usepackage[utf8]{inputenc}
+  \usepackage{textcomp} % provide euro and other symbols
+\else % if luatex or xetex
+  \usepackage{unicode-math} % this also loads fontspec
+  \defaultfontfeatures{Scale=MatchLowercase}
+  \defaultfontfeatures[\rmfamily]{Ligatures=TeX,Scale=1}
+\fi
+\usepackage{lmodern}
+\ifPDFTeX\else
+  % xetex/luatex font selection
+\fi
+% Use upquote if available, for straight quotes in verbatim environments
+\IfFileExists{upquote.sty}{\usepackage{upquote}}{}
+\IfFileExists{microtype.sty}{% use microtype if available
+  \usepackage[]{microtype}
+  \UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts
+}{}
+\makeatletter
+\@ifundefined{KOMAClassName}{% if non-KOMA class
+  \IfFileExists{parskip.sty}{%
+    \usepackage{parskip}
+  }{% else
+    \setlength{\parindent}{0pt}
+    \setlength{\parskip}{6pt plus 2pt minus 1pt}}
+}{% if KOMA class
+  \KOMAoptions{parskip=half}}
+\makeatother
+\ifLuaTeX
+\usepackage[bidi=basic,shorthands=off]{babel}
+\else
+\usepackage[bidi=default,shorthands=off]{babel}
+\fi
+\ifLuaTeX
+  \usepackage{selnolig} % disable illegal ligatures
+\fi
+\setlength{\emergencystretch}{3em} % prevent overfull lines
+\providecommand{\tightlist}{%
+  \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}}
+\usepackage{bookmark}
+\IfFileExists{xurl.sty}{\usepackage{xurl}}{} % add URL line breaks if available
+\urlstyle{same}
+\hypersetup{
+  pdflang={en-US},
+  hidelinks,
+  pdfcreator={LaTeX via pandoc}}
+
+\author{}
+\date{}
+
+\begin{document}
+
+Test document.
+
+\end{document}
+```
+
+PDF standard support: PDF/UA-1 with tagging
+```
+% pandoc -t latex -s
+---
+pdfstandard: ua-1
+lang: en-US
+---
+
+Accessible document.
+^D
+\DocumentMetadata{
+  pdfstandard={ua-1},
+  tagging=on,
+  lang=en-US,
+  xmp=true}
+% Options for packages loaded elsewhere
+\PassOptionsToPackage{unicode}{hyperref}
+\PassOptionsToPackage{hyphens}{url}
+\documentclass[
+  american,
+]{article}
+\usepackage{xcolor}
+\usepackage{amsmath,amssymb}
+\setcounter{secnumdepth}{-\maxdimen} % remove section numbering
+\usepackage{iftex}
+\ifPDFTeX
+  \usepackage[T1]{fontenc}
+  \usepackage[utf8]{inputenc}
+  \usepackage{textcomp} % provide euro and other symbols
+\else % if luatex or xetex
+  \usepackage{unicode-math} % this also loads fontspec
+  \defaultfontfeatures{Scale=MatchLowercase}
+  \defaultfontfeatures[\rmfamily]{Ligatures=TeX,Scale=1}
+\fi
+\usepackage{lmodern}
+\ifPDFTeX\else
+  % xetex/luatex font selection
+\fi
+% Use upquote if available, for straight quotes in verbatim environments
+\IfFileExists{upquote.sty}{\usepackage{upquote}}{}
+\IfFileExists{microtype.sty}{% use microtype if available
+  \usepackage[]{microtype}
+  \UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts
+}{}
+\makeatletter
+\@ifundefined{KOMAClassName}{% if non-KOMA class
+  \IfFileExists{parskip.sty}{%
+    \usepackage{parskip}
+  }{% else
+    \setlength{\parindent}{0pt}
+    \setlength{\parskip}{6pt plus 2pt minus 1pt}}
+}{% if KOMA class
+  \KOMAoptions{parskip=half}}
+\makeatother
+\ifLuaTeX
+\usepackage[bidi=basic,shorthands=off]{babel}
+\else
+\usepackage[bidi=default,shorthands=off]{babel}
+\fi
+\ifLuaTeX
+  \usepackage{selnolig} % disable illegal ligatures
+\fi
+\setlength{\emergencystretch}{3em} % prevent overfull lines
+\providecommand{\tightlist}{%
+  \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}}
+\usepackage{bookmark}
+\IfFileExists{xurl.sty}{\usepackage{xurl}}{} % add URL line breaks if available
+\urlstyle{same}
+\hypersetup{
+  pdflang={en-US},
+  hidelinks,
+  pdfcreator={LaTeX via pandoc}}
+
+\author{}
+\date{}
+
+\begin{document}
+
+Accessible document.
+
+\end{document}
+```
+
+PDF standard support: multiple standards with version
+```
+% pandoc -t latex -s
+---
+pdfstandard:
+  - a-2b
+  - ua-1
+  - "1.7"
+lang: de-DE
+---
+
+Multi-standard document.
+^D
+\DocumentMetadata{
+  pdfversion=1.7,
+  pdfstandard={a-2b,ua-1},
+  tagging=on,
+  lang=de-DE,
+  xmp=true}
+% Options for packages loaded elsewhere
+\PassOptionsToPackage{unicode}{hyperref}
+\PassOptionsToPackage{hyphens}{url}
+\documentclass[
+  ngerman,
+]{article}
+\usepackage{xcolor}
+\usepackage{amsmath,amssymb}
+\setcounter{secnumdepth}{-\maxdimen} % remove section numbering
+\usepackage{iftex}
+\ifPDFTeX
+  \usepackage[T1]{fontenc}
+  \usepackage[utf8]{inputenc}
+  \usepackage{textcomp} % provide euro and other symbols
+\else % if luatex or xetex
+  \usepackage{unicode-math} % this also loads fontspec
+  \defaultfontfeatures{Scale=MatchLowercase}
+  \defaultfontfeatures[\rmfamily]{Ligatures=TeX,Scale=1}
+\fi
+\usepackage{lmodern}
+\ifPDFTeX\else
+  % xetex/luatex font selection
+\fi
+% Use upquote if available, for straight quotes in verbatim environments
+\IfFileExists{upquote.sty}{\usepackage{upquote}}{}
+\IfFileExists{microtype.sty}{% use microtype if available
+  \usepackage[]{microtype}
+  \UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts
+}{}
+\makeatletter
+\@ifundefined{KOMAClassName}{% if non-KOMA class
+  \IfFileExists{parskip.sty}{%
+    \usepackage{parskip}
+  }{% else
+    \setlength{\parindent}{0pt}
+    \setlength{\parskip}{6pt plus 2pt minus 1pt}}
+}{% if KOMA class
+  \KOMAoptions{parskip=half}}
+\makeatother
+\ifLuaTeX
+\usepackage[bidi=basic,shorthands=off]{babel}
+\else
+\usepackage[bidi=default,shorthands=off]{babel}
+\fi
+\ifLuaTeX
+  \usepackage{selnolig} % disable illegal ligatures
+\fi
+\setlength{\emergencystretch}{3em} % prevent overfull lines
+\providecommand{\tightlist}{%
+  \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}}
+\usepackage{bookmark}
+\IfFileExists{xurl.sty}{\usepackage{xurl}}{} % add URL line breaks if available
+\urlstyle{same}
+\hypersetup{
+  pdflang={de-DE},
+  hidelinks,
+  pdfcreator={LaTeX via pandoc}}
+
+\author{}
+\date{}
+
+\begin{document}
+
+Multi-standard document.
+
+\end{document}
+```
+
+PDF standard support: PDF/A-1b infers version 1.4
+```
+% pandoc -t latex -s
+---
+pdfstandard: a-1b
+lang: en-US
+---
+
+PDF/A-1 document.
+^D
+\DocumentMetadata{
+  pdfversion=1.4,
+  pdfstandard={a-1b},
+  lang=en-US,
+  xmp=true}
+% Options for packages loaded elsewhere
+\PassOptionsToPackage{unicode}{hyperref}
+\PassOptionsToPackage{hyphens}{url}
+\documentclass[
+  american,
+]{article}
+\usepackage{xcolor}
+\usepackage{amsmath,amssymb}
+\setcounter{secnumdepth}{-\maxdimen} % remove section numbering
+\usepackage{iftex}
+\ifPDFTeX
+  \usepackage[T1]{fontenc}
+  \usepackage[utf8]{inputenc}
+  \usepackage{textcomp} % provide euro and other symbols
+\else % if luatex or xetex
+  \usepackage{unicode-math} % this also loads fontspec
+  \defaultfontfeatures{Scale=MatchLowercase}
+  \defaultfontfeatures[\rmfamily]{Ligatures=TeX,Scale=1}
+\fi
+\usepackage{lmodern}
+\ifPDFTeX\else
+  % xetex/luatex font selection
+\fi
+% Use upquote if available, for straight quotes in verbatim environments
+\IfFileExists{upquote.sty}{\usepackage{upquote}}{}
+\IfFileExists{microtype.sty}{% use microtype if available
+  \usepackage[]{microtype}
+  \UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts
+}{}
+\makeatletter
+\@ifundefined{KOMAClassName}{% if non-KOMA class
+  \IfFileExists{parskip.sty}{%
+    \usepackage{parskip}
+  }{% else
+    \setlength{\parindent}{0pt}
+    \setlength{\parskip}{6pt plus 2pt minus 1pt}}
+}{% if KOMA class
+  \KOMAoptions{parskip=half}}
+\makeatother
+\ifLuaTeX
+\usepackage[bidi=basic,shorthands=off]{babel}
+\else
+\usepackage[bidi=default,shorthands=off]{babel}
+\fi
+\ifLuaTeX
+  \usepackage{selnolig} % disable illegal ligatures
+\fi
+\setlength{\emergencystretch}{3em} % prevent overfull lines
+\providecommand{\tightlist}{%
+  \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}}
+\usepackage{bookmark}
+\IfFileExists{xurl.sty}{\usepackage{xurl}}{} % add URL line breaks if available
+\urlstyle{same}
+\hypersetup{
+  pdflang={en-US},
+  hidelinks,
+  pdfcreator={LaTeX via pandoc}}
+
+\author{}
+\date{}
+
+\begin{document}
+
+PDF/A-1 document.
+
+\end{document}
+```
+
+PDF standard support: PDF/A-4 with no version inference (uses default 2.0)
+```
+% pandoc -t latex -s
+---
+pdfstandard: a-4
+lang: en-US
+---
+
+PDF/A-4 document.
+^D
+\DocumentMetadata{
+  pdfstandard={a-4},
+  lang=en-US,
+  xmp=true}
+% Options for packages loaded elsewhere
+\PassOptionsToPackage{unicode}{hyperref}
+\PassOptionsToPackage{hyphens}{url}
+\documentclass[
+  american,
+]{article}
+\usepackage{xcolor}
+\usepackage{amsmath,amssymb}
+\setcounter{secnumdepth}{-\maxdimen} % remove section numbering
+\usepackage{iftex}
+\ifPDFTeX
+  \usepackage[T1]{fontenc}
+  \usepackage[utf8]{inputenc}
+  \usepackage{textcomp} % provide euro and other symbols
+\else % if luatex or xetex
+  \usepackage{unicode-math} % this also loads fontspec
+  \defaultfontfeatures{Scale=MatchLowercase}
+  \defaultfontfeatures[\rmfamily]{Ligatures=TeX,Scale=1}
+\fi
+\usepackage{lmodern}
+\ifPDFTeX\else
+  % xetex/luatex font selection
+\fi
+% Use upquote if available, for straight quotes in verbatim environments
+\IfFileExists{upquote.sty}{\usepackage{upquote}}{}
+\IfFileExists{microtype.sty}{% use microtype if available
+  \usepackage[]{microtype}
+  \UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts
+}{}
+\makeatletter
+\@ifundefined{KOMAClassName}{% if non-KOMA class
+  \IfFileExists{parskip.sty}{%
+    \usepackage{parskip}
+  }{% else
+    \setlength{\parindent}{0pt}
+    \setlength{\parskip}{6pt plus 2pt minus 1pt}}
+}{% if KOMA class
+  \KOMAoptions{parskip=half}}
+\makeatother
+\ifLuaTeX
+\usepackage[bidi=basic,shorthands=off]{babel}
+\else
+\usepackage[bidi=default,shorthands=off]{babel}
+\fi
+\ifLuaTeX
+  \usepackage{selnolig} % disable illegal ligatures
+\fi
+\setlength{\emergencystretch}{3em} % prevent overfull lines
+\providecommand{\tightlist}{%
+  \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}}
+\usepackage{bookmark}
+\IfFileExists{xurl.sty}{\usepackage{xurl}}{} % add URL line breaks if available
+\urlstyle{same}
+\hypersetup{
+  pdflang={en-US},
+  hidelinks,
+  pdfcreator={LaTeX via pandoc}}
+
+\author{}
+\date{}
+
+\begin{document}
+
+PDF/A-4 document.
+
+\end{document}
+```
+
+PDF standard support: numeric YAML version (2 becomes 2.0)
+```
+% pandoc -t latex -s
+---
+pdfstandard:
+  - ua-1
+  - 2
+lang: en-US
+---
+
+PDF 2.0 document.
+^D
+\DocumentMetadata{
+  pdfversion=2.0,
+  pdfstandard={ua-1},
+  tagging=on,
+  lang=en-US,
+  xmp=true}
+% Options for packages loaded elsewhere
+\PassOptionsToPackage{unicode}{hyperref}
+\PassOptionsToPackage{hyphens}{url}
+\documentclass[
+  american,
+]{article}
+\usepackage{xcolor}
+\usepackage{amsmath,amssymb}
+\setcounter{secnumdepth}{-\maxdimen} % remove section numbering
+\usepackage{iftex}
+\ifPDFTeX
+  \usepackage[T1]{fontenc}
+  \usepackage[utf8]{inputenc}
+  \usepackage{textcomp} % provide euro and other symbols
+\else % if luatex or xetex
+  \usepackage{unicode-math} % this also loads fontspec
+  \defaultfontfeatures{Scale=MatchLowercase}
+  \defaultfontfeatures[\rmfamily]{Ligatures=TeX,Scale=1}
+\fi
+\usepackage{lmodern}
+\ifPDFTeX\else
+  % xetex/luatex font selection
+\fi
+% Use upquote if available, for straight quotes in verbatim environments
+\IfFileExists{upquote.sty}{\usepackage{upquote}}{}
+\IfFileExists{microtype.sty}{% use microtype if available
+  \usepackage[]{microtype}
+  \UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts
+}{}
+\makeatletter
+\@ifundefined{KOMAClassName}{% if non-KOMA class
+  \IfFileExists{parskip.sty}{%
+    \usepackage{parskip}
+  }{% else
+    \setlength{\parindent}{0pt}
+    \setlength{\parskip}{6pt plus 2pt minus 1pt}}
+}{% if KOMA class
+  \KOMAoptions{parskip=half}}
+\makeatother
+\ifLuaTeX
+\usepackage[bidi=basic,shorthands=off]{babel}
+\else
+\usepackage[bidi=default,shorthands=off]{babel}
+\fi
+\ifLuaTeX
+  \usepackage{selnolig} % disable illegal ligatures
+\fi
+\setlength{\emergencystretch}{3em} % prevent overfull lines
+\providecommand{\tightlist}{%
+  \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}}
+\usepackage{bookmark}
+\IfFileExists{xurl.sty}{\usepackage{xurl}}{} % add URL line breaks if available
+\urlstyle{same}
+\hypersetup{
+  pdflang={en-US},
+  hidelinks,
+  pdfcreator={LaTeX via pandoc}}
+
+\author{}
+\date{}
+
+\begin{document}
+
+PDF 2.0 document.
+
+\end{document}
+```
+
+PDF standard support: explicit version overrides inferred version
+```
+% pandoc -t latex -s
+---
+pdfstandard:
+  - a-2b
+  - "1.5"
+lang: en-US
+---
+
+Explicit version document.
+^D
+\DocumentMetadata{
+  pdfversion=1.5,
+  pdfstandard={a-2b},
+  lang=en-US,
+  xmp=true}
+% Options for packages loaded elsewhere
+\PassOptionsToPackage{unicode}{hyperref}
+\PassOptionsToPackage{hyphens}{url}
+\documentclass[
+  american,
+]{article}
+\usepackage{xcolor}
+\usepackage{amsmath,amssymb}
+\setcounter{secnumdepth}{-\maxdimen} % remove section numbering
+\usepackage{iftex}
+\ifPDFTeX
+  \usepackage[T1]{fontenc}
+  \usepackage[utf8]{inputenc}
+  \usepackage{textcomp} % provide euro and other symbols
+\else % if luatex or xetex
+  \usepackage{unicode-math} % this also loads fontspec
+  \defaultfontfeatures{Scale=MatchLowercase}
+  \defaultfontfeatures[\rmfamily]{Ligatures=TeX,Scale=1}
+\fi
+\usepackage{lmodern}
+\ifPDFTeX\else
+  % xetex/luatex font selection
+\fi
+% Use upquote if available, for straight quotes in verbatim environments
+\IfFileExists{upquote.sty}{\usepackage{upquote}}{}
+\IfFileExists{microtype.sty}{% use microtype if available
+  \usepackage[]{microtype}
+  \UseMicrotypeSet[protrusion]{basicmath} % disable protrusion for tt fonts
+}{}
+\makeatletter
+\@ifundefined{KOMAClassName}{% if non-KOMA class
+  \IfFileExists{parskip.sty}{%
+    \usepackage{parskip}
+  }{% else
+    \setlength{\parindent}{0pt}
+    \setlength{\parskip}{6pt plus 2pt minus 1pt}}
+}{% if KOMA class
+  \KOMAoptions{parskip=half}}
+\makeatother
+\ifLuaTeX
+\usepackage[bidi=basic,shorthands=off]{babel}
+\else
+\usepackage[bidi=default,shorthands=off]{babel}
+\fi
+\ifLuaTeX
+  \usepackage{selnolig} % disable illegal ligatures
+\fi
+\setlength{\emergencystretch}{3em} % prevent overfull lines
+\providecommand{\tightlist}{%
+  \setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}}
+\usepackage{bookmark}
+\IfFileExists{xurl.sty}{\usepackage{xurl}}{} % add URL line breaks if available
+\urlstyle{same}
+\hypersetup{
+  pdflang={en-US},
+  hidelinks,
+  pdfcreator={LaTeX via pandoc}}
+
+\author{}
+\date{}
+
+\begin{document}
+
+Explicit version document.
+
+\end{document}
+```
diff --git a/test/command/reset-citation-positions.md b/test/command/reset-citation-positions.md
new file mode 100644
--- /dev/null
+++ b/test/command/reset-citation-positions.md
@@ -0,0 +1,41 @@
+```
+% pandoc --citeproc -t plain --csl command/chicago-fullnote-bibliography.csl
+---
+suppress-bibliography: true
+references:
+- id: foo
+  name: John doe
+  title: A Book
+  type: book
+  publisher: Oxford University Press
+  issued: 2010
+...
+
+# Chapter one
+
+Blah [@foo, p. 7].
+
+Blah [@foo, p. 8].
+
+# Chapter two {.reset-citation-positions}
+
+Blah [@foo, p. 57].
+^D
+Chapter one
+
+Blah.[1]
+
+Blah.[2]
+
+Chapter two
+
+Blah.[3]
+
+[1] A Book (Oxford University Press, 2010), 7.
+
+[2] A Book, 8.
+
+[3] A Book (Oxford University Press, 2010), 57.
+
+```
+
diff --git a/test/docbook-reader.docbook b/test/docbook-reader.docbook
--- a/test/docbook-reader.docbook
+++ b/test/docbook-reader.docbook
@@ -27,9 +27,9 @@
   This is a set of tests for pandoc. Most of them are adapted from John
   Gruber’s markdown test suite.
 </para>
-<sect1 id="headers">
+<sect1 id="headers" role="sect1role">
   <title>Headers</title>
-  <sect2 id="level-2-with-an-embedded-link">
+  <sect2 id="level-2-with-an-embedded-link" role="sect2role">
     <title>Level 2 with an <ulink url="/url">embedded link</ulink></title>
     <sect3 id="level-3-with-emphasis">
       <title>Level 3 with <emphasis>emphasis</emphasis></title>
@@ -74,6 +74,9 @@
   <para>
     Here’s a regular paragraph.
   </para>
+  <para role="pararole">
+    And here’s a regular paragraph with a role.
+  </para>
   <para>
     In Markdown 1.0.0 and earlier. Version 8. This line turns into a list
     item. Because a hard-wrapped line in the middle of a paragraph looked like
@@ -93,6 +96,11 @@
       This is a block quote. It is pretty short.
     </para>
   </blockquote>
+  <blockquote role="roleblockquote">
+    <para>
+      This is a block quote with a role.
+    </para>
+  </blockquote>
   <blockquote>
     <para>
       Code in a block quote:
@@ -234,6 +242,26 @@
       </listitem>
     </orderedlist>
     <para>
+      with role:
+    </para>
+    <orderedlist role="listrole" numeration="arabic">
+      <listitem>
+        <para>
+          First
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          Second
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          Third
+        </para>
+      </listitem>
+    </orderedlist>
+    <para>
       and tight:
     </para>
     <orderedlist spacing="compact" numeration="arabic">
@@ -703,6 +731,12 @@
     So is <emphasis role="strong"><emphasis>this</emphasis></emphasis> word.
   </para>
   <para>
+    So is <emphasis role="emphasisrole"><emphasis>this</emphasis></emphasis> word with a role.
+  </para>
+  <para>
+    So is <phrase role="phraserole"><phrase>this</phrase></phrase> phrase with a role.
+  </para>
+  <para>
     This is code: <literal>&gt;</literal>, <literal>$</literal>,
     <literal>\</literal>, <literal>\$</literal>,
     <literal>&lt;html&gt;</literal>.
@@ -1408,7 +1442,7 @@
   <para>
     Table with attributes
   </para>
-  <table xml:id="mytableid1" class="mytableclass1 mytableclass2" tabstyle="mytabstyle1">
+  <table xml:id="mytableid1" class="mytableclass1 mytableclass2" tabstyle="mytabstyle1" role="tablerole1">
     <title>
       Attribute table caption
     </title>
@@ -1444,7 +1478,7 @@
   <para>
     Table with attributes, without caption
   </para>
-  <informaltable xml:id="mytableid2" class="mytableclass3 mytableclass4" tabstyle="mytabstyle2">
+  <informaltable xml:id="mytableid2" class="mytableclass3 mytableclass4" tabstyle="mytabstyle2" role="tablerole2">
     <tgroup>
       <thead>
 	<th>
diff --git a/test/docbook-reader.native b/test/docbook-reader.native
--- a/test/docbook-reader.native
+++ b/test/docbook-reader.native
@@ -62,10 +62,16 @@
       , Space
       , Str "suite."
       ]
-  , Header 1 ( "headers" , [] , [] ) [ Str "Headers" ]
   , Header
+      1
+      ( "headers" , [] , [ ( "role" , "sect1role" ) ] )
+      [ Str "Headers" ]
+  , Header
       2
-      ( "level-2-with-an-embedded-link" , [] , [] )
+      ( "level-2-with-an-embedded-link"
+      , []
+      , [ ( "role" , "sect2role" ) ]
+      )
       [ Str "Level"
       , Space
       , Str "2"
@@ -151,6 +157,29 @@
       , Space
       , Str "paragraph."
       ]
+  , Div
+      ( ""
+      , []
+      , [ ( "wrapper" , "1" ) , ( "role" , "pararole" ) ]
+      )
+      [ Para
+          [ Str "And"
+          , Space
+          , Str "here\8217s"
+          , Space
+          , Str "a"
+          , Space
+          , Str "regular"
+          , Space
+          , Str "paragraph"
+          , Space
+          , Str "with"
+          , Space
+          , Str "a"
+          , Space
+          , Str "role."
+          ]
+      ]
   , Para
       [ Str "In"
       , Space
@@ -251,6 +280,31 @@
           , Str "short."
           ]
       ]
+  , Div
+      ( ""
+      , []
+      , [ ( "wrapper" , "1" ) , ( "role" , "roleblockquote" ) ]
+      )
+      [ BlockQuote
+          [ Para
+              [ Str "This"
+              , Space
+              , Str "is"
+              , Space
+              , Str "a"
+              , Space
+              , Str "block"
+              , Space
+              , Str "quote"
+              , Space
+              , Str "with"
+              , Space
+              , Str "a"
+              , Space
+              , Str "role."
+              ]
+          ]
+      ]
   , BlockQuote
       [ Para
           [ Str "Code"
@@ -348,6 +402,19 @@
       , [ Para [ Str "Second" ] ]
       , [ Para [ Str "Third" ] ]
       ]
+  , Para [ Str "with" , Space , Str "role:" ]
+  , Div
+      ( ""
+      , []
+      , [ ( "wrapper" , "1" ) , ( "role" , "listrole" ) ]
+      )
+      [ OrderedList
+          ( 1 , Decimal , DefaultDelim )
+          [ [ Para [ Str "First" ] ]
+          , [ Para [ Str "Second" ] ]
+          , [ Para [ Str "Third" ] ]
+          ]
+      ]
   , Para [ Str "and" , Space , Str "tight:" ]
   , OrderedList
       ( 1 , Decimal , DefaultDelim )
@@ -932,6 +999,38 @@
       , Str "word."
       ]
   , Para
+      [ Str "So"
+      , Space
+      , Str "is"
+      , Space
+      , Emph [ Emph [ Str "this" ] ]
+      , Space
+      , Str "word"
+      , Space
+      , Str "with"
+      , Space
+      , Str "a"
+      , Space
+      , Str "role."
+      ]
+  , Para
+      [ Str "So"
+      , Space
+      , Str "is"
+      , Space
+      , Span
+          ( "" , [ "phraserole" ] , [ ( "role" , "phraserole" ) ] )
+          [ Str "this" ]
+      , Space
+      , Str "phrase"
+      , Space
+      , Str "with"
+      , Space
+      , Str "a"
+      , Space
+      , Str "role."
+      ]
+  , Para
       [ Str "This"
       , Space
       , Str "is"
@@ -2570,7 +2669,9 @@
   , Table
       ( "mytableid1"
       , [ "mytableclass1" , "mytableclass2" ]
-      , [ ( "custom-style" , "mytabstyle1" ) ]
+      , [ ( "role" , "tablerole1" )
+        , ( "custom-style" , "mytabstyle1" )
+        ]
       )
       (Caption
          Nothing
@@ -2636,7 +2737,9 @@
   , Table
       ( "mytableid2"
       , [ "mytableclass3" , "mytableclass4" ]
-      , [ ( "custom-style" , "mytabstyle2" ) ]
+      , [ ( "role" , "tablerole2" )
+        , ( "custom-style" , "mytabstyle2" )
+        ]
       )
       (Caption Nothing [])
       [ ( AlignDefault , ColWidthDefault )
diff --git a/test/docx/german-reference.docx b/test/docx/german-reference.docx
new file mode 100644
Binary files /dev/null and b/test/docx/german-reference.docx differ
diff --git a/test/odt/native/tableWithSpans.native b/test/odt/native/tableWithSpans.native
new file mode 100644
--- /dev/null
+++ b/test/odt/native/tableWithSpans.native
@@ -0,0 +1,134 @@
+[ Table
+    ( "" , [] , [] )
+    (Caption Nothing [])
+    [ ( AlignDefault , ColWidthDefault )
+    , ( AlignDefault , ColWidthDefault )
+    , ( AlignDefault , ColWidthDefault )
+    ]
+    (TableHead
+       ( "" , [] , [] )
+       [ Row
+           ( "" , [] , [] )
+           [ Cell
+               ( "" , [] , [] )
+               AlignDefault
+               (RowSpan 2)
+               (ColSpan 1)
+               [ Plain
+                   [ Str "H1"
+                   , Space
+                   , Str "Rowspan"
+                   , Space
+                   , Str "2"
+                   ]
+               ]
+           , Cell
+               ( "" , [] , [] )
+               AlignDefault
+               (RowSpan 1)
+               (ColSpan 1)
+               [ Plain [ Str "H1-2" ] ]
+           , Cell
+               ( "" , [] , [] )
+               AlignDefault
+               (RowSpan 1)
+               (ColSpan 1)
+               [ Plain [ Str "H1-3" ] ]
+           ]
+       , Row
+           ( "" , [] , [] )
+           [ Cell
+               ( "" , [] , [] )
+               AlignDefault
+               (RowSpan 1)
+               (ColSpan 2)
+               [ Plain [ Str "H2-2/3" ] ]
+           ]
+       ])
+    [ TableBody
+        ( "" , [] , [] )
+        (RowHeadColumns 0)
+        []
+        [ Row
+            ( "" , [] , [] )
+            [ Cell
+                ( "" , [] , [] )
+                AlignDefault
+                (RowSpan 1)
+                (ColSpan 1)
+                [ Plain [ Str "B1-1" ] ]
+            , Cell
+                ( "" , [] , [] )
+                AlignDefault
+                (RowSpan 1)
+                (ColSpan 1)
+                [ Plain [ Str "B1-2" ] ]
+            , Cell
+                ( "" , [] , [] )
+                AlignDefault
+                (RowSpan 3)
+                (ColSpan 1)
+                [ Plain [ Str "Rowspan" , Space , Str "3" ] ]
+            ]
+        , Row
+            ( "" , [] , [] )
+            [ Cell
+                ( "" , [] , [] )
+                AlignDefault
+                (RowSpan 1)
+                (ColSpan 1)
+                [ Plain [ Str "B2-1" ] ]
+            , Cell
+                ( "" , [] , [] )
+                AlignDefault
+                (RowSpan 1)
+                (ColSpan 1)
+                [ Plain [ Str "B2-2" ] ]
+            ]
+        , Row
+            ( "" , [] , [] )
+            [ Cell
+                ( "" , [] , [] )
+                AlignDefault
+                (RowSpan 1)
+                (ColSpan 2)
+                [ Plain [ Str "Columnspan" , Space , Str "2" ] ]
+            ]
+        , Row
+            ( "" , [] , [] )
+            [ Cell
+                ( "" , [] , [] )
+                AlignDefault
+                (RowSpan 1)
+                (ColSpan 1)
+                [ Plain [ Str "B4-1" ] ]
+            , Cell
+                ( "" , [] , [] )
+                AlignDefault
+                (RowSpan 2)
+                (ColSpan 2)
+                [ Plain
+                    [ Str "Columnspan"
+                    , Space
+                    , Str "&"
+                    , Space
+                    , Str "Rowspan"
+                    , Space
+                    , Str "2"
+                    ]
+                ]
+            ]
+        , Row
+            ( "" , [] , [] )
+            [ Cell
+                ( "" , [] , [] )
+                AlignDefault
+                (RowSpan 1)
+                (ColSpan 1)
+                [ Plain [ Str "B5-1" ] ]
+            ]
+        ]
+    ]
+    (TableFoot ( "" , [] , [] ) [])
+, Para []
+]
diff --git a/test/odt/odt/tableWithSpans.odt b/test/odt/odt/tableWithSpans.odt
new file mode 100644
Binary files /dev/null and b/test/odt/odt/tableWithSpans.odt differ
diff --git a/test/pptx/metadata-speaker-notes/input.native b/test/pptx/metadata-speaker-notes/input.native
new file mode 100644
--- /dev/null
+++ b/test/pptx/metadata-speaker-notes/input.native
@@ -0,0 +1,3 @@
+Pandoc (Meta {unMeta = fromList [("author",MetaInlines [Str "Jesse",Space,Str "Rosenthal"]),("notes",MetaBlocks [Para [Str "These",Space,Str "are",Space,Str "speaker",Space,Str "notes",Space,Str "from",Space,Str "metadata."]]),("title",MetaInlines [Str "Testing"])]})
+[Header 1 ("a-header",[],[]) [Str "A",Space,Str "header"]
+,Para [Str "And",Space,Str "a",Space,Str "new",Space,Str "slide."]]
diff --git a/test/pptx/metadata-speaker-notes/output.pptx b/test/pptx/metadata-speaker-notes/output.pptx
new file mode 100644
Binary files /dev/null and b/test/pptx/metadata-speaker-notes/output.pptx differ
diff --git a/test/pptx/metadata-speaker-notes/templated.pptx b/test/pptx/metadata-speaker-notes/templated.pptx
new file mode 100644
Binary files /dev/null and b/test/pptx/metadata-speaker-notes/templated.pptx differ
diff --git a/test/tables/nordics.markdown b/test/tables/nordics.markdown
--- a/test/tables/nordics.markdown
+++ b/test/tables/nordics.markdown
@@ -1,18 +1,18 @@
-  -------------------------------------------------------------------------------
-           Name           Capital                 Population\     Area\
-                                                  (in 2018)       (in km^2^)
-  ----------------------- ----------------------- --------------- ---------------
-          Denmark         Copenhagen              5,809,502       43,094
-
-          Finland         Helsinki                5,537,364       338,145
-
-          Iceland         Reykjavik               343,518         103,000
-
-          Norway          Oslo                    5,372,191       323,802
-
-          Sweden          Stockholm               10,313,447      450,295
-
-           Total                                  27,376,022      1,258,336
-  -------------------------------------------------------------------------------
++-----------------------+-----------------------+---------------+---------------+
+| Name                  | Capital               | Population\   | Area\         |
+|                       |                       | (in 2018)     | (in km^2^)    |
++:=====================:+:======================+:==============+:==============+
+| Denmark               | Copenhagen            | 5,809,502     | 43,094        |
++-----------------------+-----------------------+---------------+---------------+
+| Finland               | Helsinki              | 5,537,364     | 338,145       |
++-----------------------+-----------------------+---------------+---------------+
+| Iceland               | Reykjavik             | 343,518       | 103,000       |
++-----------------------+-----------------------+---------------+---------------+
+| Norway                | Oslo                  | 5,372,191     | 323,802       |
++-----------------------+-----------------------+---------------+---------------+
+| Sweden                | Stockholm             | 10,313,447    | 450,295       |
++=======================+=======================+===============+===============+
+| Total                 |                       | 27,376,022    | 1,258,336     |
++=======================+=======================+===============+===============+
 
-  : States belonging to the *Nordics.* {#nordics source="wikipedia"}
+: States belonging to the *Nordics.* {#nordics source="wikipedia"}
diff --git a/test/typst-reader.native b/test/typst-reader.native
--- a/test/typst-reader.native
+++ b/test/typst-reader.native
@@ -528,8 +528,7 @@
                       (RowSpan 1)
                       (ColSpan 1)
                       [ CodeBlock
-                          ( "" , [] , [] )
-                          "-- document body here --\n  "
+                          ( "" , [] , [] ) "-- document body here --"
                       ]
                   ]
               ]
@@ -4132,7 +4131,7 @@
                           (ColSpan 1)
                           [ CodeBlock
                               ( "" , [] , [] )
-                              "lr([sum_(k = 0)^n e^(k^2)], size: #50%)\n  "
+                              "lr([sum_(k = 0)^n e^(k^2)], size: #50%)"
                           ]
                       ]
                   ]
@@ -4199,7 +4198,7 @@
                           (ColSpan 1)
                           [ CodeBlock
                               ( "" , [] , [] )
-                              "lr(angle.l i, 2^(2^i) angle.r)\n  "
+                              "lr(angle.l i, 2^(2^i) angle.r)"
                           ]
                       ]
                   ]
@@ -4274,8 +4273,7 @@
                           AlignDefault
                           (RowSpan 1)
                           (ColSpan 1)
-                          [ CodeBlock
-                              ( "" , [] , [] ) "(1 / n^(alpha))\n  "
+                          [ CodeBlock ( "" , [] , [] ) "(1 / n^(alpha))"
                           ]
                       ]
                   , Row
@@ -4297,7 +4295,7 @@
                           (ColSpan 1)
                           [ CodeBlock
                               ( "" , [] , [] )
-                              "paren.l 1 / n^(alpha) paren.r\n  "
+                              "paren.l 1 / n^(alpha) paren.r"
                           ]
                       ]
                   ]
@@ -4362,7 +4360,7 @@
                           (ColSpan 1)
                           [ CodeBlock
                               ( "" , [] , [] )
-                              "lr(frac(dif f, dif x) |)_(x_0)\n  "
+                              "lr(frac(dif f, dif x) |)_(x_0)"
                           ]
                       ]
                   ]
@@ -4431,7 +4429,7 @@
                           (RowSpan 1)
                           (ColSpan 1)
                           [ CodeBlock
-                              ( "" , [] , [] ) "$ mat(a, b; c, d) $\n  "
+                              ( "" , [] , [] ) "$ mat(a, b; c, d) $"
                           ]
                       ]
                   ]
@@ -4576,7 +4574,7 @@
                           (ColSpan 1)
                           [ CodeBlock
                               ( "" , [] , [] )
-                              "$ f_n = cases(\n    a &\"if\" n = 0,\n    r dot f_(n - 1) &\"else\"\n  ) $\n  "
+                              "$ f_n = cases(\n  a &\"if\" n = 0,\n  r dot f_(n - 1) &\"else\"\n) $"
                           ]
                       ]
                   ]
@@ -4784,7 +4782,7 @@
                           (RowSpan 1)
                           (ColSpan 1)
                           [ CodeBlock
-                              ( "" , [] , [] ) "$ S = k dot lg W $\n  "
+                              ( "" , [] , [] ) "$ S = k dot lg W $"
                           ]
                       ]
                   ]
@@ -4837,7 +4835,7 @@
                           (ColSpan 1)
                           [ CodeBlock
                               ( "" , [] , [] )
-                              "$ sin(x) = x - x^3 / 3! \\\n      + x^5 / 5! - dots.h.c $\n  "
+                              "$ sin(x) = x - x^3 / 3! \\\n    + x^5 / 5! - dots.h.c $"
                           ]
                       ]
                   ]
@@ -4886,7 +4884,7 @@
                           (ColSpan 1)
                           [ CodeBlock
                               ( "" , [] , [] )
-                              "$ nabla dot bold(D) &= rho \\\n    nabla dot bold(B) &= 0 $\n  "
+                              "$ nabla dot bold(D) &= rho \\\n  nabla dot bold(B) &= 0 $"
                           ]
                       ]
                   ]
@@ -4977,8 +4975,7 @@
                           AlignDefault
                           (RowSpan 1)
                           (ColSpan 1)
-                          [ CodeBlock ( "" , [] , [] ) "f: RR -> RR\n  "
-                          ]
+                          [ CodeBlock ( "" , [] , [] ) "f: RR -> RR" ]
                       ]
                   , Row
                       ( "" , [] , [] )
@@ -5027,7 +5024,7 @@
                           (ColSpan 1)
                           [ CodeBlock
                               ( "" , [] , [] )
-                              "lim_(h -> 0) (f(x + h) - f(x)) / h\n  "
+                              "lim_(h -> 0) (f(x + h) - f(x)) / h"
                           ]
                       ]
                   , Row
@@ -5050,7 +5047,7 @@
                           (ColSpan 1)
                           [ CodeBlock
                               ( "" , [] , [] )
-                              "integral x^2 dif x = x^3 \\/ 3 + C\n  "
+                              "integral x^2 dif x = x^3 \\/ 3 + C"
                           ]
                       ]
                   , Row
@@ -5073,7 +5070,7 @@
                           (ColSpan 1)
                           [ CodeBlock
                               ( "" , [] , [] )
-                              "nabla = bold(i) dif / (dif x) + bold(j) dif / (dif y) + bold(k) dif / (dif z)\n  "
+                              "nabla = bold(i) dif / (dif x) + bold(j) dif / (dif y) + bold(k) dif / (dif z)"
                           ]
                       ]
                   ]
@@ -5225,7 +5222,7 @@
                           (ColSpan 1)
                           [ CodeBlock
                               ( "" , [] , [] )
-                              "sigma^2 = sqrt(sum(x_i - mu)^2 \\/ N)\n  "
+                              "sigma^2 = sqrt(sum(x_i - mu)^2 \\/ N)"
                           ]
                       ]
                   , Row
@@ -5248,7 +5245,7 @@
                           (ColSpan 1)
                           [ CodeBlock
                               ( "" , [] , [] )
-                              "E(X) = mu_X = sum(x_i - P(x_i))\n  "
+                              "E(X) = mu_X = sum(x_i - P(x_i))"
                           ]
                       ]
                   ]
@@ -5303,7 +5300,7 @@
                       (ColSpan 1)
                       [ CodeBlock
                           ( "" , [] , [] )
-                          "1 / sqrt(2 sigma^2 pi)\n    e^(- (x - mu)^2 / (2 sigma^2))\n  "
+                          "1 / sqrt(2 sigma^2 pi)\n  e^(- (x - mu)^2 / (2 sigma^2))"
                       ]
                   ]
               ]
diff --git a/test/writer.asciidoc b/test/writer.asciidoc
--- a/test/writer.asciidoc
+++ b/test/writer.asciidoc
@@ -529,7 +529,7 @@
 
 Greater-than: ++>++
 
-Hash: #
+Hash: ++#++
 
 Period: .
 
diff --git a/test/writer.asciidoc_legacy b/test/writer.asciidoc_legacy
--- a/test/writer.asciidoc_legacy
+++ b/test/writer.asciidoc_legacy
@@ -528,7 +528,7 @@
 
 Greater-than: ++>++
 
-Hash: #
+Hash: ++#++
 
 Period: .
 
diff --git a/test/writer.html5 b/test/writer.html5
--- a/test/writer.html5
+++ b/test/writer.html5
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
+<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
   <meta charset="utf-8" />
   <meta name="generator" content="pandoc" />
diff --git a/test/writer.mediawiki b/test/writer.mediawiki
--- a/test/writer.mediawiki
+++ b/test/writer.mediawiki
@@ -1,6 +1,5 @@
 This is a set of tests for pandoc. Most of them are adapted from John Gruber’s markdown test suite.
 
-
 -----
 
 <span id="headers"></span>
@@ -34,7 +33,6 @@
 
 with no blank line
 
-
 -----
 
 <span id="paragraphs"></span>
@@ -49,7 +47,6 @@
 There should be a hard line break<br />
 here.
 
-
 -----
 
 <span id="block-quotes"></span>
@@ -74,12 +71,12 @@
 <blockquote>nested
 </blockquote>
 <blockquote>nested
-</blockquote></blockquote>
+</blockquote>
+</blockquote>
 This should not be a block quote: 2 &gt; 1.
 
 And a following paragraph.
 
-
 -----
 
 <span id="code-blocks"></span>
@@ -224,9 +221,7 @@
 <li>more items
 <ol style="list-style-type: upper-alpha;">
 <li>a subsublist</li>
-<li>a subsublist</li></ol>
-</li></ol>
-</li></ol>
+<li>a subsublist</li></ol></li></ol></li></ol>
 
 Nesting:
 
@@ -237,10 +232,7 @@
 <ol start="6" style="list-style-type: decimal;">
 <li>Decimal start with 6
 <ol start="3" style="list-style-type: lower-alpha;">
-<li>Lower alpha with paren</li></ol>
-</li></ol>
-</li></ol>
-</li></ol>
+<li>Lower alpha with paren</li></ol></li></ol></li></ol></li></ol>
 
 Autonumbering:
 
@@ -254,7 +246,6 @@
 
 B. Williams
 
-
 -----
 
 <span id="definition-lists"></span>
@@ -296,7 +287,8 @@
 <dt>''orange''</dt>
 <dd><p>orange fruit</p>
 <pre>{ orange code block }</pre>
-<blockquote><p>orange block quote</p></blockquote></dd></dl>
+<blockquote><p>orange block quote</p>
+</blockquote></dd></dl>
 
 Multiple definitions, tight:
 
@@ -332,31 +324,26 @@
 Simple block on one line:
 
 <div>
-
 foo
-
 </div>
+
 And nested without indentation:
 
 <div>
-
 <div>
-
 <div>
-
 foo
 
-
 </div>
 
 </div>
-<div>
 
+<div>
 bar
-
 </div>
 
 </div>
+
 Interpreted markdown in a table:
 
 <table>
@@ -373,11 +360,10 @@
 Here’s a simple block:
 
 <div>
-
 foo
 
-
 </div>
+
 This should be a code block, though:
 
 <pre>&lt;div&gt;
@@ -389,18 +375,15 @@
 Now, nested:
 
 <div>
-
 <div>
-
 <div>
-
 foo
-
 </div>
 
 </div>
 
 </div>
+
 This should just be an HTML comment:
 
 <!-- Comment -->
@@ -463,7 +446,6 @@
 
 These should not be superscripts or subscripts, because of the unescaped spaces: a^b c^d, a~b c~d.
 
-
 -----
 
 <span id="smart-quotes-ellipses-dashes"></span>
@@ -485,13 +467,12 @@
 
 Ellipses…and…and….
 
-
 -----
 
 <span id="latex"></span>
 = LaTeX =
 
-* 
+*
 * <math display="inline">2+2=4</math>
 * <math display="inline">x \in y</math>
 * <math display="inline">\alpha \wedge \omega</math>
@@ -509,8 +490,6 @@
 
 Here’s a LaTeX table:
 
-
-
 -----
 
 <span id="special-characters"></span>
@@ -566,7 +545,6 @@
 
 Minus: -
 
-
 -----
 
 <span id="links"></span>
@@ -651,12 +629,10 @@
 From “Voyage dans la Lune” by Georges Melies (1902):
 
 <div class="figure">
-
 [[File:lalune.jpg|lalune]]
-
 </div>
-Here is a movie [[File:movie.jpg|movie]] icon.
 
+Here is a movie [[File:movie.jpg|movie]] icon.
 
 -----
 
diff --git a/test/writer.typst b/test/writer.typst
--- a/test/writer.typst
+++ b/test/writer.typst
@@ -1,13 +1,9 @@
 #let horizontalrule = line(start: (25%,0%), end: (75%,0%))
 
-#show terms: it => {
-  it.children
-    .map(child => [
-      #strong[#child.term]
-      #block(inset: (left: 1.5em, top: -0.4em))[#child.description]
-      ])
-    .join()
-}
+#show terms.item: it => block(breakable: false)[
+  #text(weight: "bold")[#it.term]
+  #block(inset: (left: 1.5em, top: -0.4em))[#it.description]
+]
 
 #set table(
   inset: 6pt,
@@ -100,14 +96,14 @@
   block(below: 1em, width: 100%)[
     #if title != none {
       align(center, block[
-          #text(weight: "bold", size: 1.5em)[#title #if thanks != none {
+          #text(weight: "bold", size: 1.5em, hyphenate: false)[#title #if thanks != none {
               footnote(thanks, numbering: "*")
               counter(footnote).update(n => n - 1)
             }]
           #(
             if subtitle != none {
               parbreak()
-              text(weight: "bold", size: 1.25em)[#subtitle]
+              text(weight: "bold", size: 1.25em, hyphenate: false)[#subtitle]
             }
            )])
     }
