packages feed

pandoc 3.11 → 3.12

raw patch · 233 files changed

+13025/−7835 lines, 233 filesdep +conduitdep +text-builderdep −prettydep −pretty-showdep ~Diffdep ~asciidocdep ~citeprocbinary-addedPVP ok

version bump matches the API change (PVP)

Dependencies added: conduit, text-builder

Dependencies removed: pretty, pretty-show

Dependency ranges changed: Diff, asciidoc, citeproc, commonmark, commonmark-extensions, commonmark-pandoc, crypton, directory, djot, doclayout, doctemplates, skylighting, skylighting-core, texmath, text, typst, unicode-data, zip-archive, zlib

API changes (from Hackage documentation)

+ Text.Pandoc.ImageSize: instance GHC.Classes.Eq Text.Pandoc.ImageSize.ImageType
+ Text.Pandoc.Shared: compactifyTable :: (Walkable Block a, Walkable [Block] a, Walkable Inline a) => a -> a
+ Text.Pandoc.Shared: stringifyInlines :: Foldable t => t Inline -> Text
+ Text.Pandoc.Sources: takeWhile1P :: forall (m :: Type -> Type) u. Monad m => (Char -> Bool) -> ParsecT Sources u m Text
+ Text.Pandoc.Sources: takeWhileP :: forall (m :: Type -> Type) u. Monad m => (Char -> Bool) -> ParsecT Sources u m Text
+ Text.Pandoc.Translations.Types: instance GHC.Enum.Bounded Text.Pandoc.Translations.Types.Term
+ Text.Pandoc.Writers.XML: instance (Text.Pandoc.Writers.XML.GConName f, Text.Pandoc.Writers.XML.GConName g) => Text.Pandoc.Writers.XML.GConName (f GHC.Generics.:+: g)
+ Text.Pandoc.Writers.XML: instance GHC.Generics.Constructor c => Text.Pandoc.Writers.XML.GConName (GHC.Generics.M1 GHC.Generics.C c f)
+ Text.Pandoc.Writers.XML: instance Text.Pandoc.Writers.XML.GConName f => Text.Pandoc.Writers.XML.GConName (GHC.Generics.M1 GHC.Generics.D d f)
- Text.Pandoc.MediaBag: MediaItem :: MimeType -> FilePath -> ByteString -> MediaItem
+ Text.Pandoc.MediaBag: MediaItem :: !MimeType -> !FilePath -> ByteString -> MediaItem
- Text.Pandoc.MediaBag: [mediaMimeType] :: MediaItem -> MimeType
+ Text.Pandoc.MediaBag: [mediaMimeType] :: MediaItem -> !MimeType
- Text.Pandoc.MediaBag: [mediaPath] :: MediaItem -> FilePath
+ Text.Pandoc.MediaBag: [mediaPath] :: MediaItem -> !FilePath
- Text.Pandoc.Writers.Shared: htmlAttrs :: HasChars a => Attr -> Doc a
+ Text.Pandoc.Writers.Shared: htmlAttrs :: (HasChars a, FromText a) => Attr -> Doc a
- Text.Pandoc.Writers.Shared: tagWithAttrs :: HasChars a => a -> Attr -> Doc a
+ Text.Pandoc.Writers.Shared: tagWithAttrs :: (HasChars a, FromText a) => a -> Attr -> Doc a

Files

AUTHORS.md view
@@ -25,6 +25,7 @@ - Amy de Buitléir - Anabra - Anders Waldenborg+- Andonome - Andreas Deininger - Andreas Lööw - Andreas Scherer@@ -87,6 +88,7 @@ - Christoffer Sawicki - Christophe Dervieux - Christopher Kenny+- Clar Fon - Clare Macrae - Clint Adams - Conal Elliott@@ -151,6 +153,7 @@ - Fyodor Sheremetyev - Gabor Pali - Gabriel Lewertowski+- Gaurav Vijay Jadhav - Gavin Beatty - George Stagg - Georgi Lyubenov@@ -366,6 +369,7 @@ - Recai Oktaş - Repetitive - Reuben Thomas+- Robert Szarka - Robertas - Rowan Rodrik van der Molen - Roland Hieber@@ -378,6 +382,7 @@ - Salim B - Sam S. Almahri - Sam May+- Samuel Huang - Samuel Tardieu - Saumel Lemmenmeier - Santiago Zarate@@ -467,6 +472,7 @@ - Yoan Blanc - You Jiangbin - Yuchen Pei+- Yusuf Efe - Zihang Chen - 3w36zj6 - arcnmx@@ -549,4 +555,5 @@ - willj-dev - wuffi - wzy+- zenor0 - λx.x
MANUAL.txt view
@@ -1,7 +1,7 @@ --- title: Pandoc User's Guide author: John MacFarlane-date: 2026-08-28+date: 2026-09-26 ---  # Synopsis@@ -2937,17 +2937,24 @@     to e.g. `20px`, but it also accepts `pt`     (12pt = 16px in most browsers). -`fontcolor`+`fontcolor`, `fontcolordark` :   sets the CSS `color` property on the `html` element. -`linkcolor`+`linkcolor`, `linkcolordark` :   sets the CSS `color` property on all links. +`quotebordercolor`, `quotebordercolordark`+:   sets the CSS `border-color` property for the left-border of+:   `blockquote` elements.++`quotecolor`, `quotecolordark`+:   sets the CSS `color` property for `blockquote` elements.+ `monofont`-:   sets the CSS `font-family` property on `code` elements.+:   sets the CSS `font-family` property on `code` and `pre` elements. -`monobackgroundcolor`-:   sets the CSS `background-color` property on `code` elements+`monobackgroundcolor`, `monobackgroundcolordark`+:   sets the CSS `background-color` property on `code` and `pre` elements     and adds extra padding.  `linestretch`@@ -2957,7 +2964,7 @@ `maxwidth` :   sets the CSS `max-width` property (default is 36em). -`backgroundcolor`+`backgroundcolor`, `backgroundcolordark` :   sets the CSS `background-color` property on the `html` element.  `margin-left`, `margin-right`, `margin-top`, `margin-bottom`@@ -2984,6 +2991,41 @@     ---  [CSS]: https://developer.mozilla.org/en-US/docs/Learn/CSS++#### Note on `color-scheme`++By default, the browser's chosen `color-scheme` (either `light` or `dark`)+will affect the color of various unstyled elements like input fields, which are+generally only applicable if your document is specifically targeting HTML.++By default, Pandoc emits `color-scheme: light dark` to allow the system to+choose between a default light-mode or dark-mode scheme, but it alters this+if variables specifically override colors, under the assumption that the user+wishes for that scheme to always be used.++Currently, this is only per a basic heuristic that checks whether the following+variables are provided:++* `fontcolor`+* `fontcolordark`+* `backgroundcolor`+* `backgroundcolordark`++And if only one "mode" is provided (light or dark), `color-scheme` is set so+that this chosen mode is used always, regardless of the system's preference for+light or dark mode.++Note that there are a few caveats to this, namely that:++* If the browser does not support `light-dark` (older than ~2024), then it will+  always choose light mode, regardless of this override.+* When printing, light mode is always chosen, and color variables are ignored.+* Syntax highlighting is currently unaffected by color scheme.++In general, if you wish to override the theming for a single scheme, you should+choose to override either the normal `*color` variables (light mode) or the+`*colordark` variables (dark mode) to ensure that the styles do not conflict+with other styles added by new versions of Pandoc or the browser itself.  ### Variables for HTML math 
benchmark/benchmark-pandoc.hs view
@@ -18,6 +18,7 @@ -} import Text.Pandoc import Text.Pandoc.MIME+import Text.Pandoc.Shared (stringify, stringifyInlines) import Control.DeepSeq (force) import Control.Monad.Except (throwError) import qualified Text.Pandoc.UTF8 as UTF8@@ -89,6 +90,19 @@                          writerFun def{ writerExtensions = wexts} d)                     doc +-- | A large inline sequence exercising both the common constructors+-- and the ones 'stringify' special-cases ('Quoted', 'Note', 'Cite').+bigInlines :: [Inline]+bigInlines = concat $ replicate 1000+  [ Str "Lorem", Space+  , Emph [Str "ipsum", Space, Strong [Str "dolor"]], Space+  , Quoted DoubleQuote [Str "sit", Space, Str "amet"], SoftBreak+  , Link nullAttr [Str "consectetur"] ("https://example.com", "title")+  , Space, Code nullAttr "adipiscing", Space+  , Note [Para [Str "footnote", Space, Emph [Str "text"]]]+  , Cite [] [Str "elit"], LineBreak+  ]+ main :: IO () main = do   inp <- UTF8.toText <$> B.readFile "test/testsuite.txt"@@ -102,4 +116,9 @@     , bgroup "readers" $ mapMaybe (readerBench doc . fst)                          (sortOn fst                            readers :: [(T.Text, Reader PandocPure)])+    , env (pure $ force bigInlines) $ \ils ->+      bgroup "stringify"+        [ bench "stringify" $ nf stringify ils+        , bench "stringifyInlines" $ nf stringifyInlines ils+        ]     ]
changelog.md view
@@ -1,5 +1,738 @@ # Revision history for pandoc +## pandoc 3.12 (2026-09-27)++  * Markdown reader:++    + Make alert keywords case-insensitive (#11836, Hendrik Erz).+      In addition, the class `alert` is now added to the produced+      Divs.+    + Fix stream position handling in `base64DataURI`.+    + Cheaply reject `bareURL` before trying uri/emailAddress.+    + Reset sourcepos in `parseWithString'` when parsing a block+      quote or list item. Otherwise it can happen that by the+      time `parseWithString'` is called, the position has already+      been set to the next file on the command line. Fixes an+      odd bug with `rebase_relative_paths` (#11888).+    + `rebase_relative_paths`: recognize URLs with unknown schemes+      (#11858).+    + Use `takeWhile1P` in the hot inline parsers `str`, `code`,+      `enclosure`, and `mmdShortSubscript`.+    + Reset sourcepos in `parseWithString'` when parsing a block quote+      or list item (#11888). This ensures that `rebase_relative_paths`+      will see the right source file.++  * Typst reader:++    + Map `form: "prose"` citations to AuthorInText (#11846,+      Samuel Huang).+    + Make the handler maps monomorphic by wrapping the handlers+      in newtypes with polymorphic fields (BlockHandler, InlineHandler).+    + Store document labels in a Set instead of a list.+    + In `pInline`, only perform the label-target check for+      `ref` elements, not for every inline element.+    + Handle `highlight` as a mark span (#11879, Samuel Huang).+    + Collapse citations around soft break (#11897).+    + Handle block content in inline element bodies (#11881,+      Samuel Huang).+    + Handle `#par` (explicit paragraph element).++  * LaTeX reader:++    + Support LaTeX3 (xparse) document commands (#7540).+      Support the features described in the LaTeX usrguide.+    + Fix `\qed` to produce U+00A0 (nbsp) instead of BEL.+    + Fix `unescapeURL` handling of escaped backslash.+    + Require that `\newif` names begin with "if".+    + Fix position off-by-one after `##` in tokenizer.+    + Fix doubled source positions in `retokenizeComment`.+    + Make raw token capture O(1) per token.+    + Make `untokenize` linear instead of quadratic.+    + Fail `macroDef` fast on non-macro-defining commands.+    + Peek at next token directly in `peekTok` instead of going+      through `satisfyTok`.+    + Skip `doMacros` state update for non-macro tokens.+    + Build command dispatch maps (`inlineCommands`, etc.) once per parse.+    + Keep ASCII quotes when ligatures are disabled.+    + Don't discard state changes made in optional arguments.+    + Keep nested conditionals balanced in `\iftrue` etc.++  * Docx reader:++    + Read ScreenTips as link titles (#11869, Robert Szarka).+    + Use `comment-id` instead of `id` in AST for comments.++  * ODT reader:++    + Rewrite in monadic style. Net -994 lines. No changes to test output.+    + Handle `textProperties` on paragraph styles (#2623).+      Previously these just got ignored, not applied to the+      paragraph's text.++  * HTML reader:++    + Don't require a closing tag for checkbox inputs.+      `<input>` is a void element.+    + Allow omitted `</tr>` in tables. The `</tr>` closing+      tag is optional in HTML.+    + Don't drop cells when there are too few `<col>` elements.+    + Let the first of duplicate attributes win. HTML specifies that+      the first occurrence wins.+    + Respect `raw_html` for inline `<style>` elements.+    + Use a Map for the note table.+    + Report the noteref position for unresolved notes. The+      ReferenceNotFound warning was logged after the+      whole document had been parsed, so it always pointed at the+      end of the input. Record the position of the first reference+      to each note and use it in the warning.+    + Find list style anywhere in the class attribute.+      Previously `<ol class="fancy lower-roman">` got DefaultStyle,+      because the whole class attribute was compared against the+      known style names. Check each class individually. As a side+      effect, an unrecognized class no longer prevents falling back+      to the style attribute.+    + Limit iframe nesting depth.+    + `htmlTag`: Don't copy the remaining input on each invocation. A+      space was appended to the remaining input to guarantee a+      TagPosition token after the parsed tag; since the input is a+      strict Text, this copied the entire remaining input every+      time htmlTag was called (e.g. for every inline HTML tag in a+      markdown document), giving quadratic behavior in tag-dense+      documents. Instead, handle the case where the tag is the+      final token by computing the end-of-input position directly.+    + Pass the tag name to `pSpanLike`. The inline dispatcher already+      knows which span-like element it is looking at, so there is+      no need for pSpanLike to try a parser.+    + Fix `pre/code` attribute precedence for first-wins dedup.+    + Implement `pSatisfy` as a single parsec primitive.+    + Add a fast path to `pTagText`: when the text contains no+      character that could parse as anything but Str, Space, or+      SoftBreak under the enabled extensions (and we are not in a+      pre element), return B.text directly.+    + Reorder `pTagContents` to try `pStr` and `pSpace` before the+      math, smart punctuation, and raw TeX parsers. This is safe+      because pStr cannot consume the special characters that+      start those parsers, and it avoids most guard checks on the+      slow path. This makes the html reader benchmark about 33%+      faster and halves its allocation.++  * Muse reader:++    + Try `str` earlier in inline parser. This makes the reader 2x+      faster and reduces heap allocation by 60%.+    + Check raw line before parsing table rows. Table row parsers+      are speculatively tried at every paragraph line boundary via+      the terminator continuation. This change gives another 2x+      speed improvement.++  * Org reader:++    + Fix hard parse failure on bare backslash.+    + Recognize `.pdf` as an image format (#11859). This matches+      the behavior of Emacs, which will render `[[file:foo.pdf]]`+      as an image.+    + Make `#+OPTIONS: ^:nil` disable all sub-/superscript parsing.+    + Allow `-` and `_` in inline footnote labels. Org footnote labels+      may contain word-constituent characters, hyphens and underscores.+    + Fix swapped arguments in exportSettings parser.+    + Don't lowercase meta keyword twice.+    + Avoid quadratic complexity when parsing table rows. Parsing+      a 32000-row table drops from 7.8s to 0.9s (and scales+      linearly instead of quadratically); maximum residency also improves.+    + Use a Set for anchor ids. Reading a document with 64000+      anchors and as many internal links drops from 23.5s to 5.2s+      and now scales linearly.++  * RST reader:++    + Use a predicate to test for for special characters.+    + Avoid double-parsing lines preceding a non-underline.+    + Fail fast when no link can start at this position.+    + Use `lookupGE` to find the next anonymous key. Parsing a+      document with 16000 anonymous links drops from 5.2s to 2.0s.+    + Replace association list with Map for resolving note references.+      Parsing a document with 16000 named notes drops from 5.8s+      to 3.5s, and scaling is now linear.+    + Fix a bug that led to an empty class for code blocks with+      no specified language.++  * CommonMark reader:++    + Avoid nested walks in `tex_math_gfm` handling.+    + Avoid rebuilding the token list with `map id` in+      `sourceToToks` in the common case where the source starts+      at line 1.++  * AsciiDoc reader:++    + Resolve footnotes, stem, and icons during conversion.+      Previously the reader made three separate passes+      over the parsed AsciiDoc AST (for footnotes, stem math types,+      and icons) before converting to the pandoc AST. Instead,+      resolve all three during the toPandoc conversion, which+      already traverses everything once, threading footnote state+      and document attributes through a StateT layer. Table+      headers are now converted before body rows so that footnote+      resolution follows document order. This makes the reader+      about twice as fast on typical documents.++  * Djot reader:++    + Fix a bug that led to an empty class for code blocks with+      no specified language.++  * Vimwiki reader:++    + Split class attribute and put it in the proper slot in+      pandoc's Attr.++  * Typst writer:++    + Omit blank line at end of block (#11844). This is just a+      cosmetic change; it is not semantically significant.+    + Emit label for bare table if present (#11849). Previously a label+      was only emitted if the table was figurized (and not+      `.typst:no-figure`).++  * RST writer:++    + Apply `nowrap` to just footnote label, not body.+      This bug surfaced after `nowrap` was fixed in doclayout.++  * Docx writer:++    + Fix sizing for images (#11838). Setting size to a percent+      now scales image to percent of page width. Previously a+      percent width or height would just provide a maximum bound+      rather than scaling.+    + Include default style even if paragraph has non-style props+      (#11867). Previously a paragraph that was, e.g. center-aligned+      would be missing its default Body Text style. Also ensure that+      any block sequence (list items, table cells, block quote,+      div), First Paragraph is set for the first paragraph in the item.+    + Use `_` to start all bookmark names (#11845). This ensures that+      they are "hidden" and will not be read by screen readers.+    + Honor CSL hanging-indent and spacing hints (#11871, Samuel Huang).+    + Add a fast path to `withDirection`.+    + Avoid double `withDirection` for Space and SoftBreak.+    + Map link/image titles to ScreenTips.+    + Write link titles as ScreenTips (#11869, Robert Szarka).+    + Properly signal error if reference docx can't be parsed.+      Previously this led to an incomprehensible error at a later+      phase.+    + Make `convertSpace` linear instead of quadratic. On an ad+      hoc benchmark (4 paragraphs of 80K words each), conversion+      time drops from 3.2s to 1.1s, and time no longer depends on+      paragraph length (16 x 20K words previously took 1.8s, now+      also 1.2s).+    + Cache token-type styles instead of rebuilding per Code.+      On an ad hoc benchmark with 100K inline code spans,+      conversion time drops from 2.4s to 1.5s.+    + Add FirstParagraph class after display math (#11900).+      This way the continuation text can be styled flush-left+      in a style where normal paragraphs are indented.+    + Treat a "mixed" widths table as all-default (#11899). Previously if+      some columns were ColWidthDefault and others ColWidth 0.x,+      we would get columns with a specified width of 0 for the default+      ones. Instead, treat all columns as default in this case.+    + Use `comment-id` instead of `id` in AST for comments.+      Note that the Docx writer will still interpret an `id`+      attribute for legacy compatibility, so if you use markdown+      files that specify `id`, they should still work.++  * TEI writer:++    + Use `rend`, not `rendition` attribute, on milestone+      (#11842, Yusuf Efe) `rendition` takes pointers to rendition+      descriptions, while `rend` is the free-text attribute,+      which is what a plain "line" value needs.++  * LaTeX writer:++    + Avoid nested walk in table cell line-break handling.+    + Don't use `footnotehyper` for notes in longtable.+      Instead, generate them manually as we do for floating tables.+      This removes our dependency on footnotehyper, and resolves a+      compatibility problem with `endfloat` (#11857).+    + Remove unused `stInternalLinks` state field. The+      field has not been read since the writer switched to always+      adding hypertargets, but we were still doing a full-document+      query to populate it.+    + Avoid needless conversions in `sectionHeader`.+      The note-free and link-free variants of the heading text+      were rendered for every heading, even though the former is+      only needed when the heading contains a note, image, or+      identified span, and the latter only for unnumbered, listed+      headings.+    + Fuse preprocessing passes in `inlineListToLaTeX`.+      The strut-insertion and quote-kerning fixups were separate+      list traversals, allocating an intermediate list each, and+      this function is called at every level of inline nesting.+      Combine them into a single pass.+    + Avoid String round-trip for highlighted inline code.+    + Don't unpack code string to choose `lstinline` delimiter.+    + Only compute PDF trailer ID when `SOURCE_DATE_EPOCH` is set.+    + Use `showHex` instead of (slower) `printf` in `toLabel`.++  * RST writer:++    + Don't re-transform stored labels and alt text.+      Reference labels, image substitution labels, and alt text are+      stored after the document-wide walk in inlineListToRST has+      already applied transformInlines (flattening, backslash-space+      insertion, etc.). Rendering them with inlineListToRST applied+      these non-idempotent transformations a second time, inserting+      duplicate `\ ` markers. With `--reference-links` this could+      make the inline reference and its definition render+      differently, producing a broken RST reference.++  * Native writer:++    + Render directly instead of using pretty-show.+      Previously writeNative used pretty-show's `ppDoc`, which shows+      the document, tokenizes and re-parses the result into a+      generic Value, and lays that out via Text.PrettyPrint.HughesPJ.+      The layout step dominated the cost of the writer (and of+      any pipeline producing native output). We now build a+      width-cached layout tree directly from the AST and render+      it with a small renderer that reproduces HughesPJ's layout+      algorithm exactly (including the ribbon computation with+      ribbonsPerLine = 1.2 and the treatment of glued closing+      delimiters), so the output is byte-for-byte identical to+      before. Verified against the old binary on all golden+      .native files and the markdown test corpus at many column+      widths, in both standalone and plain modes. The writer is+      about 4x faster. Also remove the pretty and pretty-show+      dependencies.++  * XML writer:++    + Respect `--standalone`. When standalone is selected,+      we get a full Pandoc element with xml header and metadata. When+      not, we get a fragment -- just the blocks.+    + Use the pretty-printer instead of manual newlines.+      Render the document with `ppcElement`, using a+      configuration that treats elements with inline content as+      inline tags, so that no significant whitespace is added+      inside them. Consecutive text nodes are merged, SoftBreak+      is written as a literal newline, and whitespace runs that+      would not survive a roundtrip (e.g. `" \n"` or `"\n\n"`)+      are encoded as Space and SoftBreak elements.+    + Encode attribute names that are not valid XML names.+      Pandoc attribute names may contain characters that are not+      allowed in XML attribute names, such as colons+      (`typst:property`). Use the common convention of encoding+      such characters as `_xHHHH_`, where `HHHH` is the hexadecimal+      code of the character: the writer encodes attribute names+      (`foo:bar` becomes `foo_x003A_bar`) and the reader decodes+      them.+    + Don't drop attributes with empty values. The writer dropped+      any attribute with an empty value, so user key-value+      attributes like `("k","")` disappeared and did not round+      trip.+    + Improve performance: the XML writer is now on par with the+      JSON writer.++  * EPUB writer:++    + Render TOC item titles in the host monad. Previously each TOC+      item title in the nav entry was rendered with a separate+      `runPure (writeHtmlStringForEPUB ...)`. Since every one of+      these invocations starts with a fresh CommonState, the+      translations YAML file was re-read and re-parsed for every+      TOC item, which accounted for a significant part of the+      EPUB writer's run time on documents with many sections.+    + Speed up MathML/SVG detection for the manifest.++  * Powerpoint writer:++    + Add archive entries in a single pass.+    + Cache the parsed slide master in WriterEnv.+    + Skip speaker-notes walk when there are no notes.++  * ANSI writer:++    + Fix missing bar on blockquote's first line (#11804,+      Gaurav Vijay Jadhav D.).++  * HTML writer:++    + Fix typo in `intrinsicEventsHTML4`. The list had+      `onmouseout` twice and was missing `onmousemove`.+    + Don't emit `<p></p>` for paragraphs with no rendered content.+    + Improve email obfuscation. Preserve formatting (e.g. emphasis)+      in the link text of obfuscated mailto links. Preserve link+      attributes in reference- and JavaScript-obfuscated links.+      Avoid double-escaping the already-rendered link text in the+      fallback branch for unparseable mailto URLs.+    + Respect `--id-prefix` in EPUB3 footnote section id.+    + Respect incremental/nonincremental classes inside columns.+    + Make KaTeX CSS URL handling consistent with the JS URL.+    + Use truncate consistently for table width percentages.+      Previously the table width could exceed the sum of column widths.+    + Avoid walking section contents when not producing slides.+    + Hoist attribute set unions to top level.+    + Make `strToHtml` more efficient. Replace the+      `T.groupBy`-based implementation, which allocated a list of+      Text fragments and round-tripped through String, with a+      simple `T.break` scanner.++  * Org writer:++    + Don't render table body rows twice. Table rows+      were converted once to compute column widths and then again+      to produce the output. Since blockListToOrg is stateful, any+      footnote in a table cell was registered twice, yielding+      duplicate footnote definitions and skewed numbering; it also+      doubled the rendering work. Reuse the first conversion.+    + Use `#+begin_export html` for raw HTML blocks. `#+begin_html`+      was removed in Org 9.0 (2016).+    + Don't treat bare punctuation as a list marker.+      The check that keeps ordered list markers from ending up at+      the beginning of a line matched `Str "."` and `Str ")"`,+      since `T.all isDigit ""` is True. Require at least one digit.+    + Don't emit `<<>>` for spans with no id.+    + Avoid `=` delimiter for inline code containing `=`.+      Org has no escape mechanism inside verbatim text, so `=code+      with ==` did not parse as verbatim. Fall back to the+      equivalent `~...~` delimiter when the content contains `=`+      (and no `~`).+    + Escape square brackets in links. Link targets+      containing square brackets broke the bracket link syntax.+      Escape targets the way Emacs' `org-link-escape` does:+      backslash-escape brackets and double backslash runs occurring+      before a bracket or at the end of the target. Link+      descriptions cannot contain escapes; instead, like+      `org-link-make-string`, insert a zero-width space between+      consecutive closing brackets and before a closing bracket at+      the end of the description.+    + Build escaped strings from chunks, not characters.+      `escapeString` allocated one Doc node per character for any+      string containing a non-alphanumeric character. Split on the+      (rare) special characters instead and emit intervening text+      as single literals. No change in output.+    + Emit definitions for footnotes nested in footnotes.+    + Use a counter for footnote numbers. Computing the+      reference number as `length stNotes + 1` walked the+      accumulated note list for every footnote, making note+      numbering quadratic in the number of notes.++  * Org reader and writer:++    + Make code line comma-escaping match Emacs' behavior.+      Org's escaping rule for code in src/example blocks adds a+      comma to lines matching `^[ \t]*,*(\*|#\+)`, i.e. lines+      already starting with commas before `*` or `#+` get an+      additional comma; unescaping removes one comma from such+      lines. The writer previously left a literal `,#+foo` line+      unescaped, and the reader then stripped its comma when+      reading the result back, corrupting the code on round trips.+      Writer and reader now both handle runs of commas, matching Emacs.++  * Text.Pandoc.ImageSize:++    + ImageType now derives Eq [API change].+    + Fix typo in bare JPEG signature detection.+    + Fix size detection for lossless (VP8L) WebP.+    + Make EMF parsing more robust.+    + Don't let zlib errors escape as exceptions in `pdfSize`.+      Treat malformed streams as a parse scanner (so we keep+      scanning the rest for a `/MediaBox`).+    + Fix AVIF detection and parsing.+    + Take bounding box origin into account for EPS. The+      size was computed from the upper corner alone, giving wrong+      dimensions for EPS files whose bounding box has a non-zero origin.+    + Handle commas and fractional numbers in SVG `viewBox`.+    + Add tests for image type and size detection (Tests.ImageSize).+    + Determine JPEG size without decoding the image (which can+      allocate huge amounts of memory). If the header scan fails,+      we still fall back to the full decoder.+    + Scan PDF object streams in chunks, not byte by byte.+    + Speed up `findSvgTag` by using a single pass. Up to 60X+      faster on files with few `<` characters.+    + Determine PNG size without decoding the image.+      If the header scan fails, we still fall back to the full decoder.+    + Use `writerDpi` for AVIF images instead of hardcoding 72.+      With the default options this changes the assumed resolution from+      72 to 96 dpi.+    + Allow whitespace between number and unit in `numUnit`.+      So e.g. `width="3 cm"` is now recognized.+    + Handle largesize and size-0 boxes in AVIF parser.+    + Make checkDpi default to 72 for negative dpi values, not+      0; a negative dpi would produce negative dimensions in+      `sizeInPoints`.++  * Text.Pandoc.SelfContained:++    + Fix inverted charset condition in `makeDataURI`.+    + Keep semicolon in `@import` fallback output.+    + Make `</script` check case-insensitive.+    + Prefix all `url(#...)` occurrences in SVG attributes.+      Previously only the first got prefixed rewritten.+    + Handle gzip decompression errors gracefully.+    + Remove unused `isHtml5` field from ConvertState.+    + Cache fetched resources. Previously every occurrence of a+      resource was fetched, decompressed, and CSS-rewritten+      independently, so a document referencing the same image+      N times triggered N network requests. Failed fetches are+      cached too, so each missing resource is now reported only once.+    + Only add `role` and `aria-label` when inlining SVGs.+      Do not add them to other elements with `src` attributes.+    + Escape only what is needed in textual data URIs.++  * Text.Pandoc.UTF8:++    + Avoid copying input when it contains no CRs. `toText` and+      `toTextLazy `unconditionally ran a CR-removing+      filter over the input, allocating a full copy of the document+      even in the common case where no CRs are present. Check for a+      CR first (B.elem, a fast memchr) and reuse the input buffer+      unchanged if none is found; for the lazy variant, do this+      chunk-wise to preserve laziness. On a 10 MB LF-only input+      this makes `toText` over 4x faster; when CRs are present the+      extra scan is not measurable.+    + Make `readFile` exception-safe. Use `withFile`+      instead of `openFile` so the handle is closed even if reading+      throws.++  * Text.Pandoc.Class:++    + Avoid copying input in `toTextM`. Skip the CR-filtering+      copy when the input contains no CRs, as already done in+      Text.Pandoc.UTF8.toText.+    + Make `runSilently` error-safe. Previously, if the action+      passed to runSilently threw an error that was later caught,+      the verbosity remained pinned at ERROR and all previously+      accumulated log messages were lost. Now the original log+      and verbosity are restored even when the action fails.+    + Fix `isRelativeToParentDir`. Compare the first path+      component rather than just looking at a prefix, to+      correctly handle paths like `..foo/bar.yaml`.+    + Fix base64 detection in `extractURIData`. The base64 indicator+      in a data URI is the final parameter of the media type and may+      follow other parameters, e.g. `charset`. Previously, the+      code expected `;base64` to be the only parameter.+    + Fix percent-decoding in `extractURIData`.+    + Report accurate offset in `toTextM` errors. Scan for the first+      invalid UTF-8 sequence and report its actual position and byte.+    + Reset HTTP manager in `setNoCheckCertificate`. The HTTP manager+      is created lazily with TLS settings based on `stNoCheckCertificate`+      and then cached in CommonState, so changing the option after the+      first request had no effect. Discard the cached manager when the+      option's value changes.+    + Don't follow symlink cycles in `addToFileTree`.+    + Finish factoring `openURL` into Text.Pandoc.Class.IO.HTTP.+      Commit 455bea907 added the new module but did not register it+      in pandoc.cabal or remove the original definitions.+    + `logOutput`: avoid multiple `hPutStrLn`, which can cause confusing+      interleaving.++  * Text.Pandoc.MediaBag:++    + Use hashlazy to avoid copying media contents.+    + Treat `data:` and `file:` URI schemes case-insensitively.+    + Make MediaItem mime type and path strict. `mediaContents`+      is left lazy so contents need not be forced at insert time.+    + Use `Text.Pandoc.URI.isURI` in `canonicalize`. `Network.URI.isURI`+      treats Windows drive-letter paths like `c:/foo.png` as URIs.+    + Only reject `..` as a path component. The insertMedia check used+      `isInfixOf`, so a harmless name like foo..bar.png was silently+      renamed to its content hash.+    + Collapse `.` and `..` components in `canonicalize`. `normalise`+      does not remove redundant path components, so `img/../a.png`+      and `a.png` were distinct keys. Use `makeCanonical` (as+      PandocPure's FileTree already does for its path-indexed map),+      which also handles duplicate and trailing slashes, replacing+      backslashes with slashes first.+    + Prevent `mediaPath` collisions between keys. The friendly+      mediaPath was derived by percent-unescaping the key, so+      distinct keys like `a%20b.png` and `a b.png` produced the same+      mediaPath ("a b.png") and silently clobbered each other on+      extraction (and inside docx/epub archives). Now the original name+      is only kept if the key contains no percent sign, so `mediaPath`+      equals the key and distinct keys yield distinct paths; anything+      percent-encoded gets a content-hash name. Hashed names can+      only coincide for identical contents, which is harmless.++  * Text.Pandoc.XML.Light:++    + Fix escaping of repeated `]]>` in `CDATA`.+    + Make `escStr` more efficient.+    + Avoid round-trips in `ppCDataS` prettify path.+      This makes `showCData` and `ppcCData` unused, so they are+      removed.+    + Parse XML fragments from the event stream instead of using+      xml-conduit's document parser, which requires a single root+      element. Our earlier woraround with a wrapper element was+      fragile. Behavior changes:++      - Content fragments with an XML declaration or DOCTYPE+        followed by multiple root elements, and text-only or empty+        input, now parse instead of erroring.+      - Attributes now preserve document order instead of being sorted+        alphabetically (the document parser stored them in a Map).+      - Errors for unresolved entities and mismatched tags now+        report source positions.++   + Use `text-builder` package for rendering, instead of text's+     lazy Text builder. On a large document this cuts docx conversion+     time by about 13%; output is byte-for-byte identical.+   + Expose `ppcTopElement` from Output.+   + ConfigPP now has a field `inlineTag` that checks for inline+     tags. Inline tags are printed on one line and not indented,+     by default. Export `useInlineTags`, `prettyConfigPP`.++  * Text.Pandoc.Sources:++    + Add bulk `takeWhileP`/`takeWhile1P` combinators [API change].+      Character streams over Sources previously had to be+      consumed one character at a time via `satisfy`, at a cost of+      several allocations and monadic binds per character. The new+      combinators scan a whole run of matching characters with a+      single parser invocation using `T.span`, while replicating the+      exact semantics of `T.pack <$> many/many1 (satisfy f)`,+      including empty-chunk handling, position updates at chunk+      boundaries, and parsec's error messages.+    + Use the new bulk `takeWhileP`/`takeWhile1P` combinators across+      readers.++  * Text.Pandoc.Translations:++    + `setTranslations` now keeps an already-loaded translation+       table when the language is unchanged, instead of+       unconditionally clearing the cache and forcing a re-read and+       re-parse of the translations YAML file on the next `translateTerm`.+    + Term names in translation files are now parsed with a+       precomputed Map lookup instead of the derived Read instance,+       which is very slow for a 22-constructor enum.++  * Text.Pandoc.Chunks:++    + Remove vestigial `nav-path` attribute and `rmNavAttrs` walk.+      This no longer did anything; output is unaffected.+    + Use `compactifyTable` for tables produced by all readers.+      Remove old ad hoc `paraToPlain` at the table cell level.+      This should ensure that we don't get tables that mix Plain+      and Para (#11864). Such tables tend to look funny when rendered in+      docx and other formats.++  * Text.Pandoc.Data:++    + Fix `getDataFileNames` with `-embed_data_files`. Previously+      it was not looking in the right directory and not+      recursing.++  * Text.Pandoc.Shared:++    + `taskListItemFromAscii`: Fix incorrect treatment of `[ ]` as+      checked.+    + Add `stringifyInlines`, a single-pass `stringify` for+      inlines [API change]. This is about 6x faster than `stringify`+      for long inline sequences+    + Speed up `stringify` by making it accumulate `[Text]`+      and concatenate once at the end, instead of mappending at every node.+    + Use `stringifyInlines` instead of `stringify` where possible.+    + Add new function `compactifyTable`. [API change]+      This converts cells that consist in a single Para block to a+      Plain, provided the table contains only such cells (or empty cells).+    + Drop expired entries in `decrementTrailingRowSpans`.+      Entries whose RowSpan fell to 0 were left in the map, relying+      on every consumer to guard against them. Delete them instead.+    + Recognize empty task list items in `toTaskListItem`.+    + Make `endsWithPlain` look inside DefinitionList.+      endsWithPlain recursed into the last item of BulletList and+      OrderedList but ignored DefinitionList, so list items ending+      with a compact definition list were treated as loose by the+      RST, Org, and Haddock writers.+    + Avoid Text -> String round-trips in `htmlAttrs`.+      This adds a FromText constraint to `htmlAttrs` and+      `tagWithAttrs` [API change].+    + Fuse traversals in `ensureValidXmlIdentifiers`.++  * Text.Pandoc.Writers.Shared:++    + Fix logic bug in `splitSentences`.+    + `lookupMetaBool`: treat empty block or inline list as False.+    + `htmlAttrs`: escape id and class attributes, like the others.+    + `stripLeadingTrailingSpace` - strip multiple Space, if present.+    + `toSubscript`: handle minus sign.+    + Fix `ensureValidXmlIdentifiers` for Figure and table+      sub-elements, resolving a bug that produced broken internal+      links in the HTML4/XHTML, EPUB, DocBook, TEI, ICML, FB2, and+      ODT writers.++  * Text.Pandoc.Parsing:++    + Improve performance of `uriScheme` by using a trie. Up to 15%+      faster in URL-heavy documents with `autolink_bare_uris`.+    + Minor code cleanup.+    + Remove `$` checks in math when delim is `\(` or `\\(`.+    + Make `anyOrderedListMarker` more efficient.++  * HTML template:++    + Dark mode support (#11831, Clar Fon).++  * reveal.js template:++    + Fix plugin paths (#11907).++  * flake.nix: parse allow-newer and allow-newer-deps in+    stack.yaml.++  * Make `embed_data_files` flag default to True. Remove flag+    settings from cabal.project. This makes it possible to+    override it on the command line.++  * Depend on commonmark 0.3.1, commonmark-extensions 0.2.7.3,+    commonmark-pandoc 0.3.0.2 (major performance improvements).++  * Depend on released asciidoc 0.1.1 (major performance+    improvements).++  * Use released texmath 0.13.3 (major performance improvements).++  * Depend on released djot 0.1.4.3 (major performance+    improvements).++  * Use released citeproc 0.14 (major performance improvements).++  * Use released doclayout 0.6.++  * Use released zip-archive 0.5 (major performance+    improvements).++  * Use released skylighting-0.15 (major performance+    improvements).++  * Depend on released doctemplates 0.11.1.++  * Depend on released typst 0.12.++  * Require text >= 2.0.++  * Bump upper bound for unicode-data.++  * Allow crypton 2.0.x.++  * Allow Diff 2.0.++  * Add `tools/diff-golden-tests.sh`.++  * Fix `tools/diff-zip.sh` on non-Darwin.++  * Add `tools/benchplot.js`. This creates a nice graph comparing two+    benchmarks.++  * `typst-properties.md`: fix fill syntax in Typst property+    examples (#11855, zenor0).++  * Remove tested-with from cabal file. We tend not to keep it up to date.++  * Fix typo in Lua filter example (#11875, Andonome).++  * Fix a bug in jats-reader.xml (duplicate attribute)+ ## pandoc 3.11 (2026-08-28)    * Add `--math-method` option. This replaces (now deprecated but@@ -60,6 +793,11 @@     + Fix treatment of DefaultHighlighting (#11829).    * Docx writer:++    + Honor CSL bibliography formatting hints emitted by citeproc (#11871).+      Bibliographies from styles with `hanging-indent="true"` (e.g. APA)+      now get a hanging indent, and CSL `line-spacing`/`entry-spacing`+      are mapped to paragraph spacing properties.      + Initialize envLang from `lang` metadata (#11301).       This ensures that setting `lang` will affect the whole document.
data/templates/common.latex view
@@ -88,9 +88,6 @@ \makeatletter \patchcmd\longtable{\par}{\if@noskipsec\mbox{}\fi\par}{}{} \makeatother-% Allow footnotes in longtable head/foot-\IfFileExists{footnotehyper.sty}{\usepackage{footnotehyper}}{\usepackage{footnote}}-\makesavenoteenv{longtable} $endif$ $endif$ $--
data/templates/default.revealjs view
@@ -30,7 +30,7 @@   <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">+  <link rel="stylesheet" href="$revealjs-url$/dist/plugin/highlight/$highlightjs-theme$.css"> $endif$ $for(css)$   <link rel="stylesheet" href="$css$"/>@@ -84,14 +84,14 @@   <script src="$revealjs-url$/dist/reveal.js"></script>    <!-- reveal.js plugins -->-  <script src="$revealjs-url$/plugin/notes/notes.js"></script>-  <script src="$revealjs-url$/plugin/search/search.js"></script>-  <script src="$revealjs-url$/plugin/zoom/zoom.js"></script>+  <script src="$revealjs-url$/dist/plugin/notes.js"></script>+  <script src="$revealjs-url$/dist/plugin/search.js"></script>+  <script src="$revealjs-url$/dist/plugin/zoom.js"></script> $if(mathjax)$-  <script src="$revealjs-url$/plugin/math/math.js"></script>+  <script src="$revealjs-url$/dist/plugin/math.js"></script> $endif$ $if(highlight-js)$-  <script src="$revealjs-url$/plugin/highlight/highlight.js"></script>+  <script src="$revealjs-url$/dist/plugin/highlight.js"></script> $endif$    <script>
data/templates/styles.html view
@@ -13,7 +13,10 @@   line-height: $linestretch$; $endif$   color: $if(fontcolor)$$fontcolor$$else$#1a1a1a$endif$;+  color: light-dark($if(fontcolor)$$fontcolor$$else$#1a1a1a$endif$, $if(fontcolordark)$$fontcolordark$$else$#fdfdfd$endif$);   background-color: $if(backgroundcolor)$$backgroundcolor$$else$#fdfdfd$endif$;+  background-color: light-dark($if(backgroundcolor)$$backgroundcolor$$else$#fdfdfd$endif$, $if(backgroundcolordark)$$backgroundcolordark$$else$#1a1a1a$endif$);+  color-scheme:$if(fontcolor)$ light$elseif(backgroundcolor)$ light$elseif(fontcolordark)$$elseif(backgroundcolordark)$$else$ light$endif$$if(fontcolordark)$ dark$elseif(backgroundcolordark)$ dark$elseif(fontcolor)$$elseif(backgroundcolor)$$else$ dark$endif$; } body {   margin: 0 auto;@@ -38,12 +41,17 @@ } @media print {   html {-    background-color: $if(backgroundcolor)$$backgroundcolor$$else$white$endif$;+    color-scheme: light only;   }-  body {+  html, body, pre, code {     background-color: transparent;+  }+  body, a, a:visited, blockquote {     color: black;   }+  pre, code {+    padding: 0;+  }   p, h2, h3 {     orphans: 3;     widows: 3;@@ -55,11 +63,13 @@ p {   margin: 1em 0; }-a {-  color: $if(linkcolor)$$linkcolor$$else$#1a1a1a$endif$;-}-a:visited {-  color: $if(linkcolor)$$linkcolor$$else$#1a1a1a$endif$;+a, a:visited {+  color: $if(linkcolor)$$linkcolor$$else$inherit$endif$;+$if(linkcolor)$+  color: light-dark($linkcolor$, $if(linkcolordark)$$linkcolordark$$else$inherit$endif$);+$elseif(linkcolordark)$+  color: light-dark(inherit, $linkcolordark$);+$endif$ } img {   max-width: 100%;@@ -88,8 +98,11 @@ blockquote {   margin: 1em 0 1em 1.7em;   padding-left: 1em;-  border-left: 2px solid #e6e6e6;-  color: #606060;+  border-left: 2px solid;+  border-color: $if(quotebordercolor)$$quotebordercolor$$else$#e6e6e6$endif$;+  border-color: light-dark($if(quotebordercolor)$$quotebordercolor$$else$#e6e6e6$endif$, $if(quotebordercolordark)$$quotebordercolordark$$else$#4c4c4c$endif$);+  color: $if(quotecolor)$$quotecolor$$else$#606060$endif$;+  color: light-dark($if(quotecolor)$$quotecolor$$else$#606060$endif$, $if(quotecolordark)$$quotecolordark$$else$#b6b6b6$endif$); } $if(abstract)$ div.abstract {@@ -109,7 +122,12 @@   font-family: $if(monofont)$$monofont$$else$Menlo, Monaco, Consolas, 'Lucida Console', monospace$endif$; $if(monobackgroundcolor)$   background-color: $monobackgroundcolor$;+  background-color: light-dark($monobackgroundcolor$, $if(monobackgroundcolordark)$$monobackgroundcolordark$$else$inherit$endif$);   padding: .2em .4em;+$elseif(monobackgroundcolordark)$+  background-color: inherit;+  background-color: light-dark(inherit, $monobackgroundcolordark$);+  padding: .2em .4em; $endif$   font-size: 85%;   margin: 0;@@ -119,7 +137,12 @@   margin: 1em 0; $if(monobackgroundcolor)$   background-color: $monobackgroundcolor$;+  background-color: light-dark($monobackgroundcolor$, $if(monobackgroundcolordark)$$monobackgroundcolordark$$else$inherit$endif$);   padding: 1em;+$elseif(monobackgroundcolordark)$+  background-color: inherit;+  background-color: light-dark(inherit, $monobackgroundcolordark$);+  padding: 1em; $endif$   overflow: auto; }@@ -134,7 +157,7 @@ } hr {   border: none;-  border-top: 1px solid #1a1a1a;+  border-top: 1px solid currentColor;   height: 1px;   margin: 1em 0; }@@ -156,11 +179,11 @@ } tbody {   margin-top: 0.5em;-  border-top: 1px solid $if(fontcolor)$$fontcolor$$else$#1a1a1a$endif$;-  border-bottom: 1px solid $if(fontcolor)$$fontcolor$$else$#1a1a1a$endif$;+  border-top: 1px solid currentColor;+  border-bottom: 1px solid currentColor; } th {-  border-top: 1px solid $if(fontcolor)$$fontcolor$$else$#1a1a1a$endif$;+  border-top: 1px solid currentColor;   padding: 0.25em 0.5em 0.25em 0.5em; } td {
pandoc.cabal view
@@ -1,6 +1,6 @@ cabal-version:   2.4 name:            pandoc-version:         3.11+version:         3.12 build-type:      Simple license:         GPL-2.0-or-later license-file:    COPYING.md@@ -11,7 +11,6 @@ stability:       alpha homepage:        https://pandoc.org category:        Text-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@@ -219,6 +218,7 @@                  test/command/*.md                  test/command/*.csl                  test/command/*.svg+                 test/command/2623.odt                  test/command/7691.docx                  test/command/9391.docx                  test/command/9358.docx@@ -281,6 +281,9 @@                  test/command/11486/scroll.revealjs                  test/command/11498.png                  test/command/11301-styles.opendocument+                 test/command/11888/section1/lab.md+                 test/command/11888/section2/lab.md+                 test/command/11888/section3/lab.md                  test/asciidoc-reader.adoc                  test/asciidoc-reader.native                  test/asciidoc-reader-include.rb@@ -463,7 +466,7 @@  flag embed_data_files   Description:   Embed data files in binary for relocatable executable.-  Default:       False+  Default:       True  flag http   Description:   Support for fetching resources using HTTP.@@ -498,11 +501,13 @@  library xml-light   import:        common-options-  build-depends: xml                   >= 1.3.12   && < 1.4,+  build-depends: conduit               >= 1.3      && < 1.4,+                 xml                   >= 1.3.12   && < 1.4,                  xml-conduit           >= 1.9.1.1  && < 1.11,                  xml-types             >= 0.3      && < 0.4,                  containers            >= 0.6.0.1  && < 0.9,-                 text                  >= 1.1.1.0  && < 2.2+                 text                  >= 2.0      && < 2.2,+                 text-builder          >= 1.0      && < 1.1    hs-source-dirs:  xml-light   exposed-modules: Text.Pandoc.XML.Light,@@ -525,17 +530,17 @@                  blaze-markup          >= 0.8      && < 0.9,                  bytestring            >= 0.9      && < 0.13,                  case-insensitive      >= 1.2      && < 1.3,-                 citeproc              >= 0.13.0.1 && < 0.14,-                 commonmark            >= 0.3      && < 0.4,-                 commonmark-extensions >= 0.2.7.1  && < 0.3,-                 commonmark-pandoc     >= 0.3      && < 0.4,+                 citeproc              >= 0.14     && < 0.15,+                 commonmark            >= 0.3.1    && < 0.4,+                 commonmark-extensions >= 0.2.7.3  && < 0.3,+                 commonmark-pandoc     >= 0.3.0.2  && < 0.4,                  containers            >= 0.6.0.1  && < 0.9,-                 crypton               >= 0.30     && < 1.2,+                 crypton               >= 0.30     && < 2.1,                  data-default          >= 0.4      && < 0.9,                  deepseq               >= 1.3      && < 1.6,-                 directory             >= 1.2.3    && < 1.4,-                 doclayout             >= 0.5.0.3  && < 0.6,-                 doctemplates          >= 0.11     && < 0.12,+                 directory             >= 1.2.5    && < 1.4,+                 doclayout             >= 0.6      && < 0.7,+                 doctemplates          >= 0.11.1   && < 0.12,                  emojis                >= 0.1.5    && < 0.2,                  exceptions            >= 0.8      && < 0.11,                  file-embed            >= 0.0      && < 0.1,@@ -550,34 +555,32 @@                  network-uri           >= 2.6      && < 2.8,                  pandoc-types          >= 1.23.1.2 && < 1.24,                  parsec                >= 3.1      && < 3.2,-                 pretty                >= 1.1      && < 1.2,-                 pretty-show           >= 1.10     && < 1.11,                  process               >= 1.2.3    && < 1.7,                  random                >= 1.2      && < 1.4,                  safe                  >= 0.3.18   && < 0.4,                  scientific            >= 0.3      && < 0.4,-                 skylighting           >= 0.14.7   && < 0.15,-                 skylighting-core      >= 0.14.7   && < 0.15,+                 skylighting           >= 0.15     && < 0.16,+                 skylighting-core      >= 0.15     && < 0.16,                  split                 >= 0.2      && < 0.3,                  syb                   >= 0.1      && < 0.8,                  tagsoup               >= 0.14.6   && < 0.15,                  temporary             >= 1.1      && < 1.4,-                 texmath               >= 0.13.2.2 && < 0.14,-                 text                  >= 1.1.1.0  && < 2.2,+                 texmath               >= 0.13.3   && < 0.14,+                 text                  >= 2.0      && < 2.2,                  text-conversions      >= 0.3      && < 0.4,                  time                  >= 1.5      && < 1.17,                  unicode-collation     >= 0.1.1    && < 0.2,-                 unicode-data          >= 0.6      && < 0.9,+                 unicode-data          >= 0.6      && < 0.10,                  unicode-transforms    >= 0.3      && < 0.5,                  yaml                  >= 0.11     && < 0.12,                  libyaml               >= 0.1.4    && < 0.2,-                 zip-archive           >= 0.4.3.1  && < 0.5,-                 zlib                  >= 0.5      && < 0.8,+                 zip-archive           >= 0.5      && < 0.6,+                 zlib                  >= 0.6      && < 0.8,                  xml                   >= 1.3.12   && < 1.4,-                 typst                 >= 0.11.0.1 && < 0.12,+                 typst                 >= 0.12     && < 0.13,                  vector                >= 0.12     && < 0.14,-                 djot                  >= 0.1.4.2  && < 0.2,-                 asciidoc              >= 0.1.0.5  && < 0.2+                 djot                  >= 0.1.4.3  && < 0.2,+                 asciidoc              >= 0.1.1    && < 0.2    if !os(windows)     build-depends:  unix >= 2.4 && < 2.9@@ -724,6 +727,7 @@                    Text.Pandoc.App.Opt,                    Text.Pandoc.App.OutputSettings,                    Text.Pandoc.Class.CommonState,+                   Text.Pandoc.Class.IO.HTTP,                    Text.Pandoc.Class.PandocMonad,                    Text.Pandoc.Class.PandocIO,                    Text.Pandoc.Class.PandocPure,@@ -771,7 +775,6 @@                    Text.Pandoc.Readers.Mdoc.Standards,                    Text.Pandoc.Readers.Typst.Parsing,                    Text.Pandoc.Readers.Typst.Math,-                   Text.Pandoc.Readers.ODT.Base,                    Text.Pandoc.Readers.ODT.Namespaces,                    Text.Pandoc.Readers.ODT.StyleReader,                    Text.Pandoc.Readers.ODT.ContentReader,@@ -780,8 +783,6 @@                    Text.Pandoc.Readers.ODT.Generic.Utils,                    Text.Pandoc.Readers.ODT.Generic.Namespaces,                    Text.Pandoc.Readers.ODT.Generic.XMLConverter,-                   Text.Pandoc.Readers.ODT.Arrows.State,-                   Text.Pandoc.Readers.ODT.Arrows.Utils,                    Text.Pandoc.Readers.Org.BlockStarts,                    Text.Pandoc.Readers.Org.Blocks,                    Text.Pandoc.Readers.Org.DocumentTree,@@ -843,12 +844,12 @@   main-is:        test-pandoc.hs   hs-source-dirs: test   build-depends:  pandoc,-                  Diff              >= 0.2     && < 1.1,+                  Diff              >= 0.2     && < 2.1,                   Glob              >= 0.7     && < 0.11,                   bytestring        >= 0.9     && < 0.13,                   containers        >= 0.4.2.1 && < 0.9,-                  directory         >= 1.2.3   && < 1.4,-                  doctemplates      >= 0.11    && < 0.12,+                  directory         >= 1.2.5   && < 1.4,+                  doctemplates      >= 0.11.1  && < 0.12,                   filepath          >= 1.1     && < 1.6,                   mtl               >= 2.2     && < 2.4,                   pandoc-types      >= 1.23.1  && < 1.24,@@ -857,17 +858,18 @@                   tasty-golden      >= 2.3     && < 2.4,                   tasty-hunit       >= 0.9     && < 0.11,                   tasty-quickcheck  >= 0.8     && < 0.12,-                  text              >= 1.1.1.0 && < 2.2,+                  text              >= 2.0     && < 2.2,                   temporary         >= 1.1     && < 1.4,                   time              >= 1.5     && < 1.17,                   xml               >= 1.3.12  && < 1.4,-                  zip-archive       >= 0.4.3   && < 0.5+                  zip-archive       >= 0.5     && < 0.6   other-modules:  Tests.Old                   Tests.Command                   Tests.Helpers                   Tests.Shared                   Tests.MediaBag                   Tests.XML+                  Tests.ImageSize                   Tests.Readers.LaTeX                   Tests.Readers.HTML                   Tests.Readers.JATS@@ -934,7 +936,7 @@   build-depends:   bytestring,                    tasty-bench >= 0.4     && <= 0.5,                    mtl         >= 2.2     && < 2.4,-                   text        >= 1.1.1.0 && < 2.2,+                   text        >= 2.0     && < 2.2,                    deepseq   -- we increase heap size to avoid benchmarking garbage collection:   ghc-options:     -rtsopts -with-rtsopts=-A8m -threaded
src/Text/Pandoc/App/Input.hs view
@@ -16,6 +16,7 @@  import Control.Monad ((>=>), when) import Control.Monad.Except (throwError, catchError)+import Data.Char (toLower) import Data.Text (Text) import Network.URI (URI (..), parseURI) import Text.Pandoc.Transforms (adjustLinksAndIds)@@ -89,8 +90,9 @@ readSource "-" = (,Nothing) <$> readStdinStrict readSource src =   case parseURI src of-    Just u | uriScheme u `elem` ["http:","https:"] -> openURL (T.pack src)-           | uriScheme u == "file:" ->+    Just u | map toLower (uriScheme u) `elem` ["http:","https:"] ->+               openURL (T.pack src)+           | map toLower (uriScheme u) == "file:" ->                (,Nothing) <$>                  readFileStrict (uriPathToPath $ T.pack $ uriPath u)     _       -> (,Nothing) <$> readFileStrict src
src/Text/Pandoc/Chunks.hs view
@@ -28,8 +28,8 @@   ) where  import Text.Pandoc.Definition-import Text.Pandoc.Shared (makeSections, stringify, inlineListToIdentifier,-                           tshow, uniqueIdent)+import Text.Pandoc.Shared (makeSections, stringifyInlines,+                           inlineListToIdentifier, tshow, uniqueIdent) import Text.Pandoc.Walk (Walkable(..), query) import Data.Aeson (FromJSON, ToJSON) import Data.Text (Text)@@ -57,8 +57,7 @@ splitIntoChunks pathTemplate numberSections mbBaseLevel                 chunklev (Pandoc meta blocks) =    addNav .-   fixInternalReferences .-   walk rmNavAttrs $+   fixInternalReferences $    ChunkedDoc{ chunkedMeta = meta              , chunkedChunks = chunks              , chunkedTOC = tocTree }@@ -214,12 +213,11 @@       , chunkPrev = Nothing       , chunkUnlisted = "unlisted" `elem` classes       , chunkContents =-         [Div (divid,"section":classes,kvs') (h : bs)]+         [Div (divid,"section":classes,kvs) (h : bs)]       }-     where kvs' = kvs ++ [("nav-path", T.pack chunkpath)]-           secnum = lookup "number" kvs+     where secnum = lookup "number" kvs            chunkpath = resolvePathTemplate pathTemplate chunknum-                        (stringify ils)+                        (stringifyInlines ils)                         divid                         (fromMaybe "" secnum)   toChunk chunknum (Div ("",["preamble"],[]) bs) =@@ -238,7 +236,7 @@       }     where       chunkpath = resolvePathTemplate pathTemplate chunknum-                        (stringify (docTitle meta))+                        (stringifyInlines (docTitle meta))                         chunkid                         "0"       chunkid = inlineListToIdentifier mempty (docTitle meta) <>@@ -246,14 +244,6 @@   toChunk _ b = error $ "toChunk called on inappropriate block " <> show b   -- should not happen ---- Remove some attributes we added just to construct chunkNext etc.-rmNavAttrs :: Block -> Block-rmNavAttrs (Div (ident,classes,kvs) bs) =-  Div (ident,classes,filter (not . isNavAttr) kvs) bs- where-  isNavAttr (k,_) = "nav-" `T.isPrefixOf` k-rmNavAttrs b = b  resolvePathTemplate :: PathTemplate                     -> Int -- ^ Chunk number
src/Text/Pandoc/Citeproc.hs view
@@ -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, makeSections)+import Text.Pandoc.Shared (stringifyInlines, tshow, makeSections) import Data.Containers.ListUtils (nubOrd) import Text.Pandoc.Walk (query, walk, walkM) import Control.Applicative ((<|>))@@ -422,7 +422,7 @@ mvPunct moveNotes locale (q : s : x@(Cite _ [il]) : ys)   | isSpacy s   , isNote il-  = let spunct = T.takeWhile isPunct $ stringify ys+  = let spunct = T.takeWhile isPunct $ stringifyInlines ys     in  if moveNotes            then if T.null spunct                    then q : x : mvPunct moveNotes locale ys@@ -436,7 +436,7 @@    | isNote (last ils)    , startWithPunct ys    , moveNotes-   = let s = stringify ys+   = let s = stringifyInlines ys          spunct = T.takeWhile isPunct s      in  Cite cs (movePunctInsideQuotes locale $                     init ils@@ -451,7 +451,7 @@ mvPunct moveNotes locale (s : x@(Cite _ (Superscript _ : _)) : ys)   | isSpacy s = x : mvPunct moveNotes locale ys mvPunct moveNotes locale (Cite cs ils : Str "." : ys)-  | "." `T.isSuffixOf` (stringify ils)+  | "." `T.isSuffixOf` (stringifyInlines ils)   = Cite cs ils : mvPunct moveNotes locale ys mvPunct moveNotes locale (x:xs) = x : mvPunct moveNotes locale xs mvPunct _ _ [] = []@@ -464,7 +464,7 @@ endWithPunct :: Bool -> [Inline] -> Bool endWithPunct _ [] = False endWithPunct onlyFinal xs@(_:_) =-  case reverse (T.unpack $ stringify xs) of+  case reverse (T.unpack $ stringifyInlines xs) of        []                       -> True        -- covers .), .", etc.:        (d:c:_) | isPunct d@@ -478,15 +478,15 @@  startWithPunct :: [Inline] -> Bool startWithPunct ils =-  case T.uncons (stringify ils) of+  case T.uncons (stringifyInlines ils) of     Just (c,_) -> c `elem` (".,;:!?" :: [Char])     Nothing -> False  truish :: MetaValue -> Bool truish (MetaBool t) = t truish (MetaString s) = isYesValue (T.toLower s)-truish (MetaInlines ils) = isYesValue (T.toLower (stringify ils))-truish (MetaBlocks [Plain ils]) = isYesValue (T.toLower (stringify ils))+truish (MetaInlines ils) = isYesValue (T.toLower (stringifyInlines ils))+truish (MetaBlocks [Plain ils]) = isYesValue (T.toLower (stringifyInlines ils)) truish _ = False  isYesValue :: Text -> Bool
src/Text/Pandoc/Citeproc/BibTeX.hs view
@@ -29,7 +29,7 @@ import Text.Pandoc.Extensions (Extension(..), extensionsFromList) import Text.Pandoc.Options (ReaderOptions(..), WriterOptions) import Text.Pandoc.Error (PandocError)-import Text.Pandoc.Shared (stringify)+import Text.Pandoc.Shared (stringify, stringifyInlines) import Text.Pandoc.Writers.LaTeX (writeLaTeX) import Text.Pandoc.Class (runPure) import qualified Text.Pandoc.Walk       as Walk@@ -262,7 +262,7 @@    getVariable v = lookupVariable (toVariable v) ref -  getVariableAsText v = (stringify . valToInlines) <$> getVariable v+  getVariableAsText v = (stringifyInlines . valToInlines) <$> getVariable v    getYear val =     case val of@@ -340,7 +340,7 @@    getContentsFor x = getVariable x >>=     if isURL x-       then Just . literal . stringify . valToInlines+       then Just . literal . stringifyInlines . valToInlines        else toLaTeX .             (if x == "title"                 then titlecase@@ -381,7 +381,7 @@     -- hyphenation:     let getLangId = do              langid <- T.strip . T.toLower <$> getRawField "langid"-             idopts <- T.strip . T.toLower . stringify <$>+             idopts <- T.strip . T.toLower . stringifyInlines <$>                            getField "langidopts" <|> return ""              case (langid, idopts) of                   ("english","variant=british")    -> return "british"@@ -842,14 +842,11 @@   updateState (\(l,m) -> (l, Map.insert k v m))   return () -take1WhileP :: Monad m => (Char -> Bool) -> ParsecT Sources u m Text-take1WhileP f = T.pack <$> many1 (satisfy f)- inBraces :: BibParser Text inBraces = do   char '{'   res <- manyTill-         (  take1WhileP (\c -> c /= '{' && c /= '}' && c /= '\\')+         (  takeWhile1P (\c -> c /= '{' && c /= '}' && c /= '\\')         <|> (char '\\' >> T.cons '\\' . T.singleton <$> anyChar)         <|> (braced <$> inBraces)          ) (char '}')@@ -862,14 +859,14 @@ inQuotes = do   char '"'   T.concat <$> manyTill-             ( take1WhileP (\c -> c /= '{' && c /= '"' && c /= '\\')+             ( takeWhile1P (\c -> c /= '{' && c /= '"' && c /= '\\')                <|> (char '\\' >> T.cons '\\' . T.singleton <$> anyChar)                <|> braced <$> inBraces             ) (char '"')  fieldName :: BibParser Text fieldName = resolveAlias . T.toLower-  <$> take1WhileP (\c ->+  <$> takeWhile1P (\c ->          isAlphaNum c || c == '-' || c == '_' || c == ':' || c == '+')  isBibtexKeyChar :: Char -> Bool@@ -883,11 +880,11 @@ bibItem = do   char '@'   pos <- getPosition-  enttype <- T.toLower <$> take1WhileP isLetter+  enttype <- T.toLower <$> takeWhile1P isLetter   spaces'   char '{'   spaces'-  entid <- take1WhileP isBibtexKeyChar+  entid <- takeWhile1P isBibtexKeyChar   spaces'   char ','   spaces'@@ -913,7 +910,7 @@ resolveAlias s = s  rawWord :: BibParser Text-rawWord = take1WhileP isAlphaNum+rawWord = takeWhile1P isAlphaNum  expandString :: BibParser Text expandString = do@@ -1039,17 +1036,17 @@  getOldDate :: Text -> Bib Date getOldDate prefix = do-  year' <- (readMay . T.unpack . fixLeadingDash . stringify+  year' <- (readMay . T.unpack . fixLeadingDash . stringifyInlines               <$> getField (prefix <> "year")) <|> return Nothing   month' <- (parseMonth <$> getRawField (prefix <> "month"))             <|> return Nothing   day' <- (readMay . T.unpack <$> getRawField (prefix <> "day"))           <|> return Nothing-  endyear' <- (readMay . T.unpack . fixLeadingDash . stringify+  endyear' <- (readMay . T.unpack . fixLeadingDash . stringifyInlines               <$> getField (prefix <> "endyear")) <|> return Nothing-  endmonth' <- (parseMonth . stringify+  endmonth' <- (parseMonth . stringifyInlines                  <$> getField (prefix <> "endmonth")) <|> return Nothing-  endday' <- (readMay . T.unpack . stringify <$>+  endday' <- (readMay . T.unpack . stringifyInlines <$>                  getField (prefix <> "endday")) <|> return Nothing   let toDateParts (y', m', d') =               DateParts $
src/Text/Pandoc/Citeproc/Locator.hs view
@@ -14,7 +14,7 @@ import qualified Data.List as L import Text.Pandoc.Definition import Text.Pandoc.Parsing-import Text.Pandoc.Shared (stringify)+import Text.Pandoc.Shared (stringify, stringifyInlines) import Control.Monad (mzero) import qualified Data.Map as M import Data.Char (isSpace, isPunctuation, isDigit)@@ -152,7 +152,7 @@           -- the pathological case is "p.3"           t <- anyToken           ts <- manyTill anyToken (try $ lookAhead lim)-          let s = acc <> stringify (t:ts)+          let s = acc <> stringifyInlines (t:ts)           case M.lookup (T.toCaseFold $ T.strip s) (unLocatorMap locMap) of             -- try to find a longer one, or return this one             Just l -> go s <|> return (s, l, False)@@ -239,7 +239,7 @@                        notFollowedBy pLocatorPunct >>                        notFollowedBy pMath >>                        anyToken)-          let s = stringify ts+          let s = stringifyInlines ts           -- otherwise look for actual digits or -s           return (T.any isDigit s, s) 
src/Text/Pandoc/Citeproc/MetaValue.hs view
@@ -9,7 +9,7 @@ import Citeproc.Types import Text.Pandoc.Definition import Text.Pandoc.Builder as B-import Text.Pandoc.Shared (stringify, blocksToInlines')+import Text.Pandoc.Shared (stringify, stringifyInlines, blocksToInlines') import Data.Maybe import Safe import qualified Data.Set as Set@@ -21,7 +21,7 @@  metaValueToText :: MetaValue -> Maybe Text metaValueToText (MetaString t) = Just t-metaValueToText (MetaInlines ils) = Just $ stringify ils+metaValueToText (MetaInlines ils) = Just $ stringifyInlines ils metaValueToText (MetaBlocks bls) = Just $ stringify bls metaValueToText (MetaList xs) = T.unwords <$> mapM metaValueToText xs metaValueToText _ = Nothing@@ -31,7 +31,7 @@ metaValueToBool (MetaString "true") = Just True metaValueToBool (MetaString "false") = Just False metaValueToBool (MetaInlines ils) =-  metaValueToBool (MetaString (stringify ils))+  metaValueToBool (MetaString (stringifyInlines ils)) metaValueToBool _ = Nothing  referenceToMetaValue :: Reference Inlines -> MetaValue
src/Text/Pandoc/Citeproc/Name.hs view
@@ -23,7 +23,7 @@     where  import Text.Pandoc.Definition-import Text.Pandoc.Shared (stringify)+import Text.Pandoc.Shared (stringifyInlines) import Citeproc.Types import Citeproc.Pandoc () import Text.Pandoc.Citeproc.Util (splitStrWhen)@@ -62,7 +62,7 @@ toName _ [Str "others"] =   return emptyName{ nameLiteral = Just "others" } toName _ [Span ("",[],[]) ils] = -- corporate author-  return emptyName{ nameLiteral = Just $ stringify ils }+  return emptyName{ nameLiteral = Just $ stringifyInlines ils } -- extended BibLaTeX name format - see #266 toName _ ils@(Str ys:_) | T.any (== '=') ys = do   let commaParts = splitWhen (== Str ",")@@ -70,17 +70,17 @@                    $ ils   let addPart ag (Str "given" : Str "=" : xs) =         ag{ nameGiven = case nameGiven ag of-                          Nothing -> Just $ stringify xs-                          Just t  -> Just $ t <> " " <> stringify xs }+                          Nothing -> Just $ stringifyInlines xs+                          Just t  -> Just $ t <> " " <> stringifyInlines xs }       addPart ag (Str "family" : Str "=" : xs) =-        ag{ nameFamily = Just $ stringify xs }+        ag{ nameFamily = Just $ stringifyInlines xs }       addPart ag (Str "prefix" : Str "=" : xs) =-        ag{ nameDroppingParticle =  Just $ stringify xs }+        ag{ nameDroppingParticle =  Just $ stringifyInlines xs }       addPart ag (Str "useprefix" : Str "=" : Str "true" : _) =         ag{ nameNonDroppingParticle = nameDroppingParticle ag           , nameDroppingParticle    = Nothing }       addPart ag (Str "suffix" : Str "=" : xs) =-        ag{ nameSuffix = Just $ stringify xs }+        ag{ nameSuffix = Just $ stringifyInlines xs }       addPart ag (Space : xs) = addPart ag xs       addPart ag _ = ag   return $ L.foldl' addPart emptyName commaParts@@ -117,10 +117,10 @@                  case break isCapitalized vonlast of                         (vs@(_:_), []) -> (init vs, [last vs])                         (vs, ws)       -> (vs, ws)-  let prefix = T.unwords $ map stringify von-  let family = T.unwords $ map stringify lastname-  let suffix = T.unwords $ map stringify jr-  let given = T.unwords $ map stringify first+  let prefix = T.unwords $ map stringifyInlines von+  let family = T.unwords $ map stringifyInlines lastname+  let suffix = T.unwords $ map stringifyInlines jr+  let given = T.unwords $ map stringifyInlines first   return     Name {  nameFamily              = if T.null family                                          then Nothing
src/Text/Pandoc/Class/IO.hs view
@@ -41,35 +41,16 @@ 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, 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 Network.URI (unEscapeString) import System.Directory (createDirectoryIfMissing) 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.IO.HTTP (openURL) import Text.Pandoc.Class.PandocMonad-       (PandocMonad,-        getMediaBag, report, extractURIData)+       (PandocMonad, getMediaBag, report) import Text.Pandoc.Definition (Pandoc, Inline (Image)) import Text.Pandoc.Error (PandocError (..)) import Text.Pandoc.Logging (LogMessage (..), messageVerbosity, showLogMessage)@@ -129,70 +110,6 @@ 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-  disableCertificateValidation <- getsCommonState stNoCheckCertificate-  case mbManager of-    Just manager -> pure manager-    Nothing -> do-      manager <- liftIO $ do-        certificateStore <- getSystemCertificateStore-        let tlsSettings = TLSSettings $-               (TLS.defaultParamsClient "localhost.localdomain" "80")-                  { TLS.clientSupported = def{ TLS.supportedCiphers =-                                               TLS.ciphersuite_default-                                             , TLS.supportedExtendedMainSecret =-                                                TLS.AllowEMS }-                  , TLS.clientShared = def-                      { TLS.sharedCAStore = certificateStore-                      , TLS.sharedValidationCache =-                          if disableCertificateValidation-                             then TLS.ValidationCache-                                   (\_ _ _ -> return TLS.ValidationCachePass)-                                   (\_ _ _ -> return ())-                             else def-                      }-                  }-        let tlsManagerSettings = mkManagerSettings tlsSettings  Nothing-        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-     report $ Fetching u-     manager <- getManager-     res <- liftIO $ E.try $ withSocketsDo $ do-       proxy <- tryIOError (getEnv "http_proxy")-       let addProxy' x = case proxy of-                            Left _ -> return x-                            Right pr -> parseUrlThrow pr >>= \r ->-                                return (addProxy (host r) (port r) x)-       req <- parseUrlThrow (unpack u) >>= addProxy'-       let req' = req{requestHeaders = customHeaders ++ requestHeaders req}-       resp <- httpLbs req' manager-       return (B.concat $ toChunks $ responseBody resp,-               UTF8.toText `fmap` lookup hContentType (responseHeaders resp))--     case res of-          Right r -> return r-          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. readFileLazy :: (PandocMonad m, MonadIO m) => FilePath -> m BL.ByteString@@ -232,19 +149,9 @@ -- | Output a log message. logOutput :: (PandocMonad m, MonadIO m) => LogMessage -> m () logOutput msg = liftIO $ do-  UTF8.hPutStr stderr $-      "[" <> T.pack (show (messageVerbosity msg)) <> "] "-  alertIndent $ T.lines $ showLogMessage msg---- | Prints the list of lines to @stderr@, indenting every but the first--- line by two spaces.-alertIndent :: [Text] -> IO ()-alertIndent [] = return ()-alertIndent (l:ls) = do-  UTF8.hPutStrLn stderr l-  mapM_ go ls-  where go l' = do UTF8.hPutStr stderr "  "-                   UTF8.hPutStrLn stderr l'+  UTF8.hPutStrLn stderr $+      "[" <> T.pack (show (messageVerbosity msg)) <> "] " <>+      T.intercalate ("\n  ") (T.lines (showLogMessage msg))  -- | Extract media from the mediabag into a directory (or a zip archive if the -- path supplied ends in @.zip@.
+ src/Text/Pandoc/Class/IO/HTTP.hs view
@@ -0,0 +1,115 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE OverloadedStrings #-}+{- |+Module      : Text.Pandoc.Class.IO.HTTP+Copyright   : Copyright (C) 2025 John MacFarlane+License     : GNU GPL, version 2 or above++Maintainer  : John MacFarlane <jgm@berkeley.edu>+Stability   : alpha+Portability : portable++HTTP fetching functionality for pandoc.+-}+module Text.Pandoc.Class.IO.HTTP+  ( openURL+  ) where++import Network.URI (URI(..), parseURI)+import Data.Text (Text)+import Control.Monad.IO.Class (MonadIO)+import Text.Pandoc.Class.PandocMonad (PandocMonad, extractURIData)+import Text.Pandoc.Error (PandocError (..))+import Text.Pandoc.MIME (MimeType)+import qualified Data.ByteString as B+import qualified Data.Text as T+import Control.Monad.Except (throwError)+#ifdef PANDOC_HTTP_SUPPORT+import Data.ByteString.Lazy (toChunks)+import Control.Monad.IO.Class (liftIO)+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, 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, report )+import qualified Data.CaseInsensitive as CI+import System.IO.Error+import Text.Pandoc.Logging (LogMessage (..))+import qualified Control.Exception as E+import qualified Text.Pandoc.UTF8 as UTF8+#endif++#ifdef PANDOC_HTTP_SUPPORT+getManager :: (PandocMonad m, MonadIO m) => m Manager+getManager = do+  mbManager <- getsCommonState stManager+  disableCertificateValidation <- getsCommonState stNoCheckCertificate+  case mbManager of+    Just manager -> pure manager+    Nothing -> do+      manager <- liftIO $ do+        certificateStore <- getSystemCertificateStore+        let tlsSettings = TLSSettings $+               (TLS.defaultParamsClient "localhost.localdomain" "80")+                  { TLS.clientSupported = def{ TLS.supportedCiphers =+                                               TLS.ciphersuite_default+                                             , TLS.supportedExtendedMainSecret =+                                                TLS.AllowEMS }+                  , TLS.clientShared = def+                      { TLS.sharedCAStore = certificateStore+                      , TLS.sharedValidationCache =+                          if disableCertificateValidation+                             then TLS.ValidationCache+                                   (\_ _ _ -> return TLS.ValidationCachePass)+                                   (\_ _ _ -> return ())+                             else def+                      }+                  }+        let tlsManagerSettings = mkManagerSettings tlsSettings  Nothing+        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+     report $ Fetching u+     manager <- getManager+     res <- liftIO $ E.try $ withSocketsDo $ do+       proxy <- tryIOError (getEnv "http_proxy")+       let addProxy' x = case proxy of+                            Left _ -> return x+                            Right pr -> parseUrlThrow pr >>= \r ->+                                return (addProxy (host r) (port r) x)+       req <- parseUrlThrow (T.unpack u) >>= addProxy'+       let req' = req{requestHeaders = customHeaders ++ requestHeaders req}+       resp <- httpLbs req' manager+       return (B.concat $ toChunks $ responseBody resp,+               UTF8.toText `fmap` lookup hContentType (responseHeaders resp))++     case res of+          Right r -> return r+          Left (e :: HttpException)+                  -> throwError $ PandocHttpError u (T.pack (show e))+#else+ | otherwise =+     throwError $ PandocHttpError u "pandoc was compiled without HTTP support"+#endif
src/Text/Pandoc/Class/PandocMonad.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE BangPatterns #-} {-# LANGUAGE CPP #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE FlexibleContexts #-}@@ -64,6 +65,9 @@ import Control.Monad.Except (MonadError (catchError, throwError)) import Control.Monad.Trans (MonadTrans, lift) import Control.Monad (when)+import Data.Char (chr, digitToInt, isHexDigit, toLower)+import Data.List (intercalate)+import Data.Word (Word8) import Data.Time (UTCTime) import Data.Time.Clock.POSIX (POSIXTime, utcTimeToPOSIXSeconds,                              posixSecondsToUTCTime)@@ -72,7 +76,7 @@                      unEscapeString, parseURIReference, isAllowedInURI,                      parseURI, URI(..) ) import System.FilePath ((</>), takeExtension, dropExtension,-                        isRelative, makeRelative)+                        isRelative, makeRelative, splitDirectories) import System.Random (StdGen) import Text.Collate.Lang (Lang(..), parseLang) import Text.Pandoc.Class.CommonState (CommonState (..))@@ -85,10 +89,10 @@ import Text.Pandoc.URI (uriPathToPath, pBase64DataURI) import qualified Data.Attoparsec.Text as A import Text.Pandoc.Walk (walkM)-import qualified Text.Pandoc.UTF8 as UTF8 import Data.ByteString.Base64 (decodeLenient) import Text.Parsec (ParsecT, getPosition, sourceLine, sourceName) import qualified Data.ByteString as B+import qualified Data.ByteString.Char8 as B8 import qualified Data.ByteString.Lazy as BL import qualified Data.Text as T import qualified Debug.Trace@@ -140,7 +144,6 @@   getCommonState :: m CommonState   -- | Set the value of the 'CommonState' used by all instances   -- of 'PandocMonad'.-  -- | Get the value of a specific field of 'CommonState'.   putCommonState :: CommonState -> m ()   -- | Get the value of a specific field of 'CommonState'.   getsCommonState :: (CommonState -> a) -> m a@@ -201,13 +204,15 @@   -- get current settings   origLog <- getsCommonState stLog   origVerbosity <- getVerbosity+  let restore = modifyCommonState+        (\st -> st { stVerbosity = origVerbosity, stLog = origLog })   -- reset log level and set verbosity to the minimum   modifyCommonState (\st -> st { stVerbosity = ERROR, stLog = []})-  result <- action+  -- restore the original log and verbosity even if the action fails+  result <- action `catchError` (\e -> restore *> throwError e)   -- get log messages reported while running `action`   newLog <- getsCommonState stLog-  modifyCommonState (\st -> st { stVerbosity = origVerbosity, stLog = origLog})-+  restore   return (result, newLog)  -- | Set request header to use in HTTP requests.@@ -221,7 +226,16 @@  -- | Determine whether certificate validation is disabled setNoCheckCertificate :: PandocMonad m => Bool -> m ()-setNoCheckCertificate noCheckCertificate = modifyCommonState $ \st -> st{stNoCheckCertificate = noCheckCertificate}+setNoCheckCertificate noCheckCertificate = modifyCommonState $ \st ->+  st{ stNoCheckCertificate = noCheckCertificate+#ifdef PANDOC_HTTP_SUPPORT+    -- discard any cached HTTP manager, since it was created with+    -- TLS settings based on the previous value of this option+    , stManager = if stNoCheckCertificate st == noCheckCertificate+                     then stManager st+                     else Nothing+#endif+    }  -- | Initialize the media bag. setMediaBag :: PandocMonad m => MediaBag -> m ()@@ -281,7 +295,7 @@ setRequestHeaders :: PandocMonad m => [(T.Text, T.Text)] -> m () setRequestHeaders hs = modifyCommonState $ \st -> st{ stRequestHeaders = hs } --- | Get the absolute UL or directory of first source file.+-- | Get the absolute URL or directory of first source file. getSourceURL :: PandocMonad m => m (Maybe T.Text) getSourceURL = getsCommonState stSourceURL @@ -372,7 +386,7 @@                => T.Text                -> m (B.ByteString, Maybe MimeType) downloadOrRead s- | "data:" `T.isPrefixOf` s,+ | T.toLower (T.take 5 s) == "data:",    Right (bs, mt) <- A.parseOnly (pBase64DataURI <* A.endOfInput) s    = pure (bs, Just mt)  | otherwise = do@@ -389,9 +403,10 @@             Nothing -> openURL s' -- will throw error     (Nothing, s') ->        case parseURI (T.unpack s') of  -- requires absolute URI-            Just URI{ uriScheme = "file:", uriPath = upath}+            Just URI{ uriScheme = sch, uriPath = upath}+              | map toLower sch == "file:"               -> readLocalFile $ uriPathToPath (T.pack upath)-            Just URI{ uriScheme = "data:", uriPath = upath}+              | map toLower sch == "data:"               -> pure $ extractURIData upath             -- We don't want to treat C:/ as a scheme:             Just u' | length (uriScheme u') > 2 -> openURL (T.pack $ show u')@@ -420,18 +435,40 @@ -- Extract data from a data URI's path component. extractURIData :: String -> (B.ByteString, Maybe MimeType) extractURIData upath =-  case break (== ';') (filter (/= ' ') mimespec) of-     (mime', ";base64") -> (decodeLenient contents, Just (T.pack mime'))-     (mime', _) -> (contents, Just (T.pack mime'))+  if isBase64+     then (decodeLenient contents, Just mime)+     else (contents, Just mime)   where-    (mimespec, rest) = break (== ',') $ unEscapeString upath-    contents = UTF8.fromString $ drop 1 rest+    (mimespec, rest) = break (== ',') upath+    -- The base64 indicator is the final parameter of the media type+    -- and may follow other parameters, e.g.+    -- data:text/plain;charset=utf-8;base64,...+    metaParts = splitParts (filter (/= ' ') (unEscapeString mimespec))+    splitParts s = case break (== ';') s of+                     (x, [])    -> [x]+                     (x, _:s')  -> x : splitParts s'+    isBase64 = length metaParts > 1 && last metaParts == "base64"+    mime = T.pack $ intercalate ";" $+             if isBase64 then init metaParts else metaParts+    -- Percent-escapes in a data URI represent raw octets (RFC 2397),+    -- so we decode them to bytes directly.  (unEscapeString cannot be+    -- used here: it UTF-8-decodes consecutive escapes, which corrupts+    -- binary data when the result is re-encoded.)+    contents = unEscapeBytes $ drop 1 rest+    unEscapeBytes = B8.pack . go+      where+        go ('%':x:y:cs)+          | isHexDigit x, isHexDigit y+            = chr (digitToInt x * 16 + digitToInt y) : go cs+        go (c:cs) = c : go cs+        go [] = []  -- | Checks if the file path is relative to a parent directory. isRelativeToParentDir :: FilePath -> Bool isRelativeToParentDir fname =-  let canonical = makeCanonical fname-   in length canonical >= 2 && take 2 canonical == ".."+  case splitDirectories (makeCanonical fname) of+    "..":_ -> True+    _      -> False  -- | Returns possible user data directory if the file path refers to a file or -- subdirectory within it.@@ -468,9 +505,9 @@ toTextM fp bs =   case TSE.decodeUtf8' . filterCRs . dropBOM $ bs of     Left (TSE.DecodeError _ (Just w)) ->-      case B.elemIndex w bs of-        Just offset ->-          throwError $ PandocUTF8DecodingError (T.pack fp) offset w+      case findDecodingError bs of+        Just (offset, w') ->+          throwError $ PandocUTF8DecodingError (T.pack fp) offset w'         Nothing -> throwError $ PandocUTF8DecodingError (T.pack fp) 0 w     Left e -> throwError $ PandocAppError (tshow e)     Right t -> return t@@ -479,7 +516,45 @@      if "\xEF\xBB\xBF" `B.isPrefixOf` bs'         then B.drop 3 bs'         else bs'-   filterCRs = B.filter (/=13)+   -- Only allocate a filtered copy if a CR is actually present;+   -- B.elem compiles to a fast memchr.+   filterCRs bs' = if 13 `B.elem` bs'+                      then B.filter (/=13) bs'+                      else bs'++-- Find the offset and value of the first byte at which UTF-8 decoding+-- fails (RFC 3629).  Used to give an accurate position in decoding+-- error messages.  (The BOM and CR bytes stripped before decoding are+-- themselves valid UTF-8, so scanning the unstripped input finds the+-- same error, at its offset in the original file.)+findDecodingError :: B.ByteString -> Maybe (Int, Word8)+findDecodingError = go 0+ where+  go !i bs = case B.uncons bs of+    Nothing -> Nothing+    Just (w, rest)+      | w < 0x80  -> go (i + 1) rest+      | w < 0xC2  -> Just (i, w)  -- continuation byte or overlong lead+      | w == 0xE0 -> cont i w rest [(0xA0,0xBF),(0x80,0xBF)]+      | w == 0xED -> cont i w rest [(0x80,0x9F),(0x80,0xBF)]  -- no surrogates+      | w < 0xE0  -> cont i w rest [(0x80,0xBF)]+      | w <  0xF0 -> cont i w rest [(0x80,0xBF),(0x80,0xBF)]+      | w == 0xF0 -> cont i w rest [(0x90,0xBF),(0x80,0xBF),(0x80,0xBF)]+      | w == 0xF4 -> cont i w rest [(0x80,0x8F),(0x80,0xBF),(0x80,0xBF)]+      | w <  0xF4 -> cont i w rest [(0x80,0xBF),(0x80,0xBF),(0x80,0xBF)]+      | otherwise -> Just (i, w)  -- above U+10FFFF+  -- check that the bytes following the lead byte w at offset i fall+  -- into the given ranges; report the first byte that does not+  cont i w = go' (i + 1)+   where+    go' !j rest [] = go j rest+    go' !j rest ((lo,hi):ranges) =+      case B.uncons rest of+        Just (b, rest')+          | b >= lo && b <= hi -> go' (j + 1) rest' ranges+          | otherwise          -> Just (j, b)+        -- input ends in the middle of a sequence: report the lead byte+        Nothing -> Just (i, w)  -- | Returns @fp@ if the file exists in the current directory; otherwise -- searches for the data file relative to @/subdir/@. Returns @Nothing@
src/Text/Pandoc/Class/PandocPure.hs view
@@ -47,7 +47,8 @@ import Data.Time.Clock.POSIX ( posixSecondsToUTCTime ) import Data.Time.LocalTime (TimeZone, utc) import Data.Word (Word8)-import System.Directory (doesDirectoryExist, getDirectoryContents)+import System.Directory (canonicalizePath, doesDirectoryExist,+                         getDirectoryContents) import System.FilePath ((</>)) import System.FilePath.Glob (match, compile) import System.Random (StdGen, mkStdGen)@@ -57,6 +58,7 @@ import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL import qualified Data.Map as M+import qualified Data.Set as Set import qualified Data.Text as T import qualified System.Directory as Directory (getModificationTime) @@ -143,20 +145,27 @@ -- | Add the specified file to the FileTree. If file -- is a directory, add its contents recursively. addToFileTree :: FileTree -> FilePath -> IO FileTree-addToFileTree tree fp = do-  isdir <- doesDirectoryExist fp-  if isdir-     then do -- recursively add contents of directories-       let isSpecial ".." = True-           isSpecial "."  = True-           isSpecial _    = False-       fs <- map (fp </>) . filter (not . isSpecial) <$> getDirectoryContents fp-       foldM addToFileTree tree fs-     else do-       contents <- B.readFile fp-       mtime <- Directory.getModificationTime fp-       return $ insertInFileTree fp FileInfo{ infoFileMTime = mtime-                                            , infoFileContents = contents } tree+addToFileTree = go Set.empty+ where+  go ancestors tree fp = do+    isdir <- doesDirectoryExist fp+    if isdir+       then do -- recursively add contents of directories+         canonical <- canonicalizePath fp+         if canonical `Set.member` ancestors+            then return tree  -- don't follow symlink cycles+            else do+              let isSpecial ".." = True+                  isSpecial "."  = True+                  isSpecial _    = False+              fs <- map (fp </>) . filter (not . isSpecial) <$>+                      getDirectoryContents fp+              foldM (go (Set.insert canonical ancestors)) tree fs+       else do+         contents <- B.readFile fp+         mtime <- Directory.getModificationTime fp+         return $ insertInFileTree fp FileInfo{ infoFileMTime = mtime+                                              , infoFileContents = contents } tree  -- | Insert an ersatz file into the 'FileTree'. insertInFileTree :: FilePath -> FileInfo -> FileTree -> FileTree
src/Text/Pandoc/Data.hs view
@@ -25,6 +25,7 @@ import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString as B import Codec.Archive.Zip+import Data.List (sort) import qualified Data.Text as T import Control.Monad.Except (throwError) import Text.Pandoc.Error (PandocError(..))@@ -220,10 +221,20 @@ #ifdef EMBED_DATA_FILES   let allDataFiles = map fst dataFiles #else-  allDataFiles <- filter (\x -> x /= "." && x /= "..") <$>-                      (getDataDir >>= getDirectoryContents)+  let listDirectoryRecursive d = do+        xs <- listDirectory d+        concat <$>+           mapM (\f -> do+                      isdir <- doesDirectoryExist (d </> f)+                      if isdir+                         then map (f </>) <$> listDirectoryRecursive (d </> f)+                         else pure [f]) xs+  ddir <- getDataDir+  allDataFiles <- ("MANUAL.txt" :) <$>+                     listDirectoryRecursive (ddir </> "data") #endif-  return $ "reference.docx" : "reference.odt" : "reference.pptx" : allDataFiles+  return $ sort $+    "reference.docx" : "reference.odt" : "reference.pptx" : allDataFiles  -- | Return appropriate user data directory for platform.  We use -- XDG_DATA_HOME (or its default value), but for backwards compatibility,
src/Text/Pandoc/ImageSize.hs view
@@ -38,8 +38,8 @@ import qualified Data.ByteString.Lazy as BL import Data.Binary.Get import Data.Bits ((.&.), shiftR, shiftL)-import Data.Word (bitReverse32, Word32)-import Data.Maybe (isJust, fromJust)+import Data.Word (Word32)+import Data.Maybe (fromMaybe) import Data.Char (isDigit) import Control.Monad import Text.Pandoc.Shared (safeRead)@@ -57,13 +57,13 @@ import qualified Data.Attoparsec.ByteString.Char8 as A import qualified Codec.Picture.Metadata as Metadata import Codec.Picture (decodeImageWithMetadata)-import Codec.Compression.Zlib (decompress)+import qualified Codec.Compression.Zlib.Internal as Zlib -- import Debug.Trace  -- quick and dirty functions to get image sizes  data ImageType = Png | Gif | Jpeg | Svg | Pdf | Eps | Emf | Tiff | Webp | Avif-                 deriving Show+                 deriving (Show, Eq) data Direction = Width | Height instance Show Direction where   show Width  = "width"@@ -118,7 +118,7 @@                      "\x47\x49\x46\x38" -> return Gif                      "\x49\x49\x2a\x00" -> return Tiff                      "\x4D\x4D\x00\x2a" -> return Tiff-                     "\xff\xd8\xff\xbd" -> return Jpeg  -- JPEG without application segment -- see p.32 in https://www.w3.org/Graphics/JPEG/itu-t81.pdf (and https://gist.github.com/leommoore/f9e57ba2aa4bf197ebc5?permalink_comment_id=3863054#gistcomment-3863054)+                     "\xff\xd8\xff\xdb" -> return Jpeg  -- JPEG without application segment -- see p.32 in https://www.w3.org/Graphics/JPEG/itu-t81.pdf (and https://gist.github.com/leommoore/f9e57ba2aa4bf197ebc5?permalink_comment_id=3863054#gistcomment-3863054)                      _ | B.take 3 img == "\xff\xd8\xff"                           && (let byte4 = B.take 1 (B.drop 3 img)                               in byte4 >= "\xe0" && byte4 <= "\xef")  -- JPEG with application segment@@ -139,18 +139,39 @@                      "RIFF"                        | B.take 4 (B.drop 8 img) == "WEBP"                                         -> return Webp-                     _ | B.take 4 (B.drop 4 img) == "ftyp" -> return Avif+                     _ | B.take 4 (B.drop 4 img) == "ftyp"+                          -- require the AVIF brand, so that other+                          -- ISO media (mp4, mov, heic...) is excluded:+                          && (B.take 4 (B.drop 8 img) == "avif" ||+                              B.take 4 (B.drop 8 img) == "avis")+                                        -> return Avif                      _ -> mzero +-- | Check for the presence of an @<svg@ (or @<SVG@) tag, in a+-- single pass over the file. findSvgTag :: ByteString -> Bool-findSvgTag img = "<svg" `B.isInfixOf` img || "<SVG" `B.isInfixOf` img+findSvgTag img = case B.elemIndex '<' img of+  Nothing -> False+  Just i ->+    case B.drop (i + 1) img of+      rest | "svg" `B.isPrefixOf` rest -> True+           | "SVG" `B.isPrefixOf` rest -> True+           | otherwise -> findSvgTag rest  imageSize :: WriterOptions -> ByteString -> Either T.Text ImageSize imageSize opts img = checkDpi <$>   case imageType img of-       Just Png  -> getSize img+       Just Png  -> case pngSize img of+                      Just sz -> Right sz+                      -- fall back to the full decoder if the header+                      -- scan fails:+                      Nothing -> getSize img        Just Gif  -> getSize img-       Just Jpeg -> getSize img+       Just Jpeg -> case jpegSize img of+                      Just sz -> Right sz+                      -- fall back to the full decoder if the header+                      -- scan fails:+                      Nothing -> getSize img        Just Tiff -> getSize img        Just Svg  -> mbToEither "could not determine SVG size" $ svgSize opts img        Just Eps  -> mbToEither "could not determine EPS size" $ epsSize img@@ -162,10 +183,10 @@   where mbToEither msg Nothing  = Left msg         mbToEither _   (Just x) = Right x         -- see #6880, some defective JPEGs may encode dpi 0, so default to 72-        -- if that value is 0+        -- if that value is 0 or negative         checkDpi size =-          size{ dpiX = if dpiX size == 0 then 72 else dpiX size-              , dpiY = if dpiY size == 0 then 72 else dpiY size }+          size{ dpiX = if dpiX size <= 0 then 72 else dpiX size+              , dpiY = if dpiY size <= 0 then 72 else dpiY size }   sizeInPixels :: ImageSize -> (Integer, Integer)@@ -236,11 +257,12 @@ showInPixel _ (Percent _) = "" showInPixel opts dim = T.pack $ show $ inPixel opts dim --- | Maybe split a string into a leading number and trailing unit, e.g. "3cm" to Just (3.0, "cm")+-- | Maybe split a string into a leading number and trailing unit, e.g. "3cm" to Just (3.0, "cm").+-- Whitespace between the number and the unit is ignored. numUnit :: T.Text -> Maybe (Double, T.Text) numUnit s =   let (nums, unit) = T.span (\c -> isDigit c || ('.'==c)) s-  in (\n -> (n, unit)) <$> safeRead nums+  in (\n -> (n, T.stripStart unit)) <$> safeRead nums  -- | Scale a dimension by a factor. scaleDimension :: Double -> Dimension -> Dimension@@ -287,12 +309,14 @@   case ls' of        []    -> mzero        (x:_) -> case B.words x of-                     [_, _, _, ux, uy] -> do-                        ux' <- safeRead $ TE.decodeUtf8 ux-                        uy' <- safeRead $ TE.decodeUtf8 uy+                     [_, llx, lly, urx, ury] -> do+                        llx' <- safeRead $ TE.decodeUtf8Lenient llx+                        lly' <- safeRead $ TE.decodeUtf8Lenient lly+                        urx' <- safeRead $ TE.decodeUtf8Lenient urx+                        ury' <- safeRead $ TE.decodeUtf8Lenient ury                         return ImageSize{-                            pxX  = ux'-                          , pxY  = uy'+                            pxX  = urx' - llx'+                          , pxY  = ury' - lly'                           , dpiX = 72                           , dpiY = 72 }                      _ -> mzero@@ -330,13 +354,10 @@       A.skipSpace       A.string "/ObjStm"       _ <- A.manyTill pLine (A.string "stream" *> pEol)-      stream <- BL.pack <$> A.manyTill-                        (AW.satisfy (const True))-                        (pEol *> A.string "endstream" *> pEol)-      let contents = BL.toStrict (decompress stream)-      case A.parseOnly pPdfSize contents of-        Left _ -> pPdfSize-        Right is -> pure is)+      stream <- pTakeUntil "endstream"+      case A.parseOnly pPdfSize <$> safeDecompress stream of+        Just (Right is) -> pure is+        _ -> pPdfSize)   <|>   (A.char '/' *> pPdfSize)  where@@ -346,6 +367,192 @@    pEol = A.satisfy iseol *> A.skipMany (A.satisfy iseol)    pLine = A.takeWhile (not . iseol) <* pEol +-- | Consume input up to and including the first occurrence of the+-- (non-empty) terminator, returning what precedes it.  Unlike+-- @manyTill anyWord8@, this consumes chunks at a time.+pTakeUntil :: ByteString -> A.Parser ByteString+pTakeUntil terminator = B.concat . reverse <$> go []+ where+  go acc = do+    chunk <- A.takeWhile (/= B.head terminator)+    let acc' = chunk : acc+    (A.string terminator *> pure acc')+      <|> (do c <- A.take 1+              go (c : acc'))++-- | Decompress a zlib stream, returning Nothing on malformed input.+-- ('Codec.Compression.Zlib.decompress' would instead throw an+-- exception from pure code when the corrupt part of its lazy result+-- is forced.)+safeDecompress :: ByteString -> Maybe ByteString+safeDecompress bs = fmap B.concat $+  Zlib.foldDecompressStreamWithInput+    (\chunk rest -> (chunk :) <$> rest)+    (const (Just []))+    (const Nothing)+    (Zlib.decompressST Zlib.zlibFormat Zlib.defaultDecompressParams)+    (BL.fromStrict bs)++-- | Extract PNG size from the IHDR and pHYs chunks, without+-- decoding any image data.  (For paletted PNGs, JuicyPixels decodes+-- the whole image before returning metadata.)+pngSize :: ByteString -> Maybe ImageSize+pngSize img =+  case runGetOrFail pPngSize (BL.fromStrict img) of+    Left _ -> Nothing+    Right (_, _, sz) -> Just sz+ where+  pPngSize = do+    skip 8  -- signature+    -- the IHDR chunk always comes first:+    ihdrLen <- getWord32be+    ihdr <- getByteString 4+    when (ihdr /= "IHDR" || ihdrLen < 13) $ fail "IHDR chunk not found"+    w <- getWord32be+    h <- getWord32be+    skip (fromIntegral ihdrLen - 8 + 4)  -- rest of chunk and CRC+    (dx, dy) <- findPhys+    return ImageSize{ pxX = toInteger w, pxY = toInteger h+                    , dpiX = dx, dpiY = dy }+  -- scan the following chunks for pHYs:+  findPhys = do+    done <- isEmpty+    if done+       then return (72, 72)+       else do+         len <- getWord32be+         typ <- getByteString 4+         case typ of+           "pHYs" -> do+             ppuX <- getWord32be+             ppuY <- getWord32be+             unit <- getWord8+             return $ if unit == 1  -- pixels per meter+                         then (dpmToDpi (toInteger ppuX),+                               dpmToDpi (toInteger ppuY))+                         else (72, 72)+           "IDAT" -> return (72, 72)  -- pHYs must precede image data+           "IEND" -> return (72, 72)+           _ -> skip (fromIntegral len + 4) *> findPhys++-- | Convert dots per meter to dots per inch, using the same integer+-- arithmetic as JuicyPixels for consistency.+dpmToDpi :: Integer -> Integer+dpmToDpi z = z * 254 `div` 10000++-- | Extract JPEG size from the header, without decoding any image+-- data.  Scans the marker segments preceding the entropy-coded data+-- for a start-of-frame marker (which gives the dimensions in pixels)+-- and JFIF APP0 and Exif APP1 segments (which give the resolution).+jpegSize :: ByteString -> Maybe ImageSize+jpegSize img =+  case runGetOrFail (skip 2 *> scanSegments Nothing Nothing)+         (BL.fromStrict img) of+    Left _ -> Nothing+    Right (_, _, sz) -> Just sz+ where+  scanSegments jfifDpi exifDpi = do+    ff <- getWord8+    when (ff /= 0xff) $ fail "malformed JPEG segment"+    marker <- skipFill+    scanSegment marker jfifDpi exifDpi++  -- extra 0xff bytes before a marker are padding:+  skipFill = do+    b <- getWord8+    if b == 0xff then skipFill else return b++  scanSegment marker jfifDpi exifDpi+    -- start of frame (baseline, progressive, etc.); C4, C8, and CC+    -- in this range are entropy-coding markers, not SOF:+    | marker >= 0xc0 && marker <= 0xcf+      && marker `notElem` [0xc4, 0xc8, 0xcc] = do+        skip 3  -- segment length and sample precision+        h <- getWord16be+        w <- getWord16be+        -- as in JuicyPixels, Exif resolution overrides JFIF:+        let (dx, dy) = fromMaybe (fromMaybe (72, 72) jfifDpi) exifDpi+        return ImageSize{ pxX = toInteger w, pxY = toInteger h+                        , dpiX = dx, dpiY = dy }+    | marker == 0xd9 || marker == 0xda =+        fail "no SOF marker before image data"  -- EOI or SOS+    | (marker >= 0xd0 && marker <= 0xd7) || marker == 0x01 =+        scanSegments jfifDpi exifDpi  -- markers without a payload+    | otherwise = do+        len <- getWord16be+        when (len < 2) $ fail "invalid segment length"+        let n = fromIntegral len - 2+        case marker of+          0xe0 -> do  -- APP0 (JFIF)+            body <- getByteString n+            scanSegments (jfifDensity body <|> jfifDpi) exifDpi+          0xe1 -> do  -- APP1 (Exif)+            body <- getByteString n+            scanSegments jfifDpi (exifDensity body <|> exifDpi)+          _ -> skip n *> scanSegments jfifDpi exifDpi++-- | Pixel density from the body of a JFIF APP0 segment.+jfifDensity :: ByteString -> Maybe (Integer, Integer)+jfifDensity body = do+  guard $ "JFIF\0" `B.isPrefixOf` body+  -- after the identifier and 2-byte version: density units,+  -- horizontal density, vertical density+  (units, x, y) <- getAt 7 ((,,) <$> getWord8 <*> getWord16be <*> getWord16be)+                     body+  case units of+    1 -> Just (toInteger x, toInteger y)  -- dots per inch+    2 -> Just (dpcmToDpi (toInteger x), dpcmToDpi (toInteger y))  -- per cm+    _ -> Nothing++-- | Resolution from the TIFF structure in the body of an Exif APP1+-- segment.+exifDensity :: ByteString -> Maybe (Integer, Integer)+exifDensity body = do+  guard $ "Exif\0\0" `B.isPrefixOf` body+  let tiff = B.drop 6 body  -- offsets are relative to the TIFF header+  (w16, w32) <- case B.take 2 tiff of+    "II" -> Just (getWord16le, getWord32le)+    "MM" -> Just (getWord16be, getWord32be)+    _    -> Nothing+  ifd <- fromIntegral <$> getAt 4 w32 tiff+  n <- fromIntegral <$> getAt ifd w16 tiff+  entries <- mapM (\i -> do let off = ifd + 2 + 12 * i+                            tag <- getAt off w16 tiff+                            return (tag, off))+                  [0 .. n - 1 :: Int]+  -- resolution unit (SHORT, stored inline in the value field):+  -- 2 = inches, 3 = centimeters+  unit <- lookup 0x0128 entries >>= \off -> getAt (off + 8) w16 tiff+  toDpi <- case unit of+             2 -> Just id+             3 -> Just dpcmToDpi+             _ -> Nothing+  let resolution tag = do+        off <- lookup tag entries+        -- the value field holds the offset of the RATIONAL value:+        valOff <- fromIntegral <$> getAt (off + 8) w32 tiff+        num <- getAt valOff w32 tiff+        den <- getAt (valOff + 4) w32 tiff+        guard $ den /= 0+        return $ toDpi (toInteger num `div` toInteger den)+  x <- resolution 0x011a  -- XResolution+  y <- resolution 0x011b  -- YResolution+  return (x, y)++-- | Convert dots per centimeter to dots per inch, using the same+-- integer arithmetic as JuicyPixels for consistency.+dpcmToDpi :: Integer -> Integer+dpcmToDpi z = z * 254 `div` 100++-- | Run a 'Get' parser at the given offset in a strict ByteString,+-- returning Nothing if it fails (e.g. by running out of input).+getAt :: Int -> Get a -> ByteString -> Maybe a+getAt off g bs+  | off < 0 = Nothing+  | otherwise = case runGetOrFail (skip off *> g) (BL.fromStrict bs) of+      Left _ -> Nothing+      Right (_, _, x) -> Just x+ getSize :: ByteString -> Either T.Text ImageSize getSize img =   case decodeImageWithMetadata img of@@ -369,8 +576,11 @@                                      $ TL.fromStrict $ UTF8.toText $ dropBOM img   let viewboxSize = do         vb <- findAttrBy (== QName "viewBox" Nothing Nothing) doc-        [_,_,w,h] <- mapM safeRead (T.words vb)-        return (w,h)+        -- per the SVG spec, the numbers are separated by whitespace+        -- and/or a comma, and may be fractional:+        [_,_,w,h] <- mapM safeRead $ T.words $+                       T.map (\c -> if c == ',' then ' ' else c) vb+        return (floor (w :: Double), floor (h :: Double))   let dpi = fromIntegral $ writerDpi opts   let dirToInt dir = do         dim <- findAttrBy (== QName dir Nothing Nothing) doc >>= lengthToDim@@ -391,26 +601,34 @@   let     parseheader = runGetOrFail $ do       skip 0x18             -- 0x00-      frameL <- getWord32le -- 0x18  measured in 1/100 of a millimetre-      frameT <- getWord32le -- 0x1C-      frameR <- getWord32le -- 0x20-      frameB <- getWord32le -- 0x24+      -- the frame bounds are signed, measured in 1/100 of a millimetre:+      frameL <- getInt32le  -- 0x18+      frameT <- getInt32le  -- 0x1C+      frameR <- getInt32le  -- 0x20+      frameB <- getInt32le  -- 0x24       skip 0x20             -- 0x28       deviceX <- getWord32le  -- 0x48 pixels of reference device       deviceY <- getWord32le  -- 0x4C       mmX <- getWord32le      -- 0x50 real mm of reference device (always 320*240?)-      mmY <- getWord32le      -- 0x58+      mmY <- getWord32le      -- 0x54       -- end of header+      -- guard against division by zero below; since the ImageSize+      -- fields are lazy, the exception would escape runGetOrFail+      when (mmX == 0 || mmY == 0) $+        fail "EMF header has zero-size reference device"+      -- compute with Integers to avoid Word32 overflow:       let-        w = (deviceX * (frameR - frameL)) `quot` (mmX * 100)-        h = (deviceY * (frameB - frameT)) `quot` (mmY * 100)-        dpiW = (deviceX * 254) `quot` (mmX * 10)-        dpiH = (deviceY * 254) `quot` (mmY * 10)+        w = (toInteger deviceX * (toInteger frameR - toInteger frameL))+              `quot` (toInteger mmX * 100)+        h = (toInteger deviceY * (toInteger frameB - toInteger frameT))+              `quot` (toInteger mmY * 100)+        dpiW = (toInteger deviceX * 254) `quot` (toInteger mmX * 10)+        dpiH = (toInteger deviceY * 254) `quot` (toInteger mmY * 10)       return $ ImageSize-        { pxX = fromIntegral w-        , pxY = fromIntegral h-        , dpiX = fromIntegral dpiW-        , dpiY = fromIntegral dpiH+        { pxX = w+        , pxY = h+        , dpiX = dpiW+        , dpiY = dpiH         }   in     case parseheader . BL.fromStrict $ img of@@ -446,24 +664,23 @@       AW.word8 0x2a       width16 <- AW.take 2       height16 <- AW.take 2-      let w = toInteger <$> decode lossySize width16-          h = toInteger <$> decode lossySize height16-      guard $ isJust w && isJust h-      return (fromJust w, fromJust h)-    losslessSizes = runGetOrFail $ do-      bitReverse32 <$> getWord32le+      case (decode lossySize width16, decode lossySize height16) of+        (Just w, Just h) -> return (toInteger w, toInteger h)+        _ -> empty+    -- The VP8L bitstream is read starting from the least significant+    -- bit of each byte, so after reading the 4 bytes as a little-endian+    -- word, width - 1 is in bits 0-13 and height - 1 in bits 14-27.+    losslessSizes = runGetOrFail getWord32le     losslessSize word = 1 + (word .&. 0x3FFF)     lossless = do       AW.string "VP8L"       AW.take 4 -- length in bytes of VP8 Lossless chunk size       AW.word8 0x2f  -- webp lossless stream magic       sizes <- AW.take 4-      let mbword = decode losslessSizes sizes-      guard $ isJust mbword-      let word = fromJust mbword-      let w = toInteger $ losslessSize word-          h = toInteger $ losslessSize (word `shiftR` 14)-      return (w, h)+      case decode losslessSizes sizes of+        Just word -> return ( toInteger $ losslessSize word+                            , toInteger $ losslessSize (word `shiftR` 14) )+        Nothing -> empty     extendedSize = runGetOrFail $ do       low <- toInteger <$> getWord16le       high <- toInteger <$> getWord8@@ -473,10 +690,9 @@       AW.take 8  -- VP8X chunk length, flags and reserved area       width24 <- AW.take 3       height24 <- AW.take 3-      let w = decode extendedSize width24-          h = decode extendedSize height24-      guard $ isJust w && isJust h-      return (fromJust w, fromJust h)+      case (decode extendedSize width24, decode extendedSize height24) of+        (Just w, Just h) -> return (w, h)+        _ -> empty  webpSize :: WriterOptions -> ByteString -> Maybe ImageSize webpSize opts img =@@ -485,32 +701,49 @@     Right sz -> Just sz { dpiX = fromIntegral $ writerDpi opts, dpiY = fromIntegral $ writerDpi opts}  avifSize :: WriterOptions -> ByteString -> Maybe ImageSize-avifSize _opts img =+avifSize opts img =   case runGetOrFail (verifyFtyp >> findAvifDimensions) (BL.fromStrict img) of     Left (_, _, _err) -> Nothing     Right (_, _, (width, height)) ->       Just $ ImageSize { pxX = fromIntegral width                        , pxY = fromIntegral height-                       , dpiX = 72-                       , dpiY = 72 }+                       , dpiX = fromIntegral $ writerDpi opts+                       , dpiY = fromIntegral $ writerDpi opts }  ---- AVIF parsing: +-- | Read an ISO BMFF box header, returning the box type, the size of+-- the box contents, and the size of the header itself.  Handles the+-- 64-bit "largesize" field (size == 1) and boxes that extend to the+-- end of the file (size == 0).+getBoxHeader :: Get (B.ByteString, Int, Int)+getBoxHeader = do+  boxSize <- getWord32be+  boxType <- getByteString 4+  case boxSize of+    0 -> do  -- the box extends to the end of the file+      rest <- lookAhead getRemainingLazyByteString+      return (boxType, fromIntegral (BL.length rest), 8)+    1 -> do  -- a 64-bit size follows the box type+      largeSize <- getWord64be+      when (largeSize < 16 || largeSize > 0x7fffffff) $+        fail "invalid box largesize"+      return (boxType, fromIntegral largeSize - 16, 16)+    _ -> do+      when (boxSize < 8) $ fail "invalid box size"+      return (boxType, fromIntegral boxSize - 8, 8)+ verifyFtyp :: Get () verifyFtyp = do-  ftypSize <- getWord32be-  when (ftypSize < 16) $ fail "Invalid ftyp size"--  ftyp <- getByteString 4-  unless (ftyp == "ftyp") $ fail "ftyp signature not found"+  (boxType, contentSize, _) <- getBoxHeader+  unless (boxType == "ftyp") $ fail "ftyp signature not found"+  when (contentSize < 8) $ fail "Invalid ftyp size"    brand <- getByteString 4   unless (brand == "avif" || brand == "avis") $ fail "Not an AVIF file"    -- Skip minor version and compatible brands-  -- (we've read 12 bytes: size+type+brand)-  let remaining_ftyp = fromIntegral ftypSize - 12-  when (remaining_ftyp > 0) $ skip remaining_ftyp+  skip (contentSize - 4)  findAvifDimensions :: Get (Word32, Word32) findAvifDimensions = searchAvifBoxes []@@ -521,10 +754,7 @@   if isempty     then fail $ "No dimensions found. Searched: " ++ show (reverse path)     else do-      boxSize <- getWord32be-      boxType <- getByteString 4--      let contentSize = fromIntegral boxSize - 8+      (boxType, contentSize, _) <- getBoxHeader       let newPath = boxType : path        -- If it's a container box, search inside it@@ -535,10 +765,8 @@           result <- tryParseDimensions boxType contentSize           case result of             Just dims -> return dims-            Nothing -> do-              -- Skip this box and continue-              when (contentSize > 0 && contentSize < 10000000) $-                skip contentSize+            Nothing ->+              -- Don't skip here - tryParseDimensions already handled it               searchAvifBoxes path  tryParseDimensions :: B.ByteString -> Int -> Get (Maybe (Word32, Word32))@@ -575,8 +803,12 @@   version <- getWord8   skip 3  -- flags -  -- Skip to width/height based on version-  let skipBytes = if version == 1 then 76 else 64+  -- Skip to width/height based on version:+  -- creation/modification times, track ID, reserved, duration+  -- (20 bytes with 32-bit times, 32 bytes with 64-bit times),+  -- then 8 reserved, layer, alternate_group, volume, reserved+  -- (2 bytes each), and the 36-byte transformation matrix+  let skipBytes = if version == 1 then 84 else 72   skip skipBytes    width <- getWord32be@@ -631,13 +863,11 @@ searchAvifBoxesInRange remaining' path   | remaining' < 8 = searchAvifBoxes path   | otherwise = do-      boxSize <- getWord32be-      boxType <- getByteString 4+      (boxType, contentSize, headerSize) <- getBoxHeader -      let contentSize = fromIntegral boxSize - 8       let newPath = boxType : path -      when (contentSize < 0 || fromIntegral boxSize > remaining') $ do+      when (contentSize + headerSize > remaining') $         fail $ "Malformed box at path: " ++ show (reverse newPath)        if isContainerBox boxType@@ -648,7 +878,8 @@             Just dims -> return dims             Nothing -> do               -- Don't skip here - tryParseDimensions already handled it-              searchAvifBoxesInRange (remaining' - fromIntegral boxSize) path+              searchAvifBoxesInRange+                (remaining' - contentSize - headerSize) path  isContainerBox :: B.ByteString -> Bool isContainerBox boxType = boxType `elem`
src/Text/Pandoc/MediaBag.hs view
@@ -23,26 +23,29 @@                      mediaDirectory,                      mediaItems                      ) where-import Crypto.Hash (hashWith, SHA1(SHA1))+import Crypto.Hash (hashlazy, Digest, SHA1) import qualified Data.ByteString.Lazy as BL+import Data.Char (toLower) import Data.Data (Data)-import qualified Data.Map as M+import qualified Data.Map.Strict as M import Data.Maybe (fromMaybe, isNothing) import Data.Typeable (Typeable) import System.FilePath import qualified System.FilePath.Posix as Posix import qualified System.FilePath.Windows as Windows import Text.Pandoc.MIME (MimeType, getMimeTypeDef, extensionFromMimeType)+import Text.Pandoc.Shared (makeCanonical) import Data.Text (Text) import qualified Data.Text as T-import Network.URI (URI (..), isURI, parseURI, unEscapeString)-import Data.List (isInfixOf)+import Network.URI (URI (..), parseURI, unEscapeString)+import Text.Pandoc.URI (isURI)  data MediaItem =   MediaItem-  { mediaMimeType :: MimeType-  , mediaPath :: FilePath+  { mediaMimeType :: !MimeType+  , mediaPath :: !FilePath   , mediaContents :: BL.ByteString+    -- ^ Left lazy so that contents need not be forced at insert time.   } deriving (Eq, Ord, Show, Data, Typeable)  -- | A container for a collection of binary resources, with names and@@ -55,14 +58,22 @@ instance Show MediaBag where   show bag = "MediaBag " ++ show (mediaDirectory bag) --- | We represent paths with /, in normalized form.  Percent-encoding--- is not resolved.+-- | Check for the (case-insensitive) @data:@ URI scheme.+isDataURI :: FilePath -> Bool+isDataURI = (== "data:") . map toLower . take 5++-- | We represent paths with /, in canonical form (redundant @.@ and+-- @..@ components removed).  Percent-encoding is not resolved. canonicalize :: FilePath -> Text--- avoid an expensive call to isURI for data URIs:-canonicalize fp@('d':'a':'t':'a':':':_) = T.pack fp canonicalize fp-  | isURI fp = T.pack fp-  | otherwise = T.replace "\\" "/" . T.pack . normalise $ fp+  -- avoid an expensive call to isURI for data URIs:+  | isDataURI fp = fp'+  | isURI fp' = fp'+  | otherwise = T.pack . makeCanonical . map slashify $ fp+ where+  fp' = T.pack fp+  slashify '\\' = '/'+  slashify c    = c  -- | Delete a media item from a 'MediaBag', or do nothing if no item corresponds -- to the given path.@@ -80,7 +91,7 @@             -> MediaBag             -> MediaBag insertMedia fp mbMime contents (MediaBag mediamap)- | 'd':'a':'t':'a':':':_ <- fp+ | isDataURI fp  , Just mt' <- mbMime    = MediaBag (M.insert fp'                MediaItem{ mediaPath = hashpath@@ -94,14 +105,24 @@   fp' = canonicalize fp   fp'' = unEscapeString $ T.unpack fp'   uri = parseURI fp-  hashpath = show (hashWith SHA1 (BL.toStrict contents)) <> ext+  hashpath = show (hashlazy contents :: Digest SHA1) <> ext+  -- We only keep the original name if the key contains no+  -- percent-encoding, i.e., unescaping is the identity; otherwise+  -- distinct keys (e.g. "a%20b.png" and "a b.png") could unescape+  -- to the same mediaPath and clobber each other on extraction.   newpath = if Posix.isRelative fp''                  && Windows.isRelative fp''                  && isNothing uri-                 && not (".." `isInfixOf` fp'')-                 && '%' `notElem` fp''+                 && not containsParentRef+                 && not (T.any (== '%') fp')                then fp''                else hashpath+  -- Check for a ".." path component (treating both / and \ as+  -- separators, since the unescaped path may contain backslashes+  -- from percent-encoding); a mere ".." substring (as in+  -- "foo..bar.png") is harmless.+  containsParentRef = ".." `elem`+    Posix.splitDirectories (map (\c -> if c == '\\' then '/' else c) fp'')   fallback = case takeExtension fp'' of                   ".gz" -> getMimeTypeDef $ dropExtension fp''                   _     -> getMimeTypeDef fp''
src/Text/Pandoc/PDF.hs view
@@ -49,7 +49,7 @@ import Text.Pandoc.Extensions (disableExtension, Extension(Ext_smart)) import Text.Pandoc.Process (pipeProcess) import System.Process (readProcessWithExitCode)-import Text.Pandoc.Shared (inDirectory, stringify, tshow)+import Text.Pandoc.Shared (inDirectory, stringify, stringifyInlines, tshow) import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Walk (walkM) import Text.Pandoc.Writers.Shared (getField, metaToContext)@@ -196,7 +196,7 @@                       _ -> []   meta' <- metaToContext opts              (return . literal . stringify)-             (return . literal . stringify)+             (return . literal . stringifyInlines)              meta   let toArgs (f, mbd) = maybe [] (\d -> ["--" <> f, T.unpack d]) mbd   let args   = mathArgs ++ concatMap toArgs
src/Text/Pandoc/Parsing.hs view
@@ -188,7 +188,6 @@       ParsecT,       SourcePos,       SourceName,-      setSourceName,       Column,       Line,       incSourceLine,
src/Text/Pandoc/Parsing/Capabilities.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE MultiParamTypeClasses      #-}+{-# LANGUAGE BangPatterns               #-} {- |    Module      : Text.Pandoc.Parsing    Copyright   : © 2006-2024 John MacFarlane@@ -121,7 +122,9 @@ -- | Update the position on which the last string ended. updateLastStrPos :: (Stream s m a, HasLastStrPosition st)                  => ParsecT s st m ()-updateLastStrPos = getPosition >>= updateState . setLastStrPos . Just+updateLastStrPos = do+  !pos <- getPosition+  updateState $ setLastStrPos $ Just pos  -- | Whether we are right after the end of a string. notAfterString :: (Stream s m a, HasLastStrPosition st) => ParsecT s st m Bool
src/Text/Pandoc/Parsing/Future.hs view
@@ -17,10 +17,8 @@   ) where -import Prelude hiding (Applicative(..))-import Control.Applicative (Applicative(..)) import Control.Monad.Reader-  ( asks, runReader, MonadReader(ask), Reader, ReaderT(ReaderT) )+  ( asks, runReader, MonadReader(ask), Reader )  -- | Reader monad wrapping the parser state. This is used to possibly -- delay evaluation until all relevant information has been parsed and@@ -31,9 +29,8 @@ instance Semigroup a => Semigroup (Future s a) where   (<>) = liftA2 (<>) -instance (Semigroup a, Monoid a) => Monoid (Future s a) where+instance Monoid a => Monoid (Future s a) where   mempty = return mempty-  mappend = (<>)  -- | Run a delayed action with the given state. runF :: Future s a -> s -> a
src/Text/Pandoc/Parsing/General.hs view
@@ -64,7 +64,6 @@  import Control.Monad   ( join-  , liftM   , unless   , void   , when@@ -129,8 +128,6 @@   , setInput   , setPosition   , skipMany-  , sourceColumn-  , sourceName   , tokenPrim   , try   , unexpected@@ -142,6 +139,7 @@ import Text.Pandoc.Parsing.Capabilities import Text.Pandoc.Parsing.State import Text.Pandoc.Parsing.Future (Future (..))+import qualified Data.Map as M import qualified Data.Set as Set import qualified Data.Text as T import qualified Text.Pandoc.Builder as B@@ -151,7 +149,7 @@ -- | Remove whitespace from start and end; just like @'trimInlines'@, -- but lifted into the 'Future' type. trimInlinesF :: Future s Inlines -> Future s Inlines-trimInlinesF = liftM trimInlines+trimInlinesF = fmap trimInlines  -- | Like @count@, but packs its result countChar :: (Stream s m Char, UpdateSourcePos s Char, Monad m)@@ -311,13 +309,15 @@                => [Text] -> ParsecT s st m Text oneOfStringsCI = oneOfStrings' ciMatch   where ciMatch x y = toLower' x == toLower' y-        -- this optimizes toLower by checking common ASCII case-        -- first, before calling the expensive unicode-aware-        -- function:-        toLower' c | isAsciiUpper c = chr (ord c + 32)-                   | isAscii c = c-                   | otherwise = toLower c +-- | Optimized 'toLower': checks the common ASCII case+-- first, before calling the expensive unicode-aware+-- function.+toLower' :: Char -> Char+toLower' c | isAsciiUpper c = chr (ord c + 32)+           | isAscii c = c+           | otherwise = toLower c+ -- | Parses a space or tab. spaceChar :: (Stream s m Char, UpdateSourcePos s Char)           => ParsecT s st m Char@@ -356,7 +356,7 @@              => Int -> ParsecT Sources st m () gobbleSpaces 0 = return () gobbleSpaces n-  | n < 0     = error "gobbleSpaces called with negative number"+  | n < 0     = Prelude.fail "gobbleSpaces called with negative number"   | otherwise = try $ do       char ' ' <|> eatOneSpaceOfTab       gobbleSpaces (n - 1)@@ -369,12 +369,11 @@   -- replace the tab on the input stream with spaces   let numSpaces = tabstop - ((sourceColumn pos - 1) `mod` tabstop)   inp <- getInput-  setInput $-    case inp of-      Sources [] -> error "eatOneSpaceOfTab - empty Sources list"-      Sources ((fp,t):rest) ->-        -- drop the tab and add spaces-        Sources ((fp, T.replicate numSpaces " " <> T.drop 1 t):rest)+  case inp of+    Sources [] -> Prelude.fail "eatOneSpaceOfTab - empty Sources list"+    Sources ((fp,t):rest) -> setInput $+      -- drop the tab and add spaces+      Sources ((fp, T.replicate numSpaces " " <> T.drop 1 t):rest)   char ' '  -- | Gobble up to n spaces; if tabs are encountered, expand them@@ -383,7 +382,7 @@                    => Int -> ParsecT Sources st m Int gobbleAtMostSpaces 0 = return 0 gobbleAtMostSpaces n-  | n < 0     = error "gobbleAtMostSpaces called with negative number"+  | n < 0     = Prelude.fail "gobbleAtMostSpaces called with negative number"   | otherwise = option 0 $ do       char ' ' <|> eatOneSpaceOfTab       (+ 1) <$> gobbleAtMostSpaces (n - 1)@@ -487,8 +486,36 @@ emailPunctChars :: Set.Set Char emailPunctChars = Set.fromList "!\"#$%&'*+-/=?^_{|}~;" +-- | Trie over 'Char', used for efficient matching against the+-- (large, static) set of known URI schemes.+data CharTrie = CharTrie !Bool !(M.Map Char CharTrie)++trieInsert :: Text -> CharTrie -> CharTrie+trieInsert t (CharTrie terminal m) =+  case T.uncons t of+    Nothing -> CharTrie True m+    Just (c, rest) -> CharTrie terminal $+      M.alter (Just . trieInsert rest . fromMaybe (CharTrie False mempty)) c m++-- | Trie of known URI schemes; keys are case-folded with 'toLower''.+schemeTrie :: CharTrie+schemeTrie = foldr trieInsert (CharTrie False mempty)+                   (map (T.map toLower') (Set.toList schemes))++-- | Parses a known URI scheme, case-insensitively, preferring the+-- longest matching scheme.  Returns the scheme as written in the input. uriScheme :: (Stream s m Char, UpdateSourcePos s Char) => ParsecT s st m Text-uriScheme = oneOfStringsCI (Set.toList schemes)+uriScheme = TL.toStrict . TB.toLazyText <$> try (go mempty schemeTrie)+ where+  go acc (CharTrie _ m) = do+    c <- anyChar+    case M.lookup (toLower' c) m of+      Nothing -> Prelude.fail "not a URI scheme"+      Just subtrie@(CharTrie terminal _) ->+        let !acc' = acc <> TB.singleton c+        in if terminal+              then option acc' (try (go acc' subtrie))+              else go acc' subtrie  -- | Parses a URI. Returns pair of original and URI-escaped version. uri :: (Stream s m Char, UpdateSourcePos s Char) => ParsecT s st m (Text, Text)@@ -661,8 +688,7 @@        let id'' = if Ext_ascii_identifiers `extensionEnabled` exts                      then toAsciiText id'                      else id'-       updateState $ updateIdentifierList $ Set.insert id'-       updateState $ updateIdentifierList $ Set.insert id''+       updateState $ updateIdentifierList (Set.insert id' . Set.insert id'')        return (id'',classes,kvs)      else do         unless (T.null ident) $ do
src/Text/Pandoc/Parsing/GridTable.hs view
@@ -39,7 +39,6 @@ import Text.Pandoc.Parsing.General import Text.Pandoc.Sources import Text.Parsec (Stream (..), ParsecT, optional, sepEndBy1, try)- import Data.Maybe (mapMaybe) import qualified Data.Text as T import qualified Text.GridTable as GT@@ -145,7 +144,7 @@                tbl   let rows = GT.rows blkTbl   let toPandocCell (GT.Cell c (GT.RowSpan rs) (GT.ColSpan cs)) =-        fmap (B.cell AlignDefault (B.RowSpan rs) (B.ColSpan cs) . plainify) <$> c+        fmap (B.cell AlignDefault (B.RowSpan rs) (B.ColSpan cs)) <$> c   rows' <- mapM (mapM toPandocCell) rows   columns <- getOption readerColumns   let colspecs = zipWith (\cs w -> (convAlign $ fst cs, B.ColWidth w))@@ -186,11 +185,6 @@    where startsWithSpace t = case T.uncons t of            Nothing     -> True            Just (c, _) -> c == ' '--plainify :: B.Blocks -> B.Blocks-plainify blks = case B.toList blks of-  [Para x] -> B.fromList [Plain x]-  _        -> blks  convAlign :: GT.Alignment -> B.Alignment convAlign GT.AlignLeft    = B.AlignLeft
src/Text/Pandoc/Parsing/Lists.hs view
@@ -24,9 +24,11 @@ import Data.Char   ( isAsciiUpper   , isAsciiLower+  , isDigit   , ord   , toLower   )+import Control.Monad (mzero) import Data.Maybe (fromMaybe) import Text.Pandoc.Definition   ( ListNumberDelim(..)@@ -160,10 +162,29 @@  -- | Parses an ordered list marker and returns list attributes. anyOrderedListMarker :: (Stream s m Char, UpdateSourcePos s Char) => ParsecT s ParserState m ListAttributes-anyOrderedListMarker = choice-  [delimParser numParser | delimParser <- [inPeriod, inOneParen, inTwoParens],-                           numParser <- [decimal, exampleNum, defaultNum, romanOne,-                           lowerAlpha, lowerRoman, upperAlpha, upperRoman]]+anyOrderedListMarker = try $ do+  openParen <- option False $ True <$ char '('+  c <- lookAhead anyChar+  let withDelim f = try $ do+        (style, start) <- f+        delim <-+          ((if style == DefaultStyle+               then DefaultDelim+               else Period) <$ char '.')+           <|>+          ((if openParen+               then TwoParens+               else OneParen) <$ char ')')+        return (start, style, delim)+  case c of+     '@' -> withDelim exampleNum+     '#' -> withDelim defaultNum+     _ | isDigit c -> withDelim exampleNum <|> withDelim decimal+       | isAsciiLower c ->+           withDelim romanOne <|> withDelim lowerAlpha <|> withDelim lowerRoman+       | isAsciiUpper c ->+           withDelim romanOne <|> withDelim upperAlpha <|> withDelim upperRoman+     _ -> mzero  -- | Parses a list number (num) followed by a period, returns list attributes. inPeriod :: (Stream s m Char, UpdateSourcePos s Char)
src/Text/Pandoc/Parsing/Math.hs view
@@ -15,7 +15,7 @@   ) where -import Control.Monad (mzero, when)+import Control.Monad (mzero, when, guard) import Data.Text (Text) import Text.Parsec ((<|>), ParsecT, Stream(..), notFollowedBy, many1, try) import Text.Pandoc.Options@@ -44,8 +44,10 @@                            (try (string "text" >>                                  (("\\text" <>) <$> inBalancedBraces 0 ""))                             <|>  (\c -> T.pack ['\\',c]) <$> anyChar))-                   <|> ("\n" <$ blankline <* notFollowedBy' blankline <* notFollowedBy (char '$'))-                   <|> (T.pack <$> many1 spaceChar <* notFollowedBy (char '$'))+                   <|> ("\n" <$ blankline <* notFollowedBy' blankline <*+                          (guard (op /= "$") <|> notFollowedBy (char '$')))+                   <|> (T.pack <$> many1 spaceChar <*+                          (guard (op /= "$") <|> notFollowedBy (char '$')))                     ) (try $ textStr cl)   notFollowedBy digit  -- to prevent capture of $5   return $ trimMath $ T.concat words'
src/Text/Pandoc/Readers/AsciiDoc.hs view
@@ -25,7 +25,7 @@ import Text.Pandoc.Definition import Text.Pandoc.Walk import Text.Pandoc.Shared (addPandocAttributes, blocksToInlines, safeRead,-                           tshow)+                           tshow, compactifyTable) import qualified Text.Pandoc.UTF8 as UTF8 import qualified AsciiDoc as A import Text.Pandoc.Error@@ -53,9 +53,6 @@    (\(sourcepos, t) ->      A.parseDocument getIncludeFile raiseError (sourceName sourcepos) t)     sources)-   >>= resolveFootnotes-   >>= resolveStem-   >>= resolveIcons    >>= toPandoc  where   getIncludeFile fp = UTF8.toText <$> readFileStrict fp@@ -63,64 +60,56 @@                             $ msg <> " at " <> show fp <>                               " char " <> show pos -toPandoc :: PandocMonad m => A.Document -> m Pandoc-toPandoc doc =-  Pandoc <$> doMeta (A.docMeta doc)-         <*> (B.toList <$> doBlocks (A.docBlocks doc))+-- Context used when converting the AsciiDoc AST: resolved footnote+-- contents, plus document attributes governing stem (math) and icon+-- interpretation.  These are used to resolve footnote references,+-- math types, and icons during conversion; doing this in the course+-- of the conversion is much cheaper than making separate passes over+-- the AST with mapInlines/mapBlocks.+data ADContext = ADContext+  { adFootnotes :: M.Map T.Text B.Inlines+  , adMathType  :: A.MathType+  , adIconFont  :: Bool+  , adIconsDir  :: T.Text+  , adIconType  :: T.Text+  } -resolveFootnotes :: Monad m => A.Document -> m A.Document-resolveFootnotes doc = do-  evalStateT (A.mapInlines go doc) (mempty :: M.Map T.Text [A.Inline])+toPandoc :: PandocMonad m => A.Document -> m Pandoc+toPandoc doc = evalStateT+  (Pandoc <$> doMeta (A.docMeta doc)+          <*> (B.toList <$> doBlocks (A.docBlocks doc)))+  ADContext+    { adFootnotes = mempty+    , adMathType = case M.lookup "stem" docattrs of+                     Just "asciimath" -> A.AsciiMath+                     _ -> A.LaTeXMath+    , adIconFont = case M.lookup "icons" docattrs of+                     Just "font" -> True+                     _ -> False+    , adIconsDir = fromMaybe "./images/icons" $ M.lookup "iconsdir" docattrs+    , adIconType = fromMaybe "png" $ M.lookup "icontype" docattrs+    }  where-   go (A.Inline attr (A.Footnote (Just (A.FootnoteId fnid)) ils)) = do-     fnmap <- get-     case M.lookup fnid fnmap of-       Just ils' ->-         pure $ A.Inline attr (A.Footnote (Just (A.FootnoteId fnid)) ils')-       Nothing -> do-         put $ M.insert fnid ils fnmap-         pure $ A.Inline attr (A.Footnote (Just (A.FootnoteId fnid)) ils)-   go x = pure x--resolveStem :: Monad m => A.Document -> m A.Document-resolveStem doc = do-  let defaultType = case M.lookup "stem" (A.docAttributes (A.docMeta doc)) of-                      Just "asciimath" -> A.AsciiMath-                      _ -> A.LaTeXMath-  let doInlineStem (A.Inline attr (A.Math Nothing t)) =-        pure $ A.Inline attr (A.Math (Just defaultType) t)-      doInlineStem x = pure x-  let doBlockStem (A.Block attr mbtit (A.MathBlock Nothing t)) =-        pure $ A.Block attr mbtit (A.MathBlock (Just defaultType) t)-      doBlockStem x = A.mapInlines doInlineStem x-  A.mapBlocks doBlockStem doc+  docattrs = A.docAttributes (A.docMeta doc)  -- resolve icons as either characters in an icon font or images-resolveIcons :: Monad m => A.Document -> m A.Document-resolveIcons doc = A.mapInlines fromIcon doc+resolveIcon :: ADContext -> A.Inline -> A.Inline+resolveIcon ctx (A.Inline attr (A.Icon name)) =+  if adIconFont ctx+     then A.Inline (addClasses ["fa", "fa-" <> name] attr) (A.Span [])+     else -- default is to use an image+          A.Inline (addClasses ["icon"] attr)+             (A.InlineImage+               (A.Target+                  (adIconsDir ctx <> "/" <> name <> "." <> adIconType ctx))+                  Nothing Nothing Nothing)  where-   docattrs = A.docAttributes (A.docMeta doc)-   iconFont = case M.lookup "icons" docattrs of-                Just "font" -> True-                _ -> False-   iconsdir = fromMaybe "./images/icons" $ M.lookup "iconsdir" docattrs-   icontype = fromMaybe "png" $ M.lookup "icontype" docattrs    addClasses cls (A.Attr ps kvs) =      A.Attr ps $       case M.lookup "role" kvs of        Just r -> M.insert "role" (T.unwords (r : cls)) kvs        Nothing -> M.insert "role" (T.unwords cls) kvs-   fromIcon (A.Inline attr (A.Icon name)) =-     if iconFont-        then pure $-              A.Inline (addClasses ["fa", "fa-" <> name] attr) (A.Span [])-        else pure $ -- default is to use an image-              A.Inline (addClasses ["icon"] attr)-                 (A.InlineImage-                   (A.Target-                      (iconsdir <> "/" <> name <> "." <> icontype))-                      Nothing Nothing Nothing)-   fromIcon x = pure x+resolveIcon _ x = x  addAttribution :: Maybe A.Attribution -> B.Blocks -> B.Blocks addAttribution Nothing bs = bs@@ -132,7 +121,7 @@  where    attrBlock = Para (B.toList $ B.text $ "\x2014 " <> t) -doMeta :: PandocMonad m => A.Meta -> m B.Meta+doMeta :: (PandocMonad m, MonadState ADContext m) => A.Meta -> m B.Meta doMeta meta = do   tit' <- doInlines (A.docTitle meta)   pure $@@ -164,7 +153,7 @@     " (" <> B.link ("mailto:" <> email) "" (B.str email) <> ")")     (A.authorEmail au) -doBlocks :: PandocMonad m => [A.Block] -> m B.Blocks+doBlocks :: (PandocMonad m, MonadState ADContext m) => [A.Block] -> m B.Blocks doBlocks = fmap mconcat . mapM doBlock  addBlockAttr :: A.Attr -> B.Blocks -> B.Blocks@@ -184,7 +173,8 @@   let tit = B.toList tit'   in case B.toList bs of     [B.Table attr _ colspecs thead tbody tfoot] ->-      B.singleton $ B.Table attr (B.Caption Nothing [B.Plain tit])+      B.singleton $ compactifyTable+                  $ B.Table attr (B.Caption Nothing [B.Plain tit])                      colspecs thead tbody tfoot     [B.Figure attr _ bs'] ->       B.singleton $ B.Figure attr (B.Caption Nothing [B.Plain tit]) bs'@@ -195,7 +185,7 @@       B.singleton $ B.Div attr (B.Div ("",["title"],[]) [B.Para tit] : bs')     _ -> B.divWith B.nullAttr (B.divWith ("",["title"],[]) (B.para tit') <> bs) -doBlock :: PandocMonad m => A.Block -> m B.Blocks+doBlock :: (PandocMonad m, MonadState ADContext m) => A.Block -> m B.Blocks doBlock (A.Block attr@(A.Attr ps kvs) mbtitle bt) = do   mbtitle' <- case mbtitle of                 Nothing -> pure Nothing@@ -231,11 +221,12 @@       addAttribution mbattrib . B.blockQuote <$> doBlocks bs     A.Verse mbattrib bs ->       addAttribution mbattrib . B.blockQuote <$> doBlocks bs-    -- TODO when texmath's asciimath parser works, convert:-    A.MathBlock (Just A.AsciiMath) t -> pure $ B.para $ B.displayMath t-    A.MathBlock (Just A.LaTeXMath) t -> pure $ B.para $ B.displayMath t-    A.MathBlock Nothing _ ->-      throwError $ PandocParseError "Encountered math type Nothing"+    A.MathBlock mbMathType t -> do+      mathType <- maybe (gets adMathType) pure mbMathType+      case mathType of+        -- TODO when texmath's asciimath parser works, convert:+        A.AsciiMath -> pure $ B.para $ B.displayMath t+        A.LaTeXMath -> pure $ B.para $ B.displayMath t     A.List (A.BulletList _) items ->       B.bulletList <$> mapM doItem items     A.List A.CheckList items ->@@ -271,8 +262,10 @@                         (B.RowSpan rowspan) (B.ColSpan colspan) . B.toList                    <$> doBlocks bs       let fromRow (A.TableRow cs) = B.Row B.nullAttr <$> mapM fromCell cs-      tbody <- B.TableBody B.nullAttr (B.RowHeadColumns 0) [] <$> mapM fromRow rows+      -- note: conversion is stateful (footnotes), so we convert in+      -- document order: header, body, footer       thead <- B.TableHead B.nullAttr <$> maybe (pure []) (mapM fromRow) mbHeader+      tbody <- B.TableBody B.nullAttr (B.RowHeadColumns 0) [] <$> mapM fromRow rows       tfoot <- B.TableFoot B.nullAttr <$> maybe (pure []) (mapM fromRow) mbFooter       let totalWidth = sum $ map (fromMaybe 1 . A.colWidth) specs       let toColSpec spec = (maybe B.AlignDefault toAlign (A.colHorizAlign spec),@@ -281,7 +274,8 @@                                    fromIntegral x / fromIntegral totalWidth))                                (A.colWidth spec))       let colspecs = map toColSpec specs-      pure $ B.table (B.Caption Nothing mempty) -- added by addBlockTitle+      pure $ compactifyTable+           $ B.table (B.Caption Nothing mempty) -- added by addBlockTitle                 colspecs thead [tbody] tfoot     A.BlockImage target mbalt mbw mbh -> do       img' <- doInline (A.Inline mempty (A.InlineImage target mbalt mbw mbh))@@ -315,7 +309,7 @@         Left _ -> pure $ B.rawBlock "html" t         Right (Pandoc _ bs) -> pure $ B.fromList bs -doItem :: PandocMonad m => A.ListItem -> m B.Blocks+doItem :: (PandocMonad m, MonadState ADContext m) => A.ListItem -> m B.Blocks doItem (A.ListItem Nothing bs) = doBlocks bs doItem (A.ListItem (Just checkstate) bs) = do   bs' <- doBlocks bs@@ -328,18 +322,24 @@            (B.Plain ils : rest) -> B.Plain (check : B.Space : ils) : rest            rest -> B.Para [check] : rest -doDefListItem :: PandocMonad m+doDefListItem :: (PandocMonad m, MonadState ADContext m)               => ([A.Inline], [A.Block]) -> m (B.Inlines , [B.Blocks]) doDefListItem (lab, bs) = do   lab' <- doInlines lab   bs' <- doBlocks bs   pure (lab', [bs']) -doInlines :: PandocMonad m => [A.Inline] -> m B.Inlines+doInlines :: (PandocMonad m, MonadState ADContext m) => [A.Inline] -> m B.Inlines doInlines = fmap mconcat . mapM doInline -doInline :: PandocMonad m => A.Inline -> m B.Inlines-doInline (A.Inline (A.Attr _ps kvs') it) = do+doInline :: (PandocMonad m, MonadState ADContext m) => A.Inline -> m B.Inlines+doInline il@(A.Inline _ A.Icon{}) = do+  ctx <- get+  doInline' (resolveIcon ctx il)+doInline il = doInline' il++doInline' :: (PandocMonad m, MonadState ADContext m) => A.Inline -> m B.Inlines+doInline' (A.Inline (A.Attr _ps kvs') it) = do   let kvs = M.mapKeys (\k -> if k == "role" then "class" else k) kvs'   addPandocAttributes (M.toList kvs) <$>    case it of@@ -354,12 +354,14 @@     A.Strikethrough ils -> B.strikeout <$> doInlines ils     A.DoubleQuoted ils -> B.doubleQuoted <$> doInlines ils     A.SingleQuoted ils -> B.singleQuoted <$> doInlines ils-    -- TODO when texmath's asciimath parser works, convert:-    A.Math (Just A.AsciiMath) t -> pure $ B.math t-    A.Math (Just A.LaTeXMath) t -> pure $ B.math t-    A.Math Nothing _ ->-      throwError $ PandocParseError "Encountered math type Nothing"-    A.Icon t -> pure $ B.spanWith ("",["icon"],[("name",t)])+    A.Math mbMathType t -> do+      mathType <- maybe (gets adMathType) pure mbMathType+      case mathType of+        -- TODO when texmath's asciimath parser works, convert:+        A.AsciiMath -> pure $ B.math t+        A.LaTeXMath -> pure $ B.math t+    A.Icon t -> -- can't happen (rewritten by resolveIcon in doInline)+      pure $ B.spanWith ("",["icon"],[("name",t)])                          (B.str ("[" <> t <> "]"))     A.Button t -> pure $ B.spanWith ("",["button"],[])                          (B.strong $ B.str ("[" <> t <> "]"))@@ -380,7 +382,19 @@                   Just (A.Height n) -> [("height", T.pack $ show n <> "px")]                   Nothing -> []       pure $ B.imageWith ("",[], width ++ height) url "" alt-    A.Footnote _ ils -> B.note . B.para <$> doInlines ils+    A.Footnote (Just (A.FootnoteId fnid)) ils -> do+      -- repeated references to the same id get the contents of the+      -- first footnote with that id+      contents <- doInlines ils+      fnmap <- gets adFootnotes+      contents' <- case M.lookup fnid fnmap of+                     Just stored -> pure stored+                     Nothing -> do+                       modify $ \ctx ->+                         ctx{ adFootnotes = M.insert fnid contents fnmap }+                       pure contents+      pure $ B.note $ B.para contents'+    A.Footnote Nothing ils -> B.note . B.para <$> doInlines ils     A.InlineAnchor t _ -> pure $ B.spanWith (t, [], []) mempty     A.BibliographyAnchor t _ -> pure $ B.spanWith (t, [], []) mempty     A.CrossReference t Nothing ->
src/Text/Pandoc/Readers/CommonMark.hs view
@@ -79,12 +79,12 @@ makeFigures b = b  sourceToToks :: (SourcePos, Text) -> [Tok]-sourceToToks (pos, s) = map adjust $ tokenize (sourceName pos) s+sourceToToks (pos, s) =+  case sourceLine pos of+    1 -> toks+    n -> map (\tok -> tok{ tokPos = incSourceLine (tokPos tok) (n - 1) }) toks  where-   adjust = case sourceLine pos of-              1 -> id-              n -> \tok -> tok{ tokPos =-                                  incSourceLine (tokPos tok) (n - 1) }+   toks = tokenize (sourceName pos) s   metaValueParser :: Monad m@@ -102,7 +102,7 @@       then walk makeFigures       else id) .   (if isEnabled Ext_tex_math_gfm opts-      then walk handleGfmMath+      then walk handleGfmMathBlock . walk handleGfmMathInline       else id) .   (if readerStripComments opts       then walk stripBlockComments . walk stripInlineComments@@ -115,9 +115,9 @@             Left err -> throwError $ fromParsecError s err             Right (Cm bls :: Cm () Blocks) -> return $ B.doc bls -handleGfmMath :: Block -> Block-handleGfmMath (CodeBlock ("",["math"],[]) raw) = Para [Math DisplayMath raw]-handleGfmMath x = walk handleGfmMathInline x+handleGfmMathBlock :: Block -> Block+handleGfmMathBlock (CodeBlock ("",["math"],[]) raw) = Para [Math DisplayMath raw]+handleGfmMathBlock x = x  handleGfmMathInline :: Inline -> Inline handleGfmMathInline (Math InlineMath math'') =
src/Text/Pandoc/Readers/Creole.hs view
@@ -91,7 +91,7 @@   where     content = brackets <|> line     brackets = try $ option "" (T.singleton <$> newline)-               <+> (char ' ' >> (manyChar (char ' ') <+> textStr "}}}") <* eol)+               <+> (char ' ' >> (takeWhileP (== ' ') <+> textStr "}}}") <* eol)     line = option "" (T.singleton <$> newline) <+> manyTillChar anyChar eol     eol = lookAhead $ try $ nowikiEnd <|> newline     nowikiStart = optional newline >> string "{{{" >> skipMany spaceChar >> newline@@ -215,8 +215,8 @@   (orig, src) <- wikiImg   return $ B.image src "" (B.str orig)   where-    linkSrc = manyChar $ noneOf "|}\n\r\t"-    linkDsc = char '|' >> manyChar (noneOf "}\n\r\t")+    linkSrc = takeWhileP (`notElem` ("|}\n\r\t" :: [Char]))+    linkDsc = char '|' >> takeWhileP (`notElem` ("}\n\r\t" :: [Char]))     wikiImg = try $ do       string "{{"       src <- linkSrc@@ -229,11 +229,11 @@   (orig, src) <- uriLink <|> wikiLink   return $ B.link src "" orig   where-    linkSrc = manyChar $ noneOf "|]\n\r\t"+    linkSrc = takeWhileP (`notElem` ("|]\n\r\t" :: [Char]))     linkDsc :: PandocMonad m => Text -> CRLParser m B.Inlines     linkDsc otxt = B.str                    <$> try (option otxt-                         (char '|' >> manyChar (noneOf "]\n\r\t")))+                         (char '|' >> takeWhileP (`notElem` ("]\n\r\t" :: [Char]))))     linkImg = try $ char '|' >> image     wikiLink = try $ do       string "[["
src/Text/Pandoc/Readers/Djot.hs view
@@ -25,7 +25,7 @@ import Text.Parsec.Pos (newPos) import Text.Pandoc.Options import Text.Pandoc.Definition-import Text.Pandoc.Shared (addPandocAttributes, tshow)+import Text.Pandoc.Shared (addPandocAttributes, tshow, compactifyTable) import qualified Text.Pandoc.UTF8 as UTF8 import Djot (ParseOptions(..), SourcePosOption(..), parseDoc, Pos(..)) import qualified Djot.AST as D@@ -72,8 +72,11 @@     D.Section bls -> divWith ("",["section"],[]) <$> convertBlocks bls     D.Heading lev ils -> header lev <$> convertInlines ils     D.BlockQuote bls -> blockQuote <$> convertBlocks bls-    D.CodeBlock lang bs -> pure $-      codeBlockWith ("", [UTF8.toText lang], []) $ UTF8.toText bs+    D.CodeBlock lang bs ->+      let classes = case UTF8.toText lang of+                      "" -> []+                      l -> [l]+      in pure $ codeBlockWith ("", classes, []) $ UTF8.toText bs     D.Div bls -> divWith nullAttr <$> convertBlocks bls     D.OrderedList olattr listSpacing items ->       orderedListWith olattr' .@@ -151,7 +154,8 @@               mapM toRow hs <*> mapM toRow rs       tbodies <- mapM toTableBody bodies       let tfoot = TableFoot mempty []-      pure $ singleton $ Table mempty capt colspecs thead tbodies tfoot+      pure $ singleton $ compactifyTable+           $ Table mempty capt colspecs thead tbodies tfoot     D.RawBlock (D.Format fmt) bs -> pure $       rawBlock (UTF8.toText fmt) (UTF8.toText bs) 
src/Text/Pandoc/Readers/Docx.hs view
@@ -80,7 +80,6 @@ import qualified Data.Map as M import qualified Data.Text as T import Data.Maybe (isJust, fromMaybe, mapMaybe)-import Data.Sequence (ViewL (..), viewl) import qualified Data.Sequence as Seq import qualified Data.Set as Set import Citeproc (ItemId(..), Val(TextVal,FancyVal), Reference(..), CitationItem(..))@@ -267,8 +266,10 @@  parPartToText :: ParPart -> T.Text parPartToText (PlainRun run)             = runToText run-parPartToText (InternalHyperLink _ children) = T.concat $ map parPartToText children-parPartToText (ExternalHyperLink _ children) = T.concat $ map parPartToText children+parPartToText (InternalHyperLink _ _ children) =+  T.concat $ map parPartToText children+parPartToText (ExternalHyperLink _ _ children) =+  T.concat $ map parPartToText children parPartToText _                          = ""  blacklistedCharStyles :: [CharStyleName]@@ -411,14 +412,15 @@     AllChanges -> do       blks <- smushBlocks <$> mapM bodyPartToBlocks bodyParts       ils <- blocksToInlinesWarn cmtId blks-      let attr = ("", ["comment-start"], ("id", cmtId) : addAuthorAndDate author date)+      let attr = ("", ["comment-start"], ("comment-id", cmtId) :+                    addAuthorAndDate author date)       return $ spanWith attr ils     _ -> return mempty parPartToInlines' (CommentEnd cmtId) = do   opts <- asks docxOptions   case readerTrackChanges opts of     AllChanges -> do-      let attr = ("", ["comment-end"], [("id", cmtId)])+      let attr = ("", ["comment-end"], [("comment-id", cmtId)])       return $ spanWith attr mempty     _ -> return mempty parPartToInlines' (BookMark _ anchor) | anchor `elem` dummyAnchors =@@ -461,19 +463,20 @@   return $ spanWith ("", ["chart"], []) $ text "[CHART]" parPartToInlines' Diagram =   return $ spanWith ("", ["diagram"], []) $ text "[DIAGRAM]"-parPartToInlines' (InternalHyperLink anchor children) = do+parPartToInlines' (InternalHyperLink anchor tooltip children) = do   ils <- smushInlines <$> mapM parPartToInlines' children-  return $ link ("#" <> anchor) "" ils-parPartToInlines' (ExternalHyperLink target children) = do+  return $ link ("#" <> anchor) tooltip ils+parPartToInlines' (ExternalHyperLink target tooltip children) = do   ils <- smushInlines <$> mapM parPartToInlines' children-  return $ link target "" ils+  return $ link target tooltip ils parPartToInlines' (PlainOMath exps) =   return $ math $ writeTeX exps parPartToInlines' (OMathPara exps) =   return $ displayMath $ writeTeX exps parPartToInlines' (Field info children) =   case info of-    HyperlinkField url -> parPartToInlines' $ ExternalHyperLink url children+    HyperlinkField url ->+      parPartToInlines' $ ExternalHyperLink url "" children     IndexrefField ie ->       pure $ spanWith ("",["indexref"],                            (("entry", entryTitle ie) :@@ -481,8 +484,10 @@                           ++ maybe [] (\x -> [("yomi",x)]) (entryYomi ie)                           ++ [("bold","") | entryBold ie]                           ++ [("italic","") | entryItalic ie])) mempty-    PagerefField fieldAnchor True -> parPartToInlines' $ InternalHyperLink fieldAnchor children-    CrossrefField fieldAnchor True -> parPartToInlines' $ InternalHyperLink fieldAnchor children+    PagerefField fieldAnchor True ->+      parPartToInlines' $ InternalHyperLink fieldAnchor "" children+    CrossrefField fieldAnchor True ->+      parPartToInlines' $ InternalHyperLink fieldAnchor "" children     EndNoteCite t -> do       formattedCite <- smushInlines <$> mapM parPartToInlines' children       opts <- asks docxOptions@@ -605,18 +610,10 @@     return $ Header n (newIdent, classes, kvs) ils makeHeaderAnchor' blk = return blk --- Rewrite a standalone paragraph block as a plain-singleParaToPlain :: Blocks -> Blocks-singleParaToPlain blks-  | (Para ils :< seeq) <- viewl $ unMany blks-  , Seq.null seeq =-      singleton $ Plain ils-singleParaToPlain blks = blks- cellToCell :: PandocMonad m => RowSpan -> Docx.Cell -> DocxContext m Pandoc.Cell cellToCell rowSpan (Docx.Cell align gridSpan _ bps) = do   blks <- smushBlocks <$> mapM bodyPartToBlocks bps-  let blks' = singleParaToPlain $ fromList $ blocksToDefinitions $ blocksToBullets $ toList blks+  let blks' = fromList $ blocksToDefinitions $ blocksToBullets $ toList blks   return (cell (convertAlign align)           rowSpan (ColSpan (fromIntegral gridSpan)) blks') @@ -860,7 +857,8 @@   let attr = case mbsty of                 Just sty | extStylesEnabled -> ("", [], [("custom-style", sty)])                 _ -> nullAttr-  return $ tableWith attr cap'+  return $ compactifyTable+         $ tableWith attr cap'                  (zip alignments widths)                  (TableHead nullAttr headerCells)                  [TableBody nullAttr 0 [] bodyCells]
src/Text/Pandoc/Readers/Docx/Parse.hs view
@@ -398,8 +398,8 @@              | CommentStart CommentId Author (Maybe CommentDate) [BodyPart]              | CommentEnd CommentId              | BookMark BookMarkId Anchor-             | InternalHyperLink Anchor [ParPart]-             | ExternalHyperLink URL [ParPart]+             | InternalHyperLink Anchor T.Text [ParPart]          -- tooltip+             | ExternalHyperLink URL T.Text [ParPart]             -- tooltip              | Drawing FilePath T.Text T.Text B.ByteString Extent -- title, alt              | Chart                                              -- placeholder for now              | Diagram                                            -- placeholder for now@@ -1217,18 +1217,20 @@     location <- asks envLocation     children <- mconcat <$> mapD (elemToParPart ns) (elChildren element)     rels <- asks envRelationships+    let tooltip = fromMaybe "" $ findAttrByName ns "w" "tooltip" element     case lookupRelationship location relId rels of       Just target ->          case findAttrByName ns "w" "anchor" element of              Just anchor -> return-               [ExternalHyperLink (target <> "#" <> anchor) children]-             Nothing -> return [ExternalHyperLink target children]-      Nothing     -> return [ExternalHyperLink "" children]+               [ExternalHyperLink (target <> "#" <> anchor) tooltip children]+             Nothing -> return [ExternalHyperLink target tooltip children]+      Nothing     -> return [ExternalHyperLink "" tooltip children] elemToParPart' ns element   | isElem ns "w" "hyperlink" element   , Just anchor <- findAttrByName ns "w" "anchor" element = do     children <- mconcat <$> mapD (elemToParPart ns) (elChildren element)-    return [InternalHyperLink anchor children]+    let tooltip = fromMaybe "" $ findAttrByName ns "w" "tooltip" element+    return [InternalHyperLink anchor tooltip children] elemToParPart' ns element   | isElem ns "w" "commentRangeStart" element   , Just cmtId <- findAttrByName ns "w" "id" element = do
src/Text/Pandoc/Readers/DokuWiki.hs view
@@ -28,7 +28,7 @@ import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (enclosed)-import Text.Pandoc.Shared (trim, stringify, tshow)+import Text.Pandoc.Shared (trim, stringifyInlines, tshow, compactifyTable) import Data.List (isPrefixOf, isSuffixOf, groupBy) import qualified Safe @@ -174,10 +174,11 @@              => DWParser m a -> DWParser m Text nestedText end = innerSpace <|> countChar 1 nonspaceChar   where-    innerSpace = try $ many1Char spaceChar <* notFollowedBy end+    innerSpace = try $ takeWhile1P (\c -> c == ' ' || c == '\t')+                        <* notFollowedBy end  monospaced :: PandocMonad m => DWParser m B.Inlines-monospaced = try $ B.code . (T.concat . map stringify . B.toList) <$> enclosed (string "''") nestedInlines+monospaced = try $ B.code . stringifyInlines <$> enclosed (string "''") nestedInlines  subscript :: PandocMonad m => DWParser m B.Inlines subscript = try $ B.subscript <$> between (string "<sub>") (try $ string "</sub>") nestedInlines@@ -238,7 +239,7 @@ nocache = try $ mempty <$ string "~~NOCACHE~~"  str :: PandocMonad m => DWParser m B.Inlines-str = B.str <$> (many1Char alphaNum <|> characterReference)+str = B.str <$> (takeWhile1P isAlphaNum <|> characterReference)  symbol :: PandocMonad m => DWParser m B.Inlines symbol = B.str <$> (notFollowedBy' blockCode *> countChar 1 nonspaceChar)@@ -517,7 +518,8 @@   let attrs =  map (\(a, _) -> (a, ColWidthDefault)) firstRow   let toRow = Row nullAttr . map B.simpleCell       toHeaderRow l = [toRow l | not (null l)]-  pure $ B.table B.emptyCaption+  pure $ compactifyTable+       $ B.table B.emptyCaption                  attrs                  (TableHead nullAttr $ toHeaderRow (map snd headerRow))                  [TableBody nullAttr 0 [] $ map (toRow . (map snd)) body]
src/Text/Pandoc/Readers/HTML.hs view
@@ -58,7 +58,9 @@ import Text.Pandoc.Logging import Text.Pandoc.Options (     Extension (Ext_epub_html_exts, Ext_empty_paragraphs, Ext_native_divs,-               Ext_native_spans, Ext_raw_html, Ext_line_blocks, Ext_raw_tex),+               Ext_native_spans, Ext_raw_html, Ext_line_blocks, Ext_raw_tex,+               Ext_smart, Ext_tex_math_dollars,+               Ext_tex_math_single_backslash, Ext_tex_math_double_backslash),     ReaderOptions (readerExtensions, readerStripComments),     extensionEnabled) import Text.Pandoc.Parsing hiding ((<|>))@@ -76,7 +78,17 @@          => ReaderOptions -- ^ Reader options          -> a             -- ^ Input to parse          -> m Pandoc-readHtml opts inp = do+readHtml = readHtmlWithDepth 0++-- Like 'readHtml', but starting at the given iframe nesting depth.+-- Used to limit recursion when the contents of iframes are fetched+-- and parsed (see pIframe).+readHtmlWithDepth :: (PandocMonad m, ToSources a)+                  => Int+                  -> ReaderOptions+                  -> a+                  -> m Pandoc+readHtmlWithDepth depth opts inp = do   let tags = stripPrefixes $ canonicalizeTags $              parseTagsOptions parseOptions{ optTagPosition = True }              (sourcesToText $ toSources inp)@@ -92,7 +104,7 @@   result <- flip runReaderT def $        runParserT parseDoc        (HTMLState def{ stateOptions = opts }-         [] Nothing Set.empty [] M.empty opts False False)+         M.empty M.empty Nothing Set.empty [] M.empty opts False False depth)        "source" tags   case result of     Right doc -> return doc@@ -127,12 +139,14 @@   walkM (replaceNotes' notes) bs  replaceNotes' :: PandocMonad m-              => [(Text, Blocks)] -> Inline -> TagParser m Inline+              => M.Map Text Blocks -> Inline -> TagParser m Inline replaceNotes' noteTbl (RawInline (Format "noteref") ref) =-  maybe warnNotFound (pure . Note . B.toList) $ lookup ref noteTbl+  maybe warnNotFound (pure . Note . B.toList) $ M.lookup ref noteTbl  where   warnNotFound = do-    pos <- getPosition+    -- use the position of the noteref, if we recorded one; the+    -- current position is at the end of the document by now:+    pos <- M.lookup ref . noteRefPos <$> getState >>= maybe getPosition pure     logMessage $ ReferenceNotFound ref pos     pure (Note []) replaceNotes' _ x = pure x@@ -298,7 +312,7 @@   let ident = fromMaybe "" (lookup "id" attr)   content <- pInTags tag block   updateState $ \s ->-    s {noteTable = (ident, content) : noteTable s}+    s {noteTable = M.insert ident content (noteTable s)}  eFootnotes :: PandocMonad m => TagParser m Blocks eFootnotes = try $ do@@ -331,6 +345,9 @@   ident <- case lookup "href" attr >>= T.uncons of              Just ('#', rest) -> return rest              _ -> mzero+  pos <- getPosition+  updateState $ \s ->+    s{ noteRefPos = M.insertWith (\_new old -> old) ident pos (noteRefPos s) }   _ <- manyTill pAny (pSatisfy (\case                                    TagClose t -> t == tag                                    _          -> False))@@ -377,8 +394,9 @@  pCheckbox :: PandocMonad m => TagParser m Inlines pCheckbox = do-  TagOpen _ attr' <- pSatisfy $ matchTagOpen "input" [("type","checkbox")]-  TagClose _ <- pSatisfy (matchTagClose "input")+  -- <input> is a void element, so the closing tag is optional+  TagOpen _ attr' <- pSelfClosing (=="input")+                       (\as -> lookup "type" as == Just "checkbox")   let attr = toStringAttr attr'   let isChecked = isJust $ lookup "checked" attr   let escapeSequence = B.str $ if isChecked then "\9746" else "\9744"@@ -409,10 +427,14 @@   let start = fromMaybe 1 $ lookup "start" attribs >>= safeRead   let style = fromMaybe DefaultStyle          $  (parseTypeAttr      <$> lookup "type" attribs)-        <|> (parseListStyleType <$> lookup "class" attribs)+        <|> (lookup "class" attribs >>= pickClassStyle)         <|> (parseListStyleType <$> (lookup "style" attribs >>= pickListStyle))         where           pickListStyle = pickStyleAttrProps ["list-style-type", "list-style"]+          -- the list style may be one of several words in the class+          -- attribute:+          pickClassStyle = L.find (/= DefaultStyle)+                             . map parseListStyleType . T.words    -- note: if they have an <ol> or <ul> not in scope of a <li>,   -- treat it as a list item, though it's not valid xhtml...@@ -520,7 +542,10 @@   skipMany pBlank   pCloses "iframe" <|> eof   url <- canonicalizeUrl $ fromAttrib "src" tag-  if T.null url+  depth <- iframeDepth <$> getState+  -- limit nesting depth, since iframes that (indirectly) embed+  -- themselves would otherwise cause infinite recursion:+  if T.null url || depth >= maxIframeDepth      then ignore $ renderTags' [tag, TagClose "iframe"]      else catchError        (do (bs, mbMime) <- openURL url@@ -529,7 +554,7 @@                | "text/html" `T.isPrefixOf` mt -> do                     let inp = UTF8.toText bs                     opts <- readerOpts <$> getState-                    Pandoc _ contents <- readHtml opts inp+                    Pandoc _ contents <- readHtmlWithDepth (depth + 1) opts inp                     return $ B.divWith ("",["iframe"],[]) $ B.fromList contents                | "image/" `T.isPrefixOf` mt -> do                     return $ B.divWith ("",["iframe"],[]) $@@ -539,6 +564,9 @@          logMessage $ CouldNotFetchResource url (renderError e)          ignore $ renderTags' [tag, TagClose "iframe"]) +maxIframeDepth :: Int+maxIframeDepth = 5+ pRawHtmlBlock :: PandocMonad m => TagParser m Blocks pRawHtmlBlock = do   raw <- pHtmlBlock "script" <|> pHtmlBlock "style" <|> pHtmlBlock "textarea"@@ -664,7 +692,9 @@   let modifyClasses f ("class",v) =         ("class", T.unwords . map f . T.words $ v)       modifyClasses _ (k,v) = (k,v)-  let attr = toAttr $ map (modifyClasses stripLanguagePrefix) $ codeAttr <> attr'+  -- pre's attributes take precedence (toAttr keeps the first of+  -- duplicate attributes):+  let attr = toAttr $ map (modifyClasses stripLanguagePrefix) $ attr' <> codeAttr   contents <- manyTill pAny (pCloses "pre" <|> eof)   let rawText = T.concat $ map tagToText contents   -- drop trailing newline if any@@ -720,11 +750,14 @@         "input"           | lookup "type" attr == Just "checkbox"           -> asks inListItem >>= guard >> pCheckbox-        "style" -> B.rawInline "html" <$> pHtmlBlock "style"+        "style"+          | extensionEnabled Ext_raw_html exts+          -> B.rawInline "html" <$> pHtmlBlock "style"+          | otherwise -> pHtmlBlock "style" >>= ignore         "script"           | Just x <- lookup "type" attr           , "math/tex" `T.isPrefixOf` x -> pScriptMath-        _ | name `elem` htmlSpanLikeElements -> pSpanLike+        _ | name `Set.member` htmlSpanLikeElements -> pSpanLike name         _ -> pRawHtmlInline     TagText _ -> pTagText     _ -> pRawHtmlInline@@ -770,18 +803,12 @@ pSubscript :: PandocMonad m => TagParser m Inlines pSubscript = pInlinesInTags "sub" B.subscript -pSpanLike :: PandocMonad m => TagParser m Inlines-pSpanLike =-  Set.foldr-    (\tagName acc -> acc <|> parseTag tagName)-    mzero-    htmlSpanLikeElements-  where-    parseTag tagName = do-      TagOpen _ attrs <- pSatisfy $ tagOpenLit tagName (const True)-      let (ids, cs, kvs) = toAttr attrs-      content <- mconcat <$> manyTill inline (pCloses tagName <|> eof)-      return $ B.spanWith (ids, tagName : cs, kvs) content+pSpanLike :: PandocMonad m => Text -> TagParser m Inlines+pSpanLike tagName = do+  TagOpen _ attrs <- pSatisfy $ tagOpenLit tagName (const True)+  let (ids, cs, kvs) = toAttr attrs+  content <- mconcat <$> manyTill inline (pCloses tagName <|> eof)+  return $ B.spanWith (ids, tagName : cs, kvs) content  pSmall :: PandocMonad m => TagParser m Inlines pSmall = pInlinesInTags "small" (B.spanWith ("",["small"],[]))@@ -973,23 +1000,43 @@   pos <- getPosition   (TagText str) <- pSatisfy isTagText   st <- getState-  qu <- ask-  parsed <- lift $ lift $-            flip runReaderT qu $ runParserT (many pTagContents) st "text"-               (Sources [(pos, str)])-  case parsed of-       Left _        -> throwError $ PandocParseError $-                        "Could not parse `" <> str <> "'"-       Right result  -> return $ mconcat result+  exts <- getOption readerExtensions+  let smart = extensionEnabled Ext_smart exts+  let mathDollars = extensionEnabled Ext_tex_math_dollars exts+  let backslashSpecial = extensionEnabled Ext_raw_tex exts ||+                         extensionEnabled Ext_tex_math_single_backslash exts ||+                         extensionEnabled Ext_tex_math_double_backslash exts+  -- Chars that could make pTagContents yield something different from+  -- a plain B.text decomposition into Str, Space, and SoftBreak:+  let needsParsing c =+        isBad c ||                    -- gets remapped (see pBad)+        (c == '$' && mathDollars) ||  -- may start tex math+        (c == '\\' && backslashSpecial) ||  -- raw tex or tex math+        (smart && (c == '\'' || c == '"' || c == '.' || c == '-'))+  if not (inPre st) && not (T.any needsParsing str)+     -- fast path: the result is a plain decomposition of the text into+     -- Str, Space, and SoftBreak, which is exactly what B.text gives us+     -- (adjacent Strs produced by pTagContents are merged by the+     -- Inlines Semigroup instance, so the results coincide):+     then return $ B.text str+     else do+       qu <- ask+       parsed <- lift $ lift $+                 flip runReaderT qu $ runParserT (many pTagContents) st "text"+                    (Sources [(pos, str)])+       case parsed of+            Left _        -> throwError $ PandocParseError $+                             "Could not parse `" <> str <> "'"+            Right result  -> return $ mconcat result  type InlinesParser m = HTMLParser m Sources  pTagContents :: PandocMonad m => InlinesParser m Inlines pTagContents =-      B.displayMath <$> mathDisplay+      pStr    -- can't consume the special chars that start the other+  <|> pSpace  -- parsers, so it is safe to try these two first+  <|> B.displayMath <$> mathDisplay   <|> B.math        <$> mathInline-  <|> pStr-  <|> pSpace   <|> smartPunctuation pTagContents   <|> pRawTeX   <|> pSymbol@@ -1184,12 +1231,19 @@   let ts = canonicalizeTags $ parseTagsOptions                                parseOptions{ optTagWarning = False                                            , optTagPosition = True }-                               (inp <> " ")-                               -- add space to ensure that-                               -- we get a TagPosition after the tag+                               inp+  -- if the tag is the last token, there is no TagPosition after it;+  -- in that case its end is the end of the input (positions are 1-based).+  -- (Note: only computed when needed, to avoid a scan of the input.)+  let endOfInput = (T.count "\n" inp + 1,+                    T.length (T.takeWhileEnd (/= '\n') inp) + 1)   (next, ln, col) <- case ts of-                      (TagPosition{} : next : TagPosition ln col : _)-                        | f next -> return (next, ln, col)+                      (TagPosition{} : next : rest)+                        | f next ->+                           case dropWhile (not . isTagPosition) rest of+                             TagPosition ln col : _ -> return (next, ln, col)+                             _ -> let (ln, col) = endOfInput+                                  in return (next, ln, col)                       _ -> mzero    -- <www.boe.es/buscar/act.php?id=BOE-A-1996-8930#a66>
src/Text/Pandoc/Readers/HTML/Parsing.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE BangPatterns          #-} {-# LANGUAGE LambdaCase            #-} {-# LANGUAGE OverloadedStrings     #-} {- |@@ -34,12 +35,15 @@ import Data.Maybe (fromMaybe) import Data.Text (Text) import Text.HTML.TagSoup-  ( Attribute, Tag (..), isTagPosition, isTagOpen, isTagClose, (~==) )+  ( Attribute, Tag (..), isTagOpen, isTagClose, (~==) ) import Text.Pandoc.Class.PandocMonad (PandocMonad (..)) import Text.Pandoc.Definition (Attr) import Text.Pandoc.Parsing-  ( (<|>), eof, getPosition, lookAhead, manyTill, newPos, option, optional-  , skipMany, setPosition, token, try)+  ( (<|>), eof, lookAhead, manyTill, newPos, option, optional+  , skipMany, try)+import Text.Parsec.Prim (mkPT, Consumed (..), Reply (..), State (..))+import Text.Parsec.Error (Message (SysUnExpect), newErrorMessage,+                          newErrorUnknown) import Text.Pandoc.Readers.HTML.TagCategories import Text.Pandoc.Readers.HTML.Types import Text.Pandoc.Shared (tshow)@@ -126,18 +130,24 @@   isBlank (TagComment _) = True   isBlank _ = False -pLocation :: PandocMonad m => TagParser m ()-pLocation = do-  (TagPosition r c) <- pSat isTagPosition-  setPosition $ newPos "input" r c--pSat :: PandocMonad m => (Tag Text -> Bool) -> TagParser m (Tag Text)-pSat f = do-  pos <- getPosition-  token tshow (const pos) (\x -> if f x then Just x else Nothing)-+-- | Skip any 'TagPosition' tokens (using them to update the source+-- position), then consume the next tag if it satisfies the predicate.+-- Fails without consuming input otherwise.  Implemented as a single+-- parsec primitive, since this is the hottest spot of the HTML reader. pSatisfy :: PandocMonad m => (Tag Text -> Bool) -> TagParser m (Tag Text)-pSatisfy f = try $ optional pLocation >> pSat f+pSatisfy f = mkPT $ \(State inp pos u) ->+  let go !pos' toks =+        case toks of+          TagPosition r c : rest -> go (newPos "input" r c) rest+          t : rest+            | f t -> Consumed (return+                       (Ok t (State rest pos' u) (newErrorUnknown pos')))+          _ -> Empty (return (Error+                 (newErrorMessage (SysUnExpect (descr toks)) pos')))+  in return (go pos inp)+ where+  descr []    = ""+  descr (t:_) = T.unpack (tshow t)  matchTagClose :: Text -> (Tag Text -> Bool) matchTagClose t = (~== TagClose t)@@ -199,20 +209,23 @@ _ `closes` _ = False  toStringAttr :: [(Text, Text)] -> [(Text, Text)]-toStringAttr = foldr go []+toStringAttr = go mempty   where-   go :: (Text, Text) -> [(Text, Text)] -> [(Text, Text)]+   go :: Set.Set Text -> [(Text, Text)] -> [(Text, Text)]+   go _ [] = []+   go seen ((x,y):rest)+     -- prevent duplicate attributes; the first one wins:+     | x' `Set.member` seen = go seen rest+     | otherwise = (x', y) : go (Set.insert x' seen) rest+    where x' = normalizeName x    -- treat xml:lang as lang-   go ("xml:lang",y) ats = go ("lang",y) ats-   -- prevent duplicate attributes-   go (x,y) ats-     | any (\(x',_) -> x == x') ats = ats-     | otherwise      =-        case T.stripPrefix "data-" x of-          Just x' | x' `Set.notMember` (html5Attributes <>-                                        html4Attributes <> rdfaAttributes)-            -> go (x',y) ats-          _ -> (x,y):ats+   normalizeName "xml:lang" = "lang"+   -- strip data- prefix unless the bare name is a standard attribute+   normalizeName x =+     case T.stripPrefix "data-" x of+       Just x' | x' `Set.notMember` standardAttrs -> x'+       _ -> x+   standardAttrs = html5Attributes <> html4Attributes <> rdfaAttributes  -- Unlike fromAttrib from tagsoup, this distinguishes -- between a missing attribute and an attribute with empty content.
src/Text/Pandoc/Readers/HTML/Table.hs view
@@ -28,7 +28,7 @@ import Text.Pandoc.Definition import Text.Pandoc.Class.PandocMonad (PandocMonad (..)) import Text.Pandoc.Parsing-  ( eof, lookAhead, many, many1, manyTill, option, optional+  ( eof, lookAhead, many, many1, manyTill, notFollowedBy, option, optional   , optionMaybe, skipMany, try ) import Text.Pandoc.Readers.HTML.Parsing import Text.Pandoc.Readers.HTML.Types (TagParser)@@ -134,7 +134,8 @@   skipMany pBlank   TagOpen _ attribs <- pSatisfy (matchTagOpen "tr" []) <* skipMany pBlank   cells <- many (pCell block BodyCell <|> pCell block HeaderCell)-  TagClose _ <- pSatisfy (matchTagClose "tr")+  -- the closing tag may be omitted (it is optional in HTML):+  optional $ pSatisfy (matchTagClose "tr")   let numheadcells = length $ takeWhile (\(ct,_) -> ct == HeaderCell) cells   return (numheadcells, Row (toAttr attribs) $ map snd cells) @@ -145,9 +146,15 @@            -> TagParser m B.Row pHeaderRow block = try $ do   skipMany pBlank-  let pThs = many (snd <$> pCell block HeaderCell)-  let mkRow (attribs, cells) = Row (toAttr attribs) cells-  mkRow <$> pInTagWithAttribs TagsRequired "tr" pThs+  TagOpen _ attribs <- pSatisfy (matchTagOpen "tr" [])+  cells <- many (snd <$> pCell block HeaderCell)+  skipMany pBlank+  -- a header row may contain only <th> cells; a following <td>+  -- means this is a body row, so we backtrack and let pRow parse it:+  notFollowedBy $ pSatisfy (matchTagOpen "td" [])+  -- the closing tag may be omitted (it is optional in HTML):+  optional $ pSatisfy (matchTagClose "tr")+  return $ Row (toAttr attribs) cells  -- | Parses a table head. If there is no @thead@ element, this looks for -- a row of @<th>@-only elements as the first line of the table.@@ -278,7 +285,9 @@   [] -> case tblType of           SimpleTable -> replicate ncols ColWidthDefault           NormalTable -> replicate ncols (ColWidth $ 1 / fromIntegral ncols)-  widths -> widths+  -- pad if fewer <col> elements than columns, so that cells in the+  -- extra columns aren't lost (the colspecs are zipped with alignments):+  widths -> widths ++ replicate (ncols - length widths) ColWidthDefault  calculateAlignments :: Int -> [TableBody] -> [Alignment] calculateAlignments cols tbodies =
src/Text/Pandoc/Readers/HTML/Types.hs view
@@ -33,7 +33,7 @@ import Text.Pandoc.Parsing   ( HasIdentifierList (..), HasLastStrPosition (..), HasLogMessages (..)   , HasMacros (..), HasQuoteContext (..), HasReaderOptions (..)-  , ParsecT, ParserState, QuoteContext (NoQuote)+  , ParsecT, ParserState, QuoteContext (NoQuote), SourcePos   ) import Text.Pandoc.TeX (Macro) @@ -46,7 +46,8 @@ -- | Global HTML parser state data HTMLState = HTMLState   { parserState :: ParserState-  , noteTable   :: [(Text, Blocks)]+  , noteTable   :: Map Text Blocks+  , noteRefPos  :: Map Text SourcePos -- ^ position of first ref to each note   , baseHref    :: Maybe URI   , identifiers :: Set Text   , logMessages :: [LogMessage]@@ -54,6 +55,7 @@   , readerOpts  :: ReaderOptions   , inFootnotes :: Bool   , inPre       :: Bool+  , iframeDepth :: Int -- ^ how many iframes deep we are (recursion limit)   }  -- | Local HTML parser state
src/Text/Pandoc/Readers/Jira.hs view
@@ -20,7 +20,7 @@ import Text.Pandoc.Builder hiding (cell) import Text.Pandoc.Error (PandocError (PandocParseError)) import Text.Pandoc.Options (ReaderOptions)-import Text.Pandoc.Shared (stringify)+import Text.Pandoc.Shared (stringifyInlines) import Text.Pandoc.Sources (ToSources(..), sourcesToText) import qualified Text.Jira.Markup as Jira @@ -140,7 +140,7 @@                             in imageWith attr (Jira.fromURL url) title mempty   Jira.Link lt alias url -> jiraLinkToPandoc lt alias url   Jira.Linebreak         -> linebreak-  Jira.Monospaced inlns  -> code . stringify . toList . fromInlines $ inlns+  Jira.Monospaced inlns  -> code . stringifyInlines . toList . fromInlines $ inlns   Jira.Space             -> space   Jira.SpecialChar c     -> str (Data.Text.singleton c)   Jira.Str t             -> str t
src/Text/Pandoc/Readers/LaTeX.hs view
@@ -24,6 +24,7 @@ import Control.Applicative (many, optional, (<|>)) import Control.Monad import Control.Monad.Except (throwError)+import Control.Monad.Reader (runReaderT) import Data.Containers.ListUtils (nubOrd) import Data.Char (isDigit, isLetter, isAlphaNum, toUpper, chr) import Data.Default@@ -87,12 +88,22 @@           -> m Pandoc readLaTeX opts ltx = do   let sources = toSources ltx-  parsed <- runParserT parseLaTeX def{ sOptions = opts } "source"+  parsed <- flip runReaderT latexEnv $+               runParserT parseLaTeX def{ sOptions = opts } "source"                (TokStream False (tokenizeSources sources))   case parsed of     Right result -> return result     Left e       -> throwError $ fromParsecError sources e +-- | The command dispatch tables, built once per parse and shared+-- through the reader environment (see 'LaTeXEnv').+latexEnv :: PandocMonad m => LaTeXEnv m+latexEnv = LaTeXEnv+  { envInlineCommands = inlineCommands+  , envBlockCommands  = blockCommands+  , envEnvironments   = environments+  }+ parseLaTeX :: PandocMonad m => LP m Pandoc parseLaTeX = do   bs <- blocks@@ -156,7 +167,7 @@   lookAhead (try (char '\\' >> letter))   toks <- getInputTokens   snd <$> (-          rawLaTeXParser toks+          rawLaTeXParser latexEnv toks              (makeAtLetterSection <|>               macroDef (const mempty) <|>               do choice (map controlSeq@@ -164,7 +175,7 @@                  skipMany opt                  braced                  return mempty) blocks-      <|> rawLaTeXParser toks+      <|> rawLaTeXParser latexEnv toks            (void (environment <|> blockCommand))            (mconcat <$> many (block <|> beginOrEndCommand))) @@ -199,10 +210,10 @@   lookAhead (try (char '\\' >> letter))   toks <- getInputTokens   raw <- snd <$>-          (   rawLaTeXParser toks+          (   rawLaTeXParser latexEnv toks               (mempty <$ (controlSeq "input" >> skipMany rawopt >> braced))               inlines-          <|> rawLaTeXParser toks (void inline) inlines+          <|> rawLaTeXParser latexEnv toks (void inline) inlines           )   finalbraces <- mconcat <$> many (try (string "{}")) -- see #5439   return $ raw <> T.pack finalbraces@@ -211,7 +222,8 @@ inlineCommand = do   lookAhead (try (char '\\' >> letter))   toks <- getInputTokens-  fst <$> rawLaTeXParser toks (void (inlineEnvironment <|> inlineCommand'))+  fst <$> rawLaTeXParser latexEnv toks+          (void (inlineEnvironment <|> inlineCommand'))           inlines  -- inline elements:@@ -337,21 +349,23 @@        rawcommand <- getRawCommand name (cmd <> star)        (guardEnabled Ext_raw_tex >> return (rawInline "latex" rawcommand))          <|> ignore rawcommand-  lookupListDefault raw names inlineCommands+  commandMap <- envInlineCommands <$> askEnv+  lookupListDefault raw names commandMap  tok :: PandocMonad m => LP m Inlines tok = tokWith inline  unescapeURL :: Text -> Text-unescapeURL = T.concat . go . T.splitOn "\\"-  where-    isEscapable c = T.any (== c) "#$%&~_^\\{}"-    go (x:xs) = x : map unescapeInterior xs-    go []     = []-    unescapeInterior t-      | Just (c, _) <- T.uncons t-      , isEscapable c = t-      | otherwise = "\\" <> t+unescapeURL t =+  let (xs, ys) = T.break (== '\\') t+  in case T.uncons ys of+       Nothing -> xs+       Just (_, rest) ->+         case T.uncons rest of+           Just (c, rest')+             | isEscapable c -> xs <> T.cons c (unescapeURL rest')+           _ -> xs <> "\\" <> unescapeURL rest+  where isEscapable c = T.any (== c) "#$%&~_^\\{}"  inlineCommands :: PandocMonad m => M.Map Text (LP m Inlines) inlineCommands = M.unions@@ -407,22 +421,30 @@     , ("sl", extractSpaces emph <$> inlines)     , ("bf", extractSpaces strong <$> inlines)     , ("tt", formatCode nullAttr <$> inlines)+    , ("ttfamily", extractSpaces (formatCode nullAttr) <$> inlines)     , ("rm", inlines)     , ("itshape", extractSpaces emph <$> inlines)     , ("slshape", extractSpaces emph <$> inlines)     , ("scshape", extractSpaces smallcaps <$> inlines)     , ("bfseries", extractSpaces strong <$> inlines)-    , ("MakeUppercase", makeUppercase <$> tok)-    , ("MakeTextUppercase", makeUppercase <$> tok) -- textcase-    , ("uppercase", makeUppercase <$> tok)-    , ("MakeLowercase", makeLowercase <$> tok)-    , ("MakeTextLowercase", makeLowercase <$> tok)-    , ("lowercase", makeLowercase <$> tok)+    , ("MakeUppercase", caseTransform "upper" T.toUpper)+    , ("MakeTextUppercase", caseTransform "upper" T.toUpper) -- textcase+    , ("uppercase", caseTransform "upper" T.toUpper)+    , ("MakeLowercase", caseTransform "lower" T.toLower)+    , ("MakeTextLowercase", caseTransform "lower" T.toLower)+    , ("lowercase", caseTransform "lower" T.toLower)+    , ("MakeTitlecase", makeTitlecaseCommand)+    , ("NoCaseChange", spanWith ("",["nocasechange"],[]) <$> tok)+    , ("CaseSwitch", tok <* tok <* tok <* tok)     , ("thanks", skipopts >> note <$> grouped block)     , ("footnote", skipopts >> footnote)     , ("footnotemark", footnotemark)     , ("footnotetext", footnotetext)     , ("newline", pure B.linebreak)+    -- xparse argument markers, in case they leak into the document:+    , ("NoValue", pure (B.str "-NoValue-"))+    , ("BooleanTrue", pure mempty)+    , ("BooleanFalse", pure mempty)     , ("passthrough", fixPassthroughEscapes <$> tok)     -- \passthrough macro used by latex writer                            -- for listings@@ -457,6 +479,7 @@     , ("iftoggle", try $ ifToggle >> inline)     -- include     , ("input", rawInlineOr "input" $ include "input")+    , ("expandableinput", rawInlineOr "expandableinput" $ include "input")     -- soul package     , ("st", extractSpaces strikeout <$> tok)     , ("ul", underline <$> tok)@@ -471,6 +494,19 @@     -- this is used internally by pandoc but the definition is too complicated     -- for pandoc to handle (see #11140):     , ("pandocbounded", tok)+    -- LaTeX3 constants+    , ("c_ampersand_str", pure (str "&"))+    , ("c_atsign_str", pure (str "@"))+    , ("c_backslash_str", pure (str "\\"))+    , ("c_left_brace_str", pure (str "{"))+    , ("c_right_brace_str", pure (str "}"))+    , ("c_circumflex_str", pure (str "^"))+    , ("c_colon_str", pure (str ":"))+    , ("c_dollar_str", pure (str "$"))+    , ("c_hash_str", pure (str "#"))+    , ("c_percent_str", pure (str "%"))+    , ("c_tilde_str", pure (str "~"))+    , ("c_underscore_str", pure (str "_"))     ]  bracedFilename :: PandocMonad m => LP m Text@@ -547,16 +583,98 @@   contents <- manyTill anyTok (controlSeq "fi")   return $ rawInline "latex" $ "\\ifdim" <> untokenize contents <> "\\fi" -makeUppercase :: Inlines -> Inlines-makeUppercase = fromList . walk (alterStr T.toUpper) . toList+-- | Parse the argument of a case-changing command (\MakeUppercase,+-- \MakeLowercase) and apply the case transformation, honoring+-- exclusions declared with \Declare*caseExclusions.+caseTransform :: PandocMonad m => Text -> (Text -> Text) -> LP m Inlines+caseTransform kind f = do+  void $ option [] keyvals -- locale options, ignored+  excl <- M.findWithDefault Set.empty kind . sCaseExclusions <$> getState+  caseTransformWith excl f <$> tok -makeLowercase :: Inlines -> Inlines-makeLowercase = fromList . walk (alterStr T.toLower) . toList+-- | Apply a case transformation to text, leaving math, code and+-- citations untouched, unwrapping (and skipping) \NoCaseChange+-- content, and skipping excluded words.+caseTransformWith :: Set.Set Text -> (Text -> Text) -> Inlines -> Inlines+caseTransformWith excl f = fromList . go . toList+  where+    go = concatMap goInline+    goInline (Span ("",["nocasechange"],[]) ils) = ils+    goInline (Str t)+      | t `Set.member` excl = [Str t]+      | otherwise = [Str (f t)]+    goInline (Emph ils) = [Emph (go ils)]+    goInline (Strong ils) = [Strong (go ils)]+    goInline (Underline ils) = [Underline (go ils)]+    goInline (Strikeout ils) = [Strikeout (go ils)]+    goInline (Superscript ils) = [Superscript (go ils)]+    goInline (Subscript ils) = [Subscript (go ils)]+    goInline (SmallCaps ils) = [SmallCaps (go ils)]+    goInline (Quoted qt ils) = [Quoted qt (go ils)]+    goInline (Span attr ils) = [Span attr (go ils)]+    goInline (Link attr ils target) = [Link attr (go ils) target]+    goInline x = [x] -- Math, Code, Cite, Space, etc. -alterStr :: (Text -> Text) -> Inline -> Inline-alterStr f (Str xs) = Str (f xs)-alterStr _ x = x+-- | Parse the arguments of \MakeTitlecase, supporting the+-- @words=all@ option and title-case exclusions.+makeTitlecaseCommand :: PandocMonad m => LP m Inlines+makeTitlecaseCommand = do+  options <- option [] keyvals+  excl <- M.findWithDefault Set.empty "title" . sCaseExclusions <$> getState+  (if lookup "words" options == Just "all"+      then makeTitlecaseAll excl+      else makeTitlecase) <$> tok +-- | Titlecase the first letter of each word (\MakeTitlecase with+-- @words=all@), skipping excluded words.+makeTitlecaseAll :: Set.Set Text -> Inlines -> Inlines+makeTitlecaseAll excl = fromList . go . toList+  where+    go [] = []+    go xs =+      let (w, rest) = break isSep xs+          (seps, rest') = span isSep rest+       in tcWord w ++ seps ++ go rest'+    isSep Space = True+    isSep SoftBreak = True+    isSep LineBreak = True+    isSep _ = False+    tcWord w+      | stringify w `Set.member` excl = w+      | otherwise = toList (makeTitlecase (fromList w))++-- | Handle \DeclareUppercaseExclusions and friends: store a+-- comma-separated list of words excluded from case changing.+declareCaseExclusions :: PandocMonad m => Text -> LP m Blocks+declareCaseExclusions kind = do+  ws <- map T.strip . T.splitOn "," . untokenize <$> braced+  updateState $ \st -> st{ sCaseExclusions =+      M.insertWith Set.union kind (Set.fromList ws) (sCaseExclusions st) }+  return mempty++-- | Uppercase the first character of the first string (LaTeX3+-- \MakeTitlecase, which title-cases only the first word by default).+makeTitlecase :: Inlines -> Inlines+makeTitlecase = fromList . snd . go . toList+  where+    go :: [Inline] -> (Bool, [Inline])+    go (x : xs) =+      case goInline x of+        (True, x')  -> (True, x' : xs)+        (False, x') -> (x' :) <$> go xs+    go [] = (False, [])+    goInline (Str t) | not (T.null t) =+      (True, Str (T.toTitle (T.take 1 t) <> T.drop 1 t))+    goInline (Emph ils) = Emph <$> go ils+    goInline (Strong ils) = Strong <$> go ils+    goInline (Underline ils) = Underline <$> go ils+    goInline (SmallCaps ils) = SmallCaps <$> go ils+    goInline (Span attr ils) = Span attr <$> go ils+    goInline (Link attr ils target) =+      (\ils' -> Link attr ils' target) <$> go ils+    goInline (Quoted qt ils) = Quoted qt <$> go ils+    goInline x = (False, x)+ fixPassthroughEscapes :: Inlines -> Inlines fixPassthroughEscapes = walk go  where@@ -704,13 +822,16 @@                 -> eatOneToken *>                     option (str "-") (symbol '-' *>                       option (str "–") (str "—" <$ symbol '-'))-        "'"     -> eatOneToken *>-                    option (str "’") (str  "”" <$ (guard ligatures *> symbol '\''))+        "'" | ligatures+                -> eatOneToken *>+                    option (str "’") (str "”" <$ symbol '\'')+            | otherwise+                -> symbolAsString         "~"     -> str "\160" <$ eatOneToken         "`" | ligatures                 -> doubleQuote <|> singleQuote <|> (str "‘" <$ symbol '`')             | otherwise-                -> str "‘" <$ symbol '`'+                -> symbolAsString         "\"" | ligatures                 -> doubleQuote <|> singleQuote <|> symbolAsString         "“"     -> doubleQuote <|> symbolAsString@@ -740,13 +861,10 @@ opt :: PandocMonad m => LP m Inlines opt = do   toks <- try (sp *> bracketedToks <* sp)-  -- now parse the toks as inlines-  st <- getState-  parsed <- runParserT (mconcat <$> many inline) st "bracketed option"-              (TokStream False toks)-  case parsed of-    Right result -> return result-    Left e       -> throwError $ fromParsecError (toSources toks) e+  -- now parse the toks as inlines; parseFromToks preserves any+  -- state changes (e.g. macro definitions), since an optional+  -- argument is not a TeX group+  parseFromToks (mconcat <$> many inline) toks  -- block elements: @@ -764,7 +882,7 @@ rule :: PandocMonad m => LP m Blocks rule = do   skipopts-  width <- T.takeWhile (\c -> isDigit c || c == '.') . stringify <$> tok+  width <- T.takeWhile (\c -> isDigit c || c == '.') . stringifyInlines <$> tok   _thickness <- tok   -- 0-width rules are used to fix spacing issues:   case safeRead width of@@ -945,7 +1063,8 @@         lookAhead $ blankline <|> startCommand         return $ curr <> mconcat rest   let raw = rawDefiniteBlock <|> rawMaybeBlock-  lookupListDefault raw names blockCommands+  commandMap <- envBlockCommands <$> askEnv+  lookupListDefault raw names commandMap  closing :: PandocMonad m => LP m Blocks closing = do@@ -1062,10 +1181,30 @@    -- include    , ("include", rawBlockOr "include" $ include "include")    , ("input", rawBlockOr "input" $ include "input")+   , ("expandableinput", rawBlockOr "expandableinput" $ include "input")    , ("subfile", rawBlockOr "subfile" doSubfile)    , ("usepackage", rawBlockOr "usepackage" usepackage)    -- preamble    , ("PackageError", mempty <$ (braced >> braced >> braced))+   -- LaTeX3 conveniences, parsed and ignored:+   , ("ExplSyntaxOn", pure mempty)+   , ("ExplSyntaxOff", pure mempty)+   , ("ShowCommand", mempty <$ withVerbatimMode (spaces *> anyControlSeq))+   , ("ShowEnvironment", mempty <$ braced)+   , ("DeclareKeys", mempty <$ (skipopts *> braced))+   , ("DeclareUnknownKeyHandler", mempty <$ (skipopts *> braced))+   , ("ProcessKeyOptions", mempty <$ skipopts)+   , ("SetKeys", mempty <$ (skipopts *> braced))+   -- LaTeX3 case changing+   , ("DeclareUppercaseExclusions", declareCaseExclusions "upper")+   , ("DeclareLowercaseExclusions", declareCaseExclusions "lower")+   , ("DeclareTitlecaseExclusions", declareCaseExclusions "title")+   , ("AddToNoCaseChangeList", mempty <$ braced)+   , ("DeclareCaseChangeEquivalent", mempty <$+        (withVerbatimMode (spaces *> anyControlSeq) *> braced))+   , ("DeclareUppercaseMapping", mempty <$ (skipopts *> braced *> braced))+   , ("DeclareLowercaseMapping", mempty <$ (skipopts *> braced *> braced))+   , ("DeclareTitlecaseMapping", mempty <$ (skipopts *> braced *> braced))    -- epigraph package    , ("epigraph", epigraph)    -- alignment@@ -1154,7 +1293,8 @@ environment = try $ do   controlSeq "begin"   name <- untokenize <$> braced-  M.findWithDefault mzero name environments <|>+  envMap <- envEnvironments <$> askEnv+  M.findWithDefault mzero name envMap <|>     langEnvironment name <|>     theoremEnvironment blocks inlines opt name <|>     if M.member name (inlineEnvironments
src/Text/Pandoc/Readers/LaTeX/Citation.hs view
@@ -13,8 +13,6 @@ import Data.Text (Text) import Control.Applicative ((<|>), optional, many) import Control.Monad (mzero)-import Control.Monad.Trans (lift)-import Control.Monad.Except (throwError) import Text.Pandoc.Parsing hiding (blankline, many, mathDisplay, mathInline,                             optional, space, spaces, withRaw, (<|>)) @@ -113,14 +111,10 @@   opt :: PandocMonad m => LP m Inlines   opt = do     toks <- try (sp *> bracketedToks <* sp)-    -- now parse the toks as inlines-    st <- getState-    parsed <- lift $-      runParserT (mconcat <$> many inline) st "bracketed option"-       (TokStream False toks)-    case parsed of-      Right result -> return result-      Left e       -> throwError $ fromParsecError (toSources toks) e+    -- now parse the toks as inlines; parseFromToks preserves any+    -- state changes (e.g. macro definitions), since an optional+    -- argument is not a TeX group+    parseFromToks (mconcat <$> many inline) toks   
src/Text/Pandoc/Readers/LaTeX/Inline.hs view
@@ -36,8 +36,7 @@ import Text.Pandoc.Readers.LaTeX.Parsing import Text.Pandoc.Extensions (extensionEnabled, Extension(..)) import Text.Pandoc.Parsing (getOption, updateState, getState, notFollowedBy,-                            manyTill, getInput, setInput, incSourceColumn,-                            option, many1)+                            manyTill, option, many1) import Data.Char (isDigit) import Text.Pandoc.Highlighting (fromListingsLanguage,) import Data.Maybe (maybeToList, fromMaybe)@@ -89,19 +88,6 @@     code . untokenize <$>       manyTill (notFollowedBy newlineTok >> verbTok marker) (symbol marker) -verbTok :: PandocMonad m => Char -> LP m Tok-verbTok stopchar = do-  t@(Tok pos toktype txt) <- anyTok-  case T.findIndex (== stopchar) txt of-       Nothing -> return t-       Just i  -> do-         let (t1, t2) = T.splitAt i txt-         TokStream macrosExpanded inp <- getInput-         setInput $ TokStream macrosExpanded-                  $ Tok (incSourceColumn pos i) Symbol (T.singleton stopchar)-                  : tokenize (incSourceColumn pos (i + 1)) (T.drop 1 t2) ++ inp-         return $ Tok pos toktype t1- listingsLanguage :: [(Text, Text)] -> Maybe Text listingsLanguage opts =   case lookup "language" opts of@@ -282,7 +268,7 @@   , ("{", lit "{")   , ("}", lit "}")   , ("-", lit "\x00ad") -- soft hyphen-  , ("qed", lit "\a0\x25FB")+  , ("qed", lit "\xa0\x25FB")   , ("lq", return (str "‘"))   , ("rq", return (str "’"))   , ("textquoteleft", return (str "‘"))
src/Text/Pandoc/Readers/LaTeX/Macro.hs view
@@ -12,7 +12,10 @@ import Text.Pandoc.Parsing hiding (blankline, mathDisplay, mathInline,                             optional, space, spaces, withRaw, (<|>)) import Control.Applicative ((<|>), optional)+import Control.Monad (guard)+import Data.Char (chr, isLetter, ord) import qualified Data.Map as M+import qualified Data.Set as Set import Data.Text (Text) import qualified Data.Text as T import qualified Data.List.NonEmpty as NonEmpty@@ -20,12 +23,17 @@  macroDef :: (PandocMonad m, Monoid a) => (Text -> a) -> LP m a macroDef constructor = do+    Tok _ (CtrlSeq name) _ <- peekTok+    -- fail quickly, before trying each alternative in turn, unless+    -- the next token can begin a macro definition:+    guard $ name `Set.member` macroDefCommands     (_, s) <- withRaw (commandDef <|> environmentDef)     (constructor (untokenize s) <$       guardDisabled Ext_latex_macros)      <|> return mempty   where commandDef = do-          nameMacroPairs <- newcommand <|>+          nameMacroPairs <- newcommand <|> newDocumentCommand <|>+            newDocumentEnvironment <|> commandCopy <|> environmentCopy <|>             checkGlobal (letmacro <|> edefmacro <|> defmacro <|> newif)           guardDisabled Ext_latex_macros <|>             mapM_ insertMacro nameMacroPairs@@ -42,6 +50,26 @@         -- @\newcommand{\envname}[n-args][default]{begin}@         -- @\newcommand{\endenvname}@ +-- | Control sequences that can begin a macro definition.  This+-- must include every control sequence that one of the parsers+-- used in 'macroDef' can start with.+macroDefCommands :: Set.Set Text+macroDefCommands = Set.fromList+  [ "global"  -- see checkGlobal+  , "let", "edef", "xdef", "def", "gdef", "newif"+  , "newcommand", "renewcommand", "providecommand"+  , "DeclareMathOperator", "DeclareRobustCommand"+  , "NewDocumentCommand", "RenewDocumentCommand"+  , "ProvideDocumentCommand", "DeclareDocumentCommand"+  , "NewExpandableDocumentCommand", "RenewExpandableDocumentCommand"+  , "ProvideExpandableDocumentCommand", "DeclareExpandableDocumentCommand"+  , "NewDocumentEnvironment", "RenewDocumentEnvironment"+  , "ProvideDocumentEnvironment", "DeclareDocumentEnvironment"+  , "NewCommandCopy", "RenewCommandCopy", "DeclareCommandCopy"+  , "NewEnvironmentCopy", "RenewEnvironmentCopy", "DeclareEnvironmentCopy"+  , "newenvironment", "renewenvironment", "provideenvironment"+  ]+ insertMacro :: PandocMonad m => (Text, Macro) -> LP m () insertMacro (name, macro'@(Macro GlobalScope _ _ _ _)) =   updateState $ \s ->@@ -116,10 +144,11 @@ -- \footrue to be a command that defines \iffoo to be \iftrue -- \foofalse to be a command that defines \iffoo to be \iffalse newif :: PandocMonad m => LP m [(Text, Macro)]-newif = do+newif = try $ do   controlSeq "newif"   withVerbatimMode $ do     Tok pos (CtrlSeq name) _ <- anyControlSeq+    guard $ "if" `T.isPrefixOf` name     -- \def\iffoo\iffalse     -- \def\footrue{\def\iffoo\iftrue}     -- \def\foofalse{\def\iffoo\iffalse}@@ -191,6 +220,241 @@           "renewcommand" -> return [(name, macro)]           _ -> [] <$ report (MacroAlreadyDefined txt pos))       <|> pure [(name, macro)]++-- | Parses a definition of the form+-- @\NewDocumentCommand\cmd{argspec}{body}@ (and the Renew, Provide,+-- Declare, and Expandable variants), with xparse (LaTeX3) argument+-- specifiers.+newDocumentCommand :: PandocMonad m => LP m [(Text, Macro)]+newDocumentCommand = try $ do+  Tok pos (CtrlSeq mtype) _ <-+        controlSeq "NewDocumentCommand"+    <|> controlSeq "RenewDocumentCommand"+    <|> controlSeq "ProvideDocumentCommand"+    <|> controlSeq "DeclareDocumentCommand"+    <|> controlSeq "NewExpandableDocumentCommand"+    <|> controlSeq "RenewExpandableDocumentCommand"+    <|> controlSeq "ProvideExpandableDocumentCommand"+    <|> controlSeq "DeclareExpandableDocumentCommand"+  withVerbatimMode $ do+    Tok _ (CtrlSeq name) txt <- do+      spaces+      anyControlSeq <|>+        (symbol '{' *> spaces *> anyControlSeq <* spaces <* symbol '}')+    spaces+    (argspecs, _) <- xparseArgSpecs False+    spaces+    contents <- bracedOrToken+    let macro = Macro GroupScope ExpandWhenUsed argspecs Nothing contents+    (do lookupMacro name+        if "Provide" `T.isPrefixOf` mtype+           then return []+           else if "New" `T.isPrefixOf` mtype+                   then [] <$ report (MacroAlreadyDefined txt pos)+                   else return [(name, macro)]) -- Renew or Declare+      <|> pure [(name, macro)]++-- | Parses a definition of the form+-- @\NewDocumentEnvironment{name}{argspec}{begin-code}{end-code}@+-- (and the Renew, Provide, and Declare variants), with xparse+-- (LaTeX3) argument specifiers.  Unlike with @\newenvironment@,+-- the arguments are also available in the end-code; to support+-- this we bind a group-scoped helper macro, at the point where+-- @\begin{name}@ is expanded, whose body is the end-code with the+-- arguments already substituted; @\end{name}@ just expands the+-- helper.  (Group scoping makes this work for nested environments.)+newDocumentEnvironment :: PandocMonad m => LP m [(Text, Macro)]+newDocumentEnvironment = try $ do+  Tok pos (CtrlSeq mtype) _ <-+        controlSeq "NewDocumentEnvironment"+    <|> controlSeq "RenewDocumentEnvironment"+    <|> controlSeq "ProvideDocumentEnvironment"+    <|> controlSeq "DeclareDocumentEnvironment"+  withVerbatimMode $ do+    spaces+    name <- T.strip . untokenize <$> braced+    spaces+    (argspecs, usesBody) <- xparseArgSpecs True+    startcontents <- spaces >> bracedOrToken+    endcontents <- spaces >> bracedOrToken+    -- we need the environment to be in a group so macros defined+    -- inside behave correctly:+    let bg = Tok pos (CtrlSeq "bgroup") "\\bgroup "+    let eg = Tok pos (CtrlSeq "egroup") "\\egroup "+    let helperName = "pandocxparseenvend" <> letterize name+    let helper = Tok pos (CtrlSeq helperName) ("\\" <> helperName <> " ")+    let defHelper = Tok pos (CtrlSeq "def") "\\def"+                  : helper+                  : Tok pos Symbol "{"+                  : endcontents ++ [Tok pos Symbol "}"]+    let result+          | usesBody =+            -- a 'b' argspec grabs everything up to \end{name} as the+            -- last argument (the argspecs end with its ArgNum, and we+            -- add a Pattern that consumes the \end{name}), so begin-+            -- and end-code run together:+            [ (name,+               Macro GroupScope ExpandWhenUsed+                 (argspecs ++ [Pattern (tokenize pos ("\\end{" <> name <> "}"))])+                 Nothing+                 (bg : defHelper ++ startcontents ++ [helper, eg])) ]+          | otherwise =+            [ (name,+               Macro GroupScope ExpandWhenUsed argspecs Nothing+                 (bg : defHelper ++ startcontents))+            , ("end" <> name,+               Macro GroupScope ExpandWhenUsed [] Nothing [helper, eg]) ]+    (do lookupMacro name+        if "Provide" `T.isPrefixOf` mtype+           then return []+           else if "New" `T.isPrefixOf` mtype+                   then [] <$ report (MacroAlreadyDefined name pos)+                   else return result) -- Renew or Declare+      <|> pure result++-- | Parses @\NewCommandCopy\new\old@ (and the Renew and Declare+-- variants): like @\let@, restricted to control sequences.+commandCopy :: PandocMonad m => LP m [(Text, Macro)]+commandCopy = try $ do+  Tok pos (CtrlSeq mtype) _ <-+        controlSeq "NewCommandCopy"+    <|> controlSeq "RenewCommandCopy"+    <|> controlSeq "DeclareCommandCopy"+  withVerbatimMode $ do+    Tok _ (CtrlSeq name) txt <- do+      spaces+      anyControlSeq <|>+        (symbol '{' *> spaces *> anyControlSeq <* spaces <* symbol '}')+    spaces+    target@(Tok _ (CtrlSeq targetName) _) <- anyControlSeq+    result <- (do m <- lookupMacro targetName+                  pure [(name, m)])+          <|> pure [(name, Macro GroupScope ExpandWhenDefined [] Nothing+                             [target])]+    (do lookupMacro name+        if "New" `T.isPrefixOf` mtype+           then [] <$ report (MacroAlreadyDefined txt pos)+           else return result) -- Renew or Declare+      <|> pure result++-- | Parses @\NewEnvironmentCopy{new}{old}@ (and the Renew and+-- Declare variants), copying both the begin and the end macros.+-- If the target environment is not macro-defined, the copy expands+-- to @\begin{old}@ / @\end{old}@.+environmentCopy :: PandocMonad m => LP m [(Text, Macro)]+environmentCopy = try $ do+  Tok pos (CtrlSeq mtype) _ <-+        controlSeq "NewEnvironmentCopy"+    <|> controlSeq "RenewEnvironmentCopy"+    <|> controlSeq "DeclareEnvironmentCopy"+  withVerbatimMode $ do+    spaces+    name <- untokenize <$> braced+    spaces+    target <- untokenize <$> braced+    let copyOne to from fallback =+          (do m <- lookupMacro from+              pure (to, m))+          <|> pure (to, Macro GroupScope ExpandWhenUsed [] Nothing+                          (tokenize pos fallback))+    beginmacro <- copyOne name target ("\\begin{" <> target <> "}")+    endmacro <- copyOne ("end" <> name) ("end" <> target)+                        ("\\end{" <> target <> "}")+    let result = [beginmacro, endmacro]+    (do lookupMacro name+        if "New" `T.isPrefixOf` mtype+           then [] <$ report (MacroAlreadyDefined name pos)+           else return result) -- Renew or Declare+      <|> pure result++-- | Encode a text using letters only (so that the result can be+-- part of a control sequence name that survives retokenization).+letterize :: Text -> Text+letterize = T.concatMap go+  where go c | isLetter c = T.singleton c+             | otherwise = "x" <> T.map toLetter (T.pack (show (ord c)))+        toLetter d = chr (ord d + 49)  -- '0'..'9' -> 'a'..'j'++-- | Parses a braced xparse argument specification, e.g.+-- @{s O{default} m}@.  The Bool parameter determines whether a+-- @b@ (environment body) specifier is allowed; the Bool in the+-- result is True if one was used (it can only come last).+xparseArgSpecs :: PandocMonad m => Bool -> LP m ([ArgSpec], Bool)+xparseArgSpecs allowBody = symbol '{' *> go 1+ where+  go n = do+    spaces+    (([], False) <$ symbol '}') <|>+      (do (spec, isBody) <- xparseArgSpec allowBody n+          if isBody+             then ([spec], True) <$ (spaces <* symbol '}')+             else do (rest, usesBody) <- go (n + numslots spec)+                     return (spec : rest, usesBody))+  -- most specifiers bind one argument; embellishments bind one+  -- argument per embellishment token+  numslots (EmbellishArg embs) = length embs+  numslots (ProcessedArg _ spec) = numslots spec+  numslots _ = 1++xparseArgSpec :: PandocMonad m => Bool -> Int -> LP m (ArgSpec, Bool)+xparseArgSpec allowBody n = go True+ where+  -- the parameter is False if the ! modifier has been seen; it+  -- disables space-skipping before optional arguments, and (for the+  -- b and c body specifiers) trimming of the ends of the body+  go noBang = do+   Tok pos _ c <- singleChar+   let plain spec = pure (spec, False)+   case c of+    "+" -> spaces *> go noBang  -- "long": no distinction+    "!" -> spaces *> go False   -- no space-skipping+    "=" -> spaces *> braced *> spaces *> go noBang+           -- key-value interface for the argument: not modeled+    ">" -> do proc <- spaces *> braced+              (spec, isBody) <- spaces *> go noBang+              if isBody+                 then pure (spec, isBody)+                      -- processors are not supported on 'b' arguments+                 else case spec of+                        ProcessedArg procs inner ->+                          pure (ProcessedArg (proc : procs) inner, False)+                        _ -> pure (ProcessedArg [proc] spec, False)+    "m" -> plain $ ArgNum n+    "o" -> plain $ DelimArg noBang (lbTok pos) (rbTok pos) Nothing+    "O" -> do dflt <- spaces *> braced+              plain $ DelimArg noBang (lbTok pos) (rbTok pos) (Just dflt)+    "s" -> plain $ BoolArg noBang (Tok pos Symbol "*")+    "t" -> specTok >>= plain . BoolArg noBang+    "d" -> do o <- specTok+              c' <- specTok+              plain $ DelimArg noBang o c' Nothing+    "D" -> do o <- specTok+              c' <- specTok+              dflt <- spaces *> braced+              plain $ DelimArg noBang o c' (Just dflt)+    "r" -> do o <- specTok+              c' <- specTok+              plain $ DelimArg noBang o c' Nothing+    "R" -> do o <- specTok+              c' <- specTok+              dflt <- spaces *> braced+              plain $ DelimArg noBang o c' (Just dflt)+    "v" -> plain VerbArg+    "e" -> do embtoks <- spaces *> braced+              plain $ EmbellishArg [(t, Nothing) | t <- embTokens embtoks]+    "E" -> do embtoks <- spaces *> braced+              spaces+              defaults <- symbol '{' *> many (try (spaces *> braced))+                            <* spaces <* symbol '}'+              plain $ EmbellishArg $+                zip (embTokens embtoks) (map Just defaults ++ repeat Nothing)+    "b" | allowBody -> pure (BodyArg noBang n, True)+    "c" | allowBody -> pure (VerbBodyArg noBang n, True)  -- verbatim body+    _   -> fail "unsupported xparse argument specifier"+  lbTok pos = Tok pos Symbol "["+  rbTok pos = Tok pos Symbol "]"+  specTok = spaces *> singleChar+  embTokens = filter (not . tokTypeIn [Spaces, Newline, Comment])  newenvironment :: PandocMonad m => LP m (Maybe (Text, Macro, Macro)) newenvironment = do
src/Text/Pandoc/Readers/LaTeX/Parsing.hs view
@@ -25,6 +25,9 @@   , LaTeXState(..)   , defaultLaTeXState   , LP+  , LaTeXEnv(..)+  , emptyLaTeXEnv+  , askEnv   , TokStream(..)   , withVerbatimMode   , rawLaTeXParser@@ -73,6 +76,7 @@   , bracedOrToken   , bracketed   , bracketedToks+  , verbTok   , parenWrapped   , dimenarg   , ignore@@ -97,11 +101,12 @@ import Control.Applicative (many, (<|>)) import Control.Monad import Control.Monad.Except (throwError)+import Control.Monad.Reader (ReaderT, ask, runReaderT) import Control.Monad.Trans (lift) import Data.Char (chr, isAlphaNum, isDigit, isLetter, ord) import Data.Default-import Data.List (intercalate)-import qualified Data.IntMap as IntMap+import Data.List (dropWhileEnd, intercalate, isSuffixOf, unfoldr)+import Numeric (showEFloat, showFFloat) import qualified Data.Map as M import qualified Data.Set as Set import Data.Text (Text)@@ -112,7 +117,7 @@ import Text.Pandoc.Builder import Text.Pandoc.Class.PandocMonad (PandocMonad, report) import Text.Pandoc.Error-         (PandocError (PandocMacroLoop,PandocShouldNeverHappenError))+         (PandocError (PandocMacroLoop)) import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (blankline, many, mathDisplay, mathInline,@@ -174,8 +179,18 @@                             , sToggles       :: M.Map Text Bool                             , sFileContents  :: M.Map Text Text                             , sEnableWithRaw :: Bool-                            , sRawTokens     :: IntMap.IntMap [Tok]+                            , sRawTokens     :: [Tok]+                              -- ^ reversed list of tokens consumed+                              -- while at least one withRaw scope is+                              -- active+                            , sRawTokenCount :: !Int+                              -- ^ length of sRawTokens+                            , sRawScopes     :: !Int+                              -- ^ number of active withRaw scopes                             , sLigatures     :: Bool+                            , sCaseExclusions :: M.Map Text (Set.Set Text)+                              -- ^ words excluded from case changing+                              -- (keys: @upper@, @lower@, @title@)                             }      deriving Show @@ -205,8 +220,11 @@                               , sToggles       = M.empty                               , sFileContents  = M.empty                               , sEnableWithRaw = True-                              , sRawTokens     = IntMap.empty+                              , sRawTokens     = []+                              , sRawTokenCount = 0+                              , sRawScopes     = 0                               , sLigatures     = True+                              , sCaseExclusions = M.empty                               }  instance PandocMonad m => HasQuoteContext LaTeXState m where@@ -267,8 +285,27 @@   uncons (TokStream _ []) = return Nothing   uncons (TokStream _ (t:ts)) = return $ Just (t, TokStream False ts) -type LP m = ParsecT TokStream LaTeXState m+type LP m = ParsecT TokStream LaTeXState (ReaderT (LaTeXEnv m) m) +-- | Environment holding the command dispatch tables.  Because these+-- tables have types that mention the parser monad, they cannot be+-- top-level constants; passing them in a reader environment ensures+-- they are constructed once per parse rather than once per use.+data LaTeXEnv m = LaTeXEnv+  { envInlineCommands :: M.Map Text (LP m Inlines)+  , envBlockCommands  :: M.Map Text (LP m Blocks)+  , envEnvironments   :: M.Map Text (LP m Blocks)+  }++-- | An environment with empty dispatch tables, for running parsers+-- that do not consult them.+emptyLaTeXEnv :: LaTeXEnv m+emptyLaTeXEnv = LaTeXEnv mempty mempty mempty++-- | Retrieve the command dispatch tables.+askEnv :: Monad m => LP m (LaTeXEnv m)+askEnv = lift ask+ withVerbatimMode :: PandocMonad m => LP m a -> LP m a withVerbatimMode parser = do   alreadyVerbatimMode <- sVerbatimMode <$> getState@@ -281,9 +318,9 @@        return result  rawLaTeXParser :: (PandocMonad m, HasMacros s, HasReaderOptions s, Show a)-               => [Tok] -> LP m () -> LP m a+               => LaTeXEnv m -> [Tok] -> LP m () -> LP m a                -> ParsecT Sources s m (a, Text)-rawLaTeXParser toks parser valParser = do+rawLaTeXParser lenv toks parser valParser = do   pstate <- getState   let lstate = def{ sOptions = extractReaderOptions pstate }   let lstate' = lstate { sMacros = extractMacros pstate :| [] }@@ -292,12 +329,14 @@                       _ -> return ()   let preparser = setStartPos >> parser   let rawparser = (,) <$> withRaw valParser <*> getState-  res' <- lift $ runParserT (withRaw (preparser >> getPosition))+  res' <- lift $ flip runReaderT lenv $+                 runParserT (withRaw (preparser >> getPosition))                             lstate "chunk" $ TokStream False toks   case res' of        Left _    -> mzero        Right (endpos, toks') -> do-         res <- lift $ runParserT rawparser lstate' "chunk"+         res <- lift $ flip runReaderT lenv $+                       runParserT rawparser lstate' "chunk"                      $ TokStream False toks'          case res of               Left _    -> mzero@@ -328,7 +367,8 @@       pstate <- getState       let lstate = def{ sOptions = extractReaderOptions pstate                       , sMacros  = extractMacros pstate :| [] }-      res <- runParserT retokenize lstate "math" $+      res <- flip runReaderT emptyLaTeXEnv $+                 runParserT retokenize lstate "math" $                  TokStream False (tokenize (initialPos "math") s)       case res of            Left e   -> Prelude.fail (show e)@@ -360,7 +400,7 @@       Sources ((_,t):rest) -> tokenizeSources $ Sources ((pos,t):rest)  tokenize :: SourcePos -> Text -> [Tok]-tokenize = totoks False+tokenize = totoks (TokenizerState False False)  where   totoks atIsLetter pos t =     case T.uncons t of@@ -389,10 +429,17 @@                   | isLetter' atIsLetter d ->                       let (ws, rest'') = T.span (isLetter' atIsLetter) rest                           (ss, rest''') = T.span isSpaceOrTab rest''-                          atIsLetter' = case ws of-                                          "makeatletter" -> True-                                          "makeatother" -> False-                                          _ -> atIsLetter+                          atIsLetter' =+                            case ws of+                              "makeatletter" ->+                                atIsLetter{ tsAtIsLetter = True }+                              "makeatother" ->+                                atIsLetter{ tsAtIsLetter = False }+                              "ExplSyntaxOn" ->+                                atIsLetter{ tsExplSyntax = True }+                              "ExplSyntaxOff" ->+                                atIsLetter{ tsExplSyntax = False }+                              _ -> atIsLetter                       in  Tok pos (CtrlSeq ws) ("\\" <> ws <> ss)                           : totoks atIsLetter' (incSourceColumn pos                                (1 + T.length ws + T.length ss)) rest'''@@ -427,7 +474,7 @@                               (incSourceColumn pos (2 + T.length t1)) t2                         Nothing -> Tok pos Symbol "#"                                   : Tok (incSourceColumn pos 1) Symbol "#"-                                  : totoks atIsLetter (incSourceColumn pos 1) t3+                                  : totoks atIsLetter (incSourceColumn pos 2) t3              _ ->                let (t1, t2) = T.span (\d -> d >= '0' && d <= '9') rest                in  case safeRead t1 of@@ -470,9 +517,18 @@ isSpaceOrTab '\t' = True isSpaceOrTab _    = False --- First parameter is True if @ is letter-isLetter' :: Bool -> Char -> Bool-isLetter' True '@' = True+-- | State threaded through the tokenizer: whether @\@@ is a letter+-- (between @\makeatletter@ and @\makeatother@), and whether @:@ and+-- @_@ are letters (between @\ExplSyntaxOn@ and @\ExplSyntaxOff@).+data TokenizerState = TokenizerState+  { tsAtIsLetter :: Bool+  , tsExplSyntax :: Bool+  }++isLetter' :: TokenizerState -> Char -> Bool+isLetter' st '@' = tsAtIsLetter st+isLetter' st ':' = tsExplSyntax st+isLetter' st '_' = tsExplSyntax st isLetter' _ c = isLetter c  isLetterOrAt :: Char -> Bool@@ -483,21 +539,23 @@ isLowerHex x = x >= '0' && x <= '9' || x >= 'a' && x <= 'f'  untokenize :: [Tok] -> Text-untokenize = foldr untokenAccum mempty--untokenAccum :: Tok -> Text -> Text-untokenAccum (Tok _ (CtrlSeq _) t) accum =-  -- insert space to prevent breaking a control sequence; see #5836-  case (T.unsnoc t, T.uncons accum) of-    (Just (_,c), Just (d,_))-      | isLetter c-      , isLetter d-      -> t <> " " <> accum-    _ -> t <> accum-untokenAccum (Tok _ _ t) accum = t <> accum+untokenize = T.concat . go+  where+    go [] = []+    go (Tok _ (CtrlSeq _) t : ts)+      -- insert space to prevent breaking a control sequence; see #5836+      | Just (_, c) <- T.unsnoc t+      , isLetter c+      , nextStartsWithLetter ts = t : " " : go ts+    go (Tok _ _ t : ts) = t : go ts+    nextStartsWithLetter (Tok _ _ t : ts) =+      case T.uncons t of+        Just (d, _) -> isLetter d+        Nothing     -> nextStartsWithLetter ts+    nextStartsWithLetter [] = False  untoken :: Tok -> Text-untoken t = untokenAccum t mempty+untoken (Tok _ _ t) = t  parseFromToks :: PandocMonad m => LP m a -> [Tok] -> LP m a parseFromToks parser toks = do@@ -507,11 +565,15 @@   case toks of      Tok pos _ _ : _ -> setPosition pos      _ -> return ()-  -- we ignore existing raw tokens maps (see #9517)-  oldRawTokens <- sRawTokens <$> getState-  updateState $ \st -> st{ sRawTokens = mempty }+  -- we ignore existing raw token accumulation (see #9517)+  oldst <- getState+  updateState $ \st -> st{ sRawTokens = []+                         , sRawTokenCount = 0+                         , sRawScopes = 0 }   result <- parser-  updateState $ \st -> st{ sRawTokens = oldRawTokens }+  updateState $ \st -> st{ sRawTokens = sRawTokens oldst+                         , sRawTokenCount = sRawTokenCount oldst+                         , sRawScopes = sRawScopes oldst }   setInput oldInput   setPosition oldpos   return result@@ -529,10 +591,9 @@     doMacros -- apply macros on remaining input stream     res <- tokenPrim (T.unpack . untoken) updatePos matcher     updateState $ \st ->-      if sEnableWithRaw st-         then-           let !newraws = IntMap.map (res:) $! sRawTokens st-            in  st{ sRawTokens = newraws }+      if sRawScopes st > 0 && sEnableWithRaw st+         then st{ sRawTokens = res : sRawTokens st+                , sRawTokenCount = sRawTokenCount st + 1 }          else st     return $! res   where matcher t | f t       = Just t@@ -544,15 +605,24 @@ peekTok :: PandocMonad m => LP m Tok peekTok = do   doMacros-  lookAhead (satisfyTok (const True))+  TokStream _ toks <- getInput+  case toks of+    t : _ -> return t+    []    -> mzero  doMacros :: PandocMonad m => LP m () doMacros = do   TokStream macrosExpanded toks <- getInput-  unless macrosExpanded $ do-    st <- getState-    unless (sVerbatimMode st) $-      doMacros' 1 toks >>= setInput . TokStream True+  unless macrosExpanded $+    case toks of+      -- only a control sequence at the head of the stream can+      -- trigger macro expansion; in other cases we skip the+      -- state update:+      Tok _ (CtrlSeq _) _ : _ -> do+        st <- getState+        unless (sVerbatimMode st) $+          doMacros' 1 toks >>= setInput . TokStream True+      _ -> return ()  doMacros' :: PandocMonad m => Int -> [Tok] -> LP m [Tok] doMacros' n inp =@@ -583,18 +653,81 @@      matchPattern toks = try $ mapM_ matchTok toks -    getargs argmap [] = return argmap-    getargs argmap (Pattern toks : rest) = try $ do+    -- the first parameter is the number of the next argument+    -- to be bound (needed for the argspecs that don't carry an+    -- argument number themselves)+    getargs _ argmap [] = return argmap+    getargs num argmap (Pattern toks : rest) = try $ do        matchPattern toks-       getargs argmap rest-    getargs argmap (ArgNum i : Pattern toks : rest) =+       getargs num argmap rest+    getargs _ argmap (ArgNum i : Pattern toks : rest) =       try $ do         x <- mconcat <$> manyTill (braced <|> ((:[]) <$> anyTok))                   (matchPattern toks)-        getargs (M.insert i x argmap) rest-    getargs argmap (ArgNum i : rest) = do+        getargs (i + 1) (M.insert i x argmap) rest+    getargs _ argmap (ArgNum i : rest) = do       x <- try $ spaces >> bracedOrToken-      getargs (M.insert i x argmap) rest+      getargs (i + 1) (M.insert i x argmap) rest+    getargs num argmap (BoolArg skipSp t@(Tok pos _ _) : rest) = do+      -- the ! modifier (skipSp False) disables space-skipping:+      let sp' = when skipSp sp+      x <- option [Tok pos (CtrlSeq "BooleanFalse") "\\BooleanFalse "]+             ([Tok pos (CtrlSeq "BooleanTrue") "\\BooleanTrue "]+               <$ try (sp' *> matchTok t))+      getargs (num + 1) (M.insert num x argmap) rest+    getargs num argmap (DelimArg skipSp open@(Tok pos _ _) close mbdef+                         : rest) = do+      let sp' = when skipSp sp+      let missing = fromMaybe [Tok pos (CtrlSeq "NoValue") "\\NoValue "] mbdef+      x <- option missing (try (sp' *> delimitedToks open close))+      getargs (num + 1) (M.insert num x argmap) rest+    getargs num argmap (VerbArg : rest) = do+      x <- verbatimArg+      getargs (num + 1) (M.insert num x argmap) rest+    -- xparse 'b' specifier: environment body, grabbed up to the+    -- \end{name} pattern; spaces at the ends are trimmed unless+    -- the ! modifier was used:+    getargs _ argmap (BodyArg doTrim i : Pattern toks : rest) = try $ do+      x <- mconcat <$> manyTill ((snd <$> withRaw (try braced))+                                  <|> ((:[]) <$> anyTok))+                (matchPattern toks)+      let x' = if doTrim then trimSpaceToks x else x+      getargs (i + 1) (M.insert i x' argmap) rest+    getargs num argmap (BodyArg _ i : rest) =+      getargs num argmap (ArgNum i : rest)+    -- xparse 'c' specifier: environment body, grabbed verbatim up+    -- to the \end{name} pattern; blank lines at the ends are+    -- trimmed unless the ! modifier was used:+    getargs _ argmap (VerbBodyArg doTrim i : Pattern toks : rest) = try $ do+      x <- mconcat <$> manyTill ((snd <$> withRaw (try braced))+                                  <|> ((:[]) <$> anyTok))+                (matchPattern toks)+      getargs (i + 1) (M.insert i (verbatimBodyToks doTrim x) argmap) rest+    getargs num argmap (VerbBodyArg _ i : rest) =+      getargs num argmap (ArgNum i : rest)+    getargs num argmap (EmbellishArg embs : rest) = do+      let grab seen+            | length seen == length embs = pure seen+            | otherwise = option seen $ try $ do+                sp+                i <- choice [ i <$ matchTok t+                            | (i, (t, _)) <- zip [(0 :: Int)..] embs+                            , i `notElem` map fst seen ]+                x <- braced <|> count 1 anyTok+                grab ((i, x) : seen)+      seen <- grab []+      let getval i (Tok pos _ _, mbdef) =+            case lookup i seen of+              Just x  -> x+              Nothing ->+                fromMaybe [Tok pos (CtrlSeq "NoValue") "\\NoValue "] mbdef+      let argmap' = foldr (\(i, e) m -> M.insert (num + i) (getval i e) m)+                          argmap (zip [0..] embs)+      getargs (num + length embs) argmap' rest+    getargs num argmap (ProcessedArg procs spec : rest) = do+      newargs <- getargs num M.empty [spec]+      newargs' <- traverse (applyArgProcessors procs) newargs+      getargs (num + M.size newargs) (M.union newargs' argmap) rest      addTok False _args spos (Tok _ (DeferredArg i) txt) acc =       Tok spos (Arg i) txt : acc@@ -615,7 +748,9 @@         $ throwError $ PandocMacroLoop name       (macros :| _ ) <- sMacros <$> getState       case M.lookup name macros of-           Nothing -> trySpecialMacro name ts+           -- the result of a special macro may itself begin with a+           -- macro call, so we continue expanding:+           Nothing -> trySpecialMacro name ts >>= doMacros' (n' + 1)            Just (Macro _scope expansionPoint argspecs optarg newtoks) -> do              let getargs' = do                    args <-@@ -623,10 +758,10 @@                         ExpandWhenUsed    -> withVerbatimMode                         ExpandWhenDefined -> id)                      $ case optarg of-                             Nothing -> getargs M.empty argspecs+                             Nothing -> getargs 1 M.empty argspecs                              Just o  -> do                                 x <- option o bracketedToks-                                getargs (M.singleton 1 x) $ drop 1 argspecs+                                getargs 2 (M.singleton 1 x) $ drop 1 argspecs                    TokStream _ rest <- getInput                    return (args, rest)              lstate <- getState@@ -634,11 +769,34 @@              case res of                Left _ -> Prelude.fail $ "Could not parse arguments for " ++                                 T.unpack name-               Right (args, rest) -> do+               Right (args', rest) -> do+                 -- An argument default may refer to other arguments+                 -- (e.g. O{#2}); resolve such references (the visited+                 -- list guards against reference cycles):+                 let resolveTok visited t@(Tok _ (Arg j) _)+                       | j `notElem` visited+                       , Just ys <- M.lookup j args'+                       = concatMap (resolveTok (j : visited)) ys+                       | otherwise = [t]+                     resolveTok _ t = [t]+                 let args = M.mapWithKey+                              (\i -> concatMap (resolveTok [i])) args'+                 -- In TeX, a newline in a macro body behaves like a+                 -- space; a single newline at the end of the body,+                 -- followed by a newline in the source, must not be+                 -- mistaken for a blank line (paragraph break):+                 let newtoks' =+                       case reverse newtoks of+                         Tok p Newline _ : rts+                           | not (any isNewlineTok (take 1 rts))+                           , (Tok _ Newline _ : _) <-+                               dropWhile (tokTypeIn [Spaces, Comment]) rest+                           -> reverse (Tok p Spaces " " : rts)+                         _ -> newtoks                  -- first boolean param is true if we're tokenizing                  -- an argument (in which case we don't want to                  -- expand #1 etc.)-                 let result = foldr (addTok False args spos) rest newtoks+                 let result = foldr (addTok False args spos) rest newtoks'                  case expansionPoint of                    ExpandWhenUsed    -> doMacros' (n' + 1) result                    ExpandWhenDefined -> return result@@ -652,15 +810,133 @@     Tok pos Word t : _       | startsWithAlphaNum t -> return $ Tok pos Spaces " " : ts'     _ -> return ts'-trySpecialMacro "iftrue" ts = handleIf (ifParser True) ts-trySpecialMacro "iffalse" ts = handleIf (ifParser False) ts+trySpecialMacro "iftrue" ts = doIf True ts+trySpecialMacro "iffalse" ts = doIf False ts trySpecialMacro "ifmmode" ts = do   mathMode <- sMathMode <$> getState-  handleIf (ifParser mathMode) ts+  doIf mathMode ts trySpecialMacro "ifstrequal" ts = do   handleIf ifStrequalParser ts+-- xparse (LaTeX3) argument conditionals:+trySpecialMacro "IfNoValueTF" ts = handleIf (xparseIf isNoValueArg True True) ts+trySpecialMacro "IfNoValueT" ts = handleIf (xparseIf isNoValueArg True False) ts+trySpecialMacro "IfNoValueF" ts = handleIf (xparseIf isNoValueArg False True) ts+trySpecialMacro "IfValueTF" ts =+  handleIf (xparseIf (not . isNoValueArg) True True) ts+trySpecialMacro "IfValueT" ts =+  handleIf (xparseIf (not . isNoValueArg) True False) ts+trySpecialMacro "IfValueF" ts =+  handleIf (xparseIf (not . isNoValueArg) False True) ts+trySpecialMacro "IfBooleanTF" ts =+  handleIf (xparseIf isBooleanTrueArg True True) ts+trySpecialMacro "IfBooleanT" ts =+  handleIf (xparseIf isBooleanTrueArg True False) ts+trySpecialMacro "IfBooleanF" ts =+  handleIf (xparseIf isBooleanTrueArg False True) ts+trySpecialMacro "IfBlankTF" ts = handleIf (xparseIf isBlankArg True True) ts+trySpecialMacro "IfBlankT" ts = handleIf (xparseIf isBlankArg True False) ts+trySpecialMacro "IfBlankF" ts = handleIf (xparseIf isBlankArg False True) ts+-- \ProcessList{list}{tokens}: apply tokens to every item of list:+trySpecialMacro "ProcessList" ts = handleIf processListParser ts+-- \UseName{string}: turn string into a csname and execute it:+trySpecialMacro "UseName" ts = handleIf useNameParser ts+-- \ExpandArgs{spec}\cmd{arg1}...: pre-expand the command's+-- arguments as described by the spec:+trySpecialMacro "ExpandArgs" ts = handleIf expandArgsParser ts+-- LaTeX3 expandable evaluators:+trySpecialMacro "inteval" ts = handleEval evalInteval ts+trySpecialMacro "fpeval" ts = handleEval evalFpeval ts+-- dimension expressions are not evaluated; we substitute the+-- expression itself:+trySpecialMacro "dimeval" ts = handleEval (Just . T.strip) ts+trySpecialMacro "skipeval" ts = handleEval (Just . T.strip) ts trySpecialMacro _ _ = mzero +-- | Parse a conditional of the kind used with xparse commands+-- (@\\IfNoValueTF@ etc.): test the first argument and select the+-- true or false branch.  The Bool parameters indicate whether a+-- true and a false branch, respectively, are to be parsed.+xparseIf :: PandocMonad m => ([Tok] -> Bool) -> Bool -> Bool -> LP m [Tok]+xparseIf test hasTrueBranch hasFalseBranch = do+  -- spaces and comments may intervene between the arguments:+  let grabArg = withVerbatimMode (spaces *> (braced <|> count 1 anyTok))+  let getBranch cond = if cond+                          then grabArg+                          else pure []+  arg <- grabArg+  trueToks <- getBranch hasTrueBranch+  falseToks <- getBranch hasFalseBranch+  TokStream _ rest <- getInput+  return $ (if test arg then trueToks else falseToks) ++ rest++isNoValueArg :: [Tok] -> Bool+isNoValueArg toks =+  case filter (not . tokTypeIn [Spaces, Newline, Comment]) toks of+    [Tok _ (CtrlSeq "NoValue") _] -> True+    [Tok _ Symbol "-", Tok _ Word "NoValue", Tok _ Symbol "-"] -> True+    _ -> False++isBooleanTrueArg :: [Tok] -> Bool+isBooleanTrueArg toks =+  case filter (not . tokTypeIn [Spaces, Newline, Comment]) toks of+    [Tok _ (CtrlSeq "BooleanTrue") _] -> True+    _ -> False++-- | An argument is \"blank\" (in the sense of @\\IfBlankTF@) if it+-- is empty or consists only of blanks.+isBlankArg :: [Tok] -> Bool+isBlankArg = all (tokTypeIn [Spaces, Newline, Comment])++-- | Parser for the arguments of @\\ProcessList{list}{tokens}@:+-- apply the tokens to every item (braced group or single token) of+-- the list.+processListParser :: PandocMonad m => LP m [Tok]+processListParser = withVerbatimMode $ do+  pos <- getPosition+  spaces+  list <- braced <|> count 1 anyTok+  spaces+  fn <- braced <|> count 1 anyTok+  TokStream _ rest <- getInput+  let items = unfoldr tokGroup list+  let braceIt item = Tok pos Symbol "{" : item ++ [Tok pos Symbol "}"]+  return $ concatMap (\item -> fn ++ braceIt item) items ++ rest++-- | Parser for the argument of @\\UseName{string}@: turn the+-- string into a control sequence.+useNameParser :: PandocMonad m => LP m [Tok]+useNameParser = do+  pos <- getPosition+  name <- untokenize <$> withVerbatimMode (spaces *> braced)+  TokStream _ rest <- getInput+  return $ Tok pos (CtrlSeq name) ("\\" <> name <> " ") : rest++-- | Parser for the arguments of @\\ExpandArgs{spec}\\cmd{arg1}...@:+-- transform each argument as described by the corresponding letter+-- of the spec (@c@ = turn a string into a control sequence, @n@ =+-- leave a braced argument unchanged, @N@ = leave a single token+-- unchanged), then put the command before the transformed+-- arguments.+expandArgsParser :: PandocMonad m => LP m [Tok]+expandArgsParser = withVerbatimMode $ do+  spec <- T.unpack . untokenize <$> (spaces *> braced)+  spaces+  cmd <- anyTok+  args <- concat <$> mapM transformArg spec+  TokStream _ rest <- getInput+  return $ cmd : args ++ rest+ where+  transformArg 'c' = do+    pos <- getPosition+    name <- untokenize <$> (spaces *> braced)+    return [Tok pos (CtrlSeq name) ("\\" <> name <> " ")]+  transformArg 'n' = do+    pos <- getPosition+    toks <- spaces *> braced+    return $ Tok pos Symbol "{" : toks ++ [Tok pos Symbol "}"]+  transformArg 'N' = spaces *> count 1 anyTok+  transformArg _ = mzero+ ifStrequalParser :: PandocMonad m => LP m [Tok] ifStrequalParser = do   str1 <- braced <|> count 1 anyTok@@ -681,15 +957,292 @@     Left _ -> Prelude.fail "Could not parse conditional"     Right ts' -> return ts' -ifParser :: PandocMonad m => Bool -> LP m [Tok]-ifParser b = do-  ifToks <- many (notFollowedBy (controlSeq "else" <|> controlSeq "fi")-                    *> anyTok)-  elseToks <- (controlSeq "else" >> manyTill anyTok (controlSeq "fi"))-                 <|> ([] <$ controlSeq "fi")-  TokStream _ rest <- getInput-  return $ (if b then ifToks else elseToks) ++ rest+-- | Names of TeX's primitive conditionals.  While scanning for the+-- @\\else@ or @\\fi@ that ends a conditional branch, we need to+-- know which control sequences begin a conditional, so that the+-- @\\else@ and @\\fi@ belonging to nested conditionals are not+-- mistaken for the end of the current one.+primitiveConditionalNames :: Set.Set Text+primitiveConditionalNames = Set.fromList+  [ "if", "ifcase", "ifcat", "ifcsname", "ifdefined", "ifdim"+  , "ifeof", "iffalse", "iffontchar", "ifhbox", "ifhmode"+  , "ifincsname", "ifinner", "ifmmode", "ifnum", "ifodd", "iftrue"+  , "ifvbox", "ifvmode", "ifvoid", "ifx" ] +-- | Handle a conditional like @\\iftrue@: select the branch before+-- @\\else@ (or @\\fi@) if the Bool is True, the else branch+-- otherwise.  The unselected branch is skipped without expansion+-- (as TeX does), keeping nested conditionals balanced.+doIf :: PandocMonad m => Bool -> [Tok] -> LP m [Tok]+doIf b ts = do+  macros <- sMacros <$> getState+  -- Conditionals defined with \newif (or \let from a primitive+  -- conditional) expand to a single conditional token; TeX+  -- recognizes these too when skipping conditional text.+  let isConditionalMacro (Macro _ _ [] Nothing [Tok _ (CtrlSeq n) _]) =+        n `Set.member` primitiveConditionalNames+      isConditionalMacro _ = False+  let conditionals = foldr+        (\m s -> M.foldrWithKey+           (\k v s' -> if isConditionalMacro v+                          then Set.insert k s'+                          else s')+           s m)+        primitiveConditionalNames macros+  case splitConditional conditionals ts of+    Just (ifToks, elseToks, rest) ->+      return $ (if b then ifToks else elseToks) ++ rest+    Nothing -> Prelude.fail "Could not parse conditional"++-- | Split the tokens following a conditional into the tokens+-- before @\\else@ (or @\\fi@), the tokens of the else branch (if+-- any), and the tokens after the matching @\\fi@.  Returns Nothing+-- if there is no matching @\\fi@.+splitConditional :: Set.Set Text -> [Tok] -> Maybe ([Tok], [Tok], [Tok])+splitConditional conditionals = goIf (0 :: Int) id+ where+  goIf _ _ [] = Nothing+  goIf depth acc (t@(Tok _ (CtrlSeq name) _) : rest)+    | name == "fi"+    , depth == 0 = Just (acc [], [], rest)+    | name == "fi" = goIf (depth - 1) (acc . (t:)) rest+    | name == "else"+    , depth == 0 = goElse (acc []) (0 :: Int) id rest+    | name `Set.member` conditionals = goIf (depth + 1) (acc . (t:)) rest+  goIf depth acc (t : rest) = goIf depth (acc . (t:)) rest+  goElse _ _ _ [] = Nothing+  goElse ifToks depth acc (t@(Tok _ (CtrlSeq name) _) : rest)+    | name == "fi"+    , depth == 0 = Just (ifToks, acc [], rest)+    | name == "fi" = goElse ifToks (depth - 1) (acc . (t:)) rest+    | name `Set.member` conditionals =+        goElse ifToks (depth + 1) (acc . (t:)) rest+  goElse ifToks depth acc (t : rest) = goElse ifToks depth (acc . (t:)) rest++-- | Handle a LaTeX3 expandable evaluator (@\inteval@, @\fpeval@,+-- ...): grab the braced argument (macros in it are expanded as it+-- is consumed), evaluate it, and substitute the result.  If the+-- expression cannot be evaluated, substitute the expression text+-- itself and report it.+handleEval :: PandocMonad m => (Text -> Maybe Text) -> [Tok] -> LP m [Tok]+handleEval evaluator ts = do+  lstate <- getState+  res <- lift $ runParserT evalParser lstate "eval" $ TokStream False ts+  case res of+    Left _ -> Prelude.fail "Could not parse evaluator argument"+    Right ts' -> return ts'+ where+  evalParser = do+    pos <- getPosition+    arg <- untokenize <$> braced+    TokStream _ rest <- getInput+    case evaluator arg of+      Just result -> return $ tokenize pos result ++ rest+      Nothing -> do+        report $ SkippedContent ("evaluation of " <> arg) pos+        return $ tokenize pos arg ++ rest++-- | Evaluate an integer expression (@\inteval@): @+ - * / ( )@,+-- with division rounding to the nearest integer (ties away from+-- zero, as in eTeX's @\numexpr@).+evalInteval :: Text -> Maybe Text+evalInteval t = do+  (n, rest) <- pIntExpr t+  guard $ T.null (skipWs rest)+  pure $ T.pack (show n)+ where+  pIntExpr s0 = pIntTerm s0 >>= addLoop+  addLoop (acc, s) =+    case T.uncons (skipWs s) of+      Just ('+', s') -> do (y, s'') <- pIntTerm s'+                           addLoop (acc + y, s'')+      Just ('-', s') -> do (y, s'') <- pIntTerm s'+                           addLoop (acc - y, s'')+      _ -> Just (acc, s)+  pIntTerm s0 = pIntFactor s0 >>= mulLoop+  mulLoop (acc, s) =+    case T.uncons (skipWs s) of+      Just ('*', s') -> do (y, s'') <- pIntFactor s'+                           mulLoop (acc * y, s'')+      Just ('/', s') -> do (y, s'') <- pIntFactor s'+                           guard $ y /= 0+                           mulLoop (divRound acc y, s'')+      _ -> Just (acc, s)+  pIntFactor s0 =+    case T.uncons (skipWs s0) of+      Just ('-', s) -> do (x, s') <- pIntFactor s+                          pure (negate x, s')+      Just ('+', s) -> pIntFactor s+      Just ('(', s) -> do+        (x, s') <- pIntExpr s+        case T.uncons (skipWs s') of+          Just (')', s'') -> pure (x, s'')+          _ -> Nothing+      Just (c, _) | isDigit c ->+        let (ds, s) = T.span isDigit (skipWs s0)+        in do n <- safeRead ds+              pure (n :: Integer, s)+      _ -> Nothing+  divRound a b =+    let (q, r) = a `quotRem` b+    in if 2 * abs r >= abs b+          then q + signum a * signum b+          else q++-- | Evaluate a floating point expression (a practical subset of+-- @\fpeval@): @+ - * / ^ ( )@ (also @**@ for @^@) and decimal+-- literals.+evalFpeval :: Text -> Maybe Text+evalFpeval t0 = do+  let t = T.replace "**" "^" t0+  (x, rest) <- pFpExpr t+  guard $ T.null (skipWs rest)+  guard $ not (isNaN x || isInfinite x)+  pure $ formatFp x+ where+  pFpExpr s0 = pFpTerm s0 >>= addLoop+  addLoop (acc, s) =+    case T.uncons (skipWs s) of+      Just ('+', s') -> do (y, s'') <- pFpTerm s'+                           addLoop (acc + y, s'')+      Just ('-', s') -> do (y, s'') <- pFpTerm s'+                           addLoop (acc - y, s'')+      _ -> Just (acc, s)+  pFpTerm s0 = pFpPow s0 >>= mulLoop+  mulLoop (acc, s) =+    case T.uncons (skipWs s) of+      Just ('*', s') -> do (y, s'') <- pFpPow s'+                           mulLoop (acc * y, s'')+      Just ('/', s') -> do (y, s'') <- pFpPow s'+                           mulLoop (acc / y, s'')+      _ -> Just (acc, s)+  pFpPow s0 = do+    (x, s) <- pFpFactor s0+    case T.uncons (skipWs s) of+      Just ('^', s') -> do (y, s'') <- pFpPow s'  -- right-associative+                           pure (x ** y, s'')+      _ -> pure (x, s)+  pFpFactor s0 =+    case T.uncons (skipWs s0) of+      Just ('-', s) -> do (x, s') <- pFpFactor s+                          pure (negate x, s')+      Just ('+', s) -> pFpFactor s+      Just ('(', s) -> do+        (x, s') <- pFpExpr s+        case T.uncons (skipWs s') of+          Just (')', s'') -> pure (x, s'')+          _ -> Nothing+      Just (c, _) | isLetter c ->+        let (name, s1) = T.span isLetter (skipWs s0)+        in case name of+             "pi"  -> pure (pi, s1)+             "deg" -> pure (pi / 180, s1)  -- one degree in radians+             _ ->+               case T.uncons (skipWs s1) of+                 Just ('(', s2) -> do+                   (args, s3) <- pFpArgs s2+                   x <- applyFn name args+                   pure (x, s3)+                 _ -> do+                   -- prefix application without parentheses,+                   -- e.g. "sqrt 2":+                   (y, s2) <- pFpFactor s1+                   x <- applyFn name [y]+                   pure (x, s2)+      Just (c, _) | isDigit c || c == '.' ->+        let s = skipWs s0+            (ds, s') = T.span (\d -> isDigit d || d == '.') s+            (expt, s'') = case T.uncons s' of+                            Just (e, r) | e == 'e' || e == 'E' ->+                              let (sign, r') =+                                    case T.uncons r of+                                      Just (sg, rr) | sg == '+' || sg == '-'+                                        -> (T.singleton sg, rr)+                                      _ -> ("", r)+                                  (eds, r'') = T.span isDigit r'+                              in if T.null eds+                                    then ("", s')+                                    else ("e" <> sign <> eds, r'')+                            _ -> ("", s')+        in do x <- safeRead (fixup ds <> expt)+              pure (x :: Double, s'')+      _ -> Nothing+  fixup ds -- make the literal readable for Haskell's 'read'+    | "." `T.isPrefixOf` ds = "0" <> fixup' ds+    | otherwise = fixup' ds+  fixup' ds+    | "." `T.isSuffixOf` ds = ds <> "0"+    | otherwise = ds+  -- comma-separated function arguments, ending with ')':+  pFpArgs s0 = do+    (x, s) <- pFpExpr s0+    case T.uncons (skipWs s) of+      Just (',', s') -> do (xs, s'') <- pFpArgs s'+                           pure (x : xs, s'')+      Just (')', s') -> pure ([x], s')+      _ -> Nothing+  applyFn name args =+    case (lookup name unaryFns, args) of+      (Just f, [x]) -> Just (f x)+      _ ->+        case (name, args) of+          ("max", _:_) -> Just (maximum args)+          ("min", _:_) -> Just (minimum args)+          ("atan", [x, y]) -> Just (atan2 x y)+          ("atand", [x, y]) -> Just (unrad (atan2 x y))+          ("round", _) -> rounder (fromInteger . round) args+          ("floor", _) -> rounder (fromInteger . floor) args+          ("ceil", _) -> rounder (fromInteger . ceiling) args+          ("trunc", _) -> rounder (fromInteger . truncate) args+          _ -> Nothing+  -- rounding functions take an optional number of decimal places:+  rounder f [x] = Just (f x)+  rounder f [x, n] | n == fromInteger (round n) =+    let m = 10 ^^ (round n :: Integer)+        -- round to 16 significant digits first (like l3fp), so+        -- that e.g. round(2.345,2) gives 2.34, not 2.35:+        y = read (showEFloat (Just 15) (x * m) "") :: Double+    in Just (f y / m)+  rounder _ _ = Nothing+  rad x = x * pi / 180+  unrad x = x * 180 / pi+  unaryFns :: [(Text, Double -> Double)]+  unaryFns =+    [ ("abs", abs), ("sign", signum), ("sqrt", sqrt)+    , ("exp", exp), ("ln", log)+    , ("fact", \x -> if x >= 0 && x == fromInteger (round x) && x < 171+                        then fromInteger (product [1 .. round x])+                        else 0 / 0)+    , ("sin", sin), ("cos", cos), ("tan", tan)+    , ("cot", recip . tan), ("sec", recip . cos), ("csc", recip . sin)+    , ("asin", asin), ("acos", acos), ("atan", atan)+    , ("acot", atan . recip), ("asec", acos . recip), ("acsc", asin . recip)+    , ("sind", sin . rad), ("cosd", cos . rad), ("tand", tan . rad)+    , ("cotd", recip . tan . rad), ("secd", recip . cos . rad)+    , ("cscd", recip . sin . rad)+    , ("asind", unrad . asin), ("acosd", unrad . acos)+    , ("atand", unrad . atan), ("acotd", unrad . atan . recip)+    , ("asecd", unrad . acos . recip), ("acscd", unrad . asin . recip)+    ]++formatFp :: Double -> Text+formatFp x0+  | x == fromInteger r && abs x < 1e16 = T.pack (show r)+  | otherwise = T.pack (trimZeros (showFFloat Nothing x ""))+ where+  -- l3fp computes with 16 significant decimal digits; round to+  -- that precision so that e.g. 0.1 + 0.2 yields 0.3.+  x = read (showEFloat (Just 15) x0 "") :: Double+  r = round x+  trimZeros s+    | '.' `elem` s = case dropWhileEnd (== '0') s of+                       s' | "." `isSuffixOf` s' -> take (length s' - 1) s'+                          | otherwise -> s'+    | otherwise = s++skipWs :: Text -> Text+skipWs = T.dropWhile (\c -> c == ' ' || c == '\t' || c == '\n' || c == '\r')+ startsWithAlphaNum :: Text -> Bool startsWithAlphaNum t =   case T.uncons t of@@ -876,10 +1429,7 @@ retokenizeComment :: PandocMonad m => LP m () retokenizeComment = (do   Tok pos Comment txt <- satisfyTok isCommentTok-  let updPos (Tok pos' toktype' txt') =-        Tok (incSourceColumn (incSourceLine pos' (sourceLine pos - 1))-             (sourceColumn pos)) toktype' txt'-  let newtoks = map updPos $ tokenize pos $ T.tail txt+  let newtoks = tokenize (incSourceColumn pos 1) $ T.tail txt   TokStream macrosExpanded ts <- getInput   setInput $ TokStream macrosExpanded ((Tok pos Symbol "%" : newtoks) ++ ts))     <|> return ()@@ -898,6 +1448,168 @@   concat <$> manyTill ((snd <$> withRaw (try braced)) <|> count 1 anyTok)                       (symbol ']') +-- | Tokens between opening and closing delimiter tokens (which are+-- compared by token type and text, ignoring position).  Nested+-- delimiter pairs are balanced (when the delimiters differ), and+-- braced groups are skipped, so delimiters inside braces don't count.+delimitedToks :: PandocMonad m => Tok -> Tok -> LP m [Tok]+delimitedToks open close = matchesTok open *> go (1 :: Int)+ where+  matchesTok (Tok _ toktype txt) =+    satisfyTok (\(Tok _ toktype' txt') -> toktype == toktype' && txt == txt')+  go n = (do ts <- snd <$> withRaw (try braced)+             (ts ++) <$> go n)+     <|> (do t <- matchesTok close+             if n == 1+                then return []+                else (t:) <$> go (n - 1))+     <|> (do t <- matchesTok open+             (t:) <$> go (n + 1))+     <|> (do t <- anyTok+             (t:) <$> go n)++-- | A verbatim argument (xparse @v@ specifier): either a braced+-- group or tokens between two identical delimiter characters.+-- The result is a single Word token containing the raw text, so+-- that its contents are not reinterpreted when substituted.+verbatimArg :: PandocMonad m => LP m [Tok]+verbatimArg = try $ do+  optional sp+  toks <- braced <|> delimited+  case toks of+    [] -> pure []+    Tok pos _ _ : _ -> pure [Tok pos Word (untokenize toks)]+ where+  delimited = do+    Tok _ Symbol t <- anySymbol+    marker <- case T.uncons t of+                Just (c, ts) | T.null ts -> return c+                _            -> mzero+    manyTill (notFollowedBy newlineTok >> verbTok marker) (symbol marker)++-- | Convert a verbatim environment body (xparse @c@ specifier) to+-- tokens for substitution.  The body is typeset verbatim by LaTeX,+-- with each space rendered as the character in slot 32 of the+-- current font (the visible space U+2423 in typewriter fonts) and+-- each source line on its own line; we emulate this by making each+-- line a single Word token (so contents are not reinterpreted),+-- with spaces replaced by U+2423 and lines separated by @\\\\@.+-- Tabs are kept as-is (LaTeX typesets the raw tab character, not a+-- visible space).  Note that the typewriter font is not automatic:+-- it comes from a @\\ttfamily@ or similar in the environment+-- definition.  If the first parameter is True, leading and trailing+-- blank lines are trimmed.+verbatimBodyToks :: Bool -> [Tok] -> [Tok]+verbatimBodyToks _ [] = []+verbatimBodyToks doTrim toks@(Tok pos _ _ : _) =+  intercalate [Tok pos (CtrlSeq "\\") "\\\\"] (map lineToks ls)+ where+  lineToks l+    | T.null l = []+    | otherwise = [Tok pos Word (T.map toVisibleSpace l)]+  toVisibleSpace c = if c == ' ' then '\x2423' else c+  ls = (if doTrim+           then dropWhileEnd isBlankLine . dropWhile isBlankLine+           else id) $ T.lines (untokenize toks)+  isBlankLine = T.all (\c -> c == ' ' || c == '\t')++-- | Apply xparse argument processors (from the @>{...}@ modifier)+-- to a grabbed argument.  Processors are applied from right to left+-- (i.e., the one nearest the argument specifier first).+applyArgProcessors :: PandocMonad m => [[Tok]] -> [Tok] -> LP m [Tok]+applyArgProcessors [] x = pure x+applyArgProcessors (p:ps) x = applyArgProcessors ps x >>= applyArgProcessor p++applyArgProcessor :: PandocMonad m => [Tok] -> [Tok] -> LP m [Tok]+applyArgProcessor proc x =+  case dropWhile spaceLike proc of+    Tok _ (CtrlSeq "TrimSpaces") _ : _ -> pure $ trimSpaceToks x+    Tok pos (CtrlSeq "ReverseBoolean") _ : _ ->+      pure $ case filter (not . spaceLike) x of+        [Tok _ (CtrlSeq "BooleanTrue") _]  ->+          [Tok pos (CtrlSeq "BooleanFalse") "\\BooleanFalse "]+        [Tok _ (CtrlSeq "BooleanFalse") _] ->+          [Tok pos (CtrlSeq "BooleanTrue") "\\BooleanTrue "]+        _ -> x+    Tok pos (CtrlSeq "SplitArgument") _ : ts+      | Just (numtoks, ts') <- tokGroup ts+      , Just numparts <- safeRead (untokenize numtoks)+      , Just (delimtoks, _) <- tokGroup ts'+      , (d : _) <- dropWhile spaceLike delimtoks ->+        let missing = [Tok pos (CtrlSeq "NoValue") "\\NoValue "]+            pieces = take (numparts + 1) $+                       map trimSpaceToks (splitToksOn d x) ++ repeat missing+        in pure $ concatMap (braceGroup pos) pieces+    Tok pos (CtrlSeq "SplitList") _ : ts+      | Just (delimtoks, _) <- tokGroup ts+      , (d : _) <- dropWhile spaceLike delimtoks ->+        pure $ concatMap (braceGroup pos) (map trimSpaceToks (splitToksOn d x))+    Tok pos _ _ : _ -> do+      -- unknown (or malformed) processor: keep the argument unprocessed+      report $ SkippedContent ("processor " <> untokenize proc) pos+      pure x+    [] -> pure x+ where+  spaceLike = tokTypeIn [Spaces, Newline, Comment]+  braceGroup pos ts = Tok pos Symbol "{" : ts ++ [Tok pos Symbol "}"]++-- | Remove space tokens at both ends of a token list.+trimSpaceToks :: [Tok] -> [Tok]+trimSpaceToks = dropWhile isSp . dropWhileEnd isSp+  where isSp = tokTypeIn [Spaces, Newline]++-- | Parse a braced group (or a single token) from the beginning of+-- a token list, skipping leading spaces; return the group's+-- contents and the remaining tokens.+tokGroup :: [Tok] -> Maybe ([Tok], [Tok])+tokGroup ts =+  case dropWhile (tokTypeIn [Spaces, Newline, Comment]) ts of+    Tok _ Symbol "{" : rest -> go (1 :: Int) [] rest+    t : rest -> Just ([t], rest)+    [] -> Nothing+ where+  go _ _ [] = Nothing+  go depth acc (t : rest)+    | isSym "{" t = go (depth + 1) (t : acc) rest+    | isSym "}" t = if depth == 1+                       then Just (reverse acc, rest)+                       else go (depth - 1) (t : acc) rest+    | otherwise = go depth (t : acc) rest+  isSym s (Tok _ Symbol s') = s == s'+  isSym _ _ = False++-- | Split a token list at each depth-0 occurrence of the delimiter+-- token (compared by token type and text).+splitToksOn :: Tok -> [Tok] -> [[Tok]]+splitToksOn (Tok _ dtype dtxt) = go (0 :: Int) []+ where+  go _ acc [] = [reverse acc]+  go depth acc (t@(Tok _ toktype txt) : rest)+    | depth == 0, toktype == dtype, txt == dtxt = reverse acc : go 0 [] rest+    | otherwise =+        let depth' = case t of+                       Tok _ Symbol "{" -> depth + 1+                       Tok _ Symbol "}" -> max 0 (depth - 1)+                       _ -> depth+        in go depth' (t : acc) rest++-- | Any token, but if the token contains @stopchar@, it is split+-- so that the part before @stopchar@ is returned and @stopchar@+-- itself (plus what follows) is left in the input.  Used for+-- parsing verbatim text delimited by @stopchar@.+verbTok :: PandocMonad m => Char -> LP m Tok+verbTok stopchar = do+  t@(Tok pos toktype txt) <- anyTok+  case T.findIndex (== stopchar) txt of+       Nothing -> return t+       Just i  -> do+         let (t1, t2) = T.splitAt i txt+         TokStream macrosExpanded inp <- getInput+         setInput $ TokStream macrosExpanded+                  $ Tok (incSourceColumn pos i) Symbol (T.singleton stopchar)+                  : tokenize (incSourceColumn pos (i + 1)) (T.drop 1 t2) ++ inp+         return $ Tok pos toktype t1+ parenWrapped :: PandocMonad m => Monoid a => LP m a -> LP m a parenWrapped parser = try $ do   symbol '('@@ -936,23 +1648,16 @@  withRaw :: PandocMonad m => LP m a -> LP m (a, [Tok]) withRaw parser = do-  rawTokensMap <- sRawTokens <$> getState-  let key = case IntMap.lookupMax rawTokensMap of-               Nothing     -> 0-               Just (n,_)  -> n + 1-  -- insert empty list at key-  updateState $ \st -> st{ sRawTokens =-                             IntMap.insert key [] $ sRawTokens st }+  startCount <- sRawTokenCount <$> getState+  updateState $ \st -> st{ sRawScopes = sRawScopes st + 1 }   result <- parser-  mbRevToks <- IntMap.lookup key . sRawTokens <$> getState-  raw <- case mbRevToks of-           Just revtoks -> do-             updateState $ \st -> st{ sRawTokens =-                                        IntMap.delete key $ sRawTokens st}-             return $ reverse revtoks-           Nothing      ->-             throwError $ PandocShouldNeverHappenError $-                "sRawTokens has nothing at key " <> T.pack (show key)+  st <- getState+  let raw = reverse $ take (sRawTokenCount st - startCount) (sRawTokens st)+  setState $ if sRawScopes st <= 1+                then st{ sRawScopes = 0+                       , sRawTokens = []+                       , sRawTokenCount = 0 }+                else st{ sRawScopes = sRawScopes st - 1 }   return (result, raw)  keyval :: PandocMonad m => LP m (Text, Text)
src/Text/Pandoc/Readers/LaTeX/Table.hs view
@@ -15,7 +15,7 @@ import qualified Data.Text as T import Control.Applicative ((<|>), optional, many) import Control.Monad (when, void)-import Text.Pandoc.Shared (safeRead, trim)+import Text.Pandoc.Shared (safeRead, trim, compactifyTable) import Text.Pandoc.Logging (LogMessage(SkippedContent)) import Text.Pandoc.Walk (walkM) import Text.Pandoc.Parsing hiding (blankline, many, mathDisplay, mathInline,@@ -190,7 +190,7 @@     -- The parsing of empty cells is important in LaTeX, especially when dealing     -- with multirow/multicolumn. See #6603.     parseEmptyCell = spaces $> emptyCell-    parseSimpleCell = simpleCell <$> (plainify . mconcat <$> many block)+    parseSimpleCell = simpleCell . mconcat <$> many block   cellAlignment :: PandocMonad m => LP m Alignment@@ -205,11 +205,6 @@         "*" -> AlignDefault         _   -> AlignDefault -plainify :: Blocks -> Blocks-plainify bs = case toList bs of-                [Para ils] -> plain (fromList ils)-                _          -> bs- multirowCell :: PandocMonad m => LP m Blocks -> LP m Cell multirowCell block = controlSeq "multirow" >> do   -- Full prototype for \multirow macro is:@@ -221,7 +216,7 @@   _ <- optional $ symbol '[' *> manyTill anyTok (symbol ']')  -- bigstrut-related   _ <- symbol '{' *> manyTill anyTok (symbol '}')             -- Cell width   _ <- optional $ symbol '[' *> manyTill anyTok (symbol ']')  -- Length used for fine-tuning-  content <- symbol '{' *> (plainify . mconcat <$> many block) <* symbol '}'+  content <- symbol '{' *> (mconcat <$> many block) <* symbol '}'   return $ cell AlignDefault (RowSpan nrows) (ColSpan 1) content  multicolumnCell :: PandocMonad m => LP m Blocks -> LP m Cell@@ -230,7 +225,7 @@   alignment <- symbol '{' *> cellAlignment <* symbol '}'    let singleCell = do-        content <- plainify . mconcat <$> many block+        content <- mconcat <$> many block         return $ cell alignment (RowSpan 1) (ColSpan span') content    -- Two possible contents: either a \multirow cell, or content.@@ -357,7 +352,8 @@   let th  = fixTableHead $ TableHead nullAttr header'   let tbs = [fixTableBody $ TableBody nullAttr 0 [] rows]   let tf  = TableFoot nullAttr []-  return $ table emptyCaption (zip aligns widths) th tbs tf+  return $ compactifyTable+         $ table emptyCaption (zip aligns widths) th tbs tf  addTableCaption :: PandocMonad m => Blocks -> LP m Blocks addTableCaption = walkM go
src/Text/Pandoc/Readers/Man.hs view
@@ -33,7 +33,7 @@ import qualified Text.Pandoc.Parsing as P import qualified Data.Foldable as Foldable import qualified Data.Set as Set-import Text.Pandoc.Shared (extractSpaces)+import Text.Pandoc.Shared (extractSpaces, compactifyTable)  data ManState = ManState { readerOptions   :: ReaderOptions                          , manLogMessages  :: []LogMessage@@ -129,7 +129,8 @@       let widths = if isPlainTable                       then repeat ColWidthDefault                       else repeat $ ColWidth (1.0 / fromIntegral (length alignments))-      return $ B.table B.emptyCaption (zip alignments widths)+      return $ compactifyTable+             $ B.table B.emptyCaption (zip alignments widths)                   (TableHead nullAttr $ toHeaderRow headerRow)                   [TableBody nullAttr 0 [] $ map toRow bodyRows]                   (TableFoot nullAttr [])) <|> fallback pos
src/Text/Pandoc/Readers/Markdown.hs view
@@ -23,7 +23,7 @@ import Control.Monad import Control.Monad.Except (throwError) import qualified Data.Bifunctor as Bifunctor-import Data.Char (isAlphaNum, isPunctuation, isSpace)+import Data.Char (isAlphaNum, isDigit, isLetter, isPunctuation, isSpace) import Data.List (transpose, elemIndex, sortOn) import qualified Data.List as L import qualified Data.Map as M@@ -53,7 +53,8 @@ import Text.Pandoc.Readers.HTML.TagCategories (voidTags) import Text.Pandoc.Readers.LaTeX (applyMacros, rawLaTeXBlock, rawLaTeXInline) import Text.Pandoc.Shared-import Text.Pandoc.URI (escapeURI, isURI, pBase64DataURI)+import Text.Pandoc.URI (escapeURI, pBase64DataURI)+import Network.URI (isURI) import Text.Pandoc.XML (fromEntities) import Text.Pandoc.Readers.Metadata (yamlBsToMeta, yamlBsToRefs, yamlMetaBlock) -- import Debug.Trace (traceShowId)@@ -412,7 +413,7 @@   char c   notFollowedBy spaces   let pEnder = try $ char c >> notFollowedBy (satisfy isAlphaNum)-  let regChunk = many1Char (noneOf ['\\','\n','&',c]) <|> litChar+  let regChunk = takeWhile1P (`notElem` ['\\','\n','&',c]) <|> litChar   let nestedChunk = (\x -> (c `T.cons` x) `T.snoc` c) <$> quotedTitle c   T.unwords . T.words . T.concat <$> manyTill (nestedChunk <|> regChunk) pEnder @@ -660,7 +661,7 @@ identifierAttr :: PandocMonad m => MarkdownParser m (Attr -> Attr) identifierAttr = try $ do   char '#'-  result <- T.pack <$> many1 (alphaNum <|> oneOf "-_:.") -- see #7920+  result <- takeWhile1P (\x -> isAlphaNum x || x `elem` ("-_:." :: [Char])) -- see #7920   return $ \(_,cs,kvs) -> (result,cs,kvs)  classAttr :: PandocMonad m => MarkdownParser m (Attr -> Attr)@@ -814,12 +815,13 @@  blockQuote :: PandocMonad m => MarkdownParser m (F Blocks) blockQuote = do+  pos <- getPosition   raw <- emailBlockQuote   (mbAlert, raw') <-     (do guardEnabled Ext_alerts         case raw of           (t:ts) | "[!" `T.isPrefixOf` t ->-              case T.strip t of+              case T.toUpper (T.strip t) of                 "[!TIP]" -> pure (Just "tip", ts)                 "[!WARNING]" -> pure (Just "warning", ts)                 "[!IMPORTANT]" -> pure (Just "important", ts)@@ -829,12 +831,13 @@           _ -> 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"+  contents <- parseFromString' (setPosition pos >> parseBlocks)+               $ T.intercalate "\n" raw' <> "\n\n"   return $     case mbAlert of       Nothing -> B.blockQuote <$> contents       Just alert ->-        (B.divWith ("", [alert], [])+        (B.divWith ("", ["alert", alert], [])           . (B.divWith ("", ["title"], []) (B.para (B.str (T.toTitle alert))) <>))            <$> contents @@ -859,7 +862,7 @@   skipNonindentSpaces   notFollowedBy $ string "p." >> spaceChar >> digit  -- page number   (do guardDisabled Ext_fancy_lists-      start <- many1Char digit >>= safeRead+      start <- takeWhile1P isDigit >>= safeRead       char '.'       gobbleSpaces 1 <|> () <$ lookAhead newline       optional $ try (gobbleAtMostSpaces 3 >> notFollowedBy spaceChar)@@ -971,11 +974,12 @@   state <- getState   let oldContext = stateParserContext state   setState $ state {stateParserContext = ListItemState}+  pos <- getPosition   (first, continuationIndent) <- rawListItem fourSpaceRule start   continuations <- many (listContinuation continuationIndent)   -- parse the extracted block, which may contain various block elements:   let raw = T.concat (first:continuations)-  contents <- parseFromString' parseBlocks raw+  contents <- parseFromString' (setPosition pos >> parseBlocks) raw   updateState (\st -> st {stateParserContext = oldContext})   exts <- getOption readerExtensions   return $ B.fromList . taskListItemFromAscii exts . B.toList <$> contents@@ -1016,8 +1020,9 @@  definitionListItem :: PandocMonad m => MarkdownParser m (F (Inlines, [Blocks])) definitionListItem = try $ do+  pos <- getPosition   rawLine' <- anyLine-  term <- parseFromString' (trimInlinesF <$> inlines) rawLine'+  term <- parseFromString' (setPosition pos >> (trimInlinesF <$> inlines)) rawLine'   isTight <- (False <$ blanklines) <|> pure True   fourSpaceRule <- (True <$ guardEnabled Ext_four_space_rule) <|> pure False   contents <- many1 $ listItem fourSpaceRule defListStart@@ -1223,8 +1228,9 @@ lineBlock = do   guardEnabled Ext_line_blocks   try $ do+    pos <- getPosition     lines' <- lineBlockLines >>=-              mapM (parseFromString' (trimInlinesF <$> inlines))+              mapM (parseFromString' (setPosition pos >> (trimInlinesF <$> inlines)))     return $ B.lineBlock <$> sequence lines'  --@@ -1311,8 +1317,9 @@ tableLine :: PandocMonad m           => [Int]           -> MarkdownParser m (F [Blocks])-tableLine indices = rawTableLine indices >>=-  fmap sequence . mapM (parseFromString' (mconcat <$> many plain))+tableLine indices = do+  raw <- rawTableLine indices+  sequence <$> mapM (parseFromString' (mconcat <$> many plain)) raw  -- Parse a multiline table row and return a list of blocks (columns). multilineRow :: PandocMonad m@@ -1397,7 +1404,7 @@                     then []                     else map (T.unlines . map trim) rawHeadsList   heads <- fmap sequence $-            mapM (parseFromString' (mconcat <$> many plain).trim) rawHeads+            mapM (parseFromString' (mconcat <$> many plain) . trim) rawHeads   return (fmap (:[]) heads, aligns, indices')  -- Parse a grid table:  starts with row of '-' on top, then header@@ -1533,7 +1540,7 @@   return $ do     caption' <- caption     (TableComponents _attr _capt colspecs th tb tf) <- tableComponents-    return $ B.tableWith attr+    return $ compactifyTable $ B.tableWith attr                 (B.simpleCaption $ B.plain caption') colspecs th tb tf  --@@ -1647,8 +1654,8 @@   skipSpaces   result <- trim . T.concat         <$> manyTill-              (   many1Char (noneOf "`\n")-              <|> many1Char (char '`')+              (   takeWhile1P (\c -> c /= '`' && c /= '\n')+              <|> takeWhile1P (== '`')               <|> (char '\n'                     >> notFollowedBy (inList >> listStart)                     >> notFollowedBy' blankline@@ -1683,7 +1690,7 @@   guardDisabled Ext_intraword_underscores     <|> guard (c == '*')     <|> (guard =<< notAfterString)-  cs <- many1Char (char c)+  cs <- takeWhile1P (== c)   (return (B.str cs) <>) <$> whitespace     <|>         case T.length cs of@@ -1783,7 +1790,7 @@         mmdShortSubscript = try $ do           guardEnabled Ext_short_subsuperscripts           char '~'-          result <- T.pack <$> many1 alphaNum+          result <- takeWhile1P isAlphaNum           return $ return $ B.str result  whitespace :: PandocMonad m => MarkdownParser m (F Inlines)@@ -1798,7 +1805,7 @@ str :: PandocMonad m => MarkdownParser m (F Inlines) str = do   !result <- mconcat <$> many1-             ( T.pack <$> (many1 alphaNum)+             ( takeWhile1P isAlphaNum               <|> "." <$ try (char '.' <* notFollowedBy (char '.')) )   updateLastStrPos   (do guardEnabled Ext_smart@@ -1862,7 +1869,8 @@         try parenthesizedChars           <|> (notFollowedBy (oneOf "\n\r )") >> litChar)           <|> (lookAhead (oneOf "\n\r") >> notFollowedBy linkTitle' >> litChar)-          <|> try (many1Char spaceChar <* notFollowedBy (oneOf "\"')"))+          <|> try (takeWhile1P (\x -> x == ' ' || x == '\t')+                    <* notFollowedBy (oneOf "\"')"))   let sourceURL = T.unwords . T.words . T.concat <$> many urlChunk   src <- try (litBetween '<' '>') <|> try base64DataURI <|> sourceURL   tit <- option "" linkTitle'@@ -1872,13 +1880,21 @@  base64DataURI :: PandocMonad m => ParsecT Sources s m Text base64DataURI = do-  Sources ((pos, txt):rest) <- getInput-  let r = A.parse (fst <$> A.match pBase64DataURI) txt-  case r of-    A.Done remaining consumed -> do-      let pos' = incSourceColumn pos (T.length consumed)-      setInput $ Sources ((pos', remaining):rest)-      return consumed+  inp <- getInput+  case inp of+    Sources ((pos, txt):rest) ->+      -- feed mempty to force a result if attoparsec returns Partial:+      case A.feed (A.parse (fst <$> A.match pBase64DataURI) txt) mempty of+        A.Done remaining consumed -> do+          -- keep the chunk's position unchanged: chunk positions are+          -- invariant (see uncons in T.P.Sources), and withRaw's+          -- sourcesDifference relies on this.  Instead, advance parsec's+          -- own position (a data URI cannot contain newlines):+          setInput $ Sources ((pos, remaining):rest)+          curPos <- getPosition+          setPosition $ incSourceColumn curPos (T.length consumed)+          return consumed+        _ -> mzero     _ -> mzero  linkTitle :: PandocMonad m => MarkdownParser m Text@@ -2021,6 +2037,20 @@ bareURL = do   guardEnabled Ext_autolink_bare_uris   getState >>= guard . stateAllowLinks+  -- Fast rejection: a bare URI must contain ':' (after the scheme) and+  -- an email address '@', in both cases before any whitespace, since+  -- neither can contain whitespace.  So if the whitespace-delimited+  -- token ahead contains neither ':' nor '@', both parsers must fail.+  -- (If the token extends beyond the current input chunk, we skip the+  -- check and just try the parsers.)+  inp <- getInput+  case unSources inp of+    (_,t):_ ->+      case T.find (\c -> isSpace c || c == ':' || c == '@') t of+        Just ':' -> return ()+        Just '@' -> return ()+        _ -> mzero+    [] -> return ()   try $ do     (cls, (orig, src)) <- (("uri",) <$> uri) <|> (("email",) <$> emailAddress)     notFollowedBy $ try $ spaces >> htmlTag (~== TagClose ("a" :: Text))@@ -2050,7 +2080,9 @@       isFragment = T.take 1 path == "#"       path' = T.unpack path       isAbsolutePath = Posix.isAbsolute path' || Windows.isAbsolute path'-   in if T.null path || isFragment || isAbsolutePath || isURI path+   in if T.null path || isFragment || isAbsolutePath || isURI (T.unpack path)+         -- note: we use Network.URI.isURI instead of T.P.URI.isURI+         -- because it doesn't whitelist schemes; see #11858.          then path          else            case takeDirectory fp of@@ -2121,7 +2153,7 @@ rawConTeXtEnvironment = try $ do   string "\\start"   completion <- inBrackets (letter <|> digit <|> spaceChar)-               <|> many1Char letter+               <|> takeWhile1P isLetter   !contents <- manyTill (rawConTeXtEnvironment <|> countChar 1 anyChar)                        (try $ string "\\stop" >> textStr completion)   return $! "\\start" <> completion <> T.concat contents <> "\\stop" <> completion@@ -2174,7 +2206,9 @@     string ":::"     skipMany (char ':')     skipMany spaceChar-    attribs <- attributes <|> ((\x -> ("",[x],[])) <$> many1Char nonspaceChar)+    attribs <- attributes <|> ((\x -> ("",[x],[])) <$>+                  takeWhile1P (\x -> x /= ' ' && x /= '\t' &&+                                     x /= '\n' && x /= '\r'))     skipMany spaceChar     skipMany (char ':')     blankline@@ -2219,7 +2253,7 @@   guardEnabled Ext_emoji   try $ do     char ':'-    emojikey <- many1Char (alphaNum <|> oneOf "_+-")+    emojikey <- takeWhile1P (\x -> isAlphaNum x || x `elem` ("_+-" :: [Char]))     char ':'     case emojiToInline emojikey of       Just i -> return (return $ B.singleton i)
src/Text/Pandoc/Readers/Mdoc.hs view
@@ -42,7 +42,7 @@ import Text.Parsec (modifyState) import qualified Text.Pandoc.Parsing as P import qualified Data.Foldable as Foldable-import Text.Pandoc.Shared (stringify)+import Text.Pandoc.Shared (stringify, stringifyInlines)  #if !MIN_VERSION_base(4,19,0) unsnoc :: [a] -> Maybe ([a], a)@@ -270,7 +270,7 @@   (Macro m _) <- lookAhead $ macro "Sh" <|> macro "Ss"   txt <- lineEnclosure m id   let lvl = if m == "Sh" then 1 else 2-  when (lvl == 1) $ modifyState $ \s -> s{currentSection = (shToSectionMode . stringify) txt}+  when (lvl == 1) $ modifyState $ \s -> s{currentSection = (shToSectionMode . stringifyInlines) txt}   return $ B.header lvl txt  parseNameSection :: PandocMonad m => MdocParser m Blocks@@ -474,7 +474,7 @@       return $ openDelim <> xform inlines <> closeDelim  codeLikeInline' :: PandocMonad m => T.Text -> T.Text -> MdocParser m Inlines-codeLikeInline' nm cl = simpleInline nm (eliminateEmpty (B.codeWith (cls cl) . stringify))+codeLikeInline' nm cl = simpleInline nm (eliminateEmpty (B.codeWith (cls cl) . stringifyInlines))  codeLikeInline :: PandocMonad m => T.Text -> MdocParser m Inlines codeLikeInline nm = codeLikeInline' nm nm@@ -671,7 +671,7 @@ parseMt :: PandocMonad m => MdocParser m Inlines parseMt = simpleInline "Mt" mailto   where mailto x | null x = B.link ("mailto:~") "" "~"-                 | otherwise = B.link ("mailto:" <> stringify x) "" x+                 | otherwise = B.link ("mailto:" <> stringifyInlines x) "" x  parsePa :: PandocMonad m => MdocParser m Inlines parsePa = simpleInline "Pa" p@@ -713,7 +713,7 @@ parseAr :: PandocMonad m => MdocParser m Inlines parseAr = simpleInline "Ar" ar   where ar x | null x = B.codeWith (cls "variable") "file ..."-             | otherwise = B.codeWith (cls "variable") $ stringify x+             | otherwise = B.codeWith (cls "variable") $ stringifyInlines x   parseCm :: PandocMonad m => MdocParser m Inlines@@ -729,7 +729,7 @@ parseCd = codeLikeInline "Cd"  parseQl :: PandocMonad m => MdocParser m Inlines-parseQl = lineEnclosure "Ql" $ B.codeWith (cls "Ql") . stringify+parseQl = lineEnclosure "Ql" $ B.codeWith (cls "Ql") . stringifyInlines  parseDq :: PandocMonad m => MdocParser m Inlines parseDq = lineEnclosure "Dq" B.doubleQuoted@@ -785,7 +785,7 @@ parseDl :: PandocMonad m => MdocParser m Blocks parseDl = do   inner <- lineEnclosure "Dl" id-  return $ B.codeBlock (stringify inner)+  return $ B.codeBlock (stringifyInlines inner)  parseD1 :: PandocMonad m => MdocParser m Blocks parseD1 = do@@ -801,7 +801,7 @@     (Just nm, x) | null x ->       op <> ok nm <> cl     (_, x) ->-      op <> (ok . stringify) x <> cl+      op <> (ok . stringifyInlines) x <> cl   where     ok = B.codeWith (cls "Nm") @@ -1193,7 +1193,7 @@   eol   lns <- many $ Just . toString <$> (str <|> blank)             <|> Nothing <$ parseSmToggle-            <|> Just . stringify <$> parseInline+            <|> Just . stringifyInlines <$> parseInline             <|> Just "" <$ emptyMacro "Pp"   return $ B.codeBlock (T.unlines (catMaybes lns)) @@ -1218,7 +1218,7 @@   emptyMacro "Ef"   return $ xform ins   where-    code = B.code . stringify+    code = B.code . stringifyInlines  skipListArgument :: (PandocMonad m) => MdocParser m () skipListArgument =@@ -1307,7 +1307,7 @@ referenceField m field = do   macro m   reference <- currentReference <$> getState-  contents <- stringify <$> litsAndDelimsToInlines+  contents <- stringifyInlines <$> litsAndDelimsToInlines   eol   modifyState $ \s -> s{currentReference = M.insertWith (++) field [contents] reference}   return ()
src/Text/Pandoc/Readers/Mdoc/Lex.hs view
@@ -142,7 +142,7 @@ mdocToken = lexComment <|> lexControlLine <|> lexTextLine  lexMacroName :: PandocMonad m => Lexer m T.Text-lexMacroName = many1Char (satisfy isMacroChar)+lexMacroName = takeWhile1P isMacroChar   where     isMacroChar '%' = True     isMacroChar x = isAlphaNum x
src/Text/Pandoc/Readers/MediaWiki.hs view
@@ -37,8 +37,8 @@ import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (tableCaption) import Text.Pandoc.Readers.HTML (htmlTag, isCommentTag, toAttr)-import Text.Pandoc.Shared (formatCode, safeRead, splitTextBy, stringify,-                           stripTrailingNewlines, trim, tshow)+import Text.Pandoc.Shared (formatCode, safeRead, splitTextBy, stringifyInlines,+                           stripTrailingNewlines, trim, tshow, compactifyTable) import Text.Pandoc.XML (fromEntities)  -- | Read mediawiki from an input string and return a Pandoc document.@@ -299,7 +299,8 @@                           else ([], hdr:rows')   let toRow = Row nullAttr       toHeaderRow l = [toRow l | not (null l)]-  return $ B.table (B.simpleCaption $ B.plain caption)+  return $ compactifyTable+         $ B.table (B.simpleCaption $ B.plain caption)                    cellspecs                    (TableHead nullAttr $ toHeaderRow headers)                    [TableBody nullAttr 0 [] $ map toRow rows]@@ -324,7 +325,7 @@   char '='   skipMany spaceChar   v <- (char '"' >> manyTillChar (satisfy (/='\n')) (char '"'))-       <|> many1Char (satisfy $ \c -> not (isSpace c) && c /= '|')+       <|> takeWhile1P (\c -> not (isSpace c) && c /= '|')   return (k,v)  tableStart :: PandocMonad m => MWParser m ()@@ -406,7 +407,8 @@   string "{{"   notFollowedBy (char '{')   lookAhead $ letter <|> digit <|> char ':'-  let chunk = template <|> variable <|> many1Char (noneOf "{}") <|> countChar 1 anyChar+  let chunk = template <|> variable <|> takeWhile1P (\c -> c /= '{' && c /= '}')+                <|> countChar 1 anyChar   contents <- manyTill chunk (try $ string "}}")   return $ "{{" <> T.concat contents <> "}}" @@ -614,7 +616,7 @@       <|> special  str :: PandocMonad m => MWParser m Inlines-str = B.str <$> many1Char (noneOf $ specialChars ++ spaceChars)+str = B.str <$> takeWhile1P (`notElem` (specialChars ++ spaceChars))  math :: PandocMonad m => MWParser m Inlines math = (B.displayMath . trim <$> try (many1 (char ':') >> textInTags "math"))@@ -711,9 +713,9 @@ image = try $ do   sym "[["   imageIdentifier-  fname <- addUnderscores <$> many1Char (noneOf "|]")+  fname <- addUnderscores <$> takeWhile1P (\c -> c /= '|' && c /= ']')   _ <- many imageOption-  dims <- try (char '|' *> sepBy (manyChar digit) (char 'x') <* string "px")+  dims <- try (char '|' *> sepBy (takeWhileP isDigit) (char 'x') <* string "px")           <|> return []   _ <- many imageOption   let kvs = case dims of@@ -723,7 +725,7 @@   let attr = ("", [], kvs)   caption <-   (B.str fname <$ sym "]]")            <|> try (char '|' *> (mconcat <$> manyTill inline (sym "]]")))-  return $ B.imageWith attr fname (stringify caption) caption+  return $ B.imageWith attr fname (stringifyInlines caption) caption  imageOption :: PandocMonad m => MWParser m Text imageOption = try $ char '|' *> opt@@ -744,7 +746,7 @@ internalLink :: PandocMonad m => MWParser m Inlines internalLink = try $ do   sym "[["-  pagename <- T.unwords . T.words <$> manyChar (noneOf "|]")+  pagename <- T.unwords . T.words <$> takeWhileP (\c -> c /= '|' && c /= ']')   label <- option (B.text pagename) $ char '|' *>              (  (mconcat <$> many1 (notFollowedBy (char ']') *> inline))              -- the "pipe trick"@@ -752,8 +754,8 @@              <|> return (B.text $ T.drop 1 $ T.dropWhile (/=':') pagename) )   sym "]]"   -- see #8525:-  linktrail <- B.text <$> manyChar (satisfy (\c -> isLetter c && not (isCJK c)))-  let link = B.linkWith (mempty, ["wikilink"], mempty) (addUnderscores pagename) (stringify label) (label <> linktrail)+  linktrail <- B.text <$> takeWhileP (\c -> isLetter c && not (isCJK c))+  let link = B.linkWith (mempty, ["wikilink"], mempty) (addUnderscores pagename) (stringifyInlines label) (label <> linktrail)   if "Category:" `T.isPrefixOf` pagename      then do        updateState $ \st -> st{ mwCategoryLinks = link : mwCategoryLinks st }
src/Text/Pandoc/Readers/Muse.hs view
@@ -22,6 +22,7 @@ import Control.Monad.Reader import Control.Monad.Except (throwError) import Data.Bifunctor+import Data.Char (isAlphaNum, isDigit, isLetter) import Data.Default import Data.List (transpose) import qualified Data.Map as M@@ -36,7 +37,7 @@ import Text.Pandoc.Logging import Text.Pandoc.Options import Text.Pandoc.Parsing-import Text.Pandoc.Shared (trimr, tshow)+import Text.Pandoc.Shared (trimr, tshow, compactifyTable)  -- | Read Muse from an input string and return a Pandoc document. readMuse :: (PandocMonad m, ToSources a)@@ -170,7 +171,7 @@   where     attr = try $ (,)       <$  many1 spaceChar-      <*> many1Char (noneOf "=\n")+      <*> takeWhile1P (\c -> c /= '=' && c /= '\n')       <*  string "=\""       <*> manyTillChar (noneOf "\"") (char '"') @@ -198,7 +199,7 @@  -- While not documented, Emacs Muse allows "-" in directive name parseDirectiveKey :: PandocMonad m => MuseParser m Text-parseDirectiveKey = char '#' *> manyChar (letter <|> char '-')+parseDirectiveKey = char '#' *> takeWhileP (\c -> isLetter c || c == '-')  parseEmacsDirective :: PandocMonad m => MuseParser m (Text, F Inlines) parseEmacsDirective = (,)@@ -645,6 +646,7 @@  museToPandocTable :: MuseTable -> Blocks museToPandocTable (MuseTable caption headers body footers) =+  compactifyTable $   B.table (B.simpleCaption $ B.plain caption)           attrs           (TableHead nullAttr $ toHeaderRow headRow)@@ -723,10 +725,29 @@ tableParseRow :: PandocMonad m               => Int -- ^ Number of separator characters               -> MuseParser m (F [Blocks])-tableParseRow n = try $ sequence <$> tableCells+tableParseRow n = try $ do+  -- A table row must contain a cell separator (whitespace followed by+  -- pipes), which cannot span lines.  Scanning the raw line for one+  -- before parsing cells avoids expensive inline parsing (that would+  -- fail and be discarded) of every line this parser is tried on.+  lineMayBeRow <- rawLineContainsSeparator+  guard lineMayBeRow+  sequence <$> tableCells   where tableCells = (:) <$> tableCell sep <*> (tableCells <|> fmap pure (tableCell eol))         tableCell p = try $ fmap B.plain . trimInlinesF . mconcat <$> manyTill inline' p         sep = try $ many1 spaceChar *> count n (char '|') *> lookAhead (void (many1 spaceChar) <|> void eol)+        pipes = T.replicate n "|"+        rawLineContainsSeparator = do+          Sources inps <- getInput+          return $ case inps of+            (_,t):rest ->+              case T.break (== '\n') t of+                (this, remainder)+                  | T.null remainder, not (null rest) ->+                      True -- line may span input chunks; don't reject+                  | otherwise -> (" " <> pipes) `T.isInfixOf` this ||+                                 ("\t" <> pipes) `T.isInfixOf` this+            [] -> False  -- | Parse a table header row. tableParseHeader :: PandocMonad m => MuseParser m (F MuseTableElement)@@ -751,6 +772,7 @@  inline' :: PandocMonad m => MuseParser m (F Inlines) inline' = whitespace+      <|> str -- tried early: all other alternatives start with non-alphanumerics       <|> br       <|> anchor       <|> footnote@@ -773,7 +795,6 @@       <|> codeTag       <|> mathTag       <|> inlineLiteralTag-      <|> str       <|> asterisks       <|> symbol       <?> "inline"@@ -790,7 +811,7 @@   <$  firstColumn   <*  char '#'   <*> letter-  <*> manyChar (letter <|> digit <|> char '-')+  <*> takeWhileP (\c -> isLetter c || isDigit c || c == '-')  anchor :: PandocMonad m => MuseParser m (F Inlines) anchor = try $ do@@ -931,13 +952,13 @@   <*> manyTillChar anyChar (closeTag "literal")  str :: PandocMonad m => MuseParser m (F Inlines)-str = return . B.str <$> many1Char alphaNum <* updateLastStrPos+str = return . B.str <$> takeWhile1P isAlphaNum <* updateLastStrPos  -- | Consume asterisks that were not used as emphasis opening. -- This prevents series of asterisks from being split into -- literal asterisk and emphasis opening. asterisks :: PandocMonad m => MuseParser m (F Inlines)-asterisks = pure . B.str <$> many1Char (char '*')+asterisks = pure . B.str <$> takeWhile1P (== '*')  symbol :: PandocMonad m => MuseParser m (F Inlines) symbol = pure . B.str . T.singleton <$> nonspaceChar@@ -986,6 +1007,6 @@           return (ext, width, align)         imageAttrs = (,)           <$  many1 spaceChar-          <*> optionMaybe (many1Char digit)+          <*> optionMaybe (takeWhile1P isDigit)           <*  many spaceChar           <*> optionMaybe (oneOf "rlf")
src/Text/Pandoc/Readers/ODT.hs view
@@ -103,7 +103,7 @@   let media = filteredFilesFromArchive archive filePathIsODTMedia   let startState = readerState styles media   either (\_ -> Left $ PandocParseError "Could not convert opendocument") Right-    (runConverter' read_body startState contentElem)+    (runConverter read_body startState contentElem)   --
− src/Text/Pandoc/Readers/ODT/Arrows/State.hs
@@ -1,153 +0,0 @@-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE TupleSections     #-}-{- |-   Module      : Text.Pandoc.Readers.ODT.Arrows.State-   Copyright   : Copyright (C) 2015 Martin Linnemann-   License     : GNU GPL, version 2 or above--   Maintainer  : Martin Linnemann <theCodingMarlin@googlemail.com>-   Stability   : alpha-   Portability : portable--An arrow that transports a state. It is in essence a more powerful version of-the standard state monad. As it is such a simple extension, there are-other version out there that do exactly the same.-The implementation is duplicated, though, to add some useful features.-Most of these might be implemented without access to innards, but it's much-faster and easier to implement this way.--}--module Text.Pandoc.Readers.ODT.Arrows.State-  ( ArrowState(..)-  , withState-  , modifyState-  , ignoringState-  , fromState-  , extractFromState-  , tryModifyState-  , withSubStateF-  , withSubStateF'-  , foldS-  , iterateS-  , iterateSL-  , iterateS'-  ) where--import qualified Data.List as L-import Control.Arrow-import qualified Control.Category as Cat-import Control.Monad-import Text.Pandoc.Readers.ODT.Arrows.Utils-import Text.Pandoc.Readers.ODT.Generic.Fallible---newtype ArrowState state a b = ArrowState-  { runArrowState :: (state, a) -> (state, b) }---- | Constructor-withState           :: (state -> a -> (state, b)) -> ArrowState state a b-withState            = ArrowState . uncurry---- | Constructor-modifyState         :: (state      ->  state    ) -> ArrowState state a a-modifyState          = ArrowState . first---- | Constructor-ignoringState       :: (         a ->         b ) -> ArrowState state a b-ignoringState        = ArrowState . second---- | Constructor-fromState           :: (state      -> (state, b)) -> ArrowState state a b-fromState            = ArrowState . (.fst)---- | Constructor-extractFromState    :: (state      ->         b ) -> ArrowState state x b-extractFromState   f = ArrowState $ \(state,_) -> (state, f state)---- | Constructor-tryModifyState      :: (state ->  Either f state)-                    -> ArrowState state a (Either f a)-tryModifyState     f = ArrowState $ \(state,a)-                                  -> (state,).Left ||| (,Right a) $ f state--instance Cat.Category (ArrowState s) where-  id                = ArrowState id-  arrow2 . arrow1   = ArrowState $ runArrowState arrow2 . runArrowState arrow1--instance Arrow (ArrowState state) where-  arr               = ignoringState-  first  a          = ArrowState $ \(s,(aF,aS))-                                    -> second (,aS) $ runArrowState a (s,aF)-  second a          = ArrowState $ \(s,(aF,aS))-                                    -> second (aF,) $ runArrowState a (s,aS)--instance ArrowChoice (ArrowState state) where-  left   a          = ArrowState $ \(s,e) -> case e of-                                 Left  l -> second Left  $ runArrowState a (s,l)-                                 Right r -> (s, Right r)-  right  a          = ArrowState $ \(s,e) -> case e of-                                 Left  l -> (s, Left l)-                                 Right r -> second Right $ runArrowState a (s,r)--instance ArrowApply (ArrowState state) where-   app             = ArrowState $ \(s, (f,b)) -> runArrowState f (s,b)---- | Switches the type of the state temporarily.--- Drops the intermediate result state, behaving like a fallible--- identity arrow, save for side effects in the state.-withSubStateF  :: ArrowState s  x (Either f s')-               -> ArrowState s' s (Either f s )-               -> ArrowState s  x (Either f x )-withSubStateF  unlift a = keepingTheValue (withSubStateF' unlift a)-                          >>^ spreadChoice-                          >>^ fmap fst---- | Switches the type of the state temporarily.--- Returns the resulting sub-state.-withSubStateF' :: ArrowState s  x (Either f s')-               -> ArrowState s' s (Either f s )-               -> ArrowState s  x (Either f s')-withSubStateF' unlift a = ArrowState go-  where go p@(s,_) = tryRunning unlift-                                ( tryRunning a (second Right) )-                                p-          where tryRunning a' b v = case runArrowState a' v of-                                      (_ , Left  f) -> (s, Left f)-                                      (x , Right y) -> b (y,x)---- | Fold a state arrow through something 'Foldable'. Collect the results--- in a 'Monoid'.--- Intermediate form of a fold between one with "only" a 'Monoid'--- and one with any function.-foldS :: (Foldable f, Monoid m) => ArrowState s x m -> ArrowState s (f x) m-foldS a = ArrowState $ \(s,f) -> foldr a' (s,mempty) f-  where a' x (s',m) = second (mappend m)  $ runArrowState a (s',x)---- | Fold a state arrow through something 'Foldable'. Collect the results in a--- 'MonadPlus'.-iterateS :: (Foldable f, MonadPlus m)-         => ArrowState s    x     y-         -> ArrowState s (f x) (m y)-iterateS a = ArrowState $ \(s,f) -> foldr a' (s,mzero) f-  where a' x (s',m) = second (mplus m.return) $ runArrowState a (s',x)---- | Fold a state arrow through something 'Foldable'. Collect the results in a--- 'MonadPlus'.-iterateSL :: (Foldable f, MonadPlus m)-          => ArrowState s    x     y-          -> ArrowState s (f x) (m y)-iterateSL a = ArrowState $ \(s,f) -> L.foldl' a' (s,mzero) f-  where a' (s',m) x = second (mplus m.return) $ runArrowState a (s',x)----- | Fold a fallible state arrow through something 'Foldable'.--- Collect the results in a 'MonadPlus'.--- If the iteration fails, the state will be reset to the initial one.-iterateS' :: (Foldable f, MonadPlus m)-          => ArrowState s    x  (Either e    y )-          -> ArrowState s (f x) (Either e (m y))-iterateS' a = ArrowState $ \(s,f) -> foldr (a' s) (s,Right mzero) f-  where a' s x (s',Right m) = case runArrowState a (s',x) of-                                (s'',Right m') -> (s'',Right $ mplus m $ return m')-                                (_  ,Left  e ) -> (s  ,Left  e )-        a' _ _   e          = e
− src/Text/Pandoc/Readers/ODT/Arrows/Utils.hs
@@ -1,241 +0,0 @@-{- |-   Module      : Text.Pandoc.Readers.ODT.Arrows.Utils-   Copyright   : Copyright (C) 2015 Martin Linnemann-   License     : GNU GPL, version 2 or above--   Maintainer  : Martin Linnemann <theCodingMarlin@googlemail.com>-   Stability   : alpha-   Portability : portable--Utility functions for Arrows (Kleisli monads).--Some general notes on notation:--* "^" is meant to stand for a pure function that is lifted into an arrow-based on its usage for that purpose in "Control.Arrow".-* "?" is meant to stand for the usage of a 'FallibleArrow' or a pure function-with an equivalent return value.-* "_" stands for the dropping of a value.--}---- We export everything-module Text.Pandoc.Readers.ODT.Arrows.Utils-  ( and2-  , and3-  , and4-  , and5-  , and6-  , liftA2-  , liftA3-  , liftA4-  , liftA5-  , liftA6-  , liftA-  , duplicate-  , (>>%)-  , keepingTheValue-  , (^|||)-  , (|||^)-  , (^|||^)-  , (^&&&)-  , (&&&^)-  , choiceToMaybe-  , maybeToChoice-  , returnV-  , FallibleArrow-  , liftAsSuccess-  , (>>?)-  , (>>?^)-  , (>>?^?)-  , (^>>?)-  , (>>?!)-  , (>>?%)-  , (>>?%?)-  , ifFailedDo-  ) where--import Prelude hiding (Applicative(..))-import Control.Arrow-import Control.Monad (join)--import Text.Pandoc.Readers.ODT.Generic.Fallible-import Text.Pandoc.Readers.ODT.Generic.Utils--and2 :: (Arrow a) => a b c -> a b c' -> a b (c,c')-and2 = (&&&)--and3 :: (Arrow a)-     => a b c0->a b c1->a b c2-     -> a b (c0,c1,c2               )-and4 :: (Arrow a)-     => a b c0->a b c1->a b c2->a b c3-     -> a b (c0,c1,c2,c3            )-and5 :: (Arrow a)-     => a b c0->a b c1->a b c2->a b c3->a b c4-     -> a b (c0,c1,c2,c3,c4         )-and6 :: (Arrow a)-     => a b c0->a b c1->a b c2->a b c3->a b c4->a b c5-     -> a b (c0,c1,c2,c3,c4,c5      )--and3 a b c           = and2 a b &&& c-                       >>^ \((z,y          ) , x) -> (z,y,x          )-and4 a b c d         = and3 a b c &&& d-                       >>^ \((z,y,x        ) , w) -> (z,y,x,w        )-and5 a b c d e       = and4 a b c d &&& e-                       >>^ \((z,y,x,w      ) , v) -> (z,y,x,w,v      )-and6 a b c d e f     = and5 a b c d e &&& f-                       >>^ \((z,y,x,w,v    ) , u) -> (z,y,x,w,v,u    )--liftA2 :: (Arrow a) => (x -> y -> z) -> a b x -> a b y -> a b z-liftA2 f a b = a &&& b >>^ uncurry f--liftA3 :: (Arrow a) => (z->y->x                -> r)-                    -> a b z->a b y->a b x-                    -> a b r-liftA4 :: (Arrow a) => (z->y->x->w             -> r)-                    -> a b z->a b y->a b x->a b w-                    -> a b r-liftA5 :: (Arrow a) => (z->y->x->w->v          -> r)-                    -> a b z->a b y->a b x->a b w->a b v-                    -> a b r-liftA6 :: (Arrow a) => (z->y->x->w->v->u       -> r)-                    -> a b z->a b y->a b x->a b w->a b v->a b u-                    -> a b r--liftA3 fun a b c           = and3 a b c           >>^ uncurry3 fun-liftA4 fun a b c d         = and4 a b c d         >>^ uncurry4 fun-liftA5 fun a b c d e       = and5 a b c d e       >>^ uncurry5 fun-liftA6 fun a b c d e f     = and6 a b c d e f     >>^ uncurry6 fun--liftA :: (Arrow a) => (y -> z) -> a b y -> a b z-liftA  fun a = a >>^ fun----- | Duplicate a value to subsequently feed it into different arrows.--- Can almost always be replaced with '(&&&)', 'keepingTheValue',--- or even '(|||)'.--- Equivalent to--- > returnA &&& returnA-duplicate :: (Arrow a) => a b (b,b)-duplicate = arr $ join (,)---- | Applies a function to the uncurried result-pair of an arrow-application.--- (The %-symbol was chosen to evoke an association with pairs.)-(>>%) :: (Arrow a) => a x (b,c) -> (b -> c -> d) -> a x d-a >>% f = a >>^ uncurry f--infixr 2 >>%----- | Duplicate a value and apply an arrow to the second instance.--- Equivalent to--- > \a -> duplicate >>> second a--- or--- > \a -> returnA &&& a-keepingTheValue :: (Arrow a) => a b c -> a b (b,c)-keepingTheValue a = returnA &&& a--( ^|||  ) :: (ArrowChoice a) => (b -> d) ->  a c d   -> a (Either b c) d-(  |||^ ) :: (ArrowChoice a) =>  a b d   -> (c -> d) -> a (Either b c) d-( ^|||^ ) :: (ArrowChoice a) => (b -> d) -> (c -> d) -> a (Either b c) d--l ^|||  r  = arr l |||     r-l  |||^ r  =     l ||| arr r-l ^|||^ r  = arr l ||| arr r--infixr 2 ^||| ,  |||^, ^|||^--( ^&&&  ) :: (Arrow a) => (b -> c) ->  a b c'   -> a b (c,c')-(  &&&^ ) :: (Arrow a) =>  a b c   -> (b -> c') -> a b (c,c')--l ^&&&  r = arr l &&&     r-l  &&&^ r =     l &&& arr r--infixr 3 ^&&&, &&&^----- | Converts @Right a@ into @Just a@ and @Left _@ into @Nothing@.-choiceToMaybe :: (ArrowChoice a) => a (Either l r) (Maybe r)-choiceToMaybe = arr eitherToMaybe---- | Converts @Nothing@ into @Left ()@ and @Just a@ into @Right a@.-maybeToChoice :: (ArrowChoice a) => a (Maybe b) (Fallible b)-maybeToChoice = arr maybeToEither---- | Lifts a constant value into an arrow-returnV :: (Arrow a) => c -> a x c-returnV = arr.const---- | Defines Left as failure, Right as success-type FallibleArrow a input failure success = a input (Either failure success)-----liftAsSuccess     :: (ArrowChoice a)-                  => a x success-                  -> FallibleArrow a x failure success-liftAsSuccess a   = a >>^ Right---- | Execute the second arrow if the first succeeds-(>>?) :: (ArrowChoice a)-            => FallibleArrow a x       failure success-            -> FallibleArrow a success failure success'-            -> FallibleArrow a x       failure success'-a >>? b = a >>> Left ^||| b---- | Execute the lifted second arrow if the first succeeds-(>>?^) :: (ArrowChoice a)-            => FallibleArrow a x       failure success-            -> (success                     -> success')-            -> FallibleArrow a x       failure success'-a >>?^ f = a >>^ Left ^|||^ Right . f---- | Execute the lifted second arrow if the first succeeds-(>>?^?) :: (ArrowChoice a)-            => FallibleArrow a x       failure success-            -> (success      -> Either failure success')-            -> FallibleArrow a x       failure success'-a >>?^? b = a >>> Left ^|||^ b---- | Execute the second arrow if the lifted first arrow succeeds-(^>>?) :: (ArrowChoice a)-            => (x            -> Either failure success)-            -> FallibleArrow a success failure success'-            -> FallibleArrow a x       failure success'-a ^>>? b = a ^>> Left ^||| b---- | Execute the second, non-fallible arrow if the first arrow succeeds-(>>?!) :: (ArrowChoice a)-            => FallibleArrow a x       failure success-            ->               a success         success'-            -> FallibleArrow a x       failure success'-a >>?! f = a >>> right f------(>>?%) :: (ArrowChoice a)-          => FallibleArrow a x f (b,b')-          -> (b -> b' -> c)-          -> FallibleArrow a x f c-a >>?% f = a >>?^ uncurry f-------(>>?%?) :: (ArrowChoice a)-           => FallibleArrow a x f (b,b')-           -> (b -> b' -> Either f c)-           -> FallibleArrow a x f c-a >>?%? f = a >>?^? uncurry f--infixr 1  >>?,  >>?^,  >>?^?-infixr 1 ^>>?, >>?!-infixr 1 >>?%, >>?%?---- | An arrow version of a short-circuit (<|>)-ifFailedDo :: (ArrowChoice a)-           => FallibleArrow a x f y-           -> FallibleArrow a x f y-           -> FallibleArrow a x f y-ifFailedDo a b = keepingTheValue a >>> repackage ^>> (b |||^ Right)-  where repackage (x , Left  _) = Left  x-        repackage (_ , Right y) = Right y--infixr 1 `ifFailedDo`
− src/Text/Pandoc/Readers/ODT/Base.hs
@@ -1,26 +0,0 @@-{- |-   Module      : Text.Pandoc.Readers.ODT.Base-   Copyright   : Copyright (C) 2015 Martin Linnemann-   License     : GNU GPL, version 2 or above--   Maintainer  : Martin Linnemann <theCodingMarlin@googlemail.com>-   Stability   : alpha-   Portability : portable--Core types of the odt reader.--}--module Text.Pandoc.Readers.ODT.Base-  ( ODTConverterState-  , XMLReader-  , XMLReaderSafe-  ) where--import Text.Pandoc.Readers.ODT.Generic.XMLConverter-import Text.Pandoc.Readers.ODT.Namespaces--type ODTConverterState s = XMLConverterState Namespace s--type XMLReader     s a b = FallibleXMLConverter Namespace s a b--type XMLReaderSafe s a b =         XMLConverter Namespace s a b
src/Text/Pandoc/Readers/ODT/ContentReader.hs view
@@ -1,10 +1,7 @@-{-# LANGUAGE Arrows            #-}-{-# LANGUAGE CPP               #-} {-# LANGUAGE DeriveFoldable    #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE PatternGuards     #-} {-# LANGUAGE RecordWildCards   #-}-{-# LANGUAGE TupleSections     #-} {-# LANGUAGE ViewPatterns      #-} {-# LANGUAGE OverloadedStrings #-} {- |@@ -24,9 +21,7 @@ , read_body ) where -import Prelude hiding (Applicative(..))-import Control.Applicative hiding (liftA, liftA2, liftA3)-import Control.Arrow+import Control.Applicative ((<|>)) import Control.Monad ((<=<))  import qualified Data.ByteString.Lazy as B@@ -48,14 +43,10 @@  import Text.Pandoc.Readers.Docx.Combine (combineBlocks) -import Text.Pandoc.Readers.ODT.Base import Text.Pandoc.Readers.ODT.Namespaces import Text.Pandoc.Readers.ODT.StyleReader -import Text.Pandoc.Readers.ODT.Arrows.State (foldS)-import Text.Pandoc.Readers.ODT.Arrows.Utils-import Text.Pandoc.Readers.ODT.Generic.Fallible-import Text.Pandoc.Readers.ODT.Generic.Utils+import Text.Pandoc.Readers.ODT.Generic.Utils (findBy) import Text.Pandoc.Readers.ODT.Generic.XMLConverter  import Network.URI (parseRelativeReference, URI(uriPath))@@ -125,13 +116,6 @@ shiftListLevel diff = modifyListLevel (+ diff)  ---swapCurrentListStyle :: Maybe ListStyle -> ReaderState-                     -> (ReaderState, Maybe ListStyle)-swapCurrentListStyle mListStyle state = ( state { currentListStyle = mListStyle }-                                        ,  currentListStyle state-                                        )---- lookupPrettyAnchor :: Anchor -> ReaderState -> Maybe Anchor lookupPrettyAnchor anchor ReaderState{..} = M.lookup anchor bookmarkAnchors @@ -158,85 +142,57 @@ -- Reader type and associated tools -------------------------------------------------------------------------------- -type ODTReader      a b = XMLReader     ReaderState a b--type ODTReaderSafe  a b = XMLReaderSafe ReaderState a b+type ODTReader a = XMLConverter Namespace ReaderState a --- | Extract something from the styles-fromStyles :: (a -> Styles -> b) -> ODTReaderSafe a b-fromStyles f =     keepingTheValue-                     (getExtraState >>^ styleSet)-               >>% f+-- | Extract the styles from the reader state+getStyles :: ODTReader Styles+getStyles = styleSet <$> getExtraState  ---getStyleByName :: ODTReader StyleName Style-getStyleByName = fromStyles lookupStyle >>^ maybeToChoice+getStyleByName :: StyleName -> ODTReader Style+getStyleByName name = getStyles >>= fromMaybeF . lookupStyle name  ---findStyleFamily :: ODTReader Style StyleFamily-findStyleFamily = fromStyles getStyleFamily >>^ maybeToChoice- ---lookupListStyle :: ODTReader StyleName ListStyle-lookupListStyle = fromStyles lookupListStyleByName >>^ maybeToChoice+lookupListStyle :: StyleName -> ODTReader ListStyle+lookupListStyle name = getStyles >>= fromMaybeF . lookupListStyleByName name  ---switchCurrentListStyle :: ODTReaderSafe (Maybe ListStyle) (Maybe ListStyle)-switchCurrentListStyle =     keepingTheValue getExtraState-                         >>% swapCurrentListStyle-                         >>> first setExtraState-                         >>^ snd+switchCurrentListStyle :: Maybe ListStyle -> ODTReader (Maybe ListStyle)+switchCurrentListStyle mListStyle = do+  state <- getExtraState+  setExtraState $ state { currentListStyle = mListStyle }+  return $ currentListStyle state  ---pushStyle :: ODTReaderSafe Style Style-pushStyle =     keepingTheValue (-                  (     keepingTheValue getExtraState-                    >>% pushStyle'-                  )-                  >>> setExtraState-                )-            >>^ fst+pushStyle :: Style -> ODTReader ()+pushStyle style = modifyExtraState (pushStyle' style)  ---popStyle :: ODTReaderSafe x x-popStyle =     keepingTheValue (-                     getExtraState-                 >>> arr popStyle'-                 >>> setExtraState-               )-           >>^ fst+popStyle :: ODTReader ()+popStyle = modifyExtraState popStyle'  ---getCurrentListLevel :: ODTReaderSafe a ListLevel-getCurrentListLevel = getExtraState >>^ currentListLevel+getCurrentListLevel :: ODTReader ListLevel+getCurrentListLevel = currentListLevel <$> getExtraState  ---getListContinuationStartCounters :: ODTReaderSafe a (M.Map ListLevel Int)-getListContinuationStartCounters = getExtraState >>^ listContinuationStartCounters-+getPreviousListStartCounter :: ListLevel -> ODTReader Int+getPreviousListStartCounter listLevel =+  M.findWithDefault 0 listLevel . listContinuationStartCounters+    <$> getExtraState  ---getPreviousListStartCounter :: ODTReaderSafe ListLevel Int-getPreviousListStartCounter = proc listLevel -> do-    counts <- getListContinuationStartCounters -< ()-    returnA -< M.findWithDefault 0 listLevel counts+updateMediaWithResource :: (FilePath, B.ByteString) -> ODTReader ()+updateMediaWithResource resource = modifyExtraState (insertMedia' resource)  ---updateMediaWithResource :: ODTReaderSafe (FilePath, B.ByteString) (FilePath, B.ByteString)-updateMediaWithResource = keepingTheValue (-                 (keepingTheValue getExtraState-                  >>% insertMedia'-                  )-                 >>> setExtraState-               )-           >>^ fst--lookupResource :: ODTReaderSafe FilePath (FilePath, B.ByteString)-lookupResource = proc target -> do-    state <- getExtraState -< ()-    case lookup target (getMediaEnv state) of-      Just bs -> returnV (target, bs) -<< ()-      Nothing -> returnV ("", B.empty) -< ()+lookupResource :: FilePath -> ODTReader (FilePath, B.ByteString)+lookupResource target = do+  state <- getExtraState+  case lookup target (getMediaEnv state) of+    Just bs -> return (target, bs)+    Nothing -> return ("", B.empty)  type AnchorPrefix = T.Text @@ -255,77 +211,58 @@ -- | First argument: basis for a new "pretty" anchor if none exists yet -- Second argument: a key ("ugly" anchor) -- Returns: saved "pretty" anchor or created new one-getPrettyAnchor :: ODTReaderSafe (AnchorPrefix, Anchor) Anchor-getPrettyAnchor = proc (baseIdent, uglyAnchor) -> do-  state <- getExtraState -< ()+getPrettyAnchor :: AnchorPrefix -> Anchor -> ODTReader Anchor+getPrettyAnchor baseIdent uglyAnchor = do+  state <- getExtraState   case lookupPrettyAnchor uglyAnchor state of-    Just prettyAnchor -> returnA -< prettyAnchor+    Just prettyAnchor -> return prettyAnchor     Nothing           -> do       let newPretty = uniqueIdentFrom baseIdent (usedAnchors state)-      modifyExtraState (putPrettyAnchor uglyAnchor newPretty) -<< newPretty+      modifyExtraState (putPrettyAnchor uglyAnchor newPretty)+      return newPretty  -- | Input: basis for a new header anchor -- Output: saved new anchor-getHeaderAnchor :: ODTReaderSafe Inlines Anchor-getHeaderAnchor = proc title -> do-  state <- getExtraState -< ()+getHeaderAnchor :: Inlines -> ODTReader Anchor+getHeaderAnchor title = do+  state <- getExtraState   let exts = extensionsFromList [Ext_auto_identifiers]   let anchor = uniqueIdent exts (toList title)                 (Set.fromList $ usedAnchors state)-  modifyExtraState (putPrettyAnchor anchor anchor) -<< anchor+  modifyExtraState (putPrettyAnchor anchor anchor)+  return anchor   -------------------------------------------------------------------------------- -- Working with styles -------------------------------------------------------------------------------- ----readStyleByName :: ODTReader a (StyleName, Style)-readStyleByName =-  findAttr NsText "style-name" >>? keepingTheValue getStyleByName >>^ liftE-  where-    liftE :: (StyleName, Fallible Style) -> Fallible (StyleName, Style)-    liftE (name, Right v) = Right (name, v)-    liftE (_, Left v)     = Left v-----isStyleToTrace :: ODTReader Style Bool-isStyleToTrace = findStyleFamily >>?^ (==FaText)+-- | Read the style referenced by the current element's+-- style-name attribute. Fails if there is no such attribute or if the+-- style cannot be found.+readStyleByName :: ODTReader (StyleName, Style)+readStyleByName = do+  name  <- findAttr NsText "style-name"+  style <- getStyleByName name+  return (name, style)  ---withNewStyle :: ODTReaderSafe x Inlines -> ODTReaderSafe x Inlines-withNewStyle a = proc x -> do-  fStyle <- readStyleByName -< ()+withNewStyle :: ODTReader Inlines -> ODTReader Inlines+withNewStyle reader = do+  fStyle <- tryC readStyleByName   case fStyle of-    Right (styleName, _) | isCodeStyle styleName -> do-      inlines <- a -< x-      arr inlineCode -<< inlines-    Right (_, style) -> do-      mFamily    <- arr styleFamily -< style-      fTextProps <- arr ( maybeToChoice-                        . textProperties-                        . styleProperties-                        )           -< style-      case fTextProps of-        Right textProps -> do-          state        <- getExtraState             -< ()-          let triple = (state, textProps, mFamily)-          modifier     <- arr modifierFromStyleDiff -< triple-          fShouldTrace <- isStyleToTrace            -< style-          case fShouldTrace of-            Right shouldTrace ->-              if shouldTrace-                then do-                  pushStyle      -< style-                  inlines   <- a -< x-                  popStyle       -< ()-                  arr modifier   -<< inlines-                else-    -- In case anything goes wrong-                      a -< x-            Left _ -> a -< x-        Left _     -> a -< x-    Left _         -> a -< x+    Right (styleName, _) | isCodeStyle styleName ->+      inlineCode <$> reader+    Right (_, style)+      | Just textProps <- textProperties (styleProperties style) -> do+          state <- getExtraState+          let mFamily  = styleFamily style+              modifier = modifierFromStyleDiff (state, textProps, mFamily)+          pushStyle style+          inlines <- reader+          popStyle+          return $ modifier inlines+    _ -> reader   where     isCodeStyle :: StyleName -> Bool     isCodeStyle "Source_Text" = True@@ -333,7 +270,7 @@     isCodeStyle _             = False      inlineCode :: Inlines -> Inlines-    inlineCode = code . T.concat . map stringify . toList+    inlineCode = code . stringifyInlines  type PropertyTriple = (ReaderState, TextProperties, Maybe StyleFamily) type InlineModifier = Inlines -> Inlines@@ -342,16 +279,15 @@ -- an instance of 'Inlines' modifierFromStyleDiff :: PropertyTriple -> InlineModifier modifierFromStyleDiff propertyTriple  =-  composition $+  foldr (.) id $   getVPosModifier propertyTriple-  : map (first ($ propertyTriple) >>> ifThen_else ignore)+  : map (\(hasChanged', modifier) ->+           if hasChanged' propertyTriple then modifier else ignore)         [ (hasEmphChanged           , emph      )         , (hasChanged isStrong      , strong    )         , (hasChanged strikethrough , strikeout )         ]   where-    ifThen_else else' (if',then') = if if' then then' else else'-     ignore = id :: InlineModifier      getVPosModifier :: PropertyTriple -> InlineModifier@@ -367,9 +303,9 @@     getVPosModifier' ( _      ,  _        ) = ignore      hasEmphChanged :: PropertyTriple -> Bool-    hasEmphChanged = swing any [ hasChanged  isEmphasised-                               , hasChanged  underline-                               ]+    hasEmphChanged triple = any ($ triple) [ hasChanged  isEmphasised+                                           , hasChanged  underline+                                           ]      hasChanged property triple@(_, property -> newProperty, _) =         (/= Just newProperty) (lookupPreviousValue property triple)@@ -414,20 +350,18 @@      = False  ---constructPara :: ODTReaderSafe a Blocks -> ODTReaderSafe a Blocks-constructPara reader = proc blocks -> do-  fStyle <- readStyleByName -< blocks+constructPara :: ODTReader Blocks -> ODTReader Blocks+constructPara reader = do+  fStyle <- tryC readStyleByName   case fStyle of-    Left   _    -> reader -< blocks-    Right (styleName, _) | isTableCaptionStyle styleName -> do-      blocks' <- reader   -< blocks-      arr tableCaptionP  -< blocks'+    Left   _    -> reader+    Right (styleName, _) | isTableCaptionStyle styleName ->+      tableCaptionP <$> reader     Right (_, style) -> do-      props <- fromStyles extendedStylePropertyChain -< [style]-      listLevel <- getCurrentListLevel -< ()-      let modifier = getParaModifier listLevel props-      blocks' <- reader   -<  blocks-      arr modifier        -<< blocks'+      styles <- getStyles+      let props = extendedStylePropertyChain [style] styles+      listLevel <- getCurrentListLevel+      getParaModifier listLevel props <$> reader   where     isTableCaptionStyle :: StyleName -> Bool     isTableCaptionStyle "Table" = True@@ -466,78 +400,72 @@ -- Then prepares the state for eventual child lists and constructs the list from -- the results. -- Two main cases are handled: The list may provide its own style or it may--- rely on a parent list's style. I the former case the current style in the+-- rely on a parent list's style. In the former case the current style in the -- state must be switched before and after the call to the child converter -- while in the latter the child converter can be called directly. -- If anything goes wrong, a default ordered-list-constructor is used.-constructList :: ODTReaderSafe x [Blocks] -> ODTReaderSafe x Blocks-constructList reader = proc x -> do-  modifyExtraState (shiftListLevel 1)                                  -< ()-  listLevel                    <- getCurrentListLevel                  -< ()-  listContinuationStartCounter <- getPreviousListStartCounter          -< listLevel-  fStyleName                   <- findAttr NsText "style-name"         -< ()-  fContNumbering               <- findAttr NsText "continue-numbering" -< ()-  listItemCount                <- reader >>^ length                    -< x+constructList :: ODTReader [Blocks] -> ODTReader Blocks+constructList reader = do+  modifyExtraState (shiftListLevel 1)+  listLevel                    <- getCurrentListLevel+  listContinuationStartCounter <- getPreviousListStartCounter listLevel+  fStyleName                   <- tryC (findAttr NsText "style-name")+  fContNumbering               <- tryC (findAttr NsText "continue-numbering") -  let continueNumbering = case fContNumbering of-                            Right "true" -> True-                            _            -> False+  let continueNumbering = fContNumbering == Right "true" -  let startNumForListLevelStyle = listStartingNumber continueNumbering listContinuationStartCounter-  let defaultOrderedListConstructor = constructOrderedList (startNumForListLevelStyle Nothing) listLevel listItemCount+      startNumForListLevelStyle mListLevelStyle+        | continueNumbering      = listContinuationStartCounter+        | isJust mListLevelStyle = listItemStart (fromJust mListLevelStyle)+        | otherwise              = 1 +      constructWith startNum constructor = do+        items <- reader+        modifyExtraState (shiftListLevel (-1))+        modifyExtraState (modifyListContinuationStartCounter listLevel+                           (startNum + length items))+        return $ constructor items++      constructOrderedList =+        let startNum = startNumForListLevelStyle Nothing+        in  constructWith startNum+              (orderedListWith (startNum, DefaultStyle, DefaultDelim))++      constructListWith listLevelStyle =+        let startNum = startNumForListLevelStyle (Just listLevelStyle)+        in  constructWith startNum (getListConstructor listLevelStyle startNum)+   case fStyleName of     Right styleName -> do-      fListStyle <- lookupListStyle -< styleName+      fListStyle <- tryC (lookupListStyle styleName)       case fListStyle of-        Right listStyle -> do-          fListLevelStyle <- arr (uncurry getListLevelStyle) -< (listLevel, listStyle)-          case fListLevelStyle of+        Right listStyle ->+          case getListLevelStyle listLevel listStyle of             Just listLevelStyle -> do-              let startNum = startNumForListLevelStyle $ Just listLevelStyle-              oldListStyle <- switchCurrentListStyle                    -<  Just listStyle-              blocks       <- constructListWith listLevelStyle startNum listLevel listItemCount -<< x-              switchCurrentListStyle                                    -<  oldListStyle-              returnA                                                   -<  blocks-            Nothing             -> defaultOrderedListConstructor -<< x-        Left _                  -> defaultOrderedListConstructor -<< x+              oldListStyle <- switchCurrentListStyle (Just listStyle)+              blocks       <- constructListWith listLevelStyle+              _            <- switchCurrentListStyle oldListStyle+              return blocks+            Nothing             -> constructOrderedList+        Left _                  -> constructOrderedList     Left _ -> do-      state      <- getExtraState        -< ()-      mListStyle <- arr currentListStyle -< state+      mListStyle <- currentListStyle <$> getExtraState       case mListStyle of-        Just listStyle -> do-          fListLevelStyle <- arr (uncurry getListLevelStyle) -< (listLevel, listStyle)-          case fListLevelStyle of-            Just listLevelStyle -> do-              let startNum = startNumForListLevelStyle $ Just listLevelStyle-              constructListWith listLevelStyle startNum listLevel listItemCount -<< x-            Nothing             -> defaultOrderedListConstructor -<< x-        Nothing                 -> defaultOrderedListConstructor -<< x-  where-    listStartingNumber continueNumbering listContinuationStartCounter mListLevelStyle-      | continueNumbering                = listContinuationStartCounter-      | isJust mListLevelStyle           = listItemStart (fromJust mListLevelStyle)-      | otherwise                        = 1-    constructOrderedList startNum listLevel listItemCount =-          reader-      >>> modifyExtraState (shiftListLevel (-1))-      >>> modifyExtraState (modifyListContinuationStartCounter listLevel (startNum + listItemCount))-      >>^ orderedListWith (startNum, DefaultStyle, DefaultDelim)-    constructListWith listLevelStyle startNum listLevel listItemCount =-          reader-      >>> getListConstructor listLevelStyle startNum-      ^>> modifyExtraState (shiftListLevel (-1))-      >>> modifyExtraState (modifyListContinuationStartCounter listLevel (startNum + listItemCount))+        Just listStyle ->+          case getListLevelStyle listLevel listStyle of+            Just listLevelStyle -> constructListWith listLevelStyle+            Nothing             -> constructOrderedList+        Nothing                 -> constructOrderedList  -------------------------------------------------------------------------------- -- Readers -------------------------------------------------------------------------------- -type ElementMatcher result = (Namespace, ElementName, ODTReader result result)+type Matcher result = ElementMatcher Namespace ReaderState result -type InlineMatcher = ElementMatcher Inlines+type InlineMatcher = Matcher Inlines -type BlockMatcher  = ElementMatcher Blocks+type BlockMatcher  = Matcher Blocks  newtype FirstMatch a = FirstMatch (Alt Maybe a)   deriving (Foldable, Monoid, Semigroup)@@ -554,33 +482,14 @@   mempty = CombiningBlocks mempty  ---matchingElement :: (Monoid e)-                => Namespace -> ElementName-                -> ODTReaderSafe  e e-                -> ElementMatcher e-matchingElement ns name reader = (ns, name, asResultAccumulator reader)-  where-   asResultAccumulator :: (ArrowChoice a, Monoid m) => a m m -> a m (Fallible m)-   asResultAccumulator a = liftAsSuccess $ keepingTheValue a >>% mappend-----matchChildContent'   :: (Monoid result)-                     => [ElementMatcher result]-                     ->  ODTReaderSafe a result-matchChildContent' ls = returnV mempty >>> matchContent' ls-----matchSmushedChildBlocks' :: [ElementMatcher CombiningBlocks] ->  ODTReaderSafe a Blocks-matchSmushedChildBlocks' ls = liftA unCombiningBlocks-                              $ returnV mempty-                                >>> matchContent' ls+matchingElement :: Namespace -> ElementName+                -> ODTReader e+                -> Matcher e+matchingElement ns name reader = (ns, name, reader)  ---matchChildContent    :: (Monoid result)-                     => [ElementMatcher result]-                     ->  ODTReaderSafe  (result, XML.Content) result-                     ->  ODTReaderSafe a result-matchChildContent ls fallback = returnV mempty >>> matchContent ls fallback+matchSmushedChildBlocks' :: [Matcher CombiningBlocks] -> ODTReader Blocks+matchSmushedChildBlocks' ls = unCombiningBlocks <$> matchContent' ls  -------------------------------------------- -- Matchers@@ -591,94 +500,83 @@ ----------------------  ----- | Open Document allows several consecutive spaces if they are marked up-read_plain_text :: ODTReaderSafe (Inlines, XML.Content) Inlines-read_plain_text =  fst ^&&& read_plain_text' >>% recover-  where-    -- fallible version-    read_plain_text' :: ODTReader (Inlines, XML.Content) Inlines-    read_plain_text' =      (     second ( arr extractText )-                              >>^ spreadChoice >>?! second text-                            )-                       >>?% mappend-    ---    extractText     :: XML.Content -> Fallible T.Text-    extractText (XML.Text cData) = succeedWith (XML.cdData cData)-    extractText         _        = failEmpty+-- | Open Document allows several consecutive spaces if they are marked up.+-- Text content of the current element is read with this fallback converter.+read_plain_text :: XML.Content -> ODTReader Inlines+read_plain_text (XML.Text cData) = return $ text $ XML.cdData cData+read_plain_text _                = return mempty  read_text_seq :: InlineMatcher read_text_seq  = matchingElement NsText "sequence"-                 $ matchChildContent [] read_plain_text+                 $ matchContent [] read_plain_text   -- specifically. I honor that, although the current implementation of 'mappend' -- for 'Inlines' in "Text.Pandoc.Builder" will collapse them again. -- The rational is to be prepared for future modifications. read_spaces      :: InlineMatcher-read_spaces       = matchingElement NsText "s" (-                          readAttrWithDefault NsText "c" 1 -- how many spaces?-                      >>^ fromList.(`replicate` Space)-                    )+read_spaces       = matchingElement NsText "s" $ do+                      count <- readAttrWithDefault NsText "c" 1 -- how many spaces?+                      return $ fromList (replicate count Space) -- read_line_break  :: InlineMatcher read_line_break   = matchingElement NsText "line-break"-                    $ returnV linebreak+                    $ return linebreak -- read_tab         :: InlineMatcher read_tab          = matchingElement NsText "tab"-                    $ returnV space+                    $ return space -- read_span        :: InlineMatcher read_span         = matchingElement NsText "span"                     $ withNewStyle-                    $ matchChildContent [ read_span-                                        , read_spaces-                                        , read_line_break-                                        , read_tab-                                        , read_link-                                        , read_frame-                                        , read_note-                                        , read_citation-                                        , read_bookmark-                                        , read_bookmark_start-                                        , read_reference_start-                                        , read_bookmark_ref-                                        , read_reference_ref-                                        ] read_plain_text+                    $ matchContent [ read_span+                                   , read_spaces+                                   , read_line_break+                                   , read_tab+                                   , read_link+                                   , read_frame+                                   , read_note+                                   , read_citation+                                   , read_bookmark+                                   , read_bookmark_start+                                   , read_reference_start+                                   , read_bookmark_ref+                                   , read_reference_ref+                                   ] read_plain_text  ---read_paragraph   :: ElementMatcher CombiningBlocks-read_paragraph    = matchingElement NsText "p" $-                      liftA CombiningBlocks $ proc blocks -> do-                        fStyle <- readStyleByName -< blocks-                        case fStyle of-                          Right style | isPreformattedStyle style -> do-                            liftA (codeBlock . stringify) $ matchParagraphContent -< blocks-                          _ ->-                            constructPara $ liftA para $ withNewStyle matchParagraphContent -< blocks-                        where-                          isPreformattedStyle :: (StyleName, Style) -> Bool-                          isPreformattedStyle ("Preformatted_20_Text", _) = True-                          isPreformattedStyle (_, Style { styleParentName = Just "Preformatted_20_Text" }) = True-                          isPreformattedStyle _ = False+read_paragraph   :: Matcher CombiningBlocks+read_paragraph    = matchingElement NsText "p" $ fmap CombiningBlocks $ do+                      fStyle <- tryC readStyleByName+                      case fStyle of+                        Right style | isPreformattedStyle style ->+                          codeBlock . stringifyInlines <$> matchParagraphContent+                        _ ->+                          constructPara (para <$> withNewStyle matchParagraphContent)+                    where+                      isPreformattedStyle :: (StyleName, Style) -> Bool+                      isPreformattedStyle ("Preformatted_20_Text", _) = True+                      isPreformattedStyle (_, Style { styleParentName = Just "Preformatted_20_Text" }) = True+                      isPreformattedStyle _ = False  -matchParagraphContent :: ODTReaderSafe a Inlines-matchParagraphContent = matchChildContent [ read_span-                                          , read_spaces-                                          , read_line_break-                                          , read_tab-                                          , read_link-                                          , read_note-                                          , read_citation-                                          , read_bookmark-                                          , read_bookmark_start-                                          , read_reference_start-                                          , read_bookmark_ref-                                          , read_reference_ref-                                          , read_frame-                                          , read_text_seq-                                          ] read_plain_text+matchParagraphContent :: ODTReader Inlines+matchParagraphContent = matchContent [ read_span+                                     , read_spaces+                                     , read_line_break+                                     , read_tab+                                     , read_link+                                     , read_note+                                     , read_citation+                                     , read_bookmark+                                     , read_bookmark_start+                                     , read_reference_start+                                     , read_bookmark_ref+                                     , read_reference_ref+                                     , read_frame+                                     , read_text_seq+                                     ] read_plain_text   ----------------------@@ -686,72 +584,69 @@ ----------------------  ---read_header      :: ElementMatcher CombiningBlocks-read_header       = matchingElement NsText "h"-                    $  proc blocks -> do-  level    <- ( readAttrWithDefault NsText "outline-level" 1-              ) -< blocks-  children <- ( matchChildContent [ read_span-                                  , read_spaces-                                  , read_line_break-                                  , read_tab-                                  , read_link-                                  , read_note-                                  , read_citation-                                  , read_bookmark-                                  , read_bookmark_start-                                  , read_reference_start-                                  , read_bookmark_ref-                                  , read_reference_ref-                                  , read_frame-                                  ] read_plain_text-              ) -< blocks-  anchor   <- getHeaderAnchor -< children+read_header      :: Matcher CombiningBlocks+read_header       = matchingElement NsText "h" $ do+  level    <- readAttrWithDefault NsText "outline-level" 1+  children <- matchContent [ read_span+                           , read_spaces+                           , read_line_break+                           , read_tab+                           , read_link+                           , read_note+                           , read_citation+                           , read_bookmark+                           , read_bookmark_start+                           , read_reference_start+                           , read_bookmark_ref+                           , read_reference_ref+                           , read_frame+                           ] read_plain_text+  anchor   <- getHeaderAnchor children   let idAttr = (anchor, [], []) -- no classes, no key-value pairs-  arr (CombiningBlocks . uncurry3 headerWith) -< (idAttr, level, children)+  return $ CombiningBlocks $ headerWith idAttr level children  ---------------------- -- Lists ----------------------  ---read_list        :: ElementMatcher CombiningBlocks+read_list        :: Matcher CombiningBlocks read_list         = matchingElement NsText "list"-                    $ liftA CombiningBlocks-                    $ constructList-                    $ matchChildContent' [ read_list_item-                                         , read_list_header-                                         ]+                    $ CombiningBlocks+                    <$> constructList+                        ( matchContent' [ read_list_item+                                        , read_list_header+                                        ] ) ---read_list_item   :: ElementMatcher [Blocks]+read_list_item   :: Matcher [Blocks] read_list_item    = read_list_element "list-item" -read_list_header :: ElementMatcher [Blocks]+read_list_header :: Matcher [Blocks] read_list_header  = read_list_element "list-header" -read_list_element               :: ElementName -> ElementMatcher [Blocks]+read_list_element               :: ElementName -> Matcher [Blocks] read_list_element listElement   = matchingElement NsText listElement-                                  $ liftA (compactify.(:[]))-                                    ( matchSmushedChildBlocks' [ read_paragraph-                                                               , read_header-                                                               , read_list-                                                               , read_section-                                                               ]-                                    )+                                  $ compactify . (:[])+                                  <$> matchSmushedChildBlocks'+                                        [ read_paragraph+                                        , read_header+                                        , read_list+                                        , read_section+                                        ]  ---------------------- -- Sections ---------------------- -read_section :: ElementMatcher CombiningBlocks+read_section :: Matcher CombiningBlocks read_section = matchingElement NsText "section"-                 $ liftA (CombiningBlocks . divWith nullAttr)-                 $ matchSmushedChildBlocks' [ read_paragraph-                                            , read_header-                                            , read_list-                                            , read_table-                                            , read_section-                                            ]+                 $ CombiningBlocks . divWith nullAttr+                 <$> matchSmushedChildBlocks' [ read_paragraph+                                              , read_header+                                              , read_list+                                              , read_table+                                              , read_section+                                              ]   ----------------------@@ -760,19 +655,19 @@  read_link        :: InlineMatcher read_link         = matchingElement NsText "a"-                    $ liftA3 link-                      ( findAttrTextWithDefault NsXLink  "href"  ""-                        >>> arr fixRelativeLink                              )-                      ( findAttrTextWithDefault NsOffice "title" ""          )-                      ( matchChildContent [ read_span-                                          , read_note-                                          , read_citation-                                          , read_bookmark-                                          , read_bookmark_start-                                          , read_reference_start-                                          , read_bookmark_ref-                                          , read_reference_ref-                                          ] read_plain_text                  )+                    $ link+                      <$> (fixRelativeLink+                             <$> findAttrWithDefault NsXLink  "href"  "")+                      <*> findAttrWithDefault NsOffice "title" ""+                      <*> matchContent [ read_span+                                       , read_note+                                       , read_citation+                                       , read_bookmark+                                       , read_bookmark_start+                                       , read_reference_start+                                       , read_bookmark_ref+                                       , read_reference_ref+                                       ] read_plain_text  fixRelativeLink :: T.Text -> T.Text fixRelativeLink uri =@@ -789,8 +684,7 @@  read_note        :: InlineMatcher read_note         = matchingElement NsText "note"-                    $ liftA note-                    $ matchChildContent' [ read_note_body ]+                    $ note <$> matchContent' [ read_note_body ]  read_note_body   :: BlockMatcher read_note_body    = matchingElement NsText "note-body"@@ -802,12 +696,11 @@  read_citation    :: InlineMatcher read_citation     = matchingElement NsText "bibliography-mark"-                    $ liftA2 cite-                      ( liftA2 makeCitation-                        ( findAttrTextWithDefault NsText "identifier" "" )-                        ( readAttrWithDefault NsText "number" 0          )-                      )-                      ( matchChildContent [] read_plain_text             )+                    $ cite+                      <$> ( makeCitation+                              <$> findAttrWithDefault NsText "identifier" ""+                              <*> readAttrWithDefault NsText "number" 0 )+                      <*> matchContent [] read_plain_text   where    makeCitation :: T.Text -> Int -> [Citation]    makeCitation citeId num = [Citation citeId [] [] NormalCitation num 0]@@ -818,16 +711,16 @@ ----------------------  ---read_table        :: ElementMatcher CombiningBlocks+read_table        :: Matcher CombiningBlocks read_table         = matchingElement NsTable "table"-                     $ liftA (CombiningBlocks . table')-                     $ (matchChildContent' [read_table_header]) &&&-                       (matchChildContent' [read_table_row])+                     $ fmap (CombiningBlocks . table')+                     $ (,) <$> matchContent' [read_table_header]+                           <*> matchContent' [read_table_row]  -- | A table without a caption. table' :: ([[Cell]], [[Cell]]) -> Blocks-table' (headers, rows) =-  table emptyCaption (replicate numcols defaults) th [tb] tf+table' (headers, rows) = compactifyTable $+    table emptyCaption (replicate numcols defaults) th [tb] tf   where     defaults = (AlignDefault, ColWidthDefault)     numcols = maximum $ map length $ headers ++ rows@@ -837,27 +730,27 @@     tf = TableFoot nullAttr []  ---read_table_header :: ElementMatcher [[Cell]]+read_table_header :: Matcher [[Cell]] read_table_header = matchingElement NsTable "table-header-rows"-                      $ matchChildContent' [ read_table_row-                                           ]+                      $ matchContent' [ read_table_row+                                      ]  ---read_table_row    :: ElementMatcher [[Cell]]+read_table_row    :: Matcher [[Cell]] read_table_row     = matchingElement NsTable "table-row"-                     $ liftA (:[])-                     $ matchChildContent'  [ read_table_cell-                                           ]+                     $ (:[])+                     <$> matchContent' [ read_table_cell+                                       ]  ---read_table_cell   :: ElementMatcher [Cell]+read_table_cell   :: Matcher [Cell] read_table_cell    = matchingElement NsTable "table-cell"-                     $ liftA3 cell'-                       (readAttrWithDefault NsTable "number-rows-spanned" 1 >>^ RowSpan)-                       (readAttrWithDefault NsTable "number-columns-spanned" 1 >>^ ColSpan)-                     $ matchSmushedChildBlocks' [ read_paragraph-                                                , read_list-                                                ]+                     $ cell'+                       <$> (RowSpan <$> readAttrWithDefault NsTable "number-rows-spanned" 1)+                       <*> (ColSpan <$> readAttrWithDefault NsTable "number-columns-spanned" 1)+                       <*> matchSmushedChildBlocks' [ read_paragraph+                                                    , read_list+                                                    ]   where     cell' rowSpan colSpan blocks = map (cell AlignDefault rowSpan colSpan) $ compactify [blocks] @@ -867,39 +760,39 @@  -- read_frame :: InlineMatcher-read_frame = matchingElement NsDraw "frame"-             $ filterChildrenName' NsDraw (`elem` ["image", "object", "text-box"])-           >>> foldS read_frame_child-           >>> arr fold+read_frame = matchingElement NsDraw "frame" $ do+  children <- filterChildrenName' NsDraw (`elem` ["image", "object", "text-box"])+  fold . mconcat <$> mapM read_frame_child children -read_frame_child :: ODTReaderSafe XML.Element (FirstMatch Inlines)-read_frame_child =-  proc child -> case elName child of-    "image"    -> read_frame_img      -< child-    "object"   -> read_frame_mathml   -< child-    "text-box" -> read_frame_text_box -< child-    _          -> returnV mempty      -< ()+read_frame_child :: XML.Element -> ODTReader (FirstMatch Inlines)+read_frame_child child =+  case elName child of+    "image"    -> read_frame_img      child+    "object"   -> read_frame_mathml   child+    "text-box" -> read_frame_text_box child+    _          -> return mempty -read_frame_img :: ODTReaderSafe XML.Element (FirstMatch Inlines)-read_frame_img =-  proc img -> do-    src <- executeIn (findAttr' NsXLink "href") -< img-    case fold src of-      ""   -> returnV mempty -< ()-      src' -> do-        let exts = extensionsFromList [Ext_auto_identifiers]-            src'' = fixRelativeLink src'-        resource   <- lookupResource                          -< T.unpack src''-        _          <- updateMediaWithResource                 -< resource-        w          <- findAttrText' NsSVG "width"             -< ()-        h          <- findAttrText' NsSVG "height"            -< ()-        titleNodes <- matchChildContent' [ read_frame_title ] -< ()-        alt        <- matchChildContent [] read_plain_text    -< ()-        arr (firstMatch . uncurry4 imageWith)                 -<-          (image_attributes w h, src'', inlineListToIdentifier exts (toList titleNodes), alt)+read_frame_img :: XML.Element -> ODTReader (FirstMatch Inlines)+read_frame_img img = do+  src <- executeIn img (findAttr' NsXLink "href")+  case fold src of+    ""   -> return mempty+    src' -> do+      let exts = extensionsFromList [Ext_auto_identifiers]+          src'' = fixRelativeLink src'+      resource   <- lookupResource (T.unpack src'')+      updateMediaWithResource resource+      w          <- findAttr' NsSVG "width"+      h          <- findAttr' NsSVG "height"+      titleNodes <- matchContent' [ read_frame_title ]+      alt        <- matchContent [] read_plain_text+      return $ firstMatch+             $ imageWith (image_attributes w h) src''+                         (inlineListToIdentifier exts (toList titleNodes))+                         alt  read_frame_title :: InlineMatcher-read_frame_title = matchingElement NsSVG "title" (matchChildContent [] read_plain_text)+read_frame_title = matchingElement NsSVG "title" (matchContent [] read_plain_text)  image_attributes :: Maybe T.Text -> Maybe T.Text -> Attr image_attributes x y =@@ -909,24 +802,23 @@     dim name (Just v) = [(name, v)]     dim _ Nothing     = [] -read_frame_mathml :: ODTReaderSafe XML.Element (FirstMatch Inlines)-read_frame_mathml =-  proc obj -> do-    src <- executeIn (findAttr' NsXLink "href") -< obj-    case fold src of-      ""   -> returnV mempty -< ()-      src' -> do-        let path = T.unpack $-                    fromMaybe src' (T.stripPrefix "./" src') <> "/content.xml"-        (_, mathml) <- lookupResource -< path-        case readMathML (UTF8.toText $ B.toStrict mathml) of-          Left _     -> returnV mempty -< ()-          Right exps -> arr (firstMatch . displayMath . writeTeX) -< exps+read_frame_mathml :: XML.Element -> ODTReader (FirstMatch Inlines)+read_frame_mathml obj = do+  src <- executeIn obj (findAttr' NsXLink "href")+  case fold src of+    ""   -> return mempty+    src' -> do+      let path = T.unpack $+                  fromMaybe src' (T.stripPrefix "./" src') <> "/content.xml"+      (_, mathml) <- lookupResource path+      case readMathML (UTF8.toText $ B.toStrict mathml) of+        Left _     -> return mempty+        Right exps -> return $ firstMatch $ displayMath $ writeTeX exps -read_frame_text_box :: ODTReaderSafe XML.Element (FirstMatch Inlines)-read_frame_text_box = proc box -> do-    paragraphs <- executeIn (matchSmushedChildBlocks' [ read_paragraph ]) -< box-    arr read_img_with_caption -< toList paragraphs+read_frame_text_box :: XML.Element -> ODTReader (FirstMatch Inlines)+read_frame_text_box box = do+  paragraphs <- executeIn box (matchSmushedChildBlocks' [ read_paragraph ])+  return $ read_img_with_caption $ toList paragraphs  read_img_with_caption :: [Block] -> FirstMatch Inlines read_img_with_caption (Para [Image attr alt (src,title)] : _) =@@ -946,26 +838,20 @@ _ANCHOR_PREFIX_ = "anchor"  ---readAnchorAttr :: ODTReader a Anchor-readAnchorAttr = findAttrText NsText "name"+readAnchorAttr :: ODTReader Anchor+readAnchorAttr = findAttr NsText "name"  -- | Beware: may fail-findAnchorName :: ODTReader AnchorPrefix Anchor-findAnchorName = (      keepingTheValue readAnchorAttr-                   >>^  spreadChoice-                 ) >>?! getPrettyAnchor-+findAnchorName :: AnchorPrefix -> ODTReader Anchor+findAnchorName anchorPrefix = do+  uglyAnchor <- readAnchorAttr+  getPrettyAnchor anchorPrefix uglyAnchor  ---maybeAddAnchorFrom :: ODTReader Inlines AnchorPrefix-                   -> ODTReaderSafe Inlines Inlines+maybeAddAnchorFrom :: ODTReader AnchorPrefix -> ODTReader Inlines maybeAddAnchorFrom anchorReader =-  keepingTheValue (anchorReader >>? findAnchorName >>?^ toAnchorElem)-  >>>-  proc (inlines, fAnchorElem) -> do-  case fAnchorElem of-    Right anchorElem -> returnA -< anchorElem-    Left _           -> returnA -< inlines+      (toAnchorElem <$> (anchorReader >>= findAnchorName))+  <|> return mempty   where     toAnchorElem :: Anchor -> Inlines     toAnchorElem anchorID = spanWith (anchorID, [], []) mempty@@ -974,12 +860,12 @@ -- read_bookmark     :: InlineMatcher read_bookmark      = matchingElement NsText "bookmark"-                     $ maybeAddAnchorFrom (liftAsSuccess $ returnV _ANCHOR_PREFIX_)+                     $ maybeAddAnchorFrom (return _ANCHOR_PREFIX_)  -- read_bookmark_start :: InlineMatcher read_bookmark_start = matchingElement NsText "bookmark-start"-                     $ maybeAddAnchorFrom (liftAsSuccess $ returnV _ANCHOR_PREFIX_)+                     $ maybeAddAnchorFrom (return _ANCHOR_PREFIX_)  -- read_reference_start :: InlineMatcher@@ -987,20 +873,18 @@                      $ maybeAddAnchorFrom readAnchorAttr  -- | Beware: may fail-findAnchorRef :: ODTReader a Anchor-findAnchorRef = (      findAttrText NsText "ref-name"-                  >>?^ (_ANCHOR_PREFIX_,)-                ) >>?! getPrettyAnchor-+findAnchorRef :: ODTReader Anchor+findAnchorRef = do+  uglyAnchor <- findAttr NsText "ref-name"+  getPrettyAnchor _ANCHOR_PREFIX_ uglyAnchor  ---maybeInAnchorRef :: ODTReaderSafe Inlines Inlines-maybeInAnchorRef = proc inlines -> do-  fRef <- findAnchorRef -< ()+maybeInAnchorRef :: Inlines -> ODTReader Inlines+maybeInAnchorRef inlines = do+  fRef <- tryC findAnchorRef   case fRef of-    Right anchor ->-      arr (toAnchorRef anchor) -<< inlines-    Left _ -> returnA -< inlines+    Right anchor -> return $ toAnchorRef anchor inlines+    Left _       -> return inlines   where     toAnchorRef :: Anchor -> Inlines -> Inlines     toAnchorRef anchor = link ("#" <> anchor) "" -- no title@@ -1008,28 +892,25 @@ -- read_bookmark_ref :: InlineMatcher read_bookmark_ref = matchingElement NsText "bookmark-ref"-                    $    maybeInAnchorRef-                     <<< matchChildContent [] read_plain_text+                    $ matchContent [] read_plain_text >>= maybeInAnchorRef  -- read_reference_ref :: InlineMatcher read_reference_ref = matchingElement NsText "reference-ref"-                    $    maybeInAnchorRef-                     <<< matchChildContent [] read_plain_text+                    $ matchContent [] read_plain_text >>= maybeInAnchorRef   ---------------------- -- Entry point ---------------------- -read_text :: ODTReaderSafe a Pandoc-read_text = matchSmushedChildBlocks' [ read_header-                                     , read_paragraph-                                     , read_list-                                     , read_section-                                     , read_table-                                     ]-            >>^ doc+read_text :: ODTReader Pandoc+read_text = doc <$> matchSmushedChildBlocks' [ read_header+                                             , read_paragraph+                                             , read_list+                                             , read_section+                                             , read_table+                                             ]  post_process :: Pandoc -> Pandoc post_process (Pandoc m blocks) =@@ -1040,11 +921,10 @@   = Table attr (Caption Nothing blks) specs th tb tf : post_process' xs post_process' bs = bs -read_body :: ODTReader a (Pandoc, MediaBag)+read_body :: ODTReader (Pandoc, MediaBag) read_body = executeInSub NsOffice "body"           $ executeInSub NsOffice "text"-          $ liftAsSuccess-          $ proc inlines -> do-             txt   <- read_text     -< inlines-             state <- getExtraState -< ()-             returnA                -< (post_process txt, getMediaBag state)+          $ do+             txt   <- read_text+             state <- getExtraState+             return (post_process txt, getMediaBag state)
src/Text/Pandoc/Readers/ODT/Generic/Fallible.hs view
@@ -9,28 +9,13 @@  Data types and utilities representing failure. Most of it is based on the "Either" type in its usual configuration (left represents failure).--In most cases, the failure type is implied or required to be a "Monoid".--The choice of "Either" instead of a custom type makes it easier to write-compatible instances of "ArrowChoice". -} --- We export everything module Text.Pandoc.Readers.ODT.Generic.Fallible   ( Failure   , Fallible-  , maybeToEither-  , eitherToMaybe-  , recover-  , failWith-  , failEmpty-  , succeedWith-  , collapseEither   , chooseMax   , chooseMaxWith-  , ChoiceVector(..)-  , SuccessList(..)   ) where  -- | Default for now. Will probably become a class at some point.@@ -38,41 +23,6 @@  type Fallible a = Either Failure a -----maybeToEither :: Maybe a -> Fallible a-maybeToEither (Just a) = Right a-maybeToEither Nothing  = Left  ()-----eitherToMaybe :: Either _l a -> Maybe a-eitherToMaybe (Left  _) = Nothing-eitherToMaybe (Right a) = Just a---- | > recover a === either (const a) id-recover :: a -> Either _f a -> a-recover a (Left  _) = a-recover _ (Right a) = a---- | I would love to use 'fail'. Alas, 'Monad.fail'...-failWith :: failure -> Either failure _x-failWith f = Left f-----failEmpty :: (Monoid failure) => Either failure _x-failEmpty = failWith mempty-----succeedWith :: a -> Either _x a-succeedWith = Right-----collapseEither :: Either failure (Either failure x)-               -> Either failure x-collapseEither (Left f         ) = Left f-collapseEither (Right (Left  f)) = Left f-collapseEither (Right (Right x)) = Right x- -- | If either of the values represents a  non-error, the result is a -- (possibly combined) non-error. If both values represent an error, an error -- is returned.@@ -90,24 +40,3 @@ chooseMaxWith  _   (Left  a) (Left  b) = Left  $ a `mappend` b chooseMaxWith  _   (Right a)     _     = Right a chooseMaxWith  _       _     (Right b) = Right b----- | Class of containers that can escalate contained 'Either's.--- The word "Vector" is meant in the sense of a disease transmitter.-class ChoiceVector v where-  spreadChoice :: v (Either f a) -> Either f (v a)--instance ChoiceVector ((,) a) where-  spreadChoice (_, Left  f) = Left  f-  spreadChoice (x, Right y) = Right (x,y)-  -- Wasn't there a newtype somewhere with the elements flipped?---- | Wrapper for a list. While the normal list instance of 'ChoiceVector'--- fails whenever it can, this type will never fail.-newtype SuccessList a = SuccessList { collectNonFailing :: [a] }-  deriving ( Eq, Ord, Show )--instance ChoiceVector SuccessList  where-  spreadChoice = Right . SuccessList . foldr unTagRight [] . collectNonFailing-    where unTagRight (Right x) = (x:)-          unTagRight _         = id
src/Text/Pandoc/Readers/ODT/Generic/Utils.hs view
@@ -12,71 +12,24 @@ -}  module Text.Pandoc.Readers.ODT.Generic.Utils-( uncurry3-, uncurry4-, uncurry5-, uncurry6-, swap-, reverseComposition-, tryToRead+( tryToRead , Lookupable(..) , readLookupable , readPercent , findBy-, swing-, composition ) where -import Control.Category (Category, (<<<), (>>>))-import qualified Control.Category as Cat (id) import Data.Char (isSpace)-import qualified Data.Foldable as F (Foldable, foldr) import Data.Maybe import Data.Text (Text) import qualified Data.Text as T --- | Equivalent to--- > foldr (.) id--- where '(.)' are 'id' are the ones from "Control.Category"--- and 'foldr' is the one from "Data.Foldable".--- The noun-form was chosen to be consistent with 'sum', 'product' etc--- based on the discussion at--- <https://groups.google.com/forum/#!topic/haskell-cafe/VkOZM1zaHOI>--- (that I was not part of)-composition        :: (Category cat, F.Foldable f) => f (cat a a) -> cat a a-composition        = F.foldr (<<<) Cat.id---- | Equivalent to--- > foldr (flip (.)) id--- where '(.)' are 'id' are the ones from "Control.Category"--- and 'foldr' is the one from "Data.Foldable".--- A reversed version of 'composition'.-reverseComposition :: (Category cat, F.Foldable f) => f (cat a a) -> cat a a-reverseComposition = F.foldr (>>>) Cat.id---- | This function often makes it possible to switch values with the functions--- that are applied to them.------ Examples:--- > swing map :: [a -> b] -> a -> [b]--- > swing any :: [a -> Bool] -> a -> Bool--- > swing foldr :: b -> a -> [a -> b -> b] -> b--- > swing scanr :: c -> a -> [a -> c -> c] -> c--- > swing zipWith :: [a -> b -> c] -> a -> [b] -> [c]--- > swing find :: [a -> Bool] -> a -> Maybe (a -> Bool)------ Stolen from <https://wiki.haskell.org/Pointfree>-swing :: (((a -> b) -> b) -> c -> d) -> c -> a -> d-swing = flip.(.flip id)--- swing f c a = f ($ a) c-- -- | Alternative to 'read'/'reads'. The former of these throws errors -- (nobody wants that) while the latter returns "to much" for simple purposes. -- This function instead applies 'reads' and returns the first match (if any) -- in a 'Maybe'. tryToRead :: (Read r) => Text -> Maybe r-tryToRead = (reads . T.unpack) >>> listToMaybe >>> fmap fst+tryToRead = fmap fst . listToMaybe . reads . T.unpack  -- | A version of 'reads' that requires a '%' sign after the number readPercent :: ReadS Int@@ -93,19 +46,6 @@ readLookupable :: (Lookupable a) => Text -> Maybe a readLookupable s =   lookup (T.takeWhile (not . isSpace) $ T.dropWhile isSpace s) lookupTable--uncurry3 :: (a->b->c                -> z) -> (a,b,c          ) -> z-uncurry4 :: (a->b->c->d             -> z) -> (a,b,c,d        ) -> z-uncurry5 :: (a->b->c->d->e          -> z) -> (a,b,c,d,e      ) -> z-uncurry6 :: (a->b->c->d->e->f       -> z) -> (a,b,c,d,e,f    ) -> z--uncurry3 fun (a,b,c          ) = fun a b c-uncurry4 fun (a,b,c,d        ) = fun a b c d-uncurry5 fun (a,b,c,d,e      ) = fun a b c d e-uncurry6 fun (a,b,c,d,e,f    ) = fun a b c d e f--swap :: (a,b) -> (b,a)-swap (a,b) = (b,a)  -- | A version of "Data.List.find" that uses a converter to a Maybe instance. -- The returned value is the first which the converter returns in a 'Just'
src/Text/Pandoc/Readers/ODT/Generic/XMLConverter.hs view
@@ -1,8 +1,4 @@ {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TupleSections   #-}-{-# LANGUAGE GADTs           #-}-{-# LANGUAGE LambdaCase      #-}-{-# LANGUAGE PatternGuards   #-} {- |    Module      : Text.Pandoc.Readers.ODT.Generic.XMLConverter    Copyright   : Copyright (C) 2015 Martin Linnemann@@ -12,35 +8,33 @@    Stability   : alpha    Portability : portable -A generalized XML parser based on stateful arrows.-It might be sufficient to define this reader as a comonad, but there is-not a lot of use in trying.+A generalized monadic XML parser. The parser navigates through an XML+tree, always looking at a \"current element\", and carries some+additional, converter-specific state. -}  module Text.Pandoc.Readers.ODT.Generic.XMLConverter ( ElementName-, XMLConverterState , XMLConverter-, FallibleXMLConverter-, runConverter'+, runConverter+, fromMaybeF+, fromFallible+, tryC , getExtraState , setExtraState , modifyExtraState-, producingExtraState-, findChild'+, getCurrentElement+, elName , filterChildrenName' , isSet' , isSetWithDefault-, elName , searchAttr , lookupAttr , lookupAttr' , lookupDefaultingAttr , findAttr'-, findAttrText' , findAttr-, findAttrText-, findAttrTextWithDefault+, findAttrWithDefault , readAttr , readAttr' , readAttrWithDefault@@ -49,271 +43,157 @@ , executeInSub , withEveryL , tryAll+, ElementMatcher , matchContent' , matchContent ) where -import           Prelude hiding (Applicative(..))-import           Control.Applicative  hiding ( liftA, liftA2 )-import           Control.Monad               ( MonadPlus )-import           Control.Arrow+import Control.Applicative ( Alternative(..), optional )+import Control.Monad ( filterM, foldM )+import Control.Monad.Except ( ExceptT, runExceptT, throwError, catchError )+import Control.Monad.State ( State, evalState, get, gets, put, modify ) -import           Data.Bool ( bool )-import           Data.Either ( rights )-import qualified Data.Map             as M-import           Data.Text (Text)-import           Data.Default-import           Data.Maybe-import qualified Data.List as L-import qualified Data.List.NonEmpty as NonEmpty-import           Data.List.NonEmpty (NonEmpty(..))+import qualified Data.Map as M+import Data.Text (Text)+import Data.Default+import Data.Maybe  import qualified Text.Pandoc.XML.Light as XML -import           Text.Pandoc.Readers.ODT.Arrows.State-import           Text.Pandoc.Readers.ODT.Arrows.Utils-import           Text.Pandoc.Readers.ODT.Generic.Namespaces-import           Text.Pandoc.Readers.ODT.Generic.Utils-import           Text.Pandoc.Readers.ODT.Generic.Fallible+import Text.Pandoc.Readers.ODT.Generic.Namespaces+import Text.Pandoc.Readers.ODT.Generic.Utils+import Text.Pandoc.Readers.ODT.Generic.Fallible  -------------------------------------------------------------------------------- --  Basis types for readability -------------------------------------------------------------------------------- --- type ElementName           = Text type AttributeName         = Text type AttributeValue        = Text-type TextAttributeValue    = Text --- type NameSpacePrefix       = Text ----type NameSpacePrefixes nsID = M.Map nsID NameSpacePrefix- ----------------------------------------------------------------------------------- Main converter state+-- Converter state -------------------------------------------------------------------------------- --- GADT so some of the NameSpaceID restrictions can be deduced-data XMLConverterState nsID extraState where-  XMLConverterState :: NameSpaceID nsID-    => { -- | A stack of parent elements. The top element is the current one.-         -- Arguably, a real Zipper would be better. But that is an-         -- optimization that can be made at a later time, e.g. when-         -- replacing Text.XML.Light.-         parentElements    :: NonEmpty XML.Element-         -- | A map from internal namespace IDs to the namespace prefixes-         -- used in XML elements-       , namespacePrefixes :: NameSpacePrefixes nsID-         -- | A map from internal namespace IDs to namespace IRIs-         -- (Only necessary for matching namespace IDs and prefixes)-       , namespaceIRIs     :: NameSpaceIRIs nsID-         -- | A place to put "something else". This feature is used heavily-         -- to keep the main code cleaner. More specifically, the main reader-         -- is divided into different stages. Each stage lifts something up-         -- here, which the next stage can then use. This could of course be-         -- generalized to a state-tree or used for the namespace IRIs. The-         -- border between states and values is an imaginary one, after all.-         -- But the separation as it is seems to be enough for now.-       , moreState         :: extraState-       }-    -> XMLConverterState nsID extraState+data XMLConverterState nsID extraState = XMLConverterState+  { -- | The element that is currently being read+    currentElement    :: XML.Element+    -- | A map from internal namespace IDs to the namespace prefixes+    -- used in XML elements+  , namespacePrefixes :: M.Map nsID NameSpacePrefix+    -- | A map from internal namespace IDs to namespace IRIs+    -- (Only necessary for matching namespace IDs and prefixes)+  , namespaceIRIs     :: NameSpaceIRIs nsID+    -- | Converter-specific state+  , moreState         :: extraState+  } --- createStartState :: (NameSpaceID nsID)-                    => XML.Element-                    -> extraState-                    -> XMLConverterState nsID extraState+                 => XML.Element+                 -> extraState+                 -> XMLConverterState nsID extraState createStartState element extraState =   XMLConverterState-       { parentElements    = element :| []+       { currentElement    = element        , namespacePrefixes = M.empty        , namespaceIRIs     = getInitialIRImap        , moreState         = extraState        } --- | Functor over extra state-instance Functor (XMLConverterState nsID) where-  fmap f ( XMLConverterState parents prefixes iRIs    extraState  )-       =   XMLConverterState parents prefixes iRIs (f extraState)-----replaceExtraState   :: extraState-                    -> XMLConverterState nsID _x-                    -> XMLConverterState nsID extraState-replaceExtraState x s-                     = fmap (const x) s-----currentElement      :: XMLConverterState nsID extraState-                    -> XML.Element-currentElement state = NonEmpty.head (parentElements state)---- | Replace the current position by another, modifying the extra state--- in the process-swapStack'          :: XMLConverterState nsID extraState-                    -> NonEmpty XML.Element-                    -> ( XMLConverterState nsID extraState-                       , NonEmpty XML.Element )-swapStack' state stack-                     = ( state { parentElements = stack }-                       , parentElements state-                       )-----pushElement         :: XML.Element-                    -> XMLConverterState nsID extraState-                    -> XMLConverterState nsID extraState-pushElement e state  = state { parentElements =-                                 NonEmpty.cons e (parentElements state) }---- | Pop the top element from the call stack, unless it is the last one.-popElement          :: XMLConverterState nsID extraState-                    -> Maybe (XMLConverterState nsID extraState)-popElement state-  | _ :| (e:es) <- parentElements state-                = Just $ state { parentElements = e :| es }-  | otherwise   = Nothing- -------------------------------------------------------------------------------- -- Main type -------------------------------------------------------------------------------- --- It might be a good idea to pack the converters in a GADT--- Downside: data instead of type--- Upside: 'Failure' could be made a parameter as well.+-- | A converter that can read from an XML tree, may fail (with+-- 'throwError' \/ 'empty'), and carries some additional state.+-- Note that state modifications survive failure; in particular, the+-- current element must be restored explicitly where necessary+-- (see 'executeIn').+type XMLConverter nsID extraState+   = ExceptT () (State (XMLConverterState nsID extraState)) ----type XMLConverter nsID extraState input output-      = ArrowState (XMLConverterState nsID extraState ) input output+-- | Run a converter on an XML element, with a given initial extra state.+runConverter :: (NameSpaceID nsID)+             => XMLConverter nsID extraState a+             -> extraState+             -> XML.Element+             -> Fallible a+runConverter converter extraState element+  = evalState (runExceptT (readNSattributes >> converter))+              (createStartState element extraState) -type FallibleXMLConverter nsID extraState input output-     = XMLConverter nsID extraState input (Fallible output)+-- | Lift a 'Maybe' value into the converter, failing on 'Nothing'.+fromMaybeF :: Maybe a -> XMLConverter nsID extraState a+fromMaybeF = maybe (throwError ()) return ----runConverter     :: XMLConverter nsID extraState input output-                 -> XMLConverterState nsID extraState-                 -> input-                 -> output-runConverter converter state input = snd $ runArrowState converter (state,input)+-- | Lift a 'Fallible' value into the converter.+fromFallible :: Fallible a -> XMLConverter nsID extraState a+fromFallible = either throwError return -runConverter' :: (NameSpaceID nsID)-              => FallibleXMLConverter nsID extraState () success-              -> extraState-              -> XML.Element-              -> Fallible success-runConverter' converter extraState element = runConverter (readNSattributes >>? converter) (createStartState element extraState) ()+-- | Run a converter, catching failure.+tryC :: XMLConverter nsID extraState a+     -> XMLConverter nsID extraState (Fallible a)+tryC converter = catchError (Right <$> converter) (return . Left)  ---getCurrentElement :: XMLConverter nsID extraState x XML.Element-getCurrentElement  = extractFromState currentElement+getCurrentElement :: XMLConverter nsID extraState XML.Element+getCurrentElement = gets currentElement  ---getExtraState     :: XMLConverter nsID extraState x extraState-getExtraState      = extractFromState moreState+getExtraState :: XMLConverter nsID extraState extraState+getExtraState = gets moreState  ---setExtraState     :: XMLConverter nsID extraState extraState extraState-setExtraState      = withState $ \state extra-                                  -> (replaceExtraState extra state , extra)----- | Lifts a function to the extra state.-modifyExtraState  :: (extraState -> extraState)-                  -> XMLConverter nsID extraState x x-modifyExtraState   = modifyState.fmap-+setExtraState :: extraState -> XMLConverter nsID extraState ()+setExtraState x = modify $ \state -> state { moreState = x } --- | First sets the extra state to the new value. Then modifies the original--- extra state with a converter that uses the new state. Finally, the--- intermediate state is dropped and the extra state is lifted into the--- state as it was at the beginning of the function.--- As a result, exactly the extra state and nothing else is changed.--- The resulting converter even behaves like an identity converter on the--- value level. ----- (The -ing form is meant to be mnemonic in a sequence of arrows as in---  convertingExtraState () converter >>> doOtherStuff)----convertingExtraState :: extraState'-                     -> FallibleXMLConverter nsID extraState' extraState extraState-                     -> FallibleXMLConverter nsID extraState x x-convertingExtraState v a = withSubStateF setVAsExtraState modifyWithA-  where-    setVAsExtraState     = liftAsSuccess $ extractFromState id >>^ replaceExtraState v-    modifyWithA          = keepingTheValue (moreState ^>> a)-                           >>^ spreadChoice >>?% flip replaceExtraState---- | First sets the extra state to the new value. Then produces a new--- extra state with a converter that uses the new state. Finally, the--- intermediate state is dropped and the extra state is lifted into the--- state as it was at the beginning of the function.--- As a result, exactly the extra state and nothing else is changed.--- The resulting converter even behaves like an identity converter on the--- value level.------ Equivalent to------ > \v x a -> convertingExtraState v (returnV x >>> a)------ (The -ing form is meant to be mnemonic in a sequence of arrows as in---  producingExtraState () () producer >>> doOtherStuff)----producingExtraState  :: extraState'-                     -> a-                     -> FallibleXMLConverter nsID extraState' a extraState-                     -> FallibleXMLConverter nsID extraState x x-producingExtraState v x a = convertingExtraState v (returnV x >>> a)-+modifyExtraState :: (extraState -> extraState)+                 -> XMLConverter nsID extraState ()+modifyExtraState f = modify $ \state -> state { moreState = f (moreState state) }  -------------------------------------------------------------------------------- -- Work in namespaces -------------------------------------------------------------------------------- --- | Arrow version of 'getIRI'-lookupNSiri             :: (NameSpaceID nsID)-                        => nsID-                        -> XMLConverter nsID extraState x (Maybe NameSpaceIRI)-lookupNSiri nsID        = extractFromState-                          $ \state -> getIRI nsID $ namespaceIRIs state+--+lookupNSiri :: (NameSpaceID nsID)+            => nsID+            -> XMLConverter nsID extraState (Maybe NameSpaceIRI)+lookupNSiri nsID = gets $ getIRI nsID . namespaceIRIs  ---lookupNSprefix           :: (NameSpaceID nsID)-                         => nsID-                         -> XMLConverter nsID extraState x (Maybe NameSpacePrefix)-lookupNSprefix nsID      = extractFromState-                           $ \state -> M.lookup nsID $ namespacePrefixes state+lookupNSprefix :: (NameSpaceID nsID)+               => nsID+               -> XMLConverter nsID extraState (Maybe NameSpacePrefix)+lookupNSprefix nsID = gets $ M.lookup nsID . namespacePrefixes  -- | Extracts namespace attributes from the current element and tries to -- update the current mapping accordingly-readNSattributes         :: (NameSpaceID nsID)-                         => FallibleXMLConverter nsID extraState x ()-readNSattributes         = fromState $ \state -> maybe (state, failEmpty     )-                                                       (     , succeedWith ())-                                                       (extractNSAttrs state )+readNSattributes :: (NameSpaceID nsID) => XMLConverter nsID extraState ()+readNSattributes = do+  state <- get+  maybe (throwError ()) put (extractNSAttrs state)   where-    extractNSAttrs       :: (NameSpaceID nsID)-                         => XMLConverterState nsID extraState-                         -> Maybe (XMLConverterState nsID extraState)-    extractNSAttrs startState-                         = L.foldl' (\state d -> state >>= addNS d)-                                 (Just startState)-                                 nsAttribs-      where nsAttribs    = mapMaybe readNSattr (XML.elAttribs element)-            element      = currentElement startState+    extractNSAttrs :: (NameSpaceID nsID)+                   => XMLConverterState nsID extraState+                   -> Maybe (XMLConverterState nsID extraState)+    extractNSAttrs startState = foldM addNS startState nsAttribs+      where nsAttribs    = mapMaybe readNSattr+                                    (XML.elAttribs $ currentElement startState)             readNSattr (XML.Attr (XML.QName name _ (Just "xmlns")) iri)                          = Just (name, iri)             readNSattr _ = Nothing-    addNS  (prefix, iri) state = fmap updateState-                                 $ getNamespaceID iri-                                 $ namespaceIRIs state-      where updateState (iris,nsID)-                         = state { namespaceIRIs     = iris-                                 , namespacePrefixes = M.insert nsID prefix-                                                       $ namespacePrefixes state-                                 }+    addNS state (prefix, iri) = updateState+                                <$> getNamespaceID iri (namespaceIRIs state)+      where updateState (iris, nsID)+              = state { namespaceIRIs     = iris+                      , namespacePrefixes = M.insert nsID prefix+                                            $ namespacePrefixes state+                      }  -------------------------------------------------------------------------------- -- Common namespace accessors@@ -321,29 +201,30 @@  -- | Given a namespace id and an element name, creates a 'XML.QName' for -- internal use-qualifyName              :: (NameSpaceID nsID)-                         => nsID -> ElementName-                         -> XMLConverter nsID extraState x XML.QName-qualifyName nsID name    =         lookupNSiri nsID-                               &&& lookupNSprefix nsID-                           >>% XML.QName name+qualifyName :: (NameSpaceID nsID)+            => nsID -> ElementName+            -> XMLConverter nsID extraState XML.QName+qualifyName nsID name = XML.QName name <$> lookupNSiri nsID+                                       <*> lookupNSprefix nsID  -- | Checks if a given element matches both a specified namespace id -- and a predicate-elemNameMatches          :: (NameSpaceID nsID)-                         => nsID -> (ElementName -> Bool)-                         -> XMLConverter nsID extraState XML.Element Bool-elemNameMatches nsID f    = keepingTheValue (lookupNSiri nsID) >>% hasMatchingName-  where hasMatchingName e iri = let name = XML.elName e-                                in     f (XML.qName name)-                                    && XML.qURI name == iri+elemNameMatches :: (NameSpaceID nsID)+                => nsID -> (ElementName -> Bool)+                -> XML.Element+                -> XMLConverter nsID extraState Bool+elemNameMatches nsID f element = do+  iri <- lookupNSiri nsID+  let name = XML.elName element+  return $ f (XML.qName name) && XML.qURI name == iri  -- | Checks if a given element matches both a specified namespace id -- and a specified element name-elemNameIs               :: (NameSpaceID nsID)-                         => nsID -> ElementName-                         -> XMLConverter nsID extraState XML.Element Bool-elemNameIs nsID name     = elemNameMatches nsID (== name)+elemNameIs :: (NameSpaceID nsID)+           => nsID -> ElementName+           -> XML.Element+           -> XMLConverter nsID extraState Bool+elemNameIs nsID name = elemNameMatches nsID (== name)  -------------------------------------------------------------------------------- -- General content@@ -352,394 +233,245 @@ elName :: XML.Element -> ElementName elName = XML.qName . XML.elName ----elContent               :: XMLConverter nsID extraState x [XML.Content]-elContent               =     getCurrentElement-                           >>^ XML.elContent- -------------------------------------------------------------------------------- -- Children --------------------------------------------------------------------------------  ------findChildren             :: (NameSpaceID nsID)-                         => nsID -> ElementName-                         -> XMLConverter nsID extraState x [XML.Element]-findChildren nsID name   =         qualifyName nsID name-                               &&& getCurrentElement-                           >>% XML.findChildren+findChildren :: (NameSpaceID nsID)+             => nsID -> ElementName+             -> XMLConverter nsID extraState [XML.Element]+findChildren nsID name = XML.findChildren <$> qualifyName nsID name+                                          <*> getCurrentElement  ---findChild'              :: (NameSpaceID nsID)-                        => nsID-                        -> ElementName-                        -> XMLConverter nsID extraState x (Maybe XML.Element)-findChild' nsID name    =         qualifyName nsID name-                              &&& getCurrentElement-                          >>% XML.findChild+findChild' :: (NameSpaceID nsID)+           => nsID -> ElementName+           -> XMLConverter nsID extraState (Maybe XML.Element)+findChild' nsID name = XML.findChild <$> qualifyName nsID name+                                     <*> getCurrentElement  ---findChild              :: (NameSpaceID nsID)-                       => nsID -> ElementName-                       -> FallibleXMLConverter nsID extraState x XML.Element-findChild nsID name    =     findChild' nsID name-                         >>> maybeToChoice+findChild :: (NameSpaceID nsID)+          => nsID -> ElementName+          -> XMLConverter nsID extraState XML.Element+findChild nsID name = findChild' nsID name >>= fromMaybeF -filterChildrenName'        :: (NameSpaceID nsID)-                           => nsID-                           -> (ElementName -> Bool)-                           -> XMLConverter nsID extraState x [XML.Element]-filterChildrenName' nsID f =     getCurrentElement-                             >>> arr XML.elChildren-                             >>> iterateS (keepingTheValue (elemNameMatches nsID f))-                             >>> arr (map fst . filter snd)+--+filterChildrenName' :: (NameSpaceID nsID)+                    => nsID+                    -> (ElementName -> Bool)+                    -> XMLConverter nsID extraState [XML.Element]+filterChildrenName' nsID f = getCurrentElement+                             >>= filterM (elemNameMatches nsID f) . XML.elChildren  -------------------------------------------------------------------------------- -- Attributes --------------------------------------------------------------------------------  ---isSet'                   :: (NameSpaceID nsID)-                         => nsID -> AttributeName-                         -> XMLConverter nsID extraState x (Maybe Bool)-isSet' nsID attrName     =     findAttr' nsID attrName-                           >>^ (>>= stringToBool')+isSet' :: (NameSpaceID nsID)+       => nsID -> AttributeName+       -> XMLConverter nsID extraState (Maybe Bool)+isSet' nsID attrName = (>>= stringToBool') <$> findAttr' nsID attrName -isSetWithDefault         :: (NameSpaceID nsID)-                         => nsID -> AttributeName-                         -> Bool-                         -> XMLConverter nsID extraState x Bool-isSetWithDefault nsID attrName def'-                         =     isSet' nsID attrName-                           >>^ fromMaybe def'+isSetWithDefault :: (NameSpaceID nsID)+                 => nsID -> AttributeName+                 -> Bool+                 -> XMLConverter nsID extraState Bool+isSetWithDefault nsID attrName def' =+  fromMaybe def' <$> isSet' nsID attrName  -- | Lookup value in a dictionary, fail if no attribute found or value -- not in dictionary-searchAttrIn             :: (NameSpaceID nsID)-                         => nsID -> AttributeName-                         -> [(AttributeValue,a)]-                         -> FallibleXMLConverter nsID extraState x a-searchAttrIn nsID attrName dict-                         =       findAttr nsID attrName-                           >>?^? maybeToChoice.(`lookup` dict )+searchAttrIn :: (NameSpaceID nsID)+             => nsID -> AttributeName+             -> [(AttributeValue,a)]+             -> XMLConverter nsID extraState a+searchAttrIn nsID attrName dict = do+  value <- findAttr nsID attrName+  fromMaybeF $ lookup value dict  -- | Lookup value in a dictionary. If attribute or value not found, -- return default value-searchAttr               :: (NameSpaceID nsID)-                         => nsID -> AttributeName-                         -> a-                         -> [(AttributeValue,a)]-                         -> XMLConverter nsID extraState x a-searchAttr nsID attrName defV dict-                         =     searchAttrIn nsID attrName dict-                           >>> const defV ^|||^ id+searchAttr :: (NameSpaceID nsID)+           => nsID -> AttributeName+           -> a+           -> [(AttributeValue,a)]+           -> XMLConverter nsID extraState a+searchAttr nsID attrName defV dict =+  searchAttrIn nsID attrName dict <|> return defV  -- | Read a 'Lookupable' attribute. Fail if no match.-lookupAttr               :: (NameSpaceID nsID, Lookupable a)-                         => nsID -> AttributeName-                         -> FallibleXMLConverter nsID extraState x a-lookupAttr nsID attrName =     lookupAttr' nsID attrName-                           >>^ maybeToChoice-+lookupAttr :: (NameSpaceID nsID, Lookupable a)+           => nsID -> AttributeName+           -> XMLConverter nsID extraState a+lookupAttr nsID attrName = lookupAttr' nsID attrName >>= fromMaybeF  -- | Read a 'Lookupable' attribute. Return the result as a 'Maybe'.-lookupAttr'              :: (NameSpaceID nsID, Lookupable a)-                         => nsID -> AttributeName-                         -> XMLConverter nsID extraState x (Maybe a)-lookupAttr' nsID attrName-                         =     findAttr' nsID attrName-                           >>^ (>>= readLookupable)+lookupAttr' :: (NameSpaceID nsID, Lookupable a)+            => nsID -> AttributeName+            -> XMLConverter nsID extraState (Maybe a)+lookupAttr' nsID attrName =+  (>>= readLookupable) <$> findAttr' nsID attrName  -- | Read a 'Lookupable' attribute with explicit default-lookupAttrWithDefault    :: (NameSpaceID nsID, Lookupable a)-                         => nsID -> AttributeName-                         -> a-                         -> XMLConverter nsID extraState x a-lookupAttrWithDefault nsID attrName deflt-                         =     lookupAttr' nsID attrName-                           >>^ fromMaybe deflt+lookupAttrWithDefault :: (NameSpaceID nsID, Lookupable a)+                      => nsID -> AttributeName+                      -> a+                      -> XMLConverter nsID extraState a+lookupAttrWithDefault nsID attrName deflt =+  fromMaybe deflt <$> lookupAttr' nsID attrName  -- | Read a 'Lookupable' attribute with implicit default-lookupDefaultingAttr     :: (NameSpaceID nsID, Lookupable a, Default a)-                         => nsID -> AttributeName-                         -> XMLConverter nsID extraState x a-lookupDefaultingAttr nsID attrName-                         = lookupAttrWithDefault nsID attrName def---- | Return value as a (Maybe Text)-findAttr'               :: (NameSpaceID nsID)-                        => nsID -> AttributeName-                        -> XMLConverter nsID extraState x (Maybe AttributeValue)-findAttr' nsID attrName =         qualifyName nsID attrName-                              &&& getCurrentElement-                          >>% XML.findAttr+lookupDefaultingAttr :: (NameSpaceID nsID, Lookupable a, Default a)+                     => nsID -> AttributeName+                     -> XMLConverter nsID extraState a+lookupDefaultingAttr nsID attrName =+  lookupAttrWithDefault nsID attrName def  -- | Return value as a (Maybe Text)-findAttrText'           :: (NameSpaceID nsID)-                        => nsID -> AttributeName-                        -> XMLConverter nsID extraState x (Maybe TextAttributeValue)-findAttrText' nsID attrName-                        =         qualifyName nsID attrName-                              &&& getCurrentElement-                          >>% XML.findAttr---- | Return value as string or fail-findAttr               :: (NameSpaceID nsID)-                       => nsID -> AttributeName-                       -> FallibleXMLConverter nsID extraState x AttributeValue-findAttr nsID attrName =     findAttr' nsID attrName-                         >>> maybeToChoice+findAttr' :: (NameSpaceID nsID)+          => nsID -> AttributeName+          -> XMLConverter nsID extraState (Maybe AttributeValue)+findAttr' nsID attrName = XML.findAttr <$> qualifyName nsID attrName+                                       <*> getCurrentElement --- | Return value as text or fail-findAttrText           :: (NameSpaceID nsID)-                       => nsID -> AttributeName-                       -> FallibleXMLConverter nsID extraState x TextAttributeValue-findAttrText nsID attrName-                       = findAttr' nsID attrName-                         >>> maybeToChoice+-- | Return value or fail+findAttr :: (NameSpaceID nsID)+         => nsID -> AttributeName+         -> XMLConverter nsID extraState AttributeValue+findAttr nsID attrName = findAttr' nsID attrName >>= fromMaybeF --- | Return value as string or return provided default value-findAttrTextWithDefault :: (NameSpaceID nsID)-                        => nsID -> AttributeName-                        -> TextAttributeValue-                        -> XMLConverter nsID extraState x TextAttributeValue-findAttrTextWithDefault nsID attrName deflt-                       = findAttr' nsID attrName-                         >>^ fromMaybe deflt+-- | Return value or return provided default value+findAttrWithDefault :: (NameSpaceID nsID)+                    => nsID -> AttributeName+                    -> AttributeValue+                    -> XMLConverter nsID extraState AttributeValue+findAttrWithDefault nsID attrName deflt =+  fromMaybe deflt <$> findAttr' nsID attrName  -- | Read and return value or fail-readAttr               :: (NameSpaceID nsID, Read attrValue)-                       => nsID -> AttributeName-                       -> FallibleXMLConverter nsID extraState x attrValue-readAttr nsID attrName =     readAttr' nsID attrName-                         >>> maybeToChoice+readAttr :: (NameSpaceID nsID, Read attrValue)+         => nsID -> AttributeName+         -> XMLConverter nsID extraState attrValue+readAttr nsID attrName = readAttr' nsID attrName >>= fromMaybeF  -- | Read and return value or return Nothing-readAttr'              :: (NameSpaceID nsID, Read attrValue)-                       => nsID -> AttributeName-                       -> XMLConverter nsID extraState x (Maybe attrValue)-readAttr' nsID attrName =     findAttr' nsID attrName-                          >>^ (>>= tryToRead)+readAttr' :: (NameSpaceID nsID, Read attrValue)+          => nsID -> AttributeName+          -> XMLConverter nsID extraState (Maybe attrValue)+readAttr' nsID attrName = (>>= tryToRead) <$> findAttr' nsID attrName  -- | Read and return value or return provided default value-readAttrWithDefault    :: (NameSpaceID nsID, Read attrValue)-                       => nsID -> AttributeName-                       -> attrValue-                       -> XMLConverter nsID extraState x attrValue-readAttrWithDefault nsID attrName deflt-                       =     findAttr' nsID attrName-                         >>^ (>>= tryToRead)-                         >>^ fromMaybe deflt+readAttrWithDefault :: (NameSpaceID nsID, Read attrValue)+                    => nsID -> AttributeName+                    -> attrValue+                    -> XMLConverter nsID extraState attrValue+readAttrWithDefault nsID attrName deflt =+  fromMaybe deflt <$> readAttr' nsID attrName  -- | Read and return value or return default value from 'Default' instance-getAttr                :: (NameSpaceID nsID, Read attrValue, Default attrValue)-                       => nsID -> AttributeName-                       -> XMLConverter nsID extraState x attrValue-getAttr nsID attrName  = readAttrWithDefault nsID attrName def+getAttr :: (NameSpaceID nsID, Read attrValue, Default attrValue)+        => nsID -> AttributeName+        -> XMLConverter nsID extraState attrValue+getAttr nsID attrName = readAttrWithDefault nsID attrName def  -------------------------------------------------------------------------------- -- Movements -------------------------------------------------------------------------------- ----jumpThere              :: XMLConverter nsID extraState XML.Element XML.Element-jumpThere              = withState (\state element-                                     -> ( pushElement element state , element )-                                   )-----swapStack             :: XMLConverter nsID extraState (NonEmpty XML.Element)-                                                      (NonEmpty XML.Element)-swapStack             = withState swapStack'-----jumpBack               :: FallibleXMLConverter nsID extraState _x _x-jumpBack               = tryModifyState (popElement >>> maybeToChoice)---- | Support function for "procedural" converters: jump to an element, execute--- a converter, jump back.--- This version is safer than 'executeThere', because it does not rely on the--- internal stack. As a result, the converter can not move around in arbitrary--- ways. The downside is of course that some of the environment is not--- accessible to the converter.-switchingTheStack      :: XMLConverter nsID moreState a b-                       -> XMLConverter nsID moreState (a, XML.Element) b-switchingTheStack a    =     second ( (:| []) ^>> swapStack )-                         >>> first  a-                         >>> second swapStack-                         >>^ fst---- | Support function for "procedural" converters: jumps to an element, executes--- a converter, jumps back.--- Make sure that the converter is well-behaved; that is it should--- return to the exact position it started from in /every possible path/ of--- execution, even if it "fails". If it does not, you may encounter--- strange bugs. If you are not sure about the behaviour or want to use--- shortcuts, you can often use 'switchingTheStack' instead.-executeThere           :: FallibleXMLConverter nsID moreState a b-                       -> FallibleXMLConverter nsID moreState (a, XML.Element) b-executeThere a         =      second jumpThere-                          >>> fst-                          ^>> a-                          >>> jumpBack -- >>? jumpBack  would not ensure the jump.-                          >>^ collapseEither----- | Do something in a specific element, then come back-executeIn   :: XMLConverter nsID extraState XML.Element s-            -> XMLConverter nsID extraState XML.Element s-executeIn a = duplicate >>> switchingTheStack a+-- | Execute a converter in a specific element, then come back.+-- The current element is restored even if the converter fails.+executeIn :: XML.Element+          -> XMLConverter nsID extraState a+          -> XMLConverter nsID extraState a+executeIn element converter = do+  oldElement <- getCurrentElement+  modify $ \state -> state { currentElement = element }+  result <- tryC converter+  modify $ \state -> state { currentElement = oldElement }+  fromFallible result --- | Do something in a sub-element, then come back-executeInSub              :: (NameSpaceID nsID)-                          => nsID -> ElementName-                          -> FallibleXMLConverter nsID extraState f s-                          -> FallibleXMLConverter nsID extraState f s-executeInSub nsID name a  =     keepingTheValue-                                  (findChild nsID name)-                            >>> ignoringState liftFailure-                            >>? switchingTheStack a-  where liftFailure (_, Left  f) = Left  f-        liftFailure (x, Right e) = Right (x, e)+-- | Execute a converter in a sub-element of the current element,+-- then come back. Fails if there is no such sub-element.+executeInSub :: (NameSpaceID nsID)+             => nsID -> ElementName+             -> XMLConverter nsID extraState a+             -> XMLConverter nsID extraState a+executeInSub nsID name converter = do+  child <- findChild nsID name+  executeIn child converter  -------------------------------------------------------------------------------- -- Iterating over children -------------------------------------------------------------------------------- --- Helper converter to prepare different types of iterations.--- It lifts the children (of a certain type) of the current element--- into the value level and pairs each one with the current input value.-prepareIteration       :: (NameSpaceID nsID)-                       => nsID -> ElementName-                       -> XMLConverter nsID extraState b [(b, XML.Element)]-prepareIteration nsID name =     keepingTheValue-                                   (findChildren nsID name)-                             >>% distributeValue-----withEveryL             :: (NameSpaceID nsID)-                       => nsID -> ElementName-                       -> FallibleXMLConverter nsID extraState a  b-                       -> FallibleXMLConverter nsID extraState a [b]-withEveryL = withEvery- -- | Applies a converter to every child element of a specific type.--- Collects results in a 'MonadPlus'. -- Fails completely if any conversion fails.-withEvery              :: (NameSpaceID nsID, MonadPlus m)-                       => nsID -> ElementName-                       -> FallibleXMLConverter nsID extraState a    b-                       -> FallibleXMLConverter nsID extraState a (m b)-withEvery nsID name a      =     prepareIteration nsID name-                             >>> iterateS' (switchingTheStack a)+withEveryL :: (NameSpaceID nsID)+           => nsID -> ElementName+           -> XMLConverter nsID extraState a+           -> XMLConverter nsID extraState [a]+withEveryL nsID name converter = do+  children <- findChildren nsID name+  mapM (`executeIn` converter) children  -- | Applies a converter to every child element of a specific type. -- Collects all successful results in a list.-tryAll                 :: (NameSpaceID nsID)-                       => nsID -> ElementName-                       -> FallibleXMLConverter nsID extraState b  a-                       ->         XMLConverter nsID extraState b [a]-tryAll nsID name a         =     prepareIteration nsID name-                             >>> iterateS (switchingTheStack a)-                             >>^ rights+tryAll :: (NameSpaceID nsID)+       => nsID -> ElementName+       -> XMLConverter nsID extraState a+       -> XMLConverter nsID extraState [a]+tryAll nsID name converter = do+  children <- findChildren nsID name+  catMaybes <$> mapM (\child -> optional (executeIn child converter)) children  -------------------------------------------------------------------------------- -- Matching children -------------------------------------------------------------------------------- -type IdXMLConverter nsID moreState x-   = XMLConverter   nsID moreState x x--type MaybeCConverter nsID moreState x-   = Maybe (IdXMLConverter nsID moreState (x, XML.Content))---- Chainable converter that helps deciding which converter to actually use.-type ContentMatchConverter nsID extraState x-   = IdXMLConverter  nsID-                     extraState-                     (MaybeCConverter nsID extraState x, XML.Content)---- Helper function: The @c@ is actually a converter that is to be selected by--- matching XML content to the first two parameters.--- The fold used to match elements however is very simple, so to use it,--- this function wraps the converter in another converter that unifies--- the accumulator. Think of a lot of converters with the resulting type--- chained together. The accumulator not only transports the element--- unchanged to the next matcher, it also does the actual selecting by--- combining the intermediate results with '(<|>)'.-makeMatcherC           :: (NameSpaceID nsID)-                       => nsID -> ElementName-                       -> FallibleXMLConverter  nsID extraState a a-                       -> ContentMatchConverter nsID extraState a-makeMatcherC nsID name c = (    second (    contentToElem-                                         >>> returnV Nothing-                                         ||| (    elemNameIs nsID name-                                              >>^ bool Nothing (Just cWithJump)-                                             )-                                        )-                             >>% (<|>)-                           ) &&&^ snd-  where cWithJump =      ( fst-                           ^&&& (      second contentToElem-                                  >>>  spreadChoice-                                  ^>>? executeThere c-                                )-                            >>% recover)-                    &&&^ snd-        contentToElem :: FallibleXMLConverter nsID extraState XML.Content XML.Element-        contentToElem = arr $ \case-                                     XML.Elem e' -> succeedWith e'-                                     _           -> failEmpty---- Creates and chains a bunch of matchers-prepareMatchersC      :: (NameSpaceID nsID)-                       => [(nsID, ElementName, FallibleXMLConverter nsID extraState x x)]-                       -> ContentMatchConverter nsID extraState x---prepareMatchersC      = foldSs . (map $ uncurry3  makeMatcherC)-prepareMatchersC      = reverseComposition . map (uncurry3  makeMatcherC)+-- | A converter for a child element with a specific name in a specific+-- namespace. The converter produces that element's contribution to the+-- overall result.+type ElementMatcher nsID extraState a+   = (nsID, ElementName, XMLConverter nsID extraState a) --- | Takes a list of element-data - converter groups and--- * Finds all content of the current element--- * Matches each group to each piece of content in order---   (at most one group per piece of content)--- * Filters non-matched content--- * Chains all found converters in content-order--- * Applies the chain to the input element-matchContent'           :: (NameSpaceID nsID)-                       => [(nsID, ElementName, FallibleXMLConverter nsID extraState a a)]-                       -> XMLConverter nsID extraState a a-matchContent' lookups   = matchContent lookups (arr fst)+-- | Like 'matchContent', but ignores non-matching content.+matchContent' :: (NameSpaceID nsID, Monoid a)+              => [ElementMatcher nsID extraState a]+              -> XMLConverter nsID extraState a+matchContent' lookups = matchContent lookups (\_ -> return mempty) --- | Takes a list of element-data - converter groups and--- * Finds all content of the current element--- * Matches each group to each piece of content in order---   (at most one group per piece of content)--- * Adds a default converter for all non-matched content--- * Chains all found converters in content-order--- * Applies the chain to the input element-matchContent          :: (NameSpaceID nsID)-                       => [(nsID, ElementName, FallibleXMLConverter nsID extraState a a)]-                       -> XMLConverter nsID extraState (a,XML.Content) a-                       -> XMLConverter nsID extraState a a-matchContent lookups fallback-                        = let matcher = prepareMatchersC lookups-                          in  keepingTheValue (-                                   elContent-                               >>> map (Nothing,)-                               ^>> iterateSL matcher-                               >>^ map swallowOrFallback-                              -- >>> foldSs-                               >>> reverseComposition-                             )-                         >>> swap-                         ^>> app+-- | Takes a list of element matchers and a fallback converter, and+-- converts the content of the current element in order: for each child+-- element, the first matcher with a matching name (if any) is applied+-- in that element; all other content is passed to the fallback+-- converter. The results are combined with 'mappend'.+-- If a matched converter fails, the corresponding element contributes+-- nothing to the result.+matchContent :: (NameSpaceID nsID, Monoid a)+             => [ElementMatcher nsID extraState a]+             -> (XML.Content -> XMLConverter nsID extraState a)+             -> XMLConverter nsID extraState a+matchContent lookups fallback = do+  contents <- XML.elContent <$> getCurrentElement+  mconcat <$> mapM matchOne contents   where-        -- let the converter swallow the content and drop the content-        -- in the return value-        swallowOrFallback (Just converter,content) = (,content) ^>> converter >>^ fst-        swallowOrFallback (Nothing       ,content) = (,content) ^>> fallback+    matchOne content@(XML.Elem element) = do+      mConverter <- findConverter element lookups+      case mConverter of+        Just converter -> executeIn element converter <|> return mempty+        Nothing        -> fallback content+    matchOne content = fallback content +    findConverter _ [] = return Nothing+    findConverter element ((nsID, name, converter):rest) = do+      matches <- elemNameIs nsID name element+      if matches+        then return $ Just converter+        else findConverter element rest+ -------------------------------------------------------------------------------- -- Internals --------------------------------------------------------------------------------@@ -750,32 +482,3 @@                   | otherwise              = Nothing   where trueValues  = ["true" ,"on" ,"1"]         falseValues = ["false","off","0"]---distributeValue ::  a -> [b] -> [(a,b)]-distributeValue = map.(,)------------------------------------------------------------------------------------{--NOTES-It might be a good idea to refactor the namespace stuff.-E.g.: if a namespace constructor took a string as a parameter, things like-> a ?>/< (NsText,"body")-would be nicer.-Together with a rename and some trickery, something like-> |< NsText "body" >< NsText "p" ?> a </> </>|-might even be possible.--Some day, XML.Light should be replaced by something better.-While doing that, it might be useful to replace String as the type of element-names with something else, too. (Of course with OverloadedStrings).-While doing that, maybe the types can be created in a way that something like-> NsText:"body"-could be used. Overloading (:) does not sounds like the best idea, but if the-element name type was a list, this might be possible.-Of course that would be a bit hackish, so the "right" way would probably be-something like-> InNS NsText "body"-but isn't that a bit boring? ;)--}
src/Text/Pandoc/Readers/ODT/StyleReader.hs view
@@ -1,5 +1,3 @@-{-# LANGUAGE CPP             #-}-{-# LANGUAGE Arrows          #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TupleSections   #-} {-# LANGUAGE OverloadedStrings #-}@@ -39,9 +37,7 @@ , readStylesAt ) where -import Prelude hiding (Applicative(..))-import Control.Applicative hiding (liftA, liftA2, liftA3)-import Control.Arrow+import Control.Applicative ((<|>), optional)  import Data.Default import qualified Data.Foldable as F@@ -56,18 +52,15 @@  import Text.Pandoc.Shared (safeRead, tshow) -import Text.Pandoc.Readers.ODT.Arrows.Utils- import Text.Pandoc.Readers.ODT.Generic.Fallible import qualified Text.Pandoc.Readers.ODT.Generic.SetMap as SM import Text.Pandoc.Readers.ODT.Generic.Utils import Text.Pandoc.Readers.ODT.Generic.XMLConverter -import Text.Pandoc.Readers.ODT.Base import Text.Pandoc.Readers.ODT.Namespaces  readStylesAt :: XML.Element -> Fallible Styles-readStylesAt e = runConverter' readAllStyles mempty e+readStylesAt e = runConverter readAllStyles mempty e  -------------------------------------------------------------------------------- -- Reader for font declarations and font pitches@@ -99,41 +92,29 @@ type FontPitches = M.Map FontFaceName FontPitch  -- To get there, the fonts have to be read and the pitches extracted.--- But the resulting map are only needed at one later place, so it should not be--- transported on the value level, especially as we already use a state arrow.--- So instead, the resulting map is lifted into the state of the reader.--- (An alternative might be ImplicitParams, but again, we already have a state.)+-- But the resulting map is only needed at one later place, so it should not be+-- transported on the value level. Instead, it is lifted into the state of the+-- reader. ----- So the main style readers will have the types-type StyleReader     a b  = XMLReader     FontPitches a b--- and-type StyleReaderSafe a b  = XMLReaderSafe FontPitches a b--- respectively.+-- So the main style readers will have the type+type StyleReader a = XMLConverter Namespace FontPitches a ----- But before we can work with these, we need to define the reader that reads--- the fonts:+-- But before we can work with this, we need to define the reader that reads+-- the fonts. It is polymorphic in the extra state, since the pitches are not+-- yet available while it runs.  -- | A reader for font pitches-fontPitchReader :: XMLReader s a FontPitches-fontPitchReader = executeInSub NsOffice "font-face-decls" (-                          withEveryL NsStyle "font-face" (liftAsSuccess (-                              findAttr' NsStyle "name"-                              &&&-                              lookupDefaultingAttr NsStyle "font-pitch"-                            ))-                    >>?^ ( M.fromList . L.foldl' accumLegalPitches [] )-                  ) `ifFailedDo` returnV (Right M.empty)-  where accumLegalPitches ls (Nothing,_) = ls-        accumLegalPitches ls (Just n,p)  = (n,p):ls----- | A wrapper around the font pitch reader that lifts the result into the--- state.-readFontPitches :: StyleReader a a-readFontPitches = producingExtraState () () fontPitchReader-+fontPitchReader :: XMLConverter Namespace s FontPitches+fontPitchReader =+  executeInSub NsOffice "font-face-decls" (do+    fonts <- withEveryL NsStyle "font-face" $ do+               name  <- findAttr' NsStyle "name"+               pitch <- lookupDefaultingAttr NsStyle "font-pitch"+               return (name, pitch)+    return $ M.fromList [ (n, p) | (Just n, p) <- fonts ])+  <|> return M.empty --- | Looking up a pitch in the state of the arrow.+-- | Looking up a pitch in the extra state. -- -- The function does the following: -- * Look for the font pitch in an attribute.@@ -141,15 +122,12 @@ --   and use the pitch from there. -- * Return the result in a Maybe ---findPitch :: XMLReaderSafe FontPitches a (Maybe FontPitch)-findPitch =     ( lookupAttr NsStyle "font-pitch"-                  `ifFailedDo`     findAttr NsStyle "font-name"-                               >>? (     keepingTheValue getExtraState-                                     >>% M.lookup-                                     >>^ maybeToChoice-                                   )-                )-            >>> choiceToMaybe+findPitch :: StyleReader (Maybe FontPitch)+findPitch = optional+  $     lookupAttr NsStyle "font-pitch"+    <|> (do fontName <- findAttr NsStyle "font-name"+            pitches  <- getExtraState+            fromMaybeF $ M.lookup fontName pitches)  -------------------------------------------------------------------------------- -- Definitions of main data@@ -416,84 +394,77 @@ --------------------------------------------------------------------------------  ---readAllStyles :: StyleReader a Styles-readAllStyles = (      readFontPitches-                  >>?! (     readAutomaticStyles-                         &&& readStyles ))-                  >>?%? chooseMax+readAllStyles :: StyleReader Styles+readAllStyles = do+  fontPitchReader >>= setExtraState+  autoStyles <- tryC readAutomaticStyles+  styles     <- tryC readStyles+  fromFallible $ chooseMax autoStyles styles  -- all top elements are always on the same hierarchy level  ---readStyles :: StyleReader a Styles-readStyles = executeInSub NsOffice "styles" $ liftAsSuccess-  $ liftA3 Styles-    ( tryAll NsStyle "style"         readStyle        >>^ M.fromList )-    ( tryAll NsText  "list-style"    readListStyle    >>^ M.fromList )-    ( tryAll NsStyle "default-style" readDefaultStyle >>^ M.fromList )+readStyles :: StyleReader Styles+readStyles = executeInSub NsOffice "styles" $+  Styles <$> (M.fromList <$> tryAll NsStyle "style"         readStyle       )+         <*> (M.fromList <$> tryAll NsText  "list-style"    readListStyle   )+         <*> (M.fromList <$> tryAll NsStyle "default-style" readDefaultStyle)  ---readAutomaticStyles :: StyleReader a Styles-readAutomaticStyles = executeInSub NsOffice "automatic-styles" $ liftAsSuccess-  $ liftA3 Styles-    ( tryAll NsStyle "style"         readStyle        >>^ M.fromList )-    ( tryAll NsText  "list-style"    readListStyle    >>^ M.fromList )-    ( returnV M.empty                                                )+readAutomaticStyles :: StyleReader Styles+readAutomaticStyles = executeInSub NsOffice "automatic-styles" $+  Styles <$> (M.fromList <$> tryAll NsStyle "style"         readStyle       )+         <*> (M.fromList <$> tryAll NsText  "list-style"    readListStyle   )+         <*> pure M.empty  ---readDefaultStyle :: StyleReader a (StyleFamily, StyleProperties)-readDefaultStyle =      lookupAttr NsStyle "family"-                   >>?! keepingTheValue readStyleProperties+readDefaultStyle :: StyleReader (StyleFamily, StyleProperties)+readDefaultStyle = (,) <$> lookupAttr NsStyle "family"+                       <*> readStyleProperties  ---readStyle :: StyleReader a (StyleName,Style)-readStyle =      findAttr NsStyle "name"-            >>?! keepingTheValue-                   ( liftA4 Style-                       ( lookupAttr' NsStyle "family"            )-                       ( findAttr'   NsStyle "parent-style-name" )-                       ( findAttr'   NsStyle "list-style-name"   )-                       readStyleProperties-                   )+readStyle :: StyleReader (StyleName, Style)+readStyle = do+  name  <- findAttr NsStyle "name"+  style <- Style <$> lookupAttr' NsStyle "family"+                 <*> findAttr'   NsStyle "parent-style-name"+                 <*> findAttr'   NsStyle "list-style-name"+                 <*> readStyleProperties+  return (name, style)  ---readStyleProperties :: StyleReaderSafe a StyleProperties-readStyleProperties = liftA2 SProps-                       ( readTextProperties >>> choiceToMaybe )-                       ( readParaProperties >>> choiceToMaybe )+readStyleProperties :: StyleReader StyleProperties+readStyleProperties = SProps <$> optional readTextProperties+                             <*> optional readParaProperties  ---readTextProperties :: StyleReader a TextProperties+readTextProperties :: StyleReader TextProperties readTextProperties =-  executeInSub NsStyle "text-properties" $ liftAsSuccess-    ( liftA6 PropT-       ( searchAttr   NsXSL_FO "font-style"  False isFontEmphasised )-       ( searchAttr   NsXSL_FO "font-weight" False isFontBold       )-       findPitch-       ( getAttr      NsStyle  "text-position"                      )-       readUnderlineMode-       readStrikeThroughMode-     )+  executeInSub NsStyle "text-properties" $+    PropT <$> searchAttr NsXSL_FO "font-style"  False isFontEmphasised+          <*> searchAttr NsXSL_FO "font-weight" False isFontBold+          <*> findPitch+          <*> getAttr    NsStyle  "text-position"+          <*> readUnderlineMode+          <*> readStrikeThroughMode   where isFontEmphasised = [("normal",False),("italic",True),("oblique",True)]         isFontBold = ("normal",False):("bold",True)                     :map ((,True) . tshow) ([100,200..900]::[Int]) -readUnderlineMode     :: StyleReaderSafe a (Maybe UnderlineMode)+readUnderlineMode     :: StyleReader (Maybe UnderlineMode) readUnderlineMode     = readLineMode "text-underline-mode"                                      "text-underline-style" -readStrikeThroughMode :: StyleReaderSafe a (Maybe UnderlineMode)+readStrikeThroughMode :: StyleReader (Maybe UnderlineMode) readStrikeThroughMode = readLineMode "text-line-through-mode"                                      "text-line-through-style" -readLineMode :: Text -> Text -> StyleReaderSafe a (Maybe UnderlineMode)-readLineMode modeAttr styleAttr = proc x -> do-  isUL <- searchAttr  NsStyle styleAttr False isLinePresent -< x-  mode <- lookupAttr' NsStyle  modeAttr                     -< x-  if isUL-    then case mode of-           Just m  -> returnA -< Just m-           Nothing -> returnA -< Just UnderlineModeNormal-    else              returnA -< Nothing+readLineMode :: Text -> Text -> StyleReader (Maybe UnderlineMode)+readLineMode modeAttr styleAttr = do+  isUL <- searchAttr  NsStyle styleAttr False isLinePresent+  mode <- lookupAttr' NsStyle  modeAttr+  return $ if isUL+             then mode <|> Just UnderlineModeNormal+             else Nothing   where     isLinePresent = ("none",False) : map (,True)                     [ "dash"      , "dot-dash" , "dot-dot-dash" , "dotted"@@ -501,20 +472,16 @@                     ]  ---readParaProperties :: StyleReader a ParaProperties+readParaProperties :: StyleReader ParaProperties readParaProperties =-   executeInSub NsStyle "paragraph-properties" $ liftAsSuccess-     ( liftA3 PropP-       ( liftA2 readNumbering-         ( isSet'           NsText   "number-lines"           )-         ( readAttr'        NsText   "line-number"            )-       )-       ( liftA2 readIndentation-         ( isSetWithDefault NsStyle  "auto-text-indent" False )-         ( getAttr          NsXSL_FO "text-indent"            )-       )-       (   getAttr          NsXSL_FO "margin-left"            )-     )+  executeInSub NsStyle "paragraph-properties" $+    PropP <$> ( readNumbering+                  <$> isSet'    NsText "number-lines"+                  <*> readAttr' NsText "line-number" )+          <*> ( readIndentation+                  <$> isSetWithDefault NsStyle  "auto-text-indent" False+                  <*> getAttr          NsXSL_FO "text-indent" )+          <*> getAttr NsXSL_FO "margin-left"   where readNumbering (Just True) (Just n) = NumberingRestart n         readNumbering (Just True)  _       = NumberingKeep         readNumbering      _       _       = NumberingNone@@ -527,39 +494,37 @@ ----  ---readListStyle :: StyleReader a (StyleName, ListStyle)-readListStyle =-       findAttr NsStyle "name"-  >>?! keepingTheValue-       ( liftA ListStyle-         $ liftA3 SM.union3-             ( readListLevelStyles NsText "list-level-style-number" LltNumbered )-             ( readListLevelStyles NsText "list-level-style-bullet" LltBullet   )-             ( readListLevelStyles NsText "list-level-style-image"  LltImage    ) >>^ M.mapMaybe chooseMostSpecificListLevelStyle-       )+readListStyle :: StyleReader (StyleName, ListStyle)+readListStyle = do+  name   <- findAttr NsStyle "name"+  styles <- SM.union3+              <$> readListLevelStyles NsText "list-level-style-number" LltNumbered+              <*> readListLevelStyles NsText "list-level-style-bullet" LltBullet+              <*> readListLevelStyles NsText "list-level-style-image"  LltImage+  return ( name+         , ListStyle $ M.mapMaybe chooseMostSpecificListLevelStyle styles )+ -- readListLevelStyles :: Namespace -> ElementName                     -> ListLevelType-                    -> StyleReaderSafe a (SM.SetMap Int ListLevelStyle)+                    -> StyleReader (SM.SetMap Int ListLevelStyle) readListLevelStyles namespace elementName levelType =-  tryAll namespace elementName (readListLevelStyle levelType)-    >>^ SM.fromList+  SM.fromList <$> tryAll namespace elementName (readListLevelStyle levelType)  ---readListLevelStyle :: ListLevelType -> StyleReader a (Int, ListLevelStyle)-readListLevelStyle levelType =      readAttr NsText "level"-                               >>?! keepingTheValue-                                    ( liftA5 toListLevelStyle-                                      ( returnV       levelType             )-                                      ( findAttr'     NsStyle "num-prefix"  )-                                      ( findAttr'     NsStyle "num-suffix"  )-                                      ( getAttr       NsStyle "num-format"  )-                                      ( findAttrText' NsText  "start-value" )-                                    )+readListLevelStyle :: ListLevelType -> StyleReader (Int, ListLevelStyle)+readListLevelStyle levelType = do+  level <- readAttr NsText "level"+  style <- toListLevelStyle+             <$> findAttr' NsStyle "num-prefix"+             <*> findAttr' NsStyle "num-suffix"+             <*> getAttr   NsStyle "num-format"+             <*> findAttr' NsText  "start-value"+  return (level, style)   where-  toListLevelStyle _ p s LinfNone b         = ListLevelStyle LltBullet p s LinfNone (startValue b)-  toListLevelStyle _ p s f@(LinfString _) b = ListLevelStyle LltBullet p s f (startValue b)-  toListLevelStyle t p s f b                = ListLevelStyle t      p s f (startValue b)+  toListLevelStyle p s LinfNone b         = ListLevelStyle LltBullet p s LinfNone (startValue b)+  toListLevelStyle p s f@(LinfString _) b = ListLevelStyle LltBullet p s f (startValue b)+  toListLevelStyle p s f b                = ListLevelStyle levelType p s f (startValue b)   startValue mbx = fromMaybe 1 (mbx >>= safeRead)  --@@ -605,7 +570,7 @@ parents               :: Style       -> Styles ->      [Style] parents style styles = L.unfoldr findNextParent style -- Ha!   where findNextParent Style{..}-          = fmap duplicate $ (`lookupStyle` styles) =<< styleParentName+          = fmap (\p -> (p, p)) $ (`lookupStyle` styles) =<< styleParentName  -- | Looks up the style family of the current style. Normally, every style -- should have one. But if not, all parents are searched.
src/Text/Pandoc/Readers/Org/BlockStarts.hs view
@@ -24,6 +24,7 @@   ) where  import Control.Monad (void, guard)+import Data.Char (isAlphaNum, isDigit) import Data.Text (Text) import Text.Pandoc.Readers.Org.Parsing import Text.Pandoc.Definition as Pandoc@@ -65,7 +66,7 @@   pure name  where    latexEnvName :: Monad m => OrgParser m Text-   latexEnvName = try $ mappend <$> many1Char alphaNum <*> option "" (textStr "*")+   latexEnvName = try $ mappend <$> takeWhile1P isAlphaNum <*> option "" (textStr "*")  listCounterCookie :: Monad m => OrgParser m Int listCounterCookie = try $@@ -73,7 +74,7 @@   *> parseNum   <* char ']'   <* (skipSpaces <|> lookAhead eol)-  where parseNum = (safeRead =<< many1Char digit)+  where parseNum = (safeRead =<< takeWhile1P isDigit)                    <|> snd <$> (lowerAlpha <|> upperAlpha)  bulletListStart :: Monad m => OrgParser m Int@@ -93,7 +94,7 @@   ind <- length <$> many spaceChar   fancy <- option False $ True <$ guardEnabled Ext_fancy_lists   -- Ordered list markers allowed in org-mode-  let styles = (many1Char digit $> (if fancy+  let styles = (takeWhile1P isDigit $> (if fancy                                        then Decimal                                        else DefaultStyle))                : if fancy
src/Text/Pandoc/Readers/Org/Blocks.hs view
@@ -31,11 +31,11 @@ import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.Options-import Text.Pandoc.Shared (compactify, compactifyDL, safeRead)+import Text.Pandoc.Shared (compactify, compactifyDL, safeRead, compactifyTable)  import Control.Monad (foldM, guard, mzero, void) import Data.Bifunctor (bimap)-import Data.Char (isSpace)+import Data.Char (isAlphaNum, isDigit, isSpace) import Data.Default (Default) import Data.Functor (($>)) import qualified Data.List as L@@ -162,7 +162,8 @@   manyTill ((,) <$> key <*> value) newline  where    key :: Monad m => OrgParser m Text-   key = try $ skipSpaces *> char ':' *> many1Char nonspaceChar+   key = try $ skipSpaces *> char ':' *>+           takeWhile1P (\c -> c /= ' ' && c /= '\t' && c /= '\n' && c /= '\r')     value :: Monad m => OrgParser m Text    value = skipSpaces *> manyTillChar anyChar endOfValue@@ -212,7 +213,7 @@      skipSpaces      metaLineStart      stringAnyCase "begin_"-     many1Char (satisfy (not . isSpace))+     takeWhile1P (not . isSpace)  admonitionBlock :: PandocMonad m                 => Text -> BlockAttributes -> Text -> OrgParser m (F Blocks)@@ -273,11 +274,15 @@    rawLine :: Monad m => OrgParser m Text    rawLine = try $ ("" <$ blankline) <|> anyLine +   -- Remove one comma from lines consisting of any number of commas+   -- followed by "*" or "#+", like Emacs' org-unescape-code-in-region.    commaEscaped suff = case T.uncons suff of      Just (',', cs)-       | "*"  <- T.take 1 cs -> cs-       | "#+" <- T.take 2 cs -> cs-     _                       -> suff+       | isEscaped cs -> cs+     _                -> suff+    where+     isEscaped cs = let cs' = T.dropWhile (== ',') cs+                    in "*" `T.isPrefixOf` cs' || "#+" `T.isPrefixOf` cs'  -- | Read but ignore all remaining block headers. ignHeaders :: Monad m => OrgParser m ()@@ -425,7 +430,7 @@ -- | Reads a line number switch option. The line number switch can be used with -- example and source blocks. lineNumberSwitch :: Monad m => OrgParser m (Char, Maybe Text, SwitchPolarity)-lineNumberSwitch = genericSwitch 'n' (manyChar digit)+lineNumberSwitch = genericSwitch 'n' (takeWhileP isDigit)  blockOption :: Monad m => OrgParser m (Text, Text) blockOption = try $ do@@ -437,7 +442,7 @@ orgParamValue = try $   skipSpaces     *> notFollowedBy orgArgKey-    *> ((char '"' *> manyChar (noneOf "\n\r\"") <* char '"') <|>+    *> ((char '"' *> takeWhileP (`notElem` ("\n\r\"" :: [Char])) <* char '"') <|>         noneOf "\n\r" `many1TillChar` endOfValue)     <* skipSpaces  where@@ -579,7 +584,7 @@ include = try $ do   metaLineStart <* stringAnyCase "include:" <* skipSpaces   filename <- includeTarget-  includeArgs <- many (try $ skipSpaces *> many1Char alphaNum)+  includeArgs <- many (try $ skipSpaces *> takeWhile1P isAlphaNum)   params <- keyValues   blocksParser <- case includeArgs of       ("example" : _) -> return $ pure . B.codeBlock <$> parseRaw@@ -608,7 +613,7 @@     manyTill (noneOf "\n\r\t") (char '"')    parseRaw :: PandocMonad m => OrgParser m Text-  parseRaw = manyChar anyChar+  parseRaw = takeWhileP (const True)    blockFilter :: [(Text, Text)] -> [Block] -> [Block]   blockFilter params blks =@@ -701,8 +706,8 @@                                 pure $ B.simpleCaption . B.plain $ ils'                     let attr = (fromMaybe mempty identMb, [],                                  blockAttrKeyValues blockAttrs)-                    pure $ B.tableWith attr capt cs th tb tf-                  _ -> tbl   -- should not happen+                    pure $ compactifyTable $ B.tableWith attr capt cs th tb tf+                  _ -> compactifyTable <$> tbl   -- should not happen               else mempty  -- | A normal org table@@ -718,7 +723,8 @@   let totalWidth = if any (isJust . columnRelWidth) colProps                    then Just . sum $ map (fromMaybe 1 . columnRelWidth) colProps                    else Nothing-  in B.tableWith nullAttr (Caption Nothing mempty)+  in compactifyTable $+     B.tableWith nullAttr (Caption Nothing mempty)                  (map (convertColProp totalWidth) colProps)                  (TableHead nullAttr $ toHeaderRow heads)                  [TableBody nullAttr 0 [] $ map toRow lns]@@ -762,7 +768,7 @@                  <$> (skipSpaces                       *> char '<'                       *> optionMaybe tableAlignFromChar)-                 <*> (optionMaybe (many1Char digit >>= safeRead)+                 <*> (optionMaybe (takeWhile1P isDigit >>= safeRead)                       <* char '>'                       <* emptyOrgCell) @@ -782,8 +788,10 @@  rowsToTable :: [OrgTableRow]             -> F OrgTable-rowsToTable = foldM rowToContent emptyTable+rowsToTable = fmap unreverseRows . foldM rowToContent emptyTable  where emptyTable = OrgTable mempty mempty mempty+       -- Rows are accumulated in reverse order (see 'appendToBody').+       unreverseRows tbl = tbl{ orgTableRows = reverse (orgTableRows tbl) }  normalizeTable :: OrgTable -> OrgTable normalizeTable (OrgTable colProps heads rows) =@@ -821,10 +829,9 @@    appendToBody :: F [Blocks] -> F OrgTable    appendToBody frow = do      newRow <- frow-     let oldRows = orgTableRows tbl-     -- NOTE: This is an inefficient O(n) operation.  This should be changed-     -- if performance ever becomes a problem.-     return tbl{ orgTableRows = oldRows ++ [newRow] }+     -- Rows are prepended to avoid quadratic behavior on long tables;+     -- the final list is reversed in 'rowsToTable'.+     return tbl{ orgTableRows = newRow : orgTableRows tbl }   --
src/Text/Pandoc/Readers/Org/ExportSettings.hs view
@@ -24,7 +24,7 @@  -- | Read and handle space separated org-mode export settings. exportSettings :: PandocMonad m => OrgParser m ()-exportSettings = void $ sepBy skipSpaces exportSetting+exportSettings = void $ sepEndBy exportSetting skipSpaces  -- | Setter function for export settings. type ExportSettingSetter a = a -> ExportSettings -> ExportSettings@@ -32,7 +32,7 @@ -- | Read and process a single org-mode export option. exportSetting :: PandocMonad m => OrgParser m () exportSetting = choice-  [ booleanSetting "^" (\val es -> es { exportSubSuperscripts = val })+  [ subSupSetting "^" (\val es -> es { exportSubSuperscripts = val })   , booleanSetting "'" (\val es -> es { exportSmartQuotes = val })   , booleanSetting "*" (\val es -> es { exportEmphasizedText = val })   , booleanSetting "-" (\val es -> es { exportSpecialStrings = val })@@ -143,6 +143,22 @@      char '"'        *> manyTillChar alphaNum (char '"') +-- | Parses either @t@, @{}@, or @nil@ into a 'SubSupOption' value.+subSupSetting :: Monad m+              => Text+              -> ExportSettingSetter SubSupOption+              -> OrgParser m ()+subSupSetting = genericExportSetting $ subSupBraced <|> subSupBoolean+ where+   subSupBraced = SubSupBraced <$ optionString "{}"++   subSupBoolean = try $ do+     exportBool <- elispBoolean+     return $+       if exportBool+       then SubSupAll+       else SubSupNone+ -- | Parses either @t@, @nil@, or @verbatim@ into a 'TeXExport' value. texSetting :: Monad m            => Text@@ -166,7 +182,7 @@ -- | Read any setting string, but ignore it and emit a warning. ignoreAndWarn :: PandocMonad m => OrgParser m () ignoreAndWarn = try $ do-  opt <- many1Char nonspaceChar+  opt <- takeWhile1P (\c -> c /= ' ' && c /= '\t' && c /= '\n' && c /= '\r')   report (UnknownOrgExportOption opt)   return () 
src/Text/Pandoc/Readers/Org/Inlines.hs view
@@ -36,6 +36,7 @@ import Control.Monad.Trans (lift) import Data.Char (isAlphaNum, isSpace) import qualified Data.Map as M+import qualified Data.Set as Set import Data.Text (Text) import qualified Data.Text as T @@ -124,7 +125,7 @@  str :: PandocMonad m => OrgParser m (F Inlines) str = return . B.str <$>-      ( many1Char (noneOf $ specialChars ++ "\n\r ") >>= updatePositions' )+      ( takeWhile1P (`notElem` (specialChars ++ "\n\r ")) >>= updatePositions' )       <* updateLastStrPos   where     updatePositions' str' = str' <$@@ -222,7 +223,7 @@ orgCiteKey :: PandocMonad m => OrgParser m Text orgCiteKey = do   char '@'-  T.pack <$> many1 (satisfy orgCiteKeyChar)+  takeWhile1P orgCiteKeyChar  orgCiteKeyChar :: Char -> Bool orgCiteKeyChar c =@@ -409,7 +410,7 @@ inlineNote :: PandocMonad m => OrgParser m (F Inlines) inlineNote = try $ do   string "[fn:"-  ref <- manyChar alphaNum+  ref <- takeWhileP (\c -> isAlphaNum c || c == '-' || c == '_')   char ':'   note <- fmap B.para . trimInlinesF . mconcat <$> many1Till inline (char ']')   unless (T.null ref) $@@ -501,7 +502,7 @@ internalLink :: Text -> Inlines -> F Inlines internalLink link title = do   ids <- asksF orgStateAnchorIds-  if link `elem` ids+  if link `Set.member` ids     then return $ B.link ("#" <> link) "" title     else let attr' = ("", ["spurious-link"] , [("target", link)])          in return $ B.spanWith attr' (B.emph title)@@ -547,7 +548,7 @@    orgInlineParamValue = try $      skipSpaces        *> notFollowedBy (char ':')-       *> many1Char (noneOf "\t\n\r ]")+       *> takeWhile1P (`notElem` ("\t\n\r ]" :: [Char]))        <* skipSpaces  @@ -801,21 +802,26 @@  -- | Read a sub- or superscript expression subOrSuperExpr :: PandocMonad m => OrgParser m (F Inlines)-subOrSuperExpr = try $-  simpleSubOrSuperText <|>-  (choice [ charsInBalanced '{' '}' (T.singleton <$> noneOf "\n\r")-          , enclosing ('(', ')') <$> charsInBalanced '(' ')' (T.singleton <$> noneOf "\n\r")-          ] >>= parseFromString (mconcat <$> many inline))- where enclosing (left, right) s = T.cons left $ T.snoc s right+subOrSuperExpr = try $ do+  subSupOption <- getExportSetting exportSubSuperscripts+  case subSupOption of+    SubSupNone   -> mzero+    SubSupBraced -> bracedText+    SubSupAll    -> simpleSubOrSuperText <|> bracedText <|> parenText+ where+   bracedText = charsInBalanced '{' '}' (T.singleton <$> noneOf "\n\r")+                >>= parseFromString (mconcat <$> many inline)+   parenText = (enclosing ('(', ')') <$>+                charsInBalanced '(' ')' (T.singleton <$> noneOf "\n\r"))+               >>= parseFromString (mconcat <$> many inline)+   enclosing (left, right) s = T.cons left $ T.snoc s right  simpleSubOrSuperText :: PandocMonad m => OrgParser m (F Inlines)-simpleSubOrSuperText = try $ do-  state <- getState-  guard . exportSubSuperscripts . orgStateExportSettings $ state+simpleSubOrSuperText = try $   return . B.str <$>     choice [ textStr "*"            , mappend <$> option "" (T.singleton <$> oneOf "+-")-                     <*> many1Char alphaNum+                     <*> takeWhile1P isAlphaNum            ]  inlineLaTeX :: PandocMonad m => OrgParser m (F Inlines)@@ -891,7 +897,7 @@   recursionDepth <- orgStateMacroDepth <$> getState   guard $ recursionDepth < 15   string "{{{"-  name <- manyChar alphaNum+  name <- takeWhileP isAlphaNum   args <- ([] <$ string "}}}")           <|> char '(' *> argument `sepBy` char ',' <* eoa   expander <- lookupMacro name <$> getState@@ -922,7 +928,7 @@   guard =<< getExportSetting exportSpecialStrings   choice [orgDash, orgEllipses, shyHyphen]   where-    shyHyphen   = pure <$> (B.str "\173" <$ string "\\-") <* updatePositions '-'+    shyHyphen   = pure <$> (B.str "\173" <$ try (string "\\-")) <* updatePositions '-'     orgDash     = pure <$> dash <* updatePositions '-'     orgEllipses = pure <$> ellipses <* updatePositions '.' 
src/Text/Pandoc/Readers/Org/Meta.hs view
@@ -30,6 +30,7 @@ import Text.Pandoc.URI (urlEncode)  import Control.Monad (mzero, void)+import Data.Char (isAlphaNum, isDigit) import Data.List (intercalate, intersperse) import Data.Map (Map) import Data.Maybe (fromMaybe)@@ -61,13 +62,13 @@  keywordLine :: PandocMonad m => OrgParser m () keywordLine = try $ do-  key   <- T.toLower <$> metaKey+  key   <- metaKey   case Map.lookup key keywordHandlers of     Nothing -> fail $ "Unknown keyword: " ++ T.unpack key     Just hd -> hd  metaKey :: Monad m => OrgParser m Text-metaKey = T.toLower <$> many1Char (noneOf ": \n\r")+metaKey = T.toLower <$> takeWhile1P (`notElem` (": \n\r" :: [Char]))                     <*  char ':'                     <*  skipSpaces @@ -175,7 +176,9 @@ -- | Parse a link type definition (like @wp https://en.wikipedia.org/wiki/@). addLinkFormatter :: Monad m => OrgParser m () addLinkFormatter = try $ do-  linkType <- T.cons <$> letter <*> manyChar (alphaNum <|> oneOf "-_") <* skipSpaces+  linkType <- T.cons <$> letter+              <*> takeWhileP (\c -> isAlphaNum c || c == '-' || c == '_')+              <* skipSpaces   formatter <- parseFormat   updateState $ \s ->     let fs = orgStateLinkFormatters s@@ -253,7 +256,7 @@  where    todoKeyword :: Monad m => OrgParser m Text    todoKeyword = do-     keyword <- many1Char nonspaceChar+     keyword <- takeWhile1P (\c -> c /= ' ' && c /= '\t' && c /= '\n' && c /= '\r')      let cleanKeyword = T.takeWhile (/= '(') keyword      skipSpaces      return cleanKeyword@@ -278,14 +281,15 @@  macroDefinition :: Monad m => OrgParser m (Text, [Text] -> Text) macroDefinition = try $ do-  macroName <- many1Char nonspaceChar <* skipSpaces+  macroName <- takeWhile1P (\c -> c /= ' ' && c /= '\t' && c /= '\n' && c /= '\r')+                <* skipSpaces   firstPart <- expansionPart   (elemOrder, parts) <- unzip <$> many ((,) <$> placeholder <*> expansionPart)   let expander = mconcat . alternate (firstPart:parts) . reorder elemOrder   return (macroName, expander)  where   placeholder :: Monad m => OrgParser m Int-  placeholder = try . fmap (fromMaybe 1 . safeRead) $ char '$' *> many1Char digit+  placeholder = try . fmap (fromMaybe 1 . safeRead) $ char '$' *> takeWhile1P isDigit    expansionPart :: Monad m => OrgParser m Text   expansionPart = try $ manyChar (notFollowedBy placeholder *> noneOf "\n\r")
src/Text/Pandoc/Readers/Org/ParserState.hs view
@@ -35,6 +35,7 @@   , returnF   , ExportSettings (..)   , ArchivedTreesOption (..)+  , SubSupOption (..)   , TeXExport (..)   , optionsToParserState   ) where@@ -91,7 +92,7 @@  -- | Org-mode parser state data OrgParserState = OrgParserState-  { orgStateAnchorIds            :: [Text]+  { orgStateAnchorIds            :: Set.Set Text   , orgStateEmphasisCharStack    :: [Char]   , orgStateEmphasisPreChars     :: [Char] -- ^ Chars allowed to occur before                                            -- emphasis; spaces and newlines are@@ -163,7 +164,7 @@  defaultOrgParserState :: OrgParserState defaultOrgParserState = OrgParserState-  { orgStateAnchorIds = []+  { orgStateAnchorIds = Set.empty   , orgStateEmphasisPreChars = "-\t ('\"{\x200B"   , orgStateEmphasisPostChars  = "-\t\n .,:!?;'\")}[\x200B"   , orgStateEmphasisCharStack = []@@ -240,6 +241,14 @@   | ArchivedTreesNoExport     -- ^ Exclude archived trees from exporting   | ArchivedTreesHeadlineOnly -- ^ Export only the headline, discard the contents +-- | Options for the handling of TeX-like sub- and superscript syntax.+-- Represents allowed values of Emacs variable+-- @org-export-with-sub-superscripts@.+data SubSupOption+  = SubSupAll                 -- ^ Interpret all sub- and superscripts (@t@)+  | SubSupBraced              -- ^ Interpret only expressions in braces (@{}@)+  | SubSupNone                -- ^ Never interpret sub-/superscripts (@nil@)+ -- | Options for the handling of LaTeX environments and fragments. -- Represents allowed values of Emacs variable @org-export-with-latex@. data TeXExport@@ -261,7 +270,8 @@   , exportPreserveBreaks   :: Bool -- ^ Whether to preserve linebreaks   , exportSmartQuotes      :: Bool -- ^ Parse quotes smartly   , exportSpecialStrings   :: Bool -- ^ Parse ellipses and dashes smartly-  , exportSubSuperscripts  :: Bool -- ^ TeX-like syntax for sub- and superscripts+  , exportSubSuperscripts  :: SubSupOption+  -- ^ TeX-like syntax for sub- and superscripts   , exportWithAuthor       :: Bool -- ^ Include author in final meta-data   , exportWithCreator      :: Bool -- ^ Include creator in final meta-data   , exportWithEmail        :: Bool -- ^ Include email in final meta-data@@ -286,7 +296,7 @@   , exportPreserveBreaks = False   , exportSmartQuotes = False   , exportSpecialStrings = True-  , exportSubSuperscripts = True+  , exportSubSuperscripts = SubSupAll   , exportWithAuthor = True   , exportWithCreator = True   , exportWithEmail = True
src/Text/Pandoc/Readers/Org/Parsing.hs view
@@ -37,6 +37,8 @@   , manyChar   , many1Char   , manyTillChar+  , takeWhileP+  , takeWhile1P   , many1Till   , many1TillChar   , notFollowedBy'@@ -94,6 +96,7 @@   , try   , sepBy   , sepBy1+  , sepEndBy   , sepEndBy1   , endBy1   , option@@ -106,6 +109,7 @@   , getPosition   ) where +import qualified Data.Set as Set import Data.Text (Text) import Text.Pandoc.Readers.Org.ParserState @@ -223,7 +227,7 @@ orgAnchor :: Monad m => OrgParser m Text orgAnchor = try $ do   string "<<"-  anchorId <- many1Char (noneOf "\t\n\r<>\"' ")+  anchorId <- takeWhile1P (`notElem` ("\t\n\r<>\"' " :: [Char]))   string ">>"   skipSpaces   recordAnchorId anchorId@@ -231,4 +235,4 @@  recordAnchorId :: Monad m => Text -> OrgParser m () recordAnchorId i = updateState $ \s ->-  s{ orgStateAnchorIds = i : orgStateAnchorIds s }+  s{ orgStateAnchorIds = Set.insert i (orgStateAnchorIds s) }
src/Text/Pandoc/Readers/Org/Shared.hs view
@@ -32,7 +32,8 @@                        `elem` imageExtensions    isKnownProtocolUri = any (\x -> (x <> "://") `T.isPrefixOf` fp) protocols -   imageExtensions = [ ".jpeg", ".jpg", ".png", ".gif", ".svg", ".webp", ".jxl", ".avif" ]+   imageExtensions = [ ".jpeg", ".jpg", ".png", ".gif", ".svg", ".webp", ".jxl", ".avif",+                       ".pdf" ]    protocols = [ "file", "http", "https" ]  -- | Cleanup and canonicalize a string describing a link.  Return @Nothing@ if
src/Text/Pandoc/Readers/Pod.hs view
@@ -16,7 +16,7 @@  import Control.Monad (void) import Control.Monad.Except (throwError)-import Data.Char (isAsciiUpper, digitToInt)+import Data.Char (isAlphaNum, isAsciiUpper, isDigit, isLetter, digitToInt) import Data.Default (Default) import Text.Pandoc.Logging import Text.Pandoc.Options@@ -30,7 +30,7 @@ import qualified Text.Pandoc.Builder as B import qualified Data.Text as T import qualified Data.Text.Read as TR-import Text.Pandoc.Shared (stringify, textToIdentifier, tshow)+import Text.Pandoc.Shared (stringifyInlines, textToIdentifier, tshow) import Data.Set (Set) import Data.Functor (($>)) import Data.Maybe (listToMaybe, fromMaybe)@@ -210,7 +210,7 @@   lookAhead (char '<')   case ctrl of     'B' -> B.strong <$> argument-    'C' -> B.code . stringify <$> argument+    'C' -> B.code . stringifyInlines <$> argument     'F' -> B.spanWith (mempty, ["filename"], mempty) <$> argument     'I' -> B.emph <$> argument     'S' -> argument  -- TODO map nbsps@@ -218,7 +218,7 @@     'Z' -> argument $> mempty      'E' -> do-      a <- stringify <$> argument+      a <- stringifyInlines <$> argument       case entity a of              -- per spec:              --   Pod parsers, when faced with some unknown "E<identifier>" code,@@ -294,9 +294,9 @@       let name' = fromMaybe (defaultLinkName dest) name in           case dest of             LinkUrl _ href -> B.link href "" name'-            LinkMan nm Nothing ->  B.linkWith (mempty, mempty, [("manual", stringify nm)]) "" "" name'-            LinkMan nm (Just sc) -> B.linkWith (mempty, mempty, [("manual", stringify nm), ("section", stringify sc)]) "" "" name'-            LinkInternal sc -> B.link ("#" <> identifier (stringify sc)) "" name'+            LinkMan nm Nothing ->  B.linkWith (mempty, mempty, [("manual", stringifyInlines nm)]) "" "" name'+            LinkMan nm (Just sc) -> B.linkWith (mempty, mempty, [("manual", stringifyInlines nm), ("section", stringifyInlines sc)]) "" "" name'+            LinkInternal sc -> B.link ("#" <> identifier (stringifyInlines sc)) "" name'      linkName sp ex = optionMaybe $ try $ many         (try format@@ -312,12 +312,12 @@     -- manual page reference, so what we are doing here is roughly equivalent     -- even though it is nonsense     url ex = do-      scheme <- many1Char (letter <|> digit <|> char '_')+      scheme <- takeWhile1P (\c -> isLetter c || isDigit c || c == '_')       colon <- T.singleton <$> char ':' <* notFollowedBy (char ':')       rst <- many (format <|> B.str <$> many1Char (podCharLess ex))       return $ LinkUrl                  (B.str scheme <> B.str colon <> mconcat rst)-                 (scheme <> colon <> stringify rst)+                 (scheme <> colon <> stringifyInlines (mconcat rst))     quotedSection sp close ex = do       let mystr = B.str <$> many1Char (podCharLess ('\"':ex) <|> try (char '"' <* notFollowedBy close))       char '"'@@ -353,7 +353,7 @@  nonEmptyLine :: PandocMonad m => PodParser m T.Text nonEmptyLine = try $ do-  pre <- manyChar spaceChar+  pre <- takeWhileP (\c -> c == ' ' || c == '\t')   something <- T.singleton <$> nonspaceChar   post <- anyLineNewline   return $ pre <> something <> post@@ -368,7 +368,7 @@   optional blanklines   return $ B.codeBlock $ mconcat $ start:lns   where-    startVerbatimLine = many1Char spaceChar <> nonEmptyLine+    startVerbatimLine = takeWhile1P (\c -> c == ' ' || c == '\t') <> nonEmptyLine  -- =begin/=end/=for and data paragraphs -- The =begin/=end (and single-paragraph =for variant) markers in Pod are@@ -390,7 +390,7 @@ -- structure. It seems unlikely this would be encountered in the wild.  regionIdentifier :: PandocMonad m => PodParser m T.Text-regionIdentifier = many1Char (alphaNum <|> oneOf "-_")+regionIdentifier = takeWhile1P (\c -> isAlphaNum c || c == '-' || c == '_')  for :: PandocMonad m => PodParser m Blocks for = do
src/Text/Pandoc/Readers/RST.hs view
@@ -18,10 +18,11 @@ import Control.Monad (forM_, guard, liftM, mplus, mzero, when, unless, void) import Control.Monad.Except (throwError) import Control.Monad.Identity (Identity (..))-import Data.Char (isHexDigit, isSpace, toUpper, isAlphaNum, generalCategory,+import Data.Char (isDigit, isHexDigit, isSpace, toUpper, isAlphaNum,+                  generalCategory,                   GeneralCategory(OpenPunctuation, InitialQuote, FinalQuote,                                   DashPunctuation, OtherSymbol))-import Data.List (deleteFirstsBy, elemIndex, partition, sort, transpose)+import Data.List (elemIndex, partition, sort, transpose) import qualified Data.Map as M import Data.Maybe (fromMaybe, maybeToList, isJust, isNothing, catMaybes) import Data.Sequence (ViewR (..), viewr)@@ -75,8 +76,35 @@ underlineChars = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"  -- treat these as potentially non-text when parsing inline:-specialChars :: [Char]-specialChars = "\\`|*_<>$:/[]{}()-.\"'\8216\8217\8220\8221"+-- (equivalent to the character class "\\`|*_<>$:/[]{}()-.\"'\8216\8217\8220\8221")+isSpecialChar :: Char -> Bool+isSpecialChar c =+  case c of+    '\\' -> True+    '`'  -> True+    '|'  -> True+    '*'  -> True+    '_'  -> True+    '<'  -> True+    '>'  -> True+    '$'  -> True+    ':'  -> True+    '/'  -> True+    '['  -> True+    ']'  -> True+    '{'  -> True+    '}'  -> True+    '('  -> True+    ')'  -> True+    '-'  -> True+    '.'  -> True+    '"'  -> True+    '\'' -> True+    '\8216' -> True+    '\8217' -> True+    '\8220' -> True+    '\8221' -> True+    _    -> False  -- -- parsing documents@@ -121,8 +149,10 @@ metaFromDefList ds meta = adjustAuthors $ foldr f meta ds  where f (k,v) =          case v of-           [[Plain ils]] ->  setMeta (T.toLower (stringify k)) $ MetaInlines ils-           _ -> setMeta (T.toLower (stringify k)) $ mconcat $ map fromList v+           [[Plain ils]] ->  setMeta (T.toLower (stringifyInlines k)) $+                                MetaInlines ils+           _ -> setMeta (T.toLower (stringifyInlines k)) $+                  mconcat $ map fromList v        adjustAuthors (Meta metamap) = Meta $ M.adjust splitAuthors "author"                                            $ M.mapKeys (\k ->                                                  if k == "authors"@@ -163,9 +193,15 @@   let (blocks', meta') = if standalone                             then titleTransform (blocks, meta)                             else (blocks, meta)-  let reversedNotes = stateNotes state-  updateState $ \s -> s { stateNotes = reverse reversedNotes }-  doc <- walkM resolveReferences =<<+  let notes = reverse $ stateNotes state+  -- Named notes are never removed, so we can look them up in a Map;+  -- auto-numbered notes are consumed in order of occurrence, so we+  -- keep them in a list (in stateNotes):+  let isAutoNote (r, _) = r == "*" || r == "#"+  let namedNotes = M.fromList $ reverse $ filter (not . isAutoNote) notes+        -- reverse, so that the first occurrence of a label wins+  updateState $ \s -> s { stateNotes = filter isAutoNote notes }+  doc <- walkM (resolveReferences namedNotes) =<<          walkM resolveBlockSubstitutions          (Pandoc meta' (blocks' ++ refBlock))   reportLogMessages@@ -187,22 +223,27 @@                    bls -> return $ Div nullAttr bls resolveBlockSubstitutions x = return x -resolveReferences :: PandocMonad m => Inline -> RSTParser m Inline-resolveReferences = resolveReferences' Set.empty+resolveReferences :: PandocMonad m+                  => M.Map Text Text  -- ^ named notes+                  -> Inline -> RSTParser m Inline+resolveReferences namedNotes = resolveReferences' namedNotes Set.empty -resolveReferences' :: PandocMonad m => Set.Set Key -> Inline -> RSTParser m Inline-resolveReferences' seen x@(Link _ ils (s,_))+resolveReferences' :: PandocMonad m+                   => M.Map Text Text -> Set.Set Key -> Inline+                   -> RSTParser m Inline+resolveReferences' namedNotes seen x@(Link _ ils (s,_))   | Just ref <- T.stripPrefix "##REF##" s = do       let isAnonKey (Key (T.uncons -> Just ('_',_))) = True           isAnonKey _                                = False       state <- getState       let keyTable = stateKeys state-      let anonKeys = sort $ filter isAnonKey $ M.keys keyTable       key <-  if ref == "_" -- anonymous key                 then-                  case anonKeys of-                    []    -> mzero -- TODO log?-                    (k:_) -> return k+                  -- anonymous keys are named _0000, _0001, ... so the+                  -- next one to use is the smallest key >= "_":+                  case M.lookupGE (Key "_") keyTable of+                    Just (k, _) | isAnonKey k -> return k+                    _                         -> mzero -- TODO log?                 else return $ toKey ref       if key `Set.member` seen          then do@@ -214,27 +255,31 @@            ((src,tit), attr) <- lookupKey [] key            when (isAnonKey key) $ updateState $ \st ->                                    st{ stateKeys = M.delete key keyTable }-           resolveReferences' (Set.insert key seen) (Link attr ils (src, tit))+           resolveReferences' namedNotes (Set.insert key seen)+             (Link attr ils (src, tit))   | Just ref <- T.stripPrefix "##NOTE##" s = do       state <- getState-      let notes = stateNotes state-      case lookup ref notes of+      let autoNotes = stateNotes state+      let mbnote = if ref == "*" || ref == "#" -- auto-numbered+                      -- consume the note, so the next auto-numbered+                      -- note doesn't get the same contents:+                      then case break ((== ref) . fst) autoNotes of+                             (xs, (_, raw) : ys) -> Just (raw, xs ++ ys)+                             _                   -> Nothing+                      else (\raw -> (raw, autoNotes)) <$>+                             M.lookup ref namedNotes+      case mbnote of         Nothing   -> do           pos <- getPosition           logMessage $ ReferenceNotFound ref pos           return x-        Just raw  -> do+        Just (raw, newnotes) -> do           -- We temporarily empty the note list while parsing the note,           -- so that we don't get infinite loops with notes inside notes...           -- Note references inside other notes are allowed in reST, but           -- not yet in this implementation.           updateState $ \st -> st{ stateNotes = [] }           contents <- parseFromString' parseBlocks raw-          let newnotes = if ref == "*" || ref == "#" -- auto-numbered-                            -- delete the note so the next auto-numbered note-                            -- doesn't get the same contents:-                            then deleteFirstsBy (==) notes [(ref,raw)]-                            else notes           updateState $ \st -> st{ stateNotes = newnotes }           return $ Note (B.toList contents)   | Just ref <- T.stripPrefix "##SUBST##" s = do@@ -255,9 +300,9 @@                    [Para [t]] -> return t                    [Para xs] -> return $ Span nullAttr xs                    bls -> return $ Span nullAttr $ blocksToInlines bls-                 resolveReferences' (Set.insert key seen) resolved+                 resolveReferences' namedNotes (Set.insert key seen) resolved   | otherwise = return x-resolveReferences' _ x = return x+resolveReferences' _ _ x = return x  parseCitation :: PandocMonad m               => (Text, Text) -> RSTParser m (Inlines, [Blocks])@@ -433,7 +478,7 @@         Nothing  -> (headerTable ++ [DoubleHeader c], length headerTable + 1)   setState (state { stateHeaderTable = headerTable' })   attr@(ident,_,_) <- registerHeader nullAttr txt-  let key = toKey (stringify txt)+  let key = toKey (stringifyInlines txt)   updateState $ \s ->     s { stateKeys = M.insert key (("#" <> ident,""), nullAttr) $ stateKeys s }   return $ B.headerWith attr level txt@@ -465,7 +510,7 @@         Nothing  -> (headerTable ++ [SingleHeader c], length headerTable + 1)   setState (state { stateHeaderTable = headerTable' })   attr@(ident,_,_) <- registerHeader nullAttr txt-  let key = toKey (stringify txt)+  let key = toKey (stringifyInlines txt)   updateState $ \s ->     s { stateKeys = M.insert key (("#" <> ident,""), nullAttr) $ stateKeys s }   return $ B.headerWith attr level txt@@ -473,7 +518,14 @@ singleHeader' :: PandocMonad m => RSTParser m (Inlines, Char) singleHeader' = try $ do   notFollowedBy' whitespace-  lookAhead $ anyLine >> oneOf underlineChars+  -- check that the next line is a full underline before committing+  -- to parsing the header text (otherwise we'd parse the first line+  -- of many paragraphs twice):+  lookAhead $ do+    anyLine+    c <- oneOf underlineChars+    skipMany (char c)+    blankline   txt <- trimInlines . mconcat <$> many1 (notFollowedBy blankline >> inline)   pos <- getPosition   let len = sourceColumn pos - 1@@ -1044,22 +1096,15 @@                  _ -> replicate numOfCols ColWidthDefault          let toRow = Row nullAttr . map B.simpleCell              toHeaderRow l = [toRow l | not (null l)]-         return $ B.table (B.simpleCaption $ B.plain title)+         return $ compactifyTable+                $ B.table (B.simpleCaption $ B.plain title)                           (zip (replicate numOfCols AlignDefault) widths)                           (TableHead nullAttr $ toHeaderRow headerRow)                           [TableBody nullAttr 0 [] $ map toRow bodyRows]                           (TableFoot nullAttr []) -singleParaToPlain :: Blocks -> Blocks-singleParaToPlain bs =-  case B.toList bs of-    [Para ils] -> B.fromList [Plain ils]-    _          -> bs- parseCell :: PandocMonad m => Text -> RSTParser m Blocks-parseCell t = singleParaToPlain-   <$> parseFromString' parseBlocks (trim t <> "\n\n")-+parseCell t = parseFromString' parseBlocks (trim t <> "\n\n")  -- TODO: --  - Only supports :format: fields with a single format for :raw: roles,@@ -1176,8 +1221,8 @@                                      then stripTrailingNewlines                                      else id           attribs = (ident, classes', kvs)-          classes' = lang-                    : ["numberLines" | isJust (lookup "number-lines" fields)]+          classes' =   [ lang | not (T.null lang) ]+                    ++ ["numberLines" | isJust (lookup "number-lines" fields)]                     ++ classes           kvs = [(k,v) | (k,v) <- fields, k /= "number-lines", k /= "class",                                           k /= "id", k /= "name"]@@ -1235,7 +1280,7 @@ noteMarker :: Monad m => RSTParser m Text noteMarker = do   char '['-  res <- many1Char digit+  res <- takeWhile1P isDigit       <|>                   try (char '#' >> liftM ("#" <>) simpleReferenceName)       <|> countChar 1 (oneOf "#*")@@ -1275,9 +1320,9 @@ targetURI = do   skipSpaces   optional $ try $ newline >> notFollowedBy blankline-  contents <- trim <$>-     many1Char (satisfy (/='\n')-     <|> try (newline >> many1 spaceChar >> noneOf " \t\n"))+  contents <- trim . mconcat <$>+     many1 (takeWhile1P (/='\n')+     <|> T.singleton <$> try (newline >> many1 spaceChar >> noneOf " \t\n"))   blanklines   return $ stripBackticks contents   where@@ -1514,7 +1559,8 @@   gridTableWith (Identity <$> parseBlocks)  table :: PandocMonad m => RSTParser m Blocks-table = gridTable <|> simpleTable False <|> simpleTable True <?> "table"+table = compactifyTable <$>+  (gridTable <|> simpleTable False <|> simpleTable True <?> "table")  -- -- inline@@ -1559,7 +1605,7 @@  symbol :: Monad m => RSTParser m Inlines symbol = do-  c <- oneOf specialChars+  c <- satisfy isSpecialChar   unless (canPrecedeOpener c) updateLastStrPos   return $ B.str $ T.singleton c @@ -1720,10 +1766,11 @@  str :: Monad m => RSTParser m Inlines str = do-  let strChar = noneOf ("\t\n " ++ specialChars)-  result <- many1Char strChar+  result <- takeWhile1P isStrChar   updateLastStrPos   return $ B.str result+ where+  isStrChar c = c /= '\t' && c /= '\n' && c /= ' ' && not (isSpecialChar c)  -- an endline character that can be treated as a space, not a structural break endline :: Monad m => RSTParser m Inlines@@ -1741,8 +1788,29 @@ --  link :: PandocMonad m => RSTParser m Inlines-link = choice [explicitLink, referenceLink, autoLink]  <?> "link"+link = do+  linkPossible+  choice [explicitLink, referenceLink, autoLink]  <?> "link" +-- Fail fast if no link parser can succeed here: each of them+-- requires one of the characters @`[_:\@@ before the next+-- whitespace ('`' for explicit links and quoted reference names,+-- '[' for citation names, '_' for reference links, ':' after the+-- scheme of a URI, '@' in an email address).  Checking this on the+-- raw input is much cheaper than running each link parser over the+-- next word only to have it fail.+linkPossible :: Monad m => RSTParser m ()+linkPossible = do+  Sources inps <- getInput+  case inps of+    [] -> mzero+    (_, t) : rest -> do+      let (w, t') = T.break (\c -> c == ' ' || c == '\t' || c == '\n') t+      guard $ (T.null t' && not (null rest))+                -- word may continue in next chunk: inconclusive+            || T.any (\c -> c == '`' || c == '[' || c == '_' ||+                            c == ':' || c == '@') w+ explicitLink :: PandocMonad m => RSTParser m Inlines explicitLink = try $ do   char '`'@@ -1763,7 +1831,7 @@   let label'' = if label' == mempty                    then B.str src                    else label'-  let key = toKey $ stringify label'+  let key = toKey $ stringifyInlines label'   unless (key == Key mempty) $ do     updateState $ \s -> s{       stateKeys = M.insert key ((src',""), nullAttr) $ stateKeys s }
src/Text/Pandoc/Readers/RTF.hs view
@@ -286,11 +286,11 @@     x <- hexDigit     y <- hexDigit     return $ hexToWord (T.pack [x,y])-  letterSequence = T.pack <$> many1 (satisfy (\c -> isAscii c && isLetter c))+  letterSequence = takeWhile1P (\c -> isAscii c && isLetter c)   unformattedText = do-    ts <-  filter (\c -> c /= '\r' && c /= '\n') <$>-           ( many1 (satisfy (\c -> not (isSpecial c) || c == '\r' || c == '\n')))-    return $! UnformattedText $! T.pack ts+    ts <-  T.filter (\c -> c /= '\r' && c /= '\n') <$>+           takeWhile1P (\c -> not (isSpecial c) || c == '\r' || c == '\n')+    return $! UnformattedText ts   grouped = do     char '{'     skipMany nl
src/Text/Pandoc/Readers/Roff.hs view
@@ -34,7 +34,7 @@ import Control.Monad.Except (throwError) import Text.Pandoc.Class.PandocMonad        (getResourcePath, readFileFromDirs, PandocMonad(..), report)-import Data.Char (isLower, toLower, toUpper, isAlphaNum)+import Data.Char (isLetter, isLower, toLower, toUpper, isAlphaNum) import Data.Default (Default) import qualified Data.Map as M import Data.List (intercalate)@@ -202,7 +202,7 @@   guard $ sourceColumn pos == 1 || afterConditional st   char '.' <|> char '\''   skipMany spacetab-  macroName <- manyChar (satisfy isAlphaNum)+  macroName <- takeWhileP isAlphaNum   case macroName of     "nop" -> return mempty     "ie"  -> lexConditional "ie"@@ -286,7 +286,7 @@  tableOption :: PandocMonad m => RoffLexer m TableOption tableOption = do-  k <- many1Char letter+  k <- takeWhile1P isLetter   v <- option "" $ try $ do          skipMany spacetab          char '('@@ -366,7 +366,7 @@ expression :: PandocMonad m => RoffLexer m (Maybe Bool) expression = do   raw <- charsInBalanced '(' ')' (T.singleton <$> (satisfy (/= '\n')))-      <|> many1Char nonspaceChar+      <|> takeWhile1P (\c -> c /= ' ' && c /= '\t' && c /= '\n' && c /= '\r')   returnValue $     case raw of       "1"  -> Just True
src/Text/Pandoc/Readers/Roff/Escape.hs view
@@ -13,7 +13,7 @@     ( PandocMonad(..), report, PandocMonad(..), report ) import Control.Monad     ( mzero, mplus, mzero, mplus )-import Data.Char (chr, isAscii, isAlphaNum)+import Data.Char (chr, isAscii, isAlphaNum, isDigit) import qualified Data.Map as M import qualified Data.Text as T import Text.Pandoc.Logging (LogMessage(..))@@ -205,7 +205,7 @@ signedNumber :: (PandocMonad m, RoffLikeLexer x) => Lexer m x T.Text signedNumber = try $ do   sign <- option "" ("-" <$ char '-' <|> "" <$ char '+')-  ds <- many1Char digit+  ds <- takeWhile1P isDigit   return (sign <> ds)  -- Parses: [..] or (..
src/Text/Pandoc/Readers/TWiki.hs view
@@ -28,7 +28,7 @@ import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (enclosed) import Text.Pandoc.Readers.HTML (htmlTag, isCommentTag)-import Text.Pandoc.Shared (tshow)+import Text.Pandoc.Shared (tshow, compactifyTable) import Text.Pandoc.XML (fromEntities)  -- | Read twiki from an input string and return a Pandoc document.@@ -217,7 +217,8 @@   return $ buildTable mempty rows $ fromMaybe (align rows, columns rows) thead   where     buildTable caption rows (aligns, heads)-                    = B.table (B.simpleCaption $ B.plain caption)+                    = compactifyTable $ B.table+                              (B.simpleCaption $ B.plain caption)                               aligns                               (TableHead nullAttr $ toHeaderRow heads)                               [TableBody nullAttr 0 [] $ map toRow rows]@@ -418,7 +419,8 @@              => TWParser m a -> TWParser m Text nestedString end = innerSpace <|> countChar 1 nonspaceChar   where-    innerSpace = try $ many1Char spaceChar <* notFollowedBy end+    innerSpace = try $ takeWhile1P (\c -> c == ' ' || c == '\t')+                        <* notFollowedBy end  boldCode :: PandocMonad m => TWParser m B.Inlines boldCode = try $ B.strong . B.code . fromEntities <$> enclosed (string "==") nestedString@@ -449,14 +451,15 @@       | otherwise = isAlphaNum c  str :: PandocMonad m => TWParser m B.Inlines-str = B.str <$> (many1Char alphaNum <|> characterReference)+str = B.str <$> (takeWhile1P isAlphaNum <|> characterReference)  nop :: PandocMonad m => TWParser m B.Inlines nop = try $ (void exclamation <|> void nopTag) >> followContent   where     exclamation   = char '!'     nopTag        = stringAnyCase "<nop>"-    followContent = B.str . fromEntities <$> many1Char nonspaceChar+    followContent = B.str . fromEntities <$>+      takeWhile1P (\c -> c /= ' ' && c /= '\t' && c /= '\n' && c /= '\r')  symbol :: PandocMonad m => TWParser m B.Inlines symbol = B.str <$> countChar 1 nonspaceChar
src/Text/Pandoc/Readers/Textile.hs view
@@ -56,7 +56,7 @@ import Text.Pandoc.Parsing import Text.Pandoc.Readers.HTML (htmlTag, isBlockTag, isInlineTag) import Text.Pandoc.Readers.LaTeX (rawLaTeXBlock, rawLaTeXInline)-import Text.Pandoc.Shared (trim, tshow)+import Text.Pandoc.Shared (trim, tshow, compactifyTable) import Text.Read (readMaybe)  -- | Parse a Textile text and return a Pandoc document.@@ -452,7 +452,8 @@                 transpose $ map (map (snd . fst)) (headers:rows)   let toRow = Row nullAttr . map B.simpleCell       toHeaderRow l = [toRow l | not (null l)]-  return $ B.table (B.simpleCaption $ B.plain caption)+  return $ compactifyTable+    $ B.table (B.simpleCaption $ B.plain caption)     (zip aligns (replicate nbOfCols ColWidthDefault))     (TableHead nullAttr $ toHeaderRow $ map snd headers)     [TableBody nullAttr 0 [] $ map (toRow . map snd) rows]@@ -679,7 +680,7 @@   let attr = case lookup "style" kvs of                Just stls -> (ident, cls, pickStylesToKVs ["width", "height"] stls)                Nothing   -> (ident, cls, kvs)-  src <- T.pack <$> many1 (noneOf " \t\n\r!(")+  src <- takeWhile1P (`notElem` (" \t\n\r!(" :: [Char]))   alt <- fmap T.pack $ option "" $ try $ char '(' *> manyTill anyChar (char ')')   char '!'   let img = B.imageWith attr src alt (B.str alt)
src/Text/Pandoc/Readers/TikiWiki.hs view
@@ -614,7 +614,7 @@ wikiLinkText :: PandocMonad m => Text -> Text -> Text -> TikiWikiParser m (Text, Text, Text) wikiLinkText start middle end = do   string (T.unpack start)-  url <- T.pack <$> many1 (noneOf $ T.unpack middle ++ "\n")+  url <- takeWhile1P (`notElem` (T.unpack middle ++ "\n"))   seg1 <- option url linkContent   seg2 <- option "" linkContent   string (T.unpack end)@@ -626,7 +626,7 @@   where     linkContent      = do       char '|'-      T.pack <$> many (noneOf $ T.unpack middle)+      takeWhileP (`notElem` T.unpack middle)  externalLink :: PandocMonad m => TikiWikiParser m B.Inlines externalLink = makeLink "[" "]|" "]"
src/Text/Pandoc/Readers/Txt2Tags.hs view
@@ -18,6 +18,7 @@ import Control.Monad (guard, void, when) import Control.Monad.Except (catchError, throwError) import Control.Monad.Reader (Reader, asks, runReader)+import Data.Char (isAlphaNum) import Data.Default import Data.List (intercalate, transpose) import Data.List.NonEmpty (nonEmpty)@@ -33,7 +34,7 @@ import Text.Pandoc.Definition import Text.Pandoc.Options import Text.Pandoc.Parsing hiding (space, spaces, uri)-import Text.Pandoc.Shared (compactify, compactifyDL)+import Text.Pandoc.Shared (compactify, compactifyDL, compactifyTable) import Text.Pandoc.URI (escapeURI)  type T2T = ParsecT Sources ParserState (Reader T2TMeta)@@ -132,7 +133,7 @@ setting :: T2T (Keyword, Value) setting = do   string "%!"-  keyword <- ignoreSpacesCap (many1Char alphaNum)+  keyword <- ignoreSpacesCap (takeWhile1P isAlphaNum)   char ':'   value <- ignoreSpacesCap (manyTillChar anyChar newline)   return (keyword, value)@@ -274,7 +275,8 @@   let headerPadded = if null tableHeader then mempty else pad size tableHeader   let toRow = Row nullAttr . map B.simpleCell       toHeaderRow l = [toRow l | not (null l)]-  return $ B.table B.emptyCaption+  return $ compactifyTable+         $ B.table B.emptyCaption                     (zip aligns (replicate ncolumns ColWidthDefault))                       (TableHead nullAttr $ toHeaderRow headerPadded)                       [TableBody nullAttr 0 [] $ map toRow rowsPadded]@@ -415,7 +417,7 @@              -> (Text -> a) -- Special Case to handle ******              -> T2T Inlines inlineMarkup p f c special = try $ do-  start <- many1Char (char c)+  start <- takeWhile1P (== c)   let l = T.length start   guard (l >= 2)   when (l == 2) (void $ notFollowedBy space)@@ -426,7 +428,7 @@   case body of     Just middle -> do       lastChar <- anyChar-      end <- many1Char (char c)+      end <- takeWhile1P (== c)       let parser inp = parseFromString' (mconcat <$> many p) inp       let start' = case T.drop 2 start of                           "" -> mempty@@ -518,7 +520,7 @@ t2tURI = do   start <- try ((<>) <$> proto <*> urlLogin) <|> guess   domain <- many1Char chars-  sep <- manyChar (char '/')+  sep <- takeWhileP (== '/')   form' <- option mempty (T.cons <$> char '?' <*> many1Char form)   anchor' <- option mempty (T.cons <$> char '#' <*> manyChar anchor)   return (start <> domain <> sep <> form' <> anchor')@@ -528,7 +530,7 @@     guess = (<>) <$> (((<>) <$> stringAnyCase "www" <*> option mempty (T.singleton <$> oneOf "23"))               <|> stringAnyCase "ftp") <*> (T.singleton <$> char '.')     login = alphaNum <|> oneOf "_.-"-    pass = manyChar (noneOf " @")+    pass = takeWhileP (\x -> x /= ' ' && x /= '@')     chars = alphaNum <|> oneOf "%._/~:,=$@&+-"     anchor = alphaNum <|> oneOf "%._0"     form = chars <|> oneOf ";*"@@ -572,7 +574,7 @@   return B.softbreak  str :: T2T Inlines-str = try $ B.str <$> many1Char (noneOf $ specialChars ++ "\n\r ")+str = try $ B.str <$> takeWhile1P (`notElem` (specialChars ++ "\n\r "))  whitespace :: T2T Inlines whitespace = try $ B.space <$ spaceChar
src/Text/Pandoc/Readers/Typst.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE RankNTypes #-} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE LambdaCase #-}@@ -30,14 +31,14 @@ import Typst ( parseTypst, evaluateTypst ) import Text.Pandoc.Error (PandocError(..)) import Text.Pandoc.Translations (Term(References), translateTerm)-import Text.Pandoc.Shared (tshow, blocksToInlines)+import Text.Pandoc.Shared (tshow, blocksToInlines, compactifyTable) import Text.Pandoc.Parsing (registerHeader, reportLogMessages) import Control.Monad.Except (throwError) import Control.Monad (MonadPlus (mplus), void, guard, foldM) import Control.Monad.Trans (lift) import qualified Data.Foldable as F import qualified Data.Map as M-import Data.Maybe (catMaybes, fromMaybe, isJust)+import Data.Maybe (catMaybes, fromMaybe, isJust, listToMaybe) import Data.Sequence (Seq) import qualified Data.Sequence as Seq import qualified Data.Set as Set@@ -96,7 +97,7 @@           ignored ("unknown block element " <> tname <>                    " at " <> tshow pos)           pure mempty-        Just handler -> handler pos mbident fields+        Just (BlockHandler handler) -> handler pos mbident fields     _ -> pure mempty  pInline :: PandocMonad m => P m B.Inlines@@ -110,54 +111,130 @@       , tname /= "math.equation" ->           B.math . writeTeX <$> pMathMany (Seq.singleton res)     Elt name@(Identifier tname) pos fields -> do-      labs <- sLabels <$> getState-      labelTarget <- (do result <- getField "target" fields-                         case result of-                           VLabel t | t `elem` labs -> pure True-                           _ -> pure False)-                  <|> pure False-      if tname == "ref" && not labelTarget-         then do-           -- @foo is a citation unless it links to a lab in the doc:-           let targetToKey (Identifier "target") = Identifier "key"-               targetToKey k = k-           case M.lookup "cite" inlineHandlers of-             Nothing -> do-               ignored ("unknown inline element " <> tname <>-                        " at " <> tshow pos)-               pure mempty-             Just handler -> handler pos Nothing (M.mapKeys targetToKey fields)+      let runInlineHandler =+            case M.lookup name inlineHandlers of+              Nothing -> do+                ignored ("unknown inline element " <> tname <>+                         " at " <> tshow pos)+                pure mempty+              Just (InlineHandler handler) -> handler pos Nothing fields+      if tname /= "ref"+         then runInlineHandler          else do-          case M.lookup name inlineHandlers of-            Nothing -> do-              ignored ("unknown inline element " <> tname <>-                       " at " <> tshow pos)-              pure mempty-            Just handler -> handler pos Nothing fields+           labs <- sLabels <$> getState+           labelTarget <- (do result <- getField "target" fields+                              case result of+                                VLabel t | t `Set.member` labs -> pure True+                                _ -> pure False)+                       <|> pure False+           if labelTarget+              then runInlineHandler+              else do+                -- @foo is a citation unless it links to a lab in the doc:+                let targetToKey (Identifier "target") = Identifier "key"+                    targetToKey k = k+                case M.lookup "cite" inlineHandlers of+                  Nothing -> do+                    ignored ("unknown inline element " <> tname <>+                             " at " <> tshow pos)+                    pure mempty+                  Just (InlineHandler handler) ->+                    handler pos Nothing (M.mapKeys targetToKey fields) --- Pull block elements out of inline elements, e.g.--- Elt "smallcaps" [ Elt "heading" [..] ] ->--- Elt "heading" [ Elt "smallcaps" [..]]. See #11017.+-- Ensure that inline elements contain only inline content: split+-- them at paragraph breaks, and pull block children out, applying the+-- element to a child's own contents (#11017, #11881).  A pandoc inline+-- cannot span paragraphs, so this is the closest structural rendering;+-- e.g. Elt "emph" [Txt "hi", parbreak, Txt "there"] becomes+-- Elt "emph" [Txt "hi"], parbreak, Elt "emph" [Txt "there"]. fixNesting :: Content -> Content-fixNesting el@(Elt name pos fields)-  | Just (VContent elts) <- M.lookup "body" fields-  = let elts' = fmap fixNesting elts-        fields' = M.insert "body" (VContent elts') fields-        in if isBlock el-              then Elt name pos fields'-              else case getField "body" fields' of-                        Just ([el'@(Elt name' pos' fields'')] :: Seq Content)-                          | isBlock el'-                          , not (isInline el')-                          , "body" `M.member` fields''-                          -> Elt name' pos' $-                               M.insert "body" (VContent-                                                (Seq.singleton-                                                  (Elt name pos fields'')))-                                        fields'-                        _ -> Elt name pos fields'+fixNesting el@(Elt name _ _)+  | Identifier tname <- name+  , "math." `T.isPrefixOf` tname = el   -- math has its own grammar+fixNesting (Elt name pos fields) = Elt name pos (M.map fixVal fields) fixNesting x = x +fixVal :: Val -> Val+fixVal (VContent cs) = VContent (fixSeq cs)+fixVal (VArray vs) = VArray (fmap fixVal vs)+fixVal (VTermItem t d) = VTermItem (fixSeq t) (fixSeq d)+fixVal v = v++fixSeq :: Seq Content -> Seq Content+fixSeq = foldMap expand . fmap fixNesting++-- Split an inline element whose body contains block content.+expand :: Content -> Seq Content+expand el@(Elt name pos fields)+  | isSplittable name+  , Just (field, VContent body) <- contentField fields+  , F.any isStrictlyBlock body+  = splitInlineBody name pos fields field body+  | otherwise = Seq.singleton el+expand x = Seq.singleton x++-- Whether the element's body is parsed with pInlines, and thus cannot+-- contain block content: anything without a block handler, except+-- footnote (body parsed as blocks), the block-body table elements, and+-- math elements.+isSplittable :: Identifier -> Bool+isSplittable name@(Identifier tname) =+  name `Set.notMember` blockKeys+    && name `Set.notMember` blockBodyElements+    && not ("math." `T.isPrefixOf` tname)++-- Elements without block handlers whose content is nonetheless parsed+-- as block content.+blockBodyElements :: Set.Set Identifier+blockBodyElements = Set.fromList+  [ "footnote", "grid.cell", "table.cell", "grid.header"+  , "table.header", "grid.footer", "table.footer" ]++-- Strictly block content, not consumable by 'pInline'.+isStrictlyBlock :: Content -> Bool+isStrictlyBlock c = isBlock c && not (isInline c)++-- The element's content field.  Only body and text: other content+-- fields, such as ref's supplement, are parameters rather than bodies.+contentField :: M.Map Identifier Val -> Maybe (Identifier, Val)+contentField fields =+  listToMaybe+    [ kv | kv@(k, VContent _) <- M.toAscList fields+         , k == Identifier "body" || k == Identifier "text" ]++-- Split the element's body at block content: inline runs are wrapped+-- back in the element, a parbreak separates paragraphs, and a block+-- child gets the element applied to its own body, if it has one.+splitInlineBody+  :: Identifier -> Maybe SourcePos -> M.Map Identifier Val+  -> Identifier -> Seq Content -> Seq Content+splitInlineBody name pos fields field =+  Seq.fromList . go [] . F.toList+ where+  wrap cs = Elt name pos (M.insert field (VContent (Seq.fromList cs)) fields)++  go run [] = flush run+  go run (c : cs)+    | isStrictlyBlock c = flush run ++ splitOff c ++ go [] cs+    | otherwise = go (c : run) cs++  flush run = [ wrap (reverse run) | not (null run) ]++  splitOff c+    | isParbreak c = [Elt "parbreak" pos mempty]+    | otherwise = case c of+        Elt bname bpos bfields+          | Just (VContent inner) <- M.lookup (Identifier "body") bfields ->+              [ Elt bname bpos+                  ( M.insert (Identifier "body")+                      (VContent (expand (wrap (F.toList inner))))+                      bfields ) ]+        _ -> [c]++isParbreak :: Content -> Bool+isParbreak (Elt "parbreak" _ _) = True+isParbreak _ = False+ pPandoc :: PandocMonad m => P m B.Pandoc pPandoc = do   Elt "document" _ fields <- pTok isDocument@@ -231,28 +308,35 @@ isInline Txt{} = True  blockKeys :: Set.Set Identifier-blockKeys = Set.fromList $ M.keys-  (blockHandlers :: M.Map Identifier-     (Maybe SourcePos -> Maybe Text ->-      M.Map Identifier Val -> P PandocPure B.Blocks))+blockKeys = Set.fromList $ M.keys blockHandlers  inlineKeys :: Set.Set Identifier-inlineKeys = Set.fromList $ M.keys-  (inlineHandlers :: M.Map Identifier-     (Maybe SourcePos -> Maybe Text ->-      M.Map Identifier Val -> P PandocPure B.Inlines))+inlineKeys = Set.fromList $ M.keys inlineHandlers -blockHandlers :: PandocMonad m =>-                   M.Map Identifier-                   (Maybe SourcePos -> Maybe Text ->-                    M.Map Identifier Val -> P m B.Blocks)+-- The handler maps are wrapped in newtypes with polymorphic fields so+-- that the maps themselves are monomorphic.  This guarantees that they+-- are constant applicative forms, constructed only once; a+-- @PandocMonad m => M.Map ...@ would be a function taking a typeclass+-- dictionary, liable to be rebuilt at each lookup.++newtype BlockHandler = BlockHandler+  (forall m. PandocMonad m+    => Maybe SourcePos -> Maybe Text -> M.Map Identifier Val+    -> P m B.Blocks)++newtype InlineHandler = InlineHandler+  (forall m. PandocMonad m+    => Maybe SourcePos -> Maybe Text -> M.Map Identifier Val+    -> P m B.Inlines)++blockHandlers :: M.Map Identifier BlockHandler blockHandlers = M.fromList-  [("text", \_ _ fields -> do+  [("text", BlockHandler $ \_ _ fields -> do       body <- getField "body" fields       -- sometimes text elements include para breaks       notFollowedBy $ void $ pWithContents pInlines body       pWithContents pBlocks body)-  ,("title", \_ _ fields -> do+  ,("title", BlockHandler $ \_ _ fields -> do       body <- getField "body" fields       case body of         VContent cs -> do@@ -260,16 +344,16 @@           updateState $ \s -> s{ sMeta = B.setMeta "title" ils (sMeta s) }           pure mempty         _ -> pure mempty)-  ,("box", \_ _ fields -> do+  ,("box", BlockHandler $ \_ _ fields -> do       body <- getField "body" fields       B.divWith ("", ["box"], []) <$> pWithContents pBlocks body)-  ,("heading", \_ mbident fields -> do+  ,("heading", BlockHandler $ \_ mbident fields -> do       body <- getField "body" fields       lev <- getField "level" fields <|> pure 1       ils <- pWithContents pInlines body       attr <- registerHeader (fromMaybe "" mbident,[],[]) ils       pure $ B.headerWith attr lev ils)-  ,("quote", \_ _ fields -> do+  ,("quote", BlockHandler $ \_ _ fields -> do       getField "block" fields >>= guard       body <- getField "body" fields >>= pWithContents pBlocks       attribution' <- getField "attribution" fields@@ -278,11 +362,11 @@                         else (\x -> B.para ("\x2014\xa0" <> x)) <$>                               (pWithContents pInlines attribution')       pure $ B.blockQuote $ body <> attribution)-  ,("list", \_ _ fields -> do+  ,("list", BlockHandler $ \_ _ fields -> do       children <- V.toList <$> getField "children" fields       B.bulletList <$> mapM (pWithContents pBlocks) children)-  ,("list.item", \_ _ fields -> getField "body" fields >>= pWithContents pBlocks)-  ,("enum", \_ _ fields -> do+  ,("list.item", BlockHandler $ \_ _ fields -> getField "body" fields >>= pWithContents pBlocks)+  ,("enum", BlockHandler $ \_ _ fields -> do       children <- V.toList <$> getField "children" fields       mbstart <- getField "start" fields       start <- case mbstart of@@ -311,8 +395,8 @@               _ -> (B.DefaultStyle, B.DefaultDelim)       let listAttr = (start, sty, delim)       B.orderedListWith listAttr <$> mapM (pWithContents pBlocks) children)-  ,("enum.item", \_ _ fields -> getField "body" fields >>= pWithContents pBlocks)-  ,("terms", \_ _ fields -> do+  ,("enum.item", BlockHandler $ \_ _ fields -> getField "body" fields >>= pWithContents pBlocks)+  ,("terms", BlockHandler $ \_ _ fields -> do       children <- V.toList <$> getField "children" fields       B.definitionList         <$> mapM@@ -324,38 +408,41 @@               _ -> pure (mempty, [])           )           children)-  ,("terms.item", \_ _ fields -> getField "body" fields >>= pWithContents pBlocks)-  ,("raw", \_ mbident fields -> do+  ,("terms.item", BlockHandler $ \_ _ fields -> getField "body" fields >>= pWithContents pBlocks)+  ,("raw", BlockHandler $ \_ mbident fields -> do       txt <- T.filter (/= '\r') <$> getField "text" fields       mblang <- getField "lang" fields       let attr = (fromMaybe "" mbident, maybe [] (\l -> [l]) mblang, [])       pure $ B.codeBlockWith attr txt)-  ,("parbreak", \_ _ _ -> pure mempty)-  ,("block", \_ mbident fields ->+  ,("parbreak", BlockHandler $ \_ _ _ -> pure mempty)+  ,("par", BlockHandler $ \_ mbident fields -> do+      maybe B.para (\ident -> B.divWith (ident, [], []) . B.para) mbident+        <$> (getField "body" fields >>= pWithContents pInlines))+  ,("block", BlockHandler $ \_ mbident fields ->       maybe id (\ident -> B.divWith (ident, [], [])) mbident         <$> (getField "body" fields >>= pWithContents pBlocks))-  ,("place", \_ _ fields -> do+  ,("place", BlockHandler $ \_ _ fields -> do       ignored "parameters of place"       getField "body" fields >>= pWithContents pBlocks)-  ,("columns", \_ _ fields -> do+  ,("columns", BlockHandler $ \_ _ fields -> do       (cnt :: Integer) <- getField "count" fields       B.divWith ("", ["columns-flow"], [("count", T.pack (show cnt))])         <$> (getField "body" fields >>= pWithContents pBlocks))-  ,("rect", \_ _ fields ->+  ,("rect", BlockHandler $ \_ _ fields ->       B.divWith ("", ["rect"], []) <$> (getField "body" fields >>= pWithContents pBlocks))-  ,("circle", \_ _ fields ->+  ,("circle", BlockHandler $ \_ _ fields ->       B.divWith ("", ["circle"], []) <$> (getField "body" fields >>= pWithContents pBlocks))-  ,("ellipse", \_ _ fields ->+  ,("ellipse", BlockHandler $ \_ _ fields ->       B.divWith ("", ["ellipse"], []) <$> (getField "body" fields >>= pWithContents pBlocks))-  ,("polygon", \_ _ fields ->+  ,("polygon", BlockHandler $ \_ _ fields ->       B.divWith ("", ["polygon"], []) <$> (getField "body" fields >>= pWithContents pBlocks))-  ,("square", \_ _ fields ->+  ,("square", BlockHandler $ \_ _ fields ->       B.divWith ("", ["square"], []) <$> (getField "body" fields >>= pWithContents pBlocks))-  ,("align", \_ _ fields -> do+  ,("align", BlockHandler $ \_ _ fields -> do       alignment <- getField "alignment" fields       B.divWith ("", [], [("align", repr alignment)])         <$> (getField "body" fields >>= pWithContents pBlocks))-  ,("stack", \_ _ fields -> do+  ,("stack", BlockHandler $ \_ _ fields -> do       (dir :: Direction) <- getField "dir" fields `mplus` pure Ltr       rawchildren <- getField "children" fields       children <-@@ -370,9 +457,9 @@         B.divWith ("", [], [("stack", repr (VDirection dir))]) $           mconcat $             map (B.divWith ("", [], [])) children)-  ,("grid", \_ mbident fields -> parseTable mbident fields)-  ,("table", \_ mbident fields -> parseTable mbident fields)-  ,("figure", \_ mbident fields -> do+  ,("grid", BlockHandler $ \_ mbident fields -> parseTable mbident fields)+  ,("table", BlockHandler $ \_ mbident fields -> parseTable mbident fields)+  ,("figure", BlockHandler $ \_ mbident fields -> do       body <- getField "body" fields >>= pWithContents pBlocks       (mbCaption :: Maybe (Seq Content)) <- getField "caption" fields       (caption :: B.Blocks) <- maybe mempty (pWithContents pBlocks) mbCaption@@ -382,14 +469,14 @@             (B.Table attr (B.Caption Nothing (B.toList caption)) colspecs thead tbodies tfoot)         _ -> B.figureWith (fromMaybe "" mbident, [], [])                           (B.Caption Nothing (B.toList caption)) body)-  ,("line", \_ _ fields ->+  ,("line", BlockHandler $ \_ _ fields ->       case ( M.lookup "start" fields               >> M.lookup "end" fields               >> M.lookup "angle" fields ) of         Nothing -> pure B.horizontalRule         _ -> pure mempty)-  ,("divider", \_ _ _fields -> pure B.horizontalRule)-  ,("numbering", \_ _ fields -> do+  ,("divider", BlockHandler $ \_ _ _fields -> pure B.horizontalRule)+  ,("numbering", BlockHandler $ \_ _ fields -> do       numStyle <- getField "numbering" fields       (nums :: V.Vector Integer) <- getField "numbers" fields       let toText v = fromMaybe "" $ fromVal v@@ -402,12 +489,12 @@                   Failure _ -> "?"               _ -> "?"       pure $ B.plain . B.text . mconcat . map toNum $ V.toList nums)-  ,("footnote.entry", \_ _ fields ->+  ,("footnote.entry", BlockHandler $ \_ _ fields ->       getField "body" fields >>= pWithContents pBlocks)-  ,("pad", \_ _ fields ->  -- ignore paddingy+  ,("pad", BlockHandler $ \_ _ fields ->  -- ignore paddingy       getField "body" fields >>= pWithContents pBlocks)-  ,("pagebreak", \_ _ _ -> pure $ B.divWith ("", ["page-break"], [("wrapper", "1")]) B.horizontalRule)-  ,("bibliography", \_ _ fields -> do+  ,("pagebreak", BlockHandler $ \_ _ _ -> pure $ B.divWith ("", ["page-break"], [("wrapper", "1")]) B.horizontalRule)+  ,("bibliography", BlockHandler $ \_ _ fields -> do       let getSources v = case v of                       VString t -> MetaString t                       VArray xs -> MetaList $ map getSources $ V.toList xs@@ -426,7 +513,7 @@                    _ -> Just . B.text <$> lift (translateTerm References)       let hdr = maybe mempty (B.header 1) mbTitle       pure $ hdr <> B.divWith ("refs", [], []) mempty)-  ,("rotate", \_ _ fields -> do+  ,("rotate", BlockHandler $ \_ _ fields -> do       body <- getField "body" fields >>= pWithContents pBlocks       let kvs = case M.lookup "angle" fields of                     Just (VAngle ang) -> [("angle", T.pack $ show ang)]@@ -434,11 +521,9 @@       pure $ B.divWith ("", ["rotate"], kvs) body)   ] -inlineHandlers :: PandocMonad m =>-    M.Map Identifier (Maybe SourcePos -> Maybe Text ->-                      M.Map Identifier Val -> P m B.Inlines)+inlineHandlers :: M.Map Identifier InlineHandler inlineHandlers = M.fromList-  [("ref", \_ _ fields -> do+  [("ref", InlineHandler $ \_ _ fields -> do       VLabel target <- getField "target" fields       supplement' <- getField "supplement" fields       supplement <- case supplement' of@@ -449,17 +534,17 @@                            pure $ B.text ("[" <> target <> "]")                       _ -> pure mempty       pure $ B.linkWith ("", ["ref"], []) ("#" <> target) "" supplement)-  ,("linebreak", \_ _ _ -> pure B.linebreak)-  ,("text", \_ _ fields -> do+  ,("linebreak", InlineHandler $ \_ _ _ -> pure B.linebreak)+  ,("text", InlineHandler $ \_ _ fields -> do       body <- getField "body" fields       (mbweight :: Maybe Text) <- getField "weight" fields       case mbweight of         Just "bold" -> B.strong <$> pWithContents pInlines body         _ -> pWithContents pInlines body)-  ,("raw", \_ _ fields -> B.code . T.filter (/= '\r') <$> getField "text" fields)-  ,("footnote", \_ _ fields ->+  ,("raw", InlineHandler $ \_ _ fields -> B.code . T.filter (/= '\r') <$> getField "text" fields)+  ,("footnote", InlineHandler $ \_ _ fields ->       B.note <$> (getField "body" fields >>= pWithContents pBlocks))-  ,("cite", \_ _ fields -> do+  ,("cite", InlineHandler $ \_ _ fields -> do       VLabel key <- getField "key" fields       (form :: Text) <- getField "form" fields <|> pure "normal"       let citation =@@ -468,44 +553,50 @@                 B.citationPrefix = mempty,                 B.citationSuffix = mempty,                 B.citationMode = case form of+                                    "prose" -> B.AuthorInText                                     "year" -> B.SuppressAuthor+                                    -- "author" and "full" have no pandoc+                                    -- equivalent; fall back to normal                                     _ -> B.NormalCitation,                 B.citationNoteNum = 0,                 B.citationHash = 0               }       pure $ B.cite [citation] (B.text $ "[" <> key <> "]"))-  ,("lower", \_ _ fields -> do+  ,("lower", InlineHandler $ \_ _ fields -> do       body <- getField "text" fields       walk (modString T.toLower) <$> pWithContents pInlines body)-  ,("upper", \_ _ fields -> do+  ,("upper", InlineHandler $ \_ _ fields -> do       body <- getField "text" fields       walk (modString T.toUpper) <$> pWithContents pInlines body)-  ,("emph", \_ _ fields -> do+  ,("emph", InlineHandler $ \_ _ fields -> do       body <- getField "body" fields       B.emph <$> pWithContents pInlines body)-  ,("strong", \_ _ fields -> do+  ,("strong", InlineHandler $ \_ _ fields -> do       body <- getField "body" fields       B.strong <$> pWithContents pInlines body)-  ,("sub", \_ _ fields -> do+  ,("sub", InlineHandler $ \_ _ fields -> do       body <- getField "body" fields       B.subscript <$> pWithContents pInlines body)-  ,("super", \_ _ fields -> do+  ,("super", InlineHandler $ \_ _ fields -> do       body <- getField "body" fields       B.superscript <$> pWithContents pInlines body)-  ,("strike", \_ _ fields -> do+  ,("strike", InlineHandler $ \_ _ fields -> do       body <- getField "body" fields       B.strikeout <$> pWithContents pInlines body)-  ,("smallcaps", \_ _ fields -> do+  ,("smallcaps", InlineHandler $ \_ _ fields -> do       body <- getField "body" fields       B.smallcaps <$> pWithContents pInlines body)-  ,("underline", \_ _ fields -> do+  ,("underline", InlineHandler $ \_ _ fields -> do       body <- getField "body" fields       B.underline <$> pWithContents pInlines body)-  ,("quote", \_ _ fields -> do+  ,("highlight", InlineHandler $ \_ _ fields -> do+      body <- getField "body" fields+      B.spanWith ("", ["mark"], []) <$> pWithContents pInlines body)+  ,("quote", InlineHandler $ \_ _ fields -> do       (getField "block" fields <|> pure False) >>= guard . not-      body <- getInlineBody fields >>= pWithContents pInlines+      body <- getField "body" fields >>= pWithContents pInlines       pure $ B.doubleQuoted $ B.trimInlines body)-  ,("link", \_ _ fields -> do+  ,("link", InlineHandler $ \_ _ fields -> do       dest <- getField "dest" fields       src <- case dest of         VString t -> pure t@@ -530,7 +621,7 @@                pWithContents                 (B.fromList . blocksToInlines . B.toList <$> pBlocks) body       pure $ B.link src "" description)-  ,("image", \mbpos _ fields -> do+  ,("image", InlineHandler $ \mbpos _ fields -> do       path <- getField "source" fields <|> getField "path" fields       alt <- (B.text <$> getField "alt" fields) `mplus` pure mempty       let basedir = maybe "." (takeDirectory . sourceName) mbpos@@ -550,10 +641,10 @@                 ++ maybe [] (\x -> [("height", x)]) mbheight             )       pure $ B.imageWith attr path' "" alt)-  ,("box", \_ _ fields -> do+  ,("box", InlineHandler $ \_ _ fields -> do       body <- getField "body" fields       B.spanWith ("", ["box"], []) <$> pWithContents pInlines body)-  ,("h", \_ _ fields -> do+  ,("h", InlineHandler $ \_ _ fields -> do       amount <- getField "amount" fields `mplus` pure (LExact 1 LEm)       let em = case amount of             LExact x LEm -> toRational x@@ -561,21 +652,21 @@               LExact x LPt -> toRational x / 12               _ -> 1 / 3 -- guess!       pure $ B.text $ getSpaceChars em)-  ,("place", \_ _ fields -> do+  ,("place", InlineHandler $ \_ _ fields -> do       ignored "parameters of place"       getField "body" fields >>= pWithContents pInlines)-  ,("align", \_ _ fields -> do+  ,("align", InlineHandler $ \_ _ fields -> do       alignment <- getField "alignment" fields       B.spanWith ("", [], [("align", repr alignment)])         <$> (getField "body" fields >>= pWithContents pInlines))-  ,("sys.version", \_ _ _ -> pure $ B.text "typst-hs")-  ,("math.equation", \_ _ fields -> do+  ,("sys.version", InlineHandler $ \_ _ _ -> pure $ B.text "typst-hs")+  ,("math.equation", InlineHandler $ \_ _ fields -> do       body <- getField "body" fields       display <- getField "block" fields       (if display then B.displayMath else B.math) . writeTeX <$> pMathMany body)-  ,("pad", \_ _ fields ->  -- ignore paddingy+  ,("pad", InlineHandler $ \_ _ fields ->  -- ignore paddingy       getField "body" fields >>= pWithContents pInlines)-  ,("rotate", \_ _ fields -> do+  ,("rotate", InlineHandler $ \_ _ fields -> do       body <- getField "body" fields >>= pWithContents pInlines       let kvs = case M.lookup "angle" fields of                     Just (VAngle ang) -> [("angle", T.pack $ show ang)]@@ -583,19 +674,6 @@       pure $ B.spanWith ("", ["rotate"], kvs) body)   ] -getInlineBody :: PandocMonad m => M.Map Identifier Val -> P m (Seq Content)-getInlineBody fields =-  parbreaksToLinebreaks <$> getField "body" fields--parbreaksToLinebreaks :: Seq Content -> Seq Content-parbreaksToLinebreaks =-  fmap go . Seq.dropWhileL isParbreak . Seq.dropWhileR isParbreak- where-   go (Elt "parbreak" pos _) = Elt "linebreak" pos mempty-   go x = x-   isParbreak (Elt "parbreak" _ _) = True-   isParbreak _ = False- pPara :: PandocMonad m => P m B.Blocks pPara = do   ils <- B.trimInlines . collapseAdjacentCites . mconcat <$> many1 pInline@@ -634,20 +712,22 @@      Cite (cs1 ++ cs2) (ils1 <> ils2) : xs    go (Cite cs1 ils1) (Space : Cite cs2 ils2 : xs) =      Cite (cs1 ++ cs2) (ils1 <> ils2) : xs+   go (Cite cs1 ils1) (SoftBreak : Cite cs2 ils2 : xs) =+     Cite (cs1 ++ cs2) (ils1 <> ils2) : xs    go x xs = x:xs  modString :: (Text -> Text) -> B.Inline -> B.Inline modString f (B.Str t) = B.Str (f t) modString _ x = x -findLabels :: Seq.Seq Content -> [Text]-findLabels = foldr go []+findLabels :: Seq.Seq Content -> Set.Set Text+findLabels = F.foldl' go Set.empty  where-   go (Txt{}) = id-   go (Lab t) = (t :)-   go (Elt{ eltFields = fs }) = \ts -> foldr go' ts fs-   go' (VContent cs) = (findLabels cs ++)-   go' _ = id+   go acc Txt{} = acc+   go acc (Lab t) = Set.insert t acc+   go acc (Elt{ eltFields = fs }) = F.foldl' go' acc fs+   go' acc (VContent cs) = F.foldl' go acc cs+   go' acc _ = acc  parseTable :: PandocMonad m            => Maybe Text -> M.Map Identifier Val -> P m B.Blocks@@ -760,14 +840,13 @@   let headRows = getRows THeader tableData   let bodyRows = getRows TBody tableData   let footRows = getRows TFooter tableData-  pure $-    B.tableWith-      (fromMaybe "" mbident, [], [])-      (B.Caption mempty mempty)-      colspecs-      (B.TableHead B.nullAttr headRows)-      [B.TableBody B.nullAttr 0 [] bodyRows]-      (B.TableFoot B.nullAttr footRows)+  pure $ compactifyTable $ B.tableWith+        (fromMaybe "" mbident, [], [])+        (B.Caption mempty mempty)+        colspecs+        (B.TableHead B.nullAttr headRows)+        [B.TableBody B.nullAttr 0 [] bodyRows]+        (B.TableFoot B.nullAttr footRows)  data TableSection = THeader | TBody | TFooter   deriving (Show, Ord, Eq)
src/Text/Pandoc/Readers/Typst/Parsing.hs view
@@ -36,7 +36,7 @@ import Text.Pandoc.Definition  data PState = PState-        { sLabels :: [Text]+        { sLabels :: Set Text         , sMeta :: Meta         , sOptions :: ReaderOptions         , sIdentifiers :: Set Text@@ -57,7 +57,7 @@ defaultPState :: PState defaultPState =   PState-  { sLabels = []+  { sLabels = Set.empty   , sMeta = mempty   , sOptions = def   , sIdentifiers = Set.empty
src/Text/Pandoc/Readers/Vimwiki.hs view
@@ -66,15 +66,17 @@ import Text.Pandoc.Parsing (ParserState, ParsecT, blanklines, emailAddress,                             many1Till, orderedListMarker, readWithM,                             registerHeader, spaceChar, stateMeta,-                            stateOptions, uri, manyTillChar, manyChar, textStr,-                            many1Char, countChar, many1TillChar,+                            stateOptions, uri, manyTillChar, textStr,+                            countChar, many1TillChar,+                            takeWhileP, takeWhile1P,                             alphaNum, anyChar, char, newline, noneOf, oneOf,                             space, spaces, string, choice, eof, lookAhead,                             many1, many, manyTill, notFollowedBy,                             skipMany1, try, option,                             updateState, getState, (<|>)) import Text.Pandoc.Sources (ToSources(..), Sources)-import Text.Pandoc.Shared (splitTextBy, stringify, stripFirstAndLast, tshow)+import Text.Pandoc.Shared (splitTextBy, stringifyInlines, stripFirstAndLast,+                           tshow) import Text.Pandoc.URI (isURI)  readVimwiki :: (PandocMonad m, ToSources a)@@ -200,7 +202,7 @@ definitionTerm :: PandocMonad m => VwParser m Inlines definitionTerm = try $ do   x <- definitionTerm1 <|> definitionTerm2-  guard (stringify x /= "")+  guard (stringifyInlines x /= "")   return x  definitionTerm1 :: PandocMonad m => VwParser m Inlines@@ -223,7 +225,7 @@ preformatted :: PandocMonad m => VwParser m Blocks preformatted = try $ do   many spaceChar >> string "{{{"-  attrText <- manyChar (noneOf "\n")+  attrText <- takeWhileP (/= '\n')   lookAhead newline   contents <- manyTillChar anyChar (try (char '\n' >> many spaceChar >> string "}}}"     >> many spaceChar >> newline))@@ -233,8 +235,11 @@  makeAttr :: Text -> Attr makeAttr s =-  let xs = splitTextBy (`elem` (" \t" :: String)) s in-    ("", syntax xs, mapMaybe nameValue xs)+  let xs = splitTextBy (`elem` (" \t" :: String)) s+      kvs = mapMaybe nameValue xs+      cls = syntax xs ++ maybe [] T.words (lookup "class" kvs)+      ident = fromMaybe "" $ lookup "id" kvs+  in (ident, cls, [(k,v) | (k,v) <- kvs, k /= "class" && k /= "id"])  syntax :: [Text] -> [Text] syntax (s:_) | not $ T.isInfixOf "=" s = [s]@@ -479,7 +484,7 @@ inlineML = choice $ whitespace endlineML:inlineList  str :: PandocMonad m => VwParser m Inlines-str = B.str <$> many1Char (noneOf $ spaceChars ++ specialChars)+str = B.str <$> takeWhile1P (`notElem` (spaceChars ++ specialChars))  whitespace :: PandocMonad m => VwParser m () -> VwParser m Inlines whitespace endline = B.space <$ (skipMany1 spaceChar <|>@@ -507,7 +512,7 @@   return $ B.spanWith (makeId contents, [], []) mempty <> B.strong contents  makeId :: Inlines -> Text-makeId i = T.concat (stringify <$> toList i)+makeId i = stringifyInlines i  emph :: PandocMonad m => VwParser m Inlines emph = try $ do
src/Text/Pandoc/Readers/XML.hs view
@@ -25,6 +25,7 @@ import Data.Text (Text) import qualified Data.Text as T import Data.Text.Lazy (fromStrict)+import qualified Data.Text.Read as TR import Data.Version (Version, makeVersion) import Text.Pandoc.Builder import Text.Pandoc.Class.PandocMonad@@ -36,7 +37,6 @@ import Text.Pandoc.XML (lookupEntity) import Text.Pandoc.XML.Light import Text.Pandoc.XMLFormat-import Text.Read (readMaybe)  -- TODO: use xmlPath state to give better context when an error occurs @@ -45,7 +45,6 @@ data XMLReaderState = XMLReaderState   { xmlApiVersion :: Version,     xmlMeta :: Meta,-    xmlContent :: [Content],     xmlPath :: [Text]   }   deriving (Show)@@ -55,7 +54,6 @@     XMLReaderState       { xmlApiVersion = pandocVersion,         xmlMeta = mempty,-        xmlContent = [],         xmlPath = ["root"]       } @@ -65,7 +63,7 @@   tree <-     either (throwError . PandocXMLError "") return $       parseXMLContents (fromStrict . sourcesToText $ sources)-  (bs, st') <- flip runStateT (def {xmlContent = tree}) $ mapM parseBlock tree+  (bs, st') <- flip runStateT def $ mapM parseBlock tree   let blockList = toList $ concatMany bs   return $ Pandoc (xmlMeta st') blockList @@ -117,7 +115,7 @@         "Header" -> (headerWith attr level) <$> getInlines (elContent e)           where             level = textToInt (attrValue atNameLevel e) 1-            attr = filterAttrAttributes [atNameLevel] $ attrFromElement e+            attr = attrFromElementExcept [atNameLevel] e         "HorizontalRule" -> return horizontalRule         "BlockQuote" -> do           contents <- getBlocks e@@ -192,25 +190,39 @@       _ -> Nothing  strContentRecursive :: Element -> Text-strContentRecursive =-  strContent-    . (\e' -> e' {elContent = map elementToStr $ elContent e'})--elementToStr :: Content -> Content-elementToStr (Elem e') = Text $ CData CDataText (strContentRecursive e') Nothing-elementToStr x = x+strContentRecursive e = case elContent e of+  [Text (CData _ s _)] -> s -- the common case: no nested elements+  cs -> T.concat $ map contentToStr cs+  where+    contentToStr (Text (CData _ s _)) = s+    contentToStr (Elem e') = strContentRecursive e'+    contentToStr (CRef _) = mempty  textToInt :: Text -> Int -> Int-textToInt t deflt =-  let safe_to_int :: Text -> Maybe Int-      safe_to_int s = readMaybe $ T.unpack s-   in case (safe_to_int t) of-        Nothing -> deflt-        Just (n) -> n+textToInt t deflt = case TR.signed TR.decimal (T.strip t) of+  Right (n, rest) | T.null rest -> n+  _ -> deflt +-- | Split text into 'Str', 'Space' and 'SoftBreak' inlines.  This is+-- 'Text.Pandoc.Builder.text', but it builds a list instead of a 'Seq'.+textToInlines :: Text -> [Inline]+textToInlines t+  | T.null t = []+  | isSpaceChar (T.head t) =+      case T.span isSpaceChar t of+        (spaces, rest) ->+          (if T.any isNewlineChar spaces then SoftBreak else Space)+            : textToInlines rest+  | otherwise =+      case T.break isSpaceChar t of+        (word, rest) -> Str word : textToInlines rest+  where+    isSpaceChar c = c == ' ' || c == '\n' || c == '\r' || c == '\t'+    isNewlineChar c = c == '\n' || c == '\r'+ parseInline :: (PandocMonad m) => Content -> XMLReader m Inlines parseInline (Text (CData _ s _)) =-  return $ text s+  return $ fromList $ textToInlines s parseInline (CRef ref) =   return $     maybe (text $ T.toUpper ref) text $@@ -245,12 +257,12 @@           where             url = attrValue atNameLinkUrl e             title = attrValue atNameTitle e-            attr = filterAttrAttributes [atNameLinkUrl, atNameTitle] $ attrFromElement e+            attr = attrFromElementExcept [atNameLinkUrl, atNameTitle] e         "Image" -> innerInlines $ imageWith attr url title           where             url = attrValue atNameImageUrl e             title = attrValue atNameTitle e-            attr = filterAttrAttributes [atNameImageUrl, atNameTitle] $ attrFromElement e+            attr = attrFromElementExcept [atNameImageUrl, atNameTitle] e         "RawInline" -> do           let format = (attrValue atNameFormat e)           return $ rawInline format $ strContentRecursive e@@ -308,6 +320,8 @@   "AlignCenter" -> AlignCenter   _ -> AlignDefault +-- NB. 'reads' is used rather than 'Data.Text.Read.double', which is+-- not correctly rounded and so would not round-trip column widths. getColWidth :: Text -> ColWidth getColWidth txt = case reads (T.unpack txt) of   [(value, "")] -> if value == 0.0 then ColWidthDefault else ColWidth value@@ -322,7 +336,7 @@  getTableBody :: (PandocMonad m) => Element -> XMLReader m (Maybe TableBody) getTableBody body_el = do-  let attr = filterAttrAttributes [atNameRowHeadColumns] $ attrFromElement body_el+  let attr = attrFromElementExcept [atNameRowHeadColumns] body_el       bh = childrenNamed tgNameBodyHeader body_el       bb = childrenNamed tgNameBodyBody body_el       headcols = textToInt (attrValue atNameRowHeadColumns body_el) 0@@ -355,7 +369,7 @@   let alignment = alignmentFromText $ attrValue atNameAlignment c       rowspan = RowSpan $ textToInt (attrValue atNameRowspan c) 1       colspan = ColSpan $ textToInt (attrValue atNameColspan c) 1-      attr = filterAttrAttributes [atNameAlignment, atNameRowspan, atNameColspan] $ attrFromElement c+      attr = attrFromElementExcept [atNameAlignment, atNameRowspan, atNameColspan] c   blocks <- getBlocks c   return $ Cell attr alignment rowspan colspan (toList blocks) @@ -461,22 +475,26 @@  type PandocAttr = (Text, [Text], [(Text, Text)]) -filterAttributes :: S.Set Text -> [(Text, Text)] -> [(Text, Text)]-filterAttributes to_be_removed a = filter keep_attr a-  where-    keep_attr (k, _) = not (k `S.member` to_be_removed)--filterAttrAttributes :: [Text] -> PandocAttr -> PandocAttr-filterAttrAttributes to_be_removed (idn, classes, a) = (idn, classes, filtered)-  where-    filtered = filterAttributes (S.fromList to_be_removed) a- attrFromElement :: Element -> PandocAttr-attrFromElement e = filterAttrAttributes ["id", "class"] (idn, classes, attributes)+attrFromElement = attrFromElementExcept []++-- | Build a pandoc 'Attr' from an element's XML attributes, skipping+-- the attributes whose (decoded) names are listed in the first argument+-- in addition to @id@ and @class@, which become the identifier and the+-- classes of the 'Attr'.+attrFromElementExcept :: [Text] -> Element -> PandocAttr+attrFromElementExcept skip e = go (elAttribs e) "" "" []   where-    idn = attrValue "id" e-    classes = T.words $ attrValue "class" e-    attributes = map (\a -> (qName $ attrKey a, attrVal a)) $ elAttribs e+    go [] idn classes kvs = (idn, T.words classes, reverse kvs)+    go (a : as) idn classes kvs =+      case qName (attrKey a) of+        "id" -> go as (attrVal a) classes kvs+        "class" -> go as idn (attrVal a) kvs+        name ->+          let name' = decodeAttrName name+           in if name' `elem` skip+                then go as idn classes kvs+                else go as idn classes ((name', attrVal a) : kvs)  addMeta :: (PandocMonad m) => (ToMetaValue a) => Text -> a -> XMLReader m () addMeta field val = modify (setMeta field val)
src/Text/Pandoc/SelfContained.hs view
@@ -16,16 +16,16 @@ the HTML using data URIs. -} module Text.Pandoc.SelfContained ( makeDataURI, makeSelfContained ) where-import Codec.Compression.GZip as Gzip+import qualified Codec.Compression.Zlib.Internal as Zlib import Control.Applicative ((<|>)) import Data.ByteString (ByteString) import Data.ByteString.Base64 (encode) import qualified Data.ByteString.Char8 as B import qualified Data.ByteString.Lazy as L import qualified Data.Text as T-import Data.Char (isAlphaNum, isAscii)+import Data.Char (toLower) import Crypto.Hash (hashWith, SHA1(SHA1))-import Network.URI (escapeURIString)+import Network.URI (escapeURIString, isUnescapedInURI) import System.FilePath (takeDirectory, takeExtension, (</>)) import Text.HTML.TagSoup import Text.Pandoc.Class.PandocMonad (PandocMonad (..), fetchItem,@@ -44,19 +44,16 @@ import qualified Data.Map as M import Control.Monad.State -isOk :: Char -> Bool-isOk c = isAscii c && isAlphaNum c- makeDataURI :: (MimeType, ByteString) -> T.Text makeDataURI (mime, raw) =   if textual-     then "data:" <> mime' <> "," <> T.pack (escapeURIString isOk (toString raw))+     then "data:" <> mime' <> "," <> T.pack (escapeURIString isUnescapedInURI (toString raw))      else "data:" <> mime' <> ";base64," <> toText (encode raw')   where textual = "text/" `T.isPrefixOf` mime         raw' = if "+xml" `T.isSuffixOf` mime                   then B.filter (/= '\r') raw  -- strip off CRs                   else raw-        mime' = if textual && T.any (== ';') mime+        mime' = if textual && not (T.any (== ';') mime)                    then mime <> ";charset=utf-8"                    else mime  -- mime type already has charset @@ -70,9 +67,10 @@  data ConvertState =   ConvertState-  { isHtml5 :: Bool-  , svgMap  :: M.Map T.Text (T.Text, [Attribute T.Text])+  { svgMap  :: M.Map T.Text (T.Text, [Attribute T.Text])     -- map from hash to (id, svg attributes)+  , fetchCache :: M.Map (MimeType, T.Text) GetDataResult+    -- cache of fetched resources, keyed on mime type hint and url   } deriving (Show)  convertTags :: PandocMonad m =>@@ -102,7 +100,7 @@                   | ("text/javascript" `T.isPrefixOf` mime ||                      "application/javascript" `T.isPrefixOf` mime ||                      "application/x-javascript" `T.isPrefixOf` mime) &&-                     not ("</script" `B.isInfixOf` bs) ->+                     not ("</script" `B.isInfixOf` B.map toLower bs) ->                      return $                        TagOpen "script" [(k,v) | (k,v) <- as                                                , k == "type" ||@@ -155,12 +153,15 @@                           Nothing -> False                           Just cs -> "inline-svg" `elem` cs        as' <- mapM (processAttribute inlineSvgs) as-       let attrs = addRole "img" $ addAriaLabel $ rights as'+       let attrs = rights as'        let svgContents = lefts as'        rest <- convertTags ts        case svgContents of          [] -> return $ TagOpen tagname attrs : rest          ((hash, tags) : _) -> do+             -- inlining the SVG loses the img element's alt text, so+             -- we add role and aria-label to the svg element:+             let svgImgAttrs = addRole "img" $ addAriaLabel attrs              -- drop "</img>" if present              let rest' = case rest of                            TagClose tn : xs | tn == tagname ->  xs@@ -168,7 +169,8 @@              svgmap <- gets svgMap              case M.lookup hash svgmap of                Just (svgid, svgattrs) -> do-                 let attrs' = [(k,v) | (k,v) <- combineSvgAttrs svgattrs attrs+                 let attrs' = [(k,v) | (k,v) <- combineSvgAttrs svgattrs+                                                  svgImgAttrs                                      , k /= "id"]                  return $ TagOpen "svg" attrs' :                           TagOpen "use" [("href", "#" <> svgid),@@ -180,7 +182,7 @@                Nothing ->                   case dropWhile (not . isTagOpenName "svg") tags of                     TagOpen "svg" svgattrs : tags' -> do-                      let attrs' = combineSvgAttrs svgattrs attrs+                      let attrs' = combineSvgAttrs svgattrs svgImgAttrs                       let svgid = case lookup "id" attrs' of                                      Just id' -> id'                                      Nothing -> "svg_" <> hash@@ -188,11 +190,7 @@                                     [(k,v) | (k,v) <- attrs', k /= "id"]                       modify $ \st ->                         st{ svgMap = M.insert hash (svgid, attrs'') (svgMap st) }-                      let fixUrl x =-                            case T.breakOn "url(#" x of-                              (_,"") -> x-                              (before, after) -> before <>-                                  "url(#" <> svgid <> "_" <> T.drop 5 after+                      let fixUrl = T.replace "url(#" ("url(#" <> svgid <> "_")                       let addIdPrefix ("id", x) = ("id", svgid <> "_" <> x)                           addIdPrefix (k, x)                            | k == "xlink:href" || k == "href" =@@ -290,7 +288,7 @@                     _ -> []  cssURLs :: PandocMonad m-        => FilePath -> ByteString -> m ByteString+        => FilePath -> ByteString -> StateT ConvertState m ByteString cssURLs d orig = do   res <- runParserT (parseCSSUrls d) () "css" orig   case res of@@ -300,21 +298,22 @@        Right bs  -> return bs  parseCSSUrls :: PandocMonad m-             => FilePath -> ParsecT ByteString () m ByteString+             => FilePath+             -> ParsecT ByteString () (StateT ConvertState m) ByteString parseCSSUrls d = B.concat <$> P.many   (pCSSWhite <|> pCSSComment <|> pCSSImport d <|> pCSSUrl d <|> pCSSOther)  pCSSImport :: PandocMonad m-           => FilePath -> ParsecT ByteString () m ByteString+           => FilePath+           -> ParsecT ByteString () (StateT ConvertState m) ByteString pCSSImport d = P.try $ do   P.string "@import"   P.spaces   res <- (pQuoted <|> pUrl) >>= handleCSSUrl d   P.spaces   P.char ';'-  P.spaces   case res of-       Left b       -> return $ B.pack "@import " <> b+       Left b       -> return $ B.pack "@import " <> b <> B.pack ";"        Right (_, b) -> return b  -- Note: some whitespace in CSS is significant, so we can't collapse it!@@ -334,7 +333,8 @@   (B.singleton <$> P.char '/')  pCSSUrl :: PandocMonad m-        => FilePath -> ParsecT ByteString () m ByteString+        => FilePath+        -> ParsecT ByteString () (StateT ConvertState m) ByteString pCSSUrl d = P.try $ do   res <- pUrl >>= handleCSSUrl d   case res of@@ -366,7 +366,7 @@  handleCSSUrl :: PandocMonad m              => FilePath -> (T.Text, ByteString)-             -> ParsecT ByteString () m+             -> ParsecT ByteString () (StateT ConvertState m)                   (Either ByteString (MimeType, ByteString)) handleCSSUrl d (url, fallback) =   case escapeURIString (/='|') (T.unpack $ trim url) of@@ -382,7 +382,7 @@                       (mt, b) <- if "text/css" `T.isPrefixOf` mt'                                     -- see #5725: in HTML5, content type                                     -- isn't allowed on style type attribute-                                    then ("text/css",) <$> cssURLs d raw+                                    then ("text/css",) <$> lift (cssURLs d raw)                                     else return (mt', raw)                       return $ Right (mt, b)                     CouldNotFetch _ -> return $ Left fallback@@ -393,19 +393,45 @@   | Fetched (MimeType, ByteString)   deriving (Show) +-- | Decompress gzipped data, catching decompression errors instead+-- of throwing an imprecise exception from pure code.+decompressGzip :: ByteString -> Either T.Text ByteString+decompressGzip bs =+  B.concat <$>+    Zlib.foldDecompressStreamWithInput+      (\chunk rest -> (chunk :) <$> rest)+      (const (Right []))+      (Left . T.pack . show)+      (Zlib.decompressST Zlib.gzipFormat Zlib.defaultDecompressParams)+      (L.fromStrict bs)+ getData :: PandocMonad m         => MimeType -> T.Text-        -> m GetDataResult+        -> StateT ConvertState m GetDataResult getData mimetype src   | "data:" `T.isPrefixOf` src = return $ AlreadyDataURI src -- already data: uri-  | otherwise = catchError fetcher handler+  | otherwise = do+      cache <- gets fetchCache+      case M.lookup (mimetype, src) cache of+        Just res -> return res+        Nothing -> do+          res <- catchError fetcher handler+          modify $ \st ->+            st{ fetchCache = M.insert (mimetype, src) res (fetchCache st) }+          return res  where    fetcher = do       let ext = T.toLower $ T.pack $ takeExtension $ T.unpack src       (raw, respMime) <- fetchItem src-      let raw' = if ext `elem` [".gz", ".svgz"]-                 then B.concat $ L.toChunks $ Gzip.decompress $ L.fromChunks [raw]-                 else raw+      if ext `elem` [".gz", ".svgz"]+         then case decompressGzip raw of+                Right raw' -> processFetched raw' respMime+                Left err -> do+                  let msg = "could not decompress: " <> err+                  report $ CouldNotFetchResource src msg+                  return $ CouldNotFetch $ PandocSomeError msg+         else processFetched raw respMime+   processFetched raw' respMime = do       let mime = case (mimetype, respMime) of                   ("",Nothing) -> "application/octet-stream"                   (x, Nothing) -> x@@ -441,10 +467,7 @@ makeSelfContained :: PandocMonad m => T.Text -> m T.Text makeSelfContained inp = do   let tags = parseTags inp-  let html5 = case tags of-                  (TagOpen "!DOCTYPE" [("html","")]:_) -> True-                  _ -> False-  let convertState = ConvertState { isHtml5 = html5,-                                    svgMap = mempty }+  let convertState = ConvertState { svgMap = mempty,+                                    fetchCache = mempty }   out' <- evalStateT (convertTags tags) convertState   return $ renderTags' out'
src/Text/Pandoc/Shared.hs view
@@ -45,9 +45,11 @@                      removeFormatting,                      deNote,                      stringify,+                     stringifyInlines,                      capitalize,                      compactify,                      compactifyDL,+                     compactifyTable,                      linesToPara,                      figureDiv,                      makeSections,@@ -96,7 +98,7 @@ import qualified Data.List as L import qualified Data.Map as M import Data.Maybe (mapMaybe)-import Data.Monoid (Any (..) )+import Data.Monoid (Any (..), All (..) ) import Data.Semigroup (Min (..)) import Data.Sequence (ViewL (..), ViewR (..), viewl, viewr) import qualified Data.Set as Set@@ -369,17 +371,17 @@ -- Footnotes are skipped (since we don't want their contents in link -- labels). stringify :: Walkable Inline a => a -> T.Text-stringify = query go . walk fixInlines-  where go :: Inline -> T.Text-        go Space                                       = " "-        go SoftBreak                                   = " "-        go (Str x)                                     = x-        go (Code _ x)                                  = x-        go (Math _ x)                                  = x-        go (RawInline (Format "html") (T.unpack -> ('<':'b':'r':_)))-                                                       = " " -- see #2105-        go LineBreak                                   = " "-        go _                                           = ""+stringify = T.concat . query go . walk fixInlines+  where go :: Inline -> [T.Text]+        go Space        = [" "]+        go SoftBreak    = [" "]+        go (Str x)      = [x]+        go (Code _ x)   = [x]+        go (Math _ x)   = [x]+        go (RawInline (Format "html") t)+          | "<br" `T.isPrefixOf` t = [" "] -- see #2105+        go LineBreak    = [" "]+        go _            = []          fixInlines :: Inline -> Inline         fixInlines (Cite _ ils) = Cite [] ils@@ -387,6 +389,38 @@         fixInlines (q@Quoted{}) = deQuote q         fixInlines x = x +-- | Like 'stringify', but specialized to sequences of inlines+-- (e.g. @['Inline']@ or 'Inlines').  Produces the same result as+-- 'stringify' in a single pass, without rebuilding the tree.+stringifyInlines :: Foldable t => t Inline -> T.Text+stringifyInlines ils0 = T.concat $ foldr go [] ils0+  where+    go :: Inline -> [T.Text] -> [T.Text]+    go il acc = case il of+      Str x           -> x : acc+      Space           -> " " : acc+      SoftBreak       -> " " : acc+      Code _ x        -> x : acc+      Math _ x        -> x : acc+      LineBreak       -> " " : acc+      RawInline (Format "html") t+        | "<br" `T.isPrefixOf` t -> " " : acc -- see #2105+      RawInline _ _   -> acc+      Note _          -> acc -- footnotes are skipped+      Cite _ ils      -> foldr go acc ils -- citation metadata is dropped+      Quoted SingleQuote ils -> "\8216" : foldr go ("\8217" : acc) ils+      Quoted DoubleQuote ils -> "\8220" : foldr go ("\8221" : acc) ils+      Emph ils        -> foldr go acc ils+      Underline ils   -> foldr go acc ils+      Strong ils      -> foldr go acc ils+      Strikeout ils   -> foldr go acc ils+      Superscript ils -> foldr go acc ils+      Subscript ils   -> foldr go acc ils+      SmallCaps ils   -> foldr go acc ils+      Span _ ils      -> foldr go acc ils+      Link _ ils _    -> foldr go acc ils+      Image _ ils _   -> foldr go acc ils+ -- | Unwrap 'Quoted' inline elements, enclosing the contents with -- English-style Unicode quotes instead. deQuote :: Inline -> Inline@@ -441,6 +475,26 @@              _     -> items         _          -> items +-- | If every cell of the table is either empty or consists of a+-- single Para or Plain element, convert all Para to Plain for a compact+-- table.+compactifyTable ::+  (Walkable Block a, Walkable [Block] a, Walkable Inline a) => a -> a+compactifyTable x = if isSimpleTable x+                       then walk fixNotes $ walk paraToPlain x+                       else x+ where+  isSimpleCell :: [Block] -> All+  isSimpleCell [] = All True+  isSimpleCell [Para _] = All True+  isSimpleCell [Plain _] = All True+  isSimpleCell _ = All False+  isSimpleTable = getAll . query isSimpleCell+  paraToPlain (Para ils) = Plain ils+  paraToPlain b = b+  -- walk descends into the notes, so we need to fix them back up:+  fixNotes (Note [Plain ils]) = Note [Para ils]+  fixNotes i = i  -- | Combine a list of lines by adding hard linebreaks. combineLines :: [[Inline]] -> [Inline]@@ -463,7 +517,8 @@               , ["figure"] `union` classes               , kv               )-      captkv = maybe mempty (\s -> [("short-caption", stringify s)]) shortcapt+      captkv = maybe mempty (\s -> [("short-caption", stringifyInlines s)])+                 shortcapt       capt = [Div ("", ["caption"], captkv) longcapt | not (null longcapt)]   in Div divattr (body ++ capt) @@ -475,7 +530,7 @@ -- | Convert Pandoc inline list to plain text identifier. inlineListToIdentifier :: Extensions -> [Inline] -> T.Text inlineListToIdentifier exts =-  textToIdentifier exts . stringify . unEmojify+  textToIdentifier exts . stringifyInlines . unEmojify   where     unEmojify :: [Inline] -> [Inline]     unEmojify@@ -672,7 +727,7 @@ taskListItemFromAscii = handleTaskListItem fromMd   where     fromMd (Str "[" : Space : Str "]" : Space : is) = Str "☐" : Space : is-    fromMd (Str "[ ]"                 : Space : is) = Str "☒" : Space : is+    fromMd (Str "[ ]"                 : Space : is) = Str "☐" : Space : is     fromMd (Str "[x]"                 : Space : is) = Str "☒" : Space : is     fromMd (Str "[X]"                 : Space : is) = Str "☒" : Space : is     fromMd [Str "[" , Space , Str "]"] = [Str "☐"]@@ -743,7 +798,8 @@     fmt = concatMap go . groupBy (\a b -> isPlaintext a && isPlaintext b)       where         go xs-          | all isPlaintext xs = B.toList $ B.codeWith attr $ stringify xs+          | all isPlaintext xs = B.toList $ B.codeWith attr $+                                   stringifyInlines xs           | otherwise = xs  --
src/Text/Pandoc/Sources.hs view
@@ -27,6 +27,8 @@   , ensureFinalNewlines   , addToInput   , satisfy+  , takeWhileP+  , takeWhile1P   , oneOf   , noneOf   , anyChar@@ -43,6 +45,8 @@ where import qualified Text.Parsec as P import Text.Parsec (Stream(..), ParsecT)+import Text.Parsec.Prim (mkPT, Consumed(..), Reply(..), State(..))+import Text.Parsec.Error (ParseError, newErrorMessage, Message(SysUnExpect)) import Text.Parsec.Pos as P import Data.Text (Text) import qualified Data.Text as T@@ -157,6 +161,80 @@ satisfy f = P.tokenPrim show updateSourcePos matcher  where   matcher !c = if f c then Just c else Nothing++-- | Consume characters while the predicate holds, returning them as+-- a 'Text'.  Always succeeds (returning an empty 'Text' if no+-- characters match).  Equivalent to @'Data.Text.pack' \<$> 'P.many'+-- ('satisfy' f)@ (including source position and error behavior), but+-- faster, because it processes whole chunks of text at a time.+takeWhileP :: Monad m => (Char -> Bool) -> ParsecT Sources u m Text+takeWhileP f = mkPT $ \st ->+  case spanSources f st of+    (t, st', err)+      | T.null t  -> return (Empty (return (Ok t st' err)))+      | otherwise -> return (Consumed (return (Ok t st' err)))++-- | Like 'takeWhileP', but requires at least one matching character.+-- Equivalent to @'Data.Text.pack' \<$> 'P.many1' ('satisfy' f)@.+takeWhile1P :: Monad m => (Char -> Bool) -> ParsecT Sources u m Text+takeWhile1P f = mkPT $ \st ->+  case spanSources f st of+    (t, st', err)+      | T.null t  -> return (Empty (return (Error err)))+      | otherwise -> return (Consumed (return (Ok t st' err)))++-- Consume characters matching the predicate from the beginning of+-- the input, returning the consumed text, the updated parser state,+-- and the error that a corresponding sequence of 'satisfy' parsers+-- would have recorded at the position where it stopped.+spanSources :: (Char -> Bool)+            -> State Sources u+            -> (Text, State Sources u, ParseError)+spanSources f (State (Sources input0) pos0 usr) = go pos0 input0 []+ where+  -- pos and committed are the position and input after the last+  -- consumed character (uncons drops leading empty chunks only when a+  -- character is actually consumed, so they must be retained if we+  -- stop at a chunk boundary).+  go !pos committed acc =+    case dropWhile (T.null . snd) committed of+      [] -> stop pos committed acc ""+      (p, t) : rest ->+        case T.span f t of+          (pre, post)+            | T.null pre -> stop pos committed acc (show (T.head t))+            | T.null post ->+                -- consumed the whole chunk: as with 'satisfy', the+                -- position jumps to the stored position of the next+                -- chunk, if any.+                let pos' = case rest of+                             (pnext, _) : _ -> pnext+                             []             -> advancePos pos pre+                in go pos' ((p, post) : rest) (pre : acc)+            | otherwise ->+                let pos' = advancePos pos pre+                in stop pos' ((p, post) : rest) (pre : acc)+                        (show (T.head post))+  stop pos committed acc msg =+    ( case acc of+        []  -> mempty+        [t] -> t+        _   -> T.concat (reverse acc)+    , State (Sources committed) pos usr+    , newErrorMessage (SysUnExpect msg) pos )++-- Advance a source position over a stretch of text, using the same+-- position updates as the 'UpdateSourcePos' instance for 'Sources'.+advancePos :: SourcePos -> Text -> SourcePos+advancePos pos t+  | T.any (\c -> c == '\n' || c == '\t') t = T.foldl' advanceChar pos t+  | otherwise = incSourceColumn pos (T.length t)+ where+  advanceChar p c =+    case c of+      '\n' -> incSourceLine (setSourceColumn p 1) 1+      '\t' -> incSourceColumn p (4 - ((sourceColumn p - 1) `mod` 4))+      _    -> incSourceColumn p 1  oneOf :: (Monad m, Stream s m Char, UpdateSourcePos s Char)       => [Char] -> ParsecT s u m Char
src/Text/Pandoc/TeX.hs view
@@ -50,5 +50,49 @@ data Macro = Macro MacroScope ExpansionPoint [ArgSpec] (Maybe [Tok]) [Tok]      deriving Show -data ArgSpec = ArgNum Int | Pattern [Tok]+data ArgSpec = ArgNum Int+             | Pattern [Tok]+             | BoolArg Bool Tok+               -- ^ xparse @s@ and @t@ specifiers: an optional token+               -- (e.g. a star); expands to @\\BooleanTrue@ or+               -- @\\BooleanFalse@.  The Bool is False if the @!@+               -- modifier was used (no space-skipping before the+               -- token).+             | DelimArg Bool Tok Tok (Maybe [Tok])+               -- ^ xparse @o@, @O@, @d@, @D@, @r@, @R@ specifiers:+               -- argument between opening and closing delimiter+               -- tokens, with an optional default; when absent and+               -- no default is given, expands to @\\NoValue@.  The+               -- Bool is False if the @!@ modifier was used (no+               -- space-skipping before the opening delimiter).+             | VerbArg+               -- ^ xparse @v@ specifier: a verbatim argument, either+               -- braced or between two identical delimiter+               -- characters; substituted as a single Word token+               -- containing the raw text.+             | EmbellishArg [(Tok, Maybe [Tok])]+               -- ^ xparse @e@ and @E@ specifiers: a set of optional+               -- \"embellishments\" (a token followed by an+               -- argument), matched in any order, each with an+               -- optional default; a missing embellishment expands+               -- to its default, or to @\\NoValue@.+             | ProcessedArg [[Tok]] ArgSpec+               -- ^ xparse @>{processor}@ modifier: the processors+               -- are applied to the grabbed argument from right to+               -- left (i.e., the one nearest the specifier first).+             | BodyArg Bool Int+               -- ^ xparse @b@ specifier (environments only): the+               -- environment body, grabbed up to the following+               -- 'Pattern' (the @\\end{...}@).  The Bool is False if+               -- the @!@ modifier was used (no space-trimming at the+               -- ends of the body).+             | VerbBodyArg Bool Int+               -- ^ xparse @c@ specifier (environments only): the+               -- environment body, grabbed verbatim up to the+               -- following 'Pattern' (the @\\end{...}@); substituted+               -- as one raw Word token per line, with spaces+               -- replaced by U+2423 and lines separated by @\\\\@,+               -- mirroring how LaTeX typesets it.  The Bool is+               -- False if the @!@ modifier was used (no trimming of+               -- leading and trailing blank lines).      deriving Show
src/Text/Pandoc/Translations.hs view
@@ -46,7 +46,12 @@ -- used. setTranslations :: PandocMonad m => Lang -> m () setTranslations lang =-  modifyCommonState $ \st -> st{ stTranslations = Just (lang, Nothing) }+  modifyCommonState $ \st ->+    case stTranslations st of+      -- if a translation table is already loaded for this language,+      -- keep it, so we don't have to parse the translation file again:+      Just (l, Just _) | l == lang -> st+      _ -> st{ stTranslations = Just (lang, Nothing) }  -- | Load term map. getTranslations :: PandocMonad m => m Translations
src/Text/Pandoc/Translations/Types.hs view
@@ -33,7 +33,6 @@ import qualified Data.Map as M import qualified Data.Text as T import GHC.Generics (Generic)-import Text.Pandoc.Shared (safeRead)  data Term =     Abstract@@ -58,13 +57,21 @@   | SeeAlso   | Table   | To-  deriving (Show, Eq, Ord, Generic, Enum, Read)+  deriving (Show, Eq, Ord, Generic, Enum, Bounded, Read)  newtype Translations = Translations (M.Map Term T.Text)         deriving (Show, Generic, Semigroup, Monoid) +-- | Map from term names to terms.  This is much faster than+-- using the derived 'Read' instance to parse term names.+termNameMap :: M.Map T.Text Term+termNameMap = M.fromList [(T.pack (show t), t) | t <- [minBound..maxBound]]++readTerm :: T.Text -> Maybe Term+readTerm t = M.lookup t termNameMap+ instance FromJSON Term where-  parseJSON (String t) = case safeRead t of+  parseJSON (String t) = case readTerm t of                                Just t' -> pure t'                                Nothing -> Prelude.fail $ "Invalid Term name " ++                                                  show t@@ -75,7 +82,7 @@     xs <- parseJSON o >>= mapM addItem . M.toList     return $ Translations (M.fromList xs)     where addItem (k,v) =-            case safeRead k of+            case readTerm k of                  Nothing -> Prelude.fail $ "Invalid Term name " ++ show k                  Just t  ->                    case v of
src/Text/Pandoc/URI.hs view
@@ -126,7 +126,7 @@ pBase64DataURI = base64uri  where   base64uri = do-    A.string "data:"+    A.asciiCI "data:"  -- the scheme is case-insensitive (RFC 3986)     mime <- do       n1 <- restrictedName       A.char '/'
src/Text/Pandoc/UTF8.hs view
@@ -50,9 +50,7 @@                   putStrLn, readFile, writeFile)  readFile :: FilePath -> IO Text-readFile f = do-  h <- openFile (encodePath f) ReadMode-  hGetContents h+readFile f = withFile (encodePath f) ReadMode hGetContents  getContents :: IO Text getContents = hGetContents stdin@@ -103,7 +101,11 @@          if "\xEF\xBB\xBF" `B.isPrefixOf` bs             then B.drop 3 bs             else bs-        filterCRs = B.filter (/='\r')+        -- Only allocate a filtered copy if a CR is actually present;+        -- B.elem compiles to a fast memchr.+        filterCRs bs = if '\r' `B.elem` bs+                          then B.filter (/='\r') bs+                          else bs  -- | Convert UTF8-encoded ByteString to String, also -- removing '\\r' characters.@@ -118,7 +120,13 @@          if "\xEF\xBB\xBF" `BL.isPrefixOf` bs             then BL.drop 3 bs             else bs-        filterCRs = BL.filter (/='\r')+        -- Work chunk-wise (rather than using BL.elem on the whole+        -- input) to preserve laziness; skip allocation for chunks+        -- that contain no CRs.+        filterCRs = BL.fromChunks . map filterChunk . BL.toChunks+        filterChunk bs = if '\r' `B.elem` bs+                            then B.filter (/='\r') bs+                            else bs  -- | Convert UTF8-encoded ByteString to String, also -- removing '\\r' characters.
src/Text/Pandoc/Writers/ANSI.hs view
@@ -190,7 +190,12 @@  blockToANSI opts (BlockQuote blocks) = do   contents <- withFewerColumns 2 $ blockListToANSI opts blocks-  return ( D.prefixed "│ " contents $$ D.blankline)+  -- D.cr ensures we start the blockquote on its own line: without it,+  -- a blockquote that begins mid-line (e.g. as the first block of a+  -- list item, sharing a line with the item's marker) would have its+  -- prefix omitted on the first line, since D.prefixed only prefixes+  -- lines that begin at column 0.+  return ( D.cr <> D.prefixed "│ " contents $$ D.blankline)  blockToANSI opts (Table _ (Caption _ caption) colSpecs (TableHead _ thead) tbody (TableFoot _ tfoot)) = do   let captionInlines = blocksToInlines caption
src/Text/Pandoc/Writers/ChunkedHTML.hs view
@@ -16,7 +16,7 @@   ) where import Text.Pandoc.Definition import Text.Pandoc.Options (WriterOptions(..))-import Text.Pandoc.Shared (stringify, tshow)+import Text.Pandoc.Shared (stringifyInlines, tshow) import Text.Pandoc.Class (PandocMonad, getPOSIXTime, runPure,                           fetchItem, insertMedia, getMediaBag) import Text.Pandoc.MediaBag (mediaItems)@@ -133,7 +133,8 @@  where   opts' = opts{ writerVariables =                   addContextVars opts' topChunk chunk $ writerVariables opts }-  meta' = setMeta "pagetitle" (MetaString (stringify $ chunkHeading chunk)) meta+  meta' = setMeta "pagetitle"+            (MetaString (stringifyInlines $ chunkHeading chunk)) meta   blocks = chunkContents chunk  tocTreeToContext :: Tree SecInfo -> Context Text@@ -146,7 +147,7 @@ secInfoToContext :: SecInfo -> Context Text secInfoToContext sec =   Context $ M.fromList-  [ ("title", SimpleVal $ literal $ stringify $ secTitle sec)+  [ ("title", SimpleVal $ literal $ stringifyInlines $ secTitle sec)   , ("number", maybe NullVal (SimpleVal . literal) (secNumber sec))   , ("id", SimpleVal $ literal $ secId sec)   , ("path", SimpleVal $ literal $ secPath sec)
src/Text/Pandoc/Writers/Djot.hs view
@@ -28,8 +28,8 @@ import qualified Data.Map as M import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.Writers.Shared ( metaToContext, defField, toLegacyTable )-import Text.Pandoc.Shared (isTightList, tshow, stringify, onlySimpleTableCells,-                           makeSections)+import Text.Pandoc.Shared (isTightList, tshow, stringifyInlines,+                           onlySimpleTableCells, makeSections) import Text.DocLayout import Text.DocTemplates (renderTemplate) @@ -240,7 +240,7 @@ inlineToDjot (Link attr ils (src,tit)) = do   opts <- gets options   description <- inlinesToDjot ils-  let ilstring = stringify ils+  let ilstring = stringifyInlines ils   let autolink = ilstring == src   let email = ("mailto:" <> ilstring) == src   let removeClass name (ident, cls, kvs) = (ident, filter (/= name) cls, kvs)
src/Text/Pandoc/Writers/DocBook.hs view
@@ -457,7 +457,7 @@       alt = if null ils                then mempty                else inTagsIndented "textobject" $-                    inTagsSimple "phrase" $ literal (stringify ils)+                    inTagsSimple "phrase" $ literal (stringifyInlines ils)   in  inTagsIndented "inlinemediaobject" $         inTagsIndented "imageobject"           (titleDoc $$ imageToDocBook opts attr src)
src/Text/Pandoc/Writers/Docx.hs view
@@ -23,6 +23,7 @@       findEntryByPath,       fromArchive,       toArchive,+      toArchiveOrFail,       toEntry,       Entry(eRelativePath) ) import Control.Monad (MonadPlus(mplus), foldM)@@ -506,10 +507,21 @@   P.setUserDataDir oldUserDataDir   let distArchive = toArchive $ BL.fromStrict res   refArchive <- case writerReferenceDoc opts of-                   Just f  -> toArchive . BL.fromStrict . fst+                   Just f  -> do+                     arch <- toArchiveOrFail . BL.fromStrict . fst                                  <$> P.fetchItem (T.pack f)-                   Nothing -> toArchive . BL.fromStrict <$>-                        readDataFile "reference.docx"+                     case arch of+                       Left err -> throwError $ PandocParseError $+                         "Could not parse reference-doc " <>+                           tshow f <> ": " <> T.pack err+                       Right x -> pure x+                   Nothing -> do+                     arch <- toArchiveOrFail . BL.fromStrict <$>+                                 readDataFile "reference.docx"+                     case arch of+                       Left err -> throwError $ PandocParseError $+                         "Could not parse reference.docx: " <> T.pack err+                       Right x -> pure x   return (refArchive, distArchive, username, utctime)  isWmlNamespace :: QName -> Bool@@ -761,7 +773,7 @@ mkCorePropsEntry :: Integer -> UTCTime -> Meta -> Entry mkCorePropsEntry epochtime utctime meta =   let metaValueToText (MetaString s)    = s-      metaValueToText (MetaInlines ils) = stringify ils+      metaValueToText (MetaInlines ils) = stringifyInlines ils       metaValueToText (MetaBlocks bs)   = stringify bs       metaValueToText (MetaBool b)      = T.pack (show b)       metaValueToText _                 = ""@@ -785,8 +797,8 @@           ,("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 "dc:title" [] (stringifyInlines $ docTitle meta)+          : mktnode "dc:creator" [] (T.intercalate "; " (map stringifyInlines $ docAuthors meta))           : [ mktnode (M.findWithDefault "" k extraCorePropsMap) [] (lookupMetaString' k meta)             | k <- M.keys (unMeta meta), k `elem` extraCoreProps]           ++ mknode "cp:keywords" [] (T.intercalate ", " keywords)
src/Text/Pandoc/Writers/Docx/OpenXML.hs view
@@ -25,7 +25,7 @@ import Control.Monad.Except (catchError) import Crypto.Hash (hashWith, SHA1(SHA1)) import qualified Data.ByteString.Lazy as BL-import Data.Char (isLetter, isSpace)+import Data.Char (isSpace, isAlphaNum) import Text.Pandoc.Char (isCJK) import Data.Ord (comparing) import Data.String (fromString)@@ -110,18 +110,60 @@     , "oMath"     ] [0..]) -sortSquashed :: [Element] -> [Element]-sortSquashed l =+-- from wml.xsd EG_PPrBase+pPrTagOrder :: M.Map Text Int+pPrTagOrder =+  M.fromList+  (zip [ "pStyle"+    , "keepNext"+    , "keepLines"+    , "pageBreakBefore"+    , "framePr"+    , "widowControl"+    , "numPr"+    , "suppressLineNumbers"+    , "pBdr"+    , "shd"+    , "tabs"+    , "suppressAutoHyphens"+    , "kinsoku"+    , "wordWrap"+    , "overflowPunct"+    , "topLinePunct"+    , "autoSpaceDE"+    , "autoSpaceDN"+    , "bidi"+    , "adjustRightInd"+    , "snapToGrid"+    , "spacing"+    , "ind"+    , "contextualSpacing"+    , "mirrorIndents"+    , "suppressOverlap"+    , "jc"+    , "textDirection"+    , "textAlignment"+    , "textboxTightWrap"+    , "outlineLvl"+    , "divId"+    , "cnfStyle"+    , "rPr"+    , "sectPr"+    , "pPrChange"+    ] [0..])++sortSquashed :: M.Map Text Int -> [Element] -> [Element]+sortSquashed tagOrder l =   sortBy (comparing tagIndex) l   where     tagIndex :: Element -> Int     tagIndex el =-      fromMaybe 0 (M.lookup tag rPrTagOrder)+      fromMaybe 0 (M.lookup tag tagOrder)       where tag = (qName . elName) el -squashProps :: EnvProps -> [Element]-squashProps (EnvProps Nothing es) = sortSquashed es-squashProps (EnvProps (Just e) es) = sortSquashed (e : es)+squashProps :: M.Map Text Int -> EnvProps -> [Element]+squashProps tagOrder (EnvProps Nothing es) = sortSquashed tagOrder es+squashProps tagOrder (EnvProps (Just e) es) = sortSquashed tagOrder (e : es)  -- | Certain characters are invalid in XML even if escaped. -- See #1992@@ -236,12 +278,18 @@              -> WS m (Text, [Element], [Element]) writeOpenXML opts (Pandoc meta blocks) = do   setupTranslations meta+  -- Cache the rStyle element for each highlighting token type, so that+  -- it need not be recomputed for every Code inline.  It depends only+  -- on the style maps, which don't change during writing.+  tokTypesMap <- M.fromList <$>+    mapM (\tt -> (tt,) <$> rStyleM (fromString $ show tt)) [KeywordTok ..]+  modify $ \st -> st{ stTokTypesMap = tokTypesMap }   let includeTOC = writerTableOfContents opts || lookupMetaBool "toc" meta   let includeLOF = writerListOfFigures opts || lookupMetaBool "lof" meta   let includeLOT = writerListOfTables opts || lookupMetaBool "lot" meta   abstractTitle <- case lookupMeta "abstract-title" meta of       Just (MetaBlocks bs)   -> pure $ stringify bs-      Just (MetaInlines ils) -> pure $ stringify ils+      Just (MetaInlines ils) -> pure $ stringifyInlines ils       Just (MetaString s)    -> pure s       _                      -> translateTerm Abstract   abstract <-@@ -326,9 +374,13 @@  -- | Convert a list of Pandoc blocks to OpenXML. blocksToOpenXML :: (PandocMonad m) => WriterOptions -> [Block] -> WS m [Content]-blocksToOpenXML opts =-  fmap concat . mapM (blockToOpenXML opts)-  . separateTables . filter (not . isForeignRawBlock)+blocksToOpenXML opts bs = do+  oldFirstPara <- gets stFirstPara+  modify $ \st -> st{ stFirstPara = True }+  result <- concat <$> mapM (blockToOpenXML opts)+            (separateTables (filter (not . isForeignRawBlock) bs))+  modify $ \st -> st{ stFirstPara = oldFirstPara }+  pure result  isForeignRawBlock :: Block -> Bool isForeignRawBlock (RawBlock format _) = format /= "openxml"@@ -358,12 +410,34 @@ dynamicStyleKey :: Text dynamicStyleKey = "custom-style" +-- | Paragraph properties for a CSL-generated bibliography, derived from+-- the hints @Text.Pandoc.Citeproc@ puts on the bibliography's Div:+-- a @hanging-indent@ class and @line-spacing@/@entry-spacing@ attributes.+cslBibParaProps :: [Text] -> [(Text, Text)] -> [Element]+cslBibParaProps classes kvs =+  [ mknode "w:ind" [("w:left", "720"), ("w:hanging", "720")] ()+  | "hanging-indent" `elem` classes ] +++  [ mknode "w:spacing" spacingAttrs () | not (null spacingAttrs) ]+  where+    spacingAttrs = lineAttr ++ entryAttr+    lineAttr = case lookup "line-spacing" kvs >>= safeRead of+                 Just ls | ls > (1 :: Double) ->+                   [ ("w:line", tshow (round (ls * 240) :: Int))+                   , ("w:lineRule", "auto") ]+                 _ -> []+    entryAttr = case lookup "entry-spacing" kvs >>= safeRead of+                  Just es | es > (0 :: Double) ->+                    -- entry-spacing is given in em; 1 em ~ 240 twips+                    [ ("w:after", tshow (round (es * 240) :: Int)) ]+                  _ -> []+ -- | Convert a Pandoc block element to OpenXML. blockToOpenXML :: (PandocMonad m) => WriterOptions -> Block -> WS m [Content] blockToOpenXML opts blk = withDirection $ blockToOpenXML' opts blk  blockToOpenXML' :: (PandocMonad m) => WriterOptions -> Block -> WS m [Content]-blockToOpenXML' opts (Div (ident,_classes,kvs) bs) = do+blockToOpenXML' opts (Div (ident,classes,kvs) bs) = do+  when ("math" `elem` classes) $ setFirstPara   stylemod <- case lookup dynamicStyleKey kvs of                    Just (fromString . T.unpack -> sty) -> do                       modify $ \s ->@@ -384,9 +458,18 @@   let langmod = case lookup "lang" kvs of                   Nothing -> id                   Just lang -> local (\env -> env{envLang = Just lang})+  -- citeproc adds formatting hints for bibliographies generated+  -- from a CSL style; see Text.Pandoc.Citeproc (#11871).+  let isCslBib = ident == "refs" || "csl-bib-body" `elem` classes+  let cslmod = if not isCslBib+                  then id+                  else case cslBibParaProps classes kvs of+                         []    -> id+                         props -> foldr (.) id (map withParaProp props)   header <- dirmod $ stylemod $ blocksToOpenXML opts hs-  contents <- dirmod $ bibmod $ stylemod $ langmod $ blocksToOpenXML opts bs'+  contents <- dirmod $ bibmod $ cslmod $ stylemod $ langmod $ blocksToOpenXML opts bs'   wrapBookmark ident $ header <> contents+ blockToOpenXML' opts (Header lev (ident,_,kvs) lst) = do   setFirstPara   let isSection = case writerTopLevelDivision opts of@@ -447,16 +530,16 @@       let displayMathPara = case lst of                                  [x] -> isDisplayMath x                                  _   -> False-      paraProps <- getParaProps displayMathPara       bodyTextStyle <- pStyleM $ if isFirstPara                        then "First Paragraph"                        else "Body Text"-      let paraProps' = case paraProps of-            []               -> [mknode "w:pPr" [] [bodyTextStyle]]-            ps               -> ps+      paraProps <- local (\env -> env{ envParaProperties =+                                        envParaProperties env <>+                                        EnvProps (Just bodyTextStyle) [] })+                      (getParaProps displayMathPara)       modify $ \s -> s { stFirstPara = False }       contents <- inlinesToOpenXML opts lst-      return [Elem $ mknode "w:p" [] (map Elem paraProps' ++ contents)]+      return [Elem $ mknode "w:p" [] (map Elem paraProps ++ contents)] blockToOpenXML' opts (LineBlock lns) = blockToOpenXML opts $ linesToPara lns blockToOpenXML' _ b@(RawBlock format str)   | format == Format "openxml" = return [@@ -678,7 +761,7 @@                    Nothing -> mempty                    Just l  -> EnvProps Nothing                                [mknode "w:lang" [("w:val", l)] ()]-  let squashed = squashProps (props <> langnode)+  let squashed = squashProps rPrTagOrder (props <> langnode)   return [mknode "w:rPr" [] squashed | (not . null) squashed]  withTextProp :: PandocMonad m => Element -> WS m a -> WS m a@@ -704,7 +787,7 @@   let listPr = [mknode "w:numPr" []                 [ mknode "w:ilvl" [("w:val",tshow listLevel)] ()                 , mknode "w:numId" [("w:val",tshow numid')] () ] | listLevel >= 0 && not displayMathPara]-  return $ case squashProps (EnvProps Nothing listPr <> props) of+  return $ case squashProps pPrTagOrder (EnvProps Nothing listPr <> props) of                 [] -> []                 ps -> [mknode "w:pPr" [] ps] @@ -754,8 +837,8 @@ inlineToOpenXML' :: PandocMonad m => WriterOptions -> Inline -> WS m [Content] inlineToOpenXML' _ (Str str) =   map Elem <$> formattedString str-inlineToOpenXML' opts Space = inlineToOpenXML opts (Str " ")-inlineToOpenXML' opts SoftBreak = inlineToOpenXML opts (Str " ")+inlineToOpenXML' opts Space = inlineToOpenXML' opts (Str " ")+inlineToOpenXML' opts SoftBreak = inlineToOpenXML' opts (Str " ") inlineToOpenXML' opts (Span ("",["mark"],[]) ils) =   withTextProp (mknode "w:highlight" [("w:val","yellow")] ()) $     inlinesToOpenXML opts ils@@ -773,16 +856,13 @@ inlineToOpenXML' opts (Span ("",["csl-indent"],[]) ils) =   inlinesToOpenXML opts ils inlineToOpenXML' _ (Span (ident,["comment-start"],kvs) ils) = do-  -- prefer the "id" in kvs, since that is the one produced by the docx-  -- reader.-  let ident' = fromMaybe ident (lookup "id" kvs)-      kvs' = filter (("id" /=) . fst) kvs+  let ident' = fromMaybe ident (lookup "comment-id" kvs <|> lookup "id" kvs)+      kvs' = filter ((\x -> x /= "comment-id" && x /= "id") . fst) kvs   modify $ \st -> st{ stComments = (("id",ident'):kvs', ils) : stComments st }   return [ Elem $ mknode "w:commentRangeStart" [("w:id", ident')] () ] inlineToOpenXML' opts (Span (ident,["comment-end"],kvs) content) = do-  -- prefer the "id" in kvs, since that is the one produced by the docx-  -- reader.-  let ident' = fromMaybe ident (lookup "id" kvs)+  -- now we use comment-id, but support id for legacy compat:+  let ident' = fromMaybe ident (lookup "comment-id" kvs <|> lookup "id" kvs)   -- process nested content: see #8189   nestedContent <- inlinesToOpenXML opts content   let thisCommentEnd =@@ -887,15 +967,14 @@        Left il -> inlineToOpenXML' opts il inlineToOpenXML' opts (Cite _ lst) = inlinesToOpenXML opts lst inlineToOpenXML' opts (Code attrs str) = do-  let alltoktypes = [KeywordTok ..]-  tokTypesMap <- mapM (\tt -> (,) tt <$> rStyleM (fromString $ show tt)) alltoktypes+  tokTypesMap <- gets stTokTypesMap   let unhighlighted = (map Elem . intercalate [br]) `fmap`                        mapM formattedString (T.lines str)       formatOpenXML _fmtOpts = intercalate [br] . map (map toHlTok)       toHlTok (toktype,tok) =         mknode "w:r" []           [ mknode "w:rPr" [] $-            maybeToList (lookup toktype tokTypesMap)+            maybeToList (M.lookup toktype tokTypesMap)             , mknode "w:t" [("xml:space","preserve")] tok ]   let highlighted =         case highlight (writerSyntaxMap opts) formatOpenXML attrs str of@@ -911,7 +990,6 @@ inlineToOpenXML' opts (Note bs) = do   notes <- gets stFootnotes   notenum <- getUniqueId-  oldFirstPara <- gets stFirstPara   footnoteStyle <- rStyleM "Footnote Reference"   let notemarker = mknode "w:r" []                    [ mknode "w:rPr" [] footnoteStyle@@ -927,19 +1005,19 @@                                 , envInNote = True })               (withParaPropM (pStyleM "Footnote Text") $                blocksToOpenXML opts $ insertNoteRef bs)-  modify $ \s -> s{ stFirstPara = oldFirstPara }   let newnote = mknode "w:footnote" [("w:id", notenum)] contents   modify $ \s -> s{ stFootnotes = newnote : notes }   return [ Elem $ mknode "w:r" []            [ mknode "w:rPr" [] footnoteStyle            , mknode "w:footnoteReference" [("w:id", notenum)] () ] ] -- internal link:-inlineToOpenXML' opts (Link _ txt (T.uncons -> Just ('#', xs),_)) = do+inlineToOpenXML' opts (Link _ txt (T.uncons -> Just ('#', xs),title)) = do   contents <- withTextPropM (rStyleM "Hyperlink") $ inlinesToOpenXML opts txt   return-    [ Elem $ mknode "w:hyperlink" [("w:anchor", toBookmarkName xs)] contents ]+    [ Elem $ mknode "w:hyperlink"+        (("w:anchor", toBookmarkName xs) : tooltipAttr title) contents ] -- external link:-inlineToOpenXML' opts (Link _ txt (src,_)) = do+inlineToOpenXML' opts (Link _ txt (src,title)) = do   contents <- withTextPropM (rStyleM "Hyperlink") $ inlinesToOpenXML opts txt   extlinks <- gets stExternalLinks   id' <- case M.lookup src extlinks of@@ -949,7 +1027,8 @@               modify $ \st -> st{ stExternalLinks =                         M.insert src i extlinks }               return i-  return [ Elem $ mknode "w:hyperlink" [("r:id",id')] contents ]+  return [ Elem $ mknode "w:hyperlink" (("r:id",id') : tooltipAttr title)+             contents ] inlineToOpenXML' opts (Image attr@(imgident, _, _) alt (src, title)) = do   pageWidth <- asks envPrintWidth   imgs <- gets stImages@@ -995,10 +1074,23 @@         (xpt,ypt) = desiredSizeInPoints opts attr                (either (const def) id (imageSize opts img))         -- 12700 emu = 1 pt-        pageWidthPt = case dimension Width attr of-                        Just (Percent a) -> pageWidth * floor (a * 127)-                        _                -> pageWidth * 12700-        (xemu,yemu) = fitToPage (xpt * 12700, ypt * 12700) pageWidthPt+        pageWidthPt = fromIntegral pageWidth+        pageWidthEmu = pageWidth * 12700+        (xpt', ypt') =+          case (dimension Width attr, dimension Height attr) of+                 (Just (Percent a), Just (Percent b))+                   -> ((a / 100.0) * pageWidthPt, (b / 100.0) * pageWidthPt)+                      -- note, should use pageHeightPt but we don't have this+                      -- information.+                 (Just (Percent a), _)+                   -> ((a / 100.0) * pageWidthPt,+                       (a / 100.0) * pageWidthPt * (ypt / xpt))+                 (_, Just (Percent b))+                   -> ((b / 100.0) * pageWidthPt * (xpt / ypt),+                       (b / 100.0) * pageWidthPt)+                 (_, _) -> (xpt, ypt)+        (xemu,yemu) = fitToPage (xpt' * 12700,+                                 ypt' * 12700) pageWidthEmu         cNvPicPr = mknode "pic:cNvPicPr" [] $                          mknode "a:picLocks" [("noChangeArrowheads","1")                                              ,("noChangeAspect","1")] ()@@ -1041,7 +1133,7 @@               , mknode "wp:effectExtent"                 [("b","0"),("l","0"),("r","0"),("t","0")] ()               , mknode "wp:docPr"-                [ ("descr", stringify alt)+                [ ("descr", stringifyInlines alt)                 , ("title", title)                 , ("id", docprid)                 , ("name","Picture")@@ -1102,11 +1194,17 @@   -- We want to clean all bidirection (bidi) and right-to-left (rtl)   -- properties from the props first. This is because we don't want   -- them to stack up.-  let paraProps' = filter (\e -> (qName . elName) e /= "bidi") (otherElements paraProps)+  let hasBidi = any (\e -> (qName . elName) e == "bidi") (otherElements paraProps)+      hasRtl = any (\e -> (qName . elName) e == "rtl") (otherElements textProps)+      paraProps' = filter (\e -> (qName . elName) e /= "bidi") (otherElements paraProps)       textProps' = filter (\e -> (qName . elName) e /= "rtl") (otherElements textProps)       paraStyle = styleElement paraProps       textStyle = styleElement textProps-  if isRTL+  if not isRTL && not hasBidi && not hasRtl+    -- fast path: LTR with no bidi/rtl props to remove, so the+    -- environment is unchanged; skip the 'local' rebuild.+    then x+    else if isRTL     -- if we are going right-to-left, we (re?)add the properties.     then flip local x $          \env -> env { envParaProperties = EnvProps paraStyle $ mknode "w:bidi" [] () : paraProps'@@ -1127,20 +1225,35 @@   return $ Elem bookmarkStart : contents ++ [Elem bookmarkEnd]  -- Word imposes a 40 character limit on bookmark names and requires--- that they begin with a letter.  So we just use a hash of the--- identifier when otherwise we'd have an illegal bookmark name.+-- that they begin with a letter or @_@ and contain only letters,+-- numbers or underscores. Bookmarks beginning with @_@ are+-- hidden in the user interface (and in particular hidden from screen+-- readers, which we want); these are to be used for cross-references.+-- When the id is otherwise illegal we use a hash of the identifier. toBookmarkName :: Text -> Text toBookmarkName s-  | Just (c, _) <- T.uncons s-  , isLetter c-  , T.length s <= 40 = s-  | otherwise = T.pack $ 'X' : drop 1 (show (hashWith SHA1 (fromText s)))+  | T.length s < 40+  , T.all (\c -> isAlphaNum c || c == '_') s+    = "_" <> s+  | otherwise = "_" <> T.pack (drop 1 (show (hashWith SHA1 (fromText s))))+  -- we drop 1 because a SHA1 is 40 characters and we need room for the `_` +-- A link's title is written as its ScreenTip (@w:tooltip@).+tooltipAttr :: Text -> [(Text, Text)]+tooltipAttr title = [("w:tooltip", title) | not (T.null title)]+ maxListLevel :: Int maxListLevel = 8 +-- Merge adjacent Strs, and any Space between two Strs, into a single+-- Str.  Chunks are accumulated and concatenated all at once, to avoid+-- quadratic copying when a long Str/Space sequence (e.g. an entire+-- paragraph) collapses into one Str. convertSpace :: [Inline] -> [Inline]-convertSpace (Str x : Space : Str y : xs) = convertSpace (Str (x <> " " <> y) : xs)-convertSpace (Str x : Str y : xs)         = convertSpace (Str (x <> y) : xs)-convertSpace (x:xs)                       = x : convertSpace xs-convertSpace []                           = []+convertSpace (Str x : xs) = go [x] xs+  where+    go acc (Str y : ys)         = go (y : acc) ys+    go acc (Space : Str y : ys) = go (y : " " : acc) ys+    go acc ys = Str (T.concat (reverse acc)) : convertSpace ys+convertSpace (x:xs) = x : convertSpace xs+convertSpace [] = []
src/Text/Pandoc/Writers/Docx/Table.hs view
@@ -178,7 +178,9 @@     rowwidth  = round (fullrow * sum widths) :: Int     widthToTwips w = floor (textwidth * w)   :: Int     mkGridCol w = mknode "w:gridCol" [("w:w", tshow (widthToTwips w))] ()-  in if all (== 0) widths+    -- A "mixed" table with both default and given widths is treated as+    -- a table with all default widths.+  in if any (== 0) widths      then ( replicate ncols $ mkGridCol (1.0 / fromIntegral ncols)           , [ ("w:type", "auto"), ("w:w", "0")])      else ( map mkGridCol widths
src/Text/Pandoc/Writers/Docx/Types.hs view
@@ -27,6 +27,7 @@ import Control.Monad.Reader import Control.Monad.State.Strict import Data.Text (Text)+import Skylighting (TokenType) import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.MIME (MimeType)@@ -68,7 +69,7 @@  data EnvProps = EnvProps{ styleElement  :: Maybe Element                         , otherElements :: [Element]-                        }+                        } deriving (Show)  instance Semigroup EnvProps where   EnvProps s es <> EnvProps s' es' = EnvProps (s <|> s') (es ++ es')@@ -87,7 +88,7 @@   , envInNote         :: Bool   , envChangesAuthor  :: Text   , envChangesDate    :: Text-  , envPrintWidth     :: Integer+  , envPrintWidth     :: Integer  -- in points   , envLang           :: Maybe Text   , envSectPr         :: Maybe Element   }@@ -120,6 +121,9 @@        , stInsId          :: Int        , stDelId          :: Int        , stStyleMaps      :: StyleMaps+       , stTokTypesMap    :: M.Map TokenType Element+         -- ^ cached rStyle element for each highlighting token type;+         --   computed once from stStyleMaps at the start of writing        , stFirstPara      :: Bool        , stFirstSectionHeader :: Bool  -- ^ True until first section header is processed        , stNumIdUsed      :: Bool  -- ^ True if the current numId (envListNumId) has been used.@@ -146,6 +150,7 @@       , stInsId          = 1       , stDelId          = 1       , stStyleMaps      = StyleMaps M.empty M.empty+      , stTokTypesMap    = M.empty       , stFirstPara      = False       , stFirstSectionHeader = True       , stNumIdUsed      = False
src/Text/Pandoc/Writers/EPUB.hs view
@@ -22,10 +22,11 @@ import Control.Monad.Except (catchError, throwError) import Control.Monad.State.Strict (State, StateT, evalState, evalStateT, get,                                    gets, lift, modify)+import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as B import qualified Data.ByteString.Lazy.Char8 as B8 import Data.Char (isAlphaNum, isAscii, isDigit)-import Data.List (isInfixOf, isPrefixOf)+import Data.List (isPrefixOf) import qualified Data.Map as M import Data.Maybe (fromMaybe, isNothing, mapMaybe, isJust, catMaybes) import qualified Data.Set as Set@@ -38,7 +39,6 @@ import Text.Pandoc.Writers.Shared (ensureValidXmlIdentifiers) import Data.Tree (Tree(..)) import Text.Pandoc.Class (PandocMonad, report)-import qualified Text.Pandoc.Class.PandocPure as P import Text.Pandoc.Data (readDataFile) import qualified Text.Pandoc.Class.PandocMonad as P import Data.Time@@ -52,7 +52,7 @@                             ObfuscationMethod (NoObfuscation), WrapOption (..),                             WriterOptions (..)) import Text.Pandoc.Shared (normalizeDate, renderTags',-                           stringify, uniqueIdent, tshow)+                           stringify, stringifyInlines, uniqueIdent, tshow) import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.UUID (getRandomUUID) import Text.Pandoc.Walk (walk, walkM)@@ -215,7 +215,7 @@        if any (\c -> creatorRole c == Just "aut") $ epubCreator m           then return m           else do-            let authors' = map stringify $ docAuthors meta+            let authors' = map stringifyInlines $ docAuthors meta             let toAuthor name = Creator{ creatorText = name                                        , creatorRole = Just "aut"                                        , creatorFileAs = Nothing }@@ -280,7 +280,7 @@  metaValueToString :: MetaValue -> Text metaValueToString (MetaString s)    = s-metaValueToString (MetaInlines ils) = stringify ils+metaValueToString (MetaInlines ils) = stringifyInlines ils metaValueToString (MetaBlocks bs)   = stringify bs metaValueToString (MetaBool True)   = "true" metaValueToString (MetaBool False)  = "false"@@ -483,7 +483,7 @@                         [] -> case epubTitle metadata of                                    []    -> "UNTITLED"                                    (x:_) -> titleText x-                        x  -> stringify x+                        x  -> stringifyInlines x    -- stylesheet   stylesheets <- case epubStylesheets metadata of@@ -589,14 +589,11 @@                                     [("page-progression-direction", "rtl")]                                   _  -> [] -  -- incredibly inefficient (TODO):-  let containsMathML ent = epub3 &&-                           "<math" `isInfixOf`-        B8.unpack (fromEntry ent)-  let containsSVG ent    = epub3 &&-                           "<svg" `isInfixOf`-        B8.unpack (fromEntry ent)-  let props ent = ["mathml" | containsMathML ent] ++ ["svg" | containsSVG ent]+  let props ent+        | epub3 = let contents = B.toStrict (fromEntry ent)+                  in  ["mathml" | "<math" `BS.isInfixOf` contents] +++                      ["svg" | "<svg" `BS.isInfixOf` contents]+        | otherwise = []    let chapterNode ent = unode "item" !                            ([("id", toId $ makeRelative epubSubdir@@ -859,7 +856,7 @@           let secnum' = case secNumber secinfo of                           Just t -> t <> " "                           Nothing -> ""-          let title' = secnum' <> stringify (secTitle secinfo)+          let title' = secnum' <> stringifyInlines (secTitle secinfo)           return $ Just $ unode "navPoint" !                    [("id", "navPoint-" <> tshow n)] $                       [ unode "navLabel" $ unode "text" title'@@ -869,7 +866,7 @@    let tpNode = unode "navPoint" !  [("id", "navPoint-0")] $                   [ unode "navLabel" $ unode "text"-                     (stringify $ docTitle' meta)+                     (stringifyInlines $ docTitle' meta)                   , unode "content" ! [("src", "text/title_page.xhtml")]                   $ () ] @@ -913,8 +910,7 @@                 -> StateT EPUBState m Entry createNavEntry opts meta metadata                vars cssvars writeHtml tocTitle version (Node _ secs) = do-  let mkItem :: Tree SecInfo -> State Int (Maybe Element)-      mkItem (Node secinfo subsecs)+  let mkItem (Node secinfo subsecs)         | secLevel secinfo > writerTOCDepth opts = return Nothing         | otherwise = do           n <- get@@ -929,13 +925,14 @@           let clean (Link _ ils _) = Span ("", [], []) ils               clean (Note _)       = Str ""               clean x              = x-          let titRendered = case P.runPure-                                  (writeHtmlStringForEPUB version-                                    opts{ writerTemplate = Nothing }-                                    (Pandoc nullMeta-                                      [Plain $ walk clean title'])) of-                                  Left _  -> stringify title'-                                  Right x -> x+          -- render in the host monad, so that a translation table+          -- loaded for one title is reused for the others:+          titRendered <- lift $ catchError+                           (writeHtmlStringForEPUB version+                              opts{ writerTemplate = Nothing }+                              (Pandoc nullMeta+                                [Plain $ walk clean title']))+                           (\_ -> return $ stringifyInlines title')           let titElements = either (const []) id $                                   parseXMLContents (TL.fromStrict titRendered) @@ -949,7 +946,7 @@                            (_:_) -> [unode "ol" ! [("class","toc")] $ subs]    let navtag = if version == EPUB3 then "nav" else "div"-  let tocBlocks = evalState (catMaybes <$> mapM mkItem secs) 1+  tocBlocks <- lift $ evalStateT (catMaybes <$> mapM mkItem secs) (1 :: Int)   let navBlocks = [RawBlock (Format "html")                   $ showElement $ -- prettyprinting introduces bad spaces                    unode navtag ! ([("epub:type","toc") | version == EPUB3] ++
src/Text/Pandoc/Writers/FB2.hs view
@@ -38,7 +38,7 @@ import Text.Pandoc.Logging import Text.Pandoc.Options (MathMethod (..), WriterOptions (..), def) import Text.Pandoc.Shared (blocksToInlines, capitalize, orderedListMarkers,-                           makeSections, tshow, stringify)+                           makeSections, tshow, stringifyInlines) import Text.Pandoc.Walk (walk) import Text.Pandoc.Writers.Shared (lookupMetaString, toLegacyTable,                                    ensureValidXmlIdentifiers)@@ -118,7 +118,7 @@         im <- insertImage InlineImage img         return [el "coverpage" im]   coverpage <- case lookupMeta "cover-image" meta' of-                    Just (MetaInlines ils) -> coverimage (stringify ils)+                    Just (MetaInlines ils) -> coverimage (stringifyInlines ils)                     Just (MetaString s) -> coverimage s                     _       -> return []   return $ el "description"
src/Text/Pandoc/Writers/HTML.hs view
@@ -48,7 +48,7 @@ import Text.Pandoc.URI (urlEncode) import Numeric (showHex) import Text.DocLayout (render, literal, Doc)-import Text.Blaze.Internal (MarkupM (Empty), customLeaf, customParent)+import Text.Blaze.Internal (MarkupM (Append, Empty), customLeaf, customParent) import Text.DocTemplates (FromContext (lookupContext), Context (..), Val(..)) import qualified Text.DocTemplates.Internal as DT import Text.Blaze.Html hiding (contents)@@ -125,20 +125,24 @@  strToHtml :: Text -> Html strToHtml t-    | T.any isSpecial t =-       let !x = L.foldl' go mempty $ T.groupBy samegroup t-        in x+    | T.any isSpecial t = go t     | otherwise = toHtml t   where-    samegroup c d = d == '\xFE0E' || not (isSpecial c || isSpecial d)     isSpecial '\'' = True     isSpecial '"' = True     isSpecial c = needsVariationSelector c-    go h "\'" = h <> preEscapedString "\'"-    go h "\"" = h <> preEscapedString "\""-    go h txt | T.length txt == 1 && T.all needsVariationSelector txt-           = h <> preEscapedString (T.unpack txt <> "\xFE0E")-    go h txt = h <> toHtml txt+    go s =+      let (plain, rest) = T.break isSpecial s+          html = if T.null plain then mempty else toHtml plain+      in  case T.uncons rest of+            Nothing -> html+            Just ('\'', rest') -> html <> preEscapedText "'" <> go rest'+            Just ('"', rest')  -> html <> preEscapedText "\"" <> go rest'+            Just (c, rest')+              -- don't add a variation selector if one is already there:+              | T.take 1 rest' == "\xFE0E" -> html <> toHtml c <> go rest'+              | otherwise -> html <> preEscapedText (T.pack [c, '\xFE0E'])+                                  <> go rest'  -- See #5469: this prevents iOS from substituting emojis. needsVariationSelector :: Char -> Bool@@ -150,6 +154,14 @@ nl :: Html nl = preEscapedString "\n" +-- | True if the markup contains no content at all.  'mconcat' and+-- '<>' on 'MarkupM' build 'Append' nodes without collapsing empty+-- markup, so simply matching on 'Empty' is not enough.+isEmptyMarkup :: MarkupM a -> Bool+isEmptyMarkup (Empty _)    = True+isEmptyMarkup (Append x y) = isEmptyMarkup x && isEmptyMarkup y+isEmptyMarkup _            = False+ -- | Convert Pandoc document to Html 5 string. writeHtml5String :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeHtml5String = writeHtmlString'@@ -286,7 +298,7 @@               (fmap layoutMarkup . blockListToHtml opts)               (fmap layoutMarkup . inlineListToHtml opts)               meta-  let stringifyHTML = escapeStringForXML . stringify+  let stringifyHTML = escapeStringForXML . stringifyInlines   let authsMeta = map (literal . stringifyHTML) $ docAuthors meta   let dateMeta  = stringifyHTML $ docDate meta   let descriptionMeta = literal $ escapeStringForXML $@@ -352,7 +364,7 @@             ]           nl           H.link ! A.rel "stylesheet" !-            A.href (toValue $ toURI html5 url <> "katex.min.css")+            A.href (toValue $ toURI html5 $ url <> "katex.min.css")          _ -> mempty   let mCss :: Maybe [Text] = lookupContext "css" metadata@@ -572,7 +584,7 @@   let container x         | html5         , epubVersion == Just EPUB3-                = H5.section ! A.id (fromString idName)+                = H5.section ! prefixedId opts (fromString idName)                              ! A.class_ className                              ! customAttribute "epub:type" "footnotes" $ x         | html5@@ -633,16 +645,24 @@               (linkText, altText) =                  if txt == T.drop 7 s' -- autolink                     then ("e", name' <> " at " <> domain')-                    else ("'" <> obfuscateString txt <> "'",+                    else ("'" <>+                          T.replace "</" "<\\/" (obfuscateMarkup txt) <> "'",                           txt <> " (" <> name' <> " at " <> domain' <> ")")-              (_, classNames, _) = attr+              (ident, classNames, kvs) = attr               classNamesStr = T.concat $ map (" "<>) classNames+              otherAttrsStr = T.concat $+                [ " id=\"" <>+                  escapeJSAttrVal (writerIdentifierPrefix opts <> ident) <>+                  "\"" | not (T.null ident) ] +++                [ " " <> k <> "=\"" <> escapeJSAttrVal v <> "\""+                | (k, v) <- kvs ]           in  case meth of                 ReferenceObfuscation ->-                     -- need to use preEscapedString or &'s are escaped to &amp; in URL-                     return $-                     preEscapedText $ "<a href=\"" <> obfuscateString s'-                     <> "\" class=\"email\">" <> obfuscateString txt <> "</a>"+                     -- preEscaped is needed or the &'s in the+                     -- entity-obfuscated text are escaped to &amp;+                     addAttrs opts (ident, "email":classNames, kvs) $+                       H.a ! A.href (preEscapedToValue $ obfuscateString s')+                           $ preEscapedText $ obfuscateMarkup txt                 JavascriptObfuscation ->                      return $                      (H.script ! A.type_ "text/javascript" $@@ -650,12 +670,12 @@                      obfuscateString domain <> "';a='" <> at' <> "';n='" <>                      obfuscateString name' <> "';e=n+a+h;\n" <>                      "document.write('<a h'+'ref'+'=\"ma'+'ilto'+':'+e+'\" clas'+'s=\"em' + 'ail" <>-                     classNamesStr <> "\">'+" <>+                     classNamesStr <> "\"" <> otherAttrsStr <> ">'+" <>                      linkText  <> "+'<\\/'+'a'+'>');\n// -->\n")) >>-                     H.noscript (preEscapedText $ obfuscateString altText)+                     H.noscript (preEscapedText $ obfuscateMarkup altText)                 _ -> throwError $ PandocSomeError $ "Unknown obfuscation method: " <> tshow meth         _ -> addAttrs opts attr $ H.a ! A.href (toValue $ toURI html5 s)-                                      $ toHtml txt  -- malformed email+                                      $ preEscapedText txt  -- malformed email  -- | Obfuscate character as entity. obfuscateChar :: Char -> Text@@ -668,6 +688,37 @@ obfuscateString :: Text -> Text obfuscateString = T.concatMap obfuscateChar . fromEntities +-- | Obfuscate the character data in a rendered HTML fragment,+-- leaving the tags themselves intact.+obfuscateMarkup :: Text -> Text+obfuscateMarkup t+  | T.null t = ""+  | otherwise =+      let (chars, rest)  = T.break (== '<') t+          (tag, rest') = T.break (== '>') rest+      in  obfuscateString chars <>+          case T.uncons rest' of+            Just ('>', rest'') -> tag <> ">" <> obfuscateMarkup rest''+            _ -> tag  -- unterminated tag; emit as is++-- | Escape text for an HTML attribute value that is embedded in a+-- single-quoted JavaScript string literal (as used in+-- 'JavascriptObfuscation').  Everything problematic is replaced+-- with an entity, which the HTML parser decodes when the string is+-- written to the document.+escapeJSAttrVal :: Text -> Text+escapeJSAttrVal = T.concatMap $ \c ->+  case c of+    '&'  -> "&amp;"+    '<'  -> "&lt;"+    '>'  -> "&gt;"+    '"'  -> "&quot;"+    '\'' -> "&#39;"+    '\\' -> "&#92;"+    '\n' -> "&#10;"+    '\r' -> "&#13;"+    _    -> T.singleton c+ -- | Create HTML tag with attributes. tagWithAttributes :: WriterOptions                   -> Bool -- ^ True for HTML5@@ -703,7 +754,7 @@   addAttr html5 mbEpubVersion x y     | T.null x = id  -- see #7546     | html5-      = if (x `Set.member` (html5Attributes <> rdfaAttributes)+      = if (x `Set.member` html5AttrsPlusRdfa             && x /= "label") -- #10048              || T.any (== ':') x -- e.g. epub: namespace              || "data-" `T.isPrefixOf` x@@ -711,12 +762,19 @@            then (customAttribute (textTag x) (toValue y) :)            else (customAttribute (textTag ("data-" <> x)) (toValue y) :)     | mbEpubVersion == Just EPUB2-    , not (x `Set.member` (html4Attributes <> rdfaAttributes) ||+    , not (x `Set.member` html4AttrsPlusRdfa ||       "xml:" `T.isPrefixOf` x)       = id     | otherwise       = (customAttribute (textTag x) (toValue y) :) +-- Top-level constants, so that the set unions are computed only once.+html5AttrsPlusRdfa :: Set.Set Text+html5AttrsPlusRdfa = html5Attributes <> rdfaAttributes++html4AttrsPlusRdfa :: Set.Set Text+html4AttrsPlusRdfa = html4Attributes <> rdfaAttributes+ attrsToHtml :: PandocMonad m             => WriterOptions -> Attr -> StateT WriterState m [Attribute] attrsToHtml opts (id',classes',keyvals) = do@@ -764,9 +822,9 @@           inlineToHtml opts (Image attr txt (src, tit))     _ -> do       contents <- inlineListToHtml opts lst-      case contents of-        Empty _ | not (isEnabled Ext_empty_paragraphs opts) -> return mempty-        _ -> return $ H.p contents+      if isEmptyMarkup contents && not (isEnabled Ext_empty_paragraphs opts)+         then return mempty+         else return $ H.p contents blockToHtmlInner opts (LineBlock lns) = do   htmlLines <- inlineListToHtml opts $ intercalate [LineBreak] lns   return $ H.div ! A.class_ "line-block" $ htmlLines@@ -796,17 +854,18 @@   let inDiv' zs = RawBlock (Format "html") ("<div class=\""                        <> fragmentClass <> "\">") :                    (zs ++ [RawBlock (Format "html") "</div>"])-  let breakOnPauses zs-        | slide = case splitBy isPause zs of+  let breakOnPauses zs = case splitBy isPause zs of                            []   -> []                            y:ys -> y ++ concatMap inDiv' ys-        | otherwise = zs+  let breakPauses = if slide+                       then walk breakOnPauses+                       else id  -- avoid a pointless traversal   let (titleBlocks, innerSecs) =         if titleSlide            -- title slides have no content of their own            then let (as, bs) = break isSec xs-                in  (walk breakOnPauses as, bs)-           else ([], walk breakOnPauses xs)+                in  (breakPauses as, bs)+           else ([], breakPauses xs)   let secttag  = if html5                     then H5.section                     else H.div@@ -910,7 +969,7 @@                  then -- we don't use blockListToHtml because it inserts                       -- a newline between the column divs, which throws                       -- off widths! see #4028-                      mconcat <$> mapM (blockToHtml opts) bs'+                      mconcat <$> mapM (blockToHtml opts') bs'                  else blockListToHtml opts' bs'   let contents' = nl >> contents >> nl   let (divtag, classes'') = if html5 && "section" `elem` classes'@@ -1110,7 +1169,7 @@     else foldl (!) H.div (A.class_ "float" : figAttrs) innards  where   captionIsAlt capt [Plain [Image (_, _, kv) desc _]] =-    let alt = fromMaybe (stringify desc) $ lookup "alt" kv+    let alt = fromMaybe (stringifyInlines desc) $ lookup "alt" kv     in stringify capt == alt   captionIsAlt _ _ = False @@ -1169,7 +1228,7 @@   let attr' = case lookup "style" kvs of                 Nothing | totalWidth < 1 && totalWidth > 0                   -> (ident,classes, ("style","width:" <>-                         T.pack (show (round (totalWidth * 100) :: Int))+                         T.pack (show (truncate (totalWidth * 100) :: Int))                          <> "%;"):kvs)                 _ -> attr   addAttrs opts attr' $ H.table $ do@@ -1378,10 +1437,8 @@ blockListToHtml :: PandocMonad m                 => WriterOptions -> [Block] -> StateT WriterState m Html blockListToHtml opts lst =-  mconcat . intersperse (nl) . filter nonempty+  mconcat . intersperse (nl) . filter (not . isEmptyMarkup)     <$> mapM (blockToHtml opts) lst-  where nonempty (Empty _) = False-        nonempty _         = True  -- | Convert list of Pandoc inline elements to HTML. inlineListToHtml :: PandocMonad m => WriterOptions -> [Inline] -> StateT WriterState m Html@@ -1601,7 +1658,7 @@                                     else link' ! A.title (toValue tit)     (Image attr@(_, _, attrList) txt (s, tit)) -> do                         epubVersion <- gets stEPUBVersion-                        let alternate = stringify txt+                        let alternate = stringifyInlines txt                         slideVariant <- gets stSlideVariant                         let isReveal = slideVariant == RevealJsSlides                         attrs <- imgAttrsToHtml opts attr@@ -1788,7 +1845,7 @@ intrinsicEventsHTML4 :: [Text] intrinsicEventsHTML4 =   [ "onclick", "ondblclick", "onmousedown", "onmouseup", "onmouseover"-  , "onmouseout", "onmouseout", "onkeypress", "onkeydown", "onkeyup"]+  , "onmousemove", "onmouseout", "onkeypress", "onkeydown", "onkeyup"]   -- | Check to see if Format is valid HTML
src/Text/Pandoc/Writers/Haddock.hs view
@@ -264,7 +264,7 @@ inlineToHaddock _ Space = return space inlineToHaddock opts (Cite _ lst) = inlineListToHaddock opts lst inlineToHaddock _ (Link _ txt (src, _)) = do-  let linktext = literal $ escapeString $ stringify txt+  let linktext = literal $ escapeString $ stringifyInlines txt   let useAuto = isURI src &&                 case txt of                       [Str s] | escapeURI s == src -> True
src/Text/Pandoc/Writers/JATS.hs view
@@ -445,7 +445,7 @@   -- Remove the alt text from images if it's the same as the caption text.   let unsetAltIfDupl = \case         Image attr alt tgt-          | stringify alt == stringify longcapt -> Image attr [] tgt+          | stringifyInlines alt == stringify longcapt -> Image attr [] tgt         inline -> inline   capt <- if null longcapt           then pure empty@@ -487,7 +487,7 @@   where    fixCitations [] = []    fixCitations (x:xs) | needsFixing x =-     x : Str (stringify ys) : fixCitations zs+     x : Str (stringifyInlines ys) : fixCitations zs      where        needsFixing (RawInline (Format "jats") z) =            "<pub-id pub-id-type=" `T.isPrefixOf` z@@ -616,7 +616,7 @@ inlineToJATS opts (Link (ident,_,kvs) txt (T.uncons -> Just ('#', src), _)) = do   let attr = mconcat              [ [("id", escapeNCName ident) | not (T.null ident)]-             , [("alt", stringify txt) | not (null txt)]+             , [("alt", stringifyInlines txt) | not (null txt)]              , [("rid", escapeNCName src)]              , [(k,v) | (k,v) <- kvs, k `elem` ["ref-type", "specific-use"]]              , [("ref-type", "bibr") | "ref-" `T.isPrefixOf` src]@@ -670,7 +670,7 @@   if null alt   then Nothing   else Just . inTagsSimple "alt-text" .-       hsep . map literal . T.words $ stringify alt+       hsep . map literal . T.words $ stringifyInlines alt  imageMimeType :: Text -> [(Text, Text)] -> (Text, Text) imageMimeType src kvs =
src/Text/Pandoc/Writers/Jira.hs view
@@ -25,7 +25,7 @@ import Text.Pandoc.Definition import Text.Pandoc.Options (WriterOptions (writerTemplate, writerWrapText),                             WrapOption (..))-import Text.Pandoc.Shared (linesToPara, stringify)+import Text.Pandoc.Shared (linesToPara, stringifyInlines) import Text.Pandoc.Templates (renderTemplate) import Text.Pandoc.Writers.Math (texMathToInlines) import Text.Pandoc.Writers.Shared (defField, metaToContext, toLegacyTable)@@ -253,7 +253,7 @@             -> JiraConverter m [Jira.Inline] imageToJira (_, classes, kvs) caption src title =   let imageWithParams ps = Jira.Image ps (Jira.URL src)-      alt = stringify caption+      alt = stringifyInlines caption   in pure . singleton . imageWithParams $      if "thumbnail" `elem` classes      then [Jira.Parameter "thumbnail" ""]@@ -270,7 +270,8 @@            -> JiraConverter m [Jira.Inline] toJiraLink (_, classes, _) (url, _) alias = do   let (linkType, url') = toLinkType url-  description <- if url `elem` [stringify alias, "mailto:" <> stringify alias]+  description <- if url `elem` [stringifyInlines alias,+                                "mailto:" <> stringifyInlines alias]                  then pure mempty                  else toJiraInlines alias   pure . singleton $ Jira.Link linkType description (Jira.URL url')
src/Text/Pandoc/Writers/LaTeX.hs view
@@ -33,7 +33,7 @@ import Crypto.Hash (hashWith, MD5(MD5)) import Data.Containers.ListUtils (nubOrd) import Data.Char (isDigit, isAscii, isLetter)-import Data.List (intersperse, partition, (\\))+import Data.List (find, intersperse, partition) import qualified Data.Set as Set import Data.Maybe (catMaybes, fromMaybe, isJust, listToMaybe, mapMaybe, isNothing) import Data.Monoid (Any (..))@@ -128,11 +128,6 @@   let blocks' = if method == Biblatex || method == Natbib                    then filter (not . isRefsDiv) blocks                    else blocks-  -- see if there are internal links-  let isInternalLink (Link _ _ (s,_))-        | Just ('#', xs) <- T.uncons s = [xs]-      isInternalLink _                 = []-  modify $ \s -> s{ stInternalLinks = query isInternalLink blocks' }   let colwidth = if writerWrapText options == WrapAuto                     then Just $ writerColumns options                     else Nothing@@ -186,22 +181,26 @@   biblioTitle <- inlineListToLaTeX lastHeader   st <- get   titleMeta <- escapeCommas <$> -- see #10501-                stringToLaTeX TextString (stringify $ docTitle meta)-  subtitleMeta <- stringToLaTeX TextString (stringify $ lookupMetaInlines "subtitle" meta)-  authorsMeta <- mapM (stringToLaTeX TextString . stringify) $ docAuthors meta+                stringToLaTeX TextString (stringifyInlines $ docTitle meta)+  subtitleMeta <- stringToLaTeX TextString+                    (stringifyInlines $ lookupMetaInlines "subtitle" meta)+  authorsMeta <- mapM (stringToLaTeX TextString . stringifyInlines) $+                   docAuthors meta   -- The trailer ID is as hash used to identify the PDF. Taking control of its   -- value is important when aiming for reproducible PDF generation. Setting   -- `SOURCE_DATE_EPOCH` is the traditional method used to control   -- reproducible builds. There are no cryptographic requirements for the ID,   -- so the 128bits (16 bytes) of MD5 are appropriate.   reproduciblePDF <- isJust <$> lookupEnv "SOURCE_DATE_EPOCH"-  trailerID <- do-    time <- getPOSIXTime-    let hash = T.pack . show . hashWith MD5 $ mconcat-               [ UTF8.fromString $ show time-               , UTF8.fromText $ render Nothing main-               ]-    pure $ mconcat [ "<", hash, "> <", hash, ">" ]+  trailerID <- if reproduciblePDF+    then do+      time <- getPOSIXTime+      let hash = T.pack . show . hashWith MD5 $ mconcat+                 [ UTF8.fromString $ show time+                 , UTF8.fromText $ render Nothing main+                 ]+      pure $ Just $ mconcat [ "<", hash, "> <", hash, ">" ]+    else pure Nothing   -- we need a default here since lang is used in template conditionals   let hasStringValue x = isJust (getField x metadata :: Maybe (Doc Text))   let geometryFromMargins = mconcat $ intersperse ("," :: Doc Text) $@@ -297,9 +296,7 @@                         | not (T.null ds) && T.all isDigit ds                           -> resetField "papersize" ("a" <> ds)                       _   -> id) .-                  (if reproduciblePDF-                    then defField "pdf-trailer-id" trailerID-                    else id) $+                  maybe id (defField "pdf-trailer-id") trailerID $                   (if not (null (pdfStandards pdfStd)) || isJust (pdfVersion pdfStd)                     then resetField "pdfstandard" $ MapVal $ Context $ M.fromList                            [ ("standards", ListVal $ map (SimpleVal . literal) (pdfStandards pdfStd))@@ -809,21 +806,19 @@   let unnumbered = "unnumbered" `elem` classes   let unlisted = "unlisted" `elem` classes   txt <- inlineListToLaTeX lst-  plain <- stringToLaTeX TextString $ T.concat $ map stringify lst+  plain <- stringToLaTeX TextString $ stringifyInlines lst   let removeInvalidInline (Note _)             = []       removeInvalidInline (Span (id', _, _) _) | not (T.null id') = []       removeInvalidInline Image{}            = []       removeInvalidInline x                    = [x]   let lstNoNotes = foldr (mappend . (\x -> walkM removeInvalidInline x)) mempty lst-  txtNoNotes <- inlineListToLaTeX lstNoNotes-  txtNoLinksNoNotes <- inlineListToLaTeX (removeLinks lstNoNotes)   -- footnotes in sections don't work (except for starred variants)   -- unless you specify an optional argument:   -- \section[mysec]{mysec\footnote{blah}}   optional <- if unnumbered || lstNoNotes == lst || null lstNoNotes                  then return empty                  else-                   return $ brackets txtNoNotes+                   brackets <$> inlineListToLaTeX lstNoNotes   let contents = if render Nothing txt == plain                     then braces txt                     else braces (text "\\texorpdfstring"@@ -862,45 +857,46 @@   lab <- labelFor ident   let star = if unnumbered then text "*" else empty   let title = star <> optional <> contents+  tocEntry <- if unnumbered && not unlisted+                 then do+                   txtNoLinksNoNotes <- inlineListToLaTeX+                                          (removeLinks lstNoNotes)+                   pure $ "\\addcontentsline{toc}" <>+                            braces (text sectionType) <>+                            braces txtNoLinksNoNotes+                 else pure empty   return $ if level' > 5               then txt               else prefix                    $$ text ('\\':sectionType) <> title <> lab-                   $$ if unnumbered && not unlisted-                         then "\\addcontentsline{toc}" <>-                                braces (text sectionType) <>-                                braces txtNoLinksNoNotes-                         else empty+                   $$ tocEntry  -- | Convert list of inline elements to LaTeX. inlineListToLaTeX :: PandocMonad m                   => [Inline]  -- ^ Inlines to convert                   -> LW m (Doc Text) inlineListToLaTeX lst = hcat <$>-  mapM inlineToLaTeX-    (addKerns . fixLineInitialSpaces . fixInitialLineBreaks $ lst)-    -- nonbreaking spaces (~) in LaTeX don't work after line breaks,-    -- so we insert a strut: this is mostly used in verse.- where fixLineInitialSpaces [] = []-       fixLineInitialSpaces (LineBreak : Str s : xs)-         | Just ('\160', _) <- T.uncons s-         = LineBreak : RawInline "latex" "\\strut " : Str s-            : fixLineInitialSpaces xs-       fixLineInitialSpaces (x:xs) = x : fixLineInitialSpaces xs-       -- We need \hfill\break for a line break at the start+  mapM inlineToLaTeX (fixInlines . fixInitialLineBreaks $ lst)+ where -- We need \hfill\break for a line break at the start        -- of a paragraph. See #5591.        fixInitialLineBreaks (LineBreak:xs) =          RawInline (Format "latex") "\\hfill\\break\n" :            fixInitialLineBreaks xs        fixInitialLineBreaks xs = xs-       addKerns [] = []-       addKerns (Str s : q@Quoted{} : rest)+       fixInlines [] = []+       -- nonbreaking spaces (~) in LaTeX don't work after line breaks,+       -- so we insert a strut: this is mostly used in verse.+       fixInlines (LineBreak : Str s : xs)+         | Just ('\160', _) <- T.uncons s+         = LineBreak : RawInline "latex" "\\strut " : fixInlines (Str s : xs)+       -- insert a thin space (kern) between adjacent quote characters:+       fixInlines (Str s : q@Quoted{} : rest)          | isQuote (T.takeEnd 1 s) =-           Str s : RawInline (Format "latex") "\\," : addKerns (q:rest)-       addKerns (q@Quoted{} : Str s : rest)+           Str s : RawInline (Format "latex") "\\," : fixInlines (q:rest)+       fixInlines (q@Quoted{} : Str s : rest)          | isQuote (T.take 1 s) =-           q : RawInline (Format "latex") "\\," : addKerns (Str s : rest)-       addKerns (x:xs) = x : addKerns xs+           q : RawInline (Format "latex") "\\," : fixInlines (Str s : rest)+       fixInlines (x:xs) = x : fixInlines xs        isQuote "\"" = True        isQuote "'" = True        isQuote "\x2018" = True@@ -994,9 +990,9 @@                                    listingsopts) <> "]"         inNote <- gets stInNote         when inNote $ modify $ \s -> s{ stVerbInNote = True }-        let chr = case "!\"'()*,-./:;?@" \\ T.unpack str of-                       (c:_) -> c-                       []    -> '!'+        let chr = fromMaybe '!' $+                    find (\c -> not (T.any (== c) str))+                      ("!\"'()*,-./:;?@" :: String)         let isEscapable '\\' = True             isEscapable '{'  = True             isEscapable '}'  = True@@ -1027,7 +1023,7 @@                  unless (T.null msg) $ report $ CouldNotHighlight msg                  rawCode                Right h -> modify (\st -> st{ stHighlighting = True }) >>-                          return (text (T.unpack h))+                          return (literal h)   -- for soul commands we need to protect VERB in an mbox or we get an error   -- (see #1294). with regular texttt we don't get an error, but we get   -- incorrect results if there is a space (see #5529).@@ -1183,7 +1179,7 @@                      Nothing                        | null description -> pure Nothing                        | otherwise -> Just <$> stringToLaTeX TextString-                                                  (stringify description)+                                              (stringifyInlines description)   let showDim dir = let d = text (show dir) <> "="                     in case dimension dir attr of                          Just (Pixel a)   ->
src/Text/Pandoc/Writers/LaTeX/Table.hs view
@@ -53,7 +53,6 @@                            $ lookup "latex-placement" kvs   -- if the float class is included in table attributes, we generate a floating   -- table environment; otherwise we use longtable-  beamer <- gets stBeamer   let float = "float" `elem` classes   let renderTable = do        let unnumbered = "unnumbered" `elem` classes@@ -83,11 +82,7 @@           then makeUnnumbered           else id) <$>           makeTable colDesc mkHead mkRow capt thead tbodies tfoot-  -- See #5367 -- footnotehyper/footnote don't work in beamer,-  -- so we need to produce the notes outside the table...-  if float || beamer-     then ($$) <$> withExternalNotes renderTable <*> getAccumulatedNotes-     else renderTable+  ($$) <$> withExternalNotes renderTable <*> getAccumulatedNotes  tableToLaTeXTable :: PandocMonad m                   => Doc Text@@ -353,11 +348,8 @@ -- For simple latex tables (without minipages or parboxes), -- we need to go to some lengths to get line breaks working: -- as LineBreak bs = \vtop{\hbox{\strut as}\hbox{\strut bs}}.-fixLineBreaks :: Block -> Block-fixLineBreaks = walk fixLineBreaks'--fixLineBreaks' :: [Inline] -> [Inline]-fixLineBreaks' ils = case splitBy (== LineBreak) ils of+fixLineBreaks :: [Inline] -> [Inline]+fixLineBreaks ils = case splitBy (== LineBreak) ils of                        []     -> []                        [xs]   -> xs                        chunks -> RawInline "tex" "\\vtop{" :
src/Text/Pandoc/Writers/LaTeX/Types.hs view
@@ -47,7 +47,6 @@   , stHighlighting  :: Bool          -- ^ true if document has highlighted code   , stIncremental   :: Bool          -- ^ true if beamer lists should be   , stZwnj          :: Bool          -- ^ true if document has a ZWNJ character-  , stInternalLinks :: [Text]        -- ^ list of internal link targets   , stBeamer        :: Bool          -- ^ produce beamer   , stEmptyLine     :: Bool          -- ^ true if no content on line   , stHasCslRefs    :: Bool          -- ^ has a Div with class refs@@ -96,7 +95,6 @@   , stHighlighting = False   , stIncremental = writerIncremental options   , stZwnj = False-  , stInternalLinks = []   , stBeamer = False   , stEmptyLine = True   , stHasCslRefs = False
src/Text/Pandoc/Writers/LaTeX/Util.hs view
@@ -39,7 +39,7 @@ import qualified Data.Text as T import Text.Pandoc.Extensions (Extension(Ext_smart)) import Data.Char (isLetter, isSpace, isDigit, isAscii, ord, isAlphaNum)-import Text.Printf (printf)+import Numeric (showHex) import Text.Pandoc.Shared (safeRead) import qualified Data.Text.Normalize as Normalize import Data.List (uncons)@@ -199,7 +199,7 @@    go = T.concatMap $ \x -> case x of      _ | (isLetter x || isDigit x) && isAscii x -> T.singleton x        | T.any (== x) "_-+=:;." -> T.singleton x-       | otherwise -> T.pack $ "ux" <> printf "%x" (ord x)+       | otherwise -> T.pack $ "ux" <> showHex (ord x) ""  -- | Puts contents into LaTeX command. inCmd :: Text -> Doc Text -> Doc Text
src/Text/Pandoc/Writers/Markdown.hs view
@@ -754,8 +754,8 @@                             (i,c,kv)                               | not (null alt)                               , Nothing <- lookup "alt" kv-                              , stringify descr /= stringify alt ->-                                 (i, c, ("alt", stringify alt) : kv)+                              , stringifyInlines descr /= stringifyInlines alt ->+                                 (i, c, ("alt", stringifyInlines alt) : kv)                             _ -> imgAttr'           contents <- inlineListToMarkdown opts [Image imgAttr'' descr tgt']           return $ contents <> blankline
src/Text/Pandoc/Writers/Markdown/Inline.hs view
@@ -678,13 +678,13 @@         where result = "[" <> linktext <> "](" <> (literal src) <> ")"               attributes = addKeyValueToAttr attr ("title", tit)     -- Use wikilinks where possible-    _ | src == stringify txt && useWikilink ->-        return $ "[[" <> literal (stringify txt) <> "]]"+    _ | src == stringifyInlines txt && useWikilink ->+        return $ "[[" <> literal (stringifyInlines txt) <> "]]"       | useAuto -> return $ "<" <> literal srcSuffix <> ">"       | useWikilink && isEnabled Ext_wikilinks_title_after_pipe opts -> return $-        "[[" <> literal src <> "|" <> literal (stringify txt) <> "]]"+        "[[" <> literal src <> "|" <> literal (stringifyInlines txt) <> "]]"       | useWikilink && isEnabled Ext_wikilinks_title_before_pipe opts -> return $-        "[[" <> literal (stringify txt) <> "|" <> literal src <> "]]"+        "[[" <> literal (stringifyInlines txt) <> "|" <> literal src <> "]]"       | useRefLinks ->            let first  = "[" <> linktext <> "]"                second = if getKey linktext == getKey reftext
src/Text/Pandoc/Writers/MediaWiki.hs view
@@ -147,7 +147,7 @@ blockToMediaWiki HorizontalRule = return $ blankline <> literal "-----" <> blankline  blockToMediaWiki (Header level (ident,_,_) inlines) = do-  let autoId = T.replace " " "_" $ stringify inlines+  let autoId = T.replace " " "_" $ stringifyInlines inlines   contents <- inlineListToMediaWiki inlines   let eqs = literal $ T.replicate level "="   return $
src/Text/Pandoc/Writers/Ms.hs view
@@ -72,8 +72,8 @@               meta   main <- blockListToMs opts blocks   hasInlineMath <- gets stHasInlineMath-  let titleMeta = (escapeStr opts . stringify) $ docTitle meta-  let authorsMeta = map (escapeStr opts . stringify) $ docAuthors meta+  let titleMeta = (escapeStr opts . stringifyInlines) $ docTitle meta+  let authorsMeta = map (escapeStr opts . stringifyInlines) $ docAuthors meta   hasHighlighting <- gets stHighlighting   let highlightingMacros = if hasHighlighting                               then case writerHighlightMethod opts of@@ -515,7 +515,7 @@       Nothing -> contents inlineToMs opts (Image attr alternate (src, _)) = do   let desc = literal "[IMAGE: " <>-             literal (escapeStr opts (stringify alternate)) <> char ']'+             literal (escapeStr opts (stringifyInlines alternate)) <> char ']'   let sizeAttrs = getSizeAttrs opts attr   let ext = takeExtension (T.unpack src)   let cmd = case ext of
src/Text/Pandoc/Writers/Native.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE OverloadedStrings #-} {- |    Module      : Text.Pandoc.Writers.Native    Copyright   : Copyright (C) 2006-2024 John MacFarlane@@ -8,23 +9,297 @@    Portability : portable  Conversion of a 'Pandoc' document to a string representation.++This used to be implemented using pretty-show's 'ppDoc' (which shows+the document, tokenizes and parses the result, and lays it out with+Text.PrettyPrint.HughesPJ).  For performance, we now build the layout+directly from the AST, carefully reproducing the exact output of the+old implementation (with @ribbonsPerLine = 1.2@). -} module Text.Pandoc.Writers.Native ( writeNative ) where+import Data.List (intersperse)+import qualified Data.Map as M import Data.Text (Text) import qualified Data.Text as T+import qualified Data.Text.Lazy as TL+import qualified Data.Text.Lazy.Builder as B import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.Options (WriterOptions (..))-import Text.Show.Pretty (ppDoc)-import Text.PrettyPrint (renderStyle, Style(..), style, char)  -- | Prettyprint Pandoc document. writeNative :: PandocMonad m => WriterOptions -> Pandoc -> m Text-writeNative opts (Pandoc meta blocks) = do-  let style' = style{ lineLength = writerColumns opts,-                      ribbonsPerLine = 1.2 }-  return $ T.pack $ renderStyle style' $-    case writerTemplate opts of-      Just _  -> ppDoc (Pandoc meta blocks) <> char '\n'-      Nothing -> ppDoc blocks+writeNative opts doc@(Pandoc _ blocks) = do+  let cols = writerColumns opts+  -- HughesPJ computes the ribbon length this way (with Float division):+  let ribbon = round (fromIntegral cols / (1.2 :: Float))+  return $ case writerTemplate opts of+    -- The old code appended a (char '\n'), which participates in layout+    -- as one extra glued character on the last line; hence glue0 = 1.+    Just _  -> render cols ribbon 1 (vDoc (pandocV doc)) <> "\n"+    Nothing -> render cols ribbon 0 (vDoc (blocksV blocks))++--+-- Layout documents (mirroring the structures pretty-show builds)+--++-- | A document together with the width of its one-line rendering.+data Doc = Doc !Int DC++-- | Either literal text, or a group that is rendered like HughesPJ's+-- 'sep': all on one line (elements joined by single spaces) if it+-- fits, otherwise vertically, with each element after the first on+-- its own line, indented by its nesting relative to the column at+-- which the group starts.+data DC = DText !Text+        | DGroup [Elt]++-- | Group element: nesting, glued prefix text, document, glued suffix.+data Elt = Elt !Int !Text Doc !Text++width :: Doc -> Int+width (Doc w _) = w++dtext :: Text -> Doc+dtext t = Doc (T.length t) (DText t)++group :: [Elt] -> Doc+group es = Doc (foldl (\acc e -> acc + 1 + eltWidth e) (-1) es) (DGroup es)+  where eltWidth (Elt _ pre d post) = T.length pre + width d + T.length post++-- | A value, i.e. a document plus an indication of whether it needs+-- parentheses when used as a constructor argument.+data V = V !Bool Doc++vDoc :: V -> Doc+vDoc (V _ d) = d++-- | Constructor applied to arguments: @hang (text c) 2 (sep args)@,+-- where non-atomic arguments are parenthesized.+con :: Text -> [V] -> V+con c [] = V True (dtext c)+con c vs = V False $ group+  [ Elt 0 "" (dtext c) ""+  , Elt 2 "" (group (map atomElt vs)) "" ]+  where+    atomElt (V True d)  = Elt 0 "" d ""+    atomElt (V False d) = Elt 0 "(" d ")"++-- | Bracketed, comma-separated block: @sep [open <+> x1, ...commas..., close]@.+block :: Text -> Text -> Text -> [Doc] -> Doc+block open comma close ds =+  group $ zipWith (\pre d -> Elt 0 pre d "") (open : repeat comma) ds+          ++ [Elt 0 "" (dtext close) ""]++listV :: (a -> V) -> [a] -> V+listV _ [] = V True (dtext "[]")+listV f xs = V True $ block "[ " ", " "]" (map (vDoc . f) xs)++tupleV :: [V] -> V+tupleV vs = V True $ block "( " ", " ")" (map vDoc vs)++-- | Record: @hang (text c) 2 (block '{' '}' fields)@ where each field+-- is @hang (text name <+> char '=') 2 value@.  Records count as atoms.+recV :: Text -> [(Text, V)] -> V+recV c fields = V True $ group+  [ Elt 0 "" (dtext c) ""+  , Elt 2 "" (block "{ " ", " "}" (map fieldDoc fields)) "" ]+  where+    fieldDoc (name, v) = group+      [ Elt 0 "" (dtext (name <> " =")) ""+      , Elt 2 "" (vDoc v) "" ]++-- | Leaf rendered via 'show' (Text, Int, Double).  Values whose+-- representation starts with @-@ get parentheses in argument position.+showV :: Show a => a -> V+showV x = V (not ("-" `T.isPrefixOf` t)) (dtext t)+  where t = T.pack (show x)++-- | Nullary constructors of enumeration types (and Bool).+enumV :: Show a => a -> V+enumV = V True . dtext . T.pack . show++mapV :: (a -> V) -> M.Map Text a -> V+mapV f m = con "fromList"+  [listV (\(k, v) -> tupleV [showV k, f v]) (M.toAscList m)]++--+-- Conversion of the Pandoc AST+--++pandocV :: Pandoc -> V+pandocV (Pandoc meta blocks) = con "Pandoc" [metaV meta, blocksV blocks]++metaV :: Meta -> V+metaV (Meta m) = recV "Meta" [("unMeta", mapV metaValueV m)]++metaValueV :: MetaValue -> V+metaValueV (MetaMap m)       = con "MetaMap" [mapV metaValueV m]+metaValueV (MetaList xs)     = con "MetaList" [listV metaValueV xs]+metaValueV (MetaBool b)      = con "MetaBool" [enumV b]+metaValueV (MetaString t)    = con "MetaString" [showV t]+metaValueV (MetaInlines ils) = con "MetaInlines" [inlinesV ils]+metaValueV (MetaBlocks bs)   = con "MetaBlocks" [blocksV bs]++blocksV :: [Block] -> V+blocksV = listV blockV++inlinesV :: [Inline] -> V+inlinesV = listV inlineV++attrV :: Attr -> V+attrV (ident, classes, kvs) =+  tupleV [ showV ident+         , listV showV classes+         , listV (\(k, v) -> tupleV [showV k, showV v]) kvs ]++formatV :: Format -> V+formatV (Format f) = con "Format" [showV f]++blockV :: Block -> V+blockV blk =+  case blk of+    Plain ils -> con "Plain" [inlinesV ils]+    Para ils -> con "Para" [inlinesV ils]+    LineBlock ilss -> con "LineBlock" [listV inlinesV ilss]+    CodeBlock attr t -> con "CodeBlock" [attrV attr, showV t]+    RawBlock f t -> con "RawBlock" [formatV f, showV t]+    BlockQuote bs -> con "BlockQuote" [blocksV bs]+    OrderedList (start, sty, delim) bss ->+      con "OrderedList" [ tupleV [showV start, enumV sty, enumV delim]+                        , listV blocksV bss ]+    BulletList bss -> con "BulletList" [listV blocksV bss]+    DefinitionList defs ->+      con "DefinitionList"+        [listV (\(ils, bss) -> tupleV [inlinesV ils, listV blocksV bss]) defs]+    Header lev attr ils -> con "Header" [showV lev, attrV attr, inlinesV ils]+    HorizontalRule -> con "HorizontalRule" []+    Table attr cap colspecs thead tbodies tfoot ->+      con "Table" [ attrV attr+                  , captionV cap+                  , listV colSpecV colspecs+                  , tableHeadV thead+                  , listV tableBodyV tbodies+                  , tableFootV tfoot ]+    Figure attr cap bs -> con "Figure" [attrV attr, captionV cap, blocksV bs]+    Div attr bs -> con "Div" [attrV attr, blocksV bs]++captionV :: Caption -> V+captionV (Caption mshort bs) =+  con "Caption" [maybeV inlinesV mshort, blocksV bs]++maybeV :: (a -> V) -> Maybe a -> V+maybeV _ Nothing  = con "Nothing" []+maybeV f (Just x) = con "Just" [f x]++colSpecV :: ColSpec -> V+colSpecV (align, cw) = tupleV [enumV align, colWidthV cw]++colWidthV :: ColWidth -> V+colWidthV (ColWidth d)    = con "ColWidth" [showV d]+colWidthV ColWidthDefault = con "ColWidthDefault" []++tableHeadV :: TableHead -> V+tableHeadV (TableHead attr rows) =+  con "TableHead" [attrV attr, listV rowV rows]++tableBodyV :: TableBody -> V+tableBodyV (TableBody attr (RowHeadColumns rhc) hd bd) =+  con "TableBody" [ attrV attr+                  , con "RowHeadColumns" [showV rhc]+                  , listV rowV hd+                  , listV rowV bd ]++tableFootV :: TableFoot -> V+tableFootV (TableFoot attr rows) =+  con "TableFoot" [attrV attr, listV rowV rows]++rowV :: Row -> V+rowV (Row attr cells) = con "Row" [attrV attr, listV cellV cells]++cellV :: Cell -> V+cellV (Cell attr align (RowSpan rs) (ColSpan cs) bs) =+  con "Cell" [ attrV attr+             , enumV align+             , con "RowSpan" [showV rs]+             , con "ColSpan" [showV cs]+             , blocksV bs ]++inlineV :: Inline -> V+inlineV inln =+  case inln of+    Str t -> con "Str" [showV t]+    Emph ils -> con "Emph" [inlinesV ils]+    Underline ils -> con "Underline" [inlinesV ils]+    Strong ils -> con "Strong" [inlinesV ils]+    Strikeout ils -> con "Strikeout" [inlinesV ils]+    Superscript ils -> con "Superscript" [inlinesV ils]+    Subscript ils -> con "Subscript" [inlinesV ils]+    SmallCaps ils -> con "SmallCaps" [inlinesV ils]+    Quoted qt ils -> con "Quoted" [enumV qt, inlinesV ils]+    Cite cits ils -> con "Cite" [listV citationV cits, inlinesV ils]+    Code attr t -> con "Code" [attrV attr, showV t]+    Space -> con "Space" []+    SoftBreak -> con "SoftBreak" []+    LineBreak -> con "LineBreak" []+    Math mt t -> con "Math" [enumV mt, showV t]+    RawInline f t -> con "RawInline" [formatV f, showV t]+    Link attr ils (url, title) ->+      con "Link" [attrV attr, inlinesV ils, tupleV [showV url, showV title]]+    Image attr ils (url, title) ->+      con "Image" [attrV attr, inlinesV ils, tupleV [showV url, showV title]]+    Note bs -> con "Note" [blocksV bs]+    Span attr ils -> con "Span" [attrV attr, inlinesV ils]++citationV :: Citation -> V+citationV cit = recV "Citation"+  [ ("citationId", showV (citationId cit))+  , ("citationPrefix", inlinesV (citationPrefix cit))+  , ("citationSuffix", inlinesV (citationSuffix cit))+  , ("citationMode", enumV (citationMode cit))+  , ("citationNoteNum", showV (citationNoteNum cit))+  , ("citationHash", showV (citationHash cit)) ]++--+-- Rendering+--++-- | Render a document, reproducing HughesPJ's layout exactly.  A+-- group is rendered on one line if its width, plus any text glued+-- after it up to the next line break ('glue'), stays within both the+-- line length (measured from the start of the line) and the ribbon+-- length (measured from the end of the indentation).  Otherwise it is+-- rendered vertically, each element after the first starting on a new+-- line, indented by the group's start column plus the element's+-- nesting; each element then makes its own layout decisions.+render :: Int -> Int -> Int -> Doc -> Text+render lineLen ribbonLen glue0 d0 =+  TL.toStrict $ B.toLazyText $ go 0 0 glue0 d0+  where+    go l c g d@(Doc w dc)+      | c + w + g <= lineLen && (c - l) + w + g <= ribbonLen = flat d+      | otherwise =+          case dc of+            DText t   -> B.fromText t+            DGroup es -> vertical l c g es++    vertical l0 c0 g = goElts True+      where+        goElts _ [] = mempty+        goElts isFirst (Elt n pre d post : rest) =+          let ind  = c0 + n+              (l, c) = if isFirst then (l0, c0) else (ind, ind)+              g' = T.length post + (if null rest then g else 0)+              lead = if isFirst+                        then mempty+                        else B.singleton '\n' <> B.fromText (T.replicate ind " ")+          in  lead <> B.fromText pre <> go l (c + T.length pre) g' d+                   <> B.fromText post <> goElts False rest++    flat (Doc _ (DText t))   = B.fromText t+    flat (Doc _ (DGroup es)) =+      mconcat $ intersperse (B.singleton ' ') $ map flatElt es++    flatElt (Elt _ pre d post) =+      B.fromText pre <> flat d <> B.fromText post
src/Text/Pandoc/Writers/ODT.hs view
@@ -38,7 +38,7 @@                             HighlightMethod(Skylighting, DefaultHighlighting)) import Text.Pandoc.Highlighting (defaultStyle) import Text.DocLayout-import Text.Pandoc.Shared (stringify, tshow)+import Text.Pandoc.Shared (stringify, stringifyInlines, tshow) import Text.Pandoc.Version (pandocVersionText) import Text.Pandoc.Writers.Shared (lookupMetaString, lookupMetaBlocks,                                    fixDisplayMath, getLang,@@ -165,7 +165,7 @@            ,("office:version","1.3")] ( inTags True "office:meta" []                  ( metaTag "meta:generator" ("Pandoc/" <> pandocVersionText)                    $$-                   metaTag "dc:title" (stringify title)+                   metaTag "dc:title" (stringifyInlines title)                    $$                    metaTag "dc:description"                           (T.intercalate "\n" (map stringify $@@ -184,7 +184,7 @@                          $$ metaTag "meta:creation-date" d                          $$ metaTag "dc:date" d                    ) (T.pack $ formatTime defaultTimeLocale "%FT%XZ" utctime)-                     (T.intercalate "; " (map stringify authors))+                     (T.intercalate "; " (map stringifyInlines authors))                    $$                    vcat userDefinedMeta                  )
src/Text/Pandoc/Writers/OOXML.hs view
@@ -89,8 +89,8 @@  type NameSpaces = [(Text, Text)] --- | Scales the image to fit the page--- sizes are passed in emu+-- | Scales the image to fit the page if it would overflow.+-- Sizes are passed in emu. fitToPage :: (Double, Double) -> Integer -> (Integer, Integer) fitToPage (x, y) pageWidth   -- Fixes width to the page width and scales the height
src/Text/Pandoc/Writers/OPML.hs view
@@ -65,7 +65,8 @@  convertDate :: [Inline] -> Text convertDate ils = maybe "" showDateTimeRFC822 $-  parseTimeM True defaultTimeLocale "%F" . T.unpack =<< normalizeDate (stringify ils)+  parseTimeM True defaultTimeLocale "%F" . T.unpack =<<+    normalizeDate (stringifyInlines ils)  -- | Convert a Block to OPML. blockToOPML :: PandocMonad m => WriterOptions -> Block -> m (Doc Text)
src/Text/Pandoc/Writers/Org.hs view
@@ -40,6 +40,7 @@  data WriterState =   WriterState { stNotes   :: [[Block]]+              , stNoteNum :: Int               , stHasMath :: Bool               , stOptions :: WriterOptions               }@@ -50,6 +51,7 @@ writeOrg :: PandocMonad m => WriterOptions -> Pandoc -> m Text writeOrg opts document = do   let st = WriterState { stNotes = [],+                         stNoteNum = 0,                          stHasMath = False,                          stOptions = opts }   evalStateT (pandocToOrg document) st@@ -66,7 +68,7 @@                (fmap chomp . inlineListToOrg)                meta   body <- blockListToOrg blocks-  notes <- gets (reverse . stNotes) >>= notesToOrg+  notes <- notesToOrg   hasMath <- gets stHasMath   let main = body $+$ notes   let context = defField "body" main@@ -88,10 +90,19 @@        Nothing  -> main        Just tpl -> renderTemplate tpl context --- | Return Org representation of notes.-notesToOrg :: PandocMonad m => [[Block]] -> Org m (Doc Text)-notesToOrg notes =-  vsep <$> zipWithM noteToOrg [1..] notes+-- | Return Org representation of the collected notes. Rendering a+-- note may add further notes to the state (notes nested inside+-- notes); keep going until all of them have been rendered.+notesToOrg :: PandocMonad m => Org m (Doc Text)+notesToOrg = vsep <$> go 0+  where+    go done = do+      notes <- gets (drop done . reverse . stNotes)+      if null notes+        then return []+        else do+          docs <- zipWithM noteToOrg [done + 1 ..] notes+          (docs ++) <$> go (done + length notes)  -- | Return Org representation of a note. noteToOrg :: PandocMonad m => Int -> [Block] -> Org m (Doc Text)@@ -114,14 +125,19 @@  -- | Escape special characters for Org. escapeString :: Text -> Doc Text-escapeString t-  | T.all isAlphaNum t = literal t-  | otherwise = mconcat $ map escChar (T.unpack t)+escapeString t =+  case T.break isSpecial t of+    (_, "") -> literal t+    (pre, post) ->+      case T.uncons post of+        -- escape special chars with ZERO WIDTH SPACE as org manual+        -- suggests+        Just (c, rest) -> (if T.null pre then mempty else literal pre)+                          <> afterBreak "\x200B" <> char c+                          <> escapeString rest+        Nothing -> literal pre  -- not reachable   where-    -- escape special chars with ZERO WIDTH SPACE as org manual suggests-   escChar c = if c == '*' || c == '#' || c == '|'-     then afterBreak "\x200B" <> char c-     else char c+    isSpecial c = c == '*' || c == '#' || c == '|'  isRawFormat :: Format -> Bool isRawFormat f =@@ -161,8 +177,8 @@   return $ blankline $$ "#+begin_verse" $$            nest 2 contents $$ "#+end_verse" <> blankline blockToOrg (RawBlock "html" str) =-  return $ blankline $$ "#+begin_html" $$-           nest 2 (literal str) $$ "#+end_html" $$ blankline+  return $ blankline $$ "#+begin_export html" $$+           nest 2 (literal str) $$ "#+end_export" $$ blankline blockToOrg b@(RawBlock f str)   | isRawFormat f = return $ literal str   | otherwise     = do@@ -213,13 +229,18 @@   let (beg, end) = case lang of         Nothing -> ("#+begin_example" <> numberlines, "#+end_example")         Just x  -> ("#+begin_src " <> x <> numberlines <> args, "#+end_src")-  -- escape special lines+  -- Escape special lines by prepending a comma. Like Emacs'+  -- org-escape-code-in-region, escape all lines consisting of+  -- indentation, then any number of commas, then "*" or "#+"; this+  -- keeps lines that already start with commas intact when the block+  -- is unescaped again.+  let needsEscape t = let t' = T.dropWhile (== ',') t+                      in T.isPrefixOf "#+" t' || T.isPrefixOf "*" t'   let escape_line line =         let (spaces, code) = T.span (\c -> c == ' ' || c == '\t') line-        in spaces <>-           (if T.isPrefixOf "#+" code || T.isPrefixOf "*" code-            then T.cons ',' code-            else code)+        in if needsEscape code+           then spaces <> T.cons ',' code+           else line   let escaped = T.unlines . map escape_line . T.lines $ str   return $ name $$ literal beg $$ literal escaped $$ literal end $$ blankline blockToOrg (BlockQuote blocks) = do@@ -246,8 +267,7 @@               middle = hcat $ intersperse sep' blocks   let makeRow = hpipeBlocks . zipWith lblock widthsInChars   let head' = makeRow headers'-  rows' <- mapM (\row -> do cols <- mapM blockListToOrg row-                            return $ makeRow cols) rows+  let rows' = map makeRow rawRows   let border ch = char '|' <> char ch <>                   (hcat . intersperse (char ch <> char '+' <> char ch) $                           map (\l -> text $ replicate l ch) widthsInChars) <>@@ -476,13 +496,14 @@         shouldFix Note{} = True    -- Prevent footnotes         shouldFix (Str "-") = True -- Prevent bullet list items         shouldFix (Str x)          -- Prevent ordered list items-          | Just (cs, c) <- T.unsnoc x = T.all isDigit cs &&+          | Just (cs, c) <- T.unsnoc x = not (T.null cs) &&+                                         T.all isDigit cs &&                                          (c == '.' || c == ')')         shouldFix _ = False  -- | Convert Pandoc inline element to Org. inlineToOrg :: PandocMonad m => Inline -> Org m (Doc Text)-inlineToOrg (Span (uid, [], []) []) =+inlineToOrg (Span (uid, [], []) []) | not (T.null uid) =   return $ "<<" <> literal uid <> ">>" inlineToOrg (Span _ lst) =   inlineListToOrg lst@@ -548,7 +569,13 @@                    _ -> mempty        return $ "[cite" <> sty <> ":" <> citeItems <> "]"      else inlineListToOrg lst-inlineToOrg (Code _ str) = return $ "=" <> literal str <> "="+inlineToOrg (Code _ str) = return $+  -- Org offers no escape mechanism inside verbatim text; if the+  -- content contains the delimiter, fall back to the other verbatim+  -- delimiter.+  if "=" `T.isInfixOf` str && not ("~" `T.isInfixOf` str)+     then "~" <> literal str <> "~"+     else "=" <> literal str <> "=" inlineToOrg (Str str) = do   opts <- gets stOptions   let str' = if isEnabled Ext_smart opts || isEnabled Ext_special_strings opts@@ -578,17 +605,49 @@ inlineToOrg (Link _ txt (src, _)) =   case txt of         [Str x] | escapeURI x == src ->  -- autolink-             return $ "[[" <> literal (orgPath x) <> "]]"-        _ -> do contents <- nowrap <$> inlineListToOrg txt-                return $ "[[" <> literal (orgPath src) <> "][" <> contents <> "]]"+             return $ "[[" <> literal (escapeLinkTarget (orgPath x)) <> "]]"+        _ -> do descr <- render Nothing . nowrap <$> inlineListToOrg txt+                return $ "[[" <> literal (escapeLinkTarget (orgPath src)) <>+                         "][" <> literal (escapeLinkDescription descr) <> "]]" inlineToOrg (Image _ _ (source, _)) =-  return $ "[[" <> literal (orgPath source) <> "]]"+  return $ "[[" <> literal (escapeLinkTarget (orgPath source)) <> "]]" inlineToOrg (Note contents) = do   -- add to notes in state-  notes <- gets stNotes-  modify $ \st -> st { stNotes = contents:notes }-  let ref = tshow $ length notes + 1+  modify $ \st -> st { stNotes = contents : stNotes st+                     , stNoteNum = stNoteNum st + 1 }+  ref <- gets (tshow . stNoteNum)   return $ "[fn:" <> literal ref <> "]"++-- | Escape a link target like Emacs' @org-link-escape@:+-- backslash-escape square brackets, and double any run of backslashes+-- occurring directly before a bracket or at the end of the target.+escapeLinkTarget :: Text -> Text+escapeLinkTarget t =+  let (pre, rest) = T.break (\c -> c == '\\' || c == '[' || c == ']') t+  in pre <> case T.uncons rest of+       Nothing -> ""+       Just ('\\', _) ->+         let (bs, rest') = T.span (== '\\') rest+         in case T.uncons rest' of+              Nothing -> bs <> bs+              Just (c, rest'')+                | c == '[' || c == ']'+                  -> bs <> bs <> "\\" <> T.cons c (escapeLinkTarget rest'')+                | otherwise -> bs <> T.cons c (escapeLinkTarget rest'')+       Just (c, rest') -> "\\" <> T.cons c (escapeLinkTarget rest')++-- | Make a link description safe: it must not contain @]]@ or end+-- with @]@. Like Emacs' @org-link-make-string@, insert a zero-width+-- space to break up the offending brackets.+escapeLinkDescription :: Text -> Text+escapeLinkDescription = fixEnd . fixDouble+  where+    fixDouble t | "]]" `T.isInfixOf` t+                            = fixDouble $ T.replace "]]" "]\x200B]" t+                | otherwise = t+    fixEnd t = case T.unsnoc t of+                 Just (t', ']') -> t' <> "\x200B]"+                 _              -> t  orgPath :: Text -> Text orgPath src = case T.uncons src of
src/Text/Pandoc/Writers/Powerpoint/Output.hs view
@@ -28,6 +28,7 @@ import Control.Monad.State     ( StateT, gets, modify, evalStateT ) import Codec.Archive.Zip+import Data.Containers.ListUtils (nubOrdOn) import Data.List (intercalate, stripPrefix, nub, union, isPrefixOf, intersperse) import Data.Bifunctor (bimap) import Data.CaseInsensitive (CI)@@ -57,6 +58,7 @@ import Text.Pandoc.Writers.Shared (metaToContext) import Text.Pandoc.Writers.OOXML import qualified Data.Map as M+import qualified Data.Set as Set import Data.Maybe (mapMaybe, listToMaybe, fromMaybe, maybeToList, catMaybes, isJust) import Text.Pandoc.ImageSize import Control.Applicative ((<|>))@@ -67,7 +69,7 @@ import Text.Pandoc.Logging (LogMessage(PowerpointTemplateWarning)) import Text.Pandoc.Writers.Math (convertMath) import Text.Pandoc.Writers.Powerpoint.Presentation-import Text.Pandoc.Shared (tshow, stringify)+import Text.Pandoc.Shared (tshow, stringify, stringifyInlines) import Skylighting (fromColor)  -- |The 'EMU' type is used to specify sizes in English Metric Units.@@ -131,6 +133,9 @@                            , envInSpeakerNotes :: Bool                            , envSlideLayouts :: Maybe SlideLayouts                            , envOtherStyleIndents :: Maybe Indents+                           -- The parsed slide master, cached to avoid+                           -- re-parsing it for every slide.+                           , envMaster :: Maybe Element                            }                  deriving (Show) @@ -151,6 +156,7 @@                   , envInSpeakerNotes = False                   , envSlideLayouts = Nothing                   , envOtherStyleIndents = Nothing+                  , envMaster = Nothing                   }  type SlideLayouts = SlideLayoutsOf SlideLayout@@ -391,16 +397,28 @@   mediaEntries <- makeMediaEntries   contentTypesEntry <- presentationToContentTypes p >>= contentTypesToEntry   -- fold everything into our inherited archive and return it.-  return $ foldr addEntryToArchive newArch' $-    slideEntries <>-    slideRelEntries <>-    spkNotesEntries <>-    spkNotesRelEntries <>-    mediaEntries <>-    [updatedMasterEntry, updatedMasterRelEntry]  <>-    [contentTypesEntry, docPropsEntry, docCustomPropsEntry, relsEntry,-     presEntry, presRelsEntry, viewPropsEntry]+  return $ addEntriesToArchive+    (slideEntries <>+     slideRelEntries <>+     spkNotesEntries <>+     spkNotesRelEntries <>+     mediaEntries <>+     [updatedMasterEntry, updatedMasterRelEntry]  <>+     [contentTypesEntry, docPropsEntry, docCustomPropsEntry, relsEntry,+      presEntry, presRelsEntry, viewPropsEntry])+    newArch' +-- | Add entries to an archive in a single pass.  Equivalent to (but+-- faster than) folding 'addEntryToArchive' over the list: for+-- duplicate paths the first entry in the list wins, and the new+-- entries precede (and replace) existing entries with the same paths.+addEntriesToArchive :: [Entry] -> Archive -> Archive+addEntriesToArchive entries archive =+  archive{ zEntries = nubOrdOn eRelativePath entries <>+                      filter (\e -> eRelativePath e `Set.notMember` newPaths)+                             (zEntries archive) }+  where newPaths = Set.fromList $ map eRelativePath entries+ updateMasterElems :: SlideLayouts -> Element -> Element -> (Element, Element) updateMasterElems layouts master masterRels = (updatedMaster, updatedMasterRels)   where@@ -661,7 +679,7 @@   context <- metaToContext opts{ writerTemplate =                                   writerTemplate opts <|> Just mempty }                 (return . literal . stringify)-                (return . literal . stringify) meta+                (return . literal . stringifyInlines) meta    let env = def { envRefArchive = refArchive                 , envDistArchive = distArchive@@ -673,6 +691,7 @@                 , envSpeakerNotesIdMap = makeSpeakerNotesMap pres                 , envSlideLayouts = Just layouts                 , envOtherStyleIndents = otherStyleIndents+                , envMaster = Just master                 }    let st = def { stMediaGlobalIds = initialGlobalIds refArchive distArchive@@ -983,9 +1002,13 @@  getMaster :: PandocMonad m => P m Element getMaster = do-  refArchive <- asks envRefArchive-  distArchive <- asks envDistArchive-  getMaster' refArchive distArchive+  mbMaster <- asks envMaster+  case mbMaster of+    Just master -> pure master+    Nothing -> do+      refArchive <- asks envRefArchive+      distArchive <- asks envDistArchive+      getMaster' refArchive distArchive  getMaster' :: PandocMonad m => Archive -> Archive -> m Element getMaster' refArchive distArchive =
src/Text/Pandoc/Writers/Powerpoint/Presentation.hs view
@@ -63,6 +63,7 @@ import qualified Data.Map as M import qualified Data.Set as S import Data.Maybe (maybeToList, fromMaybe, listToMaybe, isNothing)+import Data.Monoid (Any(..)) import Text.Pandoc.Highlighting import qualified Data.Text as T import Control.Applicative ((<|>))@@ -845,11 +846,14 @@   return $ filter (not . isNotesDiv) blks  handleAndFilterSpeakerNotes :: [Block] -> Pres ([Block], SpeakerNotes)-handleAndFilterSpeakerNotes blks = do-  modify $ \st -> st{stSpeakerNotes = mempty}-  blks' <- walkM handleAndFilterSpeakerNotes' blks-  spkNotes <- gets stSpeakerNotes-  return (blks', spkNotes)+handleAndFilterSpeakerNotes blks+  -- avoid an expensive walk in the common case of no notes divs:+  | not (getAny (query (Any . isNotesDiv) blks)) = return (blks, mempty)+  | otherwise = do+      modify $ \st -> st{stSpeakerNotes = mempty}+      blks' <- walkM handleAndFilterSpeakerNotes' blks+      spkNotes <- gets stSpeakerNotes+      return (blks', spkNotes)  blocksToSlide :: [Block] -> Pres Slide blocksToSlide blks = do@@ -1165,7 +1169,7 @@                    Just (MetaList xs) -> Just $ map Shared.stringify xs                    _                  -> Nothing -      authors = case map Shared.stringify $ docAuthors meta of+      authors = case map Shared.stringifyInlines $ docAuthors meta of                   [] -> Nothing                   ss -> Just $ T.intercalate "; " ss @@ -1186,7 +1190,7 @@             , dcDescription = description             , cpCategory = Shared.stringify <$> lookupMeta "category" meta             , dcDate =-              let t = Shared.stringify (docDate meta)+              let t = Shared.stringifyInlines (docDate meta)               in if T.null t                  then Nothing                  else Just t
src/Text/Pandoc/Writers/RST.hs view
@@ -116,7 +116,11 @@ -- | Return RST representation of a reference key. keyToRST :: PandocMonad m => ([Inline], (Text, Text)) -> RST m (Doc Text) keyToRST (label, (src, _)) = do-  label' <- inlineListToRST label+  -- The stored label has already been normalized by the walk in+  -- inlineListToRST.  Use writeInlines to avoid re-applying the+  -- (non-idempotent) transformations: the definition must render+  -- exactly like the inline reference, which uses writeInlines.+  label' <- writeInlines label   let label'' = if (==':') `T.any` (render Nothing label' :: Text)                    then char '`' <> label' <> char '`'                    else label'@@ -132,7 +136,7 @@ noteToRST num note = do   contents <- blockListToRST note   let marker = ".. [" <> text (show num) <> "]"-  return $ nowrap $ marker $$ nest 3 contents+  return $ nowrap marker $$ nest 3 contents  -- | Return RST representation of picture reference table. pictRefsToRST :: PandocMonad m@@ -146,7 +150,8 @@           => ([Inline], (Attr, Text, Text, Maybe Text))           -> RST m (Doc Text) pictToRST (label, (attr, src, _, mbtarget)) = do-  label' <- inlineListToRST label+  -- the stored label was already normalized; see keyToRST+  label' <- writeInlines label   dims   <- imageDimsToRST attr   let (_, cls, _) = attr       classes = case cls of@@ -900,7 +905,9 @@                  modify $ \st -> st { stImages =                         (alt', (attr,src,tit, mbtarget)):stImages st }                  return alt'-  inlineListToRST txt+  -- the alt text has already been normalized by the walk in+  -- inlineListToRST, so don't apply the transformations again+  writeInlines txt  imageDimsToRST :: PandocMonad m => Attr -> RST m (Doc Text) imageDimsToRST attr = do
src/Text/Pandoc/Writers/Shared.hs view
@@ -77,7 +77,8 @@ import Text.Pandoc.Parsing (runParser, eof, defaultParserState,                             anyOrderedListMarker) import Text.DocLayout-import Text.Pandoc.Shared (stringify, makeSections, blocksToInlines)+import Text.Pandoc.Shared (stringify, stringifyInlines, makeSections,+                           blocksToInlines) import Text.Pandoc.Walk (Walkable(..)) import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.XML (escapeStringForXML, rdfaAttributes, html5Attributes)@@ -195,24 +196,25 @@                _                                 -> Nothing  -- | Produce an HTML tag with the given pandoc attributes.-tagWithAttrs :: HasChars a => a -> Attr -> Doc a+tagWithAttrs :: (HasChars a, FromText a) => a -> Attr -> Doc a tagWithAttrs tag attr = "<" <> literal tag <> (htmlAttrs attr) <> ">"  -- | Produce HTML for the given pandoc attributes, to be used in HTML tags-htmlAttrs :: HasChars a => Attr -> Doc a+htmlAttrs :: (HasChars a, FromText a) => Attr -> Doc a htmlAttrs (ident, classes, kvs) = addSpaceIfNotEmpty (hsep [   if T.null ident       then empty-      else "id=" <> doubleQuotes (text $ T.unpack ident)+      else "id=" <> doubleQuotes (literal $ fromText (escapeStringForXML ident))   ,if null classes       then empty-      else "class=" <> doubleQuotes (text $ T.unpack (T.unwords classes))+      else "class=" <> doubleQuotes+             (literal $ fromText . escapeStringForXML $ T.unwords classes)   ,hsep (map (\(k,v) -> formatKey k <> "=" <>-                doubleQuotes (text $ T.unpack (escapeStringForXML v))) kvs)+                doubleQuotes (literal $ fromText (escapeStringForXML v))) kvs)   ])  where-   formatKey x = text . T.unpack $-        if (x `Set.member` (html5Attributes <> rdfaAttributes)+   formatKey x = literal . fromText $+        if ((x `Set.member` html5Attributes || x `Set.member` rdfaAttributes)             && x /= "label") -- #10048              || T.any (== ':') x -- e.g. epub: namespace              || "data-" `T.isPrefixOf` x@@ -259,8 +261,8 @@ -- | Remove leading and trailing 'Space' and 'SoftBreak' elements. stripLeadingTrailingSpace :: [Inline] -> [Inline] stripLeadingTrailingSpace = go . reverse . go . reverse-  where go (Space:xs)     = xs-        go (SoftBreak:xs) = xs+  where go (Space:xs)     = go xs+        go (SoftBreak:xs) = go xs         go xs             = xs  -- | Put display math in its own block (for ODT/DOCX).@@ -607,8 +609,8 @@ lookupMetaBool :: Text -> Meta -> Bool lookupMetaBool key meta =   case lookupMeta key meta of-      Just (MetaBlocks _)  -> True-      Just (MetaInlines _) -> True+      Just (MetaBlocks bs)  -> not (null bs)+      Just (MetaInlines ils) -> not (null ils)       Just (MetaString x)  -> not (T.null x)       Just (MetaBool True) -> True       _                    -> False@@ -646,7 +648,7 @@ lookupMetaString key meta =   case lookupMeta key meta of          Just (MetaString s)    -> s-         Just (MetaInlines ils) -> stringify ils+         Just (MetaInlines ils) -> stringifyInlines ils          Just (MetaBlocks bs)   -> stringify bs          Just (MetaBool b)      -> T.pack (show b)          _                      -> ""@@ -677,6 +679,7 @@ toSubscript '=' = Just '\x208C' toSubscript '(' = Just '\x208D' toSubscript ')' = Just '\x208E'+toSubscript '\x2212' = Just '\x208B' -- unicode minus toSubscript c   | c >= '0' && c <= '9' =                  Just $ chr (0x2080 + (ord c - 48))@@ -716,6 +719,10 @@     Just Plain{} -> True     Just (BulletList is) -> maybe False endsWithPlain (lastMay is)     Just (OrderedList _ is) -> maybe False endsWithPlain (lastMay is)+    Just (DefinitionList defs) ->+      case lastMay defs of+        Just (_, ds) -> maybe False endsWithPlain (lastMay ds)+        Nothing      -> False     _ -> False  -- | Convert the relevant components of a new-style table (with block@@ -799,12 +806,13 @@   isSentenceEnding t =     case T.unsnoc t of       Just (t',c)-        | c == '.' || c == '!' || c == '?'+        | c == '.'         , not (isInitial t') -> True+        | c == '!' || c == '?' -> True         | c == ')' || c == ']' || c == '"' || c == '\x201D' ->            case T.unsnoc t' of-             Just (t'',d) -> d == '.' || d == '!' || d == '?' &&-                             not (isInitial t'')+             Just (t'',d) -> (d == '.' && not (isInitial t''))+                               || d == '!' || d == '?'              _ -> False       _ -> False    where@@ -814,44 +822,46 @@ -- and modify internal links accordingly. (Yes, XML allows an -- underscore, but HTML 4 doesn't, so we are more conservative.) ensureValidXmlIdentifiers :: Pandoc -> Pandoc-ensureValidXmlIdentifiers = walk fixLinks . walkAttr fixIdentifiers+ensureValidXmlIdentifiers = walk goInline . walk goBlock  where-  fixIdentifiers (ident, classes, kvs) =+  fixAttr (ident, classes, kvs) =     (case T.uncons ident of       Nothing -> ident       Just (c, _) | isLetter c -> ident       _ -> "id_" <> ident,      classes, kvs)-  needsFixing src =+  fixSrc src =     case T.uncons src of       Just ('#',t) ->         case T.uncons t of-          Just (c,_) | not (isLetter c) -> Just ("#id_" <> t)-          _ -> Nothing-      _ -> Nothing-  fixLinks (Link attr ils (src, tit))-    | Just src' <- needsFixing src = Link attr ils (src', tit)-  fixLinks (Image attr ils (src, tit))-    | Just src' <- needsFixing src = Image attr ils (src', tit)-  fixLinks x = x+          Just (c,_) | not (isLetter c) -> "#id_" <> t+          _ -> src+      _ -> src --- | Walk Pandoc document, modifying attributes.-walkAttr :: (Attr -> Attr) -> Pandoc -> Pandoc-walkAttr f = walk goInline . walk goBlock- where-  goInline (Span attr ils) = Span (f attr) ils-  goInline (Link attr ils target) = Link (f attr) ils target-  goInline (Image attr ils target) = Image (f attr) ils target-  goInline (Code attr txt) = Code (f attr) txt+  goInline (Span attr ils) = Span (fixAttr attr) ils+  goInline (Link attr ils (src, tit)) = Link (fixAttr attr) ils (fixSrc src, tit)+  goInline (Image attr ils (src, tit)) =+    Image (fixAttr attr) ils (fixSrc src, tit)+  goInline (Code attr txt) = Code (fixAttr attr) txt   goInline x = x -  goBlock (Header lev attr ils) = Header lev (f attr) ils-  goBlock (CodeBlock attr txt) = CodeBlock (f attr) txt+  goBlock (Header lev attr ils) = Header lev (fixAttr attr) ils+  goBlock (CodeBlock attr txt) = CodeBlock (fixAttr attr) txt   goBlock (Table attr cap colspecs thead tbodies tfoot) =-    Table (f attr) cap colspecs thead tbodies tfoot-  goBlock (Div attr bs) = Div (f attr) bs+    Table (fixAttr attr) cap colspecs+      (goTableHead thead) (map goTableBody tbodies) (goTableFoot tfoot)+  goBlock (Div attr bs) = Div (fixAttr attr) bs+  goBlock (Figure attr cap bs) = Figure (fixAttr attr) cap bs   goBlock x = x +  goTableHead (TableHead attr rows) = TableHead (fixAttr attr) (map goRow rows)+  goTableBody (TableBody attr rhc hd bd) =+    TableBody (fixAttr attr) rhc (map goRow hd) (map goRow bd)+  goTableFoot (TableFoot attr rows) = TableFoot (fixAttr attr) (map goRow rows)+  goRow (Row attr cells) = Row (fixAttr attr) (map goCell cells)+  goCell (Cell attr align rowspan colspan bs) =+    Cell (fixAttr attr) align rowspan colspan bs+ -- | Convert links to spans; most useful when writing elements that must not -- contain links, e.g. to avoid nested links. removeLinks :: [Inline] -> [Inline]@@ -878,8 +888,12 @@ toTaskListItem :: MonadPlus m => [Block] -> m (Bool, [Block]) toTaskListItem (Plain (Str "☐":Space:ils):xs) = pure (False, Plain ils:xs) toTaskListItem (Plain (Str "☒":Space:ils):xs) = pure (True, Plain ils:xs)+toTaskListItem (Plain [Str "☐"]:xs)           = pure (False, Plain []:xs)+toTaskListItem (Plain [Str "☒"]:xs)           = pure (True, Plain []:xs) toTaskListItem (Para  (Str "☐":Space:ils):xs) = pure (False, Para ils:xs) toTaskListItem (Para  (Str "☒":Space:ils):xs) = pure (True, Para ils:xs)+toTaskListItem (Para  [Str "☐"]:xs)           = pure (False, Para []:xs)+toTaskListItem (Para  [Str "☒"]:xs)           = pure (True, Para []:xs) toTaskListItem _                              = mzero  -- | Add an opener and closer to a Doc. If the Doc begins or ends@@ -960,9 +974,11 @@ -- For handling previous row spans that are next to the end of a row's cells -- that were previously added with 'insertCurrentSpansAtColumn'. decrementTrailingRowSpans :: Int -> M.Map Int (RowSpan, ColSpan) -> M.Map Int (RowSpan, ColSpan)-decrementTrailingRowSpans columnPosition = M.mapWithKey decrementTrailing+decrementTrailingRowSpans columnPosition = M.mapMaybeWithKey decrementTrailing   where-    decrementTrailing previousColumnPosition previousSpan@(RowSpan rowSpan, colSpan) =-      if previousColumnPosition >= columnPosition && rowSpan >= 1-        then (RowSpan rowSpan - 1, colSpan)-        else previousSpan+    decrementTrailing previousColumnPosition previousSpan@(RowSpan rowSpan, colSpan)+      | previousColumnPosition >= columnPosition && rowSpan >= 1 =+          if rowSpan > 1+            then Just (RowSpan rowSpan - 1, colSpan)+            else Nothing  -- span is used up; drop it instead of keeping a 0 entry+      | otherwise = Just previousSpan
src/Text/Pandoc/Writers/TEI.hs view
@@ -179,7 +179,7 @@ blockToTEI _ HorizontalRule = return $   selfClosingTag "milestone" [("unit","undefined")                              ,("type","separator")-                             ,("rendition","line")]+                             ,("rend","line")] blockToTEI opts (Figure attr capt bs) =   blockToTEI opts (figureDiv attr capt bs) 
src/Text/Pandoc/Writers/Typst.hs view
@@ -33,7 +33,8 @@ import Text.Pandoc.Writers.Shared ( lookupMetaInlines, lookupMetaString,                                     metaToContext, defField, resetField,                                     setupTranslations )-import Text.Pandoc.Shared (isTightList, orderedListMarkers, tshow, stringify)+import Text.Pandoc.Shared (isTightList, orderedListMarkers, tshow,+                           stringifyInlines) import Text.Pandoc.Highlighting (highlight, formatTypstBlock, formatTypstInline,                                  styleToTypst) import Text.Pandoc.Translations (Term(Abstract), translateTerm)@@ -362,17 +363,18 @@                 $$ footer             )             $$ ")"-      return $ if "typst:no-figure" `elem` tabclasses-        then toTypstBracesSetText typstTextAttrs table-        else "#figure("-            $$-            nest 2-            ("align(center)[" <> toTypstPoundSetText typstTextAttrs <> "#" <> table <> "]"-              $$ capt'-              $$ typstFigureKind-              $$ ")")-            $$ lab-          $$ blankline+      return $+        (if "typst:no-figure" `elem` tabclasses+            then toTypstBracesSetText typstTextAttrs table+            else "#figure("+                 $$+                 nest 2+                 ("align(center)[" <> toTypstPoundSetText typstTextAttrs <> "#" <> table <> "]"+                   $$ capt'+                   $$ typstFigureKind+                   $$ ")"))+        $$ lab+        $$ blankline     Figure (ident,_,kvs) (Caption _mbshort capt) blocks -> do       caption <- blocksToTypst capt       opts <-  gets stOptions@@ -402,7 +404,7 @@       contents <- blocksToTypst blocks       return $ "#block" <> toTypstPropsListParens typstAttrs <> "["         $$ toTypstPoundSetText typstTextAttrs-        $$ contents+        $$ chomp contents         $$ ("]" <+> lab)  defListItemToTypst :: PandocMonad m => ([Inline], [[Block]]) -> TW m (Doc Text)@@ -410,7 +412,7 @@   modify $ \st -> st{ stEscapeContext = TermContext }   term' <- inlinesToTypst term   modify $ \st -> st{ stEscapeContext = NormalContext }-  defns' <- mapM blocksToTypst defns+  defns' <- mapM (fmap chomp . blocksToTypst) defns   return $     case defns of       [[Plain _]] -> hang 4 (nowrap ("/ " <> term' <> ": ")) (vcat defns')@@ -598,7 +600,7 @@     Just alt -> Just alt     Nothing -> case imgInlines of                  [] -> Nothing-                 _ -> Just (stringify imgInlines)+                 _ -> Just (stringifyInlines imgInlines)  textstyle :: PandocMonad m => Doc Text -> [Inline] -> TW m (Doc Text) textstyle s inlines = do
src/Text/Pandoc/Writers/XML.hs view
@@ -1,6 +1,8 @@ {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeOperators #-}  -- | --   Module      : Text.Pandoc.Writers.XML@@ -18,19 +20,19 @@ import Data.Maybe (mapMaybe) import qualified Data.Text as T import Data.Version (versionBranch)+import GHC.Generics import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.Options (WriterOptions (..)) import Text.Pandoc.XML.Light import qualified Text.Pandoc.XML.Light as XML import Text.Pandoc.XMLFormat-import Text.XML.Light (xml_header)  type PandocAttr = Text.Pandoc.Definition.Attr  writeXML :: (PandocMonad m) => WriterOptions -> Pandoc -> m T.Text-writeXML _ doc = do-  return $ pandocToXmlText doc+writeXML opts doc = do+  return $ pandocToXmlText opts doc  text_node :: T.Text -> Content text_node text = Text (CData CDataText text Nothing)@@ -65,40 +67,85 @@ elementWithAttrAndContents :: T.Text -> PandocAttr -> [Content] -> Element elementWithAttrAndContents tag attr contents = addAttrAttributes attr $ elementWithContents tag contents -asBlockOfInlines :: Element -> [Content]-asBlockOfInlines el = [Elem el, text_node "\n"]+-- | Extract the name of a value's constructor via GHC.Generics.+class GConName f where+  gConName :: f p -> String -asBlockOfBlocks :: Element -> [Content]-asBlockOfBlocks el = [Elem newline_before_first, newline]-  where-    newline = text_node "\n"-    newline_before_first = if null (elContent el) then el else prependContents [newline] el+instance (GConName f) => GConName (M1 D d f) where+  gConName (M1 x) = gConName x -itemName :: (Show a) => a -> T.Text-itemName a = T.pack $ takeWhile (/= ' ') (show a)+instance (GConName f, GConName g) => GConName (f :+: g) where+  gConName (L1 x) = gConName x+  gConName (R1 x) = gConName x +instance (Constructor c) => GConName (M1 C c f) where+  gConName = conName++itemName :: (Generic a, GConName (Rep a)) => a -> T.Text+itemName = T.pack . gConName . from+ intAsText :: Int -> T.Text intAsText i = T.pack $ show i -itemAsEmptyElement :: (Show a) => a -> Element+itemAsEmptyElement :: (Generic a, GConName (Rep a)) => a -> Element itemAsEmptyElement item = emptyElement $ itemName item -pandocToXmlText :: Pandoc -> T.Text-pandocToXmlText (Pandoc (Meta meta) blocks) = with_header . with_blocks . with_meta . with_version $ el-  where-    el = prependContents [text_node "\n"] $ emptyElement "Pandoc"-    with_version = addAttribute atNameApiVersion (T.intercalate "," $ map (T.pack . show) $ versionBranch pandocTypesVersion)-    with_meta = appendContents (metaMapToXML meta "meta")-    with_blocks = appendContents (asBlockOfBlocks $ elementWithContents "blocks" $ blocksToXML blocks)-    with_header :: Element -> T.Text-    with_header e = T.concat [T.pack xml_header, "\n", showElement e]+pandocToXmlText :: WriterOptions -> Pandoc -> T.Text+pandocToXmlText opts (Pandoc (Meta meta) blocks) =+  case writerTemplate opts of+       Just _ -> -- standalone document; include Pandoc and Meta+         ppcTopElement configPP . with_blocks . with_meta . with_version $ el+       Nothing -> -- fragment; just include blocks, as native writer does+         mconcat $ map (ppcContent configPP) block_contents+ where+   el = emptyElement "Pandoc"+   with_version = addAttribute atNameApiVersion version+   version = (T.intercalate "," $ map (T.pack . show)+                                $ versionBranch pandocTypesVersion)+   with_meta = appendContents (metaMapToXML meta "meta")+   with_blocks = appendContents $ asContents block_element+   block_element = elementWithContents "blocks" block_contents+   block_contents = blocksToXML blocks +-- | Pretty-printing configuration: the contents of elements that+-- contain inline content are kept on a single line, so that no+-- significant whitespace is added inside them.+configPP :: ConfigPP+configPP = useInlineTags (isInlineTag . qName) prettyConfigPP++-- | Check whether a tag is for an element with inline content.+isInlineTag :: T.Text -> Bool+isInlineTag t =+  case t of+    "Para" -> True+    "Plain" -> True+    "Header" -> True+    "MetaInlines" -> True+    "Emph" -> True+    "Strong" -> True+    "Strikeout" -> True+    "Superscript" -> True+    "Subscript" -> True+    "SmallCaps" -> True+    "Underline" -> True+    "Quoted" -> True+    "Cite" -> True+    "Link" -> True+    "Image" -> True+    "Span" -> True+    _ ->+      t == tgNameLineItem+        || t == tgNameDefListTerm+        || t == tgNameCitationPrefix+        || t == tgNameCitationSuffix+        || t == tgNameShortCaption+ metaMapToXML :: Map T.Text MetaValue -> T.Text -> [Content]-metaMapToXML mmap tag = asBlockOfBlocks $ elementWithContents tag entries+metaMapToXML mmap tag = asContents $ elementWithContents tag entries   where     entries = concatMap to_entry $ toList mmap     to_entry :: (T.Text, MetaValue) -> [Content]-    to_entry (text, metavalue) = asBlockOfBlocks with_key+    to_entry (text, metavalue) = asContents with_key       where         entry = elementWithContents tgNameMetaMapEntry $ metaValueToXML metavalue         with_key = addAttribute atNameMetaMapEntryKey text entry@@ -108,21 +155,60 @@   let name = itemName value       el = itemAsEmptyElement value    in case (value) of-        MetaBool b -> asBlockOfInlines $ addAttribute atNameMetaBoolValue bool_value el+        MetaBool b -> asContents $ addAttribute atNameMetaBoolValue bool_value el           where             bool_value = if b then "true" else "false"-        MetaString s -> asBlockOfInlines $ appendContents [text_node s] el-        MetaInlines inlines -> asBlockOfInlines $ appendContents (inlinesToXML inlines) el-        MetaBlocks blocks -> asBlockOfBlocks $ appendContents (blocksToXML blocks) el-        MetaList items -> asBlockOfBlocks $ appendContents (concatMap metaValueToXML items) el+        MetaString s -> asContents $ appendContents [text_node s] el+        MetaInlines inlines -> asContents $ appendContents (inlinesToXML inlines) el+        MetaBlocks blocks -> asContents $ appendContents (blocksToXML blocks) el+        MetaList items -> asContents $ appendContents (concatMap metaValueToXML items) el         MetaMap mm -> metaMapToXML mm name  blocksToXML :: [Block] -> [Content] blocksToXML blocks = concatMap blockToXML blocks  inlinesToXML :: [Inline] -> [Content]-inlinesToXML inlines = concatMap inlineContentToContents (ilsToIlsContent inlines [])+inlinesToXML inlines = concatMap wsRunsAsElements $ mergeTextNodes $ concatMap inlineContentToContents (ilsToIlsContent inlines []) +-- | Merge consecutive text nodes into a single text node; otherwise+-- the pretty-printer would render each one on a line of its own.+mergeTextNodes :: [Content] -> [Content]+mergeTextNodes (Text (CData CDataText t _) : rest) =+  text_node (T.concat (t : ts)) : mergeTextNodes rest'+  where+    (ts, rest') = textRun rest+    textRun (Text (CData CDataText t' _) : cs) =+      let (ts', cs') = textRun cs in (t' : ts', cs')+    textRun cs = ([], cs)+mergeTextNodes (c : rest) = c : mergeTextNodes rest+mergeTextNodes [] = []++-- | A whitespace run in a text node is read back as a single Space+-- (a run of spaces) or a single SoftBreak (a run containing a+-- newline), so runs like " \n" or "\n\n" would not roundtrip: encode+-- them as sequences of Space and SoftBreak elements instead.+wsRunsAsElements :: Content -> [Content]+wsRunsAsElements (Text (CData CDataText t _))+  | hasLongWsRun = go [] (T.groupBy (\a b -> isWs a == isWs b) t)+  where+    isWs ch = ch == ' ' || ch == '\n'+    -- a whitespace run needs encoding only if it is longer than one+    -- character (single-character runs are " " or "\n", which are+    -- kept); the common case of no such run avoids the work below+    hasLongWsRun = fst $ T.foldl' adjacent (False, False) t+    adjacent (found, prevWs) ch =+      let ws = isWs ch in (found || (prevWs && ws), ws)+    keep r = r == " " || r == "\n" || not (T.any isWs r)+    go acc (r : rs)+      | keep r = go (r : acc) rs+      | otherwise = flush acc ++ map toElem (T.unpack r) ++ go [] rs+    go acc [] = flush acc+    flush [] = []+    flush acc = [text_node $ T.concat $ reverse acc]+    toElem '\n' = Elem $ emptyElement "SoftBreak"+    toElem _ = Elem $ emptyElement "Space"+wsRunsAsElements c = [c]+ data InlineContent   = NormalInline Inline   | ElSpace Int@@ -159,28 +245,28 @@ asContents el = [Elem el]  wrapBlocks :: T.Text -> [Block] -> [Content]-wrapBlocks tag blocks = asBlockOfBlocks $ elementWithContents tag $ blocksToXML blocks+wrapBlocks tag blocks = asContents $ elementWithContents tag $ blocksToXML blocks  wrapArrayOfBlocks :: T.Text -> [[Block]] -> [Content] wrapArrayOfBlocks tag array = concatMap (wrapBlocks tag) array  -- wrapInlines :: T.Text -> [Inline] -> [Content]--- wrapInlines tag inlines = asBlockOfInlines $ element_with_contents tag $ inlinesToXML inlines+-- wrapInlines tag inlines = asContents $ element_with_contents tag $ inlinesToXML inlines  blockToXML :: Block -> [Content] blockToXML block =   let el = itemAsEmptyElement block    in case (block) of-        Para inlines -> asBlockOfInlines $ appendContents (inlinesToXML inlines) el-        Header level (idn, cls, attrs) inlines -> asBlockOfInlines $ appendContents (inlinesToXML inlines) with_attr+        Para inlines -> asContents $ appendContents (inlinesToXML inlines) el+        Header level (idn, cls, attrs) inlines -> asContents $ appendContents (inlinesToXML inlines) with_attr           where             with_attr = addAttrAttributes (idn, cls, attrs ++ [(atNameLevel, intAsText level)]) el-        Plain inlines -> asBlockOfInlines $ appendContents (inlinesToXML inlines) el-        Div attr blocks -> asBlockOfBlocks $ appendContents (blocksToXML blocks) with_attr+        Plain inlines -> asContents $ appendContents (inlinesToXML inlines) el+        Div attr blocks -> asContents $ appendContents (blocksToXML blocks) with_attr           where             with_attr = addAttrAttributes attr el-        BulletList items -> asBlockOfBlocks $ appendContents (wrapArrayOfBlocks tgNameListItem items) el-        OrderedList (start, style, delim) items -> asBlockOfBlocks $ with_contents . with_attrs $ el+        BulletList items -> asContents $ appendContents (wrapArrayOfBlocks tgNameListItem items) el+        OrderedList (start, style, delim) items -> asContents $ with_contents . with_attrs $ el           where             with_attrs =               addAttributes@@ -191,16 +277,16 @@                     ]                 )             with_contents = appendContents (wrapArrayOfBlocks tgNameListItem items)-        BlockQuote blocks -> asBlockOfBlocks $ appendContents (blocksToXML blocks) el-        HorizontalRule -> asBlockOfInlines el-        CodeBlock attr text -> asBlockOfInlines $ with_contents . with_attr $ el+        BlockQuote blocks -> asContents $ appendContents (blocksToXML blocks) el+        HorizontalRule -> asContents el+        CodeBlock attr text -> asContents $ with_contents . with_attr $ el           where             with_contents = appendContents [text_node text]             with_attr = addAttrAttributes attr-        LineBlock lins -> asBlockOfBlocks $ appendContents (concatMap wrapInlines lins) el+        LineBlock lins -> asContents $ appendContents (concatMap wrapInlines lins) el           where             wrapInlines inlines = asContents $ appendContents (inlinesToXML inlines) $ emptyElement tgNameLineItem-        Table attr caption colspecs thead tbodies tfoot -> asBlockOfBlocks $ with_foot . with_bodies . with_head . with_colspecs . with_caption . with_attr $ el+        Table attr caption colspecs thead tbodies tfoot -> asContents $ with_foot . with_bodies . with_head . with_colspecs . with_caption . with_attr $ el           where             with_attr = addAttrAttributes attr             with_caption = appendContents (captionToXML caption)@@ -208,7 +294,7 @@             with_head = appendContents (tableHeadToXML thead)             with_bodies = appendContents (concatMap tableBodyToXML tbodies)             with_foot = appendContents (tableFootToXML tfoot)-        Figure attr caption blocks -> asBlockOfBlocks $ with_contents . with_caption . with_attr $ el+        Figure attr caption blocks -> asContents $ with_contents . with_caption . with_attr $ el           where             with_attr = addAttrAttributes attr             with_caption = appendContents (captionToXML caption)@@ -216,7 +302,7 @@         RawBlock (Format format) text -> asContents $ appendContents [text_node text] raw           where             raw = addAttribute atNameFormat format el-        DefinitionList items -> asBlockOfBlocks $ appendContents (map definitionListItemToXML items) el+        DefinitionList items -> asContents $ appendContents (map definitionListItemToXML items) el  inlineToXML :: Inline -> [Content] inlineToXML inline =@@ -235,17 +321,17 @@         SmallCaps inlines -> wrapInlines inlines         Superscript inlines -> wrapInlines inlines         Subscript inlines -> wrapInlines inlines-        SoftBreak -> asContents el+        SoftBreak -> [text_node "\n"]         LineBreak -> asContents el         Span attr inlines -> asContents $ appendContents (inlinesToXML inlines) with_attr           where             with_attr = addAttrAttributes attr el         Link (idn, cls, attrs) inlines (url, title) -> asContents $ appendContents (inlinesToXML inlines) with_attr           where-            with_attr = addAttrAttributes (idn, cls, attrs ++ [(atNameLinkUrl, url), (atNameTitle, title)]) el+            with_attr = addAttrAttributes (idn, cls, attrs ++ optionalAttribute atNameLinkUrl url ++ optionalAttribute atNameTitle title) el         Image (idn, cls, attrs) inlines (url, title) -> asContents $ appendContents (inlinesToXML inlines) with_attr           where-            with_attr = addAttrAttributes (idn, cls, attrs ++ [(atNameImageUrl, url), (atNameTitle, title)]) el+            with_attr = addAttrAttributes (idn, cls, attrs ++ optionalAttribute atNameImageUrl url ++ optionalAttribute atNameTitle title) el         RawInline (Format format) text -> asContents $ appendContents [text_node text] raw           where             raw = addAttribute atNameFormat format el@@ -262,10 +348,15 @@  -- TODO: don't let an attribute overwrite id or class maybeAttribute :: (T.Text, T.Text) -> Maybe XML.Attr-maybeAttribute (_, "") = Nothing maybeAttribute ("", _) = Nothing-maybeAttribute (name, value) = Just $ XML.Attr (unqual name) value+maybeAttribute (name, value) = Just $ XML.Attr (unqual $ encodeAttrName name) value +-- | An optional attribute, omitted when its value is empty (the+-- reader treats a missing attribute as an empty value).+optionalAttribute :: T.Text -> T.Text -> [(T.Text, T.Text)]+optionalAttribute _ "" = []+optionalAttribute name value = [(name, value)]+ validAttributes :: [(T.Text, T.Text)] -> [XML.Attr] validAttributes pairs = mapMaybe maybeAttribute pairs @@ -286,13 +377,17 @@ addAttrAttributes :: PandocAttr -> Element -> Element addAttrAttributes (identifier, classes, attributes) el = addAttributes attrs' el   where-    attrs' = mapMaybe maybeAttribute (("id", identifier) : ("class", T.intercalate " " classes) : attributes)+    attrs' =+      mapMaybe maybeAttribute $+        optionalAttribute "id" identifier+          ++ optionalAttribute "class" (T.intercalate " " classes)+          ++ attributes  addCitations :: [Citation] -> Element -> Element-addCitations citations el = appendContents [Elem $ elementWithContents tgNameCitations $ (text_node "\n") : concatMap citation_to_elem citations] el+addCitations citations el = appendContents [Elem $ elementWithContents tgNameCitations $ concatMap citation_to_elem citations] el   where     citation_to_elem :: Citation -> [Content]-    citation_to_elem citation = asBlockOfInlines with_suffix+    citation_to_elem citation = asContents with_suffix       where         cit_elem = elementWithAttributes (itemName citation) attrs         prefix = citationPrefix citation@@ -309,7 +404,7 @@           map             (\(n, v) -> XML.Attr (unqual n) v)             [ ("id", citationId citation),-              (atNameCitationMode, T.pack $ show $ citationMode citation),+              (atNameCitationMode, itemName $ citationMode citation),               (atNameCitationNoteNum, intAsText $ citationNoteNum citation),               (atNameCitationHash, intAsText $ citationHash citation)             ]@@ -317,18 +412,18 @@ definitionListItemToXML :: ([Inline], [[Block]]) -> Content definitionListItemToXML (inlines, defs) = Elem $ elementWithContents tgNameDefListItem $ term ++ wrapArrayOfBlocks tgNameDefListDef defs   where-    term = asBlockOfInlines $ appendContents (inlinesToXML inlines) $ emptyElement tgNameDefListTerm+    term = asContents $ appendContents (inlinesToXML inlines) $ emptyElement tgNameDefListTerm  captionToXML :: Caption -> [Content]-captionToXML (Caption short blocks) = asBlockOfBlocks with_short_caption+captionToXML (Caption short blocks) = asContents with_short_caption   where     el = elementWithContents "Caption" $ blocksToXML blocks     with_short_caption = case (short) of-      Just inlines -> prependContents (asBlockOfInlines $ elementWithContents tgNameShortCaption $ inlinesToXML inlines) el+      Just inlines -> prependContents (asContents $ elementWithContents tgNameShortCaption $ inlinesToXML inlines) el       _ -> el  colSpecToXML :: (Alignment, ColWidth) -> [Content]-colSpecToXML (align, cw) = asBlockOfInlines colspec+colSpecToXML (align, cw) = asContents colspec   where     colspec = elementWithAttributes "ColSpec" $ validAttributes [(atNameAlignment, itemName align), (atNameColWidth, colwidth)]     colwidth = case (cw) of@@ -336,27 +431,27 @@       ColWidthDefault -> "0"  colSpecsToXML :: [(Alignment, ColWidth)] -> [Content]-colSpecsToXML colspecs = asBlockOfBlocks $ elementWithContents tgNameColspecs $ concatMap colSpecToXML colspecs+colSpecsToXML colspecs = asContents $ elementWithContents tgNameColspecs $ concatMap colSpecToXML colspecs  tableHeadToXML :: TableHead -> [Content]-tableHeadToXML (TableHead attr rows) = asBlockOfBlocks $ elementWithAttrAndContents "TableHead" attr $ concatMap rowToXML rows+tableHeadToXML (TableHead attr rows) = asContents $ elementWithAttrAndContents "TableHead" attr $ concatMap rowToXML rows  tableBodyToXML :: TableBody -> [Content]-tableBodyToXML (TableBody (idn, cls, attrs) (RowHeadColumns headcols) hrows brows) = asBlockOfBlocks $ elementWithAttrAndContents "TableBody" attr children+tableBodyToXML (TableBody (idn, cls, attrs) (RowHeadColumns headcols) hrows brows) = asContents $ elementWithAttrAndContents "TableBody" attr children   where     attr = (idn, cls, (atNameRowHeadColumns, intAsText headcols) : attrs)-    header_rows = asBlockOfBlocks $ elementWithContents tgNameBodyHeader $ concatMap rowToXML hrows-    body_rows = asBlockOfBlocks $ elementWithContents tgNameBodyBody $ concatMap rowToXML brows+    header_rows = asContents $ elementWithContents tgNameBodyHeader $ concatMap rowToXML hrows+    body_rows = asContents $ elementWithContents tgNameBodyBody $ concatMap rowToXML brows     children = header_rows ++ body_rows  tableFootToXML :: TableFoot -> [Content]-tableFootToXML (TableFoot attr rows) = asBlockOfBlocks $ elementWithAttrAndContents "TableFoot" attr $ concatMap rowToXML rows+tableFootToXML (TableFoot attr rows) = asContents $ elementWithAttrAndContents "TableFoot" attr $ concatMap rowToXML rows  rowToXML :: Row -> [Content]-rowToXML (Row attr cells) = asBlockOfBlocks $ elementWithAttrAndContents "Row" attr $ concatMap cellToXML cells+rowToXML (Row attr cells) = asContents $ elementWithAttrAndContents "Row" attr $ concatMap cellToXML cells  cellToXML :: Cell -> [Content]-cellToXML (Cell (idn, cls, attrs) alignment (RowSpan rowspan) (ColSpan colspan) blocks) = asBlockOfBlocks $ elementWithAttrAndContents "Cell" attr $ blocksToXML blocks+cellToXML (Cell (idn, cls, attrs) alignment (RowSpan rowspan) (ColSpan colspan) blocks) = asContents $ elementWithAttrAndContents "Cell" attr $ blocksToXML blocks   where     with_alignment a = (atNameAlignment, itemName alignment) : a     with_rowspan a = if rowspan > 1 then (atNameRowspan, intAsText rowspan) : a else a
src/Text/Pandoc/XMLFormat.hs view
@@ -2,7 +2,9 @@ {-# LANGUAGE ScopedTypeVariables #-}  module Text.Pandoc.XMLFormat-  ( atNameAlignment,+  ( decodeAttrName,+    encodeAttrName,+    atNameAlignment,     atNameApiVersion,     atNameCitationHash,     atNameCitationMode,@@ -41,7 +43,93 @@   ) where +import Data.Char (chr, digitToInt, isAsciiLower, isAsciiUpper, isDigit, isHexDigit, ord, toUpper) import Data.Text (Text)+import qualified Data.Text as T+import Numeric (showHex)++-- | Encode an attribute name so that it is a valid XML name.+-- Characters that are not allowed in XML names -- and colons, which+-- XML parsers treat as namespace separators -- are encoded as+-- _xHHHH_, where HHHH is the hexadecimal code of the character+-- (uppercase, at least four digits).  An underscore that introduces+-- a literal "_x" is encoded as _x005F_, so that decoding is+-- unambiguous.  For example, "typst:property" is encoded as+-- "typst_x003A_property".+encodeAttrName :: Text -> Text+encodeAttrName name =+  case T.unpack name of+    [] -> name+    c : cs+      | isNameStartChar c && all isNameChar cs && not ("_x" `T.isInfixOf` name) ->+          name+      | otherwise ->+          T.pack $ concat $ go isNameStartChar (c : cs)+  where+    go _ [] = []+    go ok (c : cs)+      | c == '_' && take 1 cs == "x" = encodeChar '_' : go isNameChar cs+      | ok c = [c] : go isNameChar cs+      | otherwise = encodeChar c : go isNameChar cs+    encodeChar c = "_x" ++ replicate (4 - length h) '0' ++ h ++ "_"+      where+        h = map toUpper $ showHex (ord c) ""++-- | Decode an attribute name encoded by 'encodeAttrName': _xHHHH_+-- sequences (four to six hexadecimal digits) are decoded to the+-- character with the given code.+decodeAttrName :: Text -> Text+decodeAttrName name+  | "_x" `T.isInfixOf` name = T.concat $ go name+  | otherwise = name+  where+    go t =+      case T.breakOn "_x" t of+        (pre, rest)+          | T.null rest -> [pre]+          | otherwise ->+              let body = T.drop 2 rest+                  digits = T.takeWhile isHexDigit body+                  n = T.length digits+                  code = T.foldl' (\acc d -> 16 * acc + digitToInt d) 0 digits+               in if n >= 4+                    && n <= 6+                    && "_" `T.isPrefixOf` T.drop n body+                    && validChar code+                    then pre : T.singleton (chr code) : go (T.drop (n + 1) body)+                    else pre : "_x" : go body+    validChar code = code <= 0x10FFFF && not (code >= 0xD800 && code <= 0xDFFF)++-- the XML NameStartChar production, without the colon (which XML+-- parsers treat as a namespace separator)+isNameStartChar :: Char -> Bool+isNameStartChar c =+  isAsciiUpper c+    || isAsciiLower c+    || c == '_'+    || (c >= '\xC0' && c <= '\xD6')+    || (c >= '\xD8' && c <= '\xF6')+    || (c >= '\xF8' && c <= '\x2FF')+    || (c >= '\x370' && c <= '\x37D')+    || (c >= '\x37F' && c <= '\x1FFF')+    || (c >= '\x200C' && c <= '\x200D')+    || (c >= '\x2070' && c <= '\x218F')+    || (c >= '\x2C00' && c <= '\x2FEF')+    || (c >= '\x3001' && c <= '\xD7FF')+    || (c >= '\xF900' && c <= '\xFDCF')+    || (c >= '\xFDF0' && c <= '\xFFFD')+    || (c >= '\x10000' && c <= '\xEFFFF')++-- the XML NameChar production, without the colon+isNameChar :: Char -> Bool+isNameChar c =+  isNameStartChar c+    || isDigit c+    || c == '-'+    || c == '.'+    || c == '\xB7'+    || (c >= '\x300' && c <= '\x36F')+    || (c >= '\x203F' && c <= '\x2040')  -- the attribute carrying the API version of pandoc types in the main Pandoc element atNameApiVersion :: Text
+ test/Tests/ImageSize.hs view
@@ -0,0 +1,335 @@+{-# LANGUAGE OverloadedStrings #-}+{- |+   Module      : Tests.ImageSize+   Copyright   : © 2025 John MacFarlane+   License     : GNU GPL, version 2 or above++   Maintainer  : John MacFarlane <jgm@berkeley.edu>+   Stability   : alpha+   Portability : portable++Tests for image type and size detection.+-}+module Tests.ImageSize (tests) where++import Data.Bits (shiftR)+import qualified Data.ByteString as B+import Data.Word (Word8)+import Test.Tasty+import Test.Tasty.HUnit+import Text.Pandoc.ImageSize+import Text.Pandoc.Options (def)++-- helpers to construct binary test data:++le32, be32 :: Int -> B.ByteString+le32 n = B.pack $ map (fromIntegral . (n `shiftR`) . (8 *)) [0,1,2,3]+be32 n = B.pack $ map (fromIntegral . (n `shiftR`) . (8 *)) [3,2,1,0]++be16 :: Int -> B.ByteString+be16 n = B.pack $ map (fromIntegral . (n `shiftR`) . (8 *)) [1,0]++-- | An ISO BMFF box with the given type and contents.+box :: B.ByteString -> B.ByteString -> B.ByteString+box name body = be32 (8 + B.length body) <> name <> body++be64 :: Int -> B.ByteString+be64 n = B.pack $ map (fromIntegral . (n `shiftR`) . (8 *)) [7,6..0]++-- | An ISO BMFF box using the 64-bit "largesize" field (size == 1).+largeBox :: B.ByteString -> B.ByteString -> B.ByteString+largeBox name body = be32 1 <> name <> be64 (16 + B.length body) <> body++-- | An ISO BMFF box with size == 0 (extends to the end of the file).+zeroBox :: B.ByteString -> B.ByteString -> B.ByteString+zeroBox name body = be32 0 <> name <> body++-- | An EMF header with the given frame bounds (1/100 mm), reference+-- device size in pixels, and reference device size in mm.+emfFile :: [Int] -> [Int] -> [Int] -> B.ByteString+emfFile frame device mm = B.concat $+  [le32 1, B.replicate 20 0] <> map le32 frame <>+  [" EMF", B.replicate 28 0] <> map le32 (device <> mm)++-- | A WebP RIFF container with the given chunk.+webpFile :: B.ByteString -> B.ByteString+webpFile chunk = "RIFF\0\0\0\0WEBP" <> chunk++svgFile :: B.ByteString -> B.ByteString+svgFile attrs =+  "<svg xmlns=\"http://www.w3.org/2000/svg\" " <> attrs <> "></svg>"++jpegBare, jpegApp0 :: B.ByteString+jpegBare = B.pack [0xff, 0xd8, 0xff, 0xdb] <> "rest"+jpegApp0 = B.pack [0xff, 0xd8, 0xff, 0xe0] <> "rest"++-- | A PNG chunk with the given type and body (and a dummy CRC).+pngChunk :: B.ByteString -> B.ByteString -> B.ByteString+pngChunk name body = be32 (B.length body) <> name <> body <> B.replicate 4 0++-- | A PNG header with the given extra chunks after IHDR (and no+-- image data).+pngFile :: [B.ByteString] -> Int -> Int -> B.ByteString+pngFile chunks w h = B.concat $+  [ "\x89PNG\r\n\x1a\n"+  , pngChunk "IHDR" (be32 w <> be32 h <> B.pack [8, 3, 0, 0, 0]) ]+  <> chunks++-- | A pHYs chunk with the given unit (1 = pixels per meter) and+-- pixel densities.+physChunk :: Word8 -> Int -> Int -> B.ByteString+physChunk unit x y = pngChunk "pHYs" (be32 x <> be32 y <> B.pack [unit])++-- | A JPEG marker segment with the given marker and body.+jpegSeg :: Word8 -> B.ByteString -> B.ByteString+jpegSeg m body = B.pack [0xff, m] <> be16 (B.length body + 2) <> body++-- | A JPEG header: SOI marker, the given segments, and a baseline+-- SOF segment with the given width and height (and no image data).+jpegFile :: [B.ByteString] -> Int -> Int -> B.ByteString+jpegFile segs w h = B.concat $ [B.pack [0xff, 0xd8]] <> segs <>+  [jpegSeg 0xc0 (B.pack [8] <> be16 h <> be16 w <> B.pack [3])]++-- | A JFIF APP0 segment with the given density units (0 = aspect+-- ratio only, 1 = dots per inch, 2 = dots per cm) and densities.+jfifSeg :: Word8 -> Int -> Int -> B.ByteString+jfifSeg units x y = jpegSeg 0xe0 $+  "JFIF\0" <> B.pack [1, 2, units] <> be16 x <> be16 y <> "\0\0"++-- | An Exif APP1 segment giving 300x200 dpi resolution.+exifSeg :: B.ByteString+exifSeg = jpegSeg 0xe1 $ "Exif\0\0"+  <> "MM" <> be16 42 <> be32 8           -- TIFF header (big-endian)+  <> be16 3                              -- IFD entry count+  <> entry 0x011a 5 (be32 50)            -- XResolution at offset 50+  <> entry 0x011b 5 (be32 58)            -- YResolution at offset 58+  <> entry 0x0128 3 (be16 2 <> be16 0)   -- ResolutionUnit: inches+  <> be32 0                              -- next IFD offset+  <> be32 300 <> be32 1                  -- XResolution = 300/1+  <> be32 200 <> be32 1                  -- YResolution = 200/1+  where entry tag typ val = be16 tag <> be16 typ <> be32 1 <> val++-- the contents of a meta box locating an ispe (image spatial+-- extents) box giving dimensions 640x480:+avifMeta :: B.ByteString+avifMeta = B.replicate 4 0 <>  -- version/flags+             box "iprp" (box "ipco"+               (box "ispe" (B.replicate 4 0 <> be32 640 <> be32 480)))++-- an AVIF image with an ispe (image spatial extents) box:+avifIspe :: B.ByteString+avifIspe = box "ftyp" ("avif" <> B.replicate 4 0) <> box "meta" avifMeta++-- an (animated) AVIF image with dimensions only in a tkhd box:+avisTkhd :: B.ByteString+avisTkhd = box "ftyp" ("avis" <> B.replicate 4 0)+        <> box "moov" (box "trak" (box "tkhd"+             (B.replicate 4 0 <>   -- version/flags+              B.replicate 72 0 <>  -- times, track id, duration, etc.+              be32 (640 * 65536) <> be32 (480 * 65536))))  -- 16.16++-- lossless webp, 100x200: width - 1 in bits 0-13, height - 1 in+-- bits 14-27 of the little-endian word after the 0x2f signature+webpLossless :: B.ByteString+webpLossless = webpFile $ "VP8L\0\0\0\0"+  <> B.pack [0x2f, 0x63, 0xc0, 0x31, 0x00]++-- lossy webp, 320x240: frame tag, then 9d 01 2a keyframe signature,+-- then 14-bit width and height as little-endian 16-bit words+webpLossy :: B.ByteString+webpLossy = webpFile $ "VP8 \0\0\0\0"+  <> B.pack [0x00, 0x00, 0x00, 0x9d, 0x01, 0x2a, 0x40, 0x01, 0xf0, 0x00]++-- extended webp, 1000x500: canvas width and height - 1 as 24-bit+-- little-endian words after the flags+webpExtended :: B.ByteString+webpExtended = webpFile $ "VP8X" <> B.replicate 8 0+  <> B.pack [0xe7, 0x03, 0x00, 0xf3, 0x01, 0x00]++-- zlib-compressed "<</MediaBox [0 0 100 200]>>"+compressedMediaBox :: B.ByteString+compressedMediaBox = B.pack+  [ 120, 156, 179, 177, 209, 247, 77, 77, 201, 76, 116, 202, 175, 80+  , 136, 54, 80, 48, 80, 48, 52, 48, 80, 48, 50, 48, 136, 181, 179+  , 3, 0, 104, 186, 6, 232 ]++tests :: [TestTree]+tests =+  [ testGroup "imageType"+    [ testCase "png" $ imageType "\x89PNG\r\n\x1a\n" @?= Just Png+    , testCase "gif" $ imageType "GIF89a" @?= Just Gif+    , testCase "tiff (little-endian)" $ imageType "II\x2a\0" @?= Just Tiff+    , testCase "tiff (big-endian)" $ imageType "MM\0\x2a" @?= Just Tiff+    , testCase "jpeg with app segment" $ imageType jpegApp0 @?= Just Jpeg+    , testCase "jpeg without app segment" $ imageType jpegBare @?= Just Jpeg+    , testCase "pdf" $ imageType "%PDF-1.5" @?= Just Pdf+    , testCase "eps" $ imageType "%!PS-Adobe-3.0 EPSF-3.0" @?= Just Eps+    , testCase "svg" $ imageType (svgFile "") @?= Just Svg+    , testCase "svg with xml declaration" $+        imageType ("<?xml version=\"1.0\"?>\n" <> svgFile "") @?= Just Svg+    , testCase "uppercase svg with xml declaration" $+        imageType "<?xml version=\"1.0\"?>\n<SVG></SVG>" @?= Just Svg+    , testCase "xml that is not svg" $+        imageType "<?xml version=\"1.0\"?>\n<html><p>svg</p></html>"+          @?= Nothing+    , testCase "svg with BOM" $+        imageType ("\xef\xbb\xbf" <> svgFile "") @?= Just Svg+    , testCase "emf" $+        imageType (emfFile [0,0,1,1] [1,1] [1,1]) @?= Just Emf+    , testCase "webp" $ imageType webpLossless @?= Just Webp+    , testCase "avif brand" $ imageType avifIspe @?= Just Avif+    , testCase "avis brand" $ imageType avisTkhd @?= Just Avif+    , testCase "other ISO media is not avif" $+        imageType (box "ftyp" ("isom" <> B.replicate 4 0)) @?= Nothing+    , testCase "garbage" $ imageType "garbage!" @?= Nothing+    ]+  , testGroup "numUnit"+    [ testCase "number and unit" $ numUnit "3cm" @?= Just (3.0, "cm")+    , testCase "space between number and unit" $+        numUnit "3 cm" @?= Just (3.0, "cm")+    , testCase "bare number" $ numUnit "3.5" @?= Just (3.5, "")+    , testCase "no number" $ numUnit "cm" @?= Nothing+    , testCase "lengthToDim with space" $+        lengthToDim "3 cm" @?= Just (Centimeter 3)+    ]+  , testGroup "imageSize"+    [ testGroup "eps"+      [ testCase "zero origin" $+          imageSize def "%!PS EPSF\n%%BoundingBox: 0 0 612 792\n"+            @?= Right (ImageSize 612 792 72 72)+      , testCase "nonzero origin" $+          imageSize def "%!PS EPSF\n%%BoundingBox: 10 20 110 220\n"+            @?= Right (ImageSize 100 200 72 72)+      , testCase "negative origin" $+          imageSize def "%!PS EPSF\n%%BoundingBox: -10 -10 90 190\n"+            @?= Right (ImageSize 100 200 72 72)+      ]+    , testGroup "pdf"+      [ testCase "MediaBox" $+          imageSize def "%PDF-1.4\n<</MediaBox [0 0 612 792]>>"+            @?= Right (ImageSize 612 792 72 72)+      , testCase "MediaBox in compressed object stream" $+          imageSize def ("%PDF-1.5\n<</Type /ObjStm>>\nstream\n"+                          <> compressedMediaBox <> "\nendstream\n")+            @?= Right (ImageSize 100 200 72 72)+      , testCase "corrupt compressed object stream" $+          imageSize def ("%PDF-1.5\n<</Type /ObjStm>>\nstream\n"+                          <> "NOT ZLIB DATA\nendstream\n")+            @?= Left "could not determine PDF size"+      , testCase "MediaBox after corrupt object stream" $+          imageSize def ("%PDF-1.5\n<</Type /ObjStm>>\nstream\n"+                          <> "NOT ZLIB DATA\nendstream\n"+                          <> "<</MediaBox [0 0 300 400]>>")+            @?= Right (ImageSize 300 400 72 72)+      , testCase "MediaBox after empty object stream" $+          imageSize def ("%PDF-1.5\n<</Type /ObjStm>>\nstream\n"+                          <> "endstream<</MediaBox [0 0 25 50]>>")+            @?= Right (ImageSize 25 50 72 72)+      ]+    , testGroup "svg"+      [ testCase "width and height attributes" $+          imageSize def (svgFile "width=\"50\" height=\"60\"")+            @?= Right (ImageSize 50 60 96 96)+      , testCase "viewBox fallback" $+          imageSize def (svgFile "viewBox=\"0 0 100 200\"")+            @?= Right (ImageSize 100 200 96 96)+      , testCase "viewBox with commas" $+          imageSize def (svgFile "viewBox=\"0,0,100,200\"")+            @?= Right (ImageSize 100 200 96 96)+      , testCase "viewBox with fractional values" $+          imageSize def (svgFile "viewBox=\"0, 0, 210.5, 297.3\"")+            @?= Right (ImageSize 210 297 96 96)+      , testCase "no size information" $+          imageSize def (svgFile "")+            @?= Left "could not determine SVG size"+      ]+    , testGroup "emf"+      [ testCase "size and dpi from header" $+          imageSize def (emfFile [0,0,10000,5000] [1024,768] [320,240])+            @?= Right (ImageSize 320 160 81 81)+      , testCase "nonzero frame origin" $+          imageSize def (emfFile [2000,1000,12000,6000] [1024,768] [320,240])+            @?= Right (ImageSize 320 160 81 81)+      , testCase "zero-size reference device" $+          imageSize def (emfFile [0,0,10000,5000] [1024,768] [0,240])+            @?= Left "could not determine EMF size"+      ]+    , testGroup "webp"+      [ testCase "lossless (VP8L)" $+          imageSize def webpLossless @?= Right (ImageSize 100 200 96 96)+      , testCase "lossy (VP8)" $+          imageSize def webpLossy @?= Right (ImageSize 320 240 96 96)+      , testCase "extended (VP8X)" $+          imageSize def webpExtended @?= Right (ImageSize 1000 500 96 96)+      ]+    , testGroup "avif"+      [ testCase "ispe box" $+          imageSize def avifIspe @?= Right (ImageSize 640 480 96 96)+      , testCase "tkhd box" $+          imageSize def avisTkhd @?= Right (ImageSize 640 480 96 96)+      , testCase "meta box with 64-bit largesize" $+          imageSize def (box "ftyp" ("avif" <> B.replicate 4 0)+                          <> largeBox "meta" avifMeta)+            @?= Right (ImageSize 640 480 96 96)+      , testCase "meta box with size 0 (extends to end of file)" $+          imageSize def (box "ftyp" ("avif" <> B.replicate 4 0)+                          <> zeroBox "meta" avifMeta)+            @?= Right (ImageSize 640 480 96 96)+      , testCase "unknown box before meta box" $+          imageSize def (box "ftyp" ("avif" <> B.replicate 4 0)+                          <> box "free" "junk" <> box "meta" avifMeta)+            @?= Right (ImageSize 640 480 96 96)+      ]+    , testGroup "png"  -- headers without image data, so these only+                       -- succeed if no decoding is attempted+      [ testCase "no pHYs chunk" $+          imageSize def (pngFile [] 640 480)+            @?= Right (ImageSize 640 480 72 72)+      , testCase "pHYs in pixels per meter" $+          imageSize def (pngFile [physChunk 1 3937 3937] 640 480)+            @?= Right (ImageSize 640 480 99 99)+      , testCase "pHYs with unknown unit" $+          imageSize def (pngFile [physChunk 0 4 3] 640 480)+            @?= Right (ImageSize 640 480 72 72)+      , testCase "pHYs after another chunk" $+          imageSize def (pngFile [ pngChunk "tEXt" "k\0v"+                                 , physChunk 1 3937 3937 ] 640 480)+            @?= Right (ImageSize 640 480 99 99)+      ]+    , testGroup "jpeg"  -- headers without image data, so these only+                        -- succeed if no decoding is attempted+      [ testCase "jfif dpi" $+          imageSize def (jpegFile [jfifSeg 1 96 96] 640 480)+            @?= Right (ImageSize 640 480 96 96)+      , testCase "jfif density in dots per cm" $+          imageSize def (jpegFile [jfifSeg 2 100 100] 640 480)+            @?= Right (ImageSize 640 480 254 254)+      , testCase "jfif aspect ratio only" $+          imageSize def (jpegFile [jfifSeg 0 1 1] 640 480)+            @?= Right (ImageSize 640 480 72 72)+      , testCase "exif resolution" $+          imageSize def (jpegFile [exifSeg] 640 480)+            @?= Right (ImageSize 640 480 300 200)+      , testCase "exif overrides jfif" $+          imageSize def (jpegFile [jfifSeg 1 96 96, exifSeg] 640 480)+            @?= Right (ImageSize 640 480 300 200)+      , testCase "no app segments" $+          imageSize def (jpegFile [jpegSeg 0xdb (B.replicate 65 0)] 640 480)+            @?= Right (ImageSize 640 480 72 72)+      , testCase "defective zero density defaults to 72 (#6880)" $+          imageSize def (jpegFile [jfifSeg 1 0 0] 640 480)+            @?= Right (ImageSize 640 480 72 72)+      ]+    , testGroup "fixtures"+      [ testCase "lalune.jpg" $ do+          img <- B.readFile "lalune.jpg"+          imageSize def img @?= Right (ImageSize 250 250 120 120)+      , testCase "fb2/test-small.png" $ do+          img <- B.readFile "fb2/test-small.png"+          imageSize def img @?= Right (ImageSize 48 32 71 71)+      , testCase "bodybg.gif" $ do+          img <- B.readFile "bodybg.gif"+          imageSize def img @?= Right (ImageSize 230 334 72 72)+      ]+    ]+  ]
test/Tests/MediaBag.hs view
@@ -6,6 +6,7 @@ -- import Tests.Helpers import Text.Pandoc.Class.IO (extractMedia) import Text.Pandoc.Class (fillMediaBag, runIOorExplode)+import Text.Pandoc.MediaBag (insertMedia, lookupMedia, mediaPath) import System.IO.Temp (withTempDirectory) import System.FilePath import Text.Pandoc.Builder as B@@ -14,6 +15,34 @@  tests :: [TestTree] tests = [+  testCase "insertMedia mediaPath sanitization" $ do+      -- a ".." substring that is not a path component is harmless+      -- and should not cause the file to be renamed:+      let bag = insertMedia "foo..bar.png" Nothing "contents" mempty+      (mediaPath <$> lookupMedia "foo..bar.png" bag) @?= Just "foo..bar.png"+      -- a ".." path component must not survive into mediaPath:+      let bag2 = insertMedia "../evil.png" Nothing "contents" mempty+      case lookupMedia "../evil.png" bag2 of+        Nothing -> assertFailure "item not found in media bag"+        Just item -> assertBool "mediaPath contains a .. component"+          (".." `notElem` splitDirectories (mediaPath item)),+  testCase "path canonicalization" $ do+      -- redundant . and .. components are collapsed, so equivalent+      -- spellings of a path refer to the same item:+      let bag = insertMedia "img/../sub/./lalune.png" Nothing "contents" mempty+      (mediaPath <$> lookupMedia "sub/lalune.png" bag) @?= Just "sub/lalune.png"+      (mediaPath <$> lookupMedia "img/../sub/lalune.png" bag)+        @?= Just "sub/lalune.png",+  testCase "no mediaPath collisions between escaped and literal keys" $ do+      -- "a%20b.png" used to unescape to the same mediaPath as the+      -- literal "a b.png", so one clobbered the other on extraction:+      let bag = insertMedia "a%20b.png" Nothing "contents1" $+                insertMedia "a b.png" Nothing "contents2" mempty+      case (lookupMedia "a%20b.png" bag, lookupMedia "a b.png" bag) of+        (Just i1, Just i2) -> assertBool+          "escaped and literal keys share a mediaPath"+          (mediaPath i1 /= mediaPath i2)+        _ -> assertFailure "items not found in media bag",   testCase "test fillMediaBag & extractMedia" $       withTempDirectory "." "extractMediaTest" $ \tmpdir -> do         -- Use absolute paths so the test does not need to change@@ -27,7 +56,9 @@                   -- absolute path -> extracted with hashed name                   B.para (B.image (T.pack absLalune) "" mempty) <>                   B.para (B.image "data:image/png;base64,cHJpbnQgImhlbGxvIgo=;.lua+%2f%2e%2e%2f%2e%2e%2fa%2elua" "" mempty) <>-                  B.para (B.image "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" "" mempty)+                  B.para (B.image "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" "" mempty) <>+                  -- the data: scheme is case-insensitive+                  B.para (B.image "DATA:image/gif;base64,dXBwZXJjYXNlIGRhdGEgdXJpIHRlc3QK" "" mempty)         let fooDir = absTmpdir </> "foo"         runIOorExplode $ do           fillMediaBag d@@ -42,6 +73,9 @@           (exists3 && not exists4)         exists5 <- doesFileExist (fooDir </> "d5fceb6532643d0d84ffe09c40c481ecdf59e15a.gif")         assertBool "data uri with gif is not properly decoded" exists5+        exists5a <- doesFileExist+          (fooDir </> "81c7546d23179ce1b344a763aa9038c3a8ff85d0.gif")+        assertBool "data uri with uppercase scheme is not extracted" exists5a         -- double-encoded version:         let e = B.doc $                   B.para (B.image "data:image/png;base64,cHJpbnQgInB3bmVkIgo=;.lua+%252f%252e%252e%252f%252e%252e%252fb%252elua" "" mempty)
test/Tests/Readers/Docx.hs view
@@ -116,6 +116,10 @@             "docx/links.docx"             "docx/links.native"           , testCompare+            "hyperlinks with ScreenTips"+            "docx/link_tooltips.docx"+            "docx/link_tooltips.native"+          , testCompare             "hyperlinks in <w:instrText> tag"             "docx/instrText_hyperlink.docx"             "docx/instrText_hyperlink.native"
test/Tests/Readers/LaTeX.hs view
@@ -250,6 +250,51 @@             mconcat ["^^" <> T.pack [i] | i <- hex] =?>             para (str $ T.pack $ ['p'..'y']++['!'..'&'])           ]+        , testGroup "symbol commands"+          [ "qed" =:+            "A\\qed" =?> para (str "A\xa0\x25FB")+          ]+        , testGroup "newif"+          [ "newif defines conditional" =:+            "\\newif\\iffoo\\footrue\\iffoo yes\\fi" =?>+            para (str "yes")+          , "newif requires name starting with if" =:+            "\\newif\\foobar\\foobar hi\\fi" =?>+            para (str "hi")+          ]+        , testGroup "conditionals"+          [ "unknown conditional nested in skipped branch" =:+            "\\iffalse X\\ifdim\\wd0>0pt Y\\fi Z\\fi W" =?>+            para "W"+          , "nested conditionals with else branches" =:+            "\\iftrue A\\iffalse B\\else C\\fi D\\else E\\fi F" =?>+            para "ACDF"+          , "newif conditional nested in skipped branch" =:+            "\\newif\\iffoo\\iffalse x\\iffoo y\\fi z\\fi w" =?>+            para "w"+          , "nested conditional in else branch" =:+            "\\iffalse a\\else b\\iffalse c\\else d\\fi e\\fi f" =?>+            para "bdef"+          ]+        , testGroup "ligatures"+          [ "quote ligatures in normal text" =:+            "it's ``x'' `y'" =?>+            para ("it\8217s " <> doubleQuoted "x" <> " " <> singleQuoted "y")+          , "apostrophe in texttt stays ASCII" =:+            "\\texttt{it's}" =?> para (code "it's")+          , "backtick in texttt stays ASCII" =:+            "\\texttt{`x'}" =?> para (code "`x'")+          ]+        , testGroup "urls"+          [ "url with escaped %" =:+            "\\url{http://example.com/a\\%b}" =?>+            para (linkWith ("",["uri"],[]) "http://example.com/a%b" ""+                   (str "http://example.com/a%b"))+          , "url with escaped backslash" =:+            "\\url{http://example.com/a\\\\b}" =?>+            para (linkWith ("",["uri"],[]) "http://example.com/a\\b" ""+                   (str "http://example.com/a\\b"))+          ]         , testGroup "memoir scene breaks"           [ "plainbreak" =:             "hello\\plainbreak{2}goodbye" =?>
test/Tests/Readers/Org/Directive.hs view
@@ -52,6 +52,18 @@                   ] =?>         para "a^b" +    , "disable braced sub/superscript syntax" =:+        T.unlines [ "#+OPTIONS: ^:nil"+                  , "a^{b} c_{d}"+                  ] =?>+        para "a^{b} c_{d}"++    , "interpret only braced sub/superscripts" =:+        T.unlines [ "#+OPTIONS: ^:{}"+                  , "a^b a^{b} a^(b)"+                  ] =?>+        para ("a^b a" <> superscript "b" <> " a^(b)")+     , "directly select drawers to be exported" =:         T.unlines [ "#+OPTIONS: d:(\"IMPORTANT\")"                   , ":IMPORTANT:"
test/Tests/Readers/Org/Inline.hs view
@@ -112,6 +112,14 @@       "line \\\\ \nbreak" =?>       para ("line" <> linebreak <> "break") +  , "Bare backslash" =:+      "a \\ b" =?>+      para ("a" <> space <> "\\" <> space <> "b")++  , "Unclosed LaTeX environment" =:+      "\\begin{align} more text" =?>+      para ("\\begin{align}" <> space <> "more" <> space <> "text")+   , "Inline note" =:       "[fn::Schreib mir eine E-Mail]" =?>       para (note $ para "Schreib mir eine E-Mail")
test/Tests/Readers/Org/Inline/Note.hs view
@@ -82,6 +82,13 @@       , headerWith ("headline", [], []) 2 "Headline"       ] +  , "Inline note with hyphen and underscore in label" =:+      "Some text[fn:my_note-1: the note]" =?>+      para (mconcat+           [ "Some", space, "text"+           , note . para $ "the" <> space <> "note"+           ])+   , "Footnote followed by two blank lines" =:       T.unlines [ "footnote[fn:blanklines]"                 , ""
test/Tests/Shared.hs view
@@ -34,6 +34,10 @@         , testGroup "collapseFilePath" testCollapse         , testGroup "toLegacyTable" testLegacyTable         , testGroup "table of contents" testTOC+        , testGroup "stringifyInlines"+          [ testProperty "stringifyInlines matches stringify"+              stringifyInlinesMatchesStringify+          ]         , testGroup "makeSections"           [ testProperty "makeSections is idempotent" makeSectionsIsIdempotent           , testCase "makeSections is idempotent for test case" $@@ -44,6 +48,10 @@                       (makeSections False Nothing d' == d')           ]         ]++stringifyInlinesMatchesStringify :: [Inline] -> Bool+stringifyInlinesMatchesStringify ils =+  stringifyInlines ils == stringify ils  makeSectionsIsIdempotent :: [Block] -> Bool makeSectionsIsIdempotent d =
test/Tests/Writers/Docx.hs view
@@ -64,6 +64,22 @@             def             "docx/links.native"             "docx/golden/links.docx"+          , testCase "link titles become ScreenTips (#11869)" $ do+              doc <- documentXml def $ Pandoc mempty+                [ Para [ Link ("", [], []) [Str "external"]+                           ("https://example.org/r.pdf", "Annual report")+                       , Space+                       , Link ("", [], []) [Str "internal"]+                           ("#methods", "Jump to Methods")+                       , Space+                       , Link ("", [], []) [Str "untitled"]+                           ("https://example.org", "")+                       ]+                , Header 1 ("methods", [], []) [Str "Methods"]+                ]+              map (findAttr (wmlName "tooltip"))+                  (findElements (wmlName "hyperlink") doc)+                @?= [Just "Annual report", Just "Jump to Methods", Nothing]           , docxTest             "inline image"             def{ writerExtensions =@@ -107,6 +123,11 @@             def             "docx/lists.native"             "docx/golden/lists.docx"+          , docxTest+            "CSL bibliography (hanging indent and spacing)"+            def+            "docx/csl_bibliography.native"+            "docx/golden/csl_bibliography.docx"           , docxTest             "lists continuing after interruption"             def
test/ansi-test.ansi view
@@ -34,6 +34,13 @@ │ │ │ Nested block quote +Numbered list item beginning with a block quote (regression test for+issue #11804, where the leading bar was dropped on the block quote’s+first line because it shared a line with the list marker):++1. +   │ List item block quote+ Multiline table with caption:  Centered    Left       Right        Default aligned
test/ansi-test.txt view
@@ -38,6 +38,12 @@ > > > Nested block quote +Numbered list item beginning with a block quote (regression test for+issue #11804, where the leading bar was dropped on the block quote's+first line because it shared a line with the list marker):++1.  > List item block quote+ Multiline table with caption:  :  Here's the caption.
test/asciidoc-reader.native view
@@ -256,3855 +256,3837 @@       ]   , Para       [ Image-          ( "" , [ "red icon" ] , [] )-          []-          ( "./images/icons/heart.png" , "" )-      ]-  , Para [ Str "anchor:tiger" ]-  , Para [ Strong [ Str "*bold*" ] ]-  , Para-      [ Link-          ( "" , [] , [] )-          [ Str "Get" , Space , Str "Report" ]-          ( "downloads/report.pdf" , "" )-      ]-  , Para-      [ Link-          ( "" , [] , [] )-          [ Str "tools.html#editors" ]-          ( "tools.html#editors" , "" )-      ]-  , Para-      [ Link-          ( "" , [] , [] )-          [ Str "Your" , Space , Str "files" ]-          ( "file:///home/username" , "" )-      ]-  , Para [ Str "Tricky" , Space , Str "cases:" ]-  , Para-      [ Link-          ( "" , [] , [] )-          [ Str "Get" , Space , Str "Report" ]-          ( "My Documents/report.pdf" , "" )-      ]-  , Para-      [ Link-          ( "" , [] , [] )-          [ Str "Get" , Space , Str "Report" ]-          ( "My&#32;Documents/report.pdf" , "" )-      ]-  , Para-      [ Link-          ( "" , [] , [] )-          [ Str "Get" , Space , Str "Report" ]-          ( "My%20Documents/report.pdf" , "" )-      ]-  , Para-      [ Link-          ( "" , [] , [] )-          [ Str "https://example.org/now_this__link_works.html" ]-          ( "https://example.org/now_this__link_works.html" , "" )-      ]-  , Para-      [ Link-          ( "" , [] , [] )-          [ Str "Subscribe" ]-          ( "join@discuss.example.org" , "" )-      ]-  , Para-      [ Link-          ( "" , [ "mail" ] , [] )-          [ Str "Click,"-          , Space-          , Str "subscribe,"-          , Space-          , Str "and"-          , Space-          , Str "participate!"-          ]-          ( "join@discuss.example.org" , "" )-      ]-  , Para-      [ Link-          ( "" , [ "cross-reference" ] , [] )-          [ Str "use"-          , Space-          , Str "attributes"-          , Space-          , Str "within"-          , Space-          , Str "the"-          , Space-          , Str "link"-          , Space-          , Str "macro"-          ]-          ( "#link-macro-attributes" , "" )-      ]-  , Figure-      ( "" , [] , [] )-      (Caption Nothing [])-      [ Plain-          [ Image-              ( "" , [] , [] ) [ Str "Sunset" ] ( "sunset.jpg" , "" )-          ]-      ]-  , Figure-      ( "" , [] , [] )-      (Caption Nothing [])-      [ Plain [ Image ( "" , [] , [] ) [] ( "name.png" , "" ) ] ]-  , Figure-      ( "" , [] , [] )-      (Caption Nothing [])-      [ Plain-          [ Image-              ( ""-              , []-              , [ ( "width" , "300px" ) , ( "height" , "400px" ) ]-              )-              [ Str "Sunset" ]-              ( "sunset.jpg" , "" )-          ]-      ]-  , Div-      ( ""-      , []-      , [ ( "wrapper" , "1" )-        , ( "alt" , "Sunset" )-        , ( "height" , "400" )-        , ( "width" , "300" )-        ]-      )-      [ Figure-          ( "" , [] , [] )-          (Caption Nothing [])-          [ Plain [ Image ( "" , [] , [] ) [] ( "sunset.jpg" , "" ) ]-          ]-      ]-  , Para [ Math DisplayMath "e=mc^2\n" ]-  , Para [ Math DisplayMath "sin n / 3\n" ]-  , Para [ Math DisplayMath "e^i\n" ]-  , Header-      2-      ( "_attribute_substitutions" , [] , [] )-      [ Str "Attribute" , Space , Str "substitutions" ]-  , Para [ Str "Foo" , Space , Str "bar" , Space , Str "baz" ]-  , Para [ Str "{nonexistent}" ]-  , Para-      [ Str "Built"-      , Space-      , Str "in:"-      , Space-      , Str "xyz"-      , Space-      , Str "a\160b\8203c'd\8216"-      ]-  , Header-      2-      ( "_bold_and_italic" , [] , [] )-      [ Str "Bold" , Space , Str "and" , Space , Str "italic" ]-  , Para-      [ Str "Constrained:"-      , Space-      , Strong-          [ Str "this"-          , Space-          , Str "is"-          , Space-          , Str "bold"-          , Space-          , Emph [ Str "and" , Space , Str "italic" ]-          ]-      , Str "."-      ]-  , Para-      [ Str "Unconstrained:"-      , Space-      , Str "wild"-      , Strong-          [ Str "content"-          , Emph [ Str "with" , Space , Str "italic" ]-          , Str "stuff"-          ]-      , Str "."-      ]-  , Header 2 ( "_monospace" , [] , [] ) [ Str "Monospace" ]-  , Para [ Code ( "" , [] , [] ) "simple" ]-  , Para-      [ Code ( "" , [] , [] ) "complex"-      , Space-      , Strong-          [ Code ( "" , [] , [] ) "with"-          , Space-          , Code ( "" , [] , [] ) "bold"-          ]-      , Space-      , Code ( "" , [] , [] ) "text"-      , Space-      , Code ( "" , [] , [] ) "and"-      , Space-      , Code ( "" , [] , [] ) "a"-      , Space-      , Link-          ( "" , [] , [] )-          [ Code ( "" , [] , [] ) "foo.html" ]-          ( "foo.html" , "" )-      ]-  , Para-      [ Str "unconstrained"-      , Code ( "" , [] , [] ) "wwow"-      , Str "okay"-      ]-  , Header-      2-      ( "_span_and_inline_attributes" , [] , [] )-      [ Str "Span"-      , Space-      , Str "and"-      , Space-      , Str "inline"-      , Space-      , Str "attributes"-      ]-  , Para-      [ Span-          ( "" , [ "red" ] , [] )-          [ Str "Bonjour" , Space , Strong [ Str "monsieur" ] ]-      ]-  , Para-      [ Str "Un"-      , Span ( "" , [ "red" ] , [] ) [ Str "constrained" ]-      , Str "content"-      ]-  , Para-      [ Str "With"-      , Space-      , Span-          ( "" , [ "mark" ] , [] )-          [ Str "no" , Space , Str "attribute" ]-      , Space-      , Str "it\8217s"-      , Space-      , Str "highlighted."-      ]-  , Header-      2-      ( "_sub_and_superscript" , [] , [] )-      [ Str "Sub"-      , Space-      , Str "and"-      , Space-      , Str "superscript"-      ]-  , Para [ Str "H" , Subscript [ Str "2" ] , Str "O" ]-  , Para-      [ Str "H"-      , Subscript [ Str "a" , Space , Str "b" ]-      , Str "O"-      ]-  , Para-      [ Str "Not"-      , Space-      , Str "subscript:"-      , Space-      , Str "H~a"-      , Space-      , Str "b~O."-      ]-  , Para [ Str "H^2&O" ]-  , Para-      [ Str "H"-      , Superscript [ Str "a" , Space , Str "b" ]-      , Str "O"-      ]-  , Para-      [ Str "Not"-      , Space-      , Str "subscript:"-      , Space-      , Str "H^a"-      , Space-      , Str "b^O."-      ]-  , Header-      2 ( "_passthrough" , [] , [] ) [ Str "Passthrough" ]-  , Para-      [ Str "Here"-      , Space-      , Str "the"-      , Space-      , Str "special"-      , Space-      , Str "characters"-      , Space-      , Str "just"-      , Space-      , Str "come"-      , Space-      , Str "through"-      , Space-      , Str "as"-      , Space-      , Str "literal:"-      ]-  , Para [ Str "<b>*test*</b>" ]-  , Para [ Str "xx<b>*test*</b>xx" ]-  , Para-      [ Str "But"-      , Space-      , Str "here"-      , Space-      , Str "they"-      , Space-      , Str "are"-      , Space-      , Str "passed"-      , Space-      , Str "through:"-      ]-  , Para [ Str "xx" , Strong [ Str "*test*" ] , Str "xx" ]-  , Header 2 ( "_quoted" , [] , [] ) [ Str "Quoted" ]-  , Para-      [ Quoted DoubleQuote [ Str "double" , Space , Str "quoted" ]-      ]-  , Para-      [ Quoted SingleQuote [ Str "single" , Space , Str "quoted" ]-      ]-  , Header 2 ( "_footnotes" , [] , [] ) [ Str "Footnotes" ]-  , Para-      [ Str "Double"-      , Note-          [ Para-              [ Str "The"-              , Space-              , Str "double"-              , Space-              , Str "hail-and-rainbow"-              , Space-              , Str "level"-              , Space-              , Str "makes"-              , Space-              , Str "my"-              , Space-              , Str "toes"-              , Space-              , Str "tingle."-              ]-          ]-      ]-  , Para-      [ Str "A"-      , Space-      , Str "bold"-      , Space-      , Str "statement!"-      , Note-          [ Para-              [ Str "Opinions"-              , Space-              , Str "are"-              , Space-              , Str "my"-              , Space-              , Str "own."-              ]-          ]-      , SoftBreak-      , Str "Another"-      , Space-      , Str "outrageous"-      , Space-      , Str "statement."-      , Note-          [ Para-              [ Str "Opinions"-              , Space-              , Str "are"-              , Space-              , Str "my"-              , Space-              , Str "own."-              ]-          ]-      ]-  , Header-      1-      ( "_block_markup" , [] , [] )-      [ Str "Block" , Space , Str "markup" ]-  , Header 2 ( "_sections" , [] , [] ) [ Str "Sections" ]-  , Header-      3-      ( "_another_level" , [] , [] )-      [ Str "Another" , Space , Str "level" ]-  , Header-      4 ( "_level_5" , [] , [] ) [ Str "Level" , Space , Str "5" ]-  , Header-      3-      ( "_markdown_style" , [] , [] )-      [ Str "Markdown" , Space , Str "style" ]-  , Header-      4-      ( "_level_5_2" , [] , [] )-      [ Str "Level" , Space , Str "5" ]-  , Header-      2-      ( "_discrete_heading" , [] , [] )-      [ Str "Discrete" , Space , Str "heading" ]-  , Header-      3-      ( "" , [] , [] )-      [ Str "A"-      , Space-      , Str "discrete"-      , Space-      , Str "heading,"-      , Space-      , Str "not"-      , Space-      , Str "a"-      , Space-      , Str "section"-      ]-  , Header 2 ( "_paragraph" , [] , [] ) [ Str "Paragraph" ]-  , Para-      [ Str "This"-      , Space-      , Str "is"-      , Space-      , Str "a"-      , Space-      , Str "paragraph"-      , SoftBreak-      , Str "whose"-      , Space-      , Str "source"-      , Space-      , Str "fits"-      , Space-      , Str "on"-      , Space-      , Str "two"-      , Space-      , Str "lines."-      ]-  , Para-      [ Str "{.This"-      , Space-      , Str "is"-      , Space-      , Str "my"-      , Space-      , Str "title}"-      , SoftBreak-      , Str "A"-      , Space-      , Str "paragraph"-      , Space-      , Str "with"-      , Space-      , Str "a"-      , Space-      , Str "title."-      ]-  , Header-      2-      ( "_example_block" , [] , [] )-      [ Str "Example" , Space , Str "block" ]-  , Div-      ( "" , [] , [] )-      [ Div-          ( "" , [ "title" ] , [] )-          [ Para [ Str "Optional" , Space , Str "title" ] ]-      , Para-          [ Str "This"-          , Space-          , Str "is"-          , Space-          , Str "an"-          , Space-          , Str "example"-          , Space-          , Str "of"-          , Space-          , Str "an"-          , Space-          , Str "example"-          , Space-          , Str "block."-          ]-      ]-  , Div-      ( "" , [ "example" ] , [] )-      [ Div-          ( "" , [ "title" ] , [] )-          [ Para [ Str "Optional" , Space , Str "title" ] ]-      , Para-          [ Str "Paragraph" , Space , Strong [ Str "one" ] , Str "." ]-      , Para-          [ Str "Paragraph" , Space , Strong [ Str "two" ] , Str "." ]-      ]-  , Header 2 ( "_admonition" , [] , [] ) [ Str "Admonition" ]-  , Para [ Str "Simple" , Space , Str "form:" ]-  , Div-      ( "" , [ "warning" ] , [] )-      [ Div ( "" , [ "title" ] , [] ) [ Para [ Str "Warning" ] ]-      , Para-          [ Str "This"-          , Space-          , Str "is"-          , Space-          , Str "very"-          , Space-          , Str "dangerous."-          , SoftBreak-          , Str "Don\8217t"-          , Space-          , Str "do"-          , Space-          , Str "it"-          , Space-          , Str "unless"-          , Space-          , Str "you"-          , Space-          , Str "understand"-          , Space-          , Str "the"-          , Space-          , Str "risks."-          ]-      ]-  , Div-      ( "" , [ "important" ] , [] )-      [ Div-          ( "" , [ "title" ] , [] )-          [ Para-              [ Str "Title"-              , Space-              , Str "of"-              , Space-              , Str "the"-              , Space-              , Str "admonition"-              ]-          ]-      , Para [ Str "Remember:" ]-      , OrderedList-          ( 1 , DefaultStyle , DefaultDelim )-          [ [ Para-                [ Str "Don\8217t"-                , Space-                , Str "do"-                , Space-                , Str "this."-                ]-            ]-          , [ Para-                [ Str "And"-                , Space-                , Str "don\8217t"-                , Space-                , Str "do"-                , Space-                , Str "that."-                ]-            ]-          ]-      ]-  , Header 2 ( "_sidebar" , [] , [] ) [ Str "Sidebar" ]-  , Para-      [ Str "A" , Space , Str "simple" , Space , Str "sidebar." ]-  , Div-      ( "" , [ "sidebar" ] , [] )-      [ Div-          ( "" , [ "title" ] , [] )-          [ Para-              [ Str "Optional"-              , Space-              , Str "Title"-              , Space-              , Strong-                  [ Str "with"-                  , Space-                  , Str "strong"-                  , Space-                  , Str "emphasis"-                  ]-              ]-          ]-      , Para-          [ Str "Here"-          , Space-          , Str "is"-          , Space-          , Str "a"-          , Space-          , Str "sidebar."-          ]-      , Div-          ( "" , [ "tip" ] , [] )-          [ Div ( "" , [ "title" ] , [] ) [ Para [ Str "Tip" ] ]-          , Para-              [ Str "It"-              , Space-              , Str "can"-              , Space-              , Str "contain"-              , Space-              , Str "any"-              , Space-              , Str "type"-              , Space-              , Str "of"-              , Space-              , Str "content."-              ]-          ]-      ]-  , Header-      2-      ( "_literal_block" , [] , [] )-      [ Str "Literal" , Space , Str "block" ]-  , Para-      [ Str "Short"-      , Space-      , Str "indented"-      , Space-      , Str "code:"-      ]-  , CodeBlock-      ( "" , [] , [] )-      "$ ls -a\n$ cat /foo/bar/baz \\\n     /bi/bim/bop\n"-  , CodeBlock-      ( "" , [] , [] ) "This is\n  a literal block too.\n"-  , CodeBlock-      ( "" , [] , [] )-      "  Fenced\n    $+ *a* literal\n\n****\nnot a sidebar\n****\n"-  , Header 2 ( "_listing" , [] , [] ) [ Str "Listing" ]-  , CodeBlock-      ( "" , [ "ruby" ] , [] )-      "require 'sinatra'\n\nget '/hi' do\n  \"Hello World!\"\nend"-  , Para [ Str "Implied:" ]-  , CodeBlock-      ( "" , [ "ruby" ] , [] )-      "require 'sinatra'\n\nget '/hi' do\n  \"Hello World!\"\nend"-  , CodeBlock-      ( "" , [ "ruby" ] , [] )-      "# A function\ndef foo\n  return 42\nend"-  , CodeBlock-      ( "hello" , [ "haskell" ] , [] )-      "putStrLn $ unwords [\"Hello\", \"world\"]"-  , Para [ Str "Line" , Space , Str "numbering:" ]-  , CodeBlock-      ( "" , [] , [ ( "options" , "linenums" ) ] )-      "puts 1\nputs 2\nputs 3"-  , CodeBlock-      ( "" , [] , [] ) "This doesn't have a language.\n  +=\"hi\""-  , Para-      [ Str "And"-      , Space-      , Str "with"-      , Space-      , Str "a"-      , Space-      , Str "callout"-      , Space-      , Str "list:"-      ]-  , CodeBlock-      ( "" , [ "ruby" ] , [] )-      "require 'sinatra' \9312\n\nget '/hi' do \9313 \9314\n  \"Hello World!\"\nend"-  , Div-      ( "" , [ "callout-list" ] , [] )-      [ OrderedList-          ( 1 , DefaultStyle , DefaultDelim )-          [ [ Para [ Str "Library" , Space , Str "import" ] ]-          , [ Para [ Str "URL" , Space , Str "mapping" ] ]-          , [ Para [ Str "Response" , Space , Str "block" ] ]-          ]-      ]-  , Para [ Str "Markdown-style" , Space , Str "fenced:" ]-  , CodeBlock-      ( "" , [ "ruby" ] , [] ) "def foo\n  return 5\nend"-  , Header 2 ( "_verse" , [] , [] ) [ Str "Verse" ]-  , BlockQuote-      [ Para-          [ Str "The"-          , Space-          , Str "fog"-          , Space-          , Str "comes"-          , LineBreak-          , Str "on"-          , Space-          , Str "little"-          , Space-          , Str "cat"-          , Space-          , Str "feet."-          ]-      , Para-          [ Str "\8212"-          , Space-          , Str "Carl"-          , Space-          , Str "Sandburg,"-          , Space-          , Str "two"-          , Space-          , Str "lines"-          , Space-          , Str "from"-          , Space-          , Str "the"-          , Space-          , Str "poem"-          , Space-          , Str "Fog"-          ]-      ]-  , BlockQuote-      [ Para-          [ Str "The"-          , Space-          , Str "fog"-          , Space-          , Str "comes"-          , LineBreak-          , Str "on"-          , Space-          , Str "little"-          , Space-          , Str "cat"-          , Space-          , Str "feet."-          , LineBreak-          , Str "It"-          , Space-          , Str "sits"-          , Space-          , Str "looking"-          , LineBreak-          , Str "over"-          , Space-          , Str "harbor"-          , Space-          , Str "and"-          , Space-          , Str "city"-          , LineBreak-          , Str "on"-          , Space-          , Str "silent"-          , Space-          , Str "haunches"-          , LineBreak-          , Str "and"-          , Space-          , Str "then"-          , Space-          , Str "moves"-          , Space-          , Str "on."-          ]-      , Para-          [ Str "\8212"-          , Space-          , Str "Carl"-          , Space-          , Str "Sandburg,"-          , Space-          , Str "Fog"-          ]-      ]-  , Header-      2 ( "_collapsible" , [] , [] ) [ Str "Collapsible" ]-  , Para-      [ Str "Click"-      , Space-      , Str "here"-      , Space-      , Str "for"-      , Space-      , Str "more."-      ]-  , Div-      ( ""-      , [ "example" ]-      , [ ( "options" , "collapsible,open" ) ]-      )-      [ Para-          [ Str "This"-          , Space-          , Str "is"-          , Space-          , Str "collapsible."-          ]-      , Para-          [ Str "It"-          , Space-          , Str "can"-          , Space-          , Str "be"-          , Space-          , Str "hidden."-          ]-      ]-  , Div-      ( "" , [] , [ ( "options" , "collapsible" ) ] )-      [ Div-          ( "" , [ "title" ] , [] )-          [ Para [ Str "Click" , Space , Str "me!" ] ]-      , Para-          [ Str "This"-          , Space-          , Str "paragraph"-          , Space-          , Str "is"-          , SoftBreak-          , Str "also"-          , Space-          , Str "collapsible."-          ]-      ]-  , Header 2 ( "_quote" , [] , [] ) [ Str "Quote" ]-  , BlockQuote-      [ Para-          [ Str "Everybody"-          , Space-          , Str "remember"-          , Space-          , Str "where"-          , Space-          , Str "we"-          , Space-          , Str "parked."-          ]-      , Para-          [ Str "\8212"-          , Space-          , Str "Captain"-          , Space-          , Str "James"-          , Space-          , Str "T."-          , Space-          , Str "Kirk,"-          , Space-          , Str "Star"-          , Space-          , Str "Trek"-          , Space-          , Str "IV:"-          , Space-          , Str "The"-          , Space-          , Str "Voyage"-          , Space-          , Str "Home"-          ]-      ]-  , BlockQuote-      [ Para-          [ Str "Dennis:"-          , Space-          , Str "Come"-          , Space-          , Str "and"-          , Space-          , Str "see"-          , Space-          , Str "the"-          , Space-          , Str "violence"-          , Space-          , Str "inherent"-          , Space-          , Str "in"-          , Space-          , Str "the"-          , Space-          , Str "system."-          , Space-          , Str "Help!"-          , Space-          , Str "Help!"-          , Space-          , Str "I\8217m"-          , Space-          , Str "being"-          , SoftBreak-          , Str "repressed."-          ]-      , Para-          [ Str "King"-          , Space-          , Str "Arthur:"-          , Space-          , Str "Bloody"-          , Space-          , Str "peasant!"-          ]-      , Para-          [ Str "Dennis:"-          , Space-          , Str "Oh,"-          , Space-          , Str "what"-          , Space-          , Str "a"-          , Space-          , Str "giveaway!"-          , Space-          , Str "Did"-          , Space-          , Str "you"-          , Space-          , Str "hear"-          , Space-          , Str "that?"-          , Space-          , Str "Did"-          , Space-          , Str "you"-          , Space-          , Str "hear"-          , Space-          , Str "that,"-          , Space-          , Str "eh?"-          , Space-          , Str "That\8217s"-          , Space-          , Str "what"-          , Space-          , Str "I\8217m"-          , SoftBreak-          , Str "on"-          , Space-          , Str "about!"-          , Space-          , Str "Did"-          , Space-          , Str "you"-          , Space-          , Str "see"-          , Space-          , Str "him"-          , Space-          , Str "repressing"-          , Space-          , Str "me?"-          , Space-          , Str "You"-          , Space-          , Str "saw"-          , Space-          , Str "him,"-          , Space-          , Str "Didn\8217t"-          , Space-          , Str "you?"-          ]-      , Para-          [ Str "\8212"-          , Space-          , Str "Monty"-          , Space-          , Str "Python"-          , Space-          , Str "and"-          , Space-          , Str "the"-          , Space-          , Str "Holy"-          , Space-          , Str "Grail"-          ]-      ]-  , Div-      ( "roads" , [ "movie" ] , [ ( "wrapper" , "1" ) ] )-      [ BlockQuote-          [ Para-              [ Str "Roads?"-              , Space-              , Str "Where"-              , Space-              , Str "we\8217re"-              , Space-              , Str "going,"-              , Space-              , Str "we"-              , Space-              , Str "don\8217t"-              , Space-              , Str "need"-              , Space-              , Str "roads."-              ]-          , Para-              [ Str "\8212"-              , Space-              , Str "Dr."-              , Space-              , Str "Emmett"-              , Space-              , Str "Brown"-              ]-          ]-      ]-  , Header 2 ( "_pass" , [] , [] ) [ Str "Pass" ]-  , Para [ Str "pass" , Space , Emph [ Str "through" ] ]-  , Header-      2-      ( "_open_block" , [] , [] )-      [ Str "Open" , Space , Str "block" ]-  , Div-      ( "" , [] , [ ( "key" , "a value" ) ] )-      [ Div-          ( "" , [ "title" ] , [] )-          [ Para [ Str "A" , Space , Str "title." ] ]-      , Para-          [ Str "Any"-          , Space-          , Str "content"-          , Space-          , Str "can"-          , Space-          , Str "go"-          , Space-          , Str "here:"-          ]-      , OrderedList-          ( 1 , DefaultStyle , DefaultDelim )-          [ [ Para [ Str "one" ] ] , [ Para [ Str "two" ] ] ]-      ]-  , Header 2 ( "_anchor" , [] , [] ) [ Str "Anchor" ]-  , Div-      ( "goals" , [] , [ ( "wrapper" , "1" ) ] )-      [ BulletList-          [ [ Para [ Str "one" ] ] , [ Para [ Str "two" ] ] ]-      ]-  , Header 2 ( "_breaks" , [] , [] ) [ Str "Breaks" ]-  , Para-      [ Str "Asciidoc"-      , Space-      , Str "thematic"-      , Space-      , Str "break:"-      ]-  , HorizontalRule-  , Para [ Str "Markdown" , Space , Str "style:" ]-  , HorizontalRule-  , HorizontalRule-  , HorizontalRule-  , HorizontalRule-  , Para [ Str "Page" , Space , Str "breaks:" ]-  , Div-      ( "" , [ "page-break" ] , [ ( "wrapper" , "1" ) ] )-      [ HorizontalRule ]-  , Div-      ( ""-      , [ "page-break" ]-      , [ ( "options" , "always" ) , ( "wrapper" , "1" ) ]-      )-      [ HorizontalRule ]-  , Header 2 ( "_list" , [] , [] ) [ Str "List" ]-  , BulletList-      [ [ Para-            [ Str "Edgar" , Space , Str "Allan" , Space , Str "Poe" ]-        ]-      , [ Para-            [ Str "Sheri" , Space , Str "S." , Space , Str "Tepper" ]-        ]-      , [ Para [ Str "Bill" , Space , Str "Bryson" ] ]-      ]-  , Div-      ( "" , [] , [] )-      [ Div-          ( "" , [ "title" ] , [] )-          [ Para-              [ Str "Kizmet\8217s"-              , Space-              , Str "Favorite"-              , Space-              , Str "Authors"-              ]-          ]-      , BulletList-          [ [ Para-                [ Str "Edgar"-                , Space-                , Str "Allan"-                , Space-                , Str "Poe"-                ]-            ]-          , [ Para-                [ Str "Sheri"-                , Space-                , Str "S."-                , Space-                , Str "Tepper"-                ]-            ]-          , [ Para [ Str "Bill" , Space , Str "Bryson" ] ]-          ]-      ]-  , BulletList-      [ [ Para-            [ Str "Edgar" , Space , Str "Allan" , Space , Str "Poe" ]-        ]-      , [ Para-            [ Str "Sheri" , Space , Str "S." , Space , Str "Tepper" ]-        ]-      , [ Para [ Str "Bill" , Space , Str "Bryson" ] ]-      ]-  , OrderedList-      ( 1 , DefaultStyle , DefaultDelim )-      [ [ Para [ Str "Nested" , Space , Str "list" ]-        , BulletList-            [ [ Para-                  [ Str "West"-                  , Space-                  , Str "wood"-                  , Space-                  , Str "maze"-                  ]-              , BulletList-                  [ [ Para [ Str "Maze" , Space , Str "heart" ]-                    , BulletList-                        [ [ Para-                              [ Str "Reflection" , Space , Str "pool" ]-                          ]-                        ]-                    ]-                  , [ Para [ Str "Secret" , Space , Str "exit" ] ]-                  ]-              ]-            , [ Para-                  [ Str "Level"-                  , Space-                  , Str "1"-                  , Space-                  , Str "list"-                  , Space-                  , Str "item"-                  ]-              , BulletList-                  [ [ Para-                        [ Str "Level"-                        , Space-                        , Str "2"-                        , Space-                        , Str "list"-                        , Space-                        , Str "item"-                        ]-                    , BulletList-                        [ [ Para-                              [ Str "Level"-                              , Space-                              , Str "3"-                              , Space-                              , Str "list"-                              , Space-                              , Str "item"-                              ]-                          , BulletList-                              [ [ Para-                                    [ Str "Level"-                                    , Space-                                    , Str "4"-                                    , Space-                                    , Str "list"-                                    , Space-                                    , Str "item"-                                    ]-                                , BulletList-                                    [ [ Para-                                          [ Str "Level"-                                          , Space-                                          , Str "5"-                                          , Space-                                          , Str "list"-                                          , Space-                                          , Str "item"-                                          ]-                                      , BulletList-                                          [ [ Para [ Str "etc." ] ] ]-                                      ]-                                    ]-                                ]-                              ]-                          ]-                        ]-                    ]-                  ]-              ]-            , [ Para-                  [ Str "Level"-                  , Space-                  , Str "1"-                  , Space-                  , Str "list"-                  , Space-                  , Str "item"-                  ]-              ]-            ]-        ]-      ]-  , BulletList-      [ [ Para [ Str "one" ] ]-      , [ Para [ Str "two" ] ]-      , [ Para [ Str "three" ] ]-      ]-  , OrderedList-      ( 1 , DefaultStyle , DefaultDelim )-      [ [ Para [ Str "Protons" ] ]-      , [ Para [ Str "Electrons" ] ]-      , [ Para [ Str "Neutrons" ] ]-      ]-  , OrderedList-      ( 1 , DefaultStyle , DefaultDelim )-      [ [ Para [ Str "Protons" ] ]-      , [ Para [ Str "Electrons" ] ]-      , [ Para [ Str "Neutrons" ] ]-      ]-  , Para-      [ Str "Start" , Space , Str "with" , Space , Str "4:" ]-  , OrderedList-      ( 4 , DefaultStyle , DefaultDelim )-      [ [ Para [ Str "Step" , Space , Str "four" ] ]-      , [ Para [ Str "Step" , Space , Str "five" ] ]-      , [ Para [ Str "Step" , Space , Str "six" ] ]-      ]-  , Para [ Str "or" ]-  , OrderedList-      ( 4 , DefaultStyle , DefaultDelim )-      [ [ Para [ Str "Step" , Space , Str "four" ] ]-      , [ Para [ Str "Step" , Space , Str "five" ] ]-      , [ Para [ Str "Step" , Space , Str "six" ] ]-      ]-  , Para [ Str "Reversed:" ]-  , Div-      ( "" , [] , [ ( "options" , "reversed" ) ] )-      [ Div-          ( "" , [ "title" ] , [] )-          [ Para-              [ Str "Parts"-              , Space-              , Str "of"-              , Space-              , Str "an"-              , Space-              , Str "atom"-              ]-          ]-      , OrderedList-          ( 1 , DefaultStyle , DefaultDelim )-          [ [ Para [ Str "Protons" ] ]-          , [ Para [ Str "Electrons" ] ]-          , [ Para [ Str "Neutrons" ] ]-          ]-      ]-  , Para [ Str "Nested" ]-  , OrderedList-      ( 1 , DefaultStyle , DefaultDelim )-      [ [ Para [ Str "Step" , Space , Str "1" ] ]-      , [ Para [ Str "Step" , Space , Str "2" ]-        , OrderedList-            ( 1 , DefaultStyle , DefaultDelim )-            [ [ Para [ Str "Step" , Space , Str "2a" ] ]-            , [ Para [ Str "Step" , Space , Str "2b" ] ]-            ]-        ]-      , [ Para [ Str "Step" , Space , Str "3" ] ]-      ]-  , Para [ Str "Mixed" , Space , Str "nested" ]-  , OrderedList-      ( 1 , DefaultStyle , DefaultDelim )-      [ [ Para [ Str "Linux" ]-        , BulletList-            [ [ Para [ Str "Fedora" ] ]-            , [ Para [ Str "Ubuntu" ] ]-            , [ Para [ Str "Slackware" ] ]-            ]-        ]-      , [ Para [ Str "BSD" ]-        , BulletList-            [ [ Para [ Str "FreeBSD" ] ] , [ Para [ Str "NetBSD" ] ] ]-        ]-      ]-  , Para [ Str "With" , Space , Str "spacing" ]-  , OrderedList-      ( 1 , DefaultStyle , DefaultDelim )-      [ [ Para [ Str "Linux" ]-        , BulletList-            [ [ Para [ Str "Fedora" ] ]-            , [ Para [ Str "Ubuntu" ] ]-            , [ Para [ Str "Slackware" ] ]-            ]-        ]-      , [ Para [ Str "BSD" ]-        , BulletList-            [ [ Para [ Str "FreeBSD" ] ] , [ Para [ Str "NetBSD" ] ] ]-        ]-      ]-  , Para-      [ Str "With" , Space , Str "number" , Space , Str "styles" ]-  , OrderedList-      ( 5 , LowerRoman , DefaultDelim )-      [ [ Para [ Str "Five" ] ]-      , [ Para [ Str "Six" ]-        , OrderedList-            ( 1 , LowerAlpha , DefaultDelim )-            [ [ Para [ Str "a" ] ]-            , [ Para [ Str "b" ] ]-            , [ Para [ Str "c" ] ]-            ]-        ]-      , [ Para [ Str "Seven" ] ]-      ]-  , Para [ Str "Checklist" ]-  , BulletList-      [ [ Para [ Str "\9746" , Space , Str "checked" ] ]-      , [ Para-            [ Str "\9746" , Space , Str "also" , Space , Str "checked" ]-        ]-      , [ Para-            [ Str "\9744" , Space , Str "not" , Space , Str "checked" ]-        ]-      , [ Para-            [ Str "normal" , Space , Str "list" , Space , Str "item" ]-        ]-      ]-  , Para-      [ Str "Separate"-      , Space-      , Str "lists"-      , Space-      , Str "with"-      , Space-      , Str "block"-      , Space-      , Str "attribute"-      ]-  , BulletList-      [ [ Para [ Str "Apples" ] ]-      , [ Para [ Str "Oranges" ]-        , OrderedList-            ( 1 , DefaultStyle , DefaultDelim )-            [ [ Para [ Str "Wash" ] ] , [ Para [ Str "Slice" ] ] ]-        ]-      ]-  , Para [ Str "Multiline" , Space , Str "items" ]-  , BulletList-      [ [ Para-            [ Str "Blah"-            , Space-            , Str "blah."-            , SoftBreak-            , Str "Blah"-            , Space-            , Str "blah."-            ]-        ]-      , [ Para-            [ Str "The"-            , Space-            , Str "document"-            , Space-            , Str "header"-            , Space-            , Str "in"-            , Space-            , Str "AsciiDoc"-            , Space-            , Str "is"-            , Space-            , Str "optional."-            , SoftBreak-            , Str "If"-            , Space-            , Str "present,"-            , Space-            , Str "it"-            , Space-            , Str "must"-            , Space-            , Str "start"-            , Space-            , Str "with"-            , Space-            , Str "a"-            , Space-            , Str "document"-            , Space-            , Str "title."-            ]-        ]-      ]-  , BulletList-      [ [ Para-            [ Str "Optional"-            , Space-            , Str "author"-            , Space-            , Str "and"-            , Space-            , Str "revision"-            , Space-            , Str "information"-            , Space-            , Str "lines"-            , SoftBreak-            , Str "immediately"-            , Space-            , Str "follow"-            , Space-            , Str "the"-            , Space-            , Str "document"-            , Space-            , Str "title."-            ]-        ]-      ]-  , BulletList-      [ [ Para-            [ Str "The"-            , Space-            , Str "document"-            , Space-            , Str "header"-            , Space-            , Str "must"-            , Space-            , Str "be"-            , Space-            , Str "separated"-            , Space-            , Str "from"-            , SoftBreak-            , Str "the"-            , Space-            , Str "remainder"-            , Space-            , Str "of"-            , Space-            , Str "the"-            , Space-            , Str "document"-            , Space-            , Str "by"-            , Space-            , Str "one"-            , Space-            , Str "or"-            , Space-            , Str "more"-            , SoftBreak-            , Str "empty"-            , Space-            , Str "lines"-            , Space-            , Str "and"-            , Space-            , Str "it"-            , Space-            , Str "cannot"-            , Space-            , Str "contain"-            , Space-            , Str "empty"-            , Space-            , Str "lines."-            ]-        ]-      ]-  , Para [ Str "Complex" , Space , Str "item" ]-  , BulletList-      [ [ Para-            [ Str "The"-            , Space-            , Str "header"-            , Space-            , Str "in"-            , Space-            , Str "AsciiDoc"-            , Space-            , Str "must"-            , Space-            , Str "start"-            , Space-            , Str "with"-            , Space-            , Str "a"-            , Space-            , Str "document"-            , Space-            , Str "title."-            ]-        , CodeBlock ( "" , [] , [] ) "= Document Title"-        , Para-            [ Str "Keep"-            , Space-            , Str "in"-            , Space-            , Str "mind"-            , Space-            , Str "that"-            , Space-            , Str "the"-            , Space-            , Str "header"-            , Space-            , Str "is"-            , Space-            , Str "optional."-            ]-        ]-      , [ Para-            [ Str "Optional"-            , Space-            , Str "author"-            , Space-            , Str "and"-            , Space-            , Str "revision"-            , Space-            , Str "information"-            , Space-            , Str "lines"-            , Space-            , Str "immediately"-            , Space-            , Str "follow"-            , Space-            , Str "the"-            , Space-            , Str "document"-            , SoftBreak-            , Str "title."-            ]-        , CodeBlock-            ( "" , [] , [] )-            "= Document Title\nDoc Writer <doc.writer@asciidoc.org>\nv1.0, 2022-01-01"-        ]-      , [ Para [ Str "Second" , Space , Str "item" ] ]-      ]-  , Para-      [ Str "Empty"-      , Space-      , Str "principle"-      , Space-      , Str "element:"-      ]-  , OrderedList-      ( 1 , DefaultStyle , DefaultDelim )-      [ [ Para [] , CodeBlock ( "" , [] , [] ) "test" ] ]-  , Header 2 ( "_table" , [] , [] ) [ Str "Table" ]-  , Header-      3-      ( "_simple_with_column_specs" , [] , [] )-      [ Str "Simple"-      , Space-      , Str "with"-      , Space-      , Str "column"-      , Space-      , Str "specs"-      ]-  , Table-      ( "" , [] , [] )-      (Caption Nothing [])-      [ ( AlignDefault , ColWidth 0.375 )-      , ( AlignDefault , ColWidth 0.25 )-      , ( AlignDefault , ColWidth 0.375 )-      ]-      (TableHead-         ( "" , [] , [] )-         [ Row-             ( "" , [] , [] )-             [ Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para-                     [ Str "This"-                     , Space-                     , Str "content"-                     , Space-                     , Str "is"-                     , Space-                     , Str "placed"-                     , Space-                     , Str "in"-                     , Space-                     , Str "the"-                     , Space-                     , Str "first"-                     , Space-                     , Str "cell"-                     , Space-                     , Str "of"-                     , Space-                     , Str "column"-                     , Space-                     , Str "1"-                     ]-                 ]-             , Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para-                     [ Str "This"-                     , Space-                     , Str "line"-                     , Space-                     , Str "starts"-                     , Space-                     , Str "with"-                     , Space-                     , Str "a"-                     , Space-                     , Str "vertical"-                     , Space-                     , Str "bar"-                     , Space-                     , Str "so"-                     , Space-                     , Str "this"-                     , Space-                     , Str "content"-                     , Space-                     , Str "is"-                     , Space-                     , Str "placed"-                     , Space-                     , Str "in"-                     , Space-                     , Str "a"-                     , Space-                     , Str "new"-                     , Space-                     , Str "cell"-                     , Space-                     , Str "in"-                     , SoftBreak-                     , Str "column"-                     , Space-                     , Str "2"-                     ]-                 ]-             , Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para-                     [ Str "When"-                     , Space-                     , Str "the"-                     , Space-                     , Str "processor"-                     , Space-                     , Str "encounters"-                     , Space-                     , Str "a"-                     , Space-                     , Str "whitespace"-                     , Space-                     , Str "followed"-                     , Space-                     , Str "by"-                     , Space-                     , Str "a"-                     , Space-                     , Str "vertical"-                     , Space-                     , Str "bar"-                     , Space-                     , Str "it"-                     , SoftBreak-                     , Str "ends"-                     , Space-                     , Str "the"-                     , Space-                     , Str "previous"-                     , Space-                     , Str "cell"-                     , Space-                     , Str "and"-                     , Space-                     , Str "starts"-                     , Space-                     , Str "a"-                     , Space-                     , Str "new"-                     , Space-                     , Str "cell"-                     ]-                 ]-             ]-         ])-      [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [] ]-      (TableFoot ( "" , [] , [] ) [])-  , Header-      3-      ( "_repeated_column_in_specs" , [] , [] )-      [ Str "Repeated"-      , Space-      , Str "column"-      , Space-      , Str "in"-      , Space-      , Str "specs"-      ]-  , Table-      ( "" , [] , [] )-      (Caption Nothing [])-      [ ( AlignDefault , ColWidthDefault )-      , ( AlignDefault , ColWidthDefault )-      ]-      (TableHead-         ( "" , [] , [] )-         [ Row-             ( "" , [] , [] )-             [ Cell-                 ( "" , [] , [] )-                 AlignRight-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para-                     [ Strong-                         [ Str "This"-                         , Space-                         , Str "cell\8217s"-                         , Space-                         , Str "specifier"-                         , Space-                         , Str "indicates"-                         , Space-                         , Str "that"-                         , Space-                         , Str "this"-                         , Space-                         , Str "cell\8217s"-                         , Space-                         , Str "content"-                         , Space-                         , Str "is"-                         , Space-                         , Str "right-aligned"-                         , Space-                         , Str "and"-                         , Space-                         , Str "bold."-                         ]-                     ]-                 ]-             , Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para-                     [ Str "The"-                     , Space-                     , Str "cell"-                     , Space-                     , Str "specifier"-                     , Space-                     , Str "on"-                     , Space-                     , Str "this"-                     , Space-                     , Str "cell"-                     , Space-                     , Str "hasn\8217t"-                     , Space-                     , Str "been"-                     , Space-                     , Str "set"-                     , Space-                     , Str "explicitly,"-                     , Space-                     , Str "so"-                     , Space-                     , Str "the"-                     , Space-                     , Str "default"-                     , SoftBreak-                     , Str "properties"-                     , Space-                     , Str "are"-                     , Space-                     , Str "applied."-                     ]-                 ]-             ]-         ])-      [ TableBody ( "" , [] , [] ) (RowHeadColumns 0) [] [] ]-      (TableFoot ( "" , [] , [] ) [])-  , Header-      3-      ( "_simple_without_column_specs" , [] , [] )-      [ Str "Simple"-      , Space-      , Str "without"-      , Space-      , Str "column"-      , Space-      , Str "specs"-      ]-  , Table-      ( "" , [] , [] )-      (Caption Nothing [])-      [ ( AlignDefault , ColWidthDefault )-      , ( AlignDefault , ColWidthDefault )-      ]-      (TableHead-         ( "" , [] , [] )-         [ Row-             ( "" , [] , [] )-             [ Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para-                     [ Str "Column"-                     , Space-                     , Str "1,"-                     , Space-                     , Str "header"-                     , Space-                     , Str "row"-                     ]-                 ]-             , Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para-                     [ Str "Column"-                     , Space-                     , Str "2,"-                     , Space-                     , Str "header"-                     , Space-                     , Str "row"-                     ]-                 ]-             ]-         ])-      [ TableBody-          ( "" , [] , [] )-          (RowHeadColumns 0)-          []-          [ Row-              ( "" , [] , [] )-              [ Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para-                      [ Str "Cell"-                      , Space-                      , Str "in"-                      , Space-                      , Str "column"-                      , Space-                      , Str "1,"-                      , Space-                      , Str "row"-                      , Space-                      , Str "2"-                      ]-                  ]-              , Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para-                      [ Str "Cell"-                      , Space-                      , Str "in"-                      , Space-                      , Str "column"-                      , Space-                      , Str "2,"-                      , Space-                      , Str "row"-                      , Space-                      , Str "2"-                      ]-                  ]-              ]-          , Row-              ( "" , [] , [] )-              [ Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para-                      [ Str "Cell"-                      , Space-                      , Str "in"-                      , Space-                      , Str "column"-                      , Space-                      , Str "1,"-                      , Space-                      , Str "row"-                      , Space-                      , Str "3"-                      ]-                  ]-              , Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para-                      [ Str "Cell"-                      , Space-                      , Str "in"-                      , Space-                      , Str "column"-                      , Space-                      , Str "2,"-                      , Space-                      , Str "row"-                      , Space-                      , Str "3"-                      ]-                  ]-              ]-          ]-      ]-      (TableFoot ( "" , [] , [] ) [])-  , Header-      3-      ( "_with_caption" , [] , [] )-      [ Str "With" , Space , Str "caption" ]-  , Table-      ( "" , [] , [] )-      (Caption-         Nothing-         [ Plain-             [ Str "My" , Space , Str "cool" , Space , Str "table." ]-         ])-      [ ( AlignDefault , ColWidthDefault )-      , ( AlignDefault , ColWidthDefault )-      ]-      (TableHead-         ( "" , [] , [] )-         [ Row-             ( "" , [] , [] )-             [ Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para-                     [ Str "Column"-                     , Space-                     , Str "1,"-                     , Space-                     , Str "header"-                     , Space-                     , Str "row"-                     ]-                 ]-             , Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para-                     [ Str "Column"-                     , Space-                     , Str "2,"-                     , Space-                     , Str "header"-                     , Space-                     , Str "row"-                     ]-                 ]-             ]-         ])-      [ TableBody-          ( "" , [] , [] )-          (RowHeadColumns 0)-          []-          [ Row-              ( "" , [] , [] )-              [ Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para-                      [ Str "Cell"-                      , Space-                      , Str "in"-                      , Space-                      , Str "column"-                      , Space-                      , Str "1,"-                      , Space-                      , Str "row"-                      , Space-                      , Str "2"-                      ]-                  ]-              , Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para-                      [ Str "Cell"-                      , Space-                      , Str "in"-                      , Space-                      , Str "column"-                      , Space-                      , Str "2,"-                      , Space-                      , Str "row"-                      , Space-                      , Str "2"-                      ]-                  ]-              ]-          , Row-              ( "" , [] , [] )-              [ Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para-                      [ Str "Cell"-                      , Space-                      , Str "in"-                      , Space-                      , Str "column"-                      , Space-                      , Str "1,"-                      , Space-                      , Str "row"-                      , Space-                      , Str "3"-                      ]-                  ]-              , Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para-                      [ Str "Cell"-                      , Space-                      , Str "in"-                      , Space-                      , Str "column"-                      , Space-                      , Str "2,"-                      , Space-                      , Str "row"-                      , Space-                      , Str "3"-                      ]-                  ]-              ]-          ]-      ]-      (TableFoot ( "" , [] , [] ) [])-  , Header-      3-      ( "_no_header" , [] , [] )-      [ Str "No" , Space , Str "header" ]-  , Para-      [ Str "By"-      , Space-      , Str "default"-      , Space-      , Str "the"-      , Space-      , Str "first"-      , Space-      , Str "line"-      , Space-      , Str "should"-      , Space-      , Str "turn"-      , Space-      , Str "into"-      , Space-      , Str "the"-      , Space-      , Str "header,"-      , Space-      , Str "but"-      , Space-      , Str "this"-      , SoftBreak-      , Str "can"-      , Space-      , Str "be"-      , Space-      , Str "disabled:"-      ]-  , Table-      ( "" , [] , [] )-      (Caption Nothing [])-      [ ( AlignDefault , ColWidthDefault )-      , ( AlignDefault , ColWidthDefault )-      ]-      (TableHead ( "" , [] , [] ) [])-      [ TableBody-          ( "" , [] , [] )-          (RowHeadColumns 0)-          []-          [ Row-              ( "" , [] , [] )-              [ Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para-                      [ Str "Cell"-                      , Space-                      , Str "in"-                      , Space-                      , Str "column"-                      , Space-                      , Str "1,"-                      , Space-                      , Str "row"-                      , Space-                      , Str "1"-                      ]-                  ]-              , Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para-                      [ Str "Cell"-                      , Space-                      , Str "in"-                      , Space-                      , Str "column"-                      , Space-                      , Str "2,"-                      , Space-                      , Str "row"-                      , Space-                      , Str "1"-                      ]-                  ]-              ]-          , Row-              ( "" , [] , [] )-              [ Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para-                      [ Str "Cell"-                      , Space-                      , Str "in"-                      , Space-                      , Str "column"-                      , Space-                      , Str "1,"-                      , Space-                      , Str "row"-                      , Space-                      , Str "2"-                      ]-                  ]-              , Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para-                      [ Str "Cell"-                      , Space-                      , Str "in"-                      , Space-                      , Str "column"-                      , Space-                      , Str "2,"-                      , Space-                      , Str "row"-                      , Space-                      , Str "2"-                      ]-                  ]-              ]-          ]-      ]-      (TableFoot ( "" , [] , [] ) [])-  , Para-      [ Str "And"-      , Space-      , Str "also"-      , Space-      , Str "explicitly"-      , Space-      , Str "enabled:"-      ]-  , Table-      ( "" , [] , [] )-      (Caption Nothing [])-      [ ( AlignDefault , ColWidthDefault )-      , ( AlignDefault , ColWidthDefault )-      ]-      (TableHead-         ( "" , [] , [] )-         [ Row-             ( "" , [] , [] )-             [ Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para [ Str "Cell" , Space , Str "A1" ] ]-             , Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para [ Str "Cell" , Space , Str "B1" ] ]-             ]-         ])-      [ TableBody-          ( "" , [] , [] )-          (RowHeadColumns 0)-          []-          [ Row-              ( "" , [] , [] )-              [ Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para [ Str "Cell" , Space , Str "A2" ] ]-              , Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para [ Str "Cell" , Space , Str "B2" ] ]-              ]-          , Row-              ( "" , [] , [] )-              [ Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para [ Str "Cell" , Space , Str "A3" ] ]-              , Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para [ Str "Cell" , Space , Str "B3" ] ]-              ]-          ]-      ]-      (TableFoot ( "" , [] , [] ) [])-  , Header 3 ( "_footer" , [] , [] ) [ Str "Footer" ]-  , Table-      ( "" , [] , [] )-      (Caption Nothing [])-      [ ( AlignDefault , ColWidth 0.4 )-      , ( AlignDefault , ColWidth 0.4 )-      , ( AlignDefault , ColWidth 0.2 )-      ]-      (TableHead-         ( "" , [] , [] )-         [ Row-             ( "" , [] , [] )-             [ Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para-                     [ Str "Column"-                     , Space-                     , Str "1,"-                     , Space-                     , Str "header"-                     , Space-                     , Str "row"-                     ]-                 ]-             , Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para-                     [ Str "Column"-                     , Space-                     , Str "2,"-                     , Space-                     , Str "header"-                     , Space-                     , Str "row"-                     ]-                 ]-             , Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para-                     [ Str "Column"-                     , Space-                     , Str "3,"-                     , Space-                     , Str "header"-                     , Space-                     , Str "row"-                     ]-                 ]-             ]-         ])-      [ TableBody-          ( "" , [] , [] )-          (RowHeadColumns 0)-          []-          [ Row-              ( "" , [] , [] )-              [ Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para-                      [ Str "Cell"-                      , Space-                      , Str "in"-                      , Space-                      , Str "column"-                      , Space-                      , Str "1,"-                      , Space-                      , Str "row"-                      , Space-                      , Str "2"-                      ]-                  ]-              , Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para-                      [ Str "Cell"-                      , Space-                      , Str "in"-                      , Space-                      , Str "column"-                      , Space-                      , Str "2,"-                      , Space-                      , Str "row"-                      , Space-                      , Str "2"-                      ]-                  ]-              , Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para-                      [ Str "Cell"-                      , Space-                      , Str "in"-                      , Space-                      , Str "column"-                      , Space-                      , Str "3,"-                      , Space-                      , Str "row"-                      , Space-                      , Str "2"-                      ]-                  ]-              ]-          , Row-              ( "" , [] , [] )-              [ Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para-                      [ Str "Column"-                      , Space-                      , Str "1,"-                      , Space-                      , Str "footer"-                      , Space-                      , Str "row"-                      ]-                  ]-              , Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para-                      [ Str "Column"-                      , Space-                      , Str "2,"-                      , Space-                      , Str "footer"-                      , Space-                      , Str "row"-                      ]-                  ]-              , Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para-                      [ Str "Column"-                      , Space-                      , Str "3,"-                      , Space-                      , Str "footer"-                      , Space-                      , Str "row"-                      ]-                  ]-              ]-          ]-      ]-      (TableFoot ( "" , [] , [] ) [])-  , Para [ Str "or" ]-  , Table-      ( "" , [] , [] )-      (Caption Nothing [])-      [ ( AlignDefault , ColWidthDefault )-      , ( AlignDefault , ColWidthDefault )-      ]-      (TableHead-         ( "" , [] , [] )-         [ Row-             ( "" , [] , [] )-             [ Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para-                     [ Str "Column"-                     , Space-                     , Str "1,"-                     , Space-                     , Str "header"-                     , Space-                     , Str "row"-                     ]-                 ]-             , Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para-                     [ Str "Column"-                     , Space-                     , Str "2,"-                     , Space-                     , Str "header"-                     , Space-                     , Str "row"-                     ]-                 ]-             ]-         ])-      [ TableBody-          ( "" , [] , [] )-          (RowHeadColumns 0)-          []-          [ Row-              ( "" , [] , [] )-              [ Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para-                      [ Str "Cell"-                      , Space-                      , Str "in"-                      , Space-                      , Str "column"-                      , Space-                      , Str "1,"-                      , Space-                      , Str "row"-                      , Space-                      , Str "2"-                      ]-                  ]-              , Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para-                      [ Str "Cell"-                      , Space-                      , Str "in"-                      , Space-                      , Str "column"-                      , Space-                      , Str "2,"-                      , Space-                      , Str "row"-                      , Space-                      , Str "2"-                      ]-                  ]-              ]-          , Row-              ( "" , [] , [] )-              [ Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para-                      [ Str "Cell"-                      , Space-                      , Str "in"-                      , Space-                      , Str "column"-                      , Space-                      , Str "1,"-                      , Space-                      , Str "row"-                      , Space-                      , Str "3"-                      ]-                  ]-              , Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para-                      [ Str "Cell"-                      , Space-                      , Str "in"-                      , Space-                      , Str "column"-                      , Space-                      , Str "2,"-                      , Space-                      , Str "row"-                      , Space-                      , Str "3"-                      ]-                  ]-              ]-          ]-      ]-      (TableFoot-         ( "" , [] , [] )-         [ Row-             ( "" , [] , [] )-             [ Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para-                     [ Str "Column"-                     , Space-                     , Str "1,"-                     , Space-                     , Str "footer"-                     , Space-                     , Str "row"-                     ]-                 ]-             , Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para-                     [ Str "Column"-                     , Space-                     , Str "2,"-                     , Space-                     , Str "footer"-                     , Space-                     , Str "row"-                     ]-                 ]-             ]-         ])-  , Header 3 ( "_alignment" , [] , [] ) [ Str "Alignment" ]-  , Table-      ( "" , [] , [] )-      (Caption Nothing [])-      [ ( AlignDefault , ColWidthDefault )-      , ( AlignDefault , ColWidthDefault )-      ]-      (TableHead-         ( "" , [] , [] )-         [ Row-             ( "" , [] , [] )-             [ Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para [ Str "Column" , Space , Str "Name" ] ]-             , Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para [ Str "Column" , Space , Str "Name" ] ]-             ]-         ])-      [ TableBody-          ( "" , [] , [] )-          (RowHeadColumns 0)-          []-          [ Row-              ( "" , [] , [] )-              [ Cell-                  ( "" , [] , [] )-                  AlignCenter-                  (RowSpan 1)-                  (ColSpan 2)-                  [ Para-                      [ Str "This"-                      , Space-                      , Str "cell"-                      , Space-                      , Str "spans"-                      , Space-                      , Str "two"-                      , Space-                      , Str "columns,"-                      , Space-                      , Str "and"-                      , Space-                      , Str "its"-                      , Space-                      , Str "content"-                      , Space-                      , Str "is"-                      , Space-                      , Str "horizontally"-                      , Space-                      , Str "centered"-                      , Space-                      , Str "because"-                      , Space-                      , Str "the"-                      , SoftBreak-                      , Str "cell"-                      , Space-                      , Str "specifier"-                      , Space-                      , Str "includes"-                      , Space-                      , Str "the"-                      , Space-                      , Code ( "" , [] , [] ) "^"-                      , Space-                      , Str "operator."-                      ]-                  ]-              ]-          , Row-              ( "" , [] , [] )-              [ Cell-                  ( "" , [] , [] )-                  AlignCenter-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para-                      [ Str "This"-                      , Space-                      , Str "content"-                      , Space-                      , Str "is"-                      , Space-                      , Str "duplicated"-                      , Space-                      , Str "in"-                      , Space-                      , Str "two"-                      , Space-                      , Str "adjacent"-                      , Space-                      , Str "columns."-                      , SoftBreak-                      , Str "Its"-                      , Space-                      , Str "content"-                      , Space-                      , Str "is"-                      , Space-                      , Str "horizontally"-                      , Space-                      , Str "centered"-                      , Space-                      , Str "because"-                      , Space-                      , Str "the"-                      , Space-                      , Str "cell"-                      , Space-                      , Str "specifier"-                      , SoftBreak-                      , Str "includes"-                      , Space-                      , Str "the"-                      , Space-                      , Code ( "" , [] , [] ) "^"-                      , Space-                      , Str "operator."-                      ]-                  ]-              , Cell-                  ( "" , [] , [] )-                  AlignCenter-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para-                      [ Str "This"-                      , Space-                      , Str "content"-                      , Space-                      , Str "is"-                      , Space-                      , Str "duplicated"-                      , Space-                      , Str "in"-                      , Space-                      , Str "two"-                      , Space-                      , Str "adjacent"-                      , Space-                      , Str "columns."-                      , SoftBreak-                      , Str "Its"-                      , Space-                      , Str "content"-                      , Space-                      , Str "is"-                      , Space-                      , Str "horizontally"-                      , Space-                      , Str "centered"-                      , Space-                      , Str "because"-                      , Space-                      , Str "the"-                      , Space-                      , Str "cell"-                      , Space-                      , Str "specifier"-                      , SoftBreak-                      , Str "includes"-                      , Space-                      , Str "the"-                      , Space-                      , Code ( "" , [] , [] ) "^"-                      , Space-                      , Str "operator."-                      ]-                  ]-              ]-          ]-      ]-      (TableFoot ( "" , [] , [] ) [])-  , Header-      3-      ( "_multiple_paragraphs_in_cells" , [] , [] )-      [ Str "Multiple"-      , Space-      , Str "paragraphs"-      , Space-      , Str "in"-      , Space-      , Str "cells"-      ]-  , Table-      ( "" , [] , [] )-      (Caption Nothing [])-      [ ( AlignDefault , ColWidthDefault ) ]-      (TableHead-         ( "" , [] , [] )-         [ Row-             ( "" , [] , [] )-             [ Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para-                     [ Str "Single"-                     , Space-                     , Str "paragraph"-                     , Space-                     , Str "on"-                     , Space-                     , Str "row"-                     , Space-                     , Str "1"-                     ]-                 ]-             ]-         ])-      [ TableBody-          ( "" , [] , [] )-          (RowHeadColumns 0)-          []-          [ Row-              ( "" , [] , [] )-              [ Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para-                      [ Str "First"-                      , Space-                      , Str "paragraph"-                      , Space-                      , Str "on"-                      , Space-                      , Str "row"-                      , Space-                      , Str "2"-                      ]-                  , Para-                      [ Str "Second"-                      , Space-                      , Str "paragraph"-                      , Space-                      , Str "on"-                      , Space-                      , Str "row"-                      , Space-                      , Str "2"-                      ]-                  ]-              ]-          ]-      ]-      (TableFoot ( "" , [] , [] ) [])-  , Header-      3-      ( "_complex_table" , [] , [] )-      [ Str "Complex" , Space , Str "table" ]-  , Table-      ( "" , [] , [] )-      (Caption Nothing [])-      [ ( AlignDefault , ColWidthDefault )-      , ( AlignDefault , ColWidthDefault )-      ]-      (TableHead-         ( "" , [] , [] )-         [ Row-             ( "" , [] , [] )-             [ Cell-                 ( "" , [] , [] )-                 AlignRight-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para-                     [ Code ( "" , [] , [] ) "This"-                     , Space-                     , Code ( "" , [] , [] ) "content"-                     , Space-                     , Code ( "" , [] , [] ) "is"-                     , Space-                     , Code ( "" , [] , [] ) "duplicated"-                     , Space-                     , Code ( "" , [] , [] ) "across"-                     , Space-                     , Code ( "" , [] , [] ) "two"-                     , Space-                     , Code ( "" , [] , [] ) "columns."-                     ]-                 , Para-                     [ Code ( "" , [] , [] ) "It"-                     , Space-                     , Code ( "" , [] , [] ) "is"-                     , Space-                     , Code ( "" , [] , [] ) "aligned"-                     , Space-                     , Code ( "" , [] , [] ) "right"-                     , Space-                     , Code ( "" , [] , [] ) "horizontally."-                     ]-                 , Para-                     [ Code ( "" , [] , [] ) "And"-                     , Space-                     , Code ( "" , [] , [] ) "it"-                     , Space-                     , Code ( "" , [] , [] ) "is"-                     , Space-                     , Code ( "" , [] , [] ) "monospaced."-                     ]-                 ]-             , Cell-                 ( "" , [] , [] )-                 AlignRight-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para-                     [ Code ( "" , [] , [] ) "This"-                     , Space-                     , Code ( "" , [] , [] ) "content"-                     , Space-                     , Code ( "" , [] , [] ) "is"-                     , Space-                     , Code ( "" , [] , [] ) "duplicated"-                     , Space-                     , Code ( "" , [] , [] ) "across"-                     , Space-                     , Code ( "" , [] , [] ) "two"-                     , Space-                     , Code ( "" , [] , [] ) "columns."-                     ]-                 , Para-                     [ Code ( "" , [] , [] ) "It"-                     , Space-                     , Code ( "" , [] , [] ) "is"-                     , Space-                     , Code ( "" , [] , [] ) "aligned"-                     , Space-                     , Code ( "" , [] , [] ) "right"-                     , Space-                     , Code ( "" , [] , [] ) "horizontally."-                     ]-                 , Para-                     [ Code ( "" , [] , [] ) "And"-                     , Space-                     , Code ( "" , [] , [] ) "it"-                     , Space-                     , Code ( "" , [] , [] ) "is"-                     , Space-                     , Code ( "" , [] , [] ) "monospaced."-                     ]-                 ]-             ]-         ])-      [ TableBody-          ( "" , [] , [] )-          (RowHeadColumns 0)-          []-          [ Row-              ( "" , [] , [] )-              [ Cell-                  ( "" , [] , [] )-                  AlignCenter-                  (RowSpan 3)-                  (ColSpan 1)-                  [ Para-                      [ Strong-                          [ Str "This"-                          , Space-                          , Str "cell"-                          , Space-                          , Str "spans"-                          , Space-                          , Str "3"-                          , Space-                          , Str "rows."-                          , Space-                          , Str "The"-                          , Space-                          , Str "content"-                          , Space-                          , Str "is"-                          , Space-                          , Str "centered"-                          , Space-                          , Str "horizontally,"-                          , Space-                          , Str "aligned"-                          , Space-                          , Str "to"-                          , Space-                          , Str "the"-                          , Space-                          , Str "bottom"-                          , Space-                          , Str "of"-                          , Space-                          , Str "the"-                          , Space-                          , Str "cell,"-                          , Space-                          , Str "and"-                          , Space-                          , Str "strong."-                          ]-                      ]-                  ]-              , Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para-                      [ Emph-                          [ Str "This"-                          , Space-                          , Str "content"-                          , Space-                          , Str "is"-                          , Space-                          , Str "emphasized."-                          ]-                      ]-                  ]-              ]-          , Row-              ( "" , [] , [] )-              [ Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ CodeBlock-                      ( "" , [] , [] )-                      "This content is aligned to the top of the cell and literal.\n\n"-                  ]-              ]-          , Row-              ( "" , [] , [] )-              [ Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ CodeBlock-                      ( "" , [] , [] )-                      "puts \"This is a source block!\""-                  ]-              ]-          ]-      ]-      (TableFoot ( "" , [] , [] ) [])-  , Header-      3-      ( "_column_styles" , [] , [] )-      [ Str "Column" , Space , Str "styles" ]-  , Table-      ( "" , [] , [] )-      (Caption Nothing [])-      [ ( AlignDefault , ColWidthDefault )-      , ( AlignDefault , ColWidthDefault )-      ]-      (TableHead-         ( "" , [] , [] )-         [ Row-             ( "" , [] , [] )-             [ Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para [ Code ( "" , [] , [] ) "monospace" ] ]-             , Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para [ Code ( "" , [] , [] ) "mono" ] ]-             ]-         ])-      [ TableBody-          ( "" , [] , [] )-          (RowHeadColumns 0)-          []-          [ Row-              ( "" , [] , [] )-              [ Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para [ Str "default" ] ]-              , Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para [ Code ( "" , [] , [] ) "mono" ] ]-              ]-          ]-      ]-      (TableFoot ( "" , [] , [] ) [])-  , Header-      3-      ( "_block_elements_in_cells" , [] , [] )-      [ Str "Block"-      , Space-      , Str "elements"-      , Space-      , Str "in"-      , Space-      , Str "cells"-      ]-  , Table-      ( "" , [] , [] )-      (Caption Nothing [])-      [ ( AlignDefault , ColWidthDefault )-      , ( AlignDefault , ColWidthDefault )-      ]-      (TableHead-         ( "" , [] , [] )-         [ Row-             ( "" , [] , [] )-             [ Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para [ Str "Normal" , Space , Str "Style" ] ]-             , Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para [ Str "AsciiDoc" , Space , Str "Style" ] ]-             ]-         ])-      [ TableBody-          ( "" , [] , [] )-          (RowHeadColumns 0)-          []-          [ Row-              ( "" , [] , [] )-              [ Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para-                      [ Str "This"-                      , Space-                      , Str "cell"-                      , Space-                      , Str "isn\8217t"-                      , Space-                      , Str "prefixed"-                      , Space-                      , Str "with"-                      , Space-                      , Str "an"-                      , Space-                      , Code ( "" , [] , [] ) "a"-                      , Str ","-                      , Space-                      , Str "so"-                      , Space-                      , Str "the"-                      , Space-                      , Str "processor"-                      , Space-                      , Str "doesn\8217t"-                      , Space-                      , Str "interpret"-                      , Space-                      , Str "the"-                      , SoftBreak-                      , Str "following"-                      , Space-                      , Str "lines"-                      , Space-                      , Str "as"-                      , Space-                      , Str "an"-                      , Space-                      , Str "AsciiDoc"-                      , Space-                      , Str "list."-                      ]-                  , Para-                      [ Str "*"-                      , Space-                      , Str "List"-                      , Space-                      , Str "item"-                      , Space-                      , Str "1"-                      , SoftBreak-                      , Str "*"-                      , Space-                      , Str "List"-                      , Space-                      , Str "item"-                      , Space-                      , Str "2"-                      , SoftBreak-                      , Str "*"-                      , Space-                      , Str "List"-                      , Space-                      , Str "item"-                      , Space-                      , Str "3"-                      ]-                  ]-              , Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para-                      [ Str "This"-                      , Space-                      , Str "cell"-                      , Space-                      , Str "is"-                      , Space-                      , Str "prefixed"-                      , Space-                      , Str "with"-                      , Space-                      , Str "an"-                      , Space-                      , Code ( "" , [] , [] ) "a"-                      , Str ","-                      , Space-                      , Str "so"-                      , Space-                      , Str "the"-                      , Space-                      , Str "processor"-                      , Space-                      , Str "interprets"-                      , Space-                      , Str "the"-                      , Space-                      , Str "following"-                      , Space-                      , Str "lines"-                      , SoftBreak-                      , Str "as"-                      , Space-                      , Str "an"-                      , Space-                      , Str "AsciiDoc"-                      , Space-                      , Str "list."-                      ]-                  , BulletList-                      [ [ Para-                            [ Str "List"-                            , Space-                            , Str "item"-                            , Space-                            , Str "1"-                            ]-                        ]-                      , [ Para-                            [ Str "List"-                            , Space-                            , Str "item"-                            , Space-                            , Str "2"-                            ]-                        ]-                      , [ Para-                            [ Str "List"-                            , Space-                            , Str "item"-                            , Space-                            , Str "3"-                            ]-                        ]-                      ]-                  ]-              ]-          , Row-              ( "" , [] , [] )-              [ Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para-                      [ Str "This"-                      , Space-                      , Str "cell"-                      , Space-                      , Str "isn\8217t"-                      , Space-                      , Str "prefixed"-                      , Space-                      , Str "with"-                      , Space-                      , Str "an"-                      , Space-                      , Code ( "" , [] , [] ) "a"-                      , Str ","-                      , Space-                      , Str "so"-                      , Space-                      , Str "the"-                      , Space-                      , Str "processor"-                      , Space-                      , Str "doesn\8217t"-                      , Space-                      , Str "interpret"-                      , Space-                      , Str "the"-                      , Space-                      , Str "listing"-                      , SoftBreak-                      , Str "block"-                      , Space-                      , Str "delimiters"-                      , Space-                      , Str "or"-                      , Space-                      , Str "the"-                      , Space-                      , Code ( "" , [] , [] ) "source"-                      , Space-                      , Str "style."-                      ]-                  , Para-                      [ Str "----"-                      , SoftBreak-                      , Str "import"-                      , Space-                      , Str "os"-                      , SoftBreak-                      , Str "print"-                      , Space-                      , Str "(\"%s\""-                      , Space-                      , Str "%(os.uname()))"-                      , SoftBreak-                      , Str "----"-                      ]-                  ]-              , Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para-                      [ Str "This"-                      , Space-                      , Str "cell"-                      , Space-                      , Str "is"-                      , Space-                      , Str "prefixed"-                      , Space-                      , Str "with"-                      , Space-                      , Str "an"-                      , Space-                      , Code ( "" , [] , [] ) "a"-                      , Str ","-                      , Space-                      , Str "so"-                      , Space-                      , Str "the"-                      , Space-                      , Str "listing"-                      , Space-                      , Str "block"-                      , Space-                      , Str "is"-                      , Space-                      , Str "processed"-                      , Space-                      , Str "and"-                      , Space-                      , Str "rendered"-                      , SoftBreak-                      , Str "according"-                      , Space-                      , Str "to"-                      , Space-                      , Str "the"-                      , Space-                      , Code ( "" , [] , [] ) "source"-                      , Space-                      , Str "style"-                      , Space-                      , Str "rules."-                      ]-                  , CodeBlock-                      ( "" , [ "python" ] , [] )-                      "import os\nprint \"%s\" %(os.uname())"-                  ]-              ]-          ]-      ]-      (TableFoot ( "" , [] , [] ) [])-  , Header-      3-      ( "_col_and_rowspan" , [] , [] )-      [ Str "Col" , Space , Str "and" , Space , Str "rowspan" ]-  , Table-      ( "" , [] , [] )-      (Caption Nothing [])-      [ ( AlignDefault , ColWidthDefault )-      , ( AlignDefault , ColWidthDefault )-      , ( AlignDefault , ColWidthDefault )-      ]-      (TableHead-         ( "" , [] , [] )-         [ Row-             ( "" , [] , [] )-             [ Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para-                     [ Str "Column"-                     , Space-                     , Str "1,"-                     , Space-                     , Str "header"-                     , Space-                     , Str "row"-                     ]-                 ]-             , Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para-                     [ Str "Column"-                     , Space-                     , Str "2,"-                     , Space-                     , Str "header"-                     , Space-                     , Str "row"-                     ]-                 ]-             , Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para-                     [ Str "Column"-                     , Space-                     , Str "3,"-                     , Space-                     , Str "header"-                     , Space-                     , Str "row"-                     ]-                 ]-             ]-         ])-      [ TableBody-          ( "" , [] , [] )-          (RowHeadColumns 0)-          []-          [ Row-              ( "" , [] , [] )-              [ Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 2)-                  (ColSpan 2)-                  [ Para-                      [ Str "This"-                      , Space-                      , Str "cell"-                      , Space-                      , Str "spans"-                      , Space-                      , Str "2"-                      , Space-                      , Str "cols"-                      , Space-                      , Str "and"-                      , Space-                      , Str "2"-                      , Space-                      , Str "rows"-                      ]-                  ]-              , Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para-                      [ Str "Cell"-                      , Space-                      , Str "in"-                      , Space-                      , Str "column"-                      , Space-                      , Str "3,"-                      , Space-                      , Str "row"-                      , Space-                      , Str "2"-                      ]-                  ]-              ]-          , Row-              ( "" , [] , [] )-              [ Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para-                      [ Str "Cell"-                      , Space-                      , Str "in"-                      , Space-                      , Str "column"-                      , Space-                      , Str "3,"-                      , Space-                      , Str "row"-                      , Space-                      , Str "3"-                      ]-                  ]-              ]-          , Row-              ( "" , [] , [] )-              [ Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 3)-                  [ Para-                      [ Str "Cell"-                      , Space-                      , Str "in"-                      , Space-                      , Str "column"-                      , Space-                      , Str "1-3,"-                      , Space-                      , Str "row"-                      , Space-                      , Str "4"-                      ]-                  ]-              ]-          ]-      ]-      (TableFoot ( "" , [] , [] ) [])-  , Header-      3-      ( "_csv_table" , [] , [] )-      [ Str "CSV" , Space , Str "table" ]-  , Table-      ( "" , [] , [] )-      (Caption Nothing [])-      [ ( AlignDefault , ColWidthDefault )-      , ( AlignDefault , ColWidthDefault )-      , ( AlignDefault , ColWidthDefault )-      ]-      (TableHead-         ( "" , [] , [] )-         [ Row-             ( "" , [] , [] )-             [ Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para [ Str "Artist" ] ]-             , Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para [ Str "Track" ] ]-             , Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para [ Str "Genre" ] ]-             ]-         ])-      [ TableBody-          ( "" , [] , [] )-          (RowHeadColumns 0)-          []-          [ Row-              ( "" , [] , [] )-              [ Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para [ Str "Baauer" ] ]-              , Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para [ Str "Harlem" , Space , Str "Shake" ] ]-              , Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para [ Str "Hip" , Space , Str "Hop" ] ]-              ]-          , Row-              ( "" , [] , [] )-              [ Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para [ Str "The" , Space , Str "Lumineers" ] ]-              , Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para [ Str "Ho" , Space , Str "Hey" ] ]-              , Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para [ Str "Folk" , Space , Str "Rock" ] ]-              ]-          ]-      ]-      (TableFoot ( "" , [] , [] ) [])-  , Para [ Str "or" ]-  , Table-      ( "" , [] , [] )-      (Caption Nothing [])-      [ ( AlignDefault , ColWidthDefault )-      , ( AlignDefault , ColWidthDefault )-      , ( AlignDefault , ColWidthDefault )-      ]-      (TableHead-         ( "" , [] , [] )-         [ Row-             ( "" , [] , [] )-             [ Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para [ Str "Artist" ] ]-             , Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para [ Str "Track" ] ]-             , Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para [ Str "Genre" ] ]-             ]-         ])-      [ TableBody-          ( "" , [] , [] )-          (RowHeadColumns 0)-          []-          [ Row-              ( "" , [] , [] )-              [ Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para [ Str "Baauer" ] ]-              , Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para [ Str "Harlem" , Space , Str "Shake" ] ]-              , Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para [ Str "Hip" , Space , Str "Hop" ] ]-              ]-          ]-      ]-      (TableFoot ( "" , [] , [] ) [])-  , Header-      3-      ( "_dsv_table" , [] , [] )-      [ Str "DSV" , Space , Str "table" ]-  , Table-      ( "" , [] , [] )-      (Caption Nothing [])-      [ ( AlignDefault , ColWidthDefault )-      , ( AlignDefault , ColWidthDefault )-      , ( AlignDefault , ColWidthDefault )-      ]-      (TableHead-         ( "" , [] , [] )-         [ Row-             ( "" , [] , [] )-             [ Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para [ Str "a" ] ]-             , Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para [ Str "b" ] ]-             , Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para [ Str "c" ] ]-             ]-         ])-      [ TableBody-          ( "" , [] , [] )-          (RowHeadColumns 0)-          []-          [ Row-              ( "" , [] , [] )-              [ Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para [ Str "d" ] ]-              , Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para [ Str "e" ] ]-              , Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para [ Str "f" ] ]-              ]-          ]-      ]-      (TableFoot ( "" , [] , [] ) [])-  , Para [ Str "or" ]-  , Table-      ( "" , [] , [] )-      (Caption Nothing [])-      [ ( AlignDefault , ColWidthDefault )-      , ( AlignDefault , ColWidthDefault )-      , ( AlignDefault , ColWidthDefault )-      ]-      (TableHead-         ( "" , [] , [] )-         [ Row-             ( "" , [] , [] )-             [ Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para [ Str "Artist" ] ]-             , Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para [ Str "Track" ] ]-             , Cell-                 ( "" , [] , [] )-                 AlignDefault-                 (RowSpan 1)-                 (ColSpan 1)-                 [ Para [ Str "Genre" ] ]-             ]-         ])-      [ TableBody-          ( "" , [] , [] )-          (RowHeadColumns 0)-          []-          [ Row-              ( "" , [] , [] )-              [ Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para [ Str "Robyn" ] ]-              , Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para [ Str "Indestructible" ] ]-              , Cell-                  ( "" , [] , [] )-                  AlignDefault-                  (RowSpan 1)-                  (ColSpan 1)-                  [ Para [ Str "Dance" ] ]+          ( "" , [ "red" , "icon" ] , [] )+          []+          ( "./images/icons/heart.png" , "" )+      ]+  , Para [ Str "anchor:tiger" ]+  , Para [ Strong [ Str "*bold*" ] ]+  , Para+      [ Link+          ( "" , [] , [] )+          [ Str "Get" , Space , Str "Report" ]+          ( "downloads/report.pdf" , "" )+      ]+  , Para+      [ Link+          ( "" , [] , [] )+          [ Str "tools.html#editors" ]+          ( "tools.html#editors" , "" )+      ]+  , Para+      [ Link+          ( "" , [] , [] )+          [ Str "Your" , Space , Str "files" ]+          ( "file:///home/username" , "" )+      ]+  , Para [ Str "Tricky" , Space , Str "cases:" ]+  , Para+      [ Link+          ( "" , [] , [] )+          [ Str "Get" , Space , Str "Report" ]+          ( "My Documents/report.pdf" , "" )+      ]+  , Para+      [ Link+          ( "" , [] , [] )+          [ Str "Get" , Space , Str "Report" ]+          ( "My&#32;Documents/report.pdf" , "" )+      ]+  , Para+      [ Link+          ( "" , [] , [] )+          [ Str "Get" , Space , Str "Report" ]+          ( "My%20Documents/report.pdf" , "" )+      ]+  , Para+      [ Link+          ( "" , [] , [] )+          [ Str "https://example.org/now_this__link_works.html" ]+          ( "https://example.org/now_this__link_works.html" , "" )+      ]+  , Para+      [ Link+          ( "" , [] , [] )+          [ Str "Subscribe" ]+          ( "join@discuss.example.org" , "" )+      ]+  , Para+      [ Link+          ( "" , [ "mail" ] , [] )+          [ Str "Click,"+          , Space+          , Str "subscribe,"+          , Space+          , Str "and"+          , Space+          , Str "participate!"+          ]+          ( "join@discuss.example.org" , "" )+      ]+  , Para+      [ Link+          ( "" , [ "cross-reference" ] , [] )+          [ Str "use"+          , Space+          , Str "attributes"+          , Space+          , Str "within"+          , Space+          , Str "the"+          , Space+          , Str "link"+          , Space+          , Str "macro"+          ]+          ( "#link-macro-attributes" , "" )+      ]+  , Figure+      ( "" , [] , [] )+      (Caption Nothing [])+      [ Plain+          [ Image+              ( "" , [] , [] ) [ Str "Sunset" ] ( "sunset.jpg" , "" )+          ]+      ]+  , Figure+      ( "" , [] , [] )+      (Caption Nothing [])+      [ Plain [ Image ( "" , [] , [] ) [] ( "name.png" , "" ) ] ]+  , Figure+      ( "" , [] , [] )+      (Caption Nothing [])+      [ Plain+          [ Image+              ( ""+              , []+              , [ ( "width" , "300px" ) , ( "height" , "400px" ) ]+              )+              [ Str "Sunset" ]+              ( "sunset.jpg" , "" )+          ]+      ]+  , Div+      ( ""+      , []+      , [ ( "wrapper" , "1" )+        , ( "alt" , "Sunset" )+        , ( "height" , "400" )+        , ( "width" , "300" )+        ]+      )+      [ Figure+          ( "" , [] , [] )+          (Caption Nothing [])+          [ Plain [ Image ( "" , [] , [] ) [] ( "sunset.jpg" , "" ) ]+          ]+      ]+  , Para [ Math DisplayMath "e=mc^2\n" ]+  , Para [ Math DisplayMath "sin n / 3\n" ]+  , Para [ Math DisplayMath "e^i\n" ]+  , Header+      2+      ( "_attribute_substitutions" , [] , [] )+      [ Str "Attribute" , Space , Str "substitutions" ]+  , Para [ Str "Foo" , Space , Str "bar" , Space , Str "baz" ]+  , Para [ Str "{nonexistent}" ]+  , Para+      [ Str "Built"+      , Space+      , Str "in:"+      , Space+      , Str "xyz"+      , Space+      , Str "a\160b\8203c'd\8216"+      ]+  , Header+      2+      ( "_bold_and_italic" , [] , [] )+      [ Str "Bold" , Space , Str "and" , Space , Str "italic" ]+  , Para+      [ Str "Constrained:"+      , Space+      , Strong+          [ Str "this"+          , Space+          , Str "is"+          , Space+          , Str "bold"+          , Space+          , Emph [ Str "and" , Space , Str "italic" ]+          ]+      , Str "."+      ]+  , Para+      [ Str "Unconstrained:"+      , Space+      , Str "wild"+      , Strong+          [ Str "content"+          , Emph [ Str "with" , Space , Str "italic" ]+          , Str "stuff"+          ]+      , Str "."+      ]+  , Header 2 ( "_monospace" , [] , [] ) [ Str "Monospace" ]+  , Para [ Code ( "" , [] , [] ) "simple" ]+  , Para+      [ Code ( "" , [] , [] ) "complex"+      , Space+      , Strong+          [ Code ( "" , [] , [] ) "with"+          , Space+          , Code ( "" , [] , [] ) "bold"+          ]+      , Space+      , Code ( "" , [] , [] ) "text"+      , Space+      , Code ( "" , [] , [] ) "and"+      , Space+      , Code ( "" , [] , [] ) "a"+      , Space+      , Link+          ( "" , [] , [] )+          [ Code ( "" , [] , [] ) "foo.html" ]+          ( "foo.html" , "" )+      ]+  , Para+      [ Str "unconstrained"+      , Code ( "" , [] , [] ) "wwow"+      , Str "okay"+      ]+  , Header+      2+      ( "_span_and_inline_attributes" , [] , [] )+      [ Str "Span"+      , Space+      , Str "and"+      , Space+      , Str "inline"+      , Space+      , Str "attributes"+      ]+  , Para+      [ Span+          ( "" , [ "red" ] , [] )+          [ Str "Bonjour" , Space , Strong [ Str "monsieur" ] ]+      ]+  , Para+      [ Str "Un"+      , Span ( "" , [ "red" ] , [] ) [ Str "constrained" ]+      , Str "content"+      ]+  , Para+      [ Str "With"+      , Space+      , Span+          ( "" , [ "mark" ] , [] )+          [ Str "no" , Space , Str "attribute" ]+      , Space+      , Str "it\8217s"+      , Space+      , Str "highlighted."+      ]+  , Header+      2+      ( "_sub_and_superscript" , [] , [] )+      [ Str "Sub"+      , Space+      , Str "and"+      , Space+      , Str "superscript"+      ]+  , Para [ Str "H" , Subscript [ Str "2" ] , Str "O" ]+  , Para+      [ Str "H"+      , Subscript [ Str "a" , Space , Str "b" ]+      , Str "O"+      ]+  , Para+      [ Str "Not"+      , Space+      , Str "subscript:"+      , Space+      , Str "H~a"+      , Space+      , Str "b~O."+      ]+  , Para [ Str "H^2&O" ]+  , Para+      [ Str "H"+      , Superscript [ Str "a" , Space , Str "b" ]+      , Str "O"+      ]+  , Para+      [ Str "Not"+      , Space+      , Str "subscript:"+      , Space+      , Str "H^a"+      , Space+      , Str "b^O."+      ]+  , Header+      2 ( "_passthrough" , [] , [] ) [ Str "Passthrough" ]+  , Para+      [ Str "Here"+      , Space+      , Str "the"+      , Space+      , Str "special"+      , Space+      , Str "characters"+      , Space+      , Str "just"+      , Space+      , Str "come"+      , Space+      , Str "through"+      , Space+      , Str "as"+      , Space+      , Str "literal:"+      ]+  , Para [ Str "<b>*test*</b>" ]+  , Para [ Str "xx<b>*test*</b>xx" ]+  , Para+      [ Str "But"+      , Space+      , Str "here"+      , Space+      , Str "they"+      , Space+      , Str "are"+      , Space+      , Str "passed"+      , Space+      , Str "through:"+      ]+  , Para [ Str "xx" , Strong [ Str "*test*" ] , Str "xx" ]+  , Header 2 ( "_quoted" , [] , [] ) [ Str "Quoted" ]+  , Para+      [ Quoted DoubleQuote [ Str "double" , Space , Str "quoted" ]+      ]+  , Para+      [ Quoted SingleQuote [ Str "single" , Space , Str "quoted" ]+      ]+  , Header 2 ( "_footnotes" , [] , [] ) [ Str "Footnotes" ]+  , Para+      [ Str "Double"+      , Note+          [ Para+              [ Str "The"+              , Space+              , Str "double"+              , Space+              , Str "hail-and-rainbow"+              , Space+              , Str "level"+              , Space+              , Str "makes"+              , Space+              , Str "my"+              , Space+              , Str "toes"+              , Space+              , Str "tingle."+              ]+          ]+      ]+  , Para+      [ Str "A"+      , Space+      , Str "bold"+      , Space+      , Str "statement!"+      , Note+          [ Para+              [ Str "Opinions"+              , Space+              , Str "are"+              , Space+              , Str "my"+              , Space+              , Str "own."+              ]+          ]+      , SoftBreak+      , Str "Another"+      , Space+      , Str "outrageous"+      , Space+      , Str "statement."+      , Note+          [ Para+              [ Str "Opinions"+              , Space+              , Str "are"+              , Space+              , Str "my"+              , Space+              , Str "own."+              ]+          ]+      ]+  , Header+      1+      ( "_block_markup" , [] , [] )+      [ Str "Block" , Space , Str "markup" ]+  , Header 2 ( "_sections" , [] , [] ) [ Str "Sections" ]+  , Header+      3+      ( "_another_level" , [] , [] )+      [ Str "Another" , Space , Str "level" ]+  , Header+      4 ( "_level_5" , [] , [] ) [ Str "Level" , Space , Str "5" ]+  , Header+      3+      ( "_markdown_style" , [] , [] )+      [ Str "Markdown" , Space , Str "style" ]+  , Header+      4+      ( "_level_5_2" , [] , [] )+      [ Str "Level" , Space , Str "5" ]+  , Header+      2+      ( "_discrete_heading" , [] , [] )+      [ Str "Discrete" , Space , Str "heading" ]+  , Header+      3+      ( "" , [] , [] )+      [ Str "A"+      , Space+      , Str "discrete"+      , Space+      , Str "heading,"+      , Space+      , Str "not"+      , Space+      , Str "a"+      , Space+      , Str "section"+      ]+  , Header 2 ( "_paragraph" , [] , [] ) [ Str "Paragraph" ]+  , Para+      [ Str "This"+      , Space+      , Str "is"+      , Space+      , Str "a"+      , Space+      , Str "paragraph"+      , SoftBreak+      , Str "whose"+      , Space+      , Str "source"+      , Space+      , Str "fits"+      , Space+      , Str "on"+      , Space+      , Str "two"+      , Space+      , Str "lines."+      ]+  , Para+      [ Str "{.This"+      , Space+      , Str "is"+      , Space+      , Str "my"+      , Space+      , Str "title}"+      , SoftBreak+      , Str "A"+      , Space+      , Str "paragraph"+      , Space+      , Str "with"+      , Space+      , Str "a"+      , Space+      , Str "title."+      ]+  , Header+      2+      ( "_example_block" , [] , [] )+      [ Str "Example" , Space , Str "block" ]+  , Div+      ( "" , [] , [] )+      [ Div+          ( "" , [ "title" ] , [] )+          [ Para [ Str "Optional" , Space , Str "title" ] ]+      , Para+          [ Str "This"+          , Space+          , Str "is"+          , Space+          , Str "an"+          , Space+          , Str "example"+          , Space+          , Str "of"+          , Space+          , Str "an"+          , Space+          , Str "example"+          , Space+          , Str "block."+          ]+      ]+  , Div+      ( "" , [ "example" ] , [] )+      [ Div+          ( "" , [ "title" ] , [] )+          [ Para [ Str "Optional" , Space , Str "title" ] ]+      , Para+          [ Str "Paragraph" , Space , Strong [ Str "one" ] , Str "." ]+      , Para+          [ Str "Paragraph" , Space , Strong [ Str "two" ] , Str "." ]+      ]+  , Header 2 ( "_admonition" , [] , [] ) [ Str "Admonition" ]+  , Para [ Str "Simple" , Space , Str "form:" ]+  , Div+      ( "" , [ "warning" ] , [] )+      [ Div ( "" , [ "title" ] , [] ) [ Para [ Str "Warning" ] ]+      , Para+          [ Str "This"+          , Space+          , Str "is"+          , Space+          , Str "very"+          , Space+          , Str "dangerous."+          , SoftBreak+          , Str "Don\8217t"+          , Space+          , Str "do"+          , Space+          , Str "it"+          , Space+          , Str "unless"+          , Space+          , Str "you"+          , Space+          , Str "understand"+          , Space+          , Str "the"+          , Space+          , Str "risks."+          ]+      ]+  , Div+      ( "" , [ "important" ] , [] )+      [ Div+          ( "" , [ "title" ] , [] )+          [ Para+              [ Str "Title"+              , Space+              , Str "of"+              , Space+              , Str "the"+              , Space+              , Str "admonition"+              ]+          ]+      , Para [ Str "Remember:" ]+      , OrderedList+          ( 1 , DefaultStyle , DefaultDelim )+          [ [ Para+                [ Str "Don\8217t"+                , Space+                , Str "do"+                , Space+                , Str "this."+                ]+            ]+          , [ Para+                [ Str "And"+                , Space+                , Str "don\8217t"+                , Space+                , Str "do"+                , Space+                , Str "that."+                ]+            ]+          ]+      ]+  , Header 2 ( "_sidebar" , [] , [] ) [ Str "Sidebar" ]+  , Para+      [ Str "A" , Space , Str "simple" , Space , Str "sidebar." ]+  , Div+      ( "" , [ "sidebar" ] , [] )+      [ Div+          ( "" , [ "title" ] , [] )+          [ Para+              [ Str "Optional"+              , Space+              , Str "Title"+              , Space+              , Strong+                  [ Str "with"+                  , Space+                  , Str "strong"+                  , Space+                  , Str "emphasis"+                  ]+              ]+          ]+      , Para+          [ Str "Here"+          , Space+          , Str "is"+          , Space+          , Str "a"+          , Space+          , Str "sidebar."+          ]+      , Div+          ( "" , [ "tip" ] , [] )+          [ Div ( "" , [ "title" ] , [] ) [ Para [ Str "Tip" ] ]+          , Para+              [ Str "It"+              , Space+              , Str "can"+              , Space+              , Str "contain"+              , Space+              , Str "any"+              , Space+              , Str "type"+              , Space+              , Str "of"+              , Space+              , Str "content."+              ]+          ]+      ]+  , Header+      2+      ( "_literal_block" , [] , [] )+      [ Str "Literal" , Space , Str "block" ]+  , Para+      [ Str "Short"+      , Space+      , Str "indented"+      , Space+      , Str "code:"+      ]+  , CodeBlock+      ( "" , [] , [] )+      "$ ls -a\n$ cat /foo/bar/baz \\\n     /bi/bim/bop\n"+  , CodeBlock+      ( "" , [] , [] ) "This is\n  a literal block too.\n"+  , CodeBlock+      ( "" , [] , [] )+      "  Fenced\n    $+ *a* literal\n\n****\nnot a sidebar\n****\n"+  , Header 2 ( "_listing" , [] , [] ) [ Str "Listing" ]+  , CodeBlock+      ( "" , [ "ruby" ] , [] )+      "require 'sinatra'\n\nget '/hi' do\n  \"Hello World!\"\nend"+  , Para [ Str "Implied:" ]+  , CodeBlock+      ( "" , [ "ruby" ] , [] )+      "require 'sinatra'\n\nget '/hi' do\n  \"Hello World!\"\nend"+  , CodeBlock+      ( "" , [ "ruby" ] , [] )+      "# A function\ndef foo\n  return 42\nend"+  , CodeBlock+      ( "hello" , [ "haskell" ] , [] )+      "putStrLn $ unwords [\"Hello\", \"world\"]"+  , Para [ Str "Line" , Space , Str "numbering:" ]+  , CodeBlock+      ( "" , [] , [ ( "options" , "linenums" ) ] )+      "puts 1\nputs 2\nputs 3"+  , CodeBlock+      ( "" , [] , [] ) "This doesn't have a language.\n  +=\"hi\""+  , Para+      [ Str "And"+      , Space+      , Str "with"+      , Space+      , Str "a"+      , Space+      , Str "callout"+      , Space+      , Str "list:"+      ]+  , CodeBlock+      ( "" , [ "ruby" ] , [] )+      "require 'sinatra' \9312\n\nget '/hi' do \9313 \9314\n  \"Hello World!\"\nend"+  , Div+      ( "" , [ "callout-list" ] , [] )+      [ OrderedList+          ( 1 , DefaultStyle , DefaultDelim )+          [ [ Para [ Str "Library" , Space , Str "import" ] ]+          , [ Para [ Str "URL" , Space , Str "mapping" ] ]+          , [ Para [ Str "Response" , Space , Str "block" ] ]+          ]+      ]+  , Para [ Str "Markdown-style" , Space , Str "fenced:" ]+  , CodeBlock+      ( "" , [ "ruby" ] , [] ) "def foo\n  return 5\nend"+  , Header 2 ( "_verse" , [] , [] ) [ Str "Verse" ]+  , BlockQuote+      [ Para+          [ Str "The"+          , Space+          , Str "fog"+          , Space+          , Str "comes"+          , LineBreak+          , Str "on"+          , Space+          , Str "little"+          , Space+          , Str "cat"+          , Space+          , Str "feet."+          ]+      , Para+          [ Str "\8212"+          , Space+          , Str "Carl"+          , Space+          , Str "Sandburg,"+          , Space+          , Str "two"+          , Space+          , Str "lines"+          , Space+          , Str "from"+          , Space+          , Str "the"+          , Space+          , Str "poem"+          , Space+          , Str "Fog"+          ]+      ]+  , BlockQuote+      [ Para+          [ Str "The"+          , Space+          , Str "fog"+          , Space+          , Str "comes"+          , LineBreak+          , Str "on"+          , Space+          , Str "little"+          , Space+          , Str "cat"+          , Space+          , Str "feet."+          , LineBreak+          , Str "It"+          , Space+          , Str "sits"+          , Space+          , Str "looking"+          , LineBreak+          , Str "over"+          , Space+          , Str "harbor"+          , Space+          , Str "and"+          , Space+          , Str "city"+          , LineBreak+          , Str "on"+          , Space+          , Str "silent"+          , Space+          , Str "haunches"+          , LineBreak+          , Str "and"+          , Space+          , Str "then"+          , Space+          , Str "moves"+          , Space+          , Str "on."+          ]+      , Para+          [ Str "\8212"+          , Space+          , Str "Carl"+          , Space+          , Str "Sandburg,"+          , Space+          , Str "Fog"+          ]+      ]+  , Header+      2 ( "_collapsible" , [] , [] ) [ Str "Collapsible" ]+  , Para+      [ Str "Click"+      , Space+      , Str "here"+      , Space+      , Str "for"+      , Space+      , Str "more."+      ]+  , Div+      ( ""+      , [ "example" ]+      , [ ( "options" , "collapsible,open" ) ]+      )+      [ Para+          [ Str "This"+          , Space+          , Str "is"+          , Space+          , Str "collapsible."+          ]+      , Para+          [ Str "It"+          , Space+          , Str "can"+          , Space+          , Str "be"+          , Space+          , Str "hidden."+          ]+      ]+  , Div+      ( "" , [] , [ ( "options" , "collapsible" ) ] )+      [ Div+          ( "" , [ "title" ] , [] )+          [ Para [ Str "Click" , Space , Str "me!" ] ]+      , Para+          [ Str "This"+          , Space+          , Str "paragraph"+          , Space+          , Str "is"+          , SoftBreak+          , Str "also"+          , Space+          , Str "collapsible."+          ]+      ]+  , Header 2 ( "_quote" , [] , [] ) [ Str "Quote" ]+  , BlockQuote+      [ Para+          [ Str "Everybody"+          , Space+          , Str "remember"+          , Space+          , Str "where"+          , Space+          , Str "we"+          , Space+          , Str "parked."+          ]+      , Para+          [ Str "\8212"+          , Space+          , Str "Captain"+          , Space+          , Str "James"+          , Space+          , Str "T."+          , Space+          , Str "Kirk,"+          , Space+          , Str "Star"+          , Space+          , Str "Trek"+          , Space+          , Str "IV:"+          , Space+          , Str "The"+          , Space+          , Str "Voyage"+          , Space+          , Str "Home"+          ]+      ]+  , BlockQuote+      [ Para+          [ Str "Dennis:"+          , Space+          , Str "Come"+          , Space+          , Str "and"+          , Space+          , Str "see"+          , Space+          , Str "the"+          , Space+          , Str "violence"+          , Space+          , Str "inherent"+          , Space+          , Str "in"+          , Space+          , Str "the"+          , Space+          , Str "system."+          , Space+          , Str "Help!"+          , Space+          , Str "Help!"+          , Space+          , Str "I\8217m"+          , Space+          , Str "being"+          , SoftBreak+          , Str "repressed."+          ]+      , Para+          [ Str "King"+          , Space+          , Str "Arthur:"+          , Space+          , Str "Bloody"+          , Space+          , Str "peasant!"+          ]+      , Para+          [ Str "Dennis:"+          , Space+          , Str "Oh,"+          , Space+          , Str "what"+          , Space+          , Str "a"+          , Space+          , Str "giveaway!"+          , Space+          , Str "Did"+          , Space+          , Str "you"+          , Space+          , Str "hear"+          , Space+          , Str "that?"+          , Space+          , Str "Did"+          , Space+          , Str "you"+          , Space+          , Str "hear"+          , Space+          , Str "that,"+          , Space+          , Str "eh?"+          , Space+          , Str "That\8217s"+          , Space+          , Str "what"+          , Space+          , Str "I\8217m"+          , SoftBreak+          , Str "on"+          , Space+          , Str "about!"+          , Space+          , Str "Did"+          , Space+          , Str "you"+          , Space+          , Str "see"+          , Space+          , Str "him"+          , Space+          , Str "repressing"+          , Space+          , Str "me?"+          , Space+          , Str "You"+          , Space+          , Str "saw"+          , Space+          , Str "him,"+          , Space+          , Str "Didn\8217t"+          , Space+          , Str "you?"+          ]+      , Para+          [ Str "\8212"+          , Space+          , Str "Monty"+          , Space+          , Str "Python"+          , Space+          , Str "and"+          , Space+          , Str "the"+          , Space+          , Str "Holy"+          , Space+          , Str "Grail"+          ]+      ]+  , Div+      ( "roads" , [ "movie" ] , [ ( "wrapper" , "1" ) ] )+      [ BlockQuote+          [ Para+              [ Str "Roads?"+              , Space+              , Str "Where"+              , Space+              , Str "we\8217re"+              , Space+              , Str "going,"+              , Space+              , Str "we"+              , Space+              , Str "don\8217t"+              , Space+              , Str "need"+              , Space+              , Str "roads."+              ]+          , Para+              [ Str "\8212"+              , Space+              , Str "Dr."+              , Space+              , Str "Emmett"+              , Space+              , Str "Brown"+              ]+          ]+      ]+  , Header 2 ( "_pass" , [] , [] ) [ Str "Pass" ]+  , Para [ Str "pass" , Space , Emph [ Str "through" ] ]+  , Header+      2+      ( "_open_block" , [] , [] )+      [ Str "Open" , Space , Str "block" ]+  , Div+      ( "" , [] , [ ( "key" , "a value" ) ] )+      [ Div+          ( "" , [ "title" ] , [] )+          [ Para [ Str "A" , Space , Str "title." ] ]+      , Para+          [ Str "Any"+          , Space+          , Str "content"+          , Space+          , Str "can"+          , Space+          , Str "go"+          , Space+          , Str "here:"+          ]+      , OrderedList+          ( 1 , DefaultStyle , DefaultDelim )+          [ [ Para [ Str "one" ] ] , [ Para [ Str "two" ] ] ]+      ]+  , Header 2 ( "_anchor" , [] , [] ) [ Str "Anchor" ]+  , Div+      ( "goals" , [] , [ ( "wrapper" , "1" ) ] )+      [ BulletList+          [ [ Para [ Str "one" ] ] , [ Para [ Str "two" ] ] ]+      ]+  , Header 2 ( "_breaks" , [] , [] ) [ Str "Breaks" ]+  , Para+      [ Str "Asciidoc"+      , Space+      , Str "thematic"+      , Space+      , Str "break:"+      ]+  , HorizontalRule+  , Para [ Str "Markdown" , Space , Str "style:" ]+  , HorizontalRule+  , HorizontalRule+  , HorizontalRule+  , HorizontalRule+  , Para [ Str "Page" , Space , Str "breaks:" ]+  , Div+      ( "" , [ "page-break" ] , [ ( "wrapper" , "1" ) ] )+      [ HorizontalRule ]+  , Div+      ( ""+      , [ "page-break" ]+      , [ ( "options" , "always" ) , ( "wrapper" , "1" ) ]+      )+      [ HorizontalRule ]+  , Header 2 ( "_list" , [] , [] ) [ Str "List" ]+  , BulletList+      [ [ Para+            [ Str "Edgar" , Space , Str "Allan" , Space , Str "Poe" ]+        ]+      , [ Para+            [ Str "Sheri" , Space , Str "S." , Space , Str "Tepper" ]+        ]+      , [ Para [ Str "Bill" , Space , Str "Bryson" ] ]+      ]+  , Div+      ( "" , [] , [] )+      [ Div+          ( "" , [ "title" ] , [] )+          [ Para+              [ Str "Kizmet\8217s"+              , Space+              , Str "Favorite"+              , Space+              , Str "Authors"+              ]+          ]+      , BulletList+          [ [ Para+                [ Str "Edgar"+                , Space+                , Str "Allan"+                , Space+                , Str "Poe"+                ]+            ]+          , [ Para+                [ Str "Sheri"+                , Space+                , Str "S."+                , Space+                , Str "Tepper"+                ]+            ]+          , [ Para [ Str "Bill" , Space , Str "Bryson" ] ]+          ]+      ]+  , BulletList+      [ [ Para+            [ Str "Edgar" , Space , Str "Allan" , Space , Str "Poe" ]+        ]+      , [ Para+            [ Str "Sheri" , Space , Str "S." , Space , Str "Tepper" ]+        ]+      , [ Para [ Str "Bill" , Space , Str "Bryson" ] ]+      ]+  , OrderedList+      ( 1 , DefaultStyle , DefaultDelim )+      [ [ Para [ Str "Nested" , Space , Str "list" ]+        , BulletList+            [ [ Para+                  [ Str "West"+                  , Space+                  , Str "wood"+                  , Space+                  , Str "maze"+                  ]+              , BulletList+                  [ [ Para [ Str "Maze" , Space , Str "heart" ]+                    , BulletList+                        [ [ Para+                              [ Str "Reflection" , Space , Str "pool" ]+                          ]+                        ]+                    ]+                  , [ Para [ Str "Secret" , Space , Str "exit" ] ]+                  ]+              ]+            , [ Para+                  [ Str "Level"+                  , Space+                  , Str "1"+                  , Space+                  , Str "list"+                  , Space+                  , Str "item"+                  ]+              , BulletList+                  [ [ Para+                        [ Str "Level"+                        , Space+                        , Str "2"+                        , Space+                        , Str "list"+                        , Space+                        , Str "item"+                        ]+                    , BulletList+                        [ [ Para+                              [ Str "Level"+                              , Space+                              , Str "3"+                              , Space+                              , Str "list"+                              , Space+                              , Str "item"+                              ]+                          , BulletList+                              [ [ Para+                                    [ Str "Level"+                                    , Space+                                    , Str "4"+                                    , Space+                                    , Str "list"+                                    , Space+                                    , Str "item"+                                    ]+                                , BulletList+                                    [ [ Para+                                          [ Str "Level"+                                          , Space+                                          , Str "5"+                                          , Space+                                          , Str "list"+                                          , Space+                                          , Str "item"+                                          ]+                                      , BulletList+                                          [ [ Para [ Str "etc." ] ] ]+                                      ]+                                    ]+                                ]+                              ]+                          ]+                        ]+                    ]+                  ]+              ]+            , [ Para+                  [ Str "Level"+                  , Space+                  , Str "1"+                  , Space+                  , Str "list"+                  , Space+                  , Str "item"+                  ]+              ]+            ]+        ]+      ]+  , BulletList+      [ [ Para [ Str "one" ] ]+      , [ Para [ Str "two" ] ]+      , [ Para [ Str "three" ] ]+      ]+  , OrderedList+      ( 1 , DefaultStyle , DefaultDelim )+      [ [ Para [ Str "Protons" ] ]+      , [ Para [ Str "Electrons" ] ]+      , [ Para [ Str "Neutrons" ] ]+      ]+  , OrderedList+      ( 1 , DefaultStyle , DefaultDelim )+      [ [ Para [ Str "Protons" ] ]+      , [ Para [ Str "Electrons" ] ]+      , [ Para [ Str "Neutrons" ] ]+      ]+  , Para+      [ Str "Start" , Space , Str "with" , Space , Str "4:" ]+  , OrderedList+      ( 4 , DefaultStyle , DefaultDelim )+      [ [ Para [ Str "Step" , Space , Str "four" ] ]+      , [ Para [ Str "Step" , Space , Str "five" ] ]+      , [ Para [ Str "Step" , Space , Str "six" ] ]+      ]+  , Para [ Str "or" ]+  , OrderedList+      ( 4 , DefaultStyle , DefaultDelim )+      [ [ Para [ Str "Step" , Space , Str "four" ] ]+      , [ Para [ Str "Step" , Space , Str "five" ] ]+      , [ Para [ Str "Step" , Space , Str "six" ] ]+      ]+  , Para [ Str "Reversed:" ]+  , Div+      ( "" , [] , [ ( "options" , "reversed" ) ] )+      [ Div+          ( "" , [ "title" ] , [] )+          [ Para+              [ Str "Parts"+              , Space+              , Str "of"+              , Space+              , Str "an"+              , Space+              , Str "atom"+              ]+          ]+      , OrderedList+          ( 1 , DefaultStyle , DefaultDelim )+          [ [ Para [ Str "Protons" ] ]+          , [ Para [ Str "Electrons" ] ]+          , [ Para [ Str "Neutrons" ] ]+          ]+      ]+  , Para [ Str "Nested" ]+  , OrderedList+      ( 1 , DefaultStyle , DefaultDelim )+      [ [ Para [ Str "Step" , Space , Str "1" ] ]+      , [ Para [ Str "Step" , Space , Str "2" ]+        , OrderedList+            ( 1 , DefaultStyle , DefaultDelim )+            [ [ Para [ Str "Step" , Space , Str "2a" ] ]+            , [ Para [ Str "Step" , Space , Str "2b" ] ]+            ]+        ]+      , [ Para [ Str "Step" , Space , Str "3" ] ]+      ]+  , Para [ Str "Mixed" , Space , Str "nested" ]+  , OrderedList+      ( 1 , DefaultStyle , DefaultDelim )+      [ [ Para [ Str "Linux" ]+        , BulletList+            [ [ Para [ Str "Fedora" ] ]+            , [ Para [ Str "Ubuntu" ] ]+            , [ Para [ Str "Slackware" ] ]+            ]+        ]+      , [ Para [ Str "BSD" ]+        , BulletList+            [ [ Para [ Str "FreeBSD" ] ] , [ Para [ Str "NetBSD" ] ] ]+        ]+      ]+  , Para [ Str "With" , Space , Str "spacing" ]+  , OrderedList+      ( 1 , DefaultStyle , DefaultDelim )+      [ [ Para [ Str "Linux" ]+        , BulletList+            [ [ Para [ Str "Fedora" ] ]+            , [ Para [ Str "Ubuntu" ] ]+            , [ Para [ Str "Slackware" ] ]+            ]+        ]+      , [ Para [ Str "BSD" ]+        , BulletList+            [ [ Para [ Str "FreeBSD" ] ] , [ Para [ Str "NetBSD" ] ] ]+        ]+      ]+  , Para+      [ Str "With" , Space , Str "number" , Space , Str "styles" ]+  , OrderedList+      ( 5 , LowerRoman , DefaultDelim )+      [ [ Para [ Str "Five" ] ]+      , [ Para [ Str "Six" ]+        , OrderedList+            ( 1 , LowerAlpha , DefaultDelim )+            [ [ Para [ Str "a" ] ]+            , [ Para [ Str "b" ] ]+            , [ Para [ Str "c" ] ]+            ]+        ]+      , [ Para [ Str "Seven" ] ]+      ]+  , Para [ Str "Checklist" ]+  , BulletList+      [ [ Para [ Str "\9746" , Space , Str "checked" ] ]+      , [ Para+            [ Str "\9746" , Space , Str "also" , Space , Str "checked" ]+        ]+      , [ Para+            [ Str "\9744" , Space , Str "not" , Space , Str "checked" ]+        ]+      , [ Para+            [ Str "normal" , Space , Str "list" , Space , Str "item" ]+        ]+      ]+  , Para+      [ Str "Separate"+      , Space+      , Str "lists"+      , Space+      , Str "with"+      , Space+      , Str "block"+      , Space+      , Str "attribute"+      ]+  , BulletList+      [ [ Para [ Str "Apples" ] ]+      , [ Para [ Str "Oranges" ]+        , OrderedList+            ( 1 , DefaultStyle , DefaultDelim )+            [ [ Para [ Str "Wash" ] ] , [ Para [ Str "Slice" ] ] ]+        ]+      ]+  , Para [ Str "Multiline" , Space , Str "items" ]+  , BulletList+      [ [ Para+            [ Str "Blah"+            , Space+            , Str "blah."+            , SoftBreak+            , Str "Blah"+            , Space+            , Str "blah."+            ]+        ]+      , [ Para+            [ Str "The"+            , Space+            , Str "document"+            , Space+            , Str "header"+            , Space+            , Str "in"+            , Space+            , Str "AsciiDoc"+            , Space+            , Str "is"+            , Space+            , Str "optional."+            , SoftBreak+            , Str "If"+            , Space+            , Str "present,"+            , Space+            , Str "it"+            , Space+            , Str "must"+            , Space+            , Str "start"+            , Space+            , Str "with"+            , Space+            , Str "a"+            , Space+            , Str "document"+            , Space+            , Str "title."+            ]+        ]+      ]+  , BulletList+      [ [ Para+            [ Str "Optional"+            , Space+            , Str "author"+            , Space+            , Str "and"+            , Space+            , Str "revision"+            , Space+            , Str "information"+            , Space+            , Str "lines"+            , SoftBreak+            , Str "immediately"+            , Space+            , Str "follow"+            , Space+            , Str "the"+            , Space+            , Str "document"+            , Space+            , Str "title."+            ]+        ]+      ]+  , BulletList+      [ [ Para+            [ Str "The"+            , Space+            , Str "document"+            , Space+            , Str "header"+            , Space+            , Str "must"+            , Space+            , Str "be"+            , Space+            , Str "separated"+            , Space+            , Str "from"+            , SoftBreak+            , Str "the"+            , Space+            , Str "remainder"+            , Space+            , Str "of"+            , Space+            , Str "the"+            , Space+            , Str "document"+            , Space+            , Str "by"+            , Space+            , Str "one"+            , Space+            , Str "or"+            , Space+            , Str "more"+            , SoftBreak+            , Str "empty"+            , Space+            , Str "lines"+            , Space+            , Str "and"+            , Space+            , Str "it"+            , Space+            , Str "cannot"+            , Space+            , Str "contain"+            , Space+            , Str "empty"+            , Space+            , Str "lines."+            ]+        ]+      ]+  , Para [ Str "Complex" , Space , Str "item" ]+  , BulletList+      [ [ Para+            [ Str "The"+            , Space+            , Str "header"+            , Space+            , Str "in"+            , Space+            , Str "AsciiDoc"+            , Space+            , Str "must"+            , Space+            , Str "start"+            , Space+            , Str "with"+            , Space+            , Str "a"+            , Space+            , Str "document"+            , Space+            , Str "title."+            ]+        , CodeBlock ( "" , [] , [] ) "= Document Title"+        , Para+            [ Str "Keep"+            , Space+            , Str "in"+            , Space+            , Str "mind"+            , Space+            , Str "that"+            , Space+            , Str "the"+            , Space+            , Str "header"+            , Space+            , Str "is"+            , Space+            , Str "optional."+            ]+        ]+      , [ Para+            [ Str "Optional"+            , Space+            , Str "author"+            , Space+            , Str "and"+            , Space+            , Str "revision"+            , Space+            , Str "information"+            , Space+            , Str "lines"+            , Space+            , Str "immediately"+            , Space+            , Str "follow"+            , Space+            , Str "the"+            , Space+            , Str "document"+            , SoftBreak+            , Str "title."+            ]+        , CodeBlock+            ( "" , [] , [] )+            "= Document Title\nDoc Writer <doc.writer@asciidoc.org>\nv1.0, 2022-01-01"+        ]+      , [ Para [ Str "Second" , Space , Str "item" ] ]+      ]+  , Para+      [ Str "Empty"+      , Space+      , Str "principle"+      , Space+      , Str "element:"+      ]+  , OrderedList+      ( 1 , DefaultStyle , DefaultDelim )+      [ [ Para [] , CodeBlock ( "" , [] , [] ) "test" ] ]+  , Header 2 ( "_table" , [] , [] ) [ Str "Table" ]+  , Header+      3+      ( "_simple_with_column_specs" , [] , [] )+      [ Str "Simple"+      , Space+      , Str "with"+      , Space+      , Str "column"+      , Space+      , Str "specs"+      ]+  , Table+      ( "" , [] , [] )+      (Caption Nothing [])+      [ ( AlignDefault , ColWidth 0.375 )+      , ( AlignDefault , ColWidth 0.25 )+      , ( AlignDefault , ColWidth 0.375 )+      ]+      (TableHead ( "" , [] , [] ) [])+      [ TableBody+          ( "" , [] , [] )+          (RowHeadColumns 0)+          []+          [ Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "This"+                      , Space+                      , Str "content"+                      , Space+                      , Str "is"+                      , Space+                      , Str "placed"+                      , Space+                      , Str "in"+                      , Space+                      , Str "the"+                      , Space+                      , Str "first"+                      , Space+                      , Str "cell"+                      , Space+                      , Str "of"+                      , Space+                      , Str "column"+                      , Space+                      , Str "1"+                      ]+                  ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "This"+                      , Space+                      , Str "line"+                      , Space+                      , Str "starts"+                      , Space+                      , Str "with"+                      , Space+                      , Str "a"+                      , Space+                      , Str "vertical"+                      , Space+                      , Str "bar"+                      , Space+                      , Str "so"+                      , Space+                      , Str "this"+                      , Space+                      , Str "content"+                      , Space+                      , Str "is"+                      , Space+                      , Str "placed"+                      , Space+                      , Str "in"+                      , Space+                      , Str "a"+                      , Space+                      , Str "new"+                      , Space+                      , Str "cell"+                      , Space+                      , Str "in"+                      , SoftBreak+                      , Str "column"+                      , Space+                      , Str "2"+                      ]+                  ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "When"+                      , Space+                      , Str "the"+                      , Space+                      , Str "processor"+                      , Space+                      , Str "encounters"+                      , Space+                      , Str "a"+                      , Space+                      , Str "whitespace"+                      , Space+                      , Str "followed"+                      , Space+                      , Str "by"+                      , Space+                      , Str "a"+                      , Space+                      , Str "vertical"+                      , Space+                      , Str "bar"+                      , Space+                      , Str "it"+                      , SoftBreak+                      , Str "ends"+                      , Space+                      , Str "the"+                      , Space+                      , Str "previous"+                      , Space+                      , Str "cell"+                      , Space+                      , Str "and"+                      , Space+                      , Str "starts"+                      , Space+                      , Str "a"+                      , Space+                      , Str "new"+                      , Space+                      , Str "cell"+                      ]+                  ]+              ]+          ]+      ]+      (TableFoot ( "" , [] , [] ) [])+  , Header+      3+      ( "_repeated_column_in_specs" , [] , [] )+      [ Str "Repeated"+      , Space+      , Str "column"+      , Space+      , Str "in"+      , Space+      , Str "specs"+      ]+  , Table+      ( "" , [] , [] )+      (Caption Nothing [])+      [ ( AlignDefault , ColWidthDefault )+      , ( AlignDefault , ColWidthDefault )+      ]+      (TableHead ( "" , [] , [] ) [])+      [ TableBody+          ( "" , [] , [] )+          (RowHeadColumns 0)+          []+          [ Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignRight+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Strong+                          [ Str "This"+                          , Space+                          , Str "cell\8217s"+                          , Space+                          , Str "specifier"+                          , Space+                          , Str "indicates"+                          , Space+                          , Str "that"+                          , Space+                          , Str "this"+                          , Space+                          , Str "cell\8217s"+                          , Space+                          , Str "content"+                          , Space+                          , Str "is"+                          , Space+                          , Str "right-aligned"+                          , Space+                          , Str "and"+                          , Space+                          , Str "bold."+                          ]+                      ]+                  ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "The"+                      , Space+                      , Str "cell"+                      , Space+                      , Str "specifier"+                      , Space+                      , Str "on"+                      , Space+                      , Str "this"+                      , Space+                      , Str "cell"+                      , Space+                      , Str "hasn\8217t"+                      , Space+                      , Str "been"+                      , Space+                      , Str "set"+                      , Space+                      , Str "explicitly,"+                      , Space+                      , Str "so"+                      , Space+                      , Str "the"+                      , Space+                      , Str "default"+                      , SoftBreak+                      , Str "properties"+                      , Space+                      , Str "are"+                      , Space+                      , Str "applied."+                      ]+                  ]+              ]+          ]+      ]+      (TableFoot ( "" , [] , [] ) [])+  , Header+      3+      ( "_simple_without_column_specs" , [] , [] )+      [ Str "Simple"+      , Space+      , Str "without"+      , Space+      , Str "column"+      , Space+      , Str "specs"+      ]+  , Table+      ( "" , [] , [] )+      (Caption Nothing [])+      [ ( AlignDefault , ColWidthDefault )+      , ( AlignDefault , ColWidthDefault )+      ]+      (TableHead+         ( "" , [] , [] )+         [ Row+             ( "" , [] , [] )+             [ Cell+                 ( "" , [] , [] )+                 AlignDefault+                 (RowSpan 1)+                 (ColSpan 1)+                 [ Plain+                     [ Str "Column"+                     , Space+                     , Str "1,"+                     , Space+                     , Str "header"+                     , Space+                     , Str "row"+                     ]+                 ]+             , Cell+                 ( "" , [] , [] )+                 AlignDefault+                 (RowSpan 1)+                 (ColSpan 1)+                 [ Plain+                     [ Str "Column"+                     , Space+                     , Str "2,"+                     , Space+                     , Str "header"+                     , Space+                     , Str "row"+                     ]+                 ]+             ]+         ])+      [ TableBody+          ( "" , [] , [] )+          (RowHeadColumns 0)+          []+          [ Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "Cell"+                      , Space+                      , Str "in"+                      , Space+                      , Str "column"+                      , Space+                      , Str "1,"+                      , Space+                      , Str "row"+                      , Space+                      , Str "2"+                      ]+                  ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "Cell"+                      , Space+                      , Str "in"+                      , Space+                      , Str "column"+                      , Space+                      , Str "2,"+                      , Space+                      , Str "row"+                      , Space+                      , Str "2"+                      ]+                  ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "Cell"+                      , Space+                      , Str "in"+                      , Space+                      , Str "column"+                      , Space+                      , Str "1,"+                      , Space+                      , Str "row"+                      , Space+                      , Str "3"+                      ]+                  ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "Cell"+                      , Space+                      , Str "in"+                      , Space+                      , Str "column"+                      , Space+                      , Str "2,"+                      , Space+                      , Str "row"+                      , Space+                      , Str "3"+                      ]+                  ]+              ]+          ]+      ]+      (TableFoot ( "" , [] , [] ) [])+  , Header+      3+      ( "_with_caption" , [] , [] )+      [ Str "With" , Space , Str "caption" ]+  , Table+      ( "" , [] , [] )+      (Caption+         Nothing+         [ Plain+             [ Str "My" , Space , Str "cool" , Space , Str "table." ]+         ])+      [ ( AlignDefault , ColWidthDefault )+      , ( AlignDefault , ColWidthDefault )+      ]+      (TableHead+         ( "" , [] , [] )+         [ Row+             ( "" , [] , [] )+             [ Cell+                 ( "" , [] , [] )+                 AlignDefault+                 (RowSpan 1)+                 (ColSpan 1)+                 [ Plain+                     [ Str "Column"+                     , Space+                     , Str "1,"+                     , Space+                     , Str "header"+                     , Space+                     , Str "row"+                     ]+                 ]+             , Cell+                 ( "" , [] , [] )+                 AlignDefault+                 (RowSpan 1)+                 (ColSpan 1)+                 [ Plain+                     [ Str "Column"+                     , Space+                     , Str "2,"+                     , Space+                     , Str "header"+                     , Space+                     , Str "row"+                     ]+                 ]+             ]+         ])+      [ TableBody+          ( "" , [] , [] )+          (RowHeadColumns 0)+          []+          [ Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "Cell"+                      , Space+                      , Str "in"+                      , Space+                      , Str "column"+                      , Space+                      , Str "1,"+                      , Space+                      , Str "row"+                      , Space+                      , Str "2"+                      ]+                  ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "Cell"+                      , Space+                      , Str "in"+                      , Space+                      , Str "column"+                      , Space+                      , Str "2,"+                      , Space+                      , Str "row"+                      , Space+                      , Str "2"+                      ]+                  ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "Cell"+                      , Space+                      , Str "in"+                      , Space+                      , Str "column"+                      , Space+                      , Str "1,"+                      , Space+                      , Str "row"+                      , Space+                      , Str "3"+                      ]+                  ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "Cell"+                      , Space+                      , Str "in"+                      , Space+                      , Str "column"+                      , Space+                      , Str "2,"+                      , Space+                      , Str "row"+                      , Space+                      , Str "3"+                      ]+                  ]+              ]+          ]+      ]+      (TableFoot ( "" , [] , [] ) [])+  , Header+      3+      ( "_no_header" , [] , [] )+      [ Str "No" , Space , Str "header" ]+  , Para+      [ Str "By"+      , Space+      , Str "default"+      , Space+      , Str "the"+      , Space+      , Str "first"+      , Space+      , Str "line"+      , Space+      , Str "should"+      , Space+      , Str "turn"+      , Space+      , Str "into"+      , Space+      , Str "the"+      , Space+      , Str "header,"+      , Space+      , Str "but"+      , Space+      , Str "this"+      , SoftBreak+      , Str "can"+      , Space+      , Str "be"+      , Space+      , Str "disabled:"+      ]+  , Table+      ( "" , [] , [] )+      (Caption Nothing [])+      [ ( AlignDefault , ColWidthDefault )+      , ( AlignDefault , ColWidthDefault )+      ]+      (TableHead ( "" , [] , [] ) [])+      [ TableBody+          ( "" , [] , [] )+          (RowHeadColumns 0)+          []+          [ Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "Cell"+                      , Space+                      , Str "in"+                      , Space+                      , Str "column"+                      , Space+                      , Str "1,"+                      , Space+                      , Str "row"+                      , Space+                      , Str "1"+                      ]+                  ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "Cell"+                      , Space+                      , Str "in"+                      , Space+                      , Str "column"+                      , Space+                      , Str "2,"+                      , Space+                      , Str "row"+                      , Space+                      , Str "1"+                      ]+                  ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "Cell"+                      , Space+                      , Str "in"+                      , Space+                      , Str "column"+                      , Space+                      , Str "1,"+                      , Space+                      , Str "row"+                      , Space+                      , Str "2"+                      ]+                  ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "Cell"+                      , Space+                      , Str "in"+                      , Space+                      , Str "column"+                      , Space+                      , Str "2,"+                      , Space+                      , Str "row"+                      , Space+                      , Str "2"+                      ]+                  ]+              ]+          ]+      ]+      (TableFoot ( "" , [] , [] ) [])+  , Para+      [ Str "And"+      , Space+      , Str "also"+      , Space+      , Str "explicitly"+      , Space+      , Str "enabled:"+      ]+  , Table+      ( "" , [] , [] )+      (Caption Nothing [])+      [ ( AlignDefault , ColWidthDefault )+      , ( AlignDefault , ColWidthDefault )+      ]+      (TableHead ( "" , [] , [] ) [])+      [ TableBody+          ( "" , [] , [] )+          (RowHeadColumns 0)+          []+          [ Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "Cell" , Space , Str "A1" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "Cell" , Space , Str "B1" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "Cell" , Space , Str "A2" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "Cell" , Space , Str "B2" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "Cell" , Space , Str "A3" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "Cell" , Space , Str "B3" ] ]+              ]+          ]+      ]+      (TableFoot ( "" , [] , [] ) [])+  , Header 3 ( "_footer" , [] , [] ) [ Str "Footer" ]+  , Table+      ( "" , [] , [] )+      (Caption Nothing [])+      [ ( AlignDefault , ColWidth 0.4 )+      , ( AlignDefault , ColWidth 0.4 )+      , ( AlignDefault , ColWidth 0.2 )+      ]+      (TableHead ( "" , [] , [] ) [])+      [ TableBody+          ( "" , [] , [] )+          (RowHeadColumns 0)+          []+          [ Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "Column"+                      , Space+                      , Str "1,"+                      , Space+                      , Str "header"+                      , Space+                      , Str "row"+                      ]+                  ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "Column"+                      , Space+                      , Str "2,"+                      , Space+                      , Str "header"+                      , Space+                      , Str "row"+                      ]+                  ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "Column"+                      , Space+                      , Str "3,"+                      , Space+                      , Str "header"+                      , Space+                      , Str "row"+                      ]+                  ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "Cell"+                      , Space+                      , Str "in"+                      , Space+                      , Str "column"+                      , Space+                      , Str "1,"+                      , Space+                      , Str "row"+                      , Space+                      , Str "2"+                      ]+                  ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "Cell"+                      , Space+                      , Str "in"+                      , Space+                      , Str "column"+                      , Space+                      , Str "2,"+                      , Space+                      , Str "row"+                      , Space+                      , Str "2"+                      ]+                  ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "Cell"+                      , Space+                      , Str "in"+                      , Space+                      , Str "column"+                      , Space+                      , Str "3,"+                      , Space+                      , Str "row"+                      , Space+                      , Str "2"+                      ]+                  ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "Column"+                      , Space+                      , Str "1,"+                      , Space+                      , Str "footer"+                      , Space+                      , Str "row"+                      ]+                  ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "Column"+                      , Space+                      , Str "2,"+                      , Space+                      , Str "footer"+                      , Space+                      , Str "row"+                      ]+                  ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "Column"+                      , Space+                      , Str "3,"+                      , Space+                      , Str "footer"+                      , Space+                      , Str "row"+                      ]+                  ]+              ]+          ]+      ]+      (TableFoot ( "" , [] , [] ) [])+  , Para [ Str "or" ]+  , Table+      ( "" , [] , [] )+      (Caption Nothing [])+      [ ( AlignDefault , ColWidthDefault )+      , ( AlignDefault , ColWidthDefault )+      ]+      (TableHead ( "" , [] , [] ) [])+      [ TableBody+          ( "" , [] , [] )+          (RowHeadColumns 0)+          []+          [ Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "Column"+                      , Space+                      , Str "1,"+                      , Space+                      , Str "header"+                      , Space+                      , Str "row"+                      ]+                  ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "Column"+                      , Space+                      , Str "2,"+                      , Space+                      , Str "header"+                      , Space+                      , Str "row"+                      ]+                  ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "Cell"+                      , Space+                      , Str "in"+                      , Space+                      , Str "column"+                      , Space+                      , Str "1,"+                      , Space+                      , Str "row"+                      , Space+                      , Str "2"+                      ]+                  ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "Cell"+                      , Space+                      , Str "in"+                      , Space+                      , Str "column"+                      , Space+                      , Str "2,"+                      , Space+                      , Str "row"+                      , Space+                      , Str "2"+                      ]+                  ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "Cell"+                      , Space+                      , Str "in"+                      , Space+                      , Str "column"+                      , Space+                      , Str "1,"+                      , Space+                      , Str "row"+                      , Space+                      , Str "3"+                      ]+                  ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "Cell"+                      , Space+                      , Str "in"+                      , Space+                      , Str "column"+                      , Space+                      , Str "2,"+                      , Space+                      , Str "row"+                      , Space+                      , Str "3"+                      ]+                  ]+              ]+          ]+      ]+      (TableFoot+         ( "" , [] , [] )+         [ Row+             ( "" , [] , [] )+             [ Cell+                 ( "" , [] , [] )+                 AlignDefault+                 (RowSpan 1)+                 (ColSpan 1)+                 [ Plain+                     [ Str "Column"+                     , Space+                     , Str "1,"+                     , Space+                     , Str "footer"+                     , Space+                     , Str "row"+                     ]+                 ]+             , Cell+                 ( "" , [] , [] )+                 AlignDefault+                 (RowSpan 1)+                 (ColSpan 1)+                 [ Plain+                     [ Str "Column"+                     , Space+                     , Str "2,"+                     , Space+                     , Str "footer"+                     , Space+                     , Str "row"+                     ]+                 ]+             ]+         ])+  , Header 3 ( "_alignment" , [] , [] ) [ Str "Alignment" ]+  , Table+      ( "" , [] , [] )+      (Caption Nothing [])+      [ ( AlignDefault , ColWidthDefault )+      , ( AlignDefault , ColWidthDefault )+      ]+      (TableHead ( "" , [] , [] ) [])+      [ TableBody+          ( "" , [] , [] )+          (RowHeadColumns 0)+          []+          [ Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "Column" , Space , Str "Name" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "Column" , Space , Str "Name" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignCenter+                  (RowSpan 1)+                  (ColSpan 2)+                  [ Plain+                      [ Str "This"+                      , Space+                      , Str "cell"+                      , Space+                      , Str "spans"+                      , Space+                      , Str "two"+                      , Space+                      , Str "columns,"+                      , Space+                      , Str "and"+                      , Space+                      , Str "its"+                      , Space+                      , Str "content"+                      , Space+                      , Str "is"+                      , Space+                      , Str "horizontally"+                      , Space+                      , Str "centered"+                      , Space+                      , Str "because"+                      , Space+                      , Str "the"+                      , SoftBreak+                      , Str "cell"+                      , Space+                      , Str "specifier"+                      , Space+                      , Str "includes"+                      , Space+                      , Str "the"+                      , Space+                      , Code ( "" , [] , [] ) "^"+                      , Space+                      , Str "operator."+                      ]+                  ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignCenter+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "This"+                      , Space+                      , Str "content"+                      , Space+                      , Str "is"+                      , Space+                      , Str "duplicated"+                      , Space+                      , Str "in"+                      , Space+                      , Str "two"+                      , Space+                      , Str "adjacent"+                      , Space+                      , Str "columns."+                      , SoftBreak+                      , Str "Its"+                      , Space+                      , Str "content"+                      , Space+                      , Str "is"+                      , Space+                      , Str "horizontally"+                      , Space+                      , Str "centered"+                      , Space+                      , Str "because"+                      , Space+                      , Str "the"+                      , Space+                      , Str "cell"+                      , Space+                      , Str "specifier"+                      , SoftBreak+                      , Str "includes"+                      , Space+                      , Str "the"+                      , Space+                      , Code ( "" , [] , [] ) "^"+                      , Space+                      , Str "operator."+                      ]+                  ]+              , Cell+                  ( "" , [] , [] )+                  AlignCenter+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "This"+                      , Space+                      , Str "content"+                      , Space+                      , Str "is"+                      , Space+                      , Str "duplicated"+                      , Space+                      , Str "in"+                      , Space+                      , Str "two"+                      , Space+                      , Str "adjacent"+                      , Space+                      , Str "columns."+                      , SoftBreak+                      , Str "Its"+                      , Space+                      , Str "content"+                      , Space+                      , Str "is"+                      , Space+                      , Str "horizontally"+                      , Space+                      , Str "centered"+                      , Space+                      , Str "because"+                      , Space+                      , Str "the"+                      , Space+                      , Str "cell"+                      , Space+                      , Str "specifier"+                      , SoftBreak+                      , Str "includes"+                      , Space+                      , Str "the"+                      , Space+                      , Code ( "" , [] , [] ) "^"+                      , Space+                      , Str "operator."+                      ]+                  ]+              ]+          ]+      ]+      (TableFoot ( "" , [] , [] ) [])+  , Header+      3+      ( "_multiple_paragraphs_in_cells" , [] , [] )+      [ Str "Multiple"+      , Space+      , Str "paragraphs"+      , Space+      , Str "in"+      , Space+      , Str "cells"+      ]+  , Table+      ( "" , [] , [] )+      (Caption Nothing [])+      [ ( AlignDefault , ColWidthDefault ) ]+      (TableHead ( "" , [] , [] ) [])+      [ TableBody+          ( "" , [] , [] )+          (RowHeadColumns 0)+          []+          [ Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Para+                      [ Str "Single"+                      , Space+                      , Str "paragraph"+                      , Space+                      , Str "on"+                      , Space+                      , Str "row"+                      , Space+                      , Str "1"+                      ]+                  ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Para+                      [ Str "First"+                      , Space+                      , Str "paragraph"+                      , Space+                      , Str "on"+                      , Space+                      , Str "row"+                      , Space+                      , Str "2"+                      ]+                  , Para+                      [ Str "Second"+                      , Space+                      , Str "paragraph"+                      , Space+                      , Str "on"+                      , Space+                      , Str "row"+                      , Space+                      , Str "2"+                      ]+                  ]+              ]+          ]+      ]+      (TableFoot ( "" , [] , [] ) [])+  , Header+      3+      ( "_complex_table" , [] , [] )+      [ Str "Complex" , Space , Str "table" ]+  , Table+      ( "" , [] , [] )+      (Caption Nothing [])+      [ ( AlignDefault , ColWidthDefault )+      , ( AlignDefault , ColWidthDefault )+      ]+      (TableHead ( "" , [] , [] ) [])+      [ TableBody+          ( "" , [] , [] )+          (RowHeadColumns 0)+          []+          [ Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignRight+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Para+                      [ Code ( "" , [] , [] ) "This"+                      , Space+                      , Code ( "" , [] , [] ) "content"+                      , Space+                      , Code ( "" , [] , [] ) "is"+                      , Space+                      , Code ( "" , [] , [] ) "duplicated"+                      , Space+                      , Code ( "" , [] , [] ) "across"+                      , Space+                      , Code ( "" , [] , [] ) "two"+                      , Space+                      , Code ( "" , [] , [] ) "columns."+                      ]+                  , Para+                      [ Code ( "" , [] , [] ) "It"+                      , Space+                      , Code ( "" , [] , [] ) "is"+                      , Space+                      , Code ( "" , [] , [] ) "aligned"+                      , Space+                      , Code ( "" , [] , [] ) "right"+                      , Space+                      , Code ( "" , [] , [] ) "horizontally."+                      ]+                  , Para+                      [ Code ( "" , [] , [] ) "And"+                      , Space+                      , Code ( "" , [] , [] ) "it"+                      , Space+                      , Code ( "" , [] , [] ) "is"+                      , Space+                      , Code ( "" , [] , [] ) "monospaced."+                      ]+                  ]+              , Cell+                  ( "" , [] , [] )+                  AlignRight+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Para+                      [ Code ( "" , [] , [] ) "This"+                      , Space+                      , Code ( "" , [] , [] ) "content"+                      , Space+                      , Code ( "" , [] , [] ) "is"+                      , Space+                      , Code ( "" , [] , [] ) "duplicated"+                      , Space+                      , Code ( "" , [] , [] ) "across"+                      , Space+                      , Code ( "" , [] , [] ) "two"+                      , Space+                      , Code ( "" , [] , [] ) "columns."+                      ]+                  , Para+                      [ Code ( "" , [] , [] ) "It"+                      , Space+                      , Code ( "" , [] , [] ) "is"+                      , Space+                      , Code ( "" , [] , [] ) "aligned"+                      , Space+                      , Code ( "" , [] , [] ) "right"+                      , Space+                      , Code ( "" , [] , [] ) "horizontally."+                      ]+                  , Para+                      [ Code ( "" , [] , [] ) "And"+                      , Space+                      , Code ( "" , [] , [] ) "it"+                      , Space+                      , Code ( "" , [] , [] ) "is"+                      , Space+                      , Code ( "" , [] , [] ) "monospaced."+                      ]+                  ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignCenter+                  (RowSpan 3)+                  (ColSpan 1)+                  [ Para+                      [ Strong+                          [ Str "This"+                          , Space+                          , Str "cell"+                          , Space+                          , Str "spans"+                          , Space+                          , Str "3"+                          , Space+                          , Str "rows."+                          , Space+                          , Str "The"+                          , Space+                          , Str "content"+                          , Space+                          , Str "is"+                          , Space+                          , Str "centered"+                          , Space+                          , Str "horizontally,"+                          , Space+                          , Str "aligned"+                          , Space+                          , Str "to"+                          , Space+                          , Str "the"+                          , Space+                          , Str "bottom"+                          , Space+                          , Str "of"+                          , Space+                          , Str "the"+                          , Space+                          , Str "cell,"+                          , Space+                          , Str "and"+                          , Space+                          , Str "strong."+                          ]+                      ]+                  ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Para+                      [ Emph+                          [ Str "This"+                          , Space+                          , Str "content"+                          , Space+                          , Str "is"+                          , Space+                          , Str "emphasized."+                          ]+                      ]+                  ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ CodeBlock+                      ( "" , [] , [] )+                      "This content is aligned to the top of the cell and literal.\n\n"+                  ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ CodeBlock+                      ( "" , [] , [] )+                      "puts \"This is a source block!\""+                  ]+              ]+          ]+      ]+      (TableFoot ( "" , [] , [] ) [])+  , Header+      3+      ( "_column_styles" , [] , [] )+      [ Str "Column" , Space , Str "styles" ]+  , Table+      ( "" , [] , [] )+      (Caption Nothing [])+      [ ( AlignDefault , ColWidthDefault )+      , ( AlignDefault , ColWidthDefault )+      ]+      (TableHead ( "" , [] , [] ) [])+      [ TableBody+          ( "" , [] , [] )+          (RowHeadColumns 0)+          []+          [ Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Code ( "" , [] , [] ) "monospace" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Code ( "" , [] , [] ) "mono" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "default" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Code ( "" , [] , [] ) "mono" ] ]+              ]+          ]+      ]+      (TableFoot ( "" , [] , [] ) [])+  , Header+      3+      ( "_block_elements_in_cells" , [] , [] )+      [ Str "Block"+      , Space+      , Str "elements"+      , Space+      , Str "in"+      , Space+      , Str "cells"+      ]+  , Table+      ( "" , [] , [] )+      (Caption Nothing [])+      [ ( AlignDefault , ColWidthDefault )+      , ( AlignDefault , ColWidthDefault )+      ]+      (TableHead ( "" , [] , [] ) [])+      [ TableBody+          ( "" , [] , [] )+          (RowHeadColumns 0)+          []+          [ Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Para [ Str "Normal" , Space , Str "Style" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Para [ Str "AsciiDoc" , Space , Str "Style" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Para+                      [ Str "This"+                      , Space+                      , Str "cell"+                      , Space+                      , Str "isn\8217t"+                      , Space+                      , Str "prefixed"+                      , Space+                      , Str "with"+                      , Space+                      , Str "an"+                      , Space+                      , Code ( "" , [] , [] ) "a"+                      , Str ","+                      , Space+                      , Str "so"+                      , Space+                      , Str "the"+                      , Space+                      , Str "processor"+                      , Space+                      , Str "doesn\8217t"+                      , Space+                      , Str "interpret"+                      , Space+                      , Str "the"+                      , SoftBreak+                      , Str "following"+                      , Space+                      , Str "lines"+                      , Space+                      , Str "as"+                      , Space+                      , Str "an"+                      , Space+                      , Str "AsciiDoc"+                      , Space+                      , Str "list."+                      ]+                  , Para+                      [ Str "*"+                      , Space+                      , Str "List"+                      , Space+                      , Str "item"+                      , Space+                      , Str "1"+                      , SoftBreak+                      , Str "*"+                      , Space+                      , Str "List"+                      , Space+                      , Str "item"+                      , Space+                      , Str "2"+                      , SoftBreak+                      , Str "*"+                      , Space+                      , Str "List"+                      , Space+                      , Str "item"+                      , Space+                      , Str "3"+                      ]+                  ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Para+                      [ Str "This"+                      , Space+                      , Str "cell"+                      , Space+                      , Str "is"+                      , Space+                      , Str "prefixed"+                      , Space+                      , Str "with"+                      , Space+                      , Str "an"+                      , Space+                      , Code ( "" , [] , [] ) "a"+                      , Str ","+                      , Space+                      , Str "so"+                      , Space+                      , Str "the"+                      , Space+                      , Str "processor"+                      , Space+                      , Str "interprets"+                      , Space+                      , Str "the"+                      , Space+                      , Str "following"+                      , Space+                      , Str "lines"+                      , SoftBreak+                      , Str "as"+                      , Space+                      , Str "an"+                      , Space+                      , Str "AsciiDoc"+                      , Space+                      , Str "list."+                      ]+                  , BulletList+                      [ [ Para+                            [ Str "List"+                            , Space+                            , Str "item"+                            , Space+                            , Str "1"+                            ]+                        ]+                      , [ Para+                            [ Str "List"+                            , Space+                            , Str "item"+                            , Space+                            , Str "2"+                            ]+                        ]+                      , [ Para+                            [ Str "List"+                            , Space+                            , Str "item"+                            , Space+                            , Str "3"+                            ]+                        ]+                      ]+                  ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Para+                      [ Str "This"+                      , Space+                      , Str "cell"+                      , Space+                      , Str "isn\8217t"+                      , Space+                      , Str "prefixed"+                      , Space+                      , Str "with"+                      , Space+                      , Str "an"+                      , Space+                      , Code ( "" , [] , [] ) "a"+                      , Str ","+                      , Space+                      , Str "so"+                      , Space+                      , Str "the"+                      , Space+                      , Str "processor"+                      , Space+                      , Str "doesn\8217t"+                      , Space+                      , Str "interpret"+                      , Space+                      , Str "the"+                      , Space+                      , Str "listing"+                      , SoftBreak+                      , Str "block"+                      , Space+                      , Str "delimiters"+                      , Space+                      , Str "or"+                      , Space+                      , Str "the"+                      , Space+                      , Code ( "" , [] , [] ) "source"+                      , Space+                      , Str "style."+                      ]+                  , Para+                      [ Str "----"+                      , SoftBreak+                      , Str "import"+                      , Space+                      , Str "os"+                      , SoftBreak+                      , Str "print"+                      , Space+                      , Str "(\"%s\""+                      , Space+                      , Str "%(os.uname()))"+                      , SoftBreak+                      , Str "----"+                      ]+                  ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Para+                      [ Str "This"+                      , Space+                      , Str "cell"+                      , Space+                      , Str "is"+                      , Space+                      , Str "prefixed"+                      , Space+                      , Str "with"+                      , Space+                      , Str "an"+                      , Space+                      , Code ( "" , [] , [] ) "a"+                      , Str ","+                      , Space+                      , Str "so"+                      , Space+                      , Str "the"+                      , Space+                      , Str "listing"+                      , Space+                      , Str "block"+                      , Space+                      , Str "is"+                      , Space+                      , Str "processed"+                      , Space+                      , Str "and"+                      , Space+                      , Str "rendered"+                      , SoftBreak+                      , Str "according"+                      , Space+                      , Str "to"+                      , Space+                      , Str "the"+                      , Space+                      , Code ( "" , [] , [] ) "source"+                      , Space+                      , Str "style"+                      , Space+                      , Str "rules."+                      ]+                  , CodeBlock+                      ( "" , [ "python" ] , [] )+                      "import os\nprint \"%s\" %(os.uname())"+                  ]+              ]+          ]+      ]+      (TableFoot ( "" , [] , [] ) [])+  , Header+      3+      ( "_col_and_rowspan" , [] , [] )+      [ Str "Col" , Space , Str "and" , Space , Str "rowspan" ]+  , Table+      ( "" , [] , [] )+      (Caption Nothing [])+      [ ( AlignDefault , ColWidthDefault )+      , ( AlignDefault , ColWidthDefault )+      , ( AlignDefault , ColWidthDefault )+      ]+      (TableHead ( "" , [] , [] ) [])+      [ TableBody+          ( "" , [] , [] )+          (RowHeadColumns 0)+          []+          [ Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "Column"+                      , Space+                      , Str "1,"+                      , Space+                      , Str "header"+                      , Space+                      , Str "row"+                      ]+                  ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "Column"+                      , Space+                      , Str "2,"+                      , Space+                      , Str "header"+                      , Space+                      , Str "row"+                      ]+                  ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "Column"+                      , Space+                      , Str "3,"+                      , Space+                      , Str "header"+                      , Space+                      , Str "row"+                      ]+                  ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 2)+                  (ColSpan 2)+                  [ Plain+                      [ Str "This"+                      , Space+                      , Str "cell"+                      , Space+                      , Str "spans"+                      , Space+                      , Str "2"+                      , Space+                      , Str "cols"+                      , Space+                      , Str "and"+                      , Space+                      , Str "2"+                      , Space+                      , Str "rows"+                      ]+                  ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "Cell"+                      , Space+                      , Str "in"+                      , Space+                      , Str "column"+                      , Space+                      , Str "3,"+                      , Space+                      , Str "row"+                      , Space+                      , Str "2"+                      ]+                  ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "Cell"+                      , Space+                      , Str "in"+                      , Space+                      , Str "column"+                      , Space+                      , Str "3,"+                      , Space+                      , Str "row"+                      , Space+                      , Str "3"+                      ]+                  ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 3)+                  [ Plain+                      [ Str "Cell"+                      , Space+                      , Str "in"+                      , Space+                      , Str "column"+                      , Space+                      , Str "1-3,"+                      , Space+                      , Str "row"+                      , Space+                      , Str "4"+                      ]+                  ]+              ]+          ]+      ]+      (TableFoot ( "" , [] , [] ) [])+  , Header+      3+      ( "_csv_table" , [] , [] )+      [ Str "CSV" , Space , Str "table" ]+  , Table+      ( "" , [] , [] )+      (Caption Nothing [])+      [ ( AlignDefault , ColWidthDefault )+      , ( AlignDefault , ColWidthDefault )+      , ( AlignDefault , ColWidthDefault )+      ]+      (TableHead+         ( "" , [] , [] )+         [ Row+             ( "" , [] , [] )+             [ Cell+                 ( "" , [] , [] )+                 AlignDefault+                 (RowSpan 1)+                 (ColSpan 1)+                 [ Plain [ Str "Artist" ] ]+             , Cell+                 ( "" , [] , [] )+                 AlignDefault+                 (RowSpan 1)+                 (ColSpan 1)+                 [ Plain [ Str "Track" ] ]+             , Cell+                 ( "" , [] , [] )+                 AlignDefault+                 (RowSpan 1)+                 (ColSpan 1)+                 [ Plain [ Str "Genre" ] ]+             ]+         ])+      [ TableBody+          ( "" , [] , [] )+          (RowHeadColumns 0)+          []+          [ Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "Baauer" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "Harlem" , Space , Str "Shake" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "Hip" , Space , Str "Hop" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "The" , Space , Str "Lumineers" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "Ho" , Space , Str "Hey" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "Folk" , Space , Str "Rock" ] ]+              ]+          ]+      ]+      (TableFoot ( "" , [] , [] ) [])+  , Para [ Str "or" ]+  , Table+      ( "" , [] , [] )+      (Caption Nothing [])+      [ ( AlignDefault , ColWidthDefault )+      , ( AlignDefault , ColWidthDefault )+      , ( AlignDefault , ColWidthDefault )+      ]+      (TableHead ( "" , [] , [] ) [])+      [ TableBody+          ( "" , [] , [] )+          (RowHeadColumns 0)+          []+          [ Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "Artist" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "Track" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "Genre" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "Baauer" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "Harlem" , Space , Str "Shake" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "Hip" , Space , Str "Hop" ] ]+              ]+          ]+      ]+      (TableFoot ( "" , [] , [] ) [])+  , Header+      3+      ( "_dsv_table" , [] , [] )+      [ Str "DSV" , Space , Str "table" ]+  , Table+      ( "" , [] , [] )+      (Caption Nothing [])+      [ ( AlignDefault , ColWidthDefault )+      , ( AlignDefault , ColWidthDefault )+      , ( AlignDefault , ColWidthDefault )+      ]+      (TableHead ( "" , [] , [] ) [])+      [ TableBody+          ( "" , [] , [] )+          (RowHeadColumns 0)+          []+          [ Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "a" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "b" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "c" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "d" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "e" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "f" ] ]+              ]+          ]+      ]+      (TableFoot ( "" , [] , [] ) [])+  , Para [ Str "or" ]+  , Table+      ( "" , [] , [] )+      (Caption Nothing [])+      [ ( AlignDefault , ColWidthDefault )+      , ( AlignDefault , ColWidthDefault )+      , ( AlignDefault , ColWidthDefault )+      ]+      (TableHead ( "" , [] , [] ) [])+      [ TableBody+          ( "" , [] , [] )+          (RowHeadColumns 0)+          []+          [ Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "Artist" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "Track" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "Genre" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "Robyn" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "Indestructible" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "Dance" ] ]               ]           ]       ]
test/command/10390.md view
@@ -11,12 +11,12 @@ <caption>Overview of basic table markup</caption> <thead> <tr>-<th><p>Key</p></th>+<th>Key</th> </tr> </thead> <tbody> <tr>-<td><p>Value</p></td>+<td>Value</td> </tr> </tbody> </table>
test/command/10490.md view
@@ -18,31 +18,31 @@ <table> <thead> <tr>-<th style=""><p>Witness program version</p></th>-<th colspan="4" style=""><p>Hash size</p></th>+<th style="">Witness program version</th>+<th colspan="4" style="">Hash size</th> </tr> </thead> <tbody> <tr>-<td><p>Mainnet</p></td>-<td><p>Testnet</p></td>-<td><p>Mainnet</p></td>-<td><p>Testnet</p></td>+<td>Mainnet</td>+<td>Testnet</td>+<td>Mainnet</td>+<td>Testnet</td> <td></td> </tr> <tr>-<td><p>0</p></td>-<td><p>p2</p></td>-<td><p>QW</p></td>-<td><p>7Xh</p></td>-<td><p>T7n</p></td>+<td>0</td>+<td>p2</td>+<td>QW</td>+<td>7Xh</td>+<td>T7n</td> </tr> <tr>-<td><p>1</p></td>-<td><p>p4</p></td>-<td><p>QY</p></td>-<td><p>7Xq</p></td>-<td><p>T7w</p></td>+<td>1</td>+<td>p4</td>+<td>QY</td>+<td>7Xq</td>+<td>T7w</td> </tr> </tbody> </table>
test/command/10643.md view
@@ -1,5 +1,5 @@ ```-% pandoc -f html -t native+% pandoc -f html+raw_html -t native <p>A<style></style>B</p> ^D [ Para@@ -8,4 +8,14 @@     , Str "B"     ] ]+```++The style element's contents should not be parsed as text+even when raw_html is disabled:++```+% pandoc -f html -t native+<p>A<style>p { color: red; }</style>B</p>+^D+[ Para [ Str "AB" ] ] ```
test/command/10965.md view
@@ -7,7 +7,6 @@ #block[ #set text(lang: "en"); This text should be in English.- ] ``` @@ -20,7 +19,6 @@ #block[ #set text(lang: "fr"); Ce texte devrait être en français.- ] ``` @@ -33,7 +31,6 @@ #block[ #set text(lang: "de"); Dieser Text sollte auf Deutsch sein.- ] ``` @@ -45,6 +42,5 @@ ^D #block[ This should not have lang set.- ] ```
test/command/11583.md view
@@ -11,6 +11,5 @@ + item + item + item- ] ```
test/command/1166.md view
@@ -24,15 +24,15 @@ </thead> <tbody> <tr>-<td>1</td>-<td>Second column of row 1.</td>+<td><p>1</p></td>+<td><p>Second column of row 1.</p></td> </tr> <tr>-<td>2</td>-<td>Second column of row 2. Second line of paragraph.</td>+<td><p>2</p></td>+<td><p>Second column of row 2. Second line of paragraph.</p></td> </tr> <tr>-<td>3</td>+<td><p>3</p></td> <td><ul> <li>Second column of row 3.</li> <li>Second item in bullet list (row 3, column 2).</li>@@ -40,7 +40,7 @@ </tr> <tr> <td></td>-<td>Row 4; column 1 will be empty.</td>+<td><p>Row 4; column 1 will be empty.</p></td> </tr> </tbody> </table>
test/command/11794.md view
@@ -6,7 +6,6 @@ ^D #block[ Hello- ] <foo>  ```
+ test/command/11849.md view
@@ -0,0 +1,42 @@+```+% pandoc -t typst+| A | B |+|---|---|+| 1 | 2 |++: Example {#table-id}+^D+#figure(+  align(center)[#table(+    columns: 2,+    align: (auto,auto,),+    table.header([A], [B],),+    table.hline(),+    [1], [2],+  )]+  , caption: [Example]+  , kind: table+  )+<table-id>++```++```+% pandoc -t typst+| A | B |+|---|---|+| 1 | 2 |++: Example {#table-id .typst:no-figure}+^D+#table(+  columns: 2,+  align: (auto,auto,),+  table.header([A], [B],),+  table.hline(),+  [1], [2],+)+<table-id>++```+
+ test/command/11858.md view
@@ -0,0 +1,11 @@+```+% pandoc -f markdown+rebase_relative_paths -t html+[Zotero item](zotero://select/library/items/ABCD1234)+[Mail](mailto:a@b.c)+^D+<p><a href="zotero://select/library/items/ABCD1234">Zotero item</a> <a+href="mailto:a@b.c">Mail</a></p>++```++
+ test/command/11869.md view
@@ -0,0 +1,8 @@+A link's title is written to docx as a ScreenTip (`w:tooltip`) and read back.++```+% pandoc -f markdown -t docx -o - | pandoc -f docx -t markdown+See [the report](https://example.org/r.pdf "Annual report, PDF, 2 MB").+^D+See [the report](https://example.org/r.pdf "Annual report, PDF, 2 MB").+```
+ test/command/11888.md view
@@ -0,0 +1,46 @@+```+% pandoc -f markdown+rebase_relative_paths command/11888/section1/lab.md command/11888/section2/lab.md command/11888/section3/lab.md+^D+<h1 id="heading-1">Heading 1</h1>+<figure>+<img src="command/11888/section1/media/Pic1_1.png" alt="pic 1" />+<figcaption aria-hidden="true">pic 1</figcaption>+</figure>+<blockquote>+<p>a blockquote</p>+<figure>+<img src="command/11888/section1/media/Pic1_2.png" alt="pic 2" />+<figcaption aria-hidden="true">pic 2</figcaption>+</figure>+<p>with an image reference</p>+<figure>+<img src="command/11888/section1/media/Pic1_3.png" alt="pic 3" />+<figcaption aria-hidden="true">pic 3</figcaption>+</figure>+</blockquote>+<h1 id="heading-2">Heading 2</h1>+<figure>+<img src="command/11888/section2/media/Pic2_1.png" alt="pic 3" />+<figcaption aria-hidden="true">pic 3</figcaption>+</figure>+<ol type="1">+<li><p>A list</p>+<figure>+<img src="command/11888/section2/media/Pic2_2.png" alt="pic 4" />+<figcaption aria-hidden="true">pic 4</figcaption>+</figure></li>+</ol>+<h1 id="heading-3">Heading 3</h1>+<figure>+<img src="command/11888/section3/media/Pic3_1.png" alt="pic 5" />+<figcaption aria-hidden="true">pic 5</figcaption>+</figure>+<ol type="1">+<li><p>A list</p>+<figure>+<img src="command/11888/section3/media/Pic3_2.png" alt="pic 6" />+<figcaption aria-hidden="true">pic 6</figcaption>+</figure></li>+</ol>++```
+ test/command/11888/section1/lab.md view
@@ -0,0 +1,11 @@+# Heading 1++![pic 1](media/Pic1_1.png)++> a blockquote+>+> ![pic 2](media/Pic1_2.png)+>+> with an image reference+>+> ![pic 3](media/Pic1_3.png)
+ test/command/11888/section2/lab.md view
@@ -0,0 +1,8 @@+# Heading 2++![pic 3](media/Pic2_1.png)++1. A list++    ![pic 4](media/Pic2_2.png)+
+ test/command/11888/section3/lab.md view
@@ -0,0 +1,7 @@+# Heading 3++![pic 5](media/Pic3_1.png)++1. A list+    +    ![pic 6](media/Pic3_2.png)
+ test/command/11897.md view
@@ -0,0 +1,67 @@+```+% pandoc -f typst -t native+One cluster: @a @b.++Split by a newline:+@c+@d.+^D+[ Para+    [ Str "One"+    , Space+    , Str "cluster:"+    , Space+    , Cite+        [ Citation+            { citationId = "a"+            , citationPrefix = []+            , citationSuffix = []+            , citationMode = NormalCitation+            , citationNoteNum = 0+            , citationHash = 0+            }+        , Citation+            { citationId = "b"+            , citationPrefix = []+            , citationSuffix = []+            , citationMode = NormalCitation+            , citationNoteNum = 0+            , citationHash = 0+            }+        ]+        [ Str "[a]" , Str "[b]" ]+    , Str "."+    ]+, Para+    [ Str "Split"+    , Space+    , Str "by"+    , Space+    , Str "a"+    , Space+    , Str "newline:"+    , SoftBreak+    , Cite+        [ Citation+            { citationId = "c"+            , citationPrefix = []+            , citationSuffix = []+            , citationMode = NormalCitation+            , citationNoteNum = 0+            , citationHash = 0+            }+        , Citation+            { citationId = "d"+            , citationPrefix = []+            , citationSuffix = []+            , citationMode = NormalCitation+            , citationNoteNum = 0+            , citationHash = 0+            }+        ]+        [ Str "[c]" , Str "[d]" ]+    , Str "."+    ]+]++```
test/command/2378.md view
@@ -14,7 +14,7 @@ \begin{longtable}[]{@{}ll@{}} \caption{a table}\tabularnewline \toprule\noalign{}-x & y\footnote{a footnote} \\+x & y\footnotemark{} \\ \midrule\noalign{} \endfirsthead \toprule\noalign{}@@ -25,4 +25,5 @@ \endlastfoot 1 & 2 \\ \end{longtable}+\footnotetext{a footnote} ```
test/command/2606.md view
@@ -7,7 +7,7 @@ <table> <tbody> <tr>-<td><p>* hello</p></td>+<td>* hello</td> </tr> </tbody> </table>@@ -41,7 +41,7 @@ <table> <tbody> <tr>-<td><p><code>* hello</code></p></td>+<td><code>* hello</code></td> </tr> </tbody> </table>
+ test/command/2623.md view
@@ -0,0 +1,7 @@+```+% pandoc command/2623.odt+^D+<p><em>Italics</em></p>+<p>not <em>italics</em></p>+```+
+ test/command/2623.odt view

binary file changed (absent → 9375 bytes)

test/command/2649.md view
@@ -32,27 +32,27 @@ <table> <tbody> <tr>-<td><p>peildatum Simbase</p></td>-<td><p>november 2005</p></td>-<td colspan="2"><p><strong>uitslagen Flohrgambiet</strong></p></td>+<td>peildatum Simbase</td>+<td>november 2005</td>+<td colspan="2"><strong>uitslagen Flohrgambiet</strong></td> </tr> <tr>-<td><p>totaal aantal partijen Simbase</p></td>-<td><p>7.316.773</p></td>-<td><p>wit wint</p></td>-<td><p>53%</p></td>+<td>totaal aantal partijen Simbase</td>+<td>7.316.773</td>+<td>wit wint</td>+<td>53%</td> </tr> <tr>-<td><p>percentage (en partijen) Flohrgambiet</p></td>-<td><p>0.023 % (1.699)</p></td>-<td><p>zwart wint</p></td>-<td><p>27%</p></td>+<td>percentage (en partijen) Flohrgambiet</td>+<td>0.023 % (1.699)</td>+<td>zwart wint</td>+<td>27%</td> </tr> <tr>-<td><p>percentage Flohrgambiet in aug 2003</p></td>-<td><p>0.035 %</p></td>-<td><p>remise</p></td>-<td><p>20%</p></td>+<td>percentage Flohrgambiet in aug 2003</td>+<td>0.035 %</td>+<td>remise</td>+<td>20%</td> </tr> </tbody> </table>
test/command/2994.md view
@@ -4,7 +4,7 @@ author="Jesse Rosenthal" date="2016-05-09T16:13:00Z"}some text to have a comment []{.comment-end id="0"}on it. ^D-I want [I left a comment.]{.comment-start id="0"+I want [I left a comment.]{.comment-start comment-id="0" author="Jesse Rosenthal" date="2016-05-09T16:13:00Z"}some text to have a-comment []{.comment-end id="0"}on it.+comment []{.comment-end comment-id="0"}on it. ```
test/command/3958.md view
@@ -9,12 +9,12 @@ % pandoc -f latex -t native \texttt{``hi''} ^D-[ Para [ Code ( "" , [] , [] ) "\8216\8216hi\8217\8217" ] ]+[ Para [ Code ( "" , [] , [] ) "``hi''" ] ] ```  ``` % pandoc -f latex -t native \texttt{`hi'} ^D-[ Para [ Code ( "" , [] , [] ) "\8216hi\8217" ] ]+[ Para [ Code ( "" , [] , [] ) "`hi'" ] ] ```
test/command/5367.md view
@@ -22,10 +22,10 @@  \begin{longtable}[]{@{}   >{\centering\arraybackslash}p{(\linewidth - 0\tabcolsep) * \real{0.1667}}@{}}-\caption[Sample table.]{Sample table.\footnote{caption footnote}}\tabularnewline+\caption[Sample table.]{Sample table.\footnotemark{}}\tabularnewline \toprule\noalign{} \begin{minipage}[b]{\linewidth}\centering-Fruit\footnote{header footnote}+Fruit\footnotemark{} \end{minipage} \\ \midrule\noalign{} \endfirsthead@@ -37,8 +37,14 @@ \endhead \bottomrule\noalign{} \endlastfoot-Bans\footnote{table cell footnote} \\+Bans\footnotemark{} \\ \end{longtable}+\addtocounter{footnote}{-2}+\footnotetext{caption footnote}+\addtocounter{footnote}{1}+\footnotetext{header footnote}+\addtocounter{footnote}{1}+\footnotetext{table cell footnote}  dolly\footnote{doc footnote} ```
test/command/6549.md view
@@ -9,13 +9,13 @@ <caption>Test table</caption> <thead> <tr>-<th>Column1</th>-<th>Column2</th>+<th><p>Column1</p></th>+<th><p>Column2</p></th> </tr> </thead> <tbody> <tr>-<td>Data1</td>+<td><p>Data1</p></td> <td><ul> <li>data1</li> <li>data2</li>
test/command/6959.md view
@@ -5,8 +5,9 @@ author="Mike" date="2020-12-17T17:39:00Z"} This is the content being commented on [[]{.comment-end id="2"}]{.comment-end id="1"} ^D-[This is the comment]{.comment-start id="1" author="Mike"-date="2020-12-17T16:53:00Z"} [Here is my reply]{.comment-start id="2"-author="Mike" date="2020-12-17T17:39:00Z"} This is the content being-commented on [[]{.comment-end id="2"}]{.comment-end id="1"}+[This is the comment]{.comment-start comment-id="1" author="Mike"+date="2020-12-17T16:53:00Z"} [Here is my reply]{.comment-start+comment-id="2" author="Mike" date="2020-12-17T17:39:00Z"} This is the+content being commented on [[]{.comment-end+comment-id="2"}]{.comment-end comment-id="1"} ```
test/command/6992.md view
@@ -23,7 +23,7 @@                AlignDefault                (RowSpan 1)                (ColSpan 4)-               [ Para [ Str "template" , Space , Str "request" ] ]+               [ Plain [ Str "template" , Space , Str "request" ] ]            ]        ])     [ TableBody@@ -37,25 +37,25 @@                 AlignDefault                 (RowSpan 1)                 (ColSpan 1)-                [ Para [ Str "mode" ] ]+                [ Plain [ Str "mode" ] ]             , Cell                 ( "" , [] , [] )                 AlignDefault                 (RowSpan 1)                 (ColSpan 1)-                [ Para [ Str "No" ] ]+                [ Plain [ Str "No" ] ]             , Cell                 ( "" , [] , [] )                 AlignDefault                 (RowSpan 1)                 (ColSpan 1)-                [ Para [ Str "String" ] ]+                [ Plain [ Str "String" ] ]             , Cell                 ( "" , [] , [] )                 AlignDefault                 (RowSpan 1)                 (ColSpan 1)-                [ Para+                [ Plain                     [ Str "MUST"                     , Space                     , Str "be"
test/command/7214.md view
@@ -6,7 +6,7 @@ |abc defghij | def      | xyz              | +------------+----------+------------------+ ^D-<table style="width:60%;">+<table style="width:59%;"> <colgroup> <col style="width: 18%" /> <col style="width: 15%" />
test/command/7436.md view
@@ -9,10 +9,10 @@ .. include:: command/three.txt ^D [ CodeBlock-    ( "" , [ "" ] , [ ( "code" , "" ) ] )+    ( "" , [] , [ ( "code" , "" ) ] )     "1st line.\n2nd line.\n3rd line.\n" , CodeBlock-    ( "" , [ "" ] , [ ( "literal" , "" ) ] )+    ( "" , [] , [ ( "literal" , "" ) ] )     "1st line.\n2nd line.\n3rd line.\n" , Para     [ Str "1st"
test/command/8110.md view
@@ -19,21 +19,21 @@ <table> <thead> <tr>-<th><p>Header text</p></th>-<th><p>Header text</p></th>-<th><p>Header text</p></th>+<th>Header text</th>+<th>Header text</th>+<th>Header text</th> </tr> </thead> <tbody> <tr>-<td><p>Example</p></td>-<td><p>Example</p></td>-<td><p>Example</p></td>+<td>Example</td>+<td>Example</td>+<td>Example</td> </tr> <tr>-<td><p>Example</p></td>-<td><p>Example</p></td>-<td><p>Example</p></td>+<td>Example</td>+<td>Example</td>+<td>Example</td> </tr> </tbody> </table>
test/command/8257.md view
@@ -10,7 +10,7 @@ | Sum  | 8£    | +======+=======+ ^D-<table style="width:21%;">+<table style="width:20%;"> <colgroup> <col style="width: 9%" /> <col style="width: 11%" />
test/command/8659.md view
@@ -22,7 +22,7 @@ | a   |  b  | c   | d         | +-----+-----+-----+-----------+ ^D-<table style="width:42%;">+<table style="width:41%;"> <colgroup> <col style="width: 8%" /> <col style="width: 8%" />@@ -44,10 +44,10 @@ </thead> <tbody> <tr>-<td style="text-align: left;">a</td>-<td>b</td>-<td style="text-align: right;">c</td>-<td style="text-align: right;">d</td>+<td style="text-align: left;"><p>a</p></td>+<td><p>b</p></td>+<td style="text-align: right;"><p>c</p></td>+<td style="text-align: right;"><p>d</p></td> </tr> </tbody> </table>
test/command/8770-block.md view
@@ -32,7 +32,7 @@ role="doc-backlink">↩︎</a></p></li> </ol> </aside>-<table style="width:17%;">+<table style="width:16%;"> <caption>Sample table.<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a></caption> <colgroup>
test/command/8770-document.md view
@@ -25,7 +25,7 @@ <h1 id="section-1">Section 1</h1> <p>hello<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a></p>-<table style="width:17%;">+<table style="width:16%;"> <caption>Sample table.<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a></caption> <colgroup>
test/command/8770-section.md view
@@ -25,7 +25,7 @@ <h1 id="section-1">Section 1</h1> <p>hello<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a></p>-<table style="width:17%;">+<table style="width:16%;"> <caption>Sample table.<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a></caption> <colgroup>
test/command/8948.md view
@@ -25,7 +25,7 @@ ![minimal](command/minimal.svg) ^D <figure>-<img role="img" aria-label="minimal" src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PgoKPHN2ZyB2aWV3Qm94PSItLjMzMyAtLjMzMyA0ODAgMTUwIiBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjojZmZmZmZmMDAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CiAgICA8cGF0aCBkPSJNIDAgMzUuNSBMIDYuNSAyMi41IEwgMTYgMzcgTCAyMyAyNCBMIDM0LjggNDMuNyBMIDQyLjUgMzAgTCA1MC4zIDQ3IEwgNTkuNyAyNy43IEwgNjkgNDcgTCA4NSAxNy43IEwgOTguMyAzOSBMIDExMyA5LjcgTCAxMjcuNyA0Mi4zIEwgMTM2LjMgMjMuNyBMIDE0NyA0NC4zIEwgMTU4LjMgMjAuMyBMIDE3MC4zIDQwLjMgTCAxNzcuNyAyNS43IEwgMTg5LjcgNDMgTCAxOTkuNyAyMSBMIDIwNy43IDM1IEwgMjE5IDExIEwgMjMzIDM3IEwgMjQwLjMgMjMuNyBMIDI1MSA0MyBMIDI2MyAxOC4zIEwgMjcyLjcgMzMuMyBMIDI4MyAxMCBMIDI5NSAzMi4zIEwgMzAxLjMgMjMgTCAzMTEuNyAzNyBMIDMyMy43IDcuNyBMIDMzOS4zIDM5IEwgMzQ2LjMgMjUuNyBMIDM1Ni4zIDQyLjMgTCAzNjkuNyAxNSBMIDM3Ni4zIDI1LjcgTCAzODQgOSBMIDM5MyAyOC4zIEwgNDAwLjMgMTkgTCA0MTEuNyAzOC4zIEwgNDIxIDIxIEwgNDM0LjMgNDMgTCA0NDUgMjUgTCA0NTMgMzYuMyBMIDQ2NC4zIDE4LjMgTCA0NzYuMiA0MC4zIEwgNDgwIDMzLjUgTCA0ODAgMjE1IEwgMCAyMTUgTCAwIDM1LjUgWiIgZmlsbD0iIzE3NTcyMCIvPgo8L3N2Zz4K" alt="minimal" />+<img src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PgoKPHN2ZyB2aWV3Qm94PSItLjMzMyAtLjMzMyA0ODAgMTUwIiBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjojZmZmZmZmMDAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CiAgICA8cGF0aCBkPSJNIDAgMzUuNSBMIDYuNSAyMi41IEwgMTYgMzcgTCAyMyAyNCBMIDM0LjggNDMuNyBMIDQyLjUgMzAgTCA1MC4zIDQ3IEwgNTkuNyAyNy43IEwgNjkgNDcgTCA4NSAxNy43IEwgOTguMyAzOSBMIDExMyA5LjcgTCAxMjcuNyA0Mi4zIEwgMTM2LjMgMjMuNyBMIDE0NyA0NC4zIEwgMTU4LjMgMjAuMyBMIDE3MC4zIDQwLjMgTCAxNzcuNyAyNS43IEwgMTg5LjcgNDMgTCAxOTkuNyAyMSBMIDIwNy43IDM1IEwgMjE5IDExIEwgMjMzIDM3IEwgMjQwLjMgMjMuNyBMIDI1MSA0MyBMIDI2MyAxOC4zIEwgMjcyLjcgMzMuMyBMIDI4MyAxMCBMIDI5NSAzMi4zIEwgMzAxLjMgMjMgTCAzMTEuNyAzNyBMIDMyMy43IDcuNyBMIDMzOS4zIDM5IEwgMzQ2LjMgMjUuNyBMIDM1Ni4zIDQyLjMgTCAzNjkuNyAxNSBMIDM3Ni4zIDI1LjcgTCAzODQgOSBMIDM5MyAyOC4zIEwgNDAwLjMgMTkgTCA0MTEuNyAzOC4zIEwgNDIxIDIxIEwgNDM0LjMgNDMgTCA0NDUgMjUgTCA0NTMgMzYuMyBMIDQ2NC4zIDE4LjMgTCA0NzYuMiA0MC4zIEwgNDgwIDMzLjUgTCA0ODAgMjE1IEwgMCAyMTUgTCAwIDM1LjUgWiIgZmlsbD0iIzE3NTcyMCIvPgo8L3N2Zz4K" alt="minimal" /> <figcaption aria-hidden="true">minimal</figcaption> </figure> ```
test/command/9387.md view
@@ -7,7 +7,6 @@ ^D #block[ test- ] #label("my label") See #link(label("my label"))[my label]. 
test/command/9467.md view
@@ -2,6 +2,6 @@ % pandoc --embed-resources ![](command/9467.svg) ^D-<p><img role="img" src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTkiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB2ZXJzaW9uPSIxLjEiIGlkPSJzdmcyIiB3aWR0aD0iMTkxLjU2MjY3IiBoZWlnaHQ9IjE1MS43MTIwMSIgdmlld0JveD0iMCAwIDE5MS41NjI2NyAxNTEuNzEyMDEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPGRlZnMgaWQ9ImRlZnM2Ij4KICAgIDxjbGlwUGF0aCBjbGlwUGF0aFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgaWQ9ImNsaXBQYXRoMjQiPgogICAgICA8cGF0aCBkPSJNIDU2LjY5MzYyLDAgMTEzLjM4NzI0LDExMy4zODcyNCAxNzAuMDgwODYsMCBaIiBpZD0icGF0aDIyIiAvPgogICAgPC9jbGlwUGF0aD4KICAgIDxjbGlwUGF0aCBjbGlwUGF0aFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgaWQ9ImNsaXBQYXRoMzgiPgogICAgICA8cGF0aCBkPSJNIDAsMCBIIDEwMCBWIDEwMCBIIDAgWiIgaWQ9InBhdGgzNiIgLz4KICAgIDwvY2xpcFBhdGg+CiAgICA8cmFkaWFsR3JhZGllbnQgZng9IjUwLjAwMDY0MSIgZnk9IjUwLjAwMDY0MSIgY3g9IjUwLjAwMDY0MSIgY3k9IjUwLjAwMDY0MSIgcj0iNTAuMDAwNjQxIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgaWQ9InJhZGlhbEdyYWRpZW50NDYiPgogICAgICA8c3RvcCBzdHlsZT0ic3RvcC1vcGFjaXR5OjE7c3RvcC1jb2xvcjojZmZmZmZmIiBvZmZzZXQ9IjAiIGlkPSJzdG9wNDAiIC8+CiAgICAgIDxzdG9wIHN0eWxlPSJzdG9wLW9wYWNpdHk6MTtzdG9wLWNvbG9yOiM5OTk5ZmYiIG9mZnNldD0iMjUuMDAwMzIiIGlkPSJzdG9wNDIiIC8+CiAgICAgIDxzdG9wIHN0eWxlPSJzdG9wLW9wYWNpdHk6MTtzdG9wLWNvbG9yOiM5OTk5ZmYiIG9mZnNldD0iNTAuMDAwNjQiIGlkPSJzdG9wNDQiIC8+CiAgICA8L3JhZGlhbEdyYWRpZW50PgogIDwvZGVmcz4KICA8ZyBpZD0iZzgiIHRyYW5zZm9ybT0ibWF0cml4KDEuMzMzMzMzMywwLDAsLTEuMzMzMzMzMywwLDE1MS43MTIpIj4KICAgIDxnIGlkPSJnMTAiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yNi42MDYsMC4xOTkpIj4KICAgICAgPGcgaWQ9ImcxMiI+CiAgICAgICAgPGcgaWQ9ImcxNCI+CiAgICAgICAgICA8ZyBpZD0iZzE2Ij4KICAgICAgICAgICAgPGcgaWQ9ImcxOCI+CiAgICAgICAgICAgICAgPGcgaWQ9ImcyMCIgY2xpcC1wYXRoPSJ1cmwoI2NsaXBQYXRoMjQpIj4KICAgICAgICAgICAgICAgIDxnIGlkPSJnMjYiIHRyYW5zZm9ybT0ibWF0cml4KDIuMjY4MDIsMCwwLDIuMjY4MDIsMTEzLjM4NzI0LDU2LjY5MzYyKSI+CiAgICAgICAgICAgICAgICAgIDxnIGlkPSJnMjgiPgogICAgICAgICAgICAgICAgICAgIDxnIGlkPSJnMzAiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC01MCwtNTApIj4KICAgICAgICAgICAgICAgICAgICAgIDxnIGlkPSJnMzIiPgogICAgICAgICAgICAgICAgICAgICAgICA8ZyBpZD0iZzM0IiBjbGlwLXBhdGg9InVybCgjY2xpcFBhdGgzOCkiPgogICAgICAgICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0gMCwwIEggMTAwIFYgMTAwIEggMCBaIiBzdHlsZT0iZmlsbDp1cmwoI3JhZGlhbEdyYWRpZW50NDYpO3N0cm9rZTpub25lIiBpZD0icGF0aDQ4IiAvPgogICAgICAgICAgICAgICAgICAgICAgICA8L2c+CiAgICAgICAgICAgICAgICAgICAgICA8L2c+CiAgICAgICAgICAgICAgICAgICAgPC9nPgogICAgICAgICAgICAgICAgICA8L2c+CiAgICAgICAgICAgICAgICA8L2c+CiAgICAgICAgICAgICAgPC9nPgogICAgICAgICAgICA8L2c+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik0gNTYuNjkzNjIsMCAxMTMuMzg3MjQsMTEzLjM4NzI0IDE3MC4wODA4NiwwIFoiIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiM5OTk5ZmY7c3Ryb2tlLXdpZHRoOjAuMzk4NTtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDoxMDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLW9wYWNpdHk6MSIgaWQ9InBhdGg1MCIgLz4KICAgICAgICAgICAgPGcgaWQ9Imc1MiI+CiAgICAgICAgICAgICAgPGcgaWQ9Imc1NCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNjMuMzM3LDUuNDU3KSI+CiAgICAgICAgICAgICAgICA8ZyBpZD0iZzU2Ij4KICAgICAgICAgICAgICAgICAgPGcgaWQ9Imc1OCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTM2LjczMSwtNS42NTYpIj4KICAgICAgICAgICAgICAgICAgICA8dGV4dCB4bWw6c3BhY2U9InByZXNlcnZlIiB0cmFuc2Zvcm09Im1hdHJpeCgxLDAsMCwtMSwzNi43MzEsNS42NTYpIiBzdHlsZT0iZm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zaXplOjkuOTYyNnB4O2ZvbnQtZmFtaWx5OkNNU1MxMDstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOkNNU1MxMDt3cml0aW5nLW1vZGU6bHItdGI7ZmlsbDojOTk5OWZmO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiBpZD0idGV4dDYyIj48dHNwYW4geD0iMCA0Ljk4MTI5OTkgOC4zODU1MiAxMy4xNzM1NDYgMTguMzIxMjIyIDIwLjcwMTI4NiAyNS4xMjg2NjYgMzAuMjc2MzQgMzcuMTkxMzgzIDQxLjAxMDA0OCA0Ni4xNTc3MjIgNTAuOTQ1NzQ3IDU2LjA5MzQyMiA2MC41MjA4MDIgNjguOTk1OTg3IDcyLjU5MzQ4MyA3NS45OTc3MDQgNzguMzc3NzY5IDgzLjE2NTc5NCA4OC4zMDM1MDUgOTMuMjg0ODA1IDk1LjY2NDg3MSIgeT0iMCIgaWQ9InRzcGFuNjAiPmdyYWRpZW50c2hhZGVkdHJpYW5nbGU8L3RzcGFuPjwvdGV4dD4KICAgICAgICAgICAgICAgICAgICA8ZyBpZD0iZzY0IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgzNi43MzEsNS42NTYpIiAvPgogICAgICAgICAgICAgICAgICA8L2c+CiAgICAgICAgICAgICAgICA8L2c+CiAgICAgICAgICAgICAgICA8ZyBpZD0iZzY2IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtNjMuMzM3LC01LjQ1NykiIC8+CiAgICAgICAgICAgICAgPC9nPgogICAgICAgICAgICA8L2c+CiAgICAgICAgICA8L2c+CiAgICAgICAgPC9nPgogICAgICA8L2c+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K" /></p>+<p><img src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTkiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB2ZXJzaW9uPSIxLjEiIGlkPSJzdmcyIiB3aWR0aD0iMTkxLjU2MjY3IiBoZWlnaHQ9IjE1MS43MTIwMSIgdmlld0JveD0iMCAwIDE5MS41NjI2NyAxNTEuNzEyMDEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPGRlZnMgaWQ9ImRlZnM2Ij4KICAgIDxjbGlwUGF0aCBjbGlwUGF0aFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgaWQ9ImNsaXBQYXRoMjQiPgogICAgICA8cGF0aCBkPSJNIDU2LjY5MzYyLDAgMTEzLjM4NzI0LDExMy4zODcyNCAxNzAuMDgwODYsMCBaIiBpZD0icGF0aDIyIiAvPgogICAgPC9jbGlwUGF0aD4KICAgIDxjbGlwUGF0aCBjbGlwUGF0aFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgaWQ9ImNsaXBQYXRoMzgiPgogICAgICA8cGF0aCBkPSJNIDAsMCBIIDEwMCBWIDEwMCBIIDAgWiIgaWQ9InBhdGgzNiIgLz4KICAgIDwvY2xpcFBhdGg+CiAgICA8cmFkaWFsR3JhZGllbnQgZng9IjUwLjAwMDY0MSIgZnk9IjUwLjAwMDY0MSIgY3g9IjUwLjAwMDY0MSIgY3k9IjUwLjAwMDY0MSIgcj0iNTAuMDAwNjQxIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgaWQ9InJhZGlhbEdyYWRpZW50NDYiPgogICAgICA8c3RvcCBzdHlsZT0ic3RvcC1vcGFjaXR5OjE7c3RvcC1jb2xvcjojZmZmZmZmIiBvZmZzZXQ9IjAiIGlkPSJzdG9wNDAiIC8+CiAgICAgIDxzdG9wIHN0eWxlPSJzdG9wLW9wYWNpdHk6MTtzdG9wLWNvbG9yOiM5OTk5ZmYiIG9mZnNldD0iMjUuMDAwMzIiIGlkPSJzdG9wNDIiIC8+CiAgICAgIDxzdG9wIHN0eWxlPSJzdG9wLW9wYWNpdHk6MTtzdG9wLWNvbG9yOiM5OTk5ZmYiIG9mZnNldD0iNTAuMDAwNjQiIGlkPSJzdG9wNDQiIC8+CiAgICA8L3JhZGlhbEdyYWRpZW50PgogIDwvZGVmcz4KICA8ZyBpZD0iZzgiIHRyYW5zZm9ybT0ibWF0cml4KDEuMzMzMzMzMywwLDAsLTEuMzMzMzMzMywwLDE1MS43MTIpIj4KICAgIDxnIGlkPSJnMTAiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yNi42MDYsMC4xOTkpIj4KICAgICAgPGcgaWQ9ImcxMiI+CiAgICAgICAgPGcgaWQ9ImcxNCI+CiAgICAgICAgICA8ZyBpZD0iZzE2Ij4KICAgICAgICAgICAgPGcgaWQ9ImcxOCI+CiAgICAgICAgICAgICAgPGcgaWQ9ImcyMCIgY2xpcC1wYXRoPSJ1cmwoI2NsaXBQYXRoMjQpIj4KICAgICAgICAgICAgICAgIDxnIGlkPSJnMjYiIHRyYW5zZm9ybT0ibWF0cml4KDIuMjY4MDIsMCwwLDIuMjY4MDIsMTEzLjM4NzI0LDU2LjY5MzYyKSI+CiAgICAgICAgICAgICAgICAgIDxnIGlkPSJnMjgiPgogICAgICAgICAgICAgICAgICAgIDxnIGlkPSJnMzAiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC01MCwtNTApIj4KICAgICAgICAgICAgICAgICAgICAgIDxnIGlkPSJnMzIiPgogICAgICAgICAgICAgICAgICAgICAgICA8ZyBpZD0iZzM0IiBjbGlwLXBhdGg9InVybCgjY2xpcFBhdGgzOCkiPgogICAgICAgICAgICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0gMCwwIEggMTAwIFYgMTAwIEggMCBaIiBzdHlsZT0iZmlsbDp1cmwoI3JhZGlhbEdyYWRpZW50NDYpO3N0cm9rZTpub25lIiBpZD0icGF0aDQ4IiAvPgogICAgICAgICAgICAgICAgICAgICAgICA8L2c+CiAgICAgICAgICAgICAgICAgICAgICA8L2c+CiAgICAgICAgICAgICAgICAgICAgPC9nPgogICAgICAgICAgICAgICAgICA8L2c+CiAgICAgICAgICAgICAgICA8L2c+CiAgICAgICAgICAgICAgPC9nPgogICAgICAgICAgICA8L2c+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik0gNTYuNjkzNjIsMCAxMTMuMzg3MjQsMTEzLjM4NzI0IDE3MC4wODA4NiwwIFoiIHN0eWxlPSJmaWxsOm5vbmU7c3Ryb2tlOiM5OTk5ZmY7c3Ryb2tlLXdpZHRoOjAuMzk4NTtzdHJva2UtbGluZWNhcDpidXR0O3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDoxMDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLW9wYWNpdHk6MSIgaWQ9InBhdGg1MCIgLz4KICAgICAgICAgICAgPGcgaWQ9Imc1MiI+CiAgICAgICAgICAgICAgPGcgaWQ9Imc1NCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNjMuMzM3LDUuNDU3KSI+CiAgICAgICAgICAgICAgICA8ZyBpZD0iZzU2Ij4KICAgICAgICAgICAgICAgICAgPGcgaWQ9Imc1OCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTM2LjczMSwtNS42NTYpIj4KICAgICAgICAgICAgICAgICAgICA8dGV4dCB4bWw6c3BhY2U9InByZXNlcnZlIiB0cmFuc2Zvcm09Im1hdHJpeCgxLDAsMCwtMSwzNi43MzEsNS42NTYpIiBzdHlsZT0iZm9udC12YXJpYW50Om5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zaXplOjkuOTYyNnB4O2ZvbnQtZmFtaWx5OkNNU1MxMDstaW5rc2NhcGUtZm9udC1zcGVjaWZpY2F0aW9uOkNNU1MxMDt3cml0aW5nLW1vZGU6bHItdGI7ZmlsbDojOTk5OWZmO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiBpZD0idGV4dDYyIj48dHNwYW4geD0iMCA0Ljk4MTI5OTkgOC4zODU1MiAxMy4xNzM1NDYgMTguMzIxMjIyIDIwLjcwMTI4NiAyNS4xMjg2NjYgMzAuMjc2MzQgMzcuMTkxMzgzIDQxLjAxMDA0OCA0Ni4xNTc3MjIgNTAuOTQ1NzQ3IDU2LjA5MzQyMiA2MC41MjA4MDIgNjguOTk1OTg3IDcyLjU5MzQ4MyA3NS45OTc3MDQgNzguMzc3NzY5IDgzLjE2NTc5NCA4OC4zMDM1MDUgOTMuMjg0ODA1IDk1LjY2NDg3MSIgeT0iMCIgaWQ9InRzcGFuNjAiPmdyYWRpZW50c2hhZGVkdHJpYW5nbGU8L3RzcGFuPjwvdGV4dD4KICAgICAgICAgICAgICAgICAgICA8ZyBpZD0iZzY0IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgzNi43MzEsNS42NTYpIiAvPgogICAgICAgICAgICAgICAgICA8L2c+CiAgICAgICAgICAgICAgICA8L2c+CiAgICAgICAgICAgICAgICA8ZyBpZD0iZzY2IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtNjMuMzM3LC01LjQ1NykiIC8+CiAgICAgICAgICAgICAgPC9nPgogICAgICAgICAgICA8L2c+CiAgICAgICAgICA8L2c+CiAgICAgICAgPC9nPgogICAgICA8L2c+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K" /></p>  ```
test/command/9635.md view
@@ -6,7 +6,7 @@  okay ^D-2> [WARNING] Div at _chunk line 1 column 1 unclosed at _chunk line 5 column 1, closing implicitly.+2> [WARNING] Div at line 1 column 1 unclosed at line 5 column 1, closing implicitly. <blockquote> <div class="fence"> <p>that is not closed</p>
test/command/9716.md view
@@ -4,7 +4,7 @@ > A suggestion. ^D [ Div-    ( "" , [ "tip" ] , [] )+    ( "" , [ "alert" , "tip" ] , [] )     [ Div ( "" , [ "title" ] , [] ) [ Para [ Str "Tip" ] ]     , Para [ Str "A" , Space , Str "suggestion." ]     ]
test/command/9807.md view
@@ -6,8 +6,6 @@ *#foo* ^D [ Para-    [ Strong-        [ SoftBreak , Str "bar" , Space , Str "baz" , SoftBreak ]-    ]+    [ Strong [ SoftBreak , Str "bar" , Space , Str "baz" ] ] ] ```
test/command/biblatex-edtf-date.md view
@@ -35,6 +35,7 @@   issued: 2004-04-05   type: article-journal - id: item5-1+  issued: "-001"   type: article-journal - id: item5-2   issued: "-0876"
test/command/completion.md view
@@ -18,7 +18,7 @@     outformats="ansi asciidoc asciidoc_legacy asciidoctor bbcode bbcode_fluxbb bbcode_hubzilla bbcode_phpbb bbcode_steam bbcode_xenforo beamer biblatex bibtex chunkedhtml commonmark commonmark_x context csljson djot docbook docbook4 docbook5 docx dokuwiki dzslides epub epub2 epub3 fb2 gfm haddock html html4 html5 icml ipynb jats jats_archiving jats_articleauthoring jats_publishing jira json latex man markdown markdown_github markdown_mmd markdown_phpextra markdown_strict markua mediawiki ms muse native odt opendocument opml org pdf plain pptx revealjs rst rtf s5 slideous slidy t2t tei texinfo textile typst vimdoc xml xwiki zimwiki"     highlight_styles="pygments tango espresso zenburn kate monochrome breezedark haddock"     math_methods="plain mathml webtex mathjax katex gladtex"-    datafiles="reference.docx reference.odt reference.pptx MANUAL.txt docx/_rels/.rels pptx/_rels/.rels abbreviations creole.lua default.csl docbook-entities.txt docx/[Content_Types].xml docx/docProps/app.xml docx/docProps/core.xml docx/docProps/custom.xml docx/word/_rels/document.xml.rels docx/word/_rels/footnotes.xml.rels docx/word/comments.xml docx/word/document.xml docx/word/fontTable.xml docx/word/footnotes.xml docx/word/numbering.xml docx/word/settings.xml docx/word/styles.xml docx/word/theme/theme1.xml docx/word/webSettings.xml dzslides/template.html epub.css init.lua odt/META-INF/manifest.xml odt/content.xml odt/manifest.rdf odt/meta.xml odt/mimetype odt/styles.xml pptx/[Content_Types].xml pptx/docProps/app.xml pptx/docProps/core.xml pptx/ppt/_rels/presentation.xml.rels pptx/ppt/notesMasters/_rels/notesMaster1.xml.rels pptx/ppt/notesMasters/notesMaster1.xml pptx/ppt/notesSlides/_rels/notesSlide1.xml.rels pptx/ppt/notesSlides/_rels/notesSlide2.xml.rels pptx/ppt/notesSlides/notesSlide1.xml pptx/ppt/notesSlides/notesSlide2.xml pptx/ppt/presProps.xml pptx/ppt/presentation.xml pptx/ppt/slideLayouts/_rels/slideLayout1.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout10.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout11.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout2.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout3.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout4.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout5.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout6.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout7.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout8.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout9.xml.rels pptx/ppt/slideLayouts/slideLayout1.xml pptx/ppt/slideLayouts/slideLayout10.xml pptx/ppt/slideLayouts/slideLayout11.xml pptx/ppt/slideLayouts/slideLayout2.xml pptx/ppt/slideLayouts/slideLayout3.xml pptx/ppt/slideLayouts/slideLayout4.xml pptx/ppt/slideLayouts/slideLayout5.xml pptx/ppt/slideLayouts/slideLayout6.xml pptx/ppt/slideLayouts/slideLayout7.xml pptx/ppt/slideLayouts/slideLayout8.xml pptx/ppt/slideLayouts/slideLayout9.xml pptx/ppt/slideMasters/_rels/slideMaster1.xml.rels pptx/ppt/slideMasters/slideMaster1.xml pptx/ppt/slides/_rels/slide1.xml.rels pptx/ppt/slides/_rels/slide2.xml.rels pptx/ppt/slides/_rels/slide3.xml.rels pptx/ppt/slides/_rels/slide4.xml.rels pptx/ppt/slides/slide1.xml pptx/ppt/slides/slide2.xml pptx/ppt/slides/slide3.xml pptx/ppt/slides/slide4.xml pptx/ppt/tableStyles.xml pptx/ppt/theme/theme1.xml pptx/ppt/theme/theme2.xml pptx/ppt/viewProps.xml templates/affiliations.jats templates/after-header-includes.latex templates/article.jats_publishing templates/common.latex templates/default.ansi templates/default.asciidoc templates/default.bbcode templates/default.beamer templates/default.biblatex templates/default.bibtex templates/default.chunkedhtml templates/default.commonmark templates/default.context templates/default.djot templates/default.docbook4 templates/default.docbook5 templates/default.dokuwiki templates/default.dzslides templates/default.epub2 templates/default.epub3 templates/default.haddock templates/default.html4 templates/default.html5 templates/default.icml templates/default.jats_archiving templates/default.jats_articleauthoring templates/default.jats_publishing templates/default.jira templates/default.latex templates/default.man templates/default.markdown templates/default.markua templates/default.mediawiki templates/default.ms templates/default.muse templates/default.opendocument templates/default.openxml templates/default.opml templates/default.org templates/default.plain templates/default.revealjs templates/default.rst templates/default.rtf templates/default.s5 templates/default.slideous templates/default.slidy templates/default.t2t templates/default.tei templates/default.texinfo templates/default.textile templates/default.typst templates/default.vimdoc templates/default.xwiki templates/default.zimwiki templates/document-metadata.latex templates/font-settings.latex templates/fonts.latex templates/hypersetup.latex templates/passoptions.latex templates/styles.citations.html templates/styles.html templates/template.typst translations/af.yaml translations/alt.yaml translations/am.yaml translations/ar.yaml translations/as.yaml translations/ast.yaml translations/az.yaml translations/be.yaml translations/bg.yaml translations/bn.yaml translations/bo.yaml translations/br.yaml translations/bs.yaml translations/bua.yaml translations/ca.yaml translations/ckb-Arab.yaml translations/ckb-Latn.yaml translations/cs.yaml translations/cu.yaml translations/cy.yaml translations/cz.yaml translations/da.yaml translations/de.yaml translations/dsb.yaml translations/el.yaml translations/en.yaml translations/eo.yaml translations/es-ES.yaml translations/es-MX.yaml translations/es.yaml translations/et.yaml translations/eu.yaml translations/fa.yaml translations/fi.yaml translations/fil.yaml translations/fr.yaml translations/fur.yaml translations/ga.yaml translations/gd.yaml translations/gl.yaml translations/grc.yaml translations/gu.yaml translations/ha.yaml translations/he.yaml translations/hi.yaml translations/hr.yaml translations/hsb.yaml translations/hu.yaml translations/hy.yaml translations/ia.yaml translations/id.yaml translations/is.yaml translations/it.yaml translations/ja.yaml translations/ka.yaml translations/km.yaml translations/kmr-Arab.yaml translations/kmr-Latn.yaml translations/kn.yaml translations/ko.yaml translations/la.yaml translations/lb.yaml translations/lo.yaml translations/lt.yaml translations/lv.yaml translations/mk.yaml translations/ml.yaml translations/mn.yaml translations/mr.yaml translations/ms.yaml translations/nb.yaml translations/nko.yaml translations/nl.yaml translations/nn.yaml translations/no.yaml translations/oc.yaml translations/or.yaml translations/pa.yaml translations/pl.yaml translations/pms.yaml translations/pt-BR.yaml translations/pt-PT.yaml translations/pt.yaml translations/rm.yaml translations/ro.yaml translations/ru.yaml translations/se.yaml translations/si.yaml translations/sk.yaml translations/sl.yaml translations/sq.yaml translations/sr-Cyrl.yaml translations/sr-Latn.yaml translations/sr.yaml translations/sv.yaml translations/ta.yaml translations/te.yaml translations/th.yaml translations/tk.yaml translations/tr.yaml translations/ua.yaml translations/ug.yaml translations/uk.yaml translations/ur.yaml translations/vi.yaml translations/zh-Hans.yaml translations/zh-Hant.yaml"+    datafiles="MANUAL.txt abbreviations creole.lua default.csl docbook-entities.txt docx/[Content_Types].xml docx/_rels/.rels docx/docProps/app.xml docx/docProps/core.xml docx/docProps/custom.xml docx/word/_rels/document.xml.rels docx/word/_rels/footnotes.xml.rels docx/word/comments.xml docx/word/document.xml docx/word/fontTable.xml docx/word/footnotes.xml docx/word/numbering.xml docx/word/settings.xml docx/word/styles.xml docx/word/theme/theme1.xml docx/word/webSettings.xml dzslides/template.html epub.css init.lua odt/META-INF/manifest.xml odt/content.xml odt/manifest.rdf odt/meta.xml odt/mimetype odt/styles.xml pptx/[Content_Types].xml pptx/_rels/.rels pptx/docProps/app.xml pptx/docProps/core.xml pptx/ppt/_rels/presentation.xml.rels pptx/ppt/notesMasters/_rels/notesMaster1.xml.rels pptx/ppt/notesMasters/notesMaster1.xml pptx/ppt/notesSlides/_rels/notesSlide1.xml.rels pptx/ppt/notesSlides/_rels/notesSlide2.xml.rels pptx/ppt/notesSlides/notesSlide1.xml pptx/ppt/notesSlides/notesSlide2.xml pptx/ppt/presProps.xml pptx/ppt/presentation.xml pptx/ppt/slideLayouts/_rels/slideLayout1.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout10.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout11.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout2.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout3.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout4.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout5.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout6.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout7.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout8.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout9.xml.rels pptx/ppt/slideLayouts/slideLayout1.xml pptx/ppt/slideLayouts/slideLayout10.xml pptx/ppt/slideLayouts/slideLayout11.xml pptx/ppt/slideLayouts/slideLayout2.xml pptx/ppt/slideLayouts/slideLayout3.xml pptx/ppt/slideLayouts/slideLayout4.xml pptx/ppt/slideLayouts/slideLayout5.xml pptx/ppt/slideLayouts/slideLayout6.xml pptx/ppt/slideLayouts/slideLayout7.xml pptx/ppt/slideLayouts/slideLayout8.xml pptx/ppt/slideLayouts/slideLayout9.xml pptx/ppt/slideMasters/_rels/slideMaster1.xml.rels pptx/ppt/slideMasters/slideMaster1.xml pptx/ppt/slides/_rels/slide1.xml.rels pptx/ppt/slides/_rels/slide2.xml.rels pptx/ppt/slides/_rels/slide3.xml.rels pptx/ppt/slides/_rels/slide4.xml.rels pptx/ppt/slides/slide1.xml pptx/ppt/slides/slide2.xml pptx/ppt/slides/slide3.xml pptx/ppt/slides/slide4.xml pptx/ppt/tableStyles.xml pptx/ppt/theme/theme1.xml pptx/ppt/theme/theme2.xml pptx/ppt/viewProps.xml reference.docx reference.odt reference.pptx templates/affiliations.jats templates/after-header-includes.latex templates/article.jats_publishing templates/common.latex templates/default.ansi templates/default.asciidoc templates/default.bbcode templates/default.beamer templates/default.biblatex templates/default.bibtex templates/default.chunkedhtml templates/default.commonmark templates/default.context templates/default.djot templates/default.docbook4 templates/default.docbook5 templates/default.dokuwiki templates/default.dzslides templates/default.epub2 templates/default.epub3 templates/default.haddock templates/default.html4 templates/default.html5 templates/default.icml templates/default.jats_archiving templates/default.jats_articleauthoring templates/default.jats_publishing templates/default.jira templates/default.latex templates/default.man templates/default.markdown templates/default.markua templates/default.mediawiki templates/default.ms templates/default.muse templates/default.opendocument templates/default.openxml templates/default.opml templates/default.org templates/default.plain templates/default.revealjs templates/default.rst templates/default.rtf templates/default.s5 templates/default.slideous templates/default.slidy templates/default.t2t templates/default.tei templates/default.texinfo templates/default.textile templates/default.typst templates/default.vimdoc templates/default.xwiki templates/default.zimwiki templates/document-metadata.latex templates/font-settings.latex templates/fonts.latex templates/hypersetup.latex templates/passoptions.latex templates/styles.citations.html templates/styles.html templates/template.typst translations/af.yaml translations/alt.yaml translations/am.yaml translations/ar.yaml translations/as.yaml translations/ast.yaml translations/az.yaml translations/be.yaml translations/bg.yaml translations/bn.yaml translations/bo.yaml translations/br.yaml translations/bs.yaml translations/bua.yaml translations/ca.yaml translations/ckb-Arab.yaml translations/ckb-Latn.yaml translations/cs.yaml translations/cu.yaml translations/cy.yaml translations/cz.yaml translations/da.yaml translations/de.yaml translations/dsb.yaml translations/el.yaml translations/en.yaml translations/eo.yaml translations/es-ES.yaml translations/es-MX.yaml translations/es.yaml translations/et.yaml translations/eu.yaml translations/fa.yaml translations/fi.yaml translations/fil.yaml translations/fr.yaml translations/fur.yaml translations/ga.yaml translations/gd.yaml translations/gl.yaml translations/grc.yaml translations/gu.yaml translations/ha.yaml translations/he.yaml translations/hi.yaml translations/hr.yaml translations/hsb.yaml translations/hu.yaml translations/hy.yaml translations/ia.yaml translations/id.yaml translations/is.yaml translations/it.yaml translations/ja.yaml translations/ka.yaml translations/km.yaml translations/kmr-Arab.yaml translations/kmr-Latn.yaml translations/kn.yaml translations/ko.yaml translations/la.yaml translations/lb.yaml translations/lo.yaml translations/lt.yaml translations/lv.yaml translations/mk.yaml translations/ml.yaml translations/mn.yaml translations/mr.yaml translations/ms.yaml translations/nb.yaml translations/nko.yaml translations/nl.yaml translations/nn.yaml translations/no.yaml translations/oc.yaml translations/or.yaml translations/pa.yaml translations/pl.yaml translations/pms.yaml translations/pt-BR.yaml translations/pt-PT.yaml translations/pt.yaml translations/rm.yaml translations/ro.yaml translations/ru.yaml translations/se.yaml translations/si.yaml translations/sk.yaml translations/sl.yaml translations/sq.yaml translations/sr-Cyrl.yaml translations/sr-Latn.yaml translations/sr.yaml translations/sv.yaml translations/ta.yaml translations/te.yaml translations/th.yaml translations/tk.yaml translations/tr.yaml translations/ua.yaml translations/ug.yaml translations/uk.yaml translations/ur.yaml translations/vi.yaml translations/zh-Hans.yaml translations/zh-Hant.yaml"      case "${prev}" in          -f|-r|--from|--read)@@ -247,7 +247,7 @@     '--list-highlight-styles[List highlighting styles]'     '-D[Format to print template for]:FORMAT:(ansi asciidoc asciidoc_legacy asciidoctor bbcode bbcode_fluxbb bbcode_hubzilla bbcode_phpbb bbcode_steam bbcode_xenforo beamer biblatex bibtex chunkedhtml commonmark commonmark_x context csljson djot docbook docbook4 docbook5 docx dokuwiki dzslides epub epub2 epub3 fb2 gfm haddock html html4 html5 icml ipynb jats jats_archiving jats_articleauthoring jats_publishing jira json latex man markdown markdown_github markdown_mmd markdown_phpextra markdown_strict markua mediawiki ms muse native odt opendocument opml org pdf plain pptx revealjs rst rtf s5 slideous slidy t2t tei texinfo textile typst vimdoc xml xwiki zimwiki)'     '--print-default-template[Format to print template for]:FORMAT:(ansi asciidoc asciidoc_legacy asciidoctor bbcode bbcode_fluxbb bbcode_hubzilla bbcode_phpbb bbcode_steam bbcode_xenforo beamer biblatex bibtex chunkedhtml commonmark commonmark_x context csljson djot docbook docbook4 docbook5 docx dokuwiki dzslides epub epub2 epub3 fb2 gfm haddock html html4 html5 icml ipynb jats jats_archiving jats_articleauthoring jats_publishing jira json latex man markdown markdown_github markdown_mmd markdown_phpextra markdown_strict markua mediawiki ms muse native odt opendocument opml org pdf plain pptx revealjs rst rtf s5 slideous slidy t2t tei texinfo textile typst vimdoc xml xwiki zimwiki)'-    '--print-default-data-file[Data file to print]:FILE:(reference.docx reference.odt reference.pptx MANUAL.txt docx/_rels/.rels pptx/_rels/.rels abbreviations creole.lua default.csl docbook-entities.txt docx/[Content_Types].xml docx/docProps/app.xml docx/docProps/core.xml docx/docProps/custom.xml docx/word/_rels/document.xml.rels docx/word/_rels/footnotes.xml.rels docx/word/comments.xml docx/word/document.xml docx/word/fontTable.xml docx/word/footnotes.xml docx/word/numbering.xml docx/word/settings.xml docx/word/styles.xml docx/word/theme/theme1.xml docx/word/webSettings.xml dzslides/template.html epub.css init.lua odt/META-INF/manifest.xml odt/content.xml odt/manifest.rdf odt/meta.xml odt/mimetype odt/styles.xml pptx/[Content_Types].xml pptx/docProps/app.xml pptx/docProps/core.xml pptx/ppt/_rels/presentation.xml.rels pptx/ppt/notesMasters/_rels/notesMaster1.xml.rels pptx/ppt/notesMasters/notesMaster1.xml pptx/ppt/notesSlides/_rels/notesSlide1.xml.rels pptx/ppt/notesSlides/_rels/notesSlide2.xml.rels pptx/ppt/notesSlides/notesSlide1.xml pptx/ppt/notesSlides/notesSlide2.xml pptx/ppt/presProps.xml pptx/ppt/presentation.xml pptx/ppt/slideLayouts/_rels/slideLayout1.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout10.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout11.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout2.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout3.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout4.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout5.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout6.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout7.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout8.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout9.xml.rels pptx/ppt/slideLayouts/slideLayout1.xml pptx/ppt/slideLayouts/slideLayout10.xml pptx/ppt/slideLayouts/slideLayout11.xml pptx/ppt/slideLayouts/slideLayout2.xml pptx/ppt/slideLayouts/slideLayout3.xml pptx/ppt/slideLayouts/slideLayout4.xml pptx/ppt/slideLayouts/slideLayout5.xml pptx/ppt/slideLayouts/slideLayout6.xml pptx/ppt/slideLayouts/slideLayout7.xml pptx/ppt/slideLayouts/slideLayout8.xml pptx/ppt/slideLayouts/slideLayout9.xml pptx/ppt/slideMasters/_rels/slideMaster1.xml.rels pptx/ppt/slideMasters/slideMaster1.xml pptx/ppt/slides/_rels/slide1.xml.rels pptx/ppt/slides/_rels/slide2.xml.rels pptx/ppt/slides/_rels/slide3.xml.rels pptx/ppt/slides/_rels/slide4.xml.rels pptx/ppt/slides/slide1.xml pptx/ppt/slides/slide2.xml pptx/ppt/slides/slide3.xml pptx/ppt/slides/slide4.xml pptx/ppt/tableStyles.xml pptx/ppt/theme/theme1.xml pptx/ppt/theme/theme2.xml pptx/ppt/viewProps.xml templates/affiliations.jats templates/after-header-includes.latex templates/article.jats_publishing templates/common.latex templates/default.ansi templates/default.asciidoc templates/default.bbcode templates/default.beamer templates/default.biblatex templates/default.bibtex templates/default.chunkedhtml templates/default.commonmark templates/default.context templates/default.djot templates/default.docbook4 templates/default.docbook5 templates/default.dokuwiki templates/default.dzslides templates/default.epub2 templates/default.epub3 templates/default.haddock templates/default.html4 templates/default.html5 templates/default.icml templates/default.jats_archiving templates/default.jats_articleauthoring templates/default.jats_publishing templates/default.jira templates/default.latex templates/default.man templates/default.markdown templates/default.markua templates/default.mediawiki templates/default.ms templates/default.muse templates/default.opendocument templates/default.openxml templates/default.opml templates/default.org templates/default.plain templates/default.revealjs templates/default.rst templates/default.rtf templates/default.s5 templates/default.slideous templates/default.slidy templates/default.t2t templates/default.tei templates/default.texinfo templates/default.textile templates/default.typst templates/default.vimdoc templates/default.xwiki templates/default.zimwiki templates/document-metadata.latex templates/font-settings.latex templates/fonts.latex templates/hypersetup.latex templates/passoptions.latex templates/styles.citations.html templates/styles.html templates/template.typst translations/af.yaml translations/alt.yaml translations/am.yaml translations/ar.yaml translations/as.yaml translations/ast.yaml translations/az.yaml translations/be.yaml translations/bg.yaml translations/bn.yaml translations/bo.yaml translations/br.yaml translations/bs.yaml translations/bua.yaml translations/ca.yaml translations/ckb-Arab.yaml translations/ckb-Latn.yaml translations/cs.yaml translations/cu.yaml translations/cy.yaml translations/cz.yaml translations/da.yaml translations/de.yaml translations/dsb.yaml translations/el.yaml translations/en.yaml translations/eo.yaml translations/es-ES.yaml translations/es-MX.yaml translations/es.yaml translations/et.yaml translations/eu.yaml translations/fa.yaml translations/fi.yaml translations/fil.yaml translations/fr.yaml translations/fur.yaml translations/ga.yaml translations/gd.yaml translations/gl.yaml translations/grc.yaml translations/gu.yaml translations/ha.yaml translations/he.yaml translations/hi.yaml translations/hr.yaml translations/hsb.yaml translations/hu.yaml translations/hy.yaml translations/ia.yaml translations/id.yaml translations/is.yaml translations/it.yaml translations/ja.yaml translations/ka.yaml translations/km.yaml translations/kmr-Arab.yaml translations/kmr-Latn.yaml translations/kn.yaml translations/ko.yaml translations/la.yaml translations/lb.yaml translations/lo.yaml translations/lt.yaml translations/lv.yaml translations/mk.yaml translations/ml.yaml translations/mn.yaml translations/mr.yaml translations/ms.yaml translations/nb.yaml translations/nko.yaml translations/nl.yaml translations/nn.yaml translations/no.yaml translations/oc.yaml translations/or.yaml translations/pa.yaml translations/pl.yaml translations/pms.yaml translations/pt-BR.yaml translations/pt-PT.yaml translations/pt.yaml translations/rm.yaml translations/ro.yaml translations/ru.yaml translations/se.yaml translations/si.yaml translations/sk.yaml translations/sl.yaml translations/sq.yaml translations/sr-Cyrl.yaml translations/sr-Latn.yaml translations/sr.yaml translations/sv.yaml translations/ta.yaml translations/te.yaml translations/th.yaml translations/tk.yaml translations/tr.yaml translations/ua.yaml translations/ug.yaml translations/uk.yaml translations/ur.yaml translations/vi.yaml translations/zh-Hans.yaml translations/zh-Hant.yaml)'+    '--print-default-data-file[Data file to print]:FILE:(MANUAL.txt abbreviations creole.lua default.csl docbook-entities.txt docx/[Content_Types].xml docx/_rels/.rels docx/docProps/app.xml docx/docProps/core.xml docx/docProps/custom.xml docx/word/_rels/document.xml.rels docx/word/_rels/footnotes.xml.rels docx/word/comments.xml docx/word/document.xml docx/word/fontTable.xml docx/word/footnotes.xml docx/word/numbering.xml docx/word/settings.xml docx/word/styles.xml docx/word/theme/theme1.xml docx/word/webSettings.xml dzslides/template.html epub.css init.lua odt/META-INF/manifest.xml odt/content.xml odt/manifest.rdf odt/meta.xml odt/mimetype odt/styles.xml pptx/[Content_Types].xml pptx/_rels/.rels pptx/docProps/app.xml pptx/docProps/core.xml pptx/ppt/_rels/presentation.xml.rels pptx/ppt/notesMasters/_rels/notesMaster1.xml.rels pptx/ppt/notesMasters/notesMaster1.xml pptx/ppt/notesSlides/_rels/notesSlide1.xml.rels pptx/ppt/notesSlides/_rels/notesSlide2.xml.rels pptx/ppt/notesSlides/notesSlide1.xml pptx/ppt/notesSlides/notesSlide2.xml pptx/ppt/presProps.xml pptx/ppt/presentation.xml pptx/ppt/slideLayouts/_rels/slideLayout1.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout10.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout11.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout2.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout3.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout4.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout5.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout6.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout7.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout8.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout9.xml.rels pptx/ppt/slideLayouts/slideLayout1.xml pptx/ppt/slideLayouts/slideLayout10.xml pptx/ppt/slideLayouts/slideLayout11.xml pptx/ppt/slideLayouts/slideLayout2.xml pptx/ppt/slideLayouts/slideLayout3.xml pptx/ppt/slideLayouts/slideLayout4.xml pptx/ppt/slideLayouts/slideLayout5.xml pptx/ppt/slideLayouts/slideLayout6.xml pptx/ppt/slideLayouts/slideLayout7.xml pptx/ppt/slideLayouts/slideLayout8.xml pptx/ppt/slideLayouts/slideLayout9.xml pptx/ppt/slideMasters/_rels/slideMaster1.xml.rels pptx/ppt/slideMasters/slideMaster1.xml pptx/ppt/slides/_rels/slide1.xml.rels pptx/ppt/slides/_rels/slide2.xml.rels pptx/ppt/slides/_rels/slide3.xml.rels pptx/ppt/slides/_rels/slide4.xml.rels pptx/ppt/slides/slide1.xml pptx/ppt/slides/slide2.xml pptx/ppt/slides/slide3.xml pptx/ppt/slides/slide4.xml pptx/ppt/tableStyles.xml pptx/ppt/theme/theme1.xml pptx/ppt/theme/theme2.xml pptx/ppt/viewProps.xml reference.docx reference.odt reference.pptx templates/affiliations.jats templates/after-header-includes.latex templates/article.jats_publishing templates/common.latex templates/default.ansi templates/default.asciidoc templates/default.bbcode templates/default.beamer templates/default.biblatex templates/default.bibtex templates/default.chunkedhtml templates/default.commonmark templates/default.context templates/default.djot templates/default.docbook4 templates/default.docbook5 templates/default.dokuwiki templates/default.dzslides templates/default.epub2 templates/default.epub3 templates/default.haddock templates/default.html4 templates/default.html5 templates/default.icml templates/default.jats_archiving templates/default.jats_articleauthoring templates/default.jats_publishing templates/default.jira templates/default.latex templates/default.man templates/default.markdown templates/default.markua templates/default.mediawiki templates/default.ms templates/default.muse templates/default.opendocument templates/default.openxml templates/default.opml templates/default.org templates/default.plain templates/default.revealjs templates/default.rst templates/default.rtf templates/default.s5 templates/default.slideous templates/default.slidy templates/default.t2t templates/default.tei templates/default.texinfo templates/default.textile templates/default.typst templates/default.vimdoc templates/default.xwiki templates/default.zimwiki templates/document-metadata.latex templates/font-settings.latex templates/fonts.latex templates/hypersetup.latex templates/passoptions.latex templates/styles.citations.html templates/styles.html templates/template.typst translations/af.yaml translations/alt.yaml translations/am.yaml translations/ar.yaml translations/as.yaml translations/ast.yaml translations/az.yaml translations/be.yaml translations/bg.yaml translations/bn.yaml translations/bo.yaml translations/br.yaml translations/bs.yaml translations/bua.yaml translations/ca.yaml translations/ckb-Arab.yaml translations/ckb-Latn.yaml translations/cs.yaml translations/cu.yaml translations/cy.yaml translations/cz.yaml translations/da.yaml translations/de.yaml translations/dsb.yaml translations/el.yaml translations/en.yaml translations/eo.yaml translations/es-ES.yaml translations/es-MX.yaml translations/es.yaml translations/et.yaml translations/eu.yaml translations/fa.yaml translations/fi.yaml translations/fil.yaml translations/fr.yaml translations/fur.yaml translations/ga.yaml translations/gd.yaml translations/gl.yaml translations/grc.yaml translations/gu.yaml translations/ha.yaml translations/he.yaml translations/hi.yaml translations/hr.yaml translations/hsb.yaml translations/hu.yaml translations/hy.yaml translations/ia.yaml translations/id.yaml translations/is.yaml translations/it.yaml translations/ja.yaml translations/ka.yaml translations/km.yaml translations/kmr-Arab.yaml translations/kmr-Latn.yaml translations/kn.yaml translations/ko.yaml translations/la.yaml translations/lb.yaml translations/lo.yaml translations/lt.yaml translations/lv.yaml translations/mk.yaml translations/ml.yaml translations/mn.yaml translations/mr.yaml translations/ms.yaml translations/nb.yaml translations/nko.yaml translations/nl.yaml translations/nn.yaml translations/no.yaml translations/oc.yaml translations/or.yaml translations/pa.yaml translations/pl.yaml translations/pms.yaml translations/pt-BR.yaml translations/pt-PT.yaml translations/pt.yaml translations/rm.yaml translations/ro.yaml translations/ru.yaml translations/se.yaml translations/si.yaml translations/sk.yaml translations/sl.yaml translations/sq.yaml translations/sr-Cyrl.yaml translations/sr-Latn.yaml translations/sr.yaml translations/sv.yaml translations/ta.yaml translations/te.yaml translations/th.yaml translations/tk.yaml translations/tr.yaml translations/ua.yaml translations/ug.yaml translations/uk.yaml translations/ur.yaml translations/vi.yaml translations/zh-Hans.yaml translations/zh-Hant.yaml)'     '--print-highlight-style[Highlighting style]:STYLE:(pygments tango espresso zenburn kate monochrome breezedark haddock)'     '-v[Print version]'     '--version[Print version]'@@ -371,7 +371,7 @@ complete -c pandoc -l list-highlight-languages -d "List highlighting languages" complete -c pandoc -l list-highlight-styles -d "List highlighting styles" complete -c pandoc -s D -l print-default-template -d "Format to print template for" -r -a "ansi asciidoc asciidoc_legacy asciidoctor bbcode bbcode_fluxbb bbcode_hubzilla bbcode_phpbb bbcode_steam bbcode_xenforo beamer biblatex bibtex chunkedhtml commonmark commonmark_x context csljson djot docbook docbook4 docbook5 docx dokuwiki dzslides epub epub2 epub3 fb2 gfm haddock html html4 html5 icml ipynb jats jats_archiving jats_articleauthoring jats_publishing jira json latex man markdown markdown_github markdown_mmd markdown_phpextra markdown_strict markua mediawiki ms muse native odt opendocument opml org pdf plain pptx revealjs rst rtf s5 slideous slidy t2t tei texinfo textile typst vimdoc xml xwiki zimwiki"-complete -c pandoc -l print-default-data-file -d "Data file to print" -r -a "reference.docx reference.odt reference.pptx MANUAL.txt docx/_rels/.rels pptx/_rels/.rels abbreviations creole.lua default.csl docbook-entities.txt docx/[Content_Types].xml docx/docProps/app.xml docx/docProps/core.xml docx/docProps/custom.xml docx/word/_rels/document.xml.rels docx/word/_rels/footnotes.xml.rels docx/word/comments.xml docx/word/document.xml docx/word/fontTable.xml docx/word/footnotes.xml docx/word/numbering.xml docx/word/settings.xml docx/word/styles.xml docx/word/theme/theme1.xml docx/word/webSettings.xml dzslides/template.html epub.css init.lua odt/META-INF/manifest.xml odt/content.xml odt/manifest.rdf odt/meta.xml odt/mimetype odt/styles.xml pptx/[Content_Types].xml pptx/docProps/app.xml pptx/docProps/core.xml pptx/ppt/_rels/presentation.xml.rels pptx/ppt/notesMasters/_rels/notesMaster1.xml.rels pptx/ppt/notesMasters/notesMaster1.xml pptx/ppt/notesSlides/_rels/notesSlide1.xml.rels pptx/ppt/notesSlides/_rels/notesSlide2.xml.rels pptx/ppt/notesSlides/notesSlide1.xml pptx/ppt/notesSlides/notesSlide2.xml pptx/ppt/presProps.xml pptx/ppt/presentation.xml pptx/ppt/slideLayouts/_rels/slideLayout1.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout10.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout11.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout2.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout3.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout4.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout5.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout6.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout7.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout8.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout9.xml.rels pptx/ppt/slideLayouts/slideLayout1.xml pptx/ppt/slideLayouts/slideLayout10.xml pptx/ppt/slideLayouts/slideLayout11.xml pptx/ppt/slideLayouts/slideLayout2.xml pptx/ppt/slideLayouts/slideLayout3.xml pptx/ppt/slideLayouts/slideLayout4.xml pptx/ppt/slideLayouts/slideLayout5.xml pptx/ppt/slideLayouts/slideLayout6.xml pptx/ppt/slideLayouts/slideLayout7.xml pptx/ppt/slideLayouts/slideLayout8.xml pptx/ppt/slideLayouts/slideLayout9.xml pptx/ppt/slideMasters/_rels/slideMaster1.xml.rels pptx/ppt/slideMasters/slideMaster1.xml pptx/ppt/slides/_rels/slide1.xml.rels pptx/ppt/slides/_rels/slide2.xml.rels pptx/ppt/slides/_rels/slide3.xml.rels pptx/ppt/slides/_rels/slide4.xml.rels pptx/ppt/slides/slide1.xml pptx/ppt/slides/slide2.xml pptx/ppt/slides/slide3.xml pptx/ppt/slides/slide4.xml pptx/ppt/tableStyles.xml pptx/ppt/theme/theme1.xml pptx/ppt/theme/theme2.xml pptx/ppt/viewProps.xml templates/affiliations.jats templates/after-header-includes.latex templates/article.jats_publishing templates/common.latex templates/default.ansi templates/default.asciidoc templates/default.bbcode templates/default.beamer templates/default.biblatex templates/default.bibtex templates/default.chunkedhtml templates/default.commonmark templates/default.context templates/default.djot templates/default.docbook4 templates/default.docbook5 templates/default.dokuwiki templates/default.dzslides templates/default.epub2 templates/default.epub3 templates/default.haddock templates/default.html4 templates/default.html5 templates/default.icml templates/default.jats_archiving templates/default.jats_articleauthoring templates/default.jats_publishing templates/default.jira templates/default.latex templates/default.man templates/default.markdown templates/default.markua templates/default.mediawiki templates/default.ms templates/default.muse templates/default.opendocument templates/default.openxml templates/default.opml templates/default.org templates/default.plain templates/default.revealjs templates/default.rst templates/default.rtf templates/default.s5 templates/default.slideous templates/default.slidy templates/default.t2t templates/default.tei templates/default.texinfo templates/default.textile templates/default.typst templates/default.vimdoc templates/default.xwiki templates/default.zimwiki templates/document-metadata.latex templates/font-settings.latex templates/fonts.latex templates/hypersetup.latex templates/passoptions.latex templates/styles.citations.html templates/styles.html templates/template.typst translations/af.yaml translations/alt.yaml translations/am.yaml translations/ar.yaml translations/as.yaml translations/ast.yaml translations/az.yaml translations/be.yaml translations/bg.yaml translations/bn.yaml translations/bo.yaml translations/br.yaml translations/bs.yaml translations/bua.yaml translations/ca.yaml translations/ckb-Arab.yaml translations/ckb-Latn.yaml translations/cs.yaml translations/cu.yaml translations/cy.yaml translations/cz.yaml translations/da.yaml translations/de.yaml translations/dsb.yaml translations/el.yaml translations/en.yaml translations/eo.yaml translations/es-ES.yaml translations/es-MX.yaml translations/es.yaml translations/et.yaml translations/eu.yaml translations/fa.yaml translations/fi.yaml translations/fil.yaml translations/fr.yaml translations/fur.yaml translations/ga.yaml translations/gd.yaml translations/gl.yaml translations/grc.yaml translations/gu.yaml translations/ha.yaml translations/he.yaml translations/hi.yaml translations/hr.yaml translations/hsb.yaml translations/hu.yaml translations/hy.yaml translations/ia.yaml translations/id.yaml translations/is.yaml translations/it.yaml translations/ja.yaml translations/ka.yaml translations/km.yaml translations/kmr-Arab.yaml translations/kmr-Latn.yaml translations/kn.yaml translations/ko.yaml translations/la.yaml translations/lb.yaml translations/lo.yaml translations/lt.yaml translations/lv.yaml translations/mk.yaml translations/ml.yaml translations/mn.yaml translations/mr.yaml translations/ms.yaml translations/nb.yaml translations/nko.yaml translations/nl.yaml translations/nn.yaml translations/no.yaml translations/oc.yaml translations/or.yaml translations/pa.yaml translations/pl.yaml translations/pms.yaml translations/pt-BR.yaml translations/pt-PT.yaml translations/pt.yaml translations/rm.yaml translations/ro.yaml translations/ru.yaml translations/se.yaml translations/si.yaml translations/sk.yaml translations/sl.yaml translations/sq.yaml translations/sr-Cyrl.yaml translations/sr-Latn.yaml translations/sr.yaml translations/sv.yaml translations/ta.yaml translations/te.yaml translations/th.yaml translations/tk.yaml translations/tr.yaml translations/ua.yaml translations/ug.yaml translations/uk.yaml translations/ur.yaml translations/vi.yaml translations/zh-Hans.yaml translations/zh-Hant.yaml"+complete -c pandoc -l print-default-data-file -d "Data file to print" -r -a "MANUAL.txt abbreviations creole.lua default.csl docbook-entities.txt docx/[Content_Types].xml docx/_rels/.rels docx/docProps/app.xml docx/docProps/core.xml docx/docProps/custom.xml docx/word/_rels/document.xml.rels docx/word/_rels/footnotes.xml.rels docx/word/comments.xml docx/word/document.xml docx/word/fontTable.xml docx/word/footnotes.xml docx/word/numbering.xml docx/word/settings.xml docx/word/styles.xml docx/word/theme/theme1.xml docx/word/webSettings.xml dzslides/template.html epub.css init.lua odt/META-INF/manifest.xml odt/content.xml odt/manifest.rdf odt/meta.xml odt/mimetype odt/styles.xml pptx/[Content_Types].xml pptx/_rels/.rels pptx/docProps/app.xml pptx/docProps/core.xml pptx/ppt/_rels/presentation.xml.rels pptx/ppt/notesMasters/_rels/notesMaster1.xml.rels pptx/ppt/notesMasters/notesMaster1.xml pptx/ppt/notesSlides/_rels/notesSlide1.xml.rels pptx/ppt/notesSlides/_rels/notesSlide2.xml.rels pptx/ppt/notesSlides/notesSlide1.xml pptx/ppt/notesSlides/notesSlide2.xml pptx/ppt/presProps.xml pptx/ppt/presentation.xml pptx/ppt/slideLayouts/_rels/slideLayout1.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout10.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout11.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout2.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout3.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout4.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout5.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout6.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout7.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout8.xml.rels pptx/ppt/slideLayouts/_rels/slideLayout9.xml.rels pptx/ppt/slideLayouts/slideLayout1.xml pptx/ppt/slideLayouts/slideLayout10.xml pptx/ppt/slideLayouts/slideLayout11.xml pptx/ppt/slideLayouts/slideLayout2.xml pptx/ppt/slideLayouts/slideLayout3.xml pptx/ppt/slideLayouts/slideLayout4.xml pptx/ppt/slideLayouts/slideLayout5.xml pptx/ppt/slideLayouts/slideLayout6.xml pptx/ppt/slideLayouts/slideLayout7.xml pptx/ppt/slideLayouts/slideLayout8.xml pptx/ppt/slideLayouts/slideLayout9.xml pptx/ppt/slideMasters/_rels/slideMaster1.xml.rels pptx/ppt/slideMasters/slideMaster1.xml pptx/ppt/slides/_rels/slide1.xml.rels pptx/ppt/slides/_rels/slide2.xml.rels pptx/ppt/slides/_rels/slide3.xml.rels pptx/ppt/slides/_rels/slide4.xml.rels pptx/ppt/slides/slide1.xml pptx/ppt/slides/slide2.xml pptx/ppt/slides/slide3.xml pptx/ppt/slides/slide4.xml pptx/ppt/tableStyles.xml pptx/ppt/theme/theme1.xml pptx/ppt/theme/theme2.xml pptx/ppt/viewProps.xml reference.docx reference.odt reference.pptx templates/affiliations.jats templates/after-header-includes.latex templates/article.jats_publishing templates/common.latex templates/default.ansi templates/default.asciidoc templates/default.bbcode templates/default.beamer templates/default.biblatex templates/default.bibtex templates/default.chunkedhtml templates/default.commonmark templates/default.context templates/default.djot templates/default.docbook4 templates/default.docbook5 templates/default.dokuwiki templates/default.dzslides templates/default.epub2 templates/default.epub3 templates/default.haddock templates/default.html4 templates/default.html5 templates/default.icml templates/default.jats_archiving templates/default.jats_articleauthoring templates/default.jats_publishing templates/default.jira templates/default.latex templates/default.man templates/default.markdown templates/default.markua templates/default.mediawiki templates/default.ms templates/default.muse templates/default.opendocument templates/default.openxml templates/default.opml templates/default.org templates/default.plain templates/default.revealjs templates/default.rst templates/default.rtf templates/default.s5 templates/default.slideous templates/default.slidy templates/default.t2t templates/default.tei templates/default.texinfo templates/default.textile templates/default.typst templates/default.vimdoc templates/default.xwiki templates/default.zimwiki templates/document-metadata.latex templates/font-settings.latex templates/fonts.latex templates/hypersetup.latex templates/passoptions.latex templates/styles.citations.html templates/styles.html templates/template.typst translations/af.yaml translations/alt.yaml translations/am.yaml translations/ar.yaml translations/as.yaml translations/ast.yaml translations/az.yaml translations/be.yaml translations/bg.yaml translations/bn.yaml translations/bo.yaml translations/br.yaml translations/bs.yaml translations/bua.yaml translations/ca.yaml translations/ckb-Arab.yaml translations/ckb-Latn.yaml translations/cs.yaml translations/cu.yaml translations/cy.yaml translations/cz.yaml translations/da.yaml translations/de.yaml translations/dsb.yaml translations/el.yaml translations/en.yaml translations/eo.yaml translations/es-ES.yaml translations/es-MX.yaml translations/es.yaml translations/et.yaml translations/eu.yaml translations/fa.yaml translations/fi.yaml translations/fil.yaml translations/fr.yaml translations/fur.yaml translations/ga.yaml translations/gd.yaml translations/gl.yaml translations/grc.yaml translations/gu.yaml translations/ha.yaml translations/he.yaml translations/hi.yaml translations/hr.yaml translations/hsb.yaml translations/hu.yaml translations/hy.yaml translations/ia.yaml translations/id.yaml translations/is.yaml translations/it.yaml translations/ja.yaml translations/ka.yaml translations/km.yaml translations/kmr-Arab.yaml translations/kmr-Latn.yaml translations/kn.yaml translations/ko.yaml translations/la.yaml translations/lb.yaml translations/lo.yaml translations/lt.yaml translations/lv.yaml translations/mk.yaml translations/ml.yaml translations/mn.yaml translations/mr.yaml translations/ms.yaml translations/nb.yaml translations/nko.yaml translations/nl.yaml translations/nn.yaml translations/no.yaml translations/oc.yaml translations/or.yaml translations/pa.yaml translations/pl.yaml translations/pms.yaml translations/pt-BR.yaml translations/pt-PT.yaml translations/pt.yaml translations/rm.yaml translations/ro.yaml translations/ru.yaml translations/se.yaml translations/si.yaml translations/sk.yaml translations/sl.yaml translations/sq.yaml translations/sr-Cyrl.yaml translations/sr-Latn.yaml translations/sr.yaml translations/sv.yaml translations/ta.yaml translations/te.yaml translations/th.yaml translations/tk.yaml translations/tr.yaml translations/ua.yaml translations/ug.yaml translations/uk.yaml translations/ur.yaml translations/vi.yaml translations/zh-Hans.yaml translations/zh-Hant.yaml" complete -c pandoc -l print-highlight-style -d "Highlighting style" -r -a "pygments tango espresso zenburn kate monochrome breezedark haddock" complete -c pandoc -s v -l version -d "Print version" complete -c pandoc -s h -l help -d "Show help"
+ test/command/latex-opt-arg-state.md view
@@ -0,0 +1,38 @@+State changes made while parsing an optional argument (which is not+a TeX group) should persist, instead of being discarded with the+sub-parse.++```+% pandoc -f latex -t native+\begin{description}+\item[\def\x{Y}k] b \x+\end{description}+^D+[ DefinitionList+    [ ( [ Str "k" ]+      , [ [ Para [ Str "b" , Space , Str "Y" ] ] ]+      )+    ]+]+```++```+% pandoc -f latex -t native+\cite[\def\x{q}]{a}\x+^D+[ Para+    [ Cite+        [ Citation+            { citationId = "a"+            , citationPrefix = []+            , citationSuffix = []+            , citationMode = NormalCitation+            , citationNoteNum = 0+            , citationHash = 0+            }+        ]+        [ RawInline (Format "latex") "\\cite[\\def\\x{q}]{a}" ]+    , Str "q"+    ]+]+```
+ test/command/latex-tokenize-hash-positions.md view
@@ -0,0 +1,13 @@+Source positions of tokens following `##` should not be off by one.+The skipped `\zzz ` spans columns 4-8, so the position after it is+column 9.++```+% pandoc -f latex -t plain --verbose+## \zzz hi+^D+2> [INFO] Parsing unescaped '#' at line 1 column 1+2> [INFO] Parsing unescaped '#' at line 1 column 2+2> [INFO] Skipped '\zzz ' at line 1 column 9+## hi+```
+ test/command/latex-url-comment-positions.md view
@@ -0,0 +1,12 @@+Tokens produced by retokenizing a comment inside a URL argument+should keep their original source positions.  Here the `}` and+`\zzz` come from the retokenized comment; the skipped `\zzz ` spans+columns 12-16, so the position after it is column 17.++```+% pandoc -f latex -t plain --verbose+\url{ab%cd}\zzz x+^D+2> [INFO] Skipped '\zzz ' at line 1 column 17+ab%cdx+```
+ test/command/latex3-commands.md view
@@ -0,0 +1,473 @@+Tests for LaTeX3 (xparse) document command support.++`o` specifier and `\IfNoValueTF`:++```+% pandoc -f latex -t plain+\NewDocumentCommand{\name}{o m}{\IfNoValueTF{#1}{#2}{#1: #2}}+\name{Alice}++\name[Dr]{Bob}+^D+Alice++Dr: Bob+```++`O` specifier with default:++```+% pandoc -f latex -t plain+\NewDocumentCommand\greet{O{Hello} m}{#1, #2!}+\greet{world}++\greet[Hi]{there}+^D+Hello, world!++Hi, there!+```++`s` specifier and `\IfBooleanTF`:++```+% pandoc -f latex -t latex+\NewDocumentCommand\maybeemph{s m}{\IfBooleanTF{#1}{\emph{#2}}{#2}}+\maybeemph{plain} and \maybeemph*{starred}+^D+plain and \emph{starred}+```++`t` specifier:++```+% pandoc -f latex -t plain+\NewDocumentCommand\q{t+ m}{\IfBooleanTF{#1}{plus #2}{noplus #2}}+\q+{a} \q{b}+^D+plus a noplus b+```++`d` specifier with custom delimiters:++```+% pandoc -f latex -t plain+\NewDocumentCommand\point{d()}{\IfNoValueTF{#1}{origin}{(#1)}}+\point, \point(3,4)+^D+origin, (3,4)+```++`r` specifier (required, custom delimiters):++```+% pandoc -f latex -t plain+\NewDocumentCommand\req{r()}{req #1}+\req(ok)+^D+req ok+```++`\DeclareDocumentCommand` redefines silently:++```+% pandoc -f latex -t plain+\DeclareDocumentCommand\x{m}{first: #1}+\DeclareDocumentCommand\x{m}{second: #1}+\x{y}+^D+second: y+```++`\ProvideDocumentCommand` does not redefine:++```+% pandoc -f latex -t plain+\NewDocumentCommand\y{m}{first: #1}+\ProvideDocumentCommand\y{m}{second: #1}+\y{z}+^D+first: z+```++A `-NoValue-` marker that leaks into the document is printed+like in xparse:++```+% pandoc -f latex -t plain+\NewDocumentCommand\val{o}{value: #1}+\val+^D+value: -NoValue-+```++Nested brackets in optional arguments:++```+% pandoc -f latex -t plain+\NewDocumentCommand\opt{o}{got #1}+\opt[a[b]c]+^D+got a[b]c+```++`\IfValueT`:++```+% pandoc -f latex -t plain+\NewDocumentCommand\test{o}{\IfValueT{#1}{seen #1}}+\test[x] \test+^D+seen x+```++Definitions pass through as raw LaTeX when `latex_macros`+is disabled:++```+% pandoc -f markdown-latex_macros -t markdown+raw_tex-raw_attribute+\NewDocumentCommand{\my}{m}{\emph{#1}}+\my{hi}+^D+\NewDocumentCommand{\my}{m}{\emph{#1}}+\my{hi}+```++`\NewDocumentEnvironment`: arguments are available in the+end code, too:++```+% pandoc -f latex -t plain+\NewDocumentEnvironment{titled}{m}{start #1.}{end #1.}+\begin{titled}{T}+middle+\end{titled}+^D+start T. middle end T.+```++Environments with arguments nest properly:++```+% pandoc -f latex -t plain+\NewDocumentEnvironment{wrap}{m}{[#1 }{ #1]}+\begin{wrap}{a}+one+\begin{wrap}{b}+two+\end{wrap}+three+\end{wrap}+^D+[a one [b two b] three a]+```++Environment wrapping another environment, with optional+argument:++```+% pandoc -f latex -t latex+\NewDocumentEnvironment{myquote}{o}+  {\begin{quote}\IfValueT{#1}{\textbf{#1}: }}+  {\end{quote}}+\begin{myquote}[Note]+Hello.+\end{myquote}+^D+\begin{quote}+\textbf{Note}: Hello.+\end{quote}+```++`O` specifier with default in environments:++```+% pandoc -f latex -t plain+\NewDocumentEnvironment{sec}{O{A} m}{(#1/#2 }{ #2)}+\begin{sec}[B]{t}+body+\end{sec}++\begin{sec}{u}+more+\end{sec}+^D+(B/t body t)++(A/u more u)+```++`b` specifier grabs the environment body, trimming+surrounding spaces:++```+% pandoc -f latex -t plain+\NewDocumentEnvironment{titledb}{m b}{Title: #1. Body: #2.}{}+\begin{titledb}{T}+some text+\end{titledb}+^D+Title: T. Body: some text.+```++`\DeclareDocumentEnvironment` redefines silently:++```+% pandoc -f latex -t plain+\DeclareDocumentEnvironment{z}{}{one}{}+\DeclareDocumentEnvironment{z}{}{two}{}+\begin{z}x\end{z}+^D+twox+```++`\ProvideDocumentEnvironment` does not redefine:++```+% pandoc -f latex -t plain+\NewDocumentEnvironment{p}{}{one}{}+\ProvideDocumentEnvironment{p}{}{two}{}+\begin{p}x\end{p}+^D+onex+```++`v` specifier (verbatim), with `\verb`-style delimiters or+braces; special characters are not reinterpreted:++```+% pandoc -f latex -t plain+\NewDocumentCommand\showv{v}{[#1]}+\showv|a_b&\foo|++\showv{xy}+^D+[a_b&\foo]++[xy]+```++`e` specifier (embellishments), matched in any order:++```+% pandoc -f latex -t plain+\NewDocumentCommand\x{e{^_}}{sup=#1, sub=#2.}+\x^{up}_{down}++\x_{down}^{up}++\x_{d}+^D+sup=up, sub=down.++sup=up, sub=down.++sup=-NoValue-, sub=d.+```++`e` specifier with `\IfNoValueTF` and single-token argument:++```+% pandoc -f latex -t plain+\NewDocumentCommand\z{e{^}}{\IfNoValueTF{#1}{none}{got #1}}+\z^2 \z+^D+got 2 none+```++`E` specifier (embellishments with defaults):++```+% pandoc -f latex -t plain+\NewDocumentCommand\y{E{^_}{{U}{D}}}{#1/#2}+\y^{a}++\y+^D+a/D++U/D+```++`\TrimSpaces` argument processor:++```+% pandoc -f latex -t plain+\NewDocumentCommand\trim{>{\TrimSpaces}m}{[#1]}+\trim{  hi  }+^D+[hi]+```++`\ReverseBoolean` argument processor:++```+% pandoc -f latex -t plain+\NewDocumentCommand\rs{>{\ReverseBoolean}s}{\IfBooleanTF{#1}{T}{F}}+\rs* \rs+^D+F T+```++`\SplitArgument` processor: splits into braced groups, trims+spaces, pads with `-NoValue-`:++```+% pandoc -f latex -t plain+\NewDocumentCommand\three{mmm}{1=#1, 2=#2, 3=#3.}+\NewDocumentCommand\splt{>{\SplitArgument{2}{;}}m}{\three#1}+\splt{a; b ;c}++\splt{a;b}+^D+1=a, 2=b, 3=c.++1=a, 2=b, 3=-NoValue-.+```++`\SplitList` processor:++```+% pandoc -f latex -t plain+\NewDocumentCommand\lst{>{\SplitList{,}}m}{items: #1}+\lst{x, y ,z}+^D+items: xyz+```++Multiple processors are applied starting from the one nearest+the argument specifier:++```+% pandoc -f latex -t plain+\NewDocumentCommand\two{mm}{(#1)(#2)}+\NewDocumentCommand\c{>{\SplitArgument{1}{;}}>{\TrimSpaces}m}{\two#1}+\c{ a;b }+^D+(a)(b)+```++Unknown processors are skipped, keeping the argument:++```+% pandoc -f latex -t plain+\NewDocumentCommand\u{>{\SomeUnknownProc}m}{[#1]}+\u{ok}+^D+[ok]+```++`\NewCommandCopy` snapshots the definition (like `\let`):++```+% pandoc -f latex -t plain+\NewDocumentCommand\old{m}{orig: #1}+\NewCommandCopy\new\old+\RenewDocumentCommand\old{m}{changed: #1}+\new{a} \old{b}+^D+orig: a changed: b+```++`\NewEnvironmentCopy`, copying a built-in environment:++```+% pandoc -f latex -t latex+\NewEnvironmentCopy{myquote}{quote}+\begin{myquote}+Hello.+\end{myquote}+^D+\begin{quote}+Hello.+\end{quote}+```++`\MakeTitlecase` uppercases the first character:++```+% pandoc -f latex -t plain+\MakeTitlecase{hello world} \MakeTitlecase{\emph{foo bar}}+^D+Hello world Foo bar+```++Code between `\ExplSyntaxOn` and `\ExplSyntaxOff` is+tokenized with `:` and `_` as letters, so it can be skipped+cleanly:++```+% pandoc -f latex -t plain+\ExplSyntaxOn+\tl_new:N \l_my_tl+\tl_set:Nn \l_my_tl {stuff}+\ExplSyntaxOff+Text after.+^D+Text after.+```++`\ShowCommand` and the key–value commands are parsed and+ignored:++```+% pandoc -f latex -t plain+\NewDocumentCommand\foo{m}{x#1}+\ShowCommand\foo+\DeclareKeys[fam]{key .store = \myval}+\SetKeys[fam]{key=5}+\ProcessKeyOptions[fam]+Done.+^D+Done.+```++`\inteval`: `+ - * /` with division rounding to nearest (ties+away from zero, as in eTeX):++```+% pandoc -f latex -t plain+\inteval{2+3*4}, \inteval{7/2}, \inteval{-7/2}, \inteval{(1+2)*3}+^D+14, 4, -4, 9+```++Macros in the evaluator argument are expanded:++```+% pandoc -f latex -t plain+\NewDocumentCommand\n{}{4}+\inteval{\n + 1}+^D+5+```++`\fpeval`: floating point expressions; results are rounded to+16 significant digits as in l3fp:++```+% pandoc -f latex -t plain+\fpeval{2^10/8}, \fpeval{0.1 + 0.2}, \fpeval{2**3}, \fpeval{1e3*2}, \fpeval{1/3}+^D+128, 0.3, 8, 2000, 0.3333333333333333+```++Expressions that cannot be evaluated are passed through as+text:++```+% pandoc -f latex -t plain+\fpeval{foo(1)}, \fpeval{1/0}, \inteval{1/0}+^D+foo(1), 1/0, 1/0+```++`\dimeval` and `\skipeval` substitute their (unevaluated)+expression:++```+% pandoc -f latex -t plain+\dimeval{2pt+3pt}, \skipeval{1em plus 2pt}+^D+2pt+3pt, 1em plus 2pt+```
+ test/command/latex3-usrguide.md view
@@ -0,0 +1,453 @@+Tests corresponding to the examples in the LaTeX usrguide+(<https://www.latex-project.org/help/documentation/usrguide.pdf>).++§2.2: the `\chapter`-like example with `s o m`:++```+% pandoc -f latex -t plain+\NewDocumentCommand\mychapter{s o m}+  {\IfBooleanTF{#1}{star: #3}{normal: \IfNoValueTF{#2}{#3}{#2} / #3}\par}+\mychapter{One}+\mychapter*{Two}+\mychapter[Short]{Long}+^D+normal: One / One++star: Two++normal: Short / Long+```++§2.3: spaces around the environment name are ignored:++```+% pandoc -f latex -t plain+\NewDocumentEnvironment{ foo }{}{[}{]}+\begin{foo}x\end{foo}+^D+[x]+```++§2.5: optional arguments may safely be nested:++```+% pandoc -f latex -t plain+\NewDocumentCommand\foo{om}{I grabbed (#1) and (#2)}+\NewDocumentCommand\baz{o}{#1-#1}+\foo[\baz[stuff]]{more stuff}+^D+I grabbed (stuff-stuff) and (more stuff)+```++§2.5: the default for `O` can be the result of grabbing another+argument:++```+% pandoc -f latex -t plain+\NewDocumentCommand\foo{O{#2} m}{opt=#1, mand=#2.}+\foo{A}++\foo[B]{A}+^D+opt=A, mand=A.++opt=B, mand=A.+```++§2.6: spaces before optional arguments are allowed by default,+but not with the `!` modifier:++```+% pandoc -f latex -t plain+\NewDocumentCommand\foobar{m o}{(#1|#2)}+\NewDocumentCommand\foobang{m !o}{(#1|#2)}+\foobar{arg1} [arg2]++\foobang{arg1} [arg2]+^D+(arg1|arg2)++(arg1|-NoValue-) [arg2]+```++§2.8: `\IfNoValueTF`:++```+% pandoc -f latex -t plain+\NewDocumentCommand\foo{o m}+  {%+    \IfNoValueTF {#1}%+      {just #2}%+      {both #1 and #2}}+\foo{m}, \foo[o]{m}+^D+just m, both o and m+```++§2.8: the `\IfBlankTF` example, including the effect of `!` on+the last `\foo`:++```+% pandoc -f latex -t plain+\NewDocumentCommand\foo{m!o}{\par #1:+  \IfNoValueTF{#2}+    {No optional}%+    {%+       \IfBlankTF{#2}+         {Blanks in or empty}%+         {Real content in}%+    }%+  \space argument!}+\foo{1}[bar] \foo{2}[ ] \foo{3}[] \foo{4}[\space] \foo{5} [x]+^D+1: Real content in argument!++2: Blanks in or empty argument!++3: Blanks in or empty argument!++4: Real content in argument!++5: No optional argument! [x]+```++§2.8: `\IfBooleanTF`:++```+% pandoc -f latex -t plain+\NewDocumentCommand\foo{sm}+  {%+    \IfBooleanTF {#1}%+      {with star: #2}%+      {without star: #2}}+\foo{a}, \foo*{b}+^D+without star: a, with star: b+```++§2.9: the `=` modifier (key–value interface for an argument) is+accepted:++```+% pandoc -f latex -t plain+\DeclareDocumentCommand\mycaption{s ={short-text} +O{#3} +m}{[#2|#3]}+\mycaption{Text}+\mycaption[Short]{Long}+^D+[Text|Text] [Short|Long]+```++§2.10: `\SplitArgument` splits into a fixed number of parts,+trimming spaces, padding with `-NoValue-`:++```+% pandoc -f latex -t plain+\NewDocumentCommand\InternalFunctionOfThreeArguments{mmm}{1=#1, 2=#2, 3=#3.}+\NewDocumentCommand\foo{>{\SplitArgument{2}{;}} m}+  {\InternalFunctionOfThreeArguments#1}+\foo{a ; b ; c}++\foo{a;b}+^D+1=a, 2=b, 3=c.++1=a, 2=b, 3=-NoValue-.+```++§2.10: a processor applied to an `e`-type argument applies to+all of its arguments:++```+% pandoc -f latex -t plain+\NewDocumentCommand\foo{ >{\TrimSpaces} e{_^} }+  { [#1](#2) }+\foo_{ a }^{ b }+^D+[a](b)+```++§2.10: `\SplitList` and `\ProcessList`:++```+% pandoc -f latex -t plain+\NewDocumentCommand\SomeDocumentCommand{m}{<#1>}+\NewDocumentCommand\foo{>{\SplitList{;}} m}+  {\ProcessList{#1}{\SomeDocumentCommand}}+\foo{a; b ;c}+^D+<a><b><c>+```++§2.10: `\ProcessList` with arbitrary tokens expecting one+argument:++```+% pandoc -f latex -t plain+\NewDocumentCommand\SomeDocumentCommand{m}{<#1>}+\NewDocumentCommand\foo{>{\SplitList{;}} m}+  {\ProcessList{#1}{Abc \SomeDocumentCommand}}+\foo{a;b}+^D+Abc <a>Abc <b>+```++§2.10: `\ReverseBoolean`:++```+% pandoc -f latex -t plain+\NewDocumentCommand\foo{>{\ReverseBoolean} s m}+  {%+    \IfBooleanTF#1%+      {without star: #2}%+      {with star: #2}}+\foo{a}, \foo*{b}+^D+without star: a, with star: b+```++§2.10: `\TrimSpaces`:++```+% pandoc -f latex -t plain+\NewDocumentCommand\foo{>{\TrimSpaces} m}{[#1]}+\foo{ hello world }+^D+[hello world]+```++§2.11: the `b` argument type grabs the body of the environment;+spaces are trimmed at both ends:++```+% pandoc -f latex -t html+\NewDocumentEnvironment{twice}{O{\ttfamily} +b}+  {#2#1#2} {}+\begin{twice}[\itshape]+  Hello world!+\end{twice}+^D+<p>Hello world!<em>Hello world!</em></p>+```++§2.12–2.13: `\NewExpandableDocumentCommand`, wrapping+`\multicolumn` in a tabular cell:++```+% pandoc -f latex -t plain+\NewExpandableDocumentCommand\MyMultiCol{m}{\multicolumn{3}{c}{#1}}+\begin{tabular}{lcr}+a & b & c \\+\MyMultiCol{stuff} \\+\end{tabular}+^D++:----+:---:+----:++| a   | b   | c   |++-----+-----+-----++| stuff           |++-----------------++```++§2.16: the `c` argument type grabs the body of the environment+verbatim; it is typeset with spaces as visible spaces (U+2423):++```+% pandoc -f latex -t plain+\NewDocumentEnvironment{MyVerbatim}{!O{\ttfamily} c}+  {\begin{center} #1 #2\end{center}} {}+\begin{MyVerbatim}[\ttfamily\itshape]+  % Some code is shown here+  $y = mx + c$+\end{MyVerbatim}+^D+␣␣%␣Some␣code␣is␣shown␣here+␣␣$y␣=␣mx␣+␣c$+```++§3: `\NewCommandCopy` copies a definition, so the original can+be redefined in terms of the copy:++```+% pandoc -f latex -t plain+\NewDocumentCommand\hi{}{Hello!}+\NewCommandCopy\hiorig\hi+\RenewDocumentCommand\hi{}{\hiorig{} And again: \hiorig}+\hi+^D+Hello! And again: Hello!+```++§4: `\UseName` turns a string into a csname and executes it:++```+% pandoc -f latex -t plain+\NewDocumentCommand\hello{}{Hello!}+\UseName{hello}+^D+Hello!+```++§4: `\ExpandArgs{c}` constructs a command name for+`\NewDocumentCommand`:++```+% pandoc -f latex -t plain+\NewDocumentCommand\newnoter{m}+  {\ExpandArgs{c}\NewDocumentCommand{#1}{m}{#1 note: ##1.}}+\newnoter{todo}+\todo{fix this}+^D+todo note: fix this.+```++§4: `\ExpandArgs{cc}` with `\NewCommandCopy`, copying a command+by string name:++```+% pandoc -f latex -t plain+\NewDocumentCommand\savebyname{m}+  {\ExpandArgs{cc}\NewCommandCopy{saved#1}{#1}}+\NewDocumentCommand\greeting{}{Hello!}+\savebyname{greeting}+\RenewDocumentCommand\greeting{}{Goodbye!}+\greeting{} \UseName{savedgreeting}+^D+Goodbye! Hello!+```++§4: `\ExpandArgs{Nc}`:++```+% pandoc -f latex -t plain+\NewDocumentCommand\target{}{found}+\ExpandArgs{Nc}\NewCommandCopy\mycopy{target}+\mycopy+^D+found+```++§5: the `\fpeval` example:++```+% pandoc -f latex -t plain+\LaTeX{} can now compute: $\fpeval{sin(3.5)/2 + 2e-3}$.+^D+LaTeX can now compute: −0.1733916138448099.+```++§5: assorted `\fpeval` operations:++```+% pandoc -f latex -t plain+\fpeval{pi}, \fpeval{sqrt 2}, \fpeval{exp(0)}, \fpeval{max(1,2,3)},+\fpeval{fact(5)}, \fpeval{abs(-4)}, \fpeval{round(2.345,2)},+\fpeval{floor(2.7)}, \fpeval{ceil(2.2)}, \fpeval{trunc(-2.7)}+^D+3.141592653589793, 1.414213562373095, 1, 3, 120, 4, 2.34, 2, 3, -2+```++§5: the `\inteval` example:++```+% pandoc -f latex -t plain+\LaTeX{} can now compute: The sum of the numbers is $\inteval{1 + 2 + 3}$.+^D+LaTeX can now compute: The sum of the numbers is 6.+```++§6: `\expandableinput` reads a file like `\input`:++```+% pandoc -f latex -t html+Before. \expandableinput{command/bar}+^D+<p>Before. <em>hi there</em></p>+```++§7: `\MakeUppercase`, `\MakeLowercase`, `\MakeTitlecase`:++```+% pandoc -f latex -t plain+\MakeUppercase{hello WORLD ßüé}++\MakeLowercase{hello WORLD ßüé}++\MakeTitlecase{hello WORLD ßüé}+^D+HELLO WORLD SSÜÉ++hello world ßüé++Hello WORLD ßüé+```++§7: the `words` option of `\MakeTitlecase`:++```+% pandoc -f latex -t plain+\MakeTitlecase[words = first]{some words}+\MakeTitlecase[words = all]{some words}+^D+Some words Some Words+```++§7: mathematical content is excluded from case changing, and+`\NoCaseChange` excludes its argument:++```+% pandoc -f latex -t markdown+\MakeUppercase{Some text $y = mx + c$}++\MakeUppercase{\NoCaseChange{iPhone}}+^D+SOME TEXT $y = mx + c$++iPhone+```++§7: `\DeclareTitlecaseExclusions`:++```+% pandoc -f latex -t plain+\DeclareTitlecaseExclusions{a,an,and,on,of,the}+\MakeTitlecase[words = all]{Of mice and men}++\MakeTitlecase[words = all]{The mill on the floss}++\MakeTitlecase[words = all]{The comedy of errors}+^D+Of Mice and Men++The Mill on the Floss++The Comedy of Errors+```++§7: `\DeclareUppercaseMapping` etc. are parsed and ignored:++```+% pandoc -f latex -t plain+\DeclareUppercaseMapping{"01F0}{\v{J}}+\DeclareLowercaseMapping[xx]{"0049}{\i}+Done.+^D+Done.+```++§8: `\textsubscript` and `\textsuperscript`:++```+% pandoc -f latex -t html+A\textsubscript{low} and B\textsuperscript{high}.+^D+<p>A<sub>low</sub> and B<sup>high</sup>.</p>+```++§9: `\listfiles` is parsed and ignored:++```+% pandoc -f latex -t plain+\listfiles[hashes]+Done.+^D+Done.+```
test/command/pandoc-citeproc-392.md view
@@ -19,11 +19,11 @@  ::::: {#refs .references .csl-bib-body} ::: {#ref-james .csl-entry}-[1. ]{.csl-left-margin}[James MRCEL. ]{.csl-right-inline}+[1. ]{.csl-left-margin}[James MRCEL.]{.csl-right-inline} :::  ::: {#ref-macfarlane .csl-entry}-[2. ]{.csl-left-margin}[MacFarlane JG. ]{.csl-right-inline}+[2. ]{.csl-left-margin}[MacFarlane JG.]{.csl-right-inline} ::: ::::: ```
test/command/pandoc-citeproc-chicago-fullnote-bibliography.md view
@@ -127,8 +127,9 @@  [^12]: [Doe, *First Book*](#ref-item1) and nowhere else. -[^13]: Like a citation without author: (), and again (), and now Doe-    with a locator (["Article," 44](#ref-item2)).+[^13]: Like a citation without author: ([](#ref-item1)), and again+    ([](#ref-item1)), and now Doe with a locator (["Article,"+    44](#ref-item2)).  [^14]: *See* [Doe, *First Book*, 32](#ref-item1). ```
+ test/command/typst-highlight.md view
@@ -0,0 +1,98 @@+Highlighted text should be read as a `mark` span, round-tripping with+the Typst writer.++```+% pandoc -f typst -t native+#highlight[hello]+^D+[ Para [ Span ( "" , [ "mark" ] , [] ) [ Str "hello" ] ] ]+```++```+% pandoc -f markdown -t typst+[hello]{.mark}+^D+#highlight[hello]+```++```+% pandoc -f typst -t typst+#highlight[hello]+^D+#highlight[hello]+```++`highlight` may also wrap multiple paragraphs. A pandoc inline cannot+span paragraphs, so the body is split at paragraph breaks and each+paragraph is read as a `mark` span. The same holds for other+inline-styling elements, such as `emph`.++```+% pandoc -f typst -t native+Before.++#highlight[+  Para one.++  Para two.+]+^D+[ Para [ Str "Before." ]+, Para+    [ Span+        ( "" , [ "mark" ] , [] )+        [ SoftBreak , Str "Para" , Space , Str "one." ]+    ]+, Para+    [ Span+        ( "" , [ "mark" ] , [] ) [ Str "Para" , Space , Str "two." ]+    ]+]+```++```+% pandoc -f typst -t typst+Before.++#highlight[+  Para one.++  Para two.+]+^D+Before.++#highlight[ Para one.]++#highlight[Para two.]+```++Highlight bodies may contain math, inline or display, without breaking+the reader.++```+% pandoc -f typst -t native+#highlight[$ a = b $]+^D+[ Para+    [ Span ( "" , [ "mark" ] , [] ) [ Math DisplayMath "a = b" ]+    ]+]+```++```+% pandoc -f typst -t native+#highlight[+  Para.++  $ c = d $+]+^D+[ Para+    [ Span ( "" , [ "mark" ] , [] ) [ SoftBreak , Str "Para." ]+    ]+, Para+    [ Span ( "" , [ "mark" ] , [] ) [ Math DisplayMath "c = d" ]+    ]+]+```
test/command/typst-property-output.md view
@@ -177,6 +177,7 @@   align: (auto,auto,auto,),   [A], [B], [C], )}+ Paragraph after. ``` @@ -231,5 +232,6 @@   align: (auto,auto,auto,),   [A], [B], [C], )+ Paragraph after. ```
test/djot-reader.native view
@@ -299,7 +299,7 @@               , Str "quote:"               ]           , CodeBlock-              ( "" , [ "" ] , [] )+              ( "" , [] , [] )               "sub status {\n    print \"working\";\n}\n"           , Para [ Str "A" , Space , Str "list:" ]           , OrderedList@@ -355,11 +355,11 @@           1 ( "" , [] , [] ) [ Str "Code" , Space , Str "Blocks" ]       , Para [ Str "Code:" ]       , CodeBlock-          ( "" , [ "" ] , [] )+          ( "" , [] , [] )           "---- (should be four hyphens)\n\nsub status {\n    print \"working\";\n}\n\nthis code block is indented by one tab\n"       , Para [ Str "And:" ]       , CodeBlock-          ( "" , [ "" ] , [] )+          ( "" , [] , [] )           "    this code block is indented by two tabs\n\nThese should not be escaped:  \\$ \\\\ \\> \\[ \\{\n"       , HorizontalRule       ]@@ -839,8 +839,7 @@             )           , ( [ Emph [ Str "orange" ] ]             , [ [ Para [ Str "orange" , Space , Str "fruit" ]-                , CodeBlock-                    ( "" , [ "" ] , [] ) "{ orange code block }\n"+                , CodeBlock ( "" , [] , [] ) "{ orange code block }\n"                 , BlockQuote                     [ Para                         [ Str "orange"@@ -1016,10 +1015,10 @@           , Space           , Str "though:"           ]-      , CodeBlock ( "" , [ "" ] , [] ) "<div>\n    foo\n</div>\n"+      , CodeBlock ( "" , [] , [] ) "<div>\n    foo\n</div>\n"       , Para           [ Str "As" , Space , Str "should" , Space , Str "this:" ]-      , CodeBlock ( "" , [ "" ] , [] ) "<div>foo</div>\n"+      , CodeBlock ( "" , [] , [] ) "<div>foo</div>\n"       , Para [ Str "Now," , Space , Str "nested:" ]       , Div           ( "" , [] , [] )@@ -1044,7 +1043,7 @@           ]       , Para [ Str "Multiline:" ]       , Para [ Str "Code" , Space , Str "block:" ]-      , CodeBlock ( "" , [ "" ] , [] ) "<!-- Comment -->\n"+      , CodeBlock ( "" , [] , [] ) "<!-- Comment -->\n"       , Para           [ Str "Just"           , Space@@ -1065,7 +1064,7 @@           , Str "line:"           ]       , Para [ Str "Code:" ]-      , CodeBlock ( "" , [ "" ] , [] ) "<hr />\n"+      , CodeBlock ( "" , [] , [] ) "<hr />\n"       , Para [ Str "Hr\8217s:" ]       , HorizontalRule       ]@@ -1824,7 +1823,7 @@               , Space               , Str "link."               ]-          , CodeBlock ( "" , [ "" ] , [] ) "[not]: /url\n"+          , CodeBlock ( "" , [] , [] ) "[not]: /url\n"           , Para               [ Str "Foo"               , Space@@ -2003,7 +2002,7 @@               , Code ( "" , [] , [] ) "<http://example.com/>"               ]           , CodeBlock-              ( "" , [ "" ] , [] ) "or here: <http://example.com/>\n"+              ( "" , [] , [] ) "or here: <http://example.com/>\n"           , HorizontalRule           ]       ]@@ -2177,7 +2176,7 @@                   , Space                   , Str "items)."                   ]-              , CodeBlock ( "" , [ "" ] , [] ) "{ <code> }\n"+              , CodeBlock ( "" , [] , [] ) "{ <code> }\n"               , Para                   [ Str "If"                   , Space
test/docx/comments.native view
@@ -1,4 +1,152 @@-[Para [Str "I",Space,Str "want",Space,Span ("",["comment-start"],[("id","0"),("author","Jesse Rosenthal"),("date","2016-05-09T16:13:00Z")]) [Str "I",Space,Str "left",Space,Str "a",Space,Str "comment."],Str "some",Space,Str "text",Space,Str "to",Space,Str "have",Space,Str "a",Space,Str "comment",Space,Span ("",["comment-end"],[("id","0")]) [],Str "on",Space,Str "it."]-,Para [Str "This",Space,Str "is",Space,Span ("",["comment-start"],[("id","1"),("author","Jesse Rosenthal"),("date","2016-05-09T16:13:00Z")]) [Str "A",Space,Str "comment",Space,Str "across",Space,Str "paragraphs."],Str "a",Space,Str "new",Space,Str "paragraph."]-,Para [Str "And",Space,Str "so",Span ("",["comment-end"],[("id","1")]) [],Space,Str "is",Space,Str "this."]-,Para [Str "One",Space,Span ("",["comment-start"],[("id","2"),("author","Jesse Rosenthal"),("date","2016-05-09T16:14:00Z")]) [Str "This",Space,Str "one",Space,Str "has",Space,Str "multiple",Space,Str "paragraphs.",LineBreak, Str "See?"],Str "more",Span ("",["comment-end"],[("id","2")]) [],Str ".",Space,Str "And",Space,Str "this",Space,Str "is",Space,Str "one",Space,Str "with",Space,Str "a",Space,Span ("",["comment-start"],[("id","3"),("author","Jesse Rosenthal"),("date","2016-06-22T14:35:00Z")]) [Str "Do",Space,Str "something."],Span ("",["comment-start"],[("id","4"),("author","Jesse Rosenthal"),("date","2016-06-22T14:36:00Z")]) [Str "Do",Space,Str "something",Space,Str "else."],Str "comment",Space,Str "in",Space,Str "a",Space,Str "comment",Span ("",["comment-end"],[("id","3")]) [Span ("",["comment-end"],[("id","4")]) []],Str "."]]+Pandoc+  Meta { unMeta = fromList [] }+  [ Para+      [ Str "I"+      , Space+      , Str "want"+      , Space+      , Span+          ( ""+          , [ "comment-start" ]+          , [ ( "comment-id" , "0" )+            , ( "author" , "Jesse Rosenthal" )+            , ( "date" , "2016-05-09T16:13:00Z" )+            ]+          )+          [ Str "I"+          , Space+          , Str "left"+          , Space+          , Str "a"+          , Space+          , Str "comment."+          ]+      , Str "some"+      , Space+      , Str "text"+      , Space+      , Str "to"+      , Space+      , Str "have"+      , Space+      , Str "a"+      , Space+      , Str "comment"+      , Space+      , Span+          ( "" , [ "comment-end" ] , [ ( "comment-id" , "0" ) ] ) []+      , Str "on"+      , Space+      , Str "it."+      ]+  , Para+      [ Str "This"+      , Space+      , Str "is"+      , Space+      , Span+          ( ""+          , [ "comment-start" ]+          , [ ( "comment-id" , "1" )+            , ( "author" , "Jesse Rosenthal" )+            , ( "date" , "2016-05-09T16:13:00Z" )+            ]+          )+          [ Str "A"+          , Space+          , Str "comment"+          , Space+          , Str "across"+          , Space+          , Str "paragraphs."+          ]+      , Str "a"+      , Space+      , Str "new"+      , Space+      , Str "paragraph."+      ]+  , Para+      [ Str "And"+      , Space+      , Str "so"+      , Span+          ( "" , [ "comment-end" ] , [ ( "comment-id" , "1" ) ] ) []+      , Space+      , Str "is"+      , Space+      , Str "this."+      ]+  , Para+      [ Str "One"+      , Space+      , Span+          ( ""+          , [ "comment-start" ]+          , [ ( "comment-id" , "2" )+            , ( "author" , "Jesse Rosenthal" )+            , ( "date" , "2016-05-09T16:14:00Z" )+            ]+          )+          [ Str "This"+          , Space+          , Str "one"+          , Space+          , Str "has"+          , Space+          , Str "multiple"+          , Space+          , Str "paragraphs."+          , LineBreak+          , Str "See?"+          ]+      , Str "more"+      , Span+          ( "" , [ "comment-end" ] , [ ( "comment-id" , "2" ) ] ) []+      , Str "."+      , Space+      , Str "And"+      , Space+      , Str "this"+      , Space+      , Str "is"+      , Space+      , Str "one"+      , Space+      , Str "with"+      , Space+      , Str "a"+      , Space+      , Span+          ( ""+          , [ "comment-start" ]+          , [ ( "comment-id" , "3" )+            , ( "author" , "Jesse Rosenthal" )+            , ( "date" , "2016-06-22T14:35:00Z" )+            ]+          )+          [ Str "Do" , Space , Str "something." ]+      , Span+          ( ""+          , [ "comment-start" ]+          , [ ( "comment-id" , "4" )+            , ( "author" , "Jesse Rosenthal" )+            , ( "date" , "2016-06-22T14:36:00Z" )+            ]+          )+          [ Str "Do" , Space , Str "something" , Space , Str "else." ]+      , Str "comment"+      , Space+      , Str "in"+      , Space+      , Str "a"+      , Space+      , Str "comment"+      , Span+          ( "" , [ "comment-end" ] , [ ( "comment-id" , "3" ) ] )+          [ Span+              ( "" , [ "comment-end" ] , [ ( "comment-id" , "4" ) ] ) []+          ]+      , Str "."+      ]+  ]
+ test/docx/csl_bibliography.native view
@@ -0,0 +1,50 @@+[ Header 1 ( "refs-title" , [] , [] ) [ Str "References" ]+, Div+    ( "refs"+    , [ "references" , "csl-bib-body" , "hanging-indent" ]+    , [ ( "entry-spacing" , "1.0" ) , ( "line-spacing" , "2.0" ) ]+    )+    [ Div+        ( "ref-doe2020" , [ "csl-entry" ] , [] )+        [ Para+            [ Str "Doe,"+            , Space+            , Str "J.,"+            , Space+            , Str "&"+            , Space+            , Str "Smith,"+            , Space+            , Str "J."+            , Space+            , Str "(2020)."+            , Space+            , Str "A"+            , Space+            , Str "very"+            , Space+            , Str "long"+            , Space+            , Str "title"+            , Space+            , Str "that"+            , Space+            , Str "wraps"+            , Space+            , Str "onto"+            , Space+            , Str "multiple"+            , Space+            , Str "lines."+            , Space+            , Emph [ Str "Journal" , Space , Str "of" , Space , Str "Testing" ]+            , Str ","+            , Space+            , Emph [ Str "12" ]+            , Str ","+            , Space+            , Str "1\8211\&25."+            ]+        ]+    ]+]
test/docx/golden/block_quotes.docx view

binary file changed (10692 → 10689 bytes)

test/docx/golden/comments.docx view

binary file changed (10824 → 10824 bytes)

+ test/docx/golden/csl_bibliography.docx view

binary file changed (absent → 10675 bytes)

test/docx/golden/custom_style_preserve.docx view

binary file changed (11236 → 11233 bytes)

test/docx/golden/headers.docx view

binary file changed (10655 → 10776 bytes)

test/docx/golden/image.docx view

binary file changed (27383 → 27434 bytes)

test/docx/golden/links.docx view

binary file changed (10842 → 10853 bytes)

test/docx/golden/lists.docx view

binary file changed (11034 → 11048 bytes)

test/docx/golden/lists_continuing.docx view

binary file changed (10702 → 10677 bytes)

test/docx/golden/lists_div_bullets.docx view

binary file changed (10685 → 10670 bytes)

test/docx/golden/lists_multiple_initial.docx view

binary file changed (10918 → 10903 bytes)

test/docx/golden/lists_restarting.docx view

binary file changed (10699 → 10686 bytes)

test/docx/golden/nested_anchors_in_header.docx view

binary file changed (10845 → 10874 bytes)

test/docx/golden/notes.docx view

binary file changed (10615 → 10625 bytes)

test/docx/golden/table_with_list_cell.docx view

binary file changed (10946 → 10951 bytes)

test/docx/golden/tables-default-widths.docx view

binary file changed (10882 → 10876 bytes)

test/docx/golden/tables.docx view

binary file changed (10870 → 10887 bytes)

test/docx/golden/task_list.docx view

binary file changed (10783 → 10773 bytes)

test/docx/golden/track_changes_scrubbed_metadata.docx view

binary file changed (10617 → 10591 bytes)

binary file changed (absent → 15874 bytes)

@@ -0,0 +1,112 @@+Pandoc+  Meta { unMeta = fromList [] }+  [ Para+      [ Str "An"+      , Space+      , Link+          ( "" , [] , [] )+          [ Str "external" , Space , Str "link" ]+          ( "https://example.org/report.pdf"+          , "Annual report, PDF, 2 MB"+          )+      , Space+      , Str "with"+      , Space+      , Str "a"+      , Space+      , Str "ScreenTip."+      ]+  , Para+      [ Str "An"+      , Space+      , Link+          ( "" , [] , [] )+          [ Str "external"+          , Space+          , Str "link"+          , Space+          , Str "to"+          , Space+          , Str "a"+          , Space+          , Str "fragment"+          ]+          ( "https://example.org/guide.html#install"+          , "Installation steps"+          )+      , Str ","+      , Space+      , Str "with"+      , Space+      , Str "a"+      , Space+      , Str "ScreenTip."+      ]+  , Para+      [ Str "An"+      , Space+      , Link+          ( "" , [] , [] )+          [ Str "internal" , Space , Str "link" ]+          ( "#methods" , "Jump to the Methods section" )+      , Space+      , Str "with"+      , Space+      , Str "a"+      , Space+      , Str "ScreenTip."+      ]+  , Para+      [ Str "A"+      , Space+      , Link+          ( "" , [] , [] )+          [ Str "link" ]+          ( "https://example.com/" , "" )+      , Space+      , Str "without"+      , Space+      , Str "a"+      , Space+      , Str "ScreenTip."+      ]+  , Para+      [ Str "A"+      , Space+      , Str "ScreenTip"+      , Space+      , Str "with"+      , Space+      , Link+          ( "" , [] , [] )+          [ Str "markup" , Space , Str "characters" ]+          ( "https://example.org/escape" , "Q&A: \"Why?\" <draft>" )+      , Str "."+      ]+  , Para+      [ Str "A"+      , Space+      , Link+          ( "" , [] , [] )+          [ Str "field-code" , Space , Str "link" ]+          ( "https://example.org/field"+          , "ScreenTip from a field code"+          )+      , Space+      , Str "with"+      , Space+      , Str "a"+      , Space+      , Str "ScreenTip."+      ]+  , Para+      [ Span ( "methods" , [ "anchor" ] , [] ) []+      , Str "Methods"+      , Space+      , Str "are"+      , Space+      , Str "described"+      , Space+      , Str "here."+      ]+  ]
test/docx/table_with_list_cell.native view
@@ -13,7 +13,7 @@                AlignDefault                (RowSpan 1)                (ColSpan 1)-               [ Plain+               [ Para                    [ Str "Cell"                    , Space                    , Str "with"@@ -26,7 +26,7 @@                AlignDefault                (RowSpan 1)                (ColSpan 1)-               [ Plain+               [ Para                    [ Str "Cell"                    , Space                    , Str "with"
test/docx/track_changes_scrubbed_metadata.native view
@@ -1,9 +1,28 @@-[Para [ Str "Here", Space, Str "is", Space, Str "a", Space-      , Span ("",["deletion"],[("author","Author")]) [Str "dummy"]-      , Span ("",["insertion"],[("author","Author")]) [Str "test"]+Pandoc+  Meta { unMeta = fromList [] }+  [ Para+      [ Str "Here"       , Space-      , Span ("",["comment-start"],[("id","3"),("author","Author")]) -        [Str "With",Space,Str "a",Space,Str "comment!"]-      , Str "document",Span ("",["comment-end"],[("id","3")]) [],Str "."+      , Str "is"+      , Space+      , Str "a"+      , Space+      , Span+          ( "" , [ "deletion" ] , [ ( "author" , "Author" ) ] )+          [ Str "dummy" ]+      , Span+          ( "" , [ "insertion" ] , [ ( "author" , "Author" ) ] )+          [ Str "test" ]+      , Space+      , Span+          ( ""+          , [ "comment-start" ]+          , [ ( "comment-id" , "3" ) , ( "author" , "Author" ) ]+          )+          [ Str "With" , Space , Str "a" , Space , Str "comment!" ]+      , Str "document"+      , Span+          ( "" , [ "comment-end" ] , [ ( "comment-id" , "3" ) ] ) []+      , Str "."       ]-]+  ]
test/jats-reader.native view
@@ -835,7 +835,9 @@               , Space               , Str "1"               ]-              ( "Title 3" , "" )+              ( "https://lh3.googleusercontent.com/dB7iirJ3ncQaVMBGE2YX-WCeoAVIChb6NAzoFcKCFChMsrixJvD7ZRbvcaC-ceXEzXYaoH4K5vaoRDsUyBHFkpIDPnsn3bnzovbvi0a2Gg=s660"+              , "Title 3"+              )           ]       , Para [ Math DisplayMath "e=mc^2" ]       ]
test/jats-reader.xml view
@@ -227,7 +227,7 @@       </abstract>       <graphic id="graphic003"       xlink:href="https://lh3.googleusercontent.com/dB7iirJ3ncQaVMBGE2YX-WCeoAVIChb6NAzoFcKCFChMsrixJvD7ZRbvcaC-ceXEzXYaoH4K5vaoRDsUyBHFkpIDPnsn3bnzovbvi0a2Gg=s660"-      xlink:href="Title 3">+      xlink:title="Title 3">         <alt-text>Alternative text 1</alt-text>         <caption><p>Google doodle from 14 March 2003</p></caption>       </graphic>@@ -236,7 +236,7 @@         <mml:math display="block" xmlns:mml="http://www.w3.org/1998/Math/MathML"><mml:mrow><mml:mi>e</mml:mi><mml:mo>=</mml:mo><mml:mi>m</mml:mi><mml:msup><mml:mi>c</mml:mi><mml:mn>2</mml:mn></mml:msup></mml:mrow></mml:math>         <graphic id="graphic004"         xlink:href="https://lh3.googleusercontent.com/dB7iirJ3ncQaVMBGE2YX-WCeoAVIChb6NAzoFcKCFChMsrixJvD7ZRbvcaC-ceXEzXYaoH4K5vaoRDsUyBHFkpIDPnsn3bnzovbvi0a2Gg=s660"-        xlink:href="Title 4">+        xlink:title="Title 4">           <alt-text>Alternative text 2</alt-text>           <caption><p>Google doodle from 14 March 2003</p></caption>         </graphic>
test/markdown-reader-more.native view
@@ -1069,7 +1069,7 @@                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Plain+                  [ Para                       [ Str "c"                       , SoftBreak                       , Str "c"
test/mediawiki-reader.native view
@@ -864,13 +864,13 @@                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Para [ Str "Orange" ] ]+                  [ Plain [ Str "Orange" ] ]               , Cell                   ( "" , [] , [] )                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Para [ Str "Apple" ] ]+                  [ Plain [ Str "Apple" ] ]               ]           , Row               ( "" , [] , [] )@@ -879,13 +879,13 @@                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Para [ Str "Bread" ] ]+                  [ Plain [ Str "Bread" ] ]               , Cell                   ( "" , [] , [] )                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Para [ Str "Pie" ] ]+                  [ Plain [ Str "Pie" ] ]               ]           , Row               ( "" , [] , [] )@@ -894,13 +894,13 @@                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Para [ Str "Butter" ] ]+                  [ Plain [ Str "Butter" ] ]               , Cell                   ( "" , [] , [] )                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Para [ Str "Ice" , Space , Str "cream" ] ]+                  [ Plain [ Str "Ice" , Space , Str "cream" ] ]               ]           ]       ]@@ -922,13 +922,13 @@                  AlignDefault                  (RowSpan 1)                  (ColSpan 1)-                 [ Para [ Str "Orange" ] ]+                 [ Plain [ Str "Orange" ] ]              , Cell                  ( "" , [] , [] )                  AlignDefault                  (RowSpan 1)                  (ColSpan 1)-                 [ Para [ Str "Apple" ] ]+                 [ Plain [ Str "Apple" ] ]              ]          ])       [ TableBody@@ -942,13 +942,13 @@                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Para [ Str "Bread" ] ]+                  [ Plain [ Str "Bread" ] ]               , Cell                   ( "" , [] , [] )                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Para [ Str "Pie" ] ]+                  [ Plain [ Str "Pie" ] ]               ]           , Row               ( "" , [] , [] )@@ -957,13 +957,13 @@                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Para [ Str "Butter" ] ]+                  [ Plain [ Str "Butter" ] ]               , Cell                   ( "" , [] , [] )                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Para [ Str "Ice" , Space , Str "cream" ] ]+                  [ Plain [ Str "Ice" , Space , Str "cream" ] ]               ]           ]       ]@@ -1043,19 +1043,19 @@                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Para [ Str "Orange" ] ]+                  [ Plain [ Str "Orange" ] ]               , Cell                   ( "" , [] , [] )                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Para [ Str "Apple" ] ]+                  [ Plain [ Str "Apple" ] ]               , Cell                   ( "" , [] , [] )                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Para [ Str "more" ] ]+                  [ Plain [ Str "more" ] ]               ]           , Row               ( "" , [] , [] )@@ -1064,19 +1064,19 @@                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Para [ Str "Bread" ] ]+                  [ Plain [ Str "Bread" ] ]               , Cell                   ( "" , [] , [] )                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Para [ Str "Pie" ] ]+                  [ Plain [ Str "Pie" ] ]               , Cell                   ( "" , [] , [] )                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Para [ Str "more" ] ]+                  [ Plain [ Str "more" ] ]               ]           , Row               ( "" , [] , [] )@@ -1085,19 +1085,19 @@                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Para [ Str "Butter" ] ]+                  [ Plain [ Str "Butter" ] ]               , Cell                   ( "" , [] , [] )                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Para [ Str "Ice" , Space , Str "cream" ] ]+                  [ Plain [ Str "Ice" , Space , Str "cream" ] ]               , Cell                   ( "" , [] , [] )                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Para [ Str "and" , Space , Str "more" ] ]+                  [ Plain [ Str "and" , Space , Str "more" ] ]               ]           ]       ]@@ -1118,19 +1118,19 @@                  AlignLeft                  (RowSpan 1)                  (ColSpan 1)-                 [ Para [ Str "Left" ] ]+                 [ Plain [ Str "Left" ] ]              , Cell                  ( "" , [] , [] )                  AlignRight                  (RowSpan 1)                  (ColSpan 1)-                 [ Para [ Str "Right" ] ]+                 [ Plain [ Str "Right" ] ]              , Cell                  ( "" , [] , [] )                  AlignCenter                  (RowSpan 1)                  (ColSpan 1)-                 [ Para [ Str "Center" ] ]+                 [ Plain [ Str "Center" ] ]              ]          ])       [ TableBody@@ -1144,19 +1144,19 @@                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Para [ Str "left" ] ]+                  [ Plain [ Str "left" ] ]               , Cell                   ( "" , [] , [] )                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Para [ Str "15.00" ] ]+                  [ Plain [ Str "15.00" ] ]               , Cell                   ( "" , [] , [] )                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Para [ Str "centered" ] ]+                  [ Plain [ Str "centered" ] ]               ]           , Row               ( "" , [] , [] )@@ -1165,19 +1165,19 @@                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Para [ Str "more" ] ]+                  [ Plain [ Str "more" ] ]               , Cell                   ( "" , [] , [] )                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Para [ Str "2.0" ] ]+                  [ Plain [ Str "2.0" ] ]               , Cell                   ( "" , [] , [] )                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Para [ Str "more" ] ]+                  [ Plain [ Str "more" ] ]               ]           ]       ]@@ -1236,13 +1236,13 @@                                  AlignDefault                                  (RowSpan 1)                                  (ColSpan 1)-                                 [ Para [ Str "fruit" ] ]+                                 [ Plain [ Str "fruit" ] ]                              , Cell                                  ( "" , [] , [] )                                  AlignDefault                                  (RowSpan 1)                                  (ColSpan 1)-                                 [ Para [ Str "topping" ] ]+                                 [ Plain [ Str "topping" ] ]                              ]                          ])                       [ TableBody@@ -1256,13 +1256,13 @@                                   AlignDefault                                   (RowSpan 1)                                   (ColSpan 1)-                                  [ Para [ Str "apple" ] ]+                                  [ Plain [ Str "apple" ] ]                               , Cell                                   ( "" , [] , [] )                                   AlignDefault                                   (RowSpan 1)                                   (ColSpan 1)-                                  [ Para+                                  [ Plain                                       [ Str "ice"                                       , Space                                       , Str "cream"@@ -1308,7 +1308,7 @@                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Para [ Str "Orange" ] ]+                  [ Plain [ Str "Orange" ] ]               ]           ]       ]@@ -1337,13 +1337,13 @@                  AlignDefault                  (RowSpan 1)                  (ColSpan 1)-                 [ Para [ Str "fruit" ] ]+                 [ Plain [ Str "fruit" ] ]              , Cell                  ( "" , [] , [] )                  AlignDefault                  (RowSpan 1)                  (ColSpan 1)-                 [ Para [ Str "topping" ] ]+                 [ Plain [ Str "topping" ] ]              ]          ])       [ TableBody@@ -1357,13 +1357,13 @@                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Para [ Str "apple" ] ]+                  [ Plain [ Str "apple" ] ]               , Cell                   ( "" , [] , [] )                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Para [ Str "ice" , Space , Str "cream" ] ]+                  [ Plain [ Str "ice" , Space , Str "cream" ] ]               ]           ]       ]
test/odt/native/simpleTableWithCaption.native view
@@ -1,1 +1,1 @@-[Table ("",[],[]) (Caption Nothing [Para [Str "Table",Space,Str "1:",Space,Str "Some",Space,Str "caption",Space,Str "for",Space,Str "a",Space,Str "table"]]) [(AlignDefault,ColWidthDefault),(AlignDefault,ColWidthDefault)] (TableHead ("",[],[]) []) [TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Content"]],Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "More",Space,Str "content"]]]]] (TableFoot ("",[],[]) []),Para []]+[Table ("",[],[]) (Caption Nothing [Para [Str "Table",Space,Str "1:",Space,Str "Some",Space,Str "caption",Space,Str "for",Space,Str "a",Space,Str "table"]]) [(AlignDefault,ColWidthDefault),(AlignDefault,ColWidthDefault)] (TableHead ("",[],[]) []) [TableBody ("",[],[]) (RowHeadColumns 0) [] [Row ("",[],[]) [Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "Content"]],Cell ("",[],[]) AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str "More",Space,Str "content"]]]]] (TableFoot ("",[],[]) []),Para [Emph []]]
test/rst-reader.native view
@@ -1458,7 +1458,7 @@                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Plain+                  [ Para                       [ Str "c"                       , SoftBreak                       , Str "c"
test/test-pandoc.hs view
@@ -9,6 +9,7 @@ import GHC.IO.Encoding import Test.Tasty import qualified Tests.Command+import qualified Tests.ImageSize import qualified Tests.Old import qualified Tests.Readers.Creole import qualified Tests.Readers.Docx@@ -65,6 +66,7 @@         , testGroup "Shared" Tests.Shared.tests         , testGroup "MediaBag" Tests.MediaBag.tests         , testGroup "XML" Tests.XML.tests+        , testGroup "ImageSize" Tests.ImageSize.tests         , testGroup "Writers"           [ testGroup "Native" Tests.Writers.Native.tests           , testGroup "ConTeXt" Tests.Writers.ConTeXt.tests
test/typst-reader.native view
@@ -88,49 +88,49 @@                       AlignDefault                       (RowSpan 1)                       (ColSpan 1)-                      [ Para [ Math InlineMath "F_{1}" ] ]+                      [ Plain [ Math InlineMath "F_{1}" ] ]                   , Cell                       ( "" , [] , [] )                       AlignDefault                       (RowSpan 1)                       (ColSpan 1)-                      [ Para [ Math InlineMath "F_{2}" ] ]+                      [ Plain [ Math InlineMath "F_{2}" ] ]                   , Cell                       ( "" , [] , [] )                       AlignDefault                       (RowSpan 1)                       (ColSpan 1)-                      [ Para [ Math InlineMath "F_{3}" ] ]+                      [ Plain [ Math InlineMath "F_{3}" ] ]                   , Cell                       ( "" , [] , [] )                       AlignDefault                       (RowSpan 1)                       (ColSpan 1)-                      [ Para [ Math InlineMath "F_{4}" ] ]+                      [ Plain [ Math InlineMath "F_{4}" ] ]                   , Cell                       ( "" , [] , [] )                       AlignDefault                       (RowSpan 1)                       (ColSpan 1)-                      [ Para [ Math InlineMath "F_{5}" ] ]+                      [ Plain [ Math InlineMath "F_{5}" ] ]                   , Cell                       ( "" , [] , [] )                       AlignDefault                       (RowSpan 1)                       (ColSpan 1)-                      [ Para [ Math InlineMath "F_{6}" ] ]+                      [ Plain [ Math InlineMath "F_{6}" ] ]                   , Cell                       ( "" , [] , [] )                       AlignDefault                       (RowSpan 1)                       (ColSpan 1)-                      [ Para [ Math InlineMath "F_{7}" ] ]+                      [ Plain [ Math InlineMath "F_{7}" ] ]                   , Cell                       ( "" , [] , [] )                       AlignDefault                       (RowSpan 1)                       (ColSpan 1)-                      [ Para [ Math InlineMath "F_{8}" ] ]+                      [ Plain [ Math InlineMath "F_{8}" ] ]                   ]               , Row                   ( "" , [] , [] )@@ -139,49 +139,49 @@                       AlignDefault                       (RowSpan 1)                       (ColSpan 1)-                      [ Para [ Str "1" ] ]+                      [ Plain [ Str "1" ] ]                   , Cell                       ( "" , [] , [] )                       AlignDefault                       (RowSpan 1)                       (ColSpan 1)-                      [ Para [ Str "1" ] ]+                      [ Plain [ Str "1" ] ]                   , Cell                       ( "" , [] , [] )                       AlignDefault                       (RowSpan 1)                       (ColSpan 1)-                      [ Para [ Str "2" ] ]+                      [ Plain [ Str "2" ] ]                   , Cell                       ( "" , [] , [] )                       AlignDefault                       (RowSpan 1)                       (ColSpan 1)-                      [ Para [ Str "3" ] ]+                      [ Plain [ Str "3" ] ]                   , Cell                       ( "" , [] , [] )                       AlignDefault                       (RowSpan 1)                       (ColSpan 1)-                      [ Para [ Str "5" ] ]+                      [ Plain [ Str "5" ] ]                   , Cell                       ( "" , [] , [] )                       AlignDefault                       (RowSpan 1)                       (ColSpan 1)-                      [ Para [ Str "8" ] ]+                      [ Plain [ Str "8" ] ]                   , Cell                       ( "" , [] , [] )                       AlignDefault                       (RowSpan 1)                       (ColSpan 1)-                      [ Para [ Str "13" ] ]+                      [ Plain [ Str "13" ] ]                   , Cell                       ( "" , [] , [] )                       AlignDefault                       (RowSpan 1)                       (ColSpan 1)-                      [ Para [ Str "21" ] ]+                      [ Plain [ Str "21" ] ]                   ]               ]           ]@@ -204,11 +204,11 @@                       , Space                       , Str "Undergrads"                       ]-                  , SoftBreak                   ]                   ( "https://github.com/johanvx/typst-undergradmath"                   , ""                   )+              , SoftBreak               ]           ]       , Para@@ -294,7 +294,7 @@                       AlignDefault                       (RowSpan 1)                       (ColSpan 1)-                      [ Para+                      [ Plain                           [ Span                               ( "" , [ "box" ] , [] )                               [ Str "2023-05-22"@@ -308,7 +308,7 @@                       AlignDefault                       (RowSpan 1)                       (ColSpan 1)-                      [ Para+                      [ Plain                           [ Str "This"                           , Space                           , Str "is"@@ -349,7 +349,7 @@                       AlignDefault                       (RowSpan 1)                       (ColSpan 1)-                      [ Para+                      [ Plain                           [ Span                               ( "" , [ "box" ] , [] ) [ Str "\128166" ]                           ]@@ -359,7 +359,7 @@                       AlignDefault                       (RowSpan 1)                       (ColSpan 1)-                      [ Para+                      [ Plain                           [ Str "Get"                           , Space                           , Str "this"@@ -404,7 +404,7 @@                       AlignDefault                       (RowSpan 1)                       (ColSpan 1)-                      [ Para+                      [ Plain                           [ Span                               ( "" , [ "box" ] , [] )                               [ Str "No"@@ -420,7 +420,7 @@                       AlignDefault                       (RowSpan 1)                       (ColSpan 1)-                      [ Para+                      [ Plain                           [ Str "Don\8217t"                           , Space                           , Str "know"@@ -558,7 +558,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "x^{2}"@@ -572,7 +572,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\sqrt{2}"@@ -595,7 +595,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "x_{i,j}"@@ -609,7 +609,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\frac{2}{3}"@@ -683,7 +683,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\alpha"@@ -697,7 +697,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\xi"@@ -720,7 +720,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\beta"@@ -734,7 +734,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\959"@@ -751,7 +751,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\gamma"@@ -771,7 +771,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\pi"@@ -794,7 +794,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\delta"@@ -814,7 +814,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\varpi"@@ -831,7 +831,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\epsilon"@@ -845,7 +845,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\rho"@@ -862,7 +862,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\varepsilon"@@ -876,7 +876,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\varrho"@@ -893,7 +893,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\zeta"@@ -907,7 +907,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\sigma"@@ -930,7 +930,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\eta"@@ -944,7 +944,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\varsigma"@@ -966,7 +966,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\theta"@@ -986,7 +986,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\tau"@@ -1003,7 +1003,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\vartheta"@@ -1017,7 +1017,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\upsilon"@@ -1040,7 +1040,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\iota"@@ -1054,7 +1054,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\phi"@@ -1077,7 +1077,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\kappa"@@ -1091,7 +1091,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\varphi"@@ -1108,7 +1108,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\lambda"@@ -1128,7 +1128,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\chi"@@ -1145,7 +1145,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\mu"@@ -1159,7 +1159,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\psi"@@ -1182,7 +1182,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\nu"@@ -1196,7 +1196,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\omega"@@ -1246,7 +1246,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\cup"@@ -1260,7 +1260,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\mathbb{R}"@@ -1277,7 +1277,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\forall"@@ -1294,7 +1294,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\cap"@@ -1308,7 +1308,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\mathbb{Z}"@@ -1325,7 +1325,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\exists"@@ -1342,7 +1342,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\subset"@@ -1356,7 +1356,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\mathbb{Q}"@@ -1373,7 +1373,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\neg"@@ -1390,7 +1390,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\subseteq"@@ -1404,7 +1404,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\mathbb{N}"@@ -1421,7 +1421,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\vee"@@ -1438,7 +1438,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\supset"@@ -1452,7 +1452,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\mathbb{C}"@@ -1469,7 +1469,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\land"@@ -1486,7 +1486,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\supseteq"@@ -1500,7 +1500,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\varnothing"@@ -1514,7 +1514,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\vdash"@@ -1531,7 +1531,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\in"@@ -1545,7 +1545,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\varnothing"@@ -1559,7 +1559,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\models"@@ -1576,7 +1576,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\notin"@@ -1590,7 +1590,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\1488"@@ -1604,7 +1604,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\smallsetminus"@@ -1814,7 +1814,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "f'"@@ -1831,7 +1831,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\dot{a}"@@ -1845,10 +1845,10 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )-                                  [ Math InlineMath "\\widetilde{a}"+                                  [ Math InlineMath "\\tilde{a}"                                   , Str "\8192"                                   , Code ( "" , [] , [] ) "tilde(a)"                                   ]@@ -1862,7 +1862,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "f''"@@ -1877,7 +1877,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\ddot{a}"@@ -1891,7 +1891,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\overline{a}"@@ -1908,7 +1908,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\Sigma^{\\ast}"@@ -1922,7 +1922,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\hat{a}"@@ -1936,7 +1936,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math@@ -2181,7 +2181,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\sin"@@ -2195,7 +2195,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\sinh"@@ -2209,7 +2209,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\arcsin"@@ -2226,7 +2226,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\cos"@@ -2240,7 +2240,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\cosh"@@ -2254,7 +2254,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\arccos"@@ -2271,7 +2271,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\tan"@@ -2285,7 +2285,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\tanh"@@ -2299,7 +2299,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\arctan"@@ -2316,7 +2316,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\sec"@@ -2330,7 +2330,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\coth"@@ -2344,7 +2344,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\min"@@ -2361,7 +2361,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\csc"@@ -2375,7 +2375,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\det"@@ -2389,7 +2389,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\max"@@ -2406,7 +2406,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\cot"@@ -2420,7 +2420,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\dim"@@ -2434,7 +2434,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\inf"@@ -2451,7 +2451,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\exp"@@ -2465,7 +2465,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\ker"@@ -2479,7 +2479,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\sup"@@ -2496,7 +2496,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\log"@@ -2510,7 +2510,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\deg"@@ -2524,7 +2524,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\liminf"@@ -2541,7 +2541,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\ln"@@ -2555,7 +2555,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\arg"@@ -2569,7 +2569,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\limsup"@@ -2586,7 +2586,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\lg"@@ -2600,7 +2600,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\gcd"@@ -2614,7 +2614,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\lim"@@ -2651,7 +2651,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "<"@@ -2668,7 +2668,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\angle"@@ -2682,7 +2682,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\cdot"@@ -2699,7 +2699,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\leq"@@ -2716,7 +2716,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\measuredangle"@@ -2730,7 +2730,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\pm"@@ -2747,7 +2747,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath ">"@@ -2764,7 +2764,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\ell"@@ -2778,7 +2778,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\mp"@@ -2795,7 +2795,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\geq"@@ -2812,7 +2812,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\parallel"@@ -2826,7 +2826,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\times"@@ -2843,7 +2843,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\neq"@@ -2860,7 +2860,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "45{^\\circ}"@@ -2874,7 +2874,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\div"@@ -2891,7 +2891,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\ll"@@ -2908,7 +2908,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\cong"@@ -2922,7 +2922,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\ast"@@ -2942,7 +2942,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\gg"@@ -2959,7 +2959,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\ncong"@@ -2974,7 +2974,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\mid"@@ -2991,7 +2991,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\approx"@@ -3005,7 +3005,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\sim"@@ -3019,7 +3019,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\nmid"@@ -3036,7 +3036,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\asymp"@@ -3055,7 +3055,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\simeq"@@ -3069,7 +3069,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "n!"@@ -3086,7 +3086,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\equiv"@@ -3100,7 +3100,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\nsim"@@ -3114,7 +3114,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\partial"@@ -3131,7 +3131,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\prec"@@ -3145,7 +3145,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\oplus"@@ -3159,7 +3159,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\nabla"@@ -3176,7 +3176,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\preceq"@@ -3190,7 +3190,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\ominus"@@ -3204,7 +3204,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\295"@@ -3221,7 +3221,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\succ"@@ -3235,7 +3235,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\odot"@@ -3249,7 +3249,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\circ"@@ -3268,7 +3268,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\succeq"@@ -3282,7 +3282,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\otimes"@@ -3296,7 +3296,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\star"@@ -3313,7 +3313,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\propto"@@ -3327,7 +3327,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\oslash"@@ -3346,7 +3346,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\sqrt{}"@@ -3363,7 +3363,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\doteq"@@ -3382,7 +3382,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\upharpoonright"@@ -3396,7 +3396,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\10003"@@ -3483,7 +3483,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\rightarrow"@@ -3500,7 +3500,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\mapsto"@@ -3520,7 +3520,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\nrightarrow"@@ -3534,7 +3534,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\longmapsto"@@ -3553,7 +3553,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\longrightarrow"@@ -3567,7 +3567,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\leftarrow"@@ -3587,7 +3587,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\Rightarrow"@@ -3605,7 +3605,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math@@ -3627,7 +3627,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\nRightarrow"@@ -3643,7 +3643,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\downarrow"@@ -3660,7 +3660,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\Longrightarrow"@@ -3676,7 +3676,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\uparrow"@@ -3693,7 +3693,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\rightsquigarrow"@@ -3708,7 +3708,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\updownarrow"@@ -3837,7 +3837,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\int"@@ -3851,7 +3851,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\iiint"@@ -3866,7 +3866,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\bigcup"@@ -3883,7 +3883,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\iint"@@ -3898,7 +3898,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\oint"@@ -3913,7 +3913,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\bigcap"@@ -3949,7 +3949,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "()"@@ -3963,7 +3963,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\langle\\rangle"@@ -3979,7 +3979,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math@@ -3997,7 +3997,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math InlineMath "\\lbrack\\rbrack"@@ -4011,7 +4011,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math@@ -4027,7 +4027,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math@@ -4046,7 +4046,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math@@ -4061,7 +4061,7 @@                           AlignDefault                           (RowSpan 1)                           (ColSpan 1)-                          [ Para+                          [ Plain                               [ Span                                   ( "" , [ "box" ] , [] )                                   [ Math@@ -5345,5 +5345,216 @@           , Space           , Str "2023-05-22"           ]+      ]+  , Header 1 ( "" , [] , [] ) [ Str "Citations" ]+  , Para+      [ Str "Normal:"+      , Space+      , Cite+          [ Citation+              { citationId = "brown01"+              , citationPrefix = []+              , citationSuffix = []+              , citationMode = NormalCitation+              , citationNoteNum = 0+              , citationHash = 0+              }+          ]+          [ Str "[brown01]" ]+      , Space+      , Str "or"+      , Space+      , Cite+          [ Citation+              { citationId = "brown01"+              , citationPrefix = []+              , citationSuffix = []+              , citationMode = NormalCitation+              , citationNoteNum = 0+              , citationHash = 0+              }+          ]+          [ Str "[brown01]" ]+      , Str "."+      ]+  , Para+      [ Str "Prose:"+      , Space+      , Cite+          [ Citation+              { citationId = "brown01"+              , citationPrefix = []+              , citationSuffix = []+              , citationMode = AuthorInText+              , citationNoteNum = 0+              , citationHash = 0+              }+          ]+          [ Str "[brown01]" ]+      ]+  , Para+      [ Str "Year:"+      , Space+      , Cite+          [ Citation+              { citationId = "brown01"+              , citationPrefix = []+              , citationSuffix = []+              , citationMode = SuppressAuthor+              , citationNoteNum = 0+              , citationHash = 0+              }+          ]+          [ Str "[brown01]" ]+      ]+  , Para+      [ Str "Author:"+      , Space+      , Cite+          [ Citation+              { citationId = "brown01"+              , citationPrefix = []+              , citationSuffix = []+              , citationMode = NormalCitation+              , citationNoteNum = 0+              , citationHash = 0+              }+          ]+          [ Str "[brown01]" ]+      ]+  , Header+      1+      ( "" , [] , [] )+      [ Str "Inline"+      , Space+      , Str "elements"+      , Space+      , Str "across"+      , Space+      , Str "paragraphs"+      ]+  , Para [ Emph [ Str "hi" ] ]+  , Para [ Emph [ Str "there" ] ]+  , Para [ Str "Hello" , Space , Strong [ Str "again" ] ]+  , Para+      [ Strong [ Str "and" , Space , Str "again" ]+      , Space+      , Str "world."+      ]+  , Para [ Underline [ Str "Para" , Space , Str "one." ] ]+  , Para [ Underline [ Str "Para" , Space , Str "two." ] ]+  , Header+      2+      ( "" , [] , [] )+      [ SmallCaps [ Str "smallcaps" , Space , Str "heading" ] ]+  , Header+      1+      ( "" , [] , [] )+      [ Str "More"+      , Space+      , Str "inline"+      , Space+      , Str "elements"+      , Space+      , Str "across"+      , Space+      , Str "paragraphs"+      ]+  , Para+      [ Link+          ( "" , [] , [] )+          [ Str "link" , Space , Str "one" ]+          ( "https://example.com/typst" , "" )+      ]+  , Para+      [ Link+          ( "" , [] , [] )+          [ Str "link" , Space , Str "two" ]+          ( "https://example.com/typst" , "" )+      ]+  , Para [ Emph [ Str "hi" , Space ] ]+  , Para [ Emph [ Str "struck" , Space , Str "middle" ] ]+  , Para [ Emph [ Space , Str "there" ] ]+  , Para [ Str "mixed" , Space , Str "case" ]+  , Para [ Str "also" , Space , Str "here" ]+  , DefinitionList+      [ ( [ Str "term" ]+        , [ [ Para [ Emph [ Str "term" , Space , Str "one" ] ]+            , Para [ Emph [ Str "term" , Space , Str "two" ] ]+            ]+          ]+        )+      ]+  , Para [ Str "Edge" , Space , Str "case" ]+  , Para+      [ Emph [ Str "split" , Space , Str "here" ]+      , Space+      , Str "continues."+      ]+  , Header+      1+      ( "" , [] , [] )+      [ Str "Splitting"+      , Space+      , Str "edge"+      , Space+      , Str "cases"+      ]+  , Para [ Span ( "edge-label" , [] , [] ) [] ]+  , Para+      [ Str "Trailing"+      , Space+      , Str "edge"+      , Space+      , Emph [ Str "splits" ]+      ]+  , Para [ Emph [ Str "here" ] , Space , Str "continues." ]+  , Para+      [ Str "Consecutive"+      , Space+      , Str "breaks"+      , Space+      , Emph [ Str "a" ]+      ]+  , Para [ Emph [ Str "b" ] , Space , Str "end." ]+  , Para [ Str "Vanishing" ]+  , Para [ Str "body." ]+  , Para+      [ Emph [ Underline [ Str "nested" , Space , Str "one" ] ] ]+  , Para+      [ Emph [ Underline [ Str "nested" , Space , Str "two" ] ] ]+  , Para [ Emph [ Str "a" , Space ] ]+  , Table+      ( "" , [] , [] )+      (Caption Nothing [])+      [ ( AlignDefault , ColWidthDefault ) ]+      (TableHead ( "" , [] , [] ) [])+      [ TableBody+          ( "" , [] , [] )+          (RowHeadColumns 0)+          []+          [ Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "cell" ] ]+              ]+          ]+      ]+      (TableFoot ( "" , [] , [] ) [])+  , Para [ Emph [ Space , Str "c" ] ]+  , Para+      [ Str "Ref"+      , Space+      , Str "supplement:"+      , Space+      , Link+          ( "" , [ "ref" ] , [] )+          [ Str "see" , Space , Str "this" , Space , Str "section" ]+          ( "#edge-label" , "" )+      , Str "."       ]   ]
test/typst-reader.typ view
@@ -26,3 +26,75 @@  #include "undergradmath.typ" ++= Citations++Normal: #cite(<brown01>) or @brown01.++Prose: #cite(<brown01>, form: "prose")++Year: #cite(<brown01>, form: "year")++Author: #cite(<brown01>, form: "author")+++= Inline elements across paragraphs++#emph[hi++there]++Hello #strong[again++and again] world.++#underline[Para one.++Para two.]++#smallcaps[#heading(level: 2)[smallcaps heading]]+++= More inline elements across paragraphs++#link("https://example.com/typst")[link one++link two]++#emph[hi #block[struck middle] there]++#lower[Mixed Case++Also Here]++/ term: #emph[term one++  term two]++Edge case #emph[++split here] continues.+++= Splitting edge cases <edge-label>++Trailing edge #emph[splits++here] continues.++Consecutive breaks #emph[a+++b] end.++Vanishing #emph[++] body.++#emph[#underline[nested one++nested two]]++#emph[a #grid(columns: 1)[cell] c]++Ref supplement: @edge-label[see this section].
test/vimwiki-reader.native view
@@ -357,10 +357,7 @@       , Str "attributes"       ]   , CodeBlock-      ( ""-      , []-      , [ ( "class" , "python" ) , ( "style" , "color:blue" ) ]-      )+      ( "" , [ "python" ] , [ ( "style" , "color:blue" ) ] )       " for i in range(1, 5):\n     print(i)"   , Header       3
test/writer.html4 view
@@ -14,7 +14,10 @@     */     html {       color: #1a1a1a;+      color: light-dark(#1a1a1a, #fdfdfd);       background-color: #fdfdfd;+      background-color: light-dark(#fdfdfd, #1a1a1a);+      color-scheme: light dark;     }     body {       margin: 0 auto;@@ -39,12 +42,17 @@     }     @media print {       html {-        background-color: white;+        color-scheme: light only;       }-      body {+      html, body, pre, code {         background-color: transparent;+      }+      body, a, a:visited, blockquote {         color: black;       }+      pre, code {+        padding: 0;+      }       p, h2, h3 {         orphans: 3;         widows: 3;@@ -56,11 +64,8 @@     p {       margin: 1em 0;     }-    a {-      color: #1a1a1a;-    }-    a:visited {-      color: #1a1a1a;+    a, a:visited {+      color: inherit;     }     img {       max-width: 100%;@@ -89,8 +94,11 @@     blockquote {       margin: 1em 0 1em 1.7em;       padding-left: 1em;-      border-left: 2px solid #e6e6e6;+      border-left: 2px solid;+      border-color: #e6e6e6;+      border-color: light-dark(#e6e6e6, #4c4c4c);       color: #606060;+      color: light-dark(#606060, #b6b6b6);     }     code {       white-space: pre-wrap;@@ -114,7 +122,7 @@     }     hr {       border: none;-      border-top: 1px solid #1a1a1a;+      border-top: 1px solid currentColor;       height: 1px;       margin: 1em 0;     }@@ -131,11 +139,11 @@     }     tbody {       margin-top: 0.5em;-      border-top: 1px solid #1a1a1a;-      border-bottom: 1px solid #1a1a1a;+      border-top: 1px solid currentColor;+      border-bottom: 1px solid currentColor;     }     th {-      border-top: 1px solid #1a1a1a;+      border-top: 1px solid currentColor;       padding: 0.25em 0.5em 0.25em 0.5em;     }     td {
test/writer.html5 view
@@ -14,7 +14,10 @@     */     html {       color: #1a1a1a;+      color: light-dark(#1a1a1a, #fdfdfd);       background-color: #fdfdfd;+      background-color: light-dark(#fdfdfd, #1a1a1a);+      color-scheme: light dark;     }     body {       margin: 0 auto;@@ -39,12 +42,17 @@     }     @media print {       html {-        background-color: white;+        color-scheme: light only;       }-      body {+      html, body, pre, code {         background-color: transparent;+      }+      body, a, a:visited, blockquote {         color: black;       }+      pre, code {+        padding: 0;+      }       p, h2, h3 {         orphans: 3;         widows: 3;@@ -56,11 +64,8 @@     p {       margin: 1em 0;     }-    a {-      color: #1a1a1a;-    }-    a:visited {-      color: #1a1a1a;+    a, a:visited {+      color: inherit;     }     img {       max-width: 100%;@@ -89,8 +94,11 @@     blockquote {       margin: 1em 0 1em 1.7em;       padding-left: 1em;-      border-left: 2px solid #e6e6e6;+      border-left: 2px solid;+      border-color: #e6e6e6;+      border-color: light-dark(#e6e6e6, #4c4c4c);       color: #606060;+      color: light-dark(#606060, #b6b6b6);     }     code {       white-space: pre-wrap;@@ -114,7 +122,7 @@     }     hr {       border: none;-      border-top: 1px solid #1a1a1a;+      border-top: 1px solid currentColor;       height: 1px;       margin: 1em 0;     }@@ -131,11 +139,11 @@     }     tbody {       margin-top: 0.5em;-      border-top: 1px solid #1a1a1a;-      border-bottom: 1px solid #1a1a1a;+      border-top: 1px solid currentColor;+      border-bottom: 1px solid currentColor;     }     th {-      border-top: 1px solid #1a1a1a;+      border-top: 1px solid currentColor;       padding: 0.25em 0.5em 0.25em 0.5em;     }     td {
test/writer.org view
@@ -387,45 +387,45 @@  Interpreted markdown in a table: -#+begin_html+#+begin_export html   <table>-#+end_html+#+end_export -#+begin_html+#+begin_export html   <tr>-#+end_html+#+end_export -#+begin_html+#+begin_export html   <td>-#+end_html+#+end_export  This is /emphasized/ -#+begin_html+#+begin_export html   </td>-#+end_html+#+end_export -#+begin_html+#+begin_export html   <td>-#+end_html+#+end_export  And this is *strong* -#+begin_html+#+begin_export html   </td>-#+end_html+#+end_export -#+begin_html+#+begin_export html   </tr>-#+end_html+#+end_export -#+begin_html+#+begin_export html   </table>-#+end_html+#+end_export -#+begin_html+#+begin_export html   <script type="text/javascript">document.write('This *should not* be interpreted as markdown');</script>-#+end_html+#+end_export  Here's a simple block: @@ -451,24 +451,24 @@  This should just be an HTML comment: -#+begin_html+#+begin_export html   <!-- Comment -->-#+end_html+#+end_export  Multiline: -#+begin_html+#+begin_export html   <!--   Blah   Blah   -->-#+end_html+#+end_export -#+begin_html+#+begin_export html   <!--       This is another comment.   -->-#+end_html+#+end_export  Code block: @@ -478,9 +478,9 @@  Just plain comment, with trailing spaces on the line: -#+begin_html+#+begin_export html   <!-- foo -->-#+end_html+#+end_export  Code: @@ -490,41 +490,41 @@  Hr's: -#+begin_html+#+begin_export html   <hr>-#+end_html+#+end_export -#+begin_html+#+begin_export html   <hr />-#+end_html+#+end_export -#+begin_html+#+begin_export html   <hr />-#+end_html+#+end_export -#+begin_html+#+begin_export html   <hr>-#+end_html+#+end_export -#+begin_html+#+begin_export html   <hr />-#+end_html+#+end_export -#+begin_html+#+begin_export html   <hr />-#+end_html+#+end_export -#+begin_html+#+begin_export html   <hr class="foo" id="bar" />-#+end_html+#+end_export -#+begin_html+#+begin_export html   <hr class="foo" id="bar" />-#+end_html+#+end_export -#+begin_html+#+begin_export html   <hr class="foo" id="bar">-#+end_html+#+end_export  -------------- @@ -598,7 +598,7 @@  These shouldn't be math: -- To get the famous equation, write =$e = mc^2$=.+- To get the famous equation, write ~$e = mc^2$~. - $22,000 is a /lot/ of money. So is $34,000. (It worked if “lot” is   emphasized.) - Shoes ($20) and socks ($5).@@ -702,7 +702,7 @@ :END: Foo [[/url/][bar]]. -With [[/url/][embedded [brackets]]].+With [[/url/][embedded [brackets​]]].  [[/url/][b]] by itself should be a link. 
test/writer.tei view
@@ -19,7 +19,7 @@ <body> <p>This is a set of tests for pandoc. Most of them are adapted from John Gruber’s markdown test suite.</p>-<milestone unit="undefined" type="separator" rendition="line" />+<milestone unit="undefined" type="separator" rend="line" /> <div type="level1" xml:id="headers">   <head>Headers</head>   <div type="level2" xml:id="level-2-with-an-embedded-link">@@ -48,7 +48,7 @@   <div type="level2" xml:id="level-2">     <head>Level 2</head>     <p>with no blank line</p>-    <milestone unit="undefined" type="separator" rendition="line" />+    <milestone unit="undefined" type="separator" rend="line" />   </div> </div> <div type="level1" xml:id="paragraphs">@@ -59,7 +59,7 @@   item.</p>   <p>Here’s one with a bullet. * criminey.</p>   <p>There should be a hard line break<lb />here.</p>-  <milestone unit="undefined" type="separator" rendition="line" />+  <milestone unit="undefined" type="separator" rend="line" /> </div> <div type="level1" xml:id="block-quotes">   <head>Block Quotes</head>@@ -93,7 +93,7 @@   </quote>   <p>This should not be a block quote: 2 &gt; 1.</p>   <p>And a following paragraph.</p>-  <milestone unit="undefined" type="separator" rendition="line" />+  <milestone unit="undefined" type="separator" rend="line" /> </div> <div type="level1" xml:id="code-blocks">   <head>Code Blocks</head>@@ -113,7 +113,7 @@  These should not be escaped:  \$ \\ \&gt; \[ \{ </ab>-  <milestone unit="undefined" type="separator" rendition="line" />+  <milestone unit="undefined" type="separator" rend="line" /> </div> <div type="level1" xml:id="lists">   <head>Lists</head>@@ -405,7 +405,7 @@     <p>Should not be a list item:</p>     <p>M.A. 2007</p>     <p>B. Williams</p>-    <milestone unit="undefined" type="separator" rendition="line" />+    <milestone unit="undefined" type="separator" rend="line" />   </div> </div> <div type="level1" xml:id="definition-lists">@@ -590,7 +590,7 @@ &lt;hr /&gt; </ab>   <p>Hr’s:</p>-  <milestone unit="undefined" type="separator" rendition="line" />+  <milestone unit="undefined" type="separator" rend="line" /> </div> <div type="level1" xml:id="inline-markup">   <head>Inline Markup</head>@@ -623,7 +623,7 @@   H<hi rendition="simple:subscript">many of them</hi>O.</p>   <p>These should not be superscripts or subscripts, because of the unescaped   spaces: a^b c^d, a~b c~d.</p>-  <milestone unit="undefined" type="separator" rendition="line" />+  <milestone unit="undefined" type="separator" rend="line" /> </div> <div type="level1" xml:id="smart-quotes-ellipses-dashes">   <head>Smart quotes, ellipses, dashes</head>@@ -640,7 +640,7 @@   <p>Some dashes: one—two — three—four — five.</p>   <p>Dashes between numbers: 5–7, 255–66, 1987–1999.</p>   <p>Ellipses…and…and….</p>-  <milestone unit="undefined" type="separator" rendition="line" />+  <milestone unit="undefined" type="separator" rend="line" /> </div> <div type="level1" xml:id="latex">   <head>LaTeX</head>@@ -692,7 +692,7 @@     </item>   </list>   <p>Here’s a LaTeX table:</p>-  <milestone unit="undefined" type="separator" rendition="line" />+  <milestone unit="undefined" type="separator" rend="line" /> </div> <div type="level1" xml:id="special-characters">   <head>Special Characters</head>@@ -735,7 +735,7 @@   <p>Bang: !</p>   <p>Plus: +</p>   <p>Minus: -</p>-  <milestone unit="undefined" type="separator" rendition="line" />+  <milestone unit="undefined" type="separator" rend="line" /> </div> <div type="level1" xml:id="links">   <head>Links</head>@@ -801,7 +801,7 @@     <ab type='codeblock '> or here: &lt;http://example.com/&gt; </ab>-    <milestone unit="undefined" type="separator" rendition="line" />+    <milestone unit="undefined" type="separator" rend="line" />   </div> </div> <div type="level1" xml:id="images">@@ -817,7 +817,7 @@     <head>movie</head>     <graphic url="movie.jpg" />   </figure> icon.</p>-  <milestone unit="undefined" type="separator" rendition="line" />+  <milestone unit="undefined" type="separator" rend="line" /> </div> <div type="level1" xml:id="footnotes">   <head>Footnotes</head>
test/writer.typst view
@@ -542,7 +542,6 @@ #block[ #block[ foo- ] ] #block[@@ -557,7 +556,6 @@  #block[ foo- ] This should be a code block, though: 
xml-light/Text/Pandoc/XML/Light.hs view
@@ -43,7 +43,10 @@  import qualified Control.Exception as E import qualified Text.XML as Conduit-import Text.XML.Unresolved (InvalidEventStream(..))+import qualified Text.XML.Stream.Parse as P+import Data.Conduit (runConduit, (.|))+import qualified Data.Conduit.List as CL+import Data.Char (isSpace) import qualified Data.Text as T import qualified Data.Text.Lazy as TL import qualified Data.Map as M@@ -64,46 +67,137 @@   elementToElement .  Conduit.documentRoot <$>     either (Left . T.pack . E.displayException) Right     (Conduit.parseText Conduit.def{ Conduit.psRetainNamespaces = True-                                  , Conduit.psDecodeEntities = decodeEnts } t)- where-   decodeEnts ref = case M.lookup ref entityMap of-                      Nothing -> XML.ContentEntity ref-                      Just t' -> XML.ContentText t'+                                  , Conduit.psDecodeEntities =+                                      entityResolver entityMap } t)  parseXMLContents :: TL.Text -> Either T.Text [Content] parseXMLContents = parseXMLContentsWithEntities mempty +-- | Parse a list of XML contents.  Unlike 'parseXMLElementWithEntities',+-- this does not require a single root element: multiple sibling+-- elements and top-level text are accepted.  An XML declaration,+-- DOCTYPE, comments, and processing instructions are skipped. parseXMLContentsWithEntities :: M.Map T.Text T.Text                              -> TL.Text -> Either T.Text [Content] parseXMLContentsWithEntities entityMap t =-  case Conduit.parseText Conduit.def{ Conduit.psRetainNamespaces = True-                                    , Conduit.psDecodeEntities = decodeEnts-                                    } t of-    Left e ->-      case E.fromException e of-        Just (ContentAfterRoot _) ->-          elContent <$> parseXMLElementWithEntities entityMap-                          ("<wrapper>" <> t <> "</wrapper>")-        _ -> Left . T.pack . E.displayException $ e-    Right x -> Right [Elem . elementToElement . Conduit.documentRoot $ x]+  case runConduit (CL.sourceList (TL.toChunks t) .| parser+                    .| CL.fold step (Right ([], []))) of+    Left err -> Left . T.pack . E.displayException $ (err :: E.SomeException)+    Right st -> normalizeTop <$> finishContents st  where-   decodeEnts ref = case M.lookup ref entityMap of-                      Nothing -> XML.ContentEntity ref-                      Just t' -> XML.ContentText t'+   parser = P.parseTextPos P.def{ P.psRetainNamespaces = True+                                , P.psDecodeEntities =+                                    entityResolver entityMap } +entityResolver :: M.Map T.Text T.Text -> T.Text -> XML.Content+entityResolver entityMap ref =+  case M.lookup ref entityMap of+    Nothing -> XML.ContentEntity ref+    Just t' -> XML.ContentText t'++-- An element that is being built, with its children so far in reverse order.+data Frame = Frame QName [Attr] [Content]++-- The top-level contents built so far (reversed) and the stack of+-- open elements (innermost first); or an error.+type BuildState = Either T.Text ([Content], [Frame])++-- Fold one parse event into the content forest being built, checking+-- that start and end tags are balanced.+step :: BuildState -> P.EventPos -> BuildState+step st@(Left _) _ = st+step (Right (cs, stack)) (pos, event) =+  case event of+    XML.EventBeginElement name attribs ->+      case mapM toAttr attribs of+        Left e -> Left e+        Right attrs -> Right (cs, Frame (nameToQName name) attrs [] : stack)+    XML.EventEndElement name ->+      case stack of+        Frame fname attrs children : stack'+          | nameToQName name == fname ->+              emit (Elem (Element fname attrs (mergeText (reverse children))+                           Nothing)) stack'+        _ -> Left $ atPos $ "Unexpected close tag " <>+                      showQ (nameToQName name)+    XML.EventContent c ->+      case contentToText c of+        Left e -> Left e+        Right txt -> emit (textContent txt) stack+    XML.EventCDATA txt -> emit (textContent txt) stack+    _ -> Right (cs, stack)+      -- skip begin/end document, doctype, comments, and PIs+ where+  -- add finished content to the enclosing element (or the top level)+  emit c [] = Right (c : cs, [])+  emit c (Frame name attrs children : stack') =+    Right (cs, Frame name attrs (c : children) : stack')++  textContent txt = Text (CData CDataText txt Nothing)++  toAttr (name, vals) =+    Attr (nameToQName name) . T.concat <$> mapM contentToText vals++  contentToText (XML.ContentText txt) = Right txt+  contentToText (XML.ContentEntity ref) =+    Left $ atPos $ "Unresolved entity &" <> ref <> ";"++  atPos msg = case pos of+                Just pr -> T.pack (show pr) <> ": " <> msg+                Nothing -> msg++finishContents :: BuildState -> Either T.Text [Content]+finishContents (Left e) = Left e+finishContents (Right (cs, [])) = Right (mergeText (reverse cs))+finishContents (Right (_, Frame name _ _ : _)) =+  Left $ "Missing close tag for " <> showQ name++showQ :: QName -> T.Text+showQ (QName name _ Nothing)    = name+showQ (QName name _ (Just pre)) = pre <> ":" <> name++-- Merge adjacent text nodes (the stream parser splits text at+-- entity and CDATA boundaries).+mergeText :: [Content] -> [Content]+mergeText (Text cd : cs) =+  case span isText cs of+    ([], _)    -> Text cd : mergeText cs+    (ts, rest) -> Text cd{ cdData = T.concat (cdData cd :+                                      [d | Text (CData _ d _) <- ts]) }+                  : mergeText rest+ where+  isText Text{} = True+  isText _      = False+mergeText (c : cs) = c : mergeText cs+mergeText [] = []++-- If the content is a single element surrounded only by whitespace+-- (as in a complete XML document, where there may be newlines after+-- an XML declaration or DOCTYPE), drop the whitespace.+normalizeTop :: [Content] -> [Content]+normalizeTop cs =+  case filter (not . isWhitespaceText) cs of+    cs'@[Elem _] -> cs'+    _            -> cs+ where+  isWhitespaceText (Text cd) = T.all isSpace (cdData cd)+  isWhitespaceText _         = False++nameToQName :: Conduit.Name -> QName+nameToQName (Conduit.Name localName mbns mbpref) =+  case mbpref of+    Nothing ->+      case T.stripPrefix "xmlns:" localName of+        Just rest -> QName rest mbns (Just "xmlns")+        Nothing   -> QName localName mbns mbpref+    _ -> QName localName mbns mbpref+ elementToElement :: Conduit.Element -> Element elementToElement (Conduit.Element name attribMap nodes) =-  Element (nameToQname name) attrs (mapMaybe nodeToContent nodes) Nothing+  Element (nameToQName name) attrs (mapMaybe nodeToContent nodes) Nothing  where-  attrs = map (\(n,v) -> Attr (nameToQname n) v) $+  attrs = map (\(n,v) -> Attr (nameToQName n) v) $               M.toList attribMap-  nameToQname (Conduit.Name localName mbns mbpref) =-    case mbpref of-      Nothing ->-        case T.stripPrefix "xmlns:" localName of-          Just rest -> QName rest mbns (Just "xmlns")-          Nothing   -> QName localName mbns mbpref-      _ -> QName localName mbns mbpref  nodeToContent :: Conduit.Node -> Maybe Content nodeToContent (Conduit.NodeElement el) =
xml-light/Text/Pandoc/XML/Light/Output.hs view
@@ -11,27 +11,30 @@    Portability : portable     This code is based on code from xml-light, released under the BSD3 license.-   We use a text Builder instead of ShowS.+   We use a TextBuilder (from the text-builder package) instead of ShowS. -} module Text.Pandoc.XML.Light.Output   ( -- * Replacement for xml-light's Text.XML.Output     ppTopElement   , ppElement   , ppContent+  , ppcTopElement   , ppcElement   , ppcContent   , showTopElement   , showElement   , showContent   , useShortEmptyTags+  , useInlineTags   , defaultConfigPP+  , prettyConfigPP   , ConfigPP(..)   ) where +import Data.List (intersperse) import Data.Text (Text) import qualified Data.Text as T-import qualified Data.Text.Lazy as TL-import Data.Text.Lazy.Builder (Builder, singleton, fromText, toLazyText)+import TextBuilder (TextBuilder, char, text, toText) import Text.Pandoc.XML.Light.Types  --@@ -46,6 +49,7 @@ -------------------------------------------------------------------------------- data ConfigPP = ConfigPP   { shortEmptyTag :: QName -> Bool+  , inlineTag     :: QName -> Bool   , prettify      :: Bool   } @@ -53,6 +57,7 @@ --  * Always use abbreviate empty tags. defaultConfigPP :: ConfigPP defaultConfigPP = ConfigPP { shortEmptyTag = const True+                           , inlineTag     = const False                            , prettify      = False                            } @@ -63,7 +68,13 @@ useShortEmptyTags :: (QName -> Bool) -> ConfigPP -> ConfigPP useShortEmptyTags p c = c { shortEmptyTag = p } +-- | The predicate specifies which tags should be treated as inline:+-- when pretty-printing, the whole content of an inline element is+-- kept on a single line, so that no whitespace is added inside it.+useInlineTags :: (QName -> Bool) -> ConfigPP -> ConfigPP+useInlineTags p c = c { inlineTag = p } + -- | Specify if we should use extra white-space to make document more readable. -- WARNING: This adds additional white-space to text elements, -- and so it may change the meaning of the document.@@ -102,47 +113,70 @@  -- | Pretty printing elements ppcElement         :: ConfigPP -> Element -> Text-ppcElement c        = TL.toStrict . toLazyText . ppElementS c mempty+ppcElement c        = toText . ppElementS c mempty  -- | Pretty printing content ppcContent         :: ConfigPP -> Content -> Text-ppcContent c        = TL.toStrict . toLazyText . ppContentS c mempty--ppcCData           :: ConfigPP -> CData -> Text-ppcCData c         = TL.toStrict . toLazyText . ppCDataS c mempty+ppcContent c        = toText . ppContentS c mempty -type Indent = Builder+type Indent = TextBuilder  -- | Pretty printing content using ShowT-ppContentS         :: ConfigPP -> Indent -> Content -> Builder+ppContentS         :: ConfigPP -> Indent -> Content -> TextBuilder ppContentS c i x = case x of                      Elem e -> ppElementS c i e                      Text t -> ppCDataS c i t                      CRef r -> showCRefS r -ppElementS         :: ConfigPP -> Indent -> Element -> Builder-ppElementS c i e = i <> tagStart (elName e) (elAttribs e) <>+ppElementS         :: ConfigPP -> Indent -> Element -> TextBuilder+ppElementS c i e = i <> ppElementS' c i e++-- | Like ppElementS, but without indentation before the element+-- itself.  The indentation is still passed down, so that block-level+-- elements nested inside an inline element are indented properly.+ppElementS'        :: ConfigPP -> Indent -> Element -> TextBuilder+ppElementS' c i e = tagStart (elName e) (elAttribs e) <>   (case elContent e of-    [] | "?" `T.isPrefixOf` qName name -> fromText " ?>"-       | shortEmptyTag c name  -> fromText " />"-    [Text t] -> singleton '>' <> ppCDataS c mempty t <> tagEnd name-    cs -> singleton '>' <> nl <>+    [] | "?" `T.isPrefixOf` qName name -> text " ?>"+       | shortEmptyTag c name  -> text " />"+    [Text t] -> char '>' <>+                ppCDataS' c (if inlineTag c name then i else mempty) t <>+                tagEnd name+    cs | inlineTag c name ->+          char '>' <> mconcat (map inlineContent cs) <> tagEnd name+       | otherwise ->+          char '>' <> nl <>           mconcat (map ((<> nl) . ppContentS c (sp <> i)) cs) <>           i <> tagEnd name-      where (nl,sp)  = if prettify c then ("\n","  ") else ("","")+      where (nl,sp)  = if prettify c+                          then (text "\n", text "  ")+                          else (mempty, mempty)+            -- content of an inline element: no indentation is+            -- emitted, so that no whitespace is added to the content+            inlineContent (Elem el) = ppElementS' c i el+            inlineContent (Text t)  = ppCDataS' c i t+            inlineContent (CRef r)  = showCRefS r   )   where name = elName e -ppCDataS           :: ConfigPP -> Indent -> CData -> Builder-ppCDataS c i t     = i <> if cdVerbatim t /= CDataText || not (prettify c)-                             then showCDataS t-                             else foldr cons mempty (T.unpack (showCData t))-  where cons         :: Char -> Builder -> Builder-        cons '\n' ys  = singleton '\n' <> i <> ys-        cons y ys     = singleton y <> ys+ppCDataS           :: ConfigPP -> Indent -> CData -> TextBuilder+ppCDataS c i t     = i <> ppCDataS' c i t +-- | Like ppCDataS, but without indentation before the text itself.+-- The indentation is still used after newlines in the text.+ppCDataS'          :: ConfigPP -> Indent -> CData -> TextBuilder+ppCDataS' c i t    = if cdVerbatim t /= CDataText || not (prettify c)+                        then showCDataS t+                        -- add indentation after newlines; escaping+                        -- neither adds nor removes newlines, so we+                        -- can split the unescaped text+                        else mconcat+                             (intersperse (char '\n' <> i)+                               (map escStr+                                 (T.split (=='\n') (cdData t))))  + --------------------------------------------------------------------------------  -- | Adds the <?xml?> header.@@ -155,43 +189,39 @@ showElement        :: Element -> Text showElement         = ppcElement defaultConfigPP -showCData          :: CData -> Text-showCData           = ppcCData defaultConfigPP- -- Note: crefs should not contain '&', ';', etc.-showCRefS          :: Text -> Builder-showCRefS r         = singleton '&' <> fromText r <> singleton ';'+showCRefS          :: Text -> TextBuilder+showCRefS r         = char '&' <> text r <> char ';'  -- | Convert a text element to characters.-showCDataS         :: CData -> Builder+showCDataS         :: CData -> TextBuilder showCDataS cd =  case cdVerbatim cd of    CDataText     -> escStr (cdData cd)-   CDataVerbatim -> fromText "<![CDATA[" <> escCData (cdData cd) <>-                    fromText "]]>"-   CDataRaw      -> fromText (cdData cd)+   CDataVerbatim -> text "<![CDATA[" <> escCData (cdData cd) <>+                    text "]]>"+   CDataRaw      -> text (cdData cd)  ---------------------------------------------------------------------------------escCData           :: Text -> Builder-escCData t-  | "]]>" `T.isPrefixOf` t =-     fromText "]]]]><![CDATA[>" <> fromText (T.drop 3 t)-escCData t-  = case T.uncons t of-      Nothing     -> mempty-      Just (c,t') -> singleton c <> escCData t'+escCData           :: Text -> TextBuilder+escCData t =+  case T.breakOn "]]>" t of+    (chunk, rest)+      | T.null rest -> text chunk+      | otherwise   -> text chunk <> text "]]]]><![CDATA[>" <>+                       escCData (T.drop 3 rest) -escChar            :: Char -> Builder+escChar            :: Char -> TextBuilder escChar c = case c of-  '<'   -> fromText "&lt;"-  '>'   -> fromText "&gt;"-  '&'   -> fromText "&amp;"-  '"'   -> fromText "&quot;"+  '<'   -> text "&lt;"+  '>'   -> text "&gt;"+  '&'   -> text "&amp;"+  '"'   -> text "&quot;"   -- we use &#39 instead of &apos; because IE apparently has difficulties   -- rendering &apos; in xhtml.   -- Reported by Rohan Drape <rohan.drape@gmail.com>.-  '\''  -> fromText "&#39;"-  _     -> singleton c+  '\''  -> text "&#39;"+  _     -> char c    {- original xml-light version:   -- NOTE: We escape '\r' explicitly because otherwise they get lost@@ -201,10 +231,13 @@       where oc = ord c   -} -escStr             :: Text -> Builder-escStr cs          = if T.any needsEscape cs-                        then mconcat (map escChar (T.unpack cs))-                        else fromText cs+escStr             :: Text -> TextBuilder+escStr cs          = case T.break needsEscape cs of+                       (chunk, rest) ->+                         case T.uncons rest of+                           Nothing -> text chunk+                           Just (c, rest') ->+                             text chunk <> escChar c <> escStr rest'  where   needsEscape '<' = True   needsEscape '>' = True@@ -213,22 +246,22 @@   needsEscape '\'' = True   needsEscape _ = False -tagEnd             :: QName -> Builder-tagEnd qn           = fromText "</" <> showQName qn <> singleton '>'+tagEnd             :: QName -> TextBuilder+tagEnd qn           = text "</" <> showQName qn <> char '>' -tagStart           :: QName -> [Attr] -> Builder-tagStart qn as      = singleton '<' <> showQName qn <> as_str+tagStart           :: QName -> [Attr] -> TextBuilder+tagStart qn as      = char '<' <> showQName qn <> as_str  where as_str       = if null as                          then mempty                          else mconcat (map showAttr as) -showAttr           :: Attr -> Builder-showAttr (Attr qn v) = singleton ' ' <> showQName qn <>-                       singleton '=' <>-                       singleton '"' <> escStr v <> singleton '"'+showAttr           :: Attr -> TextBuilder+showAttr (Attr qn v) = char ' ' <> showQName qn <>+                       char '=' <>+                       char '"' <> escStr v <> char '"' -showQName          :: QName -> Builder+showQName          :: QName -> TextBuilder showQName q         =   case qPrefix q of-    Nothing -> fromText (qName q)-    Just p  -> fromText p <> singleton ':' <> fromText (qName q)+    Nothing -> text (qName q)+    Just p  -> text p <> char ':' <> text (qName q)