packages feed

pandoc 3.13 → 3.14

raw patch · 282 files changed

+5535/−680 lines, 282 filesdep ~citeprocdep ~commonmarkdep ~commonmark-extensionsbinary-addedPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: citeproc, commonmark, commonmark-extensions, crypton, emojis, skylighting, skylighting-core, texmath, tls, typst

API changes (from Hackage documentation)

+ Text.Pandoc.Class: getDataDirs :: PandocMonad m => FilePath -> m [FilePath]
+ Text.Pandoc.Class: getPandocDataDirs :: PandocMonad m => m [FilePath]

Files

AUTHORS.md view
@@ -6,6 +6,7 @@ - Agustín Martín Barbero - Akash Patel - Akos Marton+- Alain Lesage - Albert Krewinkel - Albert Lei - Alex Ivkin@@ -47,6 +48,7 @@ - Artyom Kazak - Aslak Hellesøy - Asliddinbek Azizovich+- Asuka - B. Scott Michel - Bastien Dumont - Ben Firshman@@ -65,6 +67,7 @@ - Brent Yorgey - Brian Leung - Bryan O'Sullivan+- Caio Vinícius - Caleb McDaniel - Caleb Mclennan - Calvin Beck@@ -218,6 +221,7 @@ - Jens Oehlschlägel - Jesse Hathaway - Jeremie Knuesel+- Jeremy Levartovsky - Jérémy Bobbio - Jeroen de Haas - Jerry Sky@@ -318,6 +322,7 @@ - Mohamed Akram - Morgan Willcock - Morton Fox+- Nacho García - Nathan Gass - Naveen - Neil Mayhew@@ -557,6 +562,7 @@ - thsutton - thron7 - timo-a+- tyler000000 - vijayphoenix - vkraven - wgevaert
MANUAL.txt view
@@ -1,7 +1,7 @@ --- title: Pandoc User's Guide author: John MacFarlane-date: 2026-10-25+date: 2026-11-10 ---  # Synopsis@@ -418,6 +418,19 @@     is not created by pandoc, so you will need to create it yourself     if you want to make use of it.) +    If the `PANDOC_DATA_DIRS` environment variable is set to a list+    of directories (separated by `:`, or `;` on Windows), these+    directories will be searched, in order, for data files not+    found in the user data directory, before pandoc falls back to+    its default data files.  This applies to all data files that+    are looked up in the user data directory, including templates,+    filters, defaults files, metadata files, and CSL styles. Only+    absolute paths in `PANDOC_DATA_DIRS` are used.  This makes it+    possible for a package manager to install, for example, Lua+    filters into `/some/prefix/share/pandoc/filters` and make them+    available by adding `/some/prefix/share/pandoc` to+    `PANDOC_DATA_DIRS`.+ `-d` *FILE*, `--defaults=`*FILE*  :   Specify a set of default option settings.  *FILE* is a YAML or JSON@@ -4043,8 +4056,20 @@ (Table styles will be added to the Table elements directly.) This extension can be used with [docx custom styles](#custom-styles). +When converting from odt or fodt, add `custom-styles`+attributes for paragraph and character styles, using the+style names shown in the word processor. Automatic styles,+which only record direct formatting, are not recorded; instead,+the named style they are based on is used. A few styles are not+recorded: the default paragraph style (`Default Paragraph Style`,+or `Standard`), the default styles of table cells, and styles+that pandoc interprets itself (such as `Emphasis` and `Strong+Emphasis`). The paragraphs of list items are not wrapped in Divs.+A heading gets a `custom-style` attribute if its style is not the+standard one for its level.+ input formats-:  `docx`+:  `docx`, `odt`, `fodt`  ### Extension: `amuse` ### @@ -5127,6 +5152,13 @@      : The caption of the table. {#ident .float latex-placement="ht"} +To repeat the caption of a `longtable` on each page, add the+`repeat-caption` class to the table's attributes. Repeated captions are+not added to the list of tables. This class has no effect on tables with+the `float` class, since those tables cannot span pages.++    : The caption of the table. {.repeat-caption}+ Note that `longtable` does not work with two-column layouts, so if you are producing a two-column document using LaTeX, you will need to specify the `float` class on every table, or use this small Lua filter@@ -7018,6 +7050,11 @@ pandoc, unless an alternative reference doc is specified using `--reference-doc`. +When reading pptx, each slide becomes a level-2 heading with the+identifier `slide-N`, followed by the slide's content. Slides hidden+from the slide show are read too, with the class `hidden` on their+heading.+ ## Incremental lists  By default, these writers produce lists that display "all at once."@@ -7133,6 +7170,10 @@ Notes are not yet supported for other slide formats, but the notes will not appear on the slides themselves. +When reading pptx, the speaker notes of each slide are read into a+`notes` div at the end of the slide's content, so a pptx file+converted to Markdown and back keeps its notes.+ ### Speaker notes on the title slide (PowerPoint)  For PowerPoint output, the title slide is generated from the document's@@ -7920,6 +7961,8 @@ attribute will be added for each style. Divs will be created to hold the paragraph styles, and Spans to hold the character styles. Table styles will be applied directly to the Table.+The odt and fodt readers also support the `styles` extension+(`-f odt+styles`), for paragraph and character styles.  For example, using the `custom-style-reference.docx` file in the test directory, we have the following different outputs:
changelog.md view
@@ -1,5 +1,185 @@ # Revision history for pandoc +## pandoc 3.14 (2026-11-10)++  * Support `PANDOC_DATA_DIRS` for data file lookup (#7922).+    If `PANDOC_DATA_DIRS` is set, the (absolute) directories it lists are+    searched, in order, for data files not found in the user data directory,+    before falling back to the default data files.  This covers templates,+    filters, defaults files, metadata files, CSL styles, and other data files.+    If `PANDOC_USER_DATA_DIRS` is set, print its value in `--version` output.++  * Reset sourcepos when parsing footnotes and grid table cells (#12001,+    extends fix for #11888). Otherwise, when one of these ends a+    file, relative paths in it can be rebased against the next+    file on the command line.++  * LaTeX reader:++    + Fix duplicated text after a control space at line end (#12051, Asuka).+    + Handle pandoc's deferred footnotes (`\pandocfootnotemark{n}` and+      `\pandocfootnotetext{n}{..}`) as produced by the LaTeX writer for+      notes in tables, captions, etc.+    + Ignore definitions of `\pandocbounded`, `\pandocfootnotemark`,+      and `\pandocfootnotetext` (e.g. from pandoc's template), which+      the reader handles directly. Previously these definitions were+      expanded, producing garbage and losing content.+    + Fix file-crossing bug in `rawLaTeXParser` (#12072).+      The raw LaTeX parser didn't work properly when crossing+      from one source file to another.++  * Typst reader:++    + Handle `columns: auto` in grid (#12061).++  * HTML reader:++    + Don't drop ordered lists and rules inside footnotes (#11852).+      Previously an `<ol>` inside the body of a note was treated as the list of+      notes, and its items were lost.  Similarly, an `<hr>` inside a note was+      dropped.++  * RST reader:++    + Strip minimum indentation from indented blocks (#12071). Previously+      the indentation of the first line was stripped, so lines with+      less indentation lost all their leading whitespace. Now, as+      in docutils, the minimum indentation is stripped and relative+      indentation is preserved.+    + Require descriptions in option list items (#12063).++  * ODT reader:++    + Support the `styles` extension (#12060).++  * Docx reader:++    + Merge adjacent styled code blocks (#9183, Caio Vinícius).+      Without `+styles`, consecutive "Source Code" paragraphs are combined into+      a single code block by `combineBlocks`. With `+styles`, each paragraph is+      first wrapped in a `Div` carrying its `custom-style` attribute, so+      the `CodeBlock` case no longer matched and every line became its+      own `Div [CodeBlock]`.+    + Don't drop contents of customXml element (#12050).+    + Read all underline types (#11988, Patrick Creighton).+    + Follow `w:numStyleLink` in abstract numberings (#9222, Caio Vinícius).++  * Pptx reader:++    + Don't add spaces between text runs (#12016, Nacho García).+    + Read the shapes inside group shapes (#12017, Nacho García).+    + Read equations and markup-compatibility blocks (#12025, Nacho Garcia).+    + Read speaker notes (Nacho García, #12023). Each slide's notes are now read+      into a `notes` div at the end of the slide's blocks, the form the pptx+      writer turns into speaker notes, so pptx -> Markdown -> pptx keeps them.+    + Read charts as tables (Nacho García, #12023). Previously, a chart was read+      as a placeholder text. We now create a table from the chart's cached+      data. The table is put in a `chart` Div whose `type` attribute holds+      the chart type. The chart's title becomes the table captions.+      The chart code is in Text.Pandoc.Readers.OOXML.Charts.+    + Mark hidden slides with a `hidden` class on the header+      (Nacho García, #12023).+    + Handle merged table cells (Nacho García).+    + Parse bold, italic, sub/superscript and hyperlinks (Nacho García). Text runs+      were previously read as plain text, so their formatting and hyperlinks+      were lost.+    + Read inherited bullets, numbering and nested lists (#12042, Nacho García).+    + Skip slide number placeholders (#12045, Nacho García).++  * Xlsx reader:++    + Resolve package-absolute relationship targets (#12013, Nacho García).+    + Read inline strings and rich text as written (#12014, Nacho García).+    + Look up a cell's font through its cell format (#12033, Nacho García).+    + Read merged cells as spans (Nacho García).+    + Render dates and times (Nacho García).+    + Render numbers as Excel's General format (Nacho García, #12023).+    + Read charts (#12043, Nacho García). Charts are now read as tables using+      the cached data.++  * Docx writer:++    + Implement flip-flopping for emph (#11961). Emph within emph becomes upright.+      This is important for correct formatting of titles in CSL, among other+      things. LaTeX does this automatically, and in HTML we achieve it via CSS.++  * EPUB writer:++    + Fix `id` clash with added title header (#12003, Alain Lesage).+    + Skip dates that cannot be normalized (#12002, Alain Lesage).++  * Ipynb writer:++    + Make ANSI escape stripping linear (#7744, Jeremy Levartovsky).++  * Vimwiki reader:++    + Support rejected todos (tyler000000).++  * LaTeX writer:++    + Fix hyperlinks for deferred footnotes (#11936). Notes in tables,+      captions, and definition list terms are rendered as a mark+      followed later by the note text. Previously we used+      `\footnotemark{}` and `\footnotetext` with manual adjustments to+      the footnote counter. But hyperref anchors each `\footnotetext` at+      the destination of the most recent `\footnotemark`, so when there+      were several such notes, all of the texts got the last mark's+      anchor and the links for the other marks were broken. We now emit+      `\pandocfootnotemark{n}` and `\pandocfootnotetext{n}{..}`, where+      `n` is a unique id. These macros, defined in the default template+      when the `deferred-notes` variable is set, save the footnote mark+      and hyperref anchor at the mark and restore them at the text. This+      removes the need for counter arithmetic, works when the footnote+      counter is reset by chapter, and handles notes nested inside+      deferred notes (whose texts are now emitted after the text+      containing their marks). **Note:** Users of custom LaTeX+      templates, or of LaTeX fragments produced without `--standalone`,+      will need to include the definitions of `\pandocfootnotemark` and+      `\pandocfootnotetext` from the default template.+    + Allow repeated longtable captions (#4558, Václav Haisman). If a+      table has a `repeat-caption` class, captions will be repeated on+      each page of a longtable.++  * HTML styles template:++    + Add emph flip-flopping. So, emph within emph renders as upright, etc.+    + Move `@print` rules to end (#12064), so they aren't overridden.++  * Text.Pandoc.PDF:++    + PDF via LaTeX: do at most 3 runs when only a TOC is present+      (#12012, Alain Lesage).++  * Text.Pandoc.Class:++    + Export new functions `getDataDirs` and `getPandocDataDirs`. [API change]++  * Text.Pandoc.Citeproc:++    + `removeFinalPeriod` now acts across Link boundary.++  * pandoc-lua-engine: raise lower bound for pandoc to 3.13 (#12066).++  * Depend on texmath 0.13.3.5, commonmark 0.3.2, commonmark-extensions 0.2.8,+    citeproc 0.14.2, typst 0.12.1, emojis 0.1.6, skylighting 0.15.0.1.++  * New bounds for crypton in pandoc-lua-engine and pandoc.++  * Makefile:++    + Fix release-templates target so it pushes tags.+    + Remove default CABALOPTS. These can be put in cabal.project.local.+      This will ensure that the same options are used by HLS, so that rebuilds+      aren't needed.+    + `make check-cabal`: exit with error status on outdated deps.+    + Reorganize `check-cabal; add `check-sdist` target we can run separately.++  * cabal.project: specify `+http` flag for all ghc versions.++  * Minimize the test docx, odt, pptx files. This reduces the+    size of the test files (and release tarball) by 1.79 MB.+ ## pandoc 3.13 (2026-10-26)    * Report the location of errors in defaults files (#8759, Andrzej Rybczak).
data/templates/common.latex view
@@ -91,6 +91,27 @@ $endif$ $endif$ $--+$-- deferred footnotes (in tables, captions, etc.)+$--+$if(deferred-notes)$+% \pandocfootnotemark{n} saves the footnote mark and hyperref anchor;+% \pandocfootnotetext{n}{text} restores them, so the footnote text+% gets the right number and link target.+\makeatletter+\protected\def\pandocfootnotemark#1{\footnotemark{}%+  \expandafter\xdef\csname pandoc@fn@#1\endcsname{%+    \noexpand\pandoc@fnrestore+    {\unexpanded\expandafter{\@thefnmark}}%+    {\ifdefined\Hy@footnote@currentHref+       \unexpanded\expandafter{\Hy@footnote@currentHref}\fi}}}+\def\pandoc@fnrestore#1#2{%+  \gdef\@thefnmark{#1}%+  \ifdefined\Hy@footnote@currentHref\gdef\Hy@footnote@currentHref{#2}\fi}+\newcommand\pandocfootnotetext[1]{%+  \csname pandoc@fn@#1\endcsname\@footnotetext}+\makeatother+$endif$+$-- $-- graphics $-- $if(graphics)$
data/templates/styles.html view
@@ -39,27 +39,6 @@     font-size: 1.8em;   } }-@media print {-  html {-    color-scheme: light only;-  }-  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;-  }-  h2, h3, h4 {-    page-break-after: avoid;-  }-} p {   margin: 1em 0; }@@ -207,6 +186,12 @@ } $endif$ span.smallcaps{font-variant: small-caps;}+em, em em em, em em em em em {+  font-style: italic;+}+em em, em em em em {+  font-style: normal;+} div.columns{display: flex; gap: 1.5em;} div.column{flex: auto;} @media screen {@@ -236,3 +221,24 @@ $if(csl-css)$ $styles.citations.html()$ $endif$+@media print {+  html {+    color-scheme: light only;+  }+  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;+  }+  h2, h3, h4 {+    page-break-after: avoid;+  }+}
pandoc.cabal view
@@ -1,6 +1,6 @@ cabal-version:   2.4 name:            pandoc-version:         3.13+version:         3.14 build-type:      Simple license:         GPL-2.0-or-later license-file:    COPYING.md@@ -233,6 +233,9 @@                  test/command/11944.docx                  test/command/11989.docx                  test/command/11991.docx+                 test/command/11988.docx+                 test/command/9222.docx+                 test/command/9183.docx                  test/command/11942.js                  test/command/biblio.bib                  test/command/averroes.bib@@ -293,6 +296,9 @@                  test/command/11888/section1/lab.md                  test/command/11888/section2/lab.md                  test/command/11888/section3/lab.md+                 test/command/12001/note/doc.md+                 test/command/12001/grid/doc.md+                 test/command/12001/next/doc.md                  test/asciidoc-reader.adoc                  test/asciidoc-reader.native                  test/asciidoc-reader-include.rb@@ -450,8 +456,8 @@                  test/pptx/*.pptx                  test/pptx/**/*.pptx                  test/pptx/**/*.native-                 test/pptx-reader/basic.pptx-                 test/pptx-reader/basic.native+                 test/pptx-reader/*.pptx+                 test/pptx-reader/*.native                  test/ipynb/*.native                  test/ipynb/*.in.native                  test/ipynb/*.out.native@@ -544,18 +550,18 @@                  blaze-markup          >= 0.8      && < 0.9,                  bytestring            >= 0.9      && < 0.13,                  case-insensitive      >= 1.2      && < 1.3,-                 citeproc              >= 0.14.1.1 && < 0.15,-                 commonmark            >= 0.3.1    && < 0.4,-                 commonmark-extensions >= 0.2.7.3  && < 0.3,+                 citeproc              >= 0.14.2   && < 0.15,+                 commonmark            >= 0.3.2    && < 0.4,+                 commonmark-extensions >= 0.2.8    && < 0.3,                  commonmark-pandoc     >= 0.3.0.2  && < 0.4,                  containers            >= 0.6.0.1  && < 0.9,-                 crypton               >= 0.30     && < 2.3,+                 crypton               >= 1.0      && < 2.3,                  data-default          >= 0.4      && < 0.9,                  deepseq               >= 1.3      && < 1.6,                  directory             >= 1.2.5    && < 1.4,                  doclayout             >= 0.6      && < 0.7,                  doctemplates          >= 0.11.1   && < 0.12,-                 emojis                >= 0.1.5    && < 0.2,+                 emojis                >= 0.1.6    && < 0.2,                  exceptions            >= 0.8      && < 0.11,                  file-embed            >= 0.0      && < 0.1,                  filepath              >= 1.1      && < 1.6,@@ -573,13 +579,13 @@                  random                >= 1.2      && < 1.4,                  safe                  >= 0.3.18   && < 0.4,                  scientific            >= 0.3      && < 0.4,-                 skylighting           >= 0.15     && < 0.16,-                 skylighting-core      >= 0.15     && < 0.16,+                 skylighting           >= 0.15.0.1 && < 0.16,+                 skylighting-core      >= 0.15.0.1 && < 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.3.3 && < 0.14,+                 texmath               >= 0.13.3.5 && < 0.14,                  text                  >= 2.0      && < 2.2,                  text-conversions      >= 0.3      && < 0.4,                  time                  >= 1.5      && < 1.17,@@ -591,7 +597,7 @@                  zip-archive           >= 0.5      && < 0.6,                  zlib                  >= 0.6      && < 0.8,                  xml                   >= 1.3.12   && < 1.4,-                 typst                 >= 0.12     && < 0.13,+                 typst                 >= 0.12.1   && < 0.13,                  vector                >= 0.12     && < 0.14,                  djot                  >= 0.1.4.3  && < 0.2,                  asciidoc              >= 0.1.1    && < 0.2@@ -609,7 +615,7 @@                  http-client           >= 0.4.30   && < 0.8,                  http-client-tls       >= 0.2.4    && < 0.5,                  network               >= 2.6      && < 3.3,-                 tls                   >= 2.0.1    && < 2.5+                 tls                   >= 2.0.1    && < 2.6   hs-source-dirs:  src    exposed-modules: Text.Pandoc,@@ -765,6 +771,7 @@                    Text.Pandoc.Readers.Docx.Symbols,                    Text.Pandoc.Readers.Docx.Fields,                    Text.Pandoc.Readers.OOXML.Shared,+                   Text.Pandoc.Readers.OOXML.Charts,                    Text.Pandoc.Readers.Pptx.Parse,                    Text.Pandoc.Readers.Pptx.Shapes,                    Text.Pandoc.Readers.Pptx.Slides,@@ -883,6 +890,7 @@                   Tests.Helpers                   Tests.Shared                   Tests.MediaBag+                  Tests.Data                   Tests.XML                   Tests.ImageSize                   Tests.Readers.LaTeX
src/Text/Pandoc/App/CommandLineOptions.hs view
@@ -42,7 +42,7 @@ import Safe (tailDef) import Skylighting (Syntax (..), defaultSyntaxMap) import System.Console.GetOpt-import System.Environment (getArgs, getProgName)+import System.Environment (getArgs, getProgName, lookupEnv) import System.Exit (exitSuccess) import System.FilePath import System.IO (stdout)@@ -1457,10 +1457,12 @@   let scriptingLine = case mbScriptingEngineName of                         Nothing -> []                         Just name -> ["Scripting engine: " ++ name]+  mbPandocDirs <- System.Environment.lookupEnv "PANDOC_DATA_DIRS"   UTF8.putStr $ T.unlines $ map T.pack $     ["pandoc " ++ showVersion pandocVersion ++ suffix] ++     featuresLine ++     scriptingLine ++-    ["User data directory: " ++ defaultDatadir,-     copyrightMessage]+    ["User data directory: " ++ defaultDatadir] +++    maybe [] (\d -> ["PANDOC_DATA_DIRS: " ++ d]) mbPandocDirs +++    [copyrightMessage]   exitSuccess
src/Text/Pandoc/App/Opt.hs view
@@ -52,7 +52,8 @@                             CiteMethod (Citeproc),                             pattern DefaultHighlightingString) import Text.Pandoc.Class (readFileStrict, fileExists, setVerbosity, report,-                          PandocMonad(lookupEnv), getUserDataDir)+                          PandocMonad(lookupEnv), getUserDataDir,+                          getPandocDataDirs) import Text.Pandoc.Error (PandocError (PandocParseError, PandocSomeError)) import Data.Containers.ListUtils (nubOrd) import Text.Pandoc.Data (defaultUserDataDir)@@ -953,17 +954,16 @@                  -> m FilePath fullDefaultsPath dataDir file = do   defaultDataDir <- liftIO defaultUserDataDir-  let ddir = fromMaybe defaultDataDir dataDir </> "defaults"+  pandocDataDirs <- getPandocDataDirs+  let ddirs = map (</> "defaults") (fromMaybe defaultDataDir dataDir : pandocDataDirs)   let findFile [] = return file       findFile (fp:fps) = do         fpExists <- fileExists fp         if fpExists            then return fp            else findFile fps-  findFile [file,-            file <.> "yaml",-            ddir </> file,-            ddir </> file <.> "yaml"]+  findFile $ [file, file <.> "yaml"] +++             concatMap (\ddir -> [ddir </> file, ddir </> file <.> "yaml"]) ddirs  -- | In a list of lists, append another list in front of every list which -- starts with specific element.
src/Text/Pandoc/Citeproc.hs view
@@ -24,6 +24,7 @@ import qualified Text.Pandoc.Builder as B import Text.Pandoc.Definition as Pandoc import Text.Pandoc.Class (PandocMonad(..), getResourcePath, getUserDataDir,+                          getPandocDataDirs,                           fetchItem, report, setResourcePath, toTextM) import Text.Pandoc.Data (readDataFile) import Text.Pandoc.Error (PandocError(..))@@ -132,9 +133,10 @@   let getFile defaultExtension fp = do         oldRp <- getResourcePath         mbUdd <- getUserDataDir-        setResourcePath $ oldRp ++ maybe []-                                   (\u -> [u <> "/csl",-                                           u <> "/csl/dependent"]) mbUdd+        pandocDataDirs <- getPandocDataDirs+        setResourcePath $ oldRp +++          concatMap (\u -> [u <> "/csl", u <> "/csl/dependent"])+            (maybe id (:) mbUdd pandocDataDirs)         let fp' = if T.any (=='.') fp || "data:" `T.isPrefixOf` fp                      then fp                      else fp <> defaultExtension@@ -637,6 +639,8 @@   case lastMay ils of     Just (Span attr ils')       -> initSafe ils ++ [Span attr (removeFinalPeriod ils')]+    Just (Link attr ils' tgt)+      -> initSafe ils ++ [Link attr (removeFinalPeriod ils') tgt]     Just (Emph ils')       -> initSafe ils ++ [Emph (removeFinalPeriod ils')]     Just (Strong ils')
src/Text/Pandoc/Class/PandocMonad.hs view
@@ -60,6 +60,8 @@   , makeCanonical   , findFileWithDataFallback   , checkUserDataDir+  , getDataDirs+  , getPandocDataDirs   ) where  import Control.Monad.Except (MonadError (catchError, throwError))@@ -75,8 +77,9 @@ import Network.URI ( escapeURIString, nonStrictRelativeTo,                      unEscapeString, parseURIReference, isAllowedInURI,                      parseURI, URI(..) )-import System.FilePath ((</>), takeExtension, dropExtension,-                        isRelative, makeRelative, splitDirectories)+import System.FilePath ((</>), takeExtension, dropExtension, isAbsolute,+                        isRelative, makeRelative, splitDirectories,+                        splitSearchPath) import System.Random (StdGen) import Text.Collate.Lang (Lang(..), parseLang) import Text.Pandoc.Class.CommonState (CommonState (..))@@ -478,6 +481,27 @@      then getUserDataDir      else return Nothing +-- | Returns the directories to search for a data file, in order of+-- priority: the user data directory (if any), followed by the+-- directories returned by 'getPandocDataDirs'.  Returns an empty list+-- if the file path is absolute or relative to a parent directory.+getDataDirs :: PandocMonad m => FilePath -> m [FilePath]+getDataDirs fname =+  if isRelative fname && not (isRelativeToParentDir fname)+     then do+       mbUserDataDir <- getUserDataDir+       pandocDataDirs <- getPandocDataDirs+       return $ maybe id (:) mbUserDataDir pandocDataDirs+     else return []++-- | Returns the directories listed in the @PANDOC_DATA_DIRS@+-- environment variable, in order.  Relative paths (including+-- empty entries) in @PANDOC_DATA_DIRS@ are ignored.+getPandocDataDirs :: PandocMonad m => m [FilePath]+getPandocDataDirs =+  maybe [] (filter isAbsolute . splitSearchPath . T.unpack)+    <$> lookupEnv "PANDOC_DATA_DIRS"+ -- | Read metadata file from the working directory or, if not found there, from -- the metadata subdirectory of the user data directory. readMetadataFile :: PandocMonad m => FilePath -> m B.ByteString@@ -557,29 +581,29 @@         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@--- if neither file exists.+-- searches for the data file relative to @/subdir/@ in the user data+-- directory and then in the system data directories (see 'getDataDirs').+-- Returns @Nothing@ if the file is not found. findFileWithDataFallback :: PandocMonad m                          => FilePath  -- ^ subdir                          -> FilePath  -- ^ fp                          -> m (Maybe FilePath) findFileWithDataFallback subdir fp = do   -- First we check to see if the file is found. If not, and if it's not-  -- an absolute path, we check to see whether it's in @userdir/@. If-  -- not, we leave it unchanged.+  -- an absolute path, we check to see whether it's in @subdir@ of+  -- one of the data directories. If not, we return Nothing.   existsInWorkingDir <- fileExists fp   if existsInWorkingDir      then return $ Just fp-     else do-       mbDataDir <- checkUserDataDir fp-       case mbDataDir of-         Nothing -> return Nothing-         Just datadir -> do-           let datafp = datadir </> subdir </> fp-           existsInDataDir <- fileExists datafp-           return $ if existsInDataDir-                    then Just datafp-                    else Nothing+     else getDataDirs fp >>= findFirst+ where+  findFirst [] = return Nothing+  findFirst (d:ds) = do+    let datafp = d </> subdir </> fp+    exists <- fileExists datafp+    if exists+       then return $ Just datafp+       else findFirst ds  -- | Traverse tree, filling media bag for any images that -- aren't already in the media bag.
src/Text/Pandoc/Data.hs view
@@ -19,8 +19,8 @@                         , getDataFileNames                         , defaultUserDataDir                         ) where-import Text.Pandoc.Class (PandocMonad(..), checkUserDataDir, getTimestamp,-                          getUserDataDir, getPOSIXTime)+import Text.Pandoc.Class (PandocMonad(..), getDataDirs, getTimestamp,+                          getPOSIXTime) import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds) import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString as B@@ -39,15 +39,31 @@ import Paths_pandoc (getDataDir) #endif --- | Read file from the default data files.+-- | Read file from the default data files, ignoring the user data+-- directory and @PANDOC_DATA_DIRS@. readDefaultDataFile :: PandocMonad m => FilePath -> m B.ByteString-readDefaultDataFile "reference.docx" =-  B.concat . BL.toChunks . fromArchive <$> getDefaultReferenceDocx-readDefaultDataFile "reference.pptx" =-  B.concat . BL.toChunks . fromArchive <$> getDefaultReferencePptx-readDefaultDataFile "reference.odt" =-  B.concat . BL.toChunks . fromArchive <$> getDefaultReferenceODT readDefaultDataFile fname =+  case buildReferenceDoc readDefaultDataFile fname of+    Just getArchive -> archiveToByteString <$> getArchive+    Nothing -> readDefaultDataFile' fname++-- | If the file is a reference document, returns an action that builds+-- it from its component files, which are read using the supplied function.+buildReferenceDoc :: PandocMonad m+                  => (FilePath -> m B.ByteString) -> FilePath+                  -> Maybe (m Archive)+buildReferenceDoc readComponent fname =+  case fname of+    "reference.docx" -> Just $ getDefaultReferenceDocx readComponent+    "reference.pptx" -> Just $ getDefaultReferencePptx readComponent+    "reference.odt"  -> Just $ getDefaultReferenceODT readComponent+    _                -> Nothing++archiveToByteString :: Archive -> B.ByteString+archiveToByteString = B.concat . BL.toChunks . fromArchive++readDefaultDataFile' :: PandocMonad m => FilePath -> m B.ByteString+readDefaultDataFile' fname = #ifdef EMBED_DATA_FILES   case lookup (makeCanonical fname) dataFiles of     Nothing       -> throwError $ PandocCouldNotFindDataFileError $ T.pack fname@@ -66,22 +82,28 @@      else throwError $ PandocCouldNotFindDataFileError $ T.pack fn #endif ---- | Read file from user data directory or,---- if not found there, from the default data files.+--- | Read file from user data directory or, if not found there,+--- from one of the directories listed in @PANDOC_DATA_DIRS@,+--- or, if not found there, from the default+--- data files.  If a reference document (e.g. @reference.docx@)+--- is not found, it is built from component files (e.g.+--- @docx/word/styles.xml@), which are looked up in the same way. readDataFile :: PandocMonad m => FilePath -> m B.ByteString-readDataFile fname = do-  datadir <- checkUserDataDir fname-  case datadir of-       Nothing -> readDefaultDataFile fname-       Just userDir -> do-         exists <- fileExists (userDir </> fname)-         if exists-            then readFileStrict (userDir </> fname)-            else readDefaultDataFile fname+readDataFile fname = getDataDirs fname >>= go+ where+  go [] = case buildReferenceDoc readDataFile fname of+            Just getArchive -> archiveToByteString <$> getArchive+            Nothing -> readDefaultDataFile' fname+  go (d:ds) = do+    exists <- fileExists (d </> fname)+    if exists+       then readFileStrict (d </> fname)+       else go ds  -- | Retrieve default reference.docx.-getDefaultReferenceDocx :: PandocMonad m => m Archive-getDefaultReferenceDocx = do+getDefaultReferenceDocx :: PandocMonad m+                        => (FilePath -> m B.ByteString) -> m Archive+getDefaultReferenceDocx readComponent = do   let paths = ["[Content_Types].xml",                "_rels/.rels",                "docProps/app.xml",@@ -101,24 +123,14 @@   let toLazy = BL.fromChunks . (:[])   let pathToEntry path = do         epochtime <- floor . utcTimeToPOSIXSeconds <$> getTimestamp-        contents <- toLazy <$> readDataFile ("docx/" ++ path)+        contents <- toLazy <$> readComponent ("docx/" ++ path)         return $ toEntry path epochtime contents-  datadir <- getUserDataDir-  mbArchive <- case datadir of-                    Nothing   -> return Nothing-                    Just d    -> do-                       exists <- fileExists (d </> "reference.docx")-                       if exists-                          then return (Just (d </> "reference.docx"))-                          else return Nothing-  case mbArchive of-     Just arch -> toArchive <$> readFileLazy arch-     Nothing   -> foldr addEntryToArchive emptyArchive <$>-                     mapM pathToEntry paths+  foldr addEntryToArchive emptyArchive <$> mapM pathToEntry paths  -- | Retrieve default reference.odt.-getDefaultReferenceODT :: PandocMonad m => m Archive-getDefaultReferenceODT = do+getDefaultReferenceODT :: PandocMonad m+                        => (FilePath -> m B.ByteString) -> m Archive+getDefaultReferenceODT readComponent = do   let paths = ["mimetype",                "manifest.rdf",                "styles.xml",@@ -127,24 +139,14 @@                "META-INF/manifest.xml"]   let pathToEntry path = do epochtime <- floor `fmap` getPOSIXTime                             contents <- (BL.fromChunks . (:[])) `fmap`-                                          readDataFile ("odt/" ++ path)+                                          readComponent ("odt/" ++ path)                             return $ toEntry path epochtime contents-  datadir <- getUserDataDir-  mbArchive <- case datadir of-                    Nothing   -> return Nothing-                    Just d    -> do-                       exists <- fileExists (d </> "reference.odt")-                       if exists-                          then return (Just (d </> "reference.odt"))-                          else return Nothing-  case mbArchive of-     Just arch -> toArchive <$> readFileLazy arch-     Nothing   -> foldr addEntryToArchive emptyArchive <$>-                     mapM pathToEntry paths+  foldr addEntryToArchive emptyArchive <$> mapM pathToEntry paths  -- | Retrieve default reference.pptx.-getDefaultReferencePptx :: PandocMonad m => m Archive-getDefaultReferencePptx = do+getDefaultReferencePptx :: PandocMonad m+                        => (FilePath -> m B.ByteString) -> m Archive+getDefaultReferencePptx readComponent = do   -- We're going to narrow this down substantially once we get it   -- working.   let paths = [ "[Content_Types].xml"@@ -201,20 +203,9 @@   let toLazy = BL.fromChunks . (:[])   let pathToEntry path = do         epochtime <- floor <$> getPOSIXTime-        contents <- toLazy <$> readDataFile ("pptx/" ++ path)+        contents <- toLazy <$> readComponent ("pptx/" ++ path)         return $ toEntry path epochtime contents-  datadir <- getUserDataDir-  mbArchive <- case datadir of-                    Nothing   -> return Nothing-                    Just d    -> do-                       exists <- fileExists (d </> "reference.pptx")-                       if exists-                          then return (Just (d </> "reference.pptx"))-                          else return Nothing-  case mbArchive of-     Just arch -> toArchive <$> readFileLazy arch-     Nothing   -> foldr addEntryToArchive emptyArchive <$>-                     mapM pathToEntry paths+  foldr addEntryToArchive emptyArchive <$> mapM pathToEntry paths  getDataFileNames :: IO [FilePath] getDataFileNames = do
src/Text/Pandoc/Extensions.hs view
@@ -541,7 +541,8 @@     , Ext_xrefs_name     , Ext_xrefs_number     ]-  getAll "odt"             = getAll "opendocument" <> autoIdExtensions+  getAll "odt"             = getAll "opendocument" <> autoIdExtensions <>+    extensionsFromList [ Ext_styles ]   getAll "fodt"            = getAll "odt"   getAll "muse"            = autoIdExtensions <>     extensionsFromList
src/Text/Pandoc/PDF.hs view
@@ -456,7 +456,8 @@      let rerunWarnings = checkForRerun logContents      tocFileExists <- fileExists (replaceExtension outfile ".toc")        -- if we have a TOC we always need 3 runs, see #10308-     let rerunWarnings' = rerunWarnings ++ ["TOC is present" | tocFileExists]+     let rerunWarnings' = rerunWarnings +++           ["TOC is present" | tocFileExists, runNumber < 3]      if not (null rerunWarnings') && runNumber < maxruns         then do           report $ MakePDFInfo "Rerun needed"
src/Text/Pandoc/Parsing/GridTable.hs view
@@ -38,7 +38,8 @@ import Text.Pandoc.Parsing.Capabilities import Text.Pandoc.Parsing.General import Text.Pandoc.Sources-import Text.Parsec (Stream (..), ParsecT, optional, sepEndBy1, try)+import Text.Parsec (Stream (..), ParsecT, getPosition, optional, sepEndBy1,+                    setPosition, try) import Data.Maybe (mapMaybe) import qualified Data.Text as T import qualified Text.GridTable as GT@@ -134,9 +135,10 @@                -> ParsecT Sources st m (mf Blocks) -- ^ Block list parser                -> ParsecT Sources st m (mf TableComponents) gridTableWith' normalization blocks = do+  pos <- getPosition   tbl <- GT.gridTable <* optional blanklines   let blkTbl = GT.mapCells-               (\lns -> parseFromString' blocks+               (\lns -> parseFromString' (setPosition pos >> blocks)                         . flip T.snoc '\n'  -- ensure proper block parsing                         . T.unlines                         . removeOneLeadingSpace
src/Text/Pandoc/Readers/Docx.hs view
@@ -331,7 +331,8 @@             superscript . go rPr{rVertAlign = Nothing}         | Just SubScrpt <- rVertAlign rPr =             subscript . go rPr{rVertAlign = Nothing}-        | Just "single" <- rUnderline rPr =+        | Just u <- rUnderline rPr+        , u /= "none" =             Pandoc.underline . go rPr{rUnderline = Nothing}         | otherwise = id   return $ go rPr'
src/Text/Pandoc/Readers/Docx/Combine.hs view
@@ -191,6 +191,13 @@   , CodeBlock attr' codeStr' :< cs' <- viewl (unMany cs)   , attr == attr' =       Many $ (bs' |> CodeBlock attr (codeStr <> "\n" <> codeStr')) >< cs'+  -- with +styles, each code paragraph is wrapped in a custom-style Div (#9183)+  | bs' :> Div divAttr [CodeBlock attr codeStr] <- viewr (unMany bs)+  , Div divAttr' [CodeBlock attr' codeStr'] :< cs' <- viewl (unMany cs)+  , divAttr == divAttr'+  , attr == attr' =+      Many $ (bs' |> Div divAttr [CodeBlock attr (codeStr <> "\n" <> codeStr')])+        >< cs' combineBlocks bs cs = bs <> cs  instance (Monoid a, Eq a) => Eq (Modifier a) where
src/Text/Pandoc/Readers/Docx/Parse.hs view
@@ -704,7 +704,23 @@           numElems = findChildrenByName namespaces "w" "num" numberingElem           absNumElems = findChildrenByName namespaces "w" "abstractNum" numberingElem           nums = mapMaybe (numElemToNum namespaces) numElems-          absNums = mapMaybe (absNumElemToAbsNum namespaces) absNumElems+          parsed = mapMaybe (\el -> (,) el <$> absNumElemToAbsNum namespaces el)+                     absNumElems+          linkVal name el = findChildByName namespaces "w" name el+                            >>= findAttrByName namespaces "w" "val"+          -- An abstractNum with w:numStyleLink has no levels of its own: they+          -- are in the abstractNum whose w:styleLink names the same numbering+          -- style (#9222).+          levelsByStyle = M.fromList+            [ (s, lvls) | (el, AbstractNumb _ lvls) <- parsed+                        , Just s <- [linkVal "styleLink" el] ]+          resolve (el, absNum@(AbstractNumb absNumId lvls))+            | null lvls+            , Just lvls' <- linkVal "numStyleLink" el+                            >>= (`M.lookup` levelsByStyle)+            = AbstractNumb absNumId lvls'+            | otherwise = absNum+          absNums = map resolve parsed       return $ Numbering namespaces nums absNums  archiveToNumbering :: Archive -> Numbering@@ -1219,7 +1235,7 @@                 (matchQName "drawingml" "chart" (Just "c") "chart") drawingElem   = return [Chart] elemToParPart' ns element-  | isElem ns "w" "r" element = do+  | isElem ns "w" "r" element || isElem ns "w" "customXml" element = do     runs <- elemToRun ns element     return $ map PlainRun runs elemToParPart' ns element@@ -1364,6 +1380,9 @@     runElems <- elemToRunElems ns element     runStyle <- elemToRunStyleD ns element     return [Run runStyle runElems]+elemToRun ns element+  | isElem ns "w" "customXml" element =+    mconcat <$> mapM (elemToRun ns) (elChildren element) elemToRun _ _ = throwError WrongElem  getParentStyleValue :: (ParStyle -> Maybe a) -> ParStyle -> Maybe a
src/Text/Pandoc/Readers/HTML.hs view
@@ -310,7 +310,7 @@        _ -> False)   let attr = toStringAttr attr'   let ident = fromMaybe "" (lookup "id" attr)-  content <- pInTags tag block+  content <- local (\s -> s{ inNoteBody = True }) $ pInTags tag block   updateState $ \s ->     s {noteTable = M.insert ident content (noteTable s)} @@ -422,7 +422,11 @@ pOrderedList :: PandocMonad m => TagParser m Blocks pOrderedList = try $ do   TagOpen _ attribs' <- pSatisfy (matchTagOpen "ol" [])-  isNoteList <- inFootnotes <$> getState+  -- an <ol> inside the body of a note is an ordered list, not the+  -- list of notes (#11852):+  inNotes <- inFootnotes <$> getState+  inBody <- asks inNoteBody+  let isNoteList = inNotes && not inBody   let attribs = toStringAttr attribs'   let start = fromMaybe 1 $ lookup "start" attribs >>= safeRead   let style = fromMaybe DefaultStyle@@ -637,7 +641,8 @@ pHrule = do   pSelfClosing (=="hr") (const True)   inNotes <- inFootnotes <$> getState-  return $ if inNotes+  inBody <- asks inNoteBody+  return $ if inNotes && not inBody               then mempty               else B.horizontalRule 
src/Text/Pandoc/Readers/HTML/Types.hs view
@@ -64,6 +64,7 @@   , inChapter    :: Bool -- ^ Set if in chapter section   , inPlain      :: Bool -- ^ Set if in pPlain   , inListItem   :: Bool -- ^ Set if in <li> tag+  , inNoteBody   :: Bool -- ^ Set if parsing the content of a footnote   }  @@ -95,7 +96,7 @@   deleteMeta s st = st {parserState = deleteMeta s $ parserState st}  instance Default HTMLLocal where-  def = HTMLLocal NoQuote False False False+  def = HTMLLocal NoQuote False False False False  instance HasLastStrPosition HTMLState where   setLastStrPos s st = st {parserState = setLastStrPos s (parserState st)}
src/Text/Pandoc/Readers/LaTeX.hs view
@@ -138,13 +138,15 @@  -- | Resolve footnote marks (\footnotemark) to actual notes using -- the footnote texts collected from \footnotetext commands.-resolveFootnoteMarks :: M.Map Int Blocks -> Inline -> Inline+-- Marks inside the resolved note texts are resolved too (each text+-- is used at most once, so this terminates).+resolveFootnoteMarks :: M.Map Text Blocks -> Inline -> Inline resolveFootnoteMarks fnTexts (Span (_, classes, kvs) _)   | "footnote-mark" `elem` classes-  , Just numText <- lookup "note-num" kvs-  , [(n, "")] <- reads (T.unpack numText)-  = case M.lookup n fnTexts of-      Just contents -> Note (toList contents)+  , Just key <- lookup "note-key" kvs+  = case M.lookup key fnTexts of+      Just contents -> Note $ walk (resolveFootnoteMarks (M.delete key fnTexts))+                            $ toList contents       Nothing       -> Str ""  -- No matching footnotetext found resolveFootnoteMarks _ x = x @@ -440,6 +442,8 @@     , ("footnote", skipopts >> footnote)     , ("footnotemark", footnotemark)     , ("footnotetext", footnotetext)+    , ("pandocfootnotemark", pandocfootnotemark)+    , ("pandocfootnotetext", pandocfootnotetext)     , ("newline", pure B.linebreak)     -- xparse argument markers, in case they leak into the document:     , ("NoValue", pure (B.str "-NoValue-"))@@ -538,7 +542,7 @@     Nothing -> do       updateState $ \st -> st{ sLastNoteNum = sLastNoteNum st + 1 }       sLastNoteNum <$> getState-  return $ B.spanWith ("", ["footnote-mark"], [("note-num", tshow noteNum)]) mempty+  return $ footnoteMarkSpan (tshow noteNum)  -- | Parse \footnotetext[n]{text}. If n is not given, use current counter. -- Stores the text in state to be resolved later.@@ -548,11 +552,51 @@   noteNum <- case mbNum of     Just n  -> return n     Nothing -> sLastNoteNum <$> getState+  storeFootnoteText (tshow noteNum)++-- | Parse \pandocfootnotemark{id}, which is produced by the LaTeX writer+-- for notes in tables, captions, etc. It is paired with+-- \pandocfootnotetext{id}{text}.+pandocfootnotemark :: PandocMonad m => LP m Inlines+pandocfootnotemark = do+  key <- untokenize <$> braced+  updateState $ \st -> st{ sLastNoteNum = sLastNoteNum st + 1 }+  return $ footnoteMarkSpan ("pandoc:" <> key)++-- | Parse \pandocfootnotetext{id}{text}.+pandocfootnotetext :: PandocMonad m => LP m Inlines+pandocfootnotetext = do+  key <- untokenize <$> braced+  storeFootnoteText ("pandoc:" <> key)++-- | A placeholder for a footnote mark, to be resolved later+-- (see 'resolveFootnoteMarks').+footnoteMarkSpan :: Text -> Inlines+footnoteMarkSpan key = B.spanWith ("", ["footnote-mark"], [("note-key", key)]) mempty++storeFootnoteText :: PandocMonad m => Text -> LP m Inlines+storeFootnoteText key = do   contents <- grouped block >>= walkM resolveNoteLabel   updateState $ \st -> st{-    sFootnoteTexts = M.insert noteNum contents (sFootnoteTexts st) }+    sFootnoteTexts = M.insert key contents (sFootnoteTexts st) }   return mempty +-- | Parse \addtocounter{ctr}{n}. Adjustments to the footnote counter+-- are tracked, so that \footnotetext gets the right number;+-- otherwise the command is treated as raw LaTeX.+addtocounter :: PandocMonad m => LP m Blocks+addtocounter = do+  (ctrAndNum, toks) <- withRaw $ (,) <$> (untokenize <$> braced)+                                     <*> (untokenize <$> braced)+  case ctrAndNum of+    ("footnote", num) | Just n <- safeRead (T.strip num) -> do+      updateState $ \st -> st{ sLastNoteNum = sLastNoteNum st + n }+      return mempty+    _ -> do+      let raw = "\\addtocounter" <> untokenize toks+      (guardEnabled Ext_raw_tex >> return (rawBlock "latex" raw))+        <|> ignore raw+ -- | Parse optional footnote number argument [n] optionalFootnoteNum :: PandocMonad m => LP m (Maybe Int) optionalFootnoteNum = option Nothing $ do@@ -765,7 +809,7 @@    [ "special", "pdfannot", "pdfstringdef"    , "bibliographystyle"    , "maketitle", "makeindex", "makeglossary"-   , "addcontentsline", "addtocontents", "addtocounter"+   , "addcontentsline", "addtocontents"       -- \ignore{} is used conventionally in literate haskell for definitions       -- that are to be processed by the compiler but not printed.    , "ignore"@@ -1094,6 +1138,7 @@ blockCommands :: PandocMonad m => M.Map Text (LP m Blocks) blockCommands = M.fromList    [ ("par", mempty <$ skipopts)+   , ("addtocounter", addtocounter)    , ("parbox",  parbox)    , ("title", mempty <$ (skipopts *>                              (grouped inline >>= addMeta "title")
src/Text/Pandoc/Readers/LaTeX/Macro.hs view
@@ -36,7 +36,9 @@             newDocumentEnvironment <|> commandCopy <|> environmentCopy <|>             checkGlobal (letmacro <|> edefmacro <|> defmacro <|> newif)           guardDisabled Ext_latex_macros <|>-            mapM_ insertMacro nameMacroPairs+            mapM_ insertMacro+              (filter ((`Set.notMember` pandocInternalMacros) . fst)+                      nameMacroPairs)         environmentDef = do           mbenv <- newenvironment           case mbenv of@@ -49,6 +51,13 @@         -- is equivalent to         -- @\newcommand{\envname}[n-args][default]{begin}@         -- @\newcommand{\endenvname}@++-- | Macros defined in pandoc's LaTeX template and handled directly+-- by the reader. Their definitions are too complex for the reader's+-- macro expansion, so we ignore them.+pandocInternalMacros :: Set.Set Text+pandocInternalMacros = Set.fromList+  [ "pandocbounded", "pandocfootnotemark", "pandocfootnotetext" ]  -- | Control sequences that can begin a macro definition.  This -- must include every control sequence that one of the parsers
src/Text/Pandoc/Readers/LaTeX/Parsing.hs view
@@ -170,7 +170,10 @@                             , sLastFigureNum :: DottedNum                             , sLastTableNum  :: DottedNum                             , sLastNoteNum   :: Int-                            , sFootnoteTexts :: M.Map Int Blocks+                            , sFootnoteTexts :: M.Map Text Blocks+                              -- ^ keys are footnote numbers for+                              -- \footnotetext and "pandoc:" ids for+                              -- \pandocfootnotetext                             , sTheoremMap    :: M.Map Text TheoremSpec                             , sLastTheoremStyle :: TheoremStyle                             , sLastLabel     :: Maybe Text@@ -350,7 +353,7 @@                 updateState (updateMacros ((NonEmpty.head (sMacros st)) <>))                 let rawChar = do                       pos <- getPosition-                      if pos >= endpos+                      if sourceName pos == sourceName endpos && pos >= endpos                          then mzero                          else anyChar                 result <- (guardEnabled Ext_latex_macros@@ -461,11 +464,10 @@                                Just ('\n', _) ->                                  Tok pos (CtrlSeq " ") ("\\" <> w1)                                  : totoks atIsLetter-                                    (incSourceColumn pos (T.length ws)) r1+                                    (incSourceColumn pos (1 + T.length w1)) r1                                _ ->                                  Tok pos (CtrlSeq " ") ws-                                 : totoks atIsLetter-                                    (incSourceColumn pos (T.length ws)) r3+                                 : totoks atIsLetter (advancePos pos ws) r3                   | otherwise  ->                       Tok pos (CtrlSeq (T.singleton d)) (T.pack [c,d])                       : totoks atIsLetter (incSourceColumn pos 2) rest'@@ -517,6 +519,12 @@          | otherwise ->            Tok pos Symbol (T.singleton c) :              totoks atIsLetter (incSourceColumn pos 1) rest++  -- Keep tabs at one column, as in the rest of the tokenizer.+  advancePos = T.foldl' $ \pos c ->+    if c == '\n'+       then setSourceColumn (incSourceLine pos 1) 1+       else incSourceColumn pos 1  isSpaceOrTab :: Char -> Bool isSpaceOrTab ' '  = True
src/Text/Pandoc/Readers/Markdown.hs view
@@ -466,7 +466,7 @@      rest <- many $ try $ blanklines >> indentSpaces >> rawLines      let raw = T.unlines (first:rest) <> "\n"      optional blanklines-     parsed <- parseFromString' parseBlocks raw+     parsed <- parseFromString' (setPosition pos >> parseBlocks) raw      oldnotes <- stateNotes' <$> getState      case M.lookup ref oldnotes of        Just _  -> logMessage $ DuplicateNoteReference ref pos
src/Text/Pandoc/Readers/ODT.hs view
@@ -61,10 +61,10 @@          => ReaderOptions          -> a          -> m Pandoc-readFODT _ inp = do+readFODT opts inp = do   root <- either (throwError . PandocXMLError "") pure             (parseXMLElement (TL.fromStrict (sourcesToText (toSources inp))))-  case elementToODT root of+  case elementToODT opts root of     Right (doc, mb) -> do       P.setMediaBag mb       return $ walk makeFigure doc@@ -74,8 +74,8 @@ -- package distributes over @content.xml@ and @styles.xml@, and they are -- all children of the root.  The converter addresses everything relative -- to the element it is given, so it can simply be handed that root.-elementToODT :: Element -> Either PandocError (Pandoc, MediaBag)-elementToODT root = do+elementToODT :: ReaderOptions -> Element -> Either PandocError (Pandoc, MediaBag)+elementToODT opts root = do   -- the local name only, consistently with the URI-based namespace   -- matching downstream; office:document-content is correctly rejected   unless (XML.qName (XML.elName root) == "document") $@@ -87,7 +87,8 @@                (readStylesAt root)   -- the media list is empty: all media arrives in office:binary-data   either (\_ -> Left $ PandocParseError "Could not convert opendocument") Right-    (runConverter read_body (readerState styles []) root)+    (runConverter read_body+       (readerState (isEnabled Ext_styles opts) styles []) root)  -- the ODT parser uses old-style figures: an image with title beginning -- "fig:" in a paragraph by itself.  Convert these to new Figure elements.@@ -108,20 +109,14 @@ readODT' :: ReaderOptions          -> B.ByteString          -> Either PandocError (Pandoc, MediaBag)-readODT' _ bytes = bytesToODT bytes-- of---                    Right (pandoc, mediaBag) -> Right (pandoc , mediaBag)---                    Left  err                -> Left err-----bytesToODT :: B.ByteString -> Either PandocError (Pandoc, MediaBag)-bytesToODT bytes = case toArchiveOrFail bytes of-  Right archive -> archiveToODT archive+readODT' opts bytes = case toArchiveOrFail bytes of+  Right archive -> archiveToODT opts archive   Left err      -> Left $ PandocParseError                         $ "Could not unzip ODT: " <> T.pack err  ---archiveToODT :: Archive -> Either PandocError (Pandoc, MediaBag)-archiveToODT archive = do+archiveToODT :: ReaderOptions -> Archive -> Either PandocError (Pandoc, MediaBag)+archiveToODT opts archive = do   let onFailure msg Nothing = Left $ PandocParseError msg       onFailure _   (Just x) = Right x   contentEntry <- onFailure "Could not find content.xml"@@ -139,7 +134,7 @@         let (dir, name) = splitFileName fp         in  (dir == "Pictures/") || (dir /= "./" && name == "content.xml")   let media = filteredFilesFromArchive archive filePathIsODTMedia-  let startState = readerState styles media+  let startState = readerState (isEnabled Ext_styles opts) styles media   either (\_ -> Left $ PandocParseError "Could not convert opendocument") Right     (runConverter read_body startState contentElem) 
src/Text/Pandoc/Readers/ODT/ContentReader.hs view
@@ -101,13 +101,16 @@                  , envMedia         :: Media                    -- | Hold binary resources used in the document                  , odtMediaBag      :: MediaBag+                   -- | Whether to record named styles in @custom-style@+                   -- attributes (the @styles@ extension)+                 , keepStyles       :: Bool                  }   deriving ( Show ) -readerState :: Styles -> [(FilePath, B.ByteString)] -> ReaderState-readerState styles media =+readerState :: Bool -> Styles -> [(FilePath, B.ByteString)] -> ReaderState+readerState keepStyles' styles media =   ReaderState styles [] 0 M.empty Nothing Nothing M.empty Set.empty-              (M.fromList media) mempty+              (M.fromList media) mempty keepStyles'  -- pushStyle'  :: Style -> ReaderState -> ReaderState@@ -270,9 +273,61 @@   style <- getStyleByName name   return (name, style) ----withNewStyle :: ODTReader Inlines -> ODTReader Inlines-withNewStyle reader = do+-- | With the @styles@ extension, the name (as shown to the user) of the+-- named style that the current element is an instance of, unless the+-- first argument says to ignore it. If the element's style is an automatic+-- one, its named parent is used. A style that is not defined at all is+-- taken to be a named one, since automatic styles are always defined.+getCustomStyle :: (StyleName -> Bool) -> ODTReader (Maybe T.Text)+getCustomStyle ignored = do+  keep <- keepStyles <$> getExtraState+  if not keep+     then return Nothing+     else do+       fName  <- tryC (findAttr NsText "style-name")+       styles <- getStyles+       return $ case fName of+         Right name+           | Just (name', mbStyle) <- lookupNamedStyle name styles+           , not (ignored name')+           -> Just (styleUIName name' mbStyle)+         _ -> Nothing++customStyleAttr :: T.Text -> Attr+customStyleAttr sty = ("", [], [("custom-style", sty)])++-- | Paragraph styles not recorded with the @styles@ extension: the+-- default style, the default styles of table cells, and styles that+-- pandoc interprets itself.+ignoredParaStyle :: StyleName -> Bool+ignoredParaStyle name = name `elem` [ "Standard"+                                    , "Table_20_Contents"+                                    , "Table_20_Heading"+                                    ] || isTableCaptionStyle name++-- | Character styles not recorded with the @styles@ extension.+ignoredCharStyle :: StyleName -> Bool+ignoredCharStyle name = name `elem` [ "Internet_20_link"+                                    , "Visited_20_Internet_20_Link"+                                    , "Emphasis"+                                    , "Strong_20_Emphasis"+                                    ] || isCodeStyle name++isCodeStyle :: StyleName -> Bool+isCodeStyle "Source_Text" = True+isCodeStyle "Source_20_Text" = True+isCodeStyle _             = False++isTableCaptionStyle :: StyleName -> Bool+isTableCaptionStyle "Table" = True+isTableCaptionStyle _       = False++-- | Apply the text formatting of the current element's style. If the+-- first argument is 'True', the formatting of a named (non-automatic)+-- style is not applied, since it is recorded in a @custom-style@+-- attribute instead; the direct formatting of an automatic style still is.+withNewStyle :: Bool -> ODTReader Inlines -> ODTReader Inlines+withNewStyle styleRecorded reader = do   fStyle <- tryC readStyleByName   case fStyle of     Right (styleName, _) | isCodeStyle styleName ->@@ -281,18 +336,15 @@       | Just textProps <- textProperties (styleProperties style) -> do           state <- getExtraState           let mFamily  = styleFamily style-              modifier = modifierFromStyleDiff (state, textProps, mFamily)+              modifier+                | styleRecorded && not (styleAutomatic style) = id+                | otherwise = modifierFromStyleDiff (state, textProps, mFamily)           pushStyle style           inlines <- reader           popStyle           return $ modifier inlines     _ -> reader   where-    isCodeStyle :: StyleName -> Bool-    isCodeStyle "Source_Text" = True-    isCodeStyle "Source_20_Text" = True-    isCodeStyle _             = False-     inlineCode :: Inlines -> Inlines     inlineCode = code . stringifyInlines @@ -387,9 +439,6 @@       listLevel <- getCurrentListLevel       getParaModifier listLevel props <$> reader   where-    isTableCaptionStyle :: StyleName -> Bool-    isTableCaptionStyle "Table" = True-    isTableCaptionStyle _       = False     tableCaptionP b = divWith ("", ["caption"], []) b  type ListConstructor = [Blocks] -> Blocks@@ -565,8 +614,14 @@                     $ return space -- read_span        :: InlineMatcher-read_span         = matchingElement NsText "span"-                    $ withNewStyle matchInlineContent+read_span         = matchingElement NsText "span" $ do+                      mbCustom <- getCustomStyle ignoredCharStyle+                      inlines  <- withNewStyle (isJust mbCustom)+                                    matchInlineContent+                      return $ case mbCustom of+                        Just sty | not (null inlines)+                          -> spanWith (customStyleAttr sty) inlines+                        _ -> inlines  -- | ODF wraps inline content in a few elements that carry nothing but -- metadata: @text:meta@ holds RDFa annotations, @text:meta-field@ marks@@ -618,13 +673,27 @@  -- read_paragraph   :: Matcher CombiningBlocks-read_paragraph    = matchingElement NsText "p" $ fmap CombiningBlocks $ do-                      fStyle <- tryC readStyleByName-                      case fStyle of-                        Right style | isPreformattedStyle style ->-                          codeBlock . stringifyInlines <$> matchInlineContent-                        _ ->-                          constructPara (para <$> withNewStyle matchInlineContent)+read_paragraph    = read_paragraph' True++-- | Read a paragraph. If the argument is 'True' and the @styles@ extension+-- is enabled, a paragraph with a named style (other than one of the+-- 'ignoredParaStyle's) is wrapped in a Div with a @custom-style@+-- attribute. This is not done for the paragraphs of list items, which+-- would make every list loose.+read_paragraph'  :: Bool -> Matcher CombiningBlocks+read_paragraph' recordStyle = matchingElement NsText "p" $ fmap CombiningBlocks $ do+                      fStyle    <- tryC readStyleByName+                      listLevel <- getCurrentListLevel+                      mbCustom  <- if recordStyle && listLevel == 0+                                      then getCustomStyle ignoredParaStyle+                                      else return Nothing+                      maybe id (divWith . customStyleAttr) mbCustom <$>+                        case fStyle of+                          Right style | isPreformattedStyle style ->+                            codeBlock . stringifyInlines <$> matchInlineContent+                          _ ->+                            constructPara (para <$> withNewStyle (isJust mbCustom)+                                                      matchInlineContent)                     where                       isPreformattedStyle :: (StyleName, Style) -> Bool                       isPreformattedStyle ("Preformatted_20_Text", _) = True@@ -681,7 +750,11 @@   level    <- readAttrWithDefault NsText "outline-level" 1   children <- matchInlineContent   anchor   <- getHeaderAnchor children-  let idAttr = (anchor, [], []) -- no classes, no key-value pairs+  -- with the styles extension, record any style other than the+  -- standard one for the heading's level+  mbCustom <- getCustomStyle (\name -> name == "Heading_20_" <> tshow level+                                    || name == "Heading")+  let idAttr = (anchor, [], maybe [] (\sty -> [("custom-style", sty)]) mbCustom)   return $ CombiningBlocks $ headerWith idAttr level children  ----------------------@@ -1006,7 +1079,9 @@  read_frame_text_box :: XML.Element -> ODTReader (FirstMatch Inlines) read_frame_text_box box = do-  paragraphs <- executeIn box (matchSmushedChildBlocks' [ read_paragraph ])+  -- caption paragraphs must not be wrapped in Divs (see+  -- 'read_img_with_caption')+  paragraphs <- executeIn box (matchSmushedChildBlocks' [ read_paragraph' False ])   return $ read_img_with_caption $ toList paragraphs  read_img_with_caption :: [Block] -> FirstMatch Inlines
src/Text/Pandoc/Readers/ODT/StyleReader.hs view
@@ -29,6 +29,8 @@ , ListLevelType        (..) , LengthOrPercent      (..) , lookupStyle+, lookupNamedStyle+, styleUIName , getListLevelStyle , getStyleFamily , lookupDefaultStyle'@@ -39,6 +41,7 @@  import Control.Applicative ((<|>), optional) +import Data.Char (chr, isHexDigit) import Data.Default import qualified Data.Foldable as F import qualified Data.Map as M@@ -46,6 +49,7 @@ import Data.Text (Text) import qualified Data.Text as T import qualified Data.Set as S+import Numeric (readHex)  import qualified Text.Pandoc.XML.Light as XML @@ -178,10 +182,17 @@                 ]  -- | A named style-data Style            = Style  { styleFamily     :: Maybe StyleFamily-                               , styleParentName :: Maybe StyleName-                               , listStyle       :: Maybe StyleName-                               , styleProperties :: StyleProperties+data Style            = Style  { styleFamily      :: Maybe StyleFamily+                               , styleParentName  :: Maybe StyleName+                               , listStyle        :: Maybe StyleName+                               , styleProperties  :: StyleProperties+                                 -- | The name shown to the user+                                 -- (@style:display-name@), if given.+                               , styleDisplayName :: Maybe Text+                                 -- | Whether this is an automatic style,+                                 -- i.e. one that only records direct+                                 -- formatting and is not visible to the user.+                               , styleAutomatic   :: Bool                                }   deriving ( Eq, Show ) @@ -404,14 +415,14 @@ -- readStyles :: StyleReader Styles readStyles = executeInSub NsOffice "styles" $-  Styles <$> (M.fromList <$> tryAll NsStyle "style"         readStyle       )+  Styles <$> (M.fromList <$> tryAll NsStyle "style"   (readStyle False) )          <*> (M.fromList <$> tryAll NsText  "list-style"    readListStyle   )          <*> (M.fromList <$> tryAll NsStyle "default-style" readDefaultStyle)  -- readAutomaticStyles :: StyleReader Styles readAutomaticStyles = executeInSub NsOffice "automatic-styles" $-  Styles <$> (M.fromList <$> tryAll NsStyle "style"         readStyle       )+  Styles <$> (M.fromList <$> tryAll NsStyle "style"   (readStyle True)  )          <*> (M.fromList <$> tryAll NsText  "list-style"    readListStyle   )          <*> pure M.empty @@ -420,14 +431,16 @@ readDefaultStyle = (,) <$> lookupAttr NsStyle "family"                        <*> readStyleProperties ----readStyle :: StyleReader (StyleName, Style)-readStyle = do+-- | Read a style; the argument says whether it is an automatic style.+readStyle :: Bool -> StyleReader (StyleName, Style)+readStyle automatic = do   name  <- findAttr NsStyle "name"   style <- Style <$> lookupAttr' NsStyle "family"                  <*> findAttr'   NsStyle "parent-style-name"                  <*> findAttr'   NsStyle "list-style-name"                  <*> readStyleProperties+                 <*> findAttr'   NsStyle "display-name"+                 <*> pure automatic   return (name, style)  --@@ -553,6 +566,48 @@ -- lookupStyle           :: StyleName   -> Styles -> Maybe Style lookupStyle name Styles{..} = M.lookup name stylesByName++-- | Find the named (i.e., non-automatic) style that an element with the+-- given style name is an instance of. An automatic style (@P1@, @T3@, ...)+-- only records direct formatting, so it is followed up its chain of parents.+-- Returns the internal name of the style together with the style, which+-- may not be defined: automatic styles always are, so an undefined style+-- is a named one.+lookupNamedStyle      :: StyleName   -> Styles+                      -> Maybe (StyleName, Maybe Style)+lookupNamedStyle name0 styles = go S.empty name0+  where+    go seen name+      | name `S.member` seen = Nothing  -- cyclic parent references+      | otherwise =+          case lookupStyle name styles of+            Nothing -> Just (name, Nothing)+            Just style+              | styleAutomatic style+                -> styleParentName style >>= go (S.insert name seen)+              | otherwise -> Just (name, Just style)++-- | The name of a style as shown to the user: its @style:display-name@,+-- if any, or else its internal name with ODF's escapes (@_20_@ for a+-- space, etc.) decoded.+styleUIName           :: StyleName   -> Maybe Style -> Text+styleUIName name mbStyle =+  fromMaybe (decodeStyleName name) (mbStyle >>= styleDisplayName)++-- | Decode the hex escapes (e.g. @_20_@) that ODF uses for characters that+-- are not allowed in style names.+decodeStyleName :: StyleName -> Text+decodeStyleName t =+  case T.breakOn "_" t of+    (before, rest)+      | T.null rest -> before+      | (hex, rest') <- T.span isHexDigit (T.drop 1 rest)+      , not (T.null hex)+      , "_" `T.isPrefixOf` rest'+      , [(n, "")] <- readHex (T.unpack hex)+      , n <= 0x10FFFF+      -> before <> T.singleton (chr n) <> decodeStyleName (T.drop 1 rest')+      | otherwise -> before <> "_" <> decodeStyleName (T.drop 1 rest)  -- lookupDefaultStyle'   :: Styles -> StyleFamily -> StyleProperties
+ src/Text/Pandoc/Readers/OOXML/Charts.hs view
@@ -0,0 +1,171 @@+{-# LANGUAGE OverloadedStrings #-}+{- |+   Module      : Text.Pandoc.Readers.OOXML.Charts+   Copyright   : © 2026 Nacho García+   License     : GNU GPL, version 2 or above++   Maintainer  : Nacho García <hello@nachogarcia.dev>+   Stability   : alpha+   Portability : portable++Reading of DrawingML charts (in PPTX slides and XLSX sheets), from the+values cached in the chart part.+-}+module Text.Pandoc.Readers.OOXML.Charts+  ( Chart(..)+  , ChartSeries(..)+  , readChart+  , chartToBlocks+  ) where++import Codec.Archive.Zip (Archive, findEntryByPath, fromEntry)+import Data.List (find, nub, transpose)+import qualified Data.Map.Strict as M+import Data.Maybe (fromMaybe, listToMaybe, mapMaybe)+import qualified Data.Text as T+import qualified Data.Text.Lazy.Encoding as TL+import Data.Text (Text)+import qualified Text.Pandoc.Builder as B+import Text.Pandoc.Definition+import Text.Pandoc.Readers.OOXML.Shared+import Text.Pandoc.XML.Light+import Text.Read (readMaybe)++-- | A chart's data, as cached in its chart part+data Chart = Chart+  { chartTitle  :: Text     -- ^ Empty if the chart has no title+  , chartTypes  :: [Text]   -- ^ Such as @bar@ or @line@, one per plot+  , chartSeries :: [ChartSeries]+  } deriving (Show)++-- | One series: its name, and its points as (category, value)+data ChartSeries = ChartSeries+  { seriesName   :: Text+  , seriesPoints :: [(Text, Text)]+  } deriving (Show)++-- | Read a chart part (@c:chartSpace@)+readChart :: Archive -> FilePath -> Either Text Chart+readChart archive path = do+  entry <- maybe (Left $ "Entry not found: " <> T.pack path) Right $+           findEntryByPath path archive+  chartSpace <- parseXMLElement (TL.decodeUtf8 (fromEntry entry))+  chart <- maybe (Left "Missing c:chart") Right $ child "chart" chartSpace+  plotArea <- maybe (Left "Missing c:plotArea") Right $+              child "plotArea" chart+  let plots = filter (("Chart" `T.isSuffixOf`) . qName . elName)+                     (elChildren plotArea)+      series = zipWith parseSeries [1..] (concatMap (children "ser") plots)+      title = maybe "" titleText (child "title" chart)+  if all (all (T.null . snd) . seriesPoints) series+     then Left "Chart has no cached values"+     else Right Chart+            { chartTitle = title+            , chartTypes = nub $ map (T.dropEnd 5 . qName . elName) plots+            , chartSeries = series+            }++-- | Text of a chart title: rich text, or a cached cell reference+titleText :: Element -> Text+titleText title =+  case child "tx" title of+    Just tx | Just rich <- child "rich" tx -> drawingMLText rich+            | otherwise -> T.unwords (cachedValues tx)+    Nothing -> ""++-- | Parse a series (@c:ser@). Its categories come from @c:cat@, or+-- @c:xVal@ in a scatter or bubble chart, and its values from @c:val@ or+-- @c:yVal@. A series without categories is numbered from 1, as in+-- PowerPoint.+parseSeries :: Int -> Element -> ChartSeries+parseSeries n ser =+  ChartSeries+    { seriesName = case child "tx" ser of+        Just tx -> T.unwords (cachedValues tx)+        Nothing -> "Series" <> T.pack (show n)+    , seriesPoints =+        [ (fromMaybe (T.pack (show (i + 1))) (M.lookup i categories), v)+        | i <- [0 .. count - 1]+        , let v = fromMaybe "" (M.lookup i values)+        ]+    }+  where+    points names = maybe (0, M.empty) cachedPoints $+      listToMaybe (mapMaybe (`child` ser) names)+    (catCount, categories) = points ["cat", "xVal"]+    (valCount, values) = points ["val", "yVal"]+    count = maximum [catCount, valCount, size values, size categories]+    size m = maybe 0 ((+ 1) . fst) (M.lookupMax m)++-- | The cached points of a data source (@c:cat@, @c:val@, ...): the+-- point count and the value of each point by index. In multi-level+-- categories only the innermost level is read.+cachedPoints :: Element -> (Int, M.Map Int Text)+cachedPoints source =+  case listToMaybe (mapMaybe cache (elChildren source)) of+    Just c ->+      ( fromMaybe 0 $ child "ptCount" c >>= findAttr (unqual "val") >>=+                        readMaybe . T.unpack+      , M.fromList+          [ (i, maybe "" strContent (child "v" pt))+          | pt <- children "pt" c+          , Just i <- [findAttr (unqual "idx") pt >>= readMaybe . T.unpack]+          ]+      )+    Nothing -> (0, M.empty)+  where+    cache el = case qName (elName el) of+      name | name `elem` ["strLit", "numLit"] -> Just el+           | name `elem` ["strRef", "numRef"] ->+               find ((`elem` ["strCache", "numCache"]) . qName . elName)+                    (elChildren el)+           | name == "multiLvlStrRef" ->+               child "multiLvlStrCache" el >>= child "lvl"+           | otherwise -> Nothing++-- | The cached values of a text source such as a series name (@c:tx@),+-- which is either a cell reference with its cache or a literal @c:v@+cachedValues :: Element -> [Text]+cachedValues tx =+  case child "v" tx of+    Just v -> [strContent v]+    Nothing -> M.elems (snd (cachedPoints tx))++-- | A chart as a table, in a @chart@ div with the chart types as its+-- @type@ attribute. Series that share their categories go in one table,+-- one column per series; otherwise each series gets its own table.+chartToBlocks :: Chart -> [Block]+chartToBlocks chart =+  [Div ("", ["chart"], [("type", T.unwords (chartTypes chart))])+       (map table groups)]+  where+    series = chartSeries chart+    categoriesOf = map fst . seriesPoints+    groups = case series of+      s:rest | all ((== categoriesOf s) . categoriesOf) rest -> [series]+      _ -> map (: []) series+    caption = if T.null (chartTitle chart)+                 then Caption Nothing []+                 else Caption Nothing [Plain (B.toList (B.text+                                               (chartTitle chart)))]+    table group =+      let cell t = Cell nullAttr AlignDefault (RowSpan 1) (ColSpan 1)+                     [Plain (B.toList (B.text t))]+          headerRow = Row nullAttr $ cell "" : map (cell . seriesName) group+          categories = maybe [] categoriesOf (listToMaybe group)+          values = transpose (map (map snd . seriesPoints) group)+          bodyRows = zipWith (\category vs -> Row nullAttr+                                              (map cell (category : vs)))+                             categories values+       in Table nullAttr caption+            (replicate (length group + 1) (AlignDefault, ColWidthDefault))+            (TableHead nullAttr [headerRow])+            [TableBody nullAttr 0 [] bodyRows]+            (TableFoot nullAttr [])++-- Chart elements are matched by local name, as the c prefix can vary.+child :: Text -> Element -> Maybe Element+child name = find ((== name) . qName . elName) . elChildren++children :: Text -> Element -> [Element]+children name = filter ((== name) . qName . elName) . elChildren
src/Text/Pandoc/Readers/OOXML/Shared.hs view
@@ -26,6 +26,9 @@   , findChildrenByName   , findElementByName   , findAttrByName+    -- * DrawingML text+  , drawingMLParagraphLines+  , drawingMLText   ) where  import qualified Data.Map as M@@ -93,3 +96,25 @@ findAttrByName ns pref name el =   let ns' = ns <> elemToNameSpaces el   in  findAttr (elemName ns' pref name) el++-- | The text of a DrawingML paragraph (@a:p@), one item per line: line+-- breaks (@a:br@) split it. The text of runs (@a:r@) and fields+-- (@a:fld@) is concatenated as written, without adding spaces between+-- them, since a run boundary can fall anywhere, even inside a word.+drawingMLParagraphLines :: Element -> [Text]+drawingMLParagraphLines = T.splitOn "\n" . T.concat . map piece . elChildren+  where+    piece el = case qName (elName el) of+      "r"   -> runText el+      "fld" -> runText el+      "br"  -> "\n"+      _     -> ""+    runText = T.concat . map strContent .+              filter ((== "t") . qName . elName) . elChildren++-- | The text of all DrawingML paragraphs (@a:p@) in an element, with a+-- space between paragraphs and in place of line breaks.+drawingMLText :: Element -> Text+drawingMLText = T.unwords . filter (not . T.null) . map T.strip .+                concatMap drawingMLParagraphLines .+                filterElementsName ((== "p") . qName)
src/Text/Pandoc/Readers/Pptx/Parse.hs view
@@ -26,7 +26,9 @@ import qualified Data.Text.Lazy.Encoding as TL import Data.Text (Text) import System.FilePath (splitFileName)+import qualified System.FilePath.Posix as Posix import Text.Pandoc.Readers.OOXML.Shared+import Text.Pandoc.Shared (collapseFilePath) import Text.Pandoc.XML.Light import Text.Read (readMaybe) @@ -46,6 +48,9 @@   , slidePath    :: FilePath   , slideElement :: Element     -- The parsed p:sld element   , slideRels    :: [(Text, Text)]  -- Slide relationships+  , slideNotes   :: Maybe Element   -- The parsed p:notes element, if any+  , slideLayout  :: Maybe Element   -- The slide's p:sldLayout+  , slideMaster  :: Maybe Element   -- Its layout's p:sldMaster   } deriving (Show)  -- | Presentation-level information from presentation.xml@@ -208,7 +213,53 @@   slideRelsPath <- getPresentationRelsPath archive slidePath'   slideRels' <- loadRelationships archive slideRelsPath -  return $ PptxSlide sid slidePath' slideElem slideRels'+  -- Follow the slide's own notesSlide relationship: notesSlideN.xml need+  -- not belong to slideN.xml. A missing or unreadable notes part means+  -- the slide has no notes; it never fails the conversion.+  let notesElem = do+        notesTarget <- findRelTargetByType archive slideRelsPath "notesSlide"+        either (const Nothing) Just $+          loadXMLFromArchive archive (resolveTarget slidePath' notesTarget)++  -- The slide's layout and master, which the text of its placeholders+  -- inherits from; either may be missing.+  let related source typeName = do+        t <- findRelTargetByType archive (relsPathOf source) typeName+        let path = resolveTarget source t+        el <- either (const Nothing) Just $ loadXMLFromArchive archive path+        return (path, el)+      layout = related slidePath' "slideLayout"+      master = layout >>= \(layoutPath, _) -> related layoutPath "slideMaster"++  return $ PptxSlide sid slidePath' slideElem slideRels' notesElem+                     (snd <$> layout) (snd <$> master)++-- | The path of a part's relationships+relsPathOf :: FilePath -> FilePath+relsPathOf path =+  let (dir, file) = splitFileName path+   in dir ++ "/_rels/" ++ file ++ ".rels"++-- | Find the target of the first relationship in a .rels part whose Type+-- ends with the given name, such as @notesSlide@.+findRelTargetByType :: Archive -> FilePath -> Text -> Maybe Text+findRelTargetByType archive relsPath typeName = do+  entry <- findEntryByPath relsPath archive+  relsElem <- either (const Nothing) Just $ parseXMLFromEntry entry+  rel <- find hasType (onlyElems $ elContent relsElem)+  findAttr (unqual "Target") rel+  where+    hasType el = maybe False (("/" <> typeName) `T.isSuffixOf`) $+                 findAttr (unqual "Type") el++-- | Resolve a relationship target to an archive path. A target starting+-- with @/@ is relative to the package root; any other target is relative+-- to the folder of the part that holds the relationship.+resolveTarget :: FilePath -> Text -> FilePath+resolveTarget source target =+  case T.unpack target of+    '/':path -> path+    path -> collapseFilePath (Posix.takeDirectory source Posix.</> path)  -- | Helper: Maybe a -> Either Text a maybeToEither :: Text -> Maybe a -> Either Text a
src/Text/Pandoc/Readers/Pptx/Shapes.hs view
@@ -14,40 +14,68 @@ module Text.Pandoc.Readers.Pptx.Shapes   ( PptxShape(..)   , PptxParagraph(..)+  , TableCell(..)   , BulletType(..)   , parseShapes   , parseShape+  , parseShapeWith+  , inheritedBullets   , shapeToBlocks+  , paragraphsToBlocks   , isTitlePlaceholder   , extractDrawingMLText   ) where  import Codec.Archive.Zip (Archive, findEntryByPath, fromEntry) import qualified Data.ByteString.Lazy as B+import Data.Char (isSpace) import Data.List (find, groupBy)-import Data.Maybe (mapMaybe)+import Data.Maybe (fromMaybe, listToMaybe, mapMaybe)+import qualified Data.Set as S import qualified Data.Text as T import Data.Text (Text) import Text.Read (readMaybe)+import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class.PandocMonad (PandocMonad) import qualified Text.Pandoc.Class.PandocMonad as P import Text.Pandoc.Definition import Text.Pandoc.Readers.OOXML.Shared+import Text.Pandoc.Readers.OOXML.Charts import Text.Pandoc.Readers.Pptx.SmartArt+import Text.Pandoc.Shared (collapseFilePath)+import Text.Pandoc.Walk (walk) import Text.Pandoc.XML.Light+import Text.TeXMath (writeTeX)+import Text.TeXMath.Readers.OMML (readOMML)+import qualified System.FilePath.Posix as Posix  -- | Paragraph with bullet/numbering information data PptxParagraph = PptxParagraph   { paraLevel   :: Int            -- Bullet level (0, 1, 2...)   , paraBullet  :: BulletType-  , paraText    :: Text+  , paraText    :: [Inline]   } deriving (Show) +-- | A table cell (@a:tc@): its text, its row and column span, and whether+-- another cell's span covers it from the left (@hMerge@) or from above+-- (@vMerge@)+data TableCell = TableCell+  { tcText    :: Text+  , tcRowSpan :: Int+  , tcColSpan :: Int+  , tcHMerge  :: Bool+  , tcVMerge  :: Bool+  } deriving (Show)++tcCovered :: TableCell -> Bool+tcCovered c = tcHMerge c || tcVMerge c+ -- | Bullet type data BulletType   = NoBullet   | Bullet                        -- Has bullet (character detected or implicit)   | WingdingsBullet              -- Detected via Wingdings symbol+  | Numbered ListAttributes      -- Automatic numbering (a:buAutoNum)   deriving (Show, Eq)  -- | Shape types in PPTX slides@@ -58,11 +86,12 @@       , picTitle  :: Text       , picAlt    :: Text       }-  | PptxTable [[Text]]                  -- Simple text cells for now+  | PptxTable [[TableCell]]             -- Rows of cells   | PptxDiagramRef       { dgmDataRelId   :: Text          -- Relationship to data.xml       , dgmLayoutRelId :: Text          -- Relationship to layout.xml       }+  | PptxChartRef Text Text              -- Relationship to the chart, URI   | PptxGraphic Text                    -- Placeholder for other graphics   deriving (Show) @@ -76,12 +105,20 @@  -- | Parse individual shape element parseShape :: NameSpaces -> Element -> Maybe PptxShape-parseShape ns el+parseShape = parseShapeWith (const Nothing)++-- | Parse a shape, given the bullet its paragraphs inherit at each level+-- (0 for the first) when they don't set one themselves+parseShapeWith :: (Int -> Maybe BulletType) -> NameSpaces -> Element+               -> Maybe PptxShape+parseShapeWith inherited ns el   -- Text box: <p:sp> with <p:txBody>+  -- A slide number placeholder only holds the number PowerPoint shows+  | isSlideNumberPlaceholder ns el = Nothing   | isElem ns "p" "sp" el =       case findChildByName ns "p" "txBody" el of         Just txBody ->-          let paras = parseParagraphs ns txBody+          let paras = parseParagraphs inherited ns txBody            in if null paras               then Nothing               else Just $ PptxTextBox paras@@ -133,34 +170,53 @@                                   (Just dataRelId, Just layoutRelId) ->                                     Just $ PptxDiagramRef dataRelId layoutRelId                                   _ -> Just $ PptxGraphic "diagram-missing-rels"+                  else if uri == chartUri+                    then+                      -- The c prefix is usually declared on c:chart itself+                      case find ((== "chart") . qName . elName)+                                (elChildren graphicData) of+                        Just chartElem+                          | Just relId <- findAttrByName+                              (ns <> elemToNameSpaces chartElem) "r" "id"+                              chartElem+                          -> Just $ PptxChartRef relId uri+                        _ -> Just $ PptxGraphic ("other: " <> uri)                   else-                    -- Other graphic (chart, etc.)+                    -- Other graphic                     Just $ PptxGraphic ("other: " <> uri)    -- Skip other shapes for now   | otherwise = Nothing  -- | Parse table rows (simple text extraction)-parseTableRows :: NameSpaces -> Element -> [[Text]]+parseTableRows :: NameSpaces -> Element -> [[TableCell]] parseTableRows ns tblElem =   let trElems = findChildrenByName ns "a" "tr" tblElem    in map (parseTableRow ns) trElems -parseTableRow :: NameSpaces -> Element -> [Text]+parseTableRow :: NameSpaces -> Element -> [TableCell] parseTableRow ns trElem =   let tcElems = findChildrenByName ns "a" "tc" trElem-   in map extractCellText tcElems+   in map parseCell tcElems   where-    extractCellText tcElem =-      -- Get text from txBody/a:p/a:r/a:t-      case findChildByName ns "a" "txBody" tcElem of-        Just txBody -> extractDrawingMLText txBody-        Nothing -> ""+    parseCell tcElem = TableCell+      { tcText = case findChildByName ns "a" "txBody" tcElem of+          -- Get text from txBody/a:p/a:r/a:t+          Just txBody -> extractDrawingMLText txBody+          Nothing -> ""+      , tcRowSpan = span' "rowSpan" tcElem+      , tcColSpan = span' "gridSpan" tcElem+      , tcHMerge = flag "hMerge" tcElem+      , tcVMerge = flag "vMerge" tcElem+      }+    span' name el = maybe 1 (max 1) $+      findAttr (unqual name) el >>= readMaybe . T.unpack+    flag name el = findAttr (unqual name) el `elem` [Just "1", Just "true"]  -- | Convert shape to Pandoc blocks shapeToBlocks :: PandocMonad m => Archive -> [(Text, Text)] -> PptxShape -> m [Block]-shapeToBlocks _archive _rels (PptxTextBox paras) =-  return $ paragraphsToBlocks paras+shapeToBlocks _archive rels (PptxTextBox paras) =+  return $ resolveLinks rels $ paragraphsToBlocks paras shapeToBlocks archive rels (PptxPicture relId title alt) = do   -- Resolve relationship to get media path   case lookup relId rels of@@ -178,13 +234,15 @@           return [Para [Image nullAttr altText (mediaPath, title)]]  shapeToBlocks _archive _rels (PptxTable rows) =-  -- Simple table representation for now-  case rows of+  case spannedRows rows of     [] -> return []     (headerRow:bodyRows) -> do-      let makeCell text = Cell nullAttr AlignDefault (RowSpan 1) (ColSpan 1) [Plain [Str text]]-          headerCells = map makeCell headerRow-          bodyCells = map (map makeCell) bodyRows+      let makeCell (TableCell text rs cs _ _) =+            Cell nullAttr AlignDefault (RowSpan rs) (ColSpan cs)+                 [Plain (B.toList (B.text text))]+          visible = map makeCell . filter (not . tcCovered)+          headerCells = visible headerRow+          bodyCells = map visible bodyRows           caption = Caption Nothing []           colSpec = replicate (length headerRow) (AlignDefault, ColWidthDefault)           headerRow' = Row nullAttr headerCells@@ -202,10 +260,46 @@       return [Para [Str $ "[Diagram parse error: " <> err <> "]"]]     Right diagram ->       return $ diagramToBlocks diagram+shapeToBlocks archive rels (PptxChartRef relId uri) =+  -- A chart whose part can't be read keeps the placeholder+  return $ either (const [Para [Str $ "[Graphic: other: " <> uri <> "]"]])+                  chartToBlocks $ do+    target <- maybe (Left $ "Relationship not found: " <> relId) Right $+              lookup relId rels+    readChart archive $ case T.unpack target of+      '/':path -> path+      path -> collapseFilePath ("ppt/slides" Posix.</> path) shapeToBlocks _archive _rels (PptxGraphic text) =   -- Placeholder for other graphics (charts, etc.)   return [Para [Str $ "[Graphic: " <> text <> "]"]] +-- | Make the spans of a table's cells fit pandoc's table model. Each row+-- has a cell for every grid column, with the cells covered by a span+-- marked. As a cell can't span the header row and the body, the header+-- row's spans are kept to that row, and the body cells they covered+-- become empty cells of their own. Row spans are clipped to the rows+-- there are.+spannedRows :: [[TableCell]] -> [[TableCell]]+spannedRows [] = []+spannedRows (header:body) =+  map (\c -> c{ tcRowSpan = 1 }) header : go headerSpanCols (length body) body+  where+    headerSpanCols = S.fromList+      [ col' | (col, c) <- zip [0 :: Int ..] header+             , tcRowSpan c > 1, not (tcCovered c)+             , col' <- [col .. col + tcColSpan c - 1] ]+    -- cols: the columns still covered by a span from the header row;+    -- left: the number of rows from this one to the last+    go _ _ [] = []+    go cols left (row:rows) =+      let cells = zip [0 ..] row+          fix (col, c)+            | col `S.member` cols && tcVMerge c =+                TableCell "" 1 1 False False+            | otherwise = c{ tcRowSpan = min (tcRowSpan c) left }+          stillCovered col = maybe False tcVMerge (lookup col cells)+       in map fix cells : go (S.filter stillCovered cols) (left - 1) rows+ -- | Resolve media path (handle relative paths) resolveMediaPath :: Text -> Text resolveMediaPath target =@@ -223,17 +317,19 @@     Nothing -> Nothing  -- | Parse paragraphs from text box-parseParagraphs :: NameSpaces -> Element -> [PptxParagraph]-parseParagraphs ns txBody =+parseParagraphs :: (Int -> Maybe BulletType) -> NameSpaces -> Element+                -> [PptxParagraph]+parseParagraphs inherited ns txBody =   let pElems = findChildrenByName ns "a" "p" txBody-   in map (parseParagraph ns) pElems+   in map (parseParagraph inherited ns) pElems  -- | Parse individual paragraph-parseParagraph :: NameSpaces -> Element -> PptxParagraph-parseParagraph ns pElem =+parseParagraph :: (Int -> Maybe BulletType) -> NameSpaces -> Element+               -> PptxParagraph+parseParagraph inherited ns pElem =   let level = parseBulletLevel ns pElem-      bullet = detectBulletType ns pElem-      text = extractParagraphText ns pElem+      bullet = detectBulletType (inherited level) ns pElem+      text = paragraphInlines pElem    in PptxParagraph level bullet text  -- | Parse bullet level from paragraph properties@@ -245,19 +341,85 @@     Just lvl -> lvl     Nothing -> 0  -- Default to level 0 --- | Detect bullet type-detectBulletType :: NameSpaces -> Element -> BulletType-detectBulletType ns pElem =-  -- Check for explicit <a:pPr><a:buChar>-  case findChildByName ns "a" "pPr" pElem >>=-       findChildByName ns "a" "buChar" of-    Just _buCharElem -> Bullet-    Nothing ->+-- | Detect bullet type: the paragraph's own, or else the one it inherits+detectBulletType :: Maybe BulletType -> NameSpaces -> Element -> BulletType+detectBulletType inherited ns pElem =+  case findChildByName ns "a" "pPr" pElem >>= explicitBullet of+    Just bullet -> bullet+    Nothing       -- Check for Wingdings symbol (common in PowerPoint)-      if hasWingdingsSymbol ns pElem-        then WingdingsBullet-        else NoBullet+      | hasWingdingsSymbol ns pElem -> WingdingsBullet+      | otherwise -> fromMaybe NoBullet inherited +-- | The bullet that paragraph properties (@a:pPr@ or @a:lvlNpPr@) set,+-- if they set one+explicitBullet :: Element -> Maybe BulletType+explicitBullet pPr = listToMaybe (mapMaybe bullet (elChildren pPr))+  where+    bullet el = case qName (elName el) of+      "buChar"    -> Just Bullet+      "buBlip"    -> Just Bullet+      "buNone"    -> Just NoBullet+      "buAutoNum" -> Just (Numbered (numbering el))+      _           -> Nothing+    numbering el =+      let scheme = fromMaybe "arabicPeriod" (findAttr (unqual "type") el)+          start = fromMaybe 1 $+                  findAttr (unqual "startAt") el >>= readMaybe . T.unpack+          style | "arabic" `T.isPrefixOf` scheme = Decimal+                | "alphaLc" `T.isPrefixOf` scheme = LowerAlpha+                | "alphaUc" `T.isPrefixOf` scheme = UpperAlpha+                | "romanLc" `T.isPrefixOf` scheme = LowerRoman+                | "romanUc" `T.isPrefixOf` scheme = UpperRoman+                | otherwise = DefaultStyle+          delim | "ParenBoth" `T.isSuffixOf` scheme = TwoParens+                | "ParenR" `T.isSuffixOf` scheme = OneParen+                | "Period" `T.isSuffixOf` scheme = Period+                | otherwise = DefaultDelim+       in (start, style, delim)++-- | The bullet a shape's paragraphs inherit at each level (0 for the+-- first), from the shape's own list style, then the matching placeholder+-- on the slide layout and on the slide master, then the master's text+-- style for that kind of placeholder (or for other text).+inheritedBullets :: Maybe Element  -- ^ Slide layout+                 -> Maybe Element  -- ^ Slide master+                 -> Element        -- ^ Shape+                 -> Int -> Maybe BulletType+inheritedBullets layout master shape level =+  listToMaybe $ mapMaybe (>>= explicitBullet) $+    [lstLevel shape] +++    case placeholder shape of+      Just (phType, phIdx) ->+        [ layout >>= findPlaceholder phType phIdx >>= lstLevel+        , master >>= findPlaceholder (masterType phType) Nothing >>= lstLevel+        , textStyle (styleFor phType) ]+      Nothing -> [textStyle "otherStyle"]+  where+    lvlName = "lvl" <> T.pack (show (level + 1)) <> "pPr"+    child name el = find ((== name) . qName . elName) (elChildren el)+    lstLevel sp = child "txBody" sp >>= child "lstStyle" >>= child lvlName+    textStyle name = master >>= child "txStyles" >>= child name >>=+                     child lvlName+    placeholder sp = do+      ph <- child "nvSpPr" sp >>= child "nvPr" >>= child "ph"+      return ( fromMaybe "obj" (findAttr (unqual "type") ph)+             , findAttr (unqual "idx") ph )+    -- A placeholder is matched by index if it has one, else by type+    findPlaceholder phType phIdx root = do+      tree <- child "cSld" root >>= child "spTree"+      let phs = [ (sp, p) | sp <- elChildren tree+                          , Just p <- [placeholder sp] ]+          byIdx = [ sp | Just i <- [phIdx], (sp, (_, j)) <- phs, j == Just i ]+          byType = [ sp | (sp, (t, _)) <- phs, masterType t == phType ||+                                              t == phType ]+      listToMaybe (byIdx ++ byType)+    masterType t = if t `elem` ["obj", "body"] then "body" else t+    styleFor t+      | t `elem` ["title", "ctrTitle"] = "titleStyle"+      | t `elem` ["obj", "body", "subTitle"] = "bodyStyle"+      | otherwise = "otherStyle"+ -- | Check if paragraph starts with Wingdings symbol hasWingdingsSymbol :: NameSpaces -> Element -> Bool hasWingdingsSymbol ns pElem =@@ -271,51 +433,163 @@                      Nothing -> False    in any checkRun runs --- | Extract text from paragraph-extractParagraphText :: NameSpaces -> Element -> Text-extractParagraphText _ns pElem =-  -- Find all <a:t> elements and concatenate-  let textElems = filterElementsName (\qn -> qName qn == "t") pElem-      texts = map strContent textElems-   in T.unwords $ filter (not . T.null) texts+-- | Inlines of a paragraph: the text of its runs and fields with their+-- bold, italic, superscript and subscript, its line breaks, its+-- hyperlinks, and its equations (@a14:m@) as math. A hyperlink's target+-- is a relationship id at this point, which 'resolveLinks' replaces.+paragraphInlines :: Element -> [Inline]+paragraphInlines pElem =+  B.toList $ foldMap linked $ groupBy (\a b -> fst a == fst b) $+    concatMap piece (elChildren pElem)+  where+    piece el = case qName (elName el) of+      "r"   -> [(runLink el, Left (runFormat el, runText el))]+      "fld" -> [(runLink el, Left (runFormat el, runText el))]+      "br"  -> [(Nothing, Right B.linebreak)]+      "m"   -> [(Nothing, Right (foldMap mathInlines (elChildren el)))]+      _     -> []+    linked grp =+      let inls = foldMap formatted $ groupBy sameFormat (map snd grp)+       in case grp of+            (Just relId, _) : _ ->+              B.linkWith ("", [], [(linkRelAttr, relId)]) "" "" inls+            _ -> inls+    sameFormat (Left (f, _)) (Left (g, _)) = f == g+    sameFormat _ _ = False+    -- Runs with the same format are joined before it is applied, so+    -- that adjacent bold runs make one strong span.+    formatted pieces@(Left (fmt, _) : _) =+      applyFormat fmt (T.concat [t | Left (_, t) <- pieces])+    formatted pieces = mconcat [ils | Right ils <- pieces] --- | Extract text from DrawingML element (finds all <a:t> descendants)+-- | The format of a run: bold, italic, and its baseline shift (positive+-- for superscript, negative for subscript)+type RunFormat = (Bool, Bool, Int)++runFormat :: Element -> RunFormat+runFormat run =+  case find ((== "rPr") . qName . elName) (elChildren run) of+    Nothing -> (False, False, 0)+    Just rPr ->+      ( flag "b" rPr, flag "i" rPr+      , fromMaybe 0 $+          findAttr (unqual "baseline") rPr >>= readMaybe . T.unpack )+  where+    flag name el = findAttr (unqual name) el `elem` [Just "1", Just "true"]++runText :: Element -> Text+runText = T.concat . map strContent .+          filter ((== "t") . qName . elName) . elChildren++-- | Text with a run format. Spaces at the edges stay outside the+-- formatting, as Markdown emphasis can't start or end with a space.+applyFormat :: RunFormat -> Text -> B.Inlines+applyFormat (bold, italic, baseline) txt+  | not (bold || italic || baseline /= 0) || T.null core = B.text txt+  | otherwise = edge leading <> wrap (B.text core) <> edge trailing+  where+    isBlank = (`elem` (" \t\r\n" :: String))+    core = T.dropAround isBlank txt+    leading = T.takeWhile isBlank txt+    trailing = T.takeWhileEnd isBlank txt+    edge t = if T.null t then mempty else B.space+    wrap = (if bold then B.strong else id) .+           (if italic then B.emph else id) .+           (if baseline > 0 then B.superscript+              else if baseline < 0 then B.subscript else id)++-- | The relationship id of a run's hyperlink (@a:hlinkClick@). Links+-- that act within the presentation, such as a jump to another slide,+-- carry an @action@ and are not kept.+runLink :: Element -> Maybe Text+runLink run = do+  rPr <- find ((== "rPr") . qName . elName) (elChildren run)+  link <- find ((== "hlinkClick") . qName . elName) (elChildren rPr)+  case findAttr (unqual "action") link of+    Just _ -> Nothing+    Nothing -> fmap attrVal $ find isRelId (elAttribs link)+  where+    isRelId a = qName (attrKey a) == "id" && qURI (attrKey a) == Just relNs+    relNs =+      "http://schemas.openxmlformats.org/officeDocument/2006/relationships"++-- | The attribute that marks a hyperlink whose target is still a+-- relationship id+linkRelAttr :: Text+linkRelAttr = "pptx-relationship"++-- | Replace the relationship id of each hyperlink read by+-- 'paragraphInlines' with the relationship's target. A link whose+-- relationship is missing is replaced by its text.+resolveLinks :: [(Text, Text)] -> [Block] -> [Block]+resolveLinks rels = walk resolve+  where+    resolve (Link (_, _, [(key, relId)]) inls _)+      | key == linkRelAttr =+          case lookup relId rels of+            Just target -> Link nullAttr inls (target, "")+            Nothing -> Span nullAttr inls+    resolve x = x++-- | An equation (@m:oMathPara@ or @m:oMath@) as math. If texmath can't+-- read it, its text is kept.+mathInlines :: Element -> B.Inlines+mathInlines el =+  case readOMML (showElement el) of+    Right exps+      | qName (elName el) == "oMathPara" -> B.displayMath (writeTeX exps)+      | otherwise -> B.math (writeTeX exps)+    Left _ -> B.text $ T.concat $ map strContent $+                filterElementsName ((== "t") . qName) el++-- | Extract text from DrawingML element (all its paragraphs) extractDrawingMLText :: Element -> Text-extractDrawingMLText el =-  let textElems = filterElementsName (\qn -> qName qn == "t") el-      texts = map strContent textElems-   in T.unwords $ filter (not . T.null) texts+extractDrawingMLText = drawingMLText --- | Convert paragraphs to blocks, grouping bullets into lists+-- | Convert paragraphs to blocks, grouping bulleted and numbered+-- paragraphs into lists. Paragraphs at a deeper level than a list item+-- go inside that item, as a nested list or as further paragraphs. paragraphsToBlocks :: [PptxParagraph] -> [Block]-paragraphsToBlocks paras =-  -- If we have multiple paragraphs with bullets, group them-  let hasBullets = any (\p -> paraBullet p /= NoBullet) paras-   in if hasBullets-      then groupBulletParagraphs paras-      else map (\p -> Para [Str $ paraText p]) paras---- | Group bullet paragraphs into lists-groupBulletParagraphs :: [PptxParagraph] -> [Block]-groupBulletParagraphs paras =-  let grouped = groupBy sameBulletLevel paras-   in concatMap groupToBlock grouped+paragraphsToBlocks [] = []+paragraphsToBlocks (p:ps)+  | paraBullet p == NoBullet = Para (paraText p) : paragraphsToBlocks ps+  -- PowerPoint shows no bullet or number on an empty paragraph+  | isEmpty p = paragraphsToBlocks ps+  | otherwise =+      let (items, rest) = listItems p (p:ps)+       in listBlock (paraBullet p) items : paragraphsToBlocks rest   where-    sameBulletLevel p1 p2 =-      (paraBullet p1 /= NoBullet) &&-      (paraBullet p2 /= NoBullet) &&-      (paraLevel p1 == paraLevel p2)+    -- The items of a list that starts with the paragraph first, with the+    -- deeper paragraphs that follow each item+    listItems first (q:qs)+      | isEmpty q, paraBullet q /= NoBullet = listItems first qs+      | paraLevel q == paraLevel first, sameKind q first =+          let (inner, rest) = span ((> paraLevel first) . paraLevel) qs+              (items, rest') = listItems first rest+           in ((Plain (paraText q) : paragraphsToBlocks inner) : items, rest')+    listItems _ qs = ([], qs)+    sameKind a b = case (paraBullet a, paraBullet b) of+      (Numbered _, Numbered _) -> True+      (Numbered _, _) -> False+      (_, Numbered _) -> False+      (x, y) -> (x == NoBullet) == (y == NoBullet)+    listBlock (Numbered attrs) items = OrderedList attrs items+    listBlock _ items = BulletList items+    isEmpty = all blank . paraText+    blank il = case il of+      Space -> True+      SoftBreak -> True+      LineBreak -> True+      Str t -> T.all isSpace t+      _ -> False -    groupToBlock :: [PptxParagraph] -> [Block]-    groupToBlock [] = []-    groupToBlock ps@(p:_)-      | paraBullet p /= NoBullet =-          -- Bullet list-          let items = map (\para -> [Plain [Str $ paraText para]]) ps-           in [BulletList items]-      | otherwise =-          -- Plain paragraph-          map (\para -> Para [Str $ paraText para]) ps+-- | Check if shape is a slide number placeholder+isSlideNumberPlaceholder :: NameSpaces -> Element -> Bool+isSlideNumberPlaceholder ns el =+  maybe False ((== Just "sldNum") . findAttr (unqual "type")) $+    findChildByName ns "p" "nvSpPr" el >>=+    findChildByName ns "p" "nvPr" >>=+    findChildByName ns "p" "ph"  -- | Check if shape is title placeholder (also used in Slides module) isTitlePlaceholder :: NameSpaces -> Element -> Bool@@ -328,3 +602,7 @@         Just phType -> phType == "title" || phType == "ctrTitle"         Nothing -> False     Nothing -> False++-- | The graphic data URI of a chart+chartUri :: Text+chartUri = "http://schemas.openxmlformats.org/drawingml/2006/chart"
src/Text/Pandoc/Readers/Pptx/Slides.hs view
@@ -15,16 +15,20 @@   ) where  import Codec.Archive.Zip (Archive)+import Control.Applicative ((<|>)) import Data.List (find)-import Data.Maybe (mapMaybe)+import qualified Data.Map as M+import Data.Maybe (fromMaybe, mapMaybe) import qualified Data.Text as T import Data.Text (Text)+import qualified Text.Pandoc.Builder as B import Text.Pandoc.Class.PandocMonad (PandocMonad) import Text.Pandoc.Definition import Text.Pandoc.Options (ReaderOptions) import Text.Pandoc.Readers.OOXML.Shared import Text.Pandoc.Readers.Pptx.Parse import Text.Pandoc.Readers.Pptx.Shapes+import Text.Pandoc.Shared (stringify) import Text.Pandoc.XML.Light  -- | Convert Pptx intermediate representation to Pandoc AST@@ -42,7 +46,7 @@ slideToBlocks :: PandocMonad m => Archive -> PptxSlide -> m [Block] slideToBlocks archive slide = do   let SlideId n = slideId slide-      slideElem = slideElement slide+      slideElem = resolveAlternateContent (slideElement slide)       rels = slideRels slide       ns = elemToNameSpaces slideElem @@ -54,20 +58,61 @@       headerText = if T.null title                    then "Slide " <> T.pack (show n)                    else title-      header = Header 2 (slideIdent, [], []) [Str headerText]+      -- A slide hidden from the slide show (show="0") is still read,+      -- with a "hidden" class on its header.+      classes = ["hidden" | findAttr (unqual "show") slideElem == Just "0"]+      header = Header 2 (slideIdent, classes, [])+                 (B.toList (B.text headerText))    -- Parse shapes and convert to blocks   case findChildByName ns "p" "cSld" slideElem >>=        findChildByName ns "p" "spTree" of-    Nothing -> return [header]+    Nothing -> return $ header : notesBlocks     Just spTree -> do       -- Filter out title placeholder shapes before parsing-      let allShapeElems = onlyElems $ elContent spTree+      let allShapeElems = ungroupShapes ns (onlyElems $ elContent spTree)           nonTitleShapeElems = filter (not . isTitlePlaceholder ns) allShapeElems-          shapes = mapMaybe (parseShape ns) nonTitleShapeElems+          inherited = inheritedBullets (slideLayout slide) (slideMaster slide)+          shapes = mapMaybe (\el -> parseShapeWith (inherited el) ns el)+                            nonTitleShapeElems       shapeBlocks <- concat <$> mapM (shapeToBlocks archive rels) shapes-      return $ header : shapeBlocks+      return $ header : shapeBlocks ++ notesBlocks+  where+    notesBlocks = maybe [] notesToBlocks (slideNotes slide) +-- | Convert a notes slide (@p:notes@) to a @notes@ div, the form the pptx+-- writer turns into speaker notes. The notes text is in the shape holding+-- the body placeholder; the slide image and slide number placeholders are+-- ignored. Notes without text yield nothing.+notesToBlocks :: Element -> [Block]+notesToBlocks notesElem =+  case filter isBody shapeElems >>= textBoxBlocks of+    blocks | T.null (T.strip (stringify blocks)) -> []+           | otherwise -> [Div ("", ["notes"], []) blocks]+  where+    ns = elemToNameSpaces notesElem+    shapeElems = maybe [] elChildren $+      findChildByName ns "p" "cSld" notesElem >>=+      findChildByName ns "p" "spTree"+    isBody el =+      case findChildByName ns "p" "nvSpPr" el >>=+           findChildByName ns "p" "nvPr" >>=+           findChildByName ns "p" "ph" of+        Just ph -> findAttr (unqual "type") ph == Just "body"+        Nothing -> False+    textBoxBlocks el = case parseShape ns el of+      Just (PptxTextBox paras) -> paragraphsToBlocks paras+      _ -> []++-- | Replace each group shape (@p:grpSp@, possibly nested) by the shapes+-- it contains, keeping document order.+ungroupShapes :: NameSpaces -> [Element] -> [Element]+ungroupShapes ns = concatMap ungroup+  where+    ungroup el+      | isElem ns "p" "grpSp" el = ungroupShapes ns (elChildren el)+      | otherwise = [el]+ -- | Extract title from title placeholder extractSlideTitle :: NameSpaces -> Element -> Text extractSlideTitle ns slideElem =@@ -81,3 +126,38 @@        in maybe "" extractDrawingMLText titleShape  -- isTitlePlaceholder is imported from Shapes module++-- | Replace each markup-compatibility block (@mc:AlternateContent@) by+-- the content of its first @mc:Choice@ whose required namespaces the+-- reader understands, or else by its @mc:Fallback@. PowerPoint and the+-- pptx writer put the shapes holding equations (@a14:m@) in such blocks.+resolveAlternateContent :: Element -> Element+resolveAlternateContent = resolve mempty+  where+    resolve ns el =+      let ns' = elemToNameSpaces el <> ns+       in el{ elContent = concatMap (content ns') (elContent el) }+    content ns (Elem el)+      | isMce "AlternateContent" el =+          let ns' = elemToNameSpaces el <> ns+              branches = elChildren el+              chosen = find (understood ns') branches <|>+                       find (isMce "Fallback") branches+           in case chosen of+                Just branch ->+                  concatMap (content (elemToNameSpaces branch <> ns'))+                            (elContent branch)+                Nothing -> []+      | otherwise = [Elem (resolve ns el)]+    content _ c = [c]+    understood ns branch =+      isMce "Choice" branch &&+      all (\prefix -> M.lookup prefix (elemToNameSpaces branch <> ns)+                         `elem` map Just understoodNamespaces)+          (T.words $ fromMaybe "" $ findAttr (unqual "Requires") branch)+    isMce name el =+      qName (elName el) == name &&+      qURI (elName el) ==+        Just "http://schemas.openxmlformats.org/markup-compatibility/2006"+    understoodNamespaces =+      [ "http://schemas.microsoft.com/office/drawing/2010/main" ] -- a14
src/Text/Pandoc/Readers/Pptx/SmartArt.hs view
@@ -22,6 +22,7 @@ import qualified Data.Text as T import qualified Data.Text.Lazy.Encoding as TL import Data.Text (Text)+import qualified Text.Pandoc.Builder as B import Text.Pandoc.Definition import Text.Pandoc.Readers.OOXML.Shared import Text.Pandoc.XML.Light@@ -136,8 +137,7 @@   -- Extract text from dgm:t element (which contains a:p/a:r/a:t)   let text = case findChildByName ns "dgm" "t" ptElem of         Just tElem ->-          -- Recursively get ALL text content from all descendants-          getAllText tElem+          drawingMLText tElem         Nothing -> ""    -- Only return nodes with actual text@@ -200,19 +200,12 @@ nodeToBlocks :: (Text, [Text]) -> [Block] nodeToBlocks (nodeText, childTexts) =   if null childTexts-    then [Para [Strong [Str nodeText]]]-    else [ Para [Strong [Str nodeText]]-         , BulletList [[Plain [Str child]] | child <- childTexts]+    then [Para [Strong (inlines nodeText)]]+    else [ Para [Strong (inlines nodeText)]+         , BulletList [[Plain (inlines child)] | child <- childTexts]          ]---- | Recursively extract all text from an element and its descendants-getAllText :: Element -> Text-getAllText el =-  let textFromContent (Text cdata) = cdData cdata-      textFromContent (Elem e) = getAllText e-      textFromContent _ = ""-      texts = map textFromContent (elContent el)-   in T.unwords $ filter (not . T.null) texts+  where+    inlines = B.toList . B.text  -- Helper functions maybeToEither :: Text -> Maybe a -> Either Text a
src/Text/Pandoc/Readers/RST.hs view
@@ -393,6 +393,7 @@   bodyElements <- do     raw <- option "" indentedBlock     parseFromString' parseBlocks $ (rawfirst <> raw) <> "\n\n"+  guard (not (null bodyElements))   optional blanklines   pure (B.code opts, [bodyElements]) @@ -554,25 +555,22 @@ -- code blocks -- --- read a line indented by a given string-indentedLine :: (HasReaderOptions st, Monad m)-             => Int -> ParsecT Sources st m Text-indentedLine indents = try $ do-  lookAhead spaceChar-  gobbleAtMostSpaces indents-  anyLine- -- one or more indented lines, possibly separated by blank lines.--- any amount of indentation will work.+-- any amount of indentation will work; the minimum indentation+-- is stripped from all lines (#12071). indentedBlock :: (HasReaderOptions st, Monad m)               => ParsecT Sources st m Text indentedBlock = try $ do-  indents <- length <$> lookAhead (many1 spaceChar)+  lookAhead spaceChar   lns <- many1 $ try $ do b <- option "" blanklines-                          l <- indentedLine indents-                          return (b <> l)+                          lookAhead spaceChar+                          n <- gobbleAtMostSpaces maxBound+                          l <- anyLine+                          return (b, n, l)   optional blanklines-  return $ T.unlines lns+  let minIndent = minimum [n | (_, n, _) <- lns]+  return $ T.unlines+    [b <> T.replicate (n - minIndent) " " <> l | (b, n, l) <- lns]  quotedBlock :: Monad m => ParsecT Sources st m Text quotedBlock = try $ do
src/Text/Pandoc/Readers/Typst.hs view
@@ -752,6 +752,7 @@     VInteger x -> pure $ replicate (fromIntegral x) B.ColWidthDefault     VArray x -> pure $ normalizeWidths $ map toWidth (V.toList x)     VNone -> pure [B.ColWidthDefault]+    VAuto -> pure [B.ColWidthDefault]     _ -> fail $ "Could not determine number of columns: " <> show columns   let numcols = length widths   align <- getField "align" fields
src/Text/Pandoc/Readers/Vimwiki.hs view
@@ -371,7 +371,7 @@ listTodoMarker :: PandocMonad m => VwParser m Inlines listTodoMarker = try $ do   x <- (many spaceChar >> char '[')-       *> oneOf " .oOX"+       *> oneOf " .oOX-"        <* (char ']' >> spaceChar)   return $ makeListMarkerSpan x @@ -383,6 +383,7 @@             'o' -> "done2"             'O' -> "done3"             'X' -> "done4"+            '-' -> "rejected"             _   -> ""     in       B.spanWith ("", [cl], []) mempty
src/Text/Pandoc/Readers/Xlsx/Cells.hs view
@@ -32,6 +32,7 @@ data CellValue   = TextValue Text   | NumberValue Double+  | BoolValue Bool   | EmptyValue   deriving (Show, Eq) 
src/Text/Pandoc/Readers/Xlsx/Parse.hs view
@@ -22,16 +22,22 @@   ) where  import Codec.Archive.Zip (Archive, Entry, findEntryByPath, fromEntry)+import Control.Monad (join)+import Data.Char (toLower) import Data.List (find) import qualified Data.Map.Strict as M import Data.Maybe (mapMaybe, fromMaybe) import qualified Data.Text as T import qualified Data.Text.Lazy.Encoding as TL import Data.Text (Text)+import Data.Time.Calendar (Day, addDays, fromGregorian, showGregorian) import qualified Data.Vector as V import System.FilePath (splitFileName)+import qualified System.FilePath.Posix as Posix+import Text.Pandoc.Readers.OOXML.Charts (Chart, readChart) import Text.Pandoc.Readers.OOXML.Shared import Text.Pandoc.Readers.Xlsx.Cells+import Text.Pandoc.Shared (collapseFilePath) import Text.Pandoc.XML.Light import Text.Read (readMaybe) @@ -51,8 +57,19 @@ -- | Style information data Styles = Styles   { styleFonts :: V.Vector FontInfo+  , styleCellFonts :: V.Vector Int+    -- ^ For each cell format (@cellXfs@), the index of its font+  , styleDateFormats :: V.Vector (Maybe DateFormat)+    -- ^ For each cell format (@cellXfs@), what it shows of a date   } deriving (Show) +-- | What a date or time number format shows+data DateFormat = DateFormat+  { dateShowsDate :: Bool+  , dateShowsTime :: Bool+  , dateShowsSeconds :: Bool+  } deriving (Show)+ -- | Complete XLSX document data Xlsx = Xlsx   { xlsxWorkbook :: XlsxWorkbook@@ -64,6 +81,7 @@ -- | Workbook information data XlsxWorkbook = XlsxWorkbook   { workbookSheetNames :: [(SheetId, Text, Text)]  -- (id, name, relId)+  , workbookDate1904 :: Bool  -- ^ Dates count from 1904 rather than 1900   } deriving (Show)  -- | Individual worksheet@@ -71,6 +89,9 @@   { sheetId :: SheetId   , sheetName :: Text   , sheetCells :: M.Map CellRef XlsxCell+  , sheetMerges :: [(CellRef, CellRef)]+    -- ^ Merged ranges, as their top-left and bottom-right cells+  , sheetCharts :: [Chart]  -- ^ In the order they are drawn   } deriving (Show)  -- | Parse XLSX archive@@ -88,21 +109,20 @@   -- Parse shared strings (look for sharedStrings relationship)   sharedStrings <- case findRelWithTarget workbookRels "sharedStrings" of     Just (_, target) -> do-      let path = "xl/" ++ T.unpack target-      el <- loadXMLFromArchive archive path+      el <- loadXMLFromArchive archive (resolveTarget workbookPath target)       parseSharedStrings el     Nothing -> Right V.empty    -- Parse styles   styles <- case findRelWithTarget workbookRels "styles" of     Just (_, target) -> do-      let path = "xl/" ++ T.unpack target-      el <- loadXMLFromArchive archive path+      el <- loadXMLFromArchive archive (resolveTarget workbookPath target)       parseStyles el-    Nothing -> Right $ Styles V.empty+    Nothing -> Right $ Styles V.empty V.empty V.empty    -- Parse worksheets-  sheets <- mapM (\sheetInfo -> parseSheet archive workbookRels sharedStrings styles sheetInfo)+  sheets <- mapM (parseSheet archive workbookPath workbookRels sharedStrings+                    styles (workbookDate1904 workbook))                  (workbookSheetNames workbook)    return $ Xlsx workbook sheets sharedStrings styles@@ -119,7 +139,8 @@     Nothing -> Left "No workbook.xml relationship found"     Just rel -> do       target <- maybeToEither "Missing Target" $ findAttr (unqual "Target") rel-      return $ T.unpack target+      -- The root relationships belong to the package itself.+      return $ resolveTarget "" target   where     isOfficeDocRel el =       case (findAttr (unqual "Type") el, findAttr (unqual "Target") el) of@@ -139,8 +160,12 @@   let sheetElems = filter (\e -> qName (elName e) == "sheet") (onlyElems $ elContent sheets)   sheetRefs <- mapM (parseSheetRef ns) (zip [1..] sheetElems) -  return $ XlsxWorkbook sheetRefs+  let date1904 = maybe False (`elem` ["1", "true"]) $+        find (\e -> qName (elName e) == "workbookPr") (elChildren wbElem)+          >>= findAttr (unqual "date1904") +  return $ XlsxWorkbook sheetRefs date1904+ parseSheetRef :: NameSpaces -> (Int, Element) -> Either Text (SheetId, Text, Text) parseSheetRef ns (idx, sheetElem) = do   let name = fromMaybe ("Sheet" <> T.pack (show idx)) $@@ -153,14 +178,22 @@ parseSharedStrings :: Element -> Either Text SharedStrings parseSharedStrings sstElem = do   let siElems = filter (\e -> qName (elName e) == "si") (onlyElems $ elContent sstElem)-      strings = map extractString siElems+      strings = map stringItemText siElems   return $ V.fromList strings-  where-    extractString siElem =-      case find (\e -> qName (elName e) == "t") (onlyElems $ elContent siElem) of-        Just tElem -> strContent tElem-        Nothing -> getAllText siElem +-- | Text of a string item (@si@ in the shared strings, @is@ in an inline+-- string cell): its @t@, or the @t@ of its rich-text runs (@r@)+-- concatenated as written. Phonetic runs (@rPh@) are skipped.+stringItemText :: Element -> Text+stringItemText el = T.concat+  [ strContent t+  | child <- elChildren el+  , t <- case qName (elName child) of+           "t" -> [child]+           "r" -> filter (\e -> qName (elName e) == "t") (elChildren child)+           _   -> []+  ]+ -- | Parse styles (fonts only for MVP) parseStyles :: Element -> Either Text Styles parseStyles stylesElem = do@@ -169,8 +202,108 @@       fontElems = maybe [] (\fe -> filter (\e -> qName (elName e) == "font") (onlyElems $ elContent fe)) fontsElem       fonts = V.fromList $ map (parseFont mempty) fontElems -  return $ Styles fonts+      -- A cell's style index (@s@) refers to a cell format in <cellXfs>,+      -- which refers to its font by @fontId@ and its number format by+      -- @numFmtId@.+      children name el = filter (\e -> qName (elName e) == name) (elChildren el)+      cellFormats =+        [ xf+        | cellXfs <- children "cellXfs" stylesElem+        , xf <- children "xf" cellXfs+        ]+      cellFonts = V.fromList+        [ fromMaybe 0 $ findAttr (unqual "fontId") xf >>= readMaybe . T.unpack+        | xf <- cellFormats+        ] +      -- Number formats: custom ones from <numFmts>, by id+      customFormats = M.fromList+        [ (fmtId, code)+        | numFmts <- children "numFmts" stylesElem+        , numFmt <- children "numFmt" numFmts+        , Just fmtId <- [findAttr (unqual "numFmtId") numFmt >>=+                           readMaybe . T.unpack]+        , Just code <- [findAttr (unqual "formatCode") numFmt]+        ]+      xfDateFormat xf = do+        fmtId <- findAttr (unqual "numFmtId") xf >>= readMaybe . T.unpack+        maybe (builtinDateFormat fmtId) customDateFormat+          (M.lookup fmtId customFormats)+      dateFormats = V.fromList $ map xfDateFormat cellFormats++  return $ Styles fonts cellFonts dateFormats++-- | The built-in number formats that show dates or times. Format 46,+-- @[h]:mm:ss@, shows a duration rather than a time of day.+builtinDateFormat :: Int -> Maybe DateFormat+builtinDateFormat fmtId+  | fmtId >= 14 && fmtId <= 17 = Just $ DateFormat True False False+  | fmtId == 18 || fmtId == 20 = Just $ DateFormat False True False+  | fmtId == 19 || fmtId == 21 || fmtId == 45 || fmtId == 47+                               = Just $ DateFormat False True True+  | fmtId == 22                = Just $ DateFormat True True False+  | otherwise                  = Nothing++-- | Whether a custom format code shows a date or a time: whether its+-- first section has a date or time token (y, m, d, h, s) outside quoted+-- text, escaped characters and brackets. A bracketed elapsed-time token+-- such as @[h]@ makes it a duration, which isn't treated as a date.+customDateFormat :: Text -> Maybe DateFormat+customDateFormat code+  | elapsed = Nothing+  | showsDate || showsTime = Just $ DateFormat showsDate showsTime hasS+  | otherwise = Nothing+  where+    (tokens, brackets) = scan (T.unpack (T.takeWhile (/= ';') code)) [] []+    has c = c `elem` tokens+    hasS = has 's'+    showsTime = has 'h' || hasS+    showsDate = has 'y' || has 'd' || (has 'm' && not showsTime)+    elapsed = any (\b -> not (null b) && all (`elem` ("hms" :: String)) b)+                  brackets+    -- Collect the letters outside quotes, escapes and brackets, and the+    -- contents of brackets.+    scan [] ts bs = (ts, bs)+    scan ('"':cs) ts bs = scan (drop 1 (dropWhile (/= '"') cs)) ts bs+    scan ('\\':_:cs) ts bs = scan cs ts bs+    scan ('_':_:cs) ts bs = scan cs ts bs+    scan ('*':_:cs) ts bs = scan cs ts bs+    scan ('[':cs) ts bs =+      let (inside, rest) = break (== ']') cs+       in scan (drop 1 rest) ts (map toLower inside : bs)+    scan (c:cs) ts bs = scan cs (toLower c : ts) bs++-- | Render a date serial number under a date or time format, as an ISO+-- 8601 date (@2023-11-01@), time (@14:30@ or @14:30:05@), or both joined+-- by a space. Nothing if the number isn't a date the format can show:+-- negative, or before the first day of the date system for a format+-- that shows the date.+formatDate :: Bool -> DateFormat -> Double -> Maybe Text+formatDate date1904 fmt serial+  | serial < 0 = Nothing+  | otherwise = do+      dateText <- if dateShowsDate fmt+                    then (\d -> [showGregorian d]) <$> day+                    else Just []+      let timeText = [ showTime | dateShowsTime fmt ]+      return $ T.pack $ unwords (dateText ++ timeText)+  where+    seconds = round (serial * 86400) :: Integer+    (days, secs) = seconds `divMod` 86400+    day :: Maybe Day+    day+      | date1904 = Just $ addDays days (fromGregorian 1904 1 1)+      -- 1900 system: day 1 is 1900-01-01, and day 60 is the 29 February+      -- 1900 that Excel counts but that didn't exist.+      | days < 1 || days == 60 = Nothing+      | days < 60 = Just $ addDays days (fromGregorian 1899 12 31)+      | otherwise = Just $ addDays days (fromGregorian 1899 12 30)+    (h, rest) = secs `divMod` 3600+    (m, s) = rest `divMod` 60+    pad n = (if n < 10 then ('0' :) else id) (show n)+    showTime = pad h ++ ":" ++ pad m +++               (if dateShowsSeconds fmt then ":" ++ pad s else "")+ parseFont :: NameSpaces -> Element -> FontInfo parseFont _ns fontElem =   FontInfo@@ -180,33 +313,86 @@     }  -- | Parse individual worksheet-parseSheet :: Archive -> [(Text, Text)] -> SharedStrings -> Styles -> (SheetId, Text, Text) -> Either Text XlsxSheet-parseSheet archive rels sharedStrings styles (sid, name, relId) = do+parseSheet :: Archive -> FilePath -> [(Text, Text)] -> SharedStrings -> Styles+           -> Bool -> (SheetId, Text, Text) -> Either Text XlsxSheet+parseSheet archive wbPath rels sharedStrings styles date1904+           (sid, name, relId) = do   target <- maybeToEither ("Sheet relationship not found: " <> relId) $             lookup relId rels -  let sheetPath = "xl/" ++ T.unpack target+  let sheetPath = resolveTarget wbPath target   sheetElem <- loadXMLFromArchive archive sheetPath -  cells <- parseSheetCells sheetElem sharedStrings styles+  cells <- parseSheetCells sheetElem sharedStrings styles date1904 -  return $ XlsxSheet sid name cells+  let merges =+        [ (from, to)+        | mergeCells <- filter (\e -> qName (elName e) == "mergeCells")+                               (elChildren sheetElem)+        , mergeCell <- filter (\e -> qName (elName e) == "mergeCell")+                              (elChildren mergeCells)+        , Just ref <- [findAttr (unqual "ref") mergeCell]+        , (fromText, toText) <- [T.breakOn ":" ref]+        , Right from <- [parseCellRef fromText]+        , Right to <- [parseCellRef (T.drop 1 toText)]+        ] +  return $ XlsxSheet sid name cells merges+           (sheetChartsOf archive sheetPath)++-- | The charts on a worksheet or chart sheet: those its drawing refers+-- to, in document order. A drawing or chart that can't be read is left+-- out.+sheetChartsOf :: Archive -> FilePath -> [Chart]+sheetChartsOf archive sheetPath = fromMaybe [] $ do+  drawingTarget <- findRelTargetByType archive (relsPathFor sheetPath)+                     "drawing"+  let drawingPath = resolveTarget sheetPath drawingTarget+  drawing <- either (const Nothing) Just $+             loadXMLFromArchive archive drawingPath+  drawingRels <- either (const Nothing) Just $+                 loadRelationships archive (relsPathFor drawingPath)+  return+    [ chart+    | chartElem <- filterElementsName ((== "chart") . qName) drawing+    , Just relId <- [lookupRelId chartElem]+    , Just chartTarget <- [lookup relId drawingRels]+    , Right chart <- [readChart archive+                        (resolveTarget drawingPath chartTarget)]+    ]+  where+    lookupRelId el = attrVal <$> find isRelId (elAttribs el)+    isRelId a = qName (attrKey a) == "id" && qURI (attrKey a) == Just+      "http://schemas.openxmlformats.org/officeDocument/2006/relationships"++-- | Find the target of the first relationship in a .rels part whose Type+-- ends with the given name, such as @drawing@+findRelTargetByType :: Archive -> FilePath -> Text -> Maybe Text+findRelTargetByType archive relsPath typeName = do+  entry <- findEntryByPath relsPath archive+  relsElem <- either (const Nothing) Just $ parseXMLFromEntry entry+  rel <- find hasType (onlyElems $ elContent relsElem)+  findAttr (unqual "Target") rel+  where+    hasType el = maybe False (("/" <> typeName) `T.isSuffixOf`) $+                 findAttr (unqual "Type") el+ -- | Parse sheet cells-parseSheetCells :: Element -> SharedStrings -> Styles -> Either Text (M.Map CellRef XlsxCell)-parseSheetCells sheetElem sharedStrings styles = do+parseSheetCells :: Element -> SharedStrings -> Styles -> Bool+                -> Either Text (M.Map CellRef XlsxCell)+parseSheetCells sheetElem sharedStrings styles date1904 = do   -- Find sheetData by local name   case find (\e -> qName (elName e) == "sheetData") (onlyElems $ elContent sheetElem) of     Nothing -> return M.empty     Just sheetData -> do       let rowElems = filter (\e -> qName (elName e) == "row") (onlyElems $ elContent sheetData)           cellElems = concatMap (\r -> filter (\e -> qName (elName e) == "c") (onlyElems $ elContent r)) rowElems-          cells = mapMaybe (parseCell sharedStrings styles) cellElems+          cells = mapMaybe (parseCell sharedStrings styles date1904) cellElems       return $ M.fromList [(cellRef c, c) | c <- cells]  -- | Parse individual cell-parseCell :: SharedStrings -> Styles -> Element -> Maybe XlsxCell-parseCell sharedStrings styles cElem = do+parseCell :: SharedStrings -> Styles -> Bool -> Element -> Maybe XlsxCell+parseCell sharedStrings styles date1904 cElem = do   -- Get cell reference   refText <- findAttr (unqual "r") cElem   cellRefParsed <- either (const Nothing) Just $ parseCellRef refText@@ -227,20 +413,32 @@                   Just idx | idx >= 0 && idx < V.length sharedStrings ->                     TextValue (sharedStrings V.! idx)                   _ -> EmptyValue+              else if cellType == "inlineStr"+                then+                  -- Inline string, held in the cell itself rather than in <v>+                  maybe EmptyValue (TextValue . stringItemText) $+                    find (\e -> qName (elName e) == "is") (elChildren cElem)               else if T.null vText                 then EmptyValue+              else if cellType == "b"+                then BoolValue (vText == "1")                 else                   -- Number                   case readMaybe (T.unpack vText) of-                    Just n -> NumberValue n+                    Just n+                      | Just txt <- dateFormat >>= \f -> formatDate date1904 f n+                      -> TextValue txt+                      | otherwise -> NumberValue n                     Nothing -> TextValue vText+      dateFormat = join $ styleIdx >>= (styleDateFormats styles V.!?)    -- Get formatting from style-  let (bold, italic) = case styleIdx of-        Just idx | idx >= 0 && idx < V.length (styleFonts styles) ->-          let font = styleFonts styles V.! idx-           in (fontBold font, fontItalic font)-        _ -> (False, False)+  let font = do+        idx <- styleIdx+        fontIdx <- styleCellFonts styles V.!? idx+        styleFonts styles V.!? fontIdx+      (bold, italic) = maybe (False, False)+                         (\f -> (fontBold f, fontItalic f)) font    return $ XlsxCell cellRefParsed value bold italic @@ -275,6 +473,15 @@   let (dir, file) = splitFileName path    in dir ++ "/_rels/" ++ file ++ ".rels" +-- | Resolve a relationship target to an archive path. A target starting+-- with @/@ is relative to the package root; any other target is relative+-- to the folder of the part that holds the relationship.+resolveTarget :: FilePath -> Text -> FilePath+resolveTarget source target =+  case T.unpack target of+    '/':path -> path+    path -> collapseFilePath (Posix.takeDirectory source Posix.</> path)+ findRelWithTarget :: [(Text, Text)] -> Text -> Maybe (Text, Text) findRelWithTarget rels targetName =   find (\(_, target) -> targetName `T.isInfixOf` target) rels@@ -282,14 +489,6 @@ maybeToEither :: Text -> Maybe a -> Either Text a maybeToEither err Nothing = Left err maybeToEither _ (Just x) = Right x--getAllText :: Element -> Text-getAllText el =-  let textFromContent (Text cdata) = cdData cdata-      textFromContent (Elem e) = getAllText e-      textFromContent _ = ""-      texts = map textFromContent (elContent el)-   in T.unwords $ filter (not . T.null) texts  addContext :: Either Text a -> Text -> Either Text a addContext (Right x) _ = Right x
src/Text/Pandoc/Readers/Xlsx/Sheets.hs view
@@ -15,11 +15,14 @@   ) where  import qualified Data.Map.Strict as M+import qualified Data.Set as S import qualified Data.Text as T import Data.List (sort, dropWhileEnd)+import Numeric (floatToDigits) import Data.Char (isSpace) import Text.Pandoc.Definition import Text.Pandoc.Options (ReaderOptions)+import Text.Pandoc.Readers.OOXML.Charts (chartToBlocks) import Text.Pandoc.Readers.Xlsx.Parse import Text.Pandoc.Readers.Xlsx.Cells import qualified Text.Pandoc.Builder as B@@ -43,7 +46,7 @@       tableBlock = case cellsToTable sheet of         Just tbl -> [tbl]         Nothing -> []  -- Empty sheet-   in header : tableBlock+   in header : tableBlock ++ concatMap chartToBlocks (sheetCharts sheet)  -- | Convert cells to Pandoc Table cellsToTable :: XlsxSheet -> Maybe Block@@ -59,7 +62,7 @@           maxRow = maximum $ map cellRefRow refs            -- Build dense grid-          grid = [ [ M.lookup (CellRef col row) cells+          grid = [ [ (CellRef col row, M.lookup (CellRef col row) cells)                    | col <- [minCol..maxCol]                    ]                  | row <- [minRow..maxRow]@@ -71,19 +74,42 @@             [] -> ([], [])            -- Filter out trailing empty rows (rows with only whitespace)-          filteredBodyRows = dropWhileEnd isEmptyRow bodyRows+          filteredBodyRows = dropWhileEnd (isEmptyRow . map snd) bodyRows+          lastRow = minRow + length filteredBodyRows -          makeCell mcell = case mcell of-            Just cell ->-              let inlines = cellToInlines cell-               in Cell nullAttr AlignDefault (RowSpan 1) (ColSpan 1) [Plain inlines]-            Nothing ->-              Cell nullAttr AlignDefault (RowSpan 1) (ColSpan 1) [Plain []]+          -- A merged range becomes one cell spanning its rows and+          -- columns, clipped to the table and kept within the header row+          -- or within the body, as a cell can't span both.+          spans = M.fromList+            [ (from, (rows, cols))+            | (from, to) <- sheetMerges sheet+            , cellRefCol from >= minCol, cellRefCol from <= maxCol+            , cellRefRow from >= minRow, cellRefRow from <= lastRow+            , let rows = if cellRefRow from == minRow+                            then 1+                            else min (cellRefRow to) lastRow+                                   - cellRefRow from + 1+                  cols = min (cellRefCol to) maxCol - cellRefCol from + 1+            , rows > 1 || cols > 1+            ]+          covered = S.fromList+            [ CellRef col row+            | (CellRef c r, (rows, cols)) <- M.toList spans+            , row <- [r .. r + rows - 1], col <- [c .. c + cols - 1]+            , (col, row) /= (c, r)+            ] +          makeCell (ref, mcell) =+            let (rows, cols) = M.findWithDefault (1, 1) ref spans+             in Cell nullAttr AlignDefault (RowSpan rows) (ColSpan cols)+                     [Plain (maybe [] cellToInlines mcell)]+          makeRow = Row nullAttr . map makeCell .+                    filter (\(ref, _) -> not (ref `S.member` covered))+           numCols = length headerRow           colSpec = replicate numCols (AlignDefault, ColWidthDefault)-          thead = TableHead nullAttr [Row nullAttr $ map makeCell headerRow]-          tbody = [TableBody nullAttr 0 [] $ map (Row nullAttr . map makeCell) filteredBodyRows]+          thead = TableHead nullAttr [makeRow headerRow]+          tbody = [TableBody nullAttr 0 [] $ map makeRow filteredBodyRows]           tfoot = TableFoot nullAttr []         in Just $ Table nullAttr (Caption Nothing []) colSpec thead tbody tfoot@@ -97,16 +123,62 @@       EmptyValue -> True       TextValue t -> T.all isSpace t       NumberValue _ -> False+      BoolValue _ -> False  -- | Convert cell to Pandoc inlines cellToInlines :: XlsxCell -> [Inline] cellToInlines cell =   let base = case cellValue cell of         TextValue t -> B.toList $ B.text t-        NumberValue n -> [Str $ T.pack $ show n]+        NumberValue n -> [Str $ formatGeneral n]+        BoolValue b -> [Str $ if b then "TRUE" else "FALSE"]         EmptyValue -> []        applyBold inls = if cellBold cell then [Strong inls] else inls       applyItalic inls = if cellItalic cell then [Emph inls] else inls     in applyItalic $ applyBold base++-- | Render a number as Excel's General format does, without rounding+-- it to fit a column: at most 15 significant digits (Excel's precision)+-- and no trailing zeros. Numbers with 12 or more digits before the+-- decimal point use scientific notation (@1.23456789012E+11@), as General+-- does, keeping all their digits; so do numbers below 1e-9.+formatGeneral :: Double -> T.Text+formatGeneral x+  | isNaN x || isInfinite x = T.pack (show x)+  | x == 0 = "0"+  | otherwise = T.pack $ sign ++ body+  where+    sign = if x < 0 then "-" else ""+    (ds, e) = roundDigits 15 (floatToDigits 10 (abs x))+    digits = concatMap show ds+    -- abs x == 0.d1d2d3... * 10^e+    body+      | e > 11 || e < -8 = scientific+      | e <= 0 = "0." ++ replicate (negate e) '0' ++ digits+      | length ds <= e = digits ++ replicate (e - length ds) '0'+      | otherwise = take e digits ++ "." ++ drop e digits+    scientific =+      let mantissa = case digits of+            d:rest@(_:_) -> d : '.' : rest+            _ -> digits+          ex = e - 1+       in mantissa ++ "E" ++ (if ex < 0 then "-" else "+") +++          (let a = show (abs ex) in if length a < 2 then '0' : a else a)++-- | Round a digit list (as from 'floatToDigits') to at most n digits,+-- dropping trailing zeros.+roundDigits :: Int -> ([Int], Int) -> ([Int], Int)+roundDigits n (ds, e) =+  case splitAt n ds of+    (_, []) -> (ds, e)+    (keep, d:_) ->+      case if d >= 5 then carry (reverse keep) else Just keep of+        Just ds' -> (dropWhileEnd (== 0) ds', e)+        Nothing -> ([1], e + 1)+  where+    -- Add one to a reversed digit list; Nothing if it overflows (all 9s).+    carry [] = Nothing+    carry (9:rest) = (++ [0]) <$> carry rest+    carry (k:rest) = Just (reverse (k + 1 : rest))
src/Text/Pandoc/Transforms.hs view
@@ -127,11 +127,13 @@                   removeANSI (CodeBlock attr code) =                     CodeBlock attr (removeANSIEscapes code)                   removeANSI x = x-                  removeANSIEscapes t-                    | Just cs <- T.stripPrefix "\x1b[" t =-                        removeANSIEscapes $ T.drop 1 $ T.dropWhile (/='m') cs-                    | Just (c, cs) <- T.uncons t = T.cons c $ removeANSIEscapes cs-                    | otherwise = ""+                  removeANSIEscapes = T.concat . stripEscapes+                  stripEscapes t =+                    case T.breakOn "\x1b[" t of+                      (before, rest)+                        | T.null rest -> [before]+                        | otherwise -> before :+                            stripEscapes (T.drop 1 $ T.dropWhile (/= 'm') rest)         go x = x  -- | Remove soft breaks between East Asian characters.
src/Text/Pandoc/Writers/Docx.hs view
@@ -501,10 +501,7 @@ loadArchives opts = do   username <- P.lookupEnv "USERNAME"   utctime <- P.getTimestamp-  oldUserDataDir <- P.getUserDataDir-  P.setUserDataDir Nothing   res <- readDefaultDataFile "reference.docx"-  P.setUserDataDir oldUserDataDir   let distArchive = toArchive $ BL.fromStrict res   refArchive <- case writerReferenceDoc opts of                    Just f  -> do
src/Text/Pandoc/Writers/Docx/OpenXML.hs view
@@ -152,9 +152,9 @@     , "pPrChange"     ] [0..]) -sortSquashed :: M.Map Text Int -> [Element] -> [Element]+sortSquashed :: M.Map Text Int -> Set.Set Element -> [Element] sortSquashed tagOrder l =-  sortBy (comparing tagIndex) l+  sortBy (comparing tagIndex) (Set.toList l)   where     tagIndex :: Element -> Int     tagIndex el =@@ -162,8 +162,10 @@       where tag = (qName . elName) el  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)+squashProps tagOrder (EnvProps Nothing es) =+  sortSquashed tagOrder es+squashProps tagOrder (EnvProps (Just e) es) =+  sortSquashed tagOrder (Set.insert e es)  -- | Certain characters are invalid in XML even if escaped. -- See #1992@@ -545,7 +547,7 @@                        else "Body Text"       paraProps <- local (\env -> env{ envParaProperties =                                         envParaProperties env <>-                                        EnvProps (Just bodyTextStyle) [] })+                                        EnvProps (Just bodyTextStyle) mempty })                       (getParaProps displayMathPara)       modify $ \s -> s { stFirstPara = False }       contents <- inlinesToOpenXML opts lst@@ -623,7 +625,7 @@     then "Figure"     else "Captioned Figure"   paraProps <- local-    (\env -> env { envParaProperties = EnvProps (Just prop) [] <>+    (\env -> env { envParaProperties = EnvProps (Just prop) mempty <>                                        envParaProperties env })     (getParaProps False) @@ -770,15 +772,29 @@   let langnode = case mblang of                    Nothing -> mempty                    Just l  -> EnvProps Nothing-                               [mknode "w:lang" [("w:val", l)] ()]+                               (Set.singleton+                                (mknode "w:lang" [("w:val", l)] ()))   let squashed = squashProps rPrTagOrder (props <> langnode)   return [mknode "w:rPr" [] squashed | (not . null) squashed]  withTextProp :: PandocMonad m => Element -> WS m a -> WS m a-withTextProp d p =-  local (\env -> env {envTextProperties = ep <> envTextProperties env}) p-  where ep = if isStyle d then EnvProps (Just d) [] else EnvProps Nothing [d]+withTextProp d+  | isStyle d = local (\env -> env{ envTextProperties = EnvProps (Just d)+                                     (otherElements (envTextProperties env)) })+  | isToggle d = local (\env ->+                          let EnvProps mbsty elts = envTextProperties env+                          in  env{ envTextProperties = EnvProps mbsty $+                                    if d `Set.member` elts+                                       then Set.delete d elts+                                       else Set.insert d elts })+  | otherwise = local (\env -> env{ envTextProperties =+                                    EnvProps Nothing (Set.singleton d) <>+                                    envTextProperties env }) +-- Let emphasis flip-flop, so emph within emph is upright:+isToggle :: Element -> Bool+isToggle el = qName (elName el) `elem` ["i", "iCs"]+ withTextPropM :: PandocMonad m => WS m Element -> WS m a -> WS m a withTextPropM md p = do   d <- md@@ -803,7 +819,8 @@         | otherwise = [mknode "w:numPr" []                         [ mknode "w:ilvl" [("w:val",tshow listLevel)] ()                         , mknode "w:numId" [("w:val",tshow numid)] () ]]-  return $ case squashProps pPrTagOrder (EnvProps Nothing listPr <> props) of+  return $ case squashProps pPrTagOrder+                  (EnvProps Nothing (Set.fromList listPr) <> props) of                 [] -> []                 ps -> [mknode "w:pPr" [] ps] @@ -1046,7 +1063,7 @@          tprops <- asks envTextProperties          let r' = fromXLElement r              mbhl = listToMaybe-               [ e | e <- otherElements tprops+               [ e | e <- Set.toList (otherElements tprops)                     , XML.qName (XML.elName e) == "highlight"                     , XML.qPrefix (XML.elName e) == Just "w" ]          return [Elem (maybe r' (`highlightMathRuns` r') mbhl)]@@ -1282,8 +1299,10 @@   -- them to stack up.   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)+      paraProps' = Set.filter (\e -> (qName . elName) e /= "bidi")+                     (otherElements paraProps)+      textProps' = Set.filter (\e -> (qName . elName) e /= "rtl")+                     (otherElements textProps)       paraStyle = styleElement paraProps       textStyle = styleElement textProps   if not isRTL && not hasBidi && not hasRtl@@ -1293,8 +1312,10 @@     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'-                     , envTextProperties = EnvProps textStyle $ mknode "w:rtl" [] () : textProps'+         \env -> env { envParaProperties = EnvProps paraStyle $+                        Set.insert (mknode "w:bidi" [] ()) paraProps'+                     , envTextProperties = EnvProps textStyle $+                        Set.insert (mknode "w:rtl" [] ()) textProps'                      }     else flip local x $ \env -> env { envParaProperties = EnvProps paraStyle paraProps'                                     , envTextProperties = EnvProps textStyle textProps'
src/Text/Pandoc/Writers/Docx/Types.hs view
@@ -69,14 +69,14 @@   data EnvProps = EnvProps{ styleElement  :: Maybe Element-                        , otherElements :: [Element]+                        , otherElements :: Set.Set Element                         } deriving (Show)  instance Semigroup EnvProps where-  EnvProps s es <> EnvProps s' es' = EnvProps (s <|> s') (es ++ es')+  EnvProps s es <> EnvProps s' es' = EnvProps (s <|> s') (es <> es')  instance Monoid EnvProps where-  mempty = EnvProps Nothing []+  mempty = EnvProps Nothing mempty   mappend = (<>)  data WriterEnv = WriterEnv@@ -195,7 +195,9 @@ withParaProp :: PandocMonad m => Element -> WS m a -> WS m a withParaProp d p =   local (\env -> env {envParaProperties = ep <> envParaProperties env}) p-  where ep = if isStyle d then EnvProps (Just d) [] else EnvProps Nothing [d]+  where ep = if isStyle d+                then EnvProps (Just d) mempty+                else EnvProps Nothing (Set.singleton d)  withParaPropM :: PandocMonad m => WS m Element -> WS m a -> WS m a withParaPropM md p = do
src/Text/Pandoc/Writers/EPUB.hs view
@@ -54,7 +54,7 @@                            stringify, stringifyInlines, uniqueIdent, tshow) import qualified Text.Pandoc.UTF8 as UTF8 import Text.Pandoc.UUID (getRandomUUID)-import Text.Pandoc.Walk (walk, walkM)+import Text.Pandoc.Walk (query, walk, walkM) import Text.Pandoc.Writers.HTML (writeHtmlStringForEPUB) import Text.Printf (printf) import Text.Pandoc.XML.Light@@ -203,13 +203,14 @@                        return m{ epubLanguage = localeLang }           else return m   let fixDate m =-       if null (epubDate m)-          then do+       -- drop dates we could not normalize (dc:date must not be empty)+       case filter (not . T.null . dateText) (epubDate m) of+         [] -> do             currentTime <- lift P.getTimestamp             return $ m{ epubDate = [ Date{                              dateText = showDateTimeISO8601 currentTime                            , dateEvent = Nothing } ] }-          else return m+         ds -> return m{ epubDate = ds }   let addAuthor m =        if any (\c -> creatorRole c == Just "aut") $ epubCreator m           then return m@@ -1257,8 +1258,10 @@  -- Add identifiers to any headers without them. addIdentifiers :: WriterOptions -> [Block] -> [Block]-addIdentifiers opts bs = evalState (mapM go bs) Set.empty- where go (Header n (ident,classes,kvs) ils) = do+addIdentifiers opts bs = evalState (mapM go bs) (Set.fromList (query getId bs))+ where getId (Header _ (ident,_,_) _) = [ident]+       getId _ = []+       go (Header n (ident,classes,kvs) ils) = do          ids <- get          let ident' = if T.null ident                          then uniqueIdent (writerExtensions opts) ils ids
src/Text/Pandoc/Writers/LaTeX.hs view
@@ -243,6 +243,7 @@                   defField "documentclass" documentClass $                   defField "verbatim-in-note" (stVerbInNote st) $                   defField "tables" (stTable st) $+                  defField "deferred-notes" (stDeferredNoteNum st > 0) $                   defField "multirow" (stMultiRow st) $                   defField "cancel" (stCancel st) $                   defField "strikeout" (stStrikeout st) $@@ -1220,6 +1221,7 @@ inlineToLaTeX (Note contents) = do   setEmptyLine False   modify (\s -> s{stInNote = True })+  numNotesBefore <- gets (length . stNotes)   contents' <- withExternalNotes $ blockListToLaTeX contents   modify (\s -> s {stInNote = False })   let optnl = case reverse contents of@@ -1238,8 +1240,14 @@   externalNotes <- gets stExternalNotes   if externalNotes      then do-       modify $ \st -> st{ stNotes = noteContents : stNotes st }-       return "\\footnotemark{}"+       -- the marks of notes nested in this one are only set when+       -- this note's text is typeset, so their texts must come after it+       notes <- gets stNotes+       let (nested, rest) = splitAt (length notes - numNotesBefore) notes+       num <- gets ((+ 1) . stDeferredNoteNum)+       modify $ \st -> st{ stNotes = nested ++ (num, noteContents) : rest+                         , stDeferredNoteNum = num }+       return $ "\\pandocfootnotemark" <> braces (text (show num))        -- note: a \n before } needed when note ends with a Verbatim environment        else return $ "\\footnote" <> beamerMark <> braces noteContents 
src/Text/Pandoc/Writers/LaTeX/Notes.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE LambdaCase        #-} {-# LANGUAGE OverloadedStrings #-} {- |    Module      : Text.Pandoc.Writers.LaTeX.Notes@@ -9,26 +8,21 @@    Stability   : alpha    Portability : portable -Output tables as LaTeX.+Output deferred footnote texts as LaTeX. -} module Text.Pandoc.Writers.LaTeX.Notes   ( notesToLaTeX   ) where -import Data.List (intersperse)-import Text.DocLayout ( Doc, braces, empty, text, vcat, ($$))+import Text.DocLayout ( Doc, braces, text, vcat ) import Data.Text (Text) -notesToLaTeX :: [Doc Text] -> Doc Text-notesToLaTeX = \case-  [] -> empty-  ns -> (case length ns of-            n | n > 1 -> "\\addtocounter" <>-                         braces "footnote" <>-                         braces (text $ show $ 1 - n)-              | otherwise -> empty)-        $$-        vcat (intersperse-               ("\\addtocounter" <> braces "footnote" <> braces "1")-               $ map (\x -> "\\footnotetext" <> braces x)-               $ reverse ns)+-- | Render the texts of notes whose marks were produced with+-- @\\pandocfootnotemark{n}@. @\\pandocfootnotetext{n}@ restores+-- the footnote mark and hyperref anchor saved by the corresponding+-- mark (see the template). Notes are stored in reverse order.+notesToLaTeX :: [(Int, Doc Text)] -> Doc Text+notesToLaTeX = vcat . map toNoteText . reverse+  where+    toNoteText (num, x) =+      "\\pandocfootnotetext" <> braces (text (show num)) <> braces x
src/Text/Pandoc/Writers/LaTeX/Table.hs view
@@ -28,7 +28,7 @@ import Text.DocLayout   ( Doc, braces, brackets, cr, empty, hcat, hsep, isEmpty, literal, nest   , text, vcat, ($$))-import Text.Pandoc.Shared (splitBy, tshow)+import Text.Pandoc.Shared (deNote, splitBy, tshow) import Text.Pandoc.Walk (walk, query) import Data.Monoid (Any(..)) import Text.Pandoc.Writers.LaTeX.Caption (getCaption)@@ -54,12 +54,14 @@   -- if the float class is included in table attributes, we generate a floating   -- table environment; otherwise we use longtable   let float = "float" `elem` classes+  let repeatCaption = not float && "repeat-caption" `elem` classes   let renderTable = do        let unnumbered = "unnumbered" `elem` classes-       capt <- case caption of-                 Caption Nothing []-                   | unnumbered || T.null ident -> pure mempty-                 _ -> captionToLaTeX inlnsToLaTeX caption ident+       (capt, repeatedCapt) <-+         case caption of+           Caption Nothing []+             | unnumbered || T.null ident -> pure (mempty, mempty)+           _ -> captionToLaTeX inlnsToLaTeX repeatCaption caption ident        let isSimpleTable =              all ((== ColWidthDefault) . snd) specs &&              all (all isSimpleCell)@@ -76,12 +78,14 @@        let makeUnnumbered x =              "{\\def\\LTcaptype{none} % do not increment counter" $$ x $$ "}"        let makeTable = if float-                          then tableToLaTeXTable placement-                          else tableToLaTeXLongtable+                          then tableToLaTeXTable placement colDesc+                                 mkHead mkRow capt+                          else tableToLaTeXLongtable colDesc mkHead mkRow capt+                                 repeatedCapt        (if unnumbered || isEmpty capt           then makeUnnumbered           else id) <$>-          makeTable colDesc mkHead mkRow capt thead tbodies tfoot+          makeTable thead tbodies tfoot   ($$) <$> withExternalNotes renderTable <*> getAccumulatedNotes  tableToLaTeXTable :: PandocMonad m@@ -132,28 +136,33 @@                       -> (Ann.TableHead -> LW m (Doc Text))                       -> ([Ann.Cell] -> LW m (Doc Text))                       -> Doc Text+                      -> Doc Text                       -> Ann.TableHead                       -> [Ann.TableBody]                       -> Ann.TableFoot                       -> LW m (Doc Text)-tableToLaTeXLongtable colDesc mkHead mkRow capt thead tbodies tfoot = do+tableToLaTeXLongtable colDesc mkHead mkRow capt repeatedCapt+                      thead tbodies tfoot = do   opts <- gets stOptions   let hasTopCaption = not (isEmpty capt) &&                         writerTableCaptionPosition opts == CaptionAbove   let hasBottomCaption = not (isEmpty capt) &&                           writerTableCaptionPosition opts == CaptionBelow-  -- The first head is not repeated on the following pages. If we were to just-  -- use a single head, without a separate first head, then the caption would be-  -- repeated on all pages that contain a part of the table. We avoid this by-  -- making the caption part of the first head. The downside is that we must-  -- duplicate the header rows for this.+  -- The first head is not repeated on the following pages. Keep the numbered+  -- caption in that head. Add the unnumbered repeated caption to the regular+  -- head only when requested. The downside is that we must duplicate the+  -- header rows.   head' <- do     case (hasTopCaption, isEmptyHead thead) of       (False, True) -> return "\\toprule\\noalign{}"       (False, False)  -> mkHead thead       (True, True)  -> return (capt <> "\\tabularnewline"                                 $$ "\\toprule\\noalign{}"-                                $$ "\\endfirsthead")+                                $$ "\\endfirsthead"+                                $$ (if isEmpty repeatedCapt+                                       then mempty+                                       else repeatedCapt <> "\\tabularnewline"+                                            $$ "\\toprule\\noalign{}"))       (True, False)   -> do         -- avoid duplicate notes in head and firsthead:         firsthead <- mkHead thead@@ -163,6 +172,9 @@         return $ capt <> "\\tabularnewline"                  $$ firsthead                  $$ "\\endfirsthead"+                 $$ (if isEmpty repeatedCapt+                        then mempty+                        else repeatedCapt <> "\\tabularnewline")                  $$ repeated   rows' <- mapM mkRow $ mconcat (map bodyRows tbodies)   lastfoot <- mapM mkRow $ footRows tfoot@@ -173,6 +185,9 @@               $$ (if hasBottomCaption                      then "\\tabularnewline" $$ capt                      else mempty)+  let repeatedFoot = "\\bottomrule\\noalign{}"+                     $$ "\\tabularnewline"+                     $$ repeatedCapt   modify $ \s -> s{ stTable = True }   beamer <- gets stBeamer   return $@@ -188,9 +203,12 @@              then [ vcat rows'                   , foot'                   ]-             else [ foot'+             else (if hasBottomCaption && not (isEmpty repeatedCapt)+                      then [ repeatedFoot, "\\endfoot" ]+                      else []) <>+                  [ foot'                   , "\\endlastfoot"-                  ,  vcat rows'+                  , vcat rows'                   ])     $$ "\\end{longtable}" @@ -257,16 +275,25 @@  captionToLaTeX :: PandocMonad m                => ([Inline] -> LW m (Doc Text))+               -> Bool     -- ^ repeat caption on subsequent pages                -> Caption                -> Text     -- ^ table identifier (label)-               -> LW m (Doc Text)-captionToLaTeX inlnsToLaTeX caption ident = do+               -> LW m (Doc Text, Doc Text)+captionToLaTeX inlnsToLaTeX repeatCaption caption ident = do   (captionText, captForLot) <- getCaption inlnsToLaTeX caption+  repeatedCaptionText <-+    if repeatCaption+       then fst <$> getCaption inlnsToLaTeX (walk deNote caption)+       else pure empty   label <- labelFor ident-  return $ if isEmpty captionText && isEmpty label-              then empty-              else "\\caption" <> captForLot <>-                     braces captionText <> label+  let capt = if isEmpty captionText && isEmpty label+                then empty+                else "\\caption" <> captForLot <>+                       braces captionText <> label+  let repeatedCapt = if isEmpty repeatedCaptionText+                        then empty+                        else "\\caption[]" <> braces repeatedCaptionText+  return (capt, repeatedCapt)  type BlocksWriter m = [Block] -> LW m (Doc Text) 
src/Text/Pandoc/Writers/LaTeX/Types.hs view
@@ -29,7 +29,8 @@   , stInItem        :: Bool          -- ^ true if in \item[..]   , stInFigure      :: Bool          -- ^ true if in figure environment   , stInCite        :: Bool          -- ^ true if in a Cite-  , stNotes         :: [Doc Text]    -- ^ notes in a minipage+  , stNotes         :: [(Int, Doc Text)] -- ^ deferred notes (id, text)+  , stDeferredNoteNum :: Int         -- ^ number of deferred notes so far   , stOLLevel       :: Int           -- ^ level of ordered list nesting   , stOptions       :: WriterOptions -- ^ writer options, so they don't have to                                      --   be parameter@@ -76,6 +77,7 @@   , stInFigure = False   , stInCite = False   , stNotes = []+  , stDeferredNoteNum = 0   , stOLLevel = 1   , stOptions = options   , stVerbInNote = False
src/Text/Pandoc/Writers/OpenDocument.hs view
@@ -16,7 +16,8 @@ module Text.Pandoc.Writers.OpenDocument ( writeOpenDocument ) where import Control.Monad (unless, liftM, when) import Control.Monad.State.Strict ( StateT(..), modify, gets, lift )-import Data.Char (chr, isDigit)+import Data.Char (chr, isDigit, isLetter, isMark, ord)+import Numeric (showHex) import Data.List (sortOn, sortBy) import qualified Data.List as L import qualified Data.Map as Map@@ -550,7 +551,7 @@         let (ident,_,kvs) = attr             i = withDirFromAttr attr $ withLangFromAttr attr $                 case lookup "custom-style" kvs of-                  Just sty -> withParagraphStyle o sty s+                  Just sty -> withParagraphStyle o (encodeStyleName sty) s                   _        -> blocksToOpenDocument o s             mkBookmarkedDiv = inTags False "text:section" [("text:name", ident)]         if T.null ident@@ -832,7 +833,7 @@         let (ident,_,kvs) = attr             i = maybe id (\sty ->                            fmap (inTags False "text:span"-                                  [ ("text:style-name", sty) ]))+                                  [ ("text:style-name", encodeStyleName sty) ]))                   (lookup "custom-style" kvs) .                  withLangFromAttr attr $ inlinesToOpenDocument o xs             mkBookmarkedSpan b =@@ -1012,6 +1013,21 @@                         ("fo:text-align", "right")]       Just LTR -> [("style:writing-mode", "lr-tb"),                         ("fo:text-align", "left")]++-- | Encode a style name as LibreOffice does: characters not allowed+-- in an XML name are replaced by their code point in hex between+-- underscores, so that, e.g., @Text body@ becomes @Text_20_body@.+encodeStyleName :: Text -> Text+encodeStyleName t = case T.uncons t of+  Nothing      -> t+  Just (c, cs) -> enc isStartChar c <> T.concatMap (enc isNameChar) cs+  where+    enc ok c+      | ok c      = T.singleton c+      | otherwise = "_" <> T.pack (showHex (ord c) "") <> "_"+    isStartChar c = isLetter c || c == '_'+    isNameChar c  = isStartChar c || isDigit c || isMark c+                    || c == '-' || c == '.'  -- | Adjust a named paragraph style for the current writing direction. -- When a direction is active, an automatic style derived from the
src/Text/Pandoc/Writers/Powerpoint/Output.hs view
@@ -593,13 +593,8 @@ presentationToArchive :: PandocMonad m                       => WriterOptions -> Meta -> Presentation -> m Archive presentationToArchive opts meta pres = do-  -- ensure that the default reference doc isn't built from-  -- pptx/ files in the user data directory:-  oldUserDataDir <- P.getUserDataDir-  P.setUserDataDir Nothing   distArchive <- toArchive . BL.fromStrict <$>                         readDefaultDataFile "reference.pptx"-  P.setUserDataDir oldUserDataDir   refArchive <- case writerReferenceDoc opts of                    Just f  -> do                      arch <- toArchiveOrFail . BL.fromStrict . fst
+ test/Tests/Data.hs view
@@ -0,0 +1,198 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TupleSections #-}+{- |+   Module      : Tests.Data+   Copyright   : © 2026 John MacFarlane+   License     : GNU GPL, version 2 or above++   Maintainer  : John MacFarlane <jgm@berkeley.edu>+   Stability   : alpha+   Portability : portable++Tests for data file lookup, including lookup in @PANDOC_DATA_DIRS@.+-}+module Tests.Data (tests) where++import Codec.Archive.Zip (Archive, filesInArchive,+#ifdef EMBED_DATA_FILES+                          findEntryByPath, fromEntry,+#endif+                          toArchive)+import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as BL+import Data.List (intercalate)+import qualified Data.Text as T+import System.FilePath ((</>), searchPathSeparator)+import System.Info (os)+import Test.Tasty+import Test.Tasty.HUnit+import Text.Pandoc.Class (PandocPure, FileInfo(..), FileTree,+                          findFileWithDataFallback, getDataDirs,+                          getPandocDataDirs, insertInFileTree,+                          modifyPureState, PureState(..), runIOorExplode,+                          runPure, setUserDataDir)+import Text.Pandoc.Data (readDataFile, readDefaultDataFile)+import Data.Time.Clock.POSIX (posixSecondsToUTCTime)++-- | An absolute path that is valid on the current platform.+absPath :: FilePath -> FilePath+absPath d = if os == "mingw32" then "C:\\" </> d else "/" </> d++userDir, dirA, dirB :: FilePath+userDir = absPath "user"+dirA = absPath "dirA"+dirB = absPath "dirB"++pandocDataDirs :: [FilePath] -> T.Text+pandocDataDirs = T.pack . intercalate [searchPathSeparator]++-- | Run a 'PandocPure' action with the given user data directory,+-- value of @PANDOC_DATA_DIRS@, and files.+runWith :: Maybe FilePath -> Maybe T.Text -> FileTree -> [(FilePath, B.ByteString)]+        -> PandocPure a -> Either String a+runWith mbUserDir mbDataDirs defaultFiles files action =+  either (Left . show) Right $ runPure $ do+    modifyPureState $ \st ->+      st{ stEnv = maybe [] (\x -> [("PANDOC_DATA_DIRS", x)]) mbDataDirs+        , stFiles = foldr (\(fp, bs) -> insertInFileTree fp (fileInfo bs))+                          defaultFiles files }+    setUserDataDir mbUserDir+    action++fileInfo :: B.ByteString -> FileInfo+fileInfo bs = FileInfo{ infoFileMTime = posixSecondsToUTCTime 0+                      , infoFileContents = bs }++-- | Default data files that are needed by the tests, placed where+-- 'PandocPure' looks for them if data files are not embedded.+getDefaultFiles :: IO (FileTree, B.ByteString, Archive)+getDefaultFiles = runIOorExplode $ do+  abbrevs <- readDefaultDataFile "abbreviations"+  refdocx <- toArchive . BL.fromStrict <$>+               readDefaultDataFile "reference.docx"+  parts <- mapM (\p -> (p,) <$> readDefaultDataFile ("docx" </> p))+                (filesInArchive refdocx)+  let tree = foldr (\(fp, bs) -> insertInFileTree ("data" </> fp) (fileInfo bs))+                   mempty+                   (("abbreviations", abbrevs) :+                     [("docx" </> p, bs) | (p, bs) <- parts])+  return (tree, abbrevs, refdocx)++#ifdef EMBED_DATA_FILES+-- | Contents of a file in a docx archive.+docxPart :: FilePath -> B.ByteString -> Maybe BL.ByteString+docxPart p = fmap fromEntry . findEntryByPath p . toArchive . BL.fromStrict+#endif++tests :: [TestTree]+tests =+  [ testGroup "getPandocDataDirs"+    [ testCase "PANDOC_DATA_DIRS unset" $+        runWith Nothing Nothing mempty [] getPandocDataDirs @?= Right []+    , testCase "ignoring relative and empty entries" $+        runWith Nothing (Just (pandocDataDirs [dirA, "relative", "", dirB]))+          mempty [] getPandocDataDirs+          @?= Right [dirA, dirB]+    ]+  , testGroup "getDataDirs"+    [ testCase "user data dir comes first" $+        runWith (Just userDir) (Just (pandocDataDirs [dirA, dirB])) mempty []+          (getDataDirs "templates/default.html5")+          @?= Right [userDir, dirA, dirB]+    , testCase "no user data dir" $+        runWith Nothing (Just (pandocDataDirs [dirA])) mempty []+          (getDataDirs "abbreviations")+          @?= Right [dirA]+    , testCase "absolute path" $+        runWith (Just userDir) (Just (pandocDataDirs [dirA])) mempty []+          (getDataDirs (absPath "abbreviations"))+          @?= Right []+    , testCase "path relative to parent directory" $+        runWith (Just userDir) (Just (pandocDataDirs [dirA])) mempty []+          (getDataDirs (".." </> "abbreviations"))+          @?= Right []+    ]+  , withResource getDefaultFiles (const (return ())) $ \getDefaults ->+    testGroup "readDataFile"+    [ testCase "user data dir takes precedence" $ do+        (defs, _, _) <- getDefaults+        runWith (Just userDir) (Just (pandocDataDirs [dirA])) defs+          [ (userDir </> "abbreviations", "user")+          , (dirA </> "abbreviations", "dirA") ]+          (readDataFile "abbreviations")+          @?= Right "user"+    , testCase "earlier PANDOC_DATA_DIRS entries take precedence" $ do+        (defs, _, _) <- getDefaults+        runWith (Just userDir) (Just (pandocDataDirs [dirA, dirB])) defs+          [ (dirA </> "abbreviations", "dirA")+          , (dirB </> "abbreviations", "dirB") ]+          (readDataFile "abbreviations")+          @?= Right "dirA"+    , testCase "later PANDOC_DATA_DIRS entries are used" $ do+        (defs, _, _) <- getDefaults+        runWith Nothing (Just (pandocDataDirs [dirA, dirB])) defs+          [ (dirB </> "abbreviations", "dirB") ]+          (readDataFile "abbreviations")+          @?= Right "dirB"+#ifdef EMBED_DATA_FILES+    , testCase "falls back to default data files" $ do+        (defs, abbrevs, _) <- getDefaults+        runWith (Just userDir) (Just (pandocDataDirs [dirA])) defs []+          (readDataFile "abbreviations")+          @?= Right abbrevs+    , testCase "readDefaultDataFile ignores data dirs" $ do+        (defs, abbrevs, _) <- getDefaults+        runWith (Just userDir) (Just (pandocDataDirs [dirA])) defs+          [ (userDir </> "abbreviations", "user")+          , (dirA </> "abbreviations", "dirA") ]+          (readDefaultDataFile "abbreviations")+          @?= Right abbrevs+    , testCase "reference.docx from PANDOC_DATA_DIRS" $ do+        (defs, _, _) <- getDefaults+        runWith Nothing (Just (pandocDataDirs [dirA])) defs+          [ (dirA </> "reference.docx", "dirA") ]+          (readDataFile "reference.docx")+          @?= Right "dirA"+    , testCase "reference.docx component from PANDOC_DATA_DIRS" $ do+        (defs, _, _) <- getDefaults+        let styles = "word" </> "styles.xml"+        runWith Nothing (Just (pandocDataDirs [dirA])) defs+          [ (dirA </> "docx" </> styles, "dirA") ]+          (docxPart styles <$> readDataFile "reference.docx")+          @?= Right (Just "dirA")+    , testCase "readDefaultDataFile ignores reference.docx components" $ do+        (defs, _, refdocx) <- getDefaults+        let styles = "word" </> "styles.xml"+        let expected = fromEntry <$> findEntryByPath styles refdocx+        runWith (Just userDir) (Just (pandocDataDirs [dirA])) defs+          [ (userDir </> "docx" </> styles, "user")+          , (dirA </> "docx" </> styles, "dirA") ]+          (docxPart styles <$> readDefaultDataFile "reference.docx")+          @?= Right expected+#endif+    ]+  , testGroup "findFileWithDataFallback"+    [ testCase "file in working directory takes precedence" $+        runWith (Just userDir) (Just (pandocDataDirs [dirA])) mempty+          [ ("f.lua", "")+          , (dirA </> "filters" </> "f.lua", "") ]+          (findFileWithDataFallback "filters" "f.lua")+          @?= Right (Just "f.lua")+    , testCase "user data dir takes precedence" $+        runWith (Just userDir) (Just (pandocDataDirs [dirA])) mempty+          [ (userDir </> "filters" </> "f.lua", "")+          , (dirA </> "filters" </> "f.lua", "") ]+          (findFileWithDataFallback "filters" "f.lua")+          @?= Right (Just (userDir </> "filters" </> "f.lua"))+    , testCase "file in PANDOC_DATA_DIRS" $+        runWith (Just userDir) (Just (pandocDataDirs [dirA, dirB])) mempty+          [ (dirB </> "filters" </> "f.lua", "") ]+          (findFileWithDataFallback "filters" "f.lua")+          @?= Right (Just (dirB </> "filters" </> "f.lua"))+    , testCase "file not found" $+        runWith (Just userDir) (Just (pandocDataDirs [dirA])) mempty []+          (findFileWithDataFallback "filters" "f.lua")+          @?= Right Nothing+    ]+  ]
test/Tests/Readers/Docx.hs view
@@ -220,6 +220,10 @@             "docx/nested_smart_tags.docx"             "docx/nested_smart_tags.native"           , testCompare+            "inline customXml elements"+            "docx/custom_xml.docx"+            "docx/custom_xml.native"+          , testCompare             "remove anchor spans with nothing pointing to them"             "docx/unused_anchors.docx"             "docx/unused_anchors.native"
test/Tests/Readers/ODT.hs view
@@ -31,7 +31,19 @@ tests = testsComparingToMarkdown ++ testsComparingToNative ++         [ testGroup "fodt" (fodtTestsComparingToMarkdown ++                             fodtTestsComparingToNative ++-                            fodtTestsComparingToOwnNative) ]+                            fodtTestsComparingToOwnNative)+        , testGroup "styles extension"+          [ unsafePerformIO $ liftM (test id "enabled")+              (compareFODTToNative stylesopts "odt/fodt/styles.fodt"+                                              "odt/fodt-native/styles.native")+          , unsafePerformIO $ liftM (test id "disabled")+              (compareFODTToNative defopts "odt/fodt/styles.fodt"+                                           "odt/fodt-native/styles-off.native")+          ]+        ]+  where+    stylesopts = defopts{ readerExtensions = enableExtension Ext_styles+                                               (readerExtensions defopts) }  testsComparingToMarkdown :: [TestTree] testsComparingToMarkdown    = map nameToTest namesOfTestsComparingToMarkdown
test/Tests/Readers/Pptx.hs view
@@ -41,8 +41,62 @@ tests :: [TestTree] tests = [ testGroup "basic"           [ testCompare+            "charts"+            "pptx-reader/charts.pptx"+            "pptx-reader/charts.native"+          , testCompare+            "run formatting and hyperlinks"+            "pptx-reader/formatting.pptx"+            "pptx-reader/formatting.native"+          , testCompare+            "group shapes"+            "pptx-reader/groups.pptx"+            "pptx-reader/groups.native"+          , testCompare+            "slide number placeholders"+            "pptx-reader/slide-numbers.pptx"+            "pptx-reader/slide-numbers.native"+          , testCompare             "text extraction"             "pptx-reader/basic.pptx"             "pptx-reader/basic.native"+          , testCompare+            "text runs and line breaks"+            "pptx-reader/runs.pptx"+            "pptx-reader/runs.native"+          ]+        , testGroup "slides"+          [ testCompare+            "hidden slides"+            "pptx-reader/hidden.pptx"+            "pptx-reader/hidden.native"+          ]+        , testGroup "tables"+          [ testCompare+            "merged cells"+            "pptx-reader/table-spans.pptx"+            "pptx-reader/table-spans.native"+          ]+        , testGroup "markup compatibility"+          [ testCompare+            "equations and alternate content"+            "pptx-reader/equations.pptx"+            "pptx-reader/equations.native"+          ]+        , testGroup "lists"+          [ testCompare+            "inherited bullets, nesting and numbering"+            "pptx-reader/lists.pptx"+            "pptx-reader/lists.native"+          , testCompare+            "lists written by the pptx writer"+            "pptx-reader/lists-writer.pptx"+            "pptx-reader/lists-writer.native"+          ]+        , testGroup "notes"+          [ testCompare+            "speaker notes"+            "pptx-reader/notes.pptx"+            "pptx-reader/notes.native"           ]         ]
test/Tests/Readers/Xlsx.hs view
@@ -41,8 +41,48 @@ tests :: [TestTree] tests = [ testGroup "basic"           [ testCompare+            "cell fonts"+            "xlsx-reader/cell-fonts.xlsx"+            "xlsx-reader/cell-fonts.native"+          , testCompare+            "inline and rich-text strings"+            "xlsx-reader/strings.xlsx"+            "xlsx-reader/strings.native"+          , testCompare             "sheet extraction"             "xlsx-reader/basic.xlsx"             "xlsx-reader/basic.native"+          , testCompare+            "package-absolute relationship targets"+            "xlsx-reader/absolute-targets.xlsx"+            "xlsx-reader/absolute-targets.native"+          ]+        , testGroup "values"+          [ testCompare+            "numbers, booleans and errors"+            "xlsx-reader/numbers.xlsx"+            "xlsx-reader/numbers.native"+          ]+        , testGroup "dates"+          [ testCompare+            "date and time formats"+            "xlsx-reader/dates.xlsx"+            "xlsx-reader/dates.native"+          , testCompare+            "1904 date system"+            "xlsx-reader/dates-1904.xlsx"+            "xlsx-reader/dates-1904.native"+          ]+        , testGroup "merged cells"+          [ testCompare+            "merged ranges as spans"+            "xlsx-reader/merged.xlsx"+            "xlsx-reader/merged.native"+          ]+        , testGroup "charts"+          [ testCompare+            "charts on a sheet and on a chart sheet"+            "xlsx-reader/charts.xlsx"+            "xlsx-reader/charts.native"           ]         ]
test/command/1023.md view
@@ -100,7 +100,7 @@   >{\raggedright\arraybackslash}p{(\linewidth - 6\tabcolsep) * \real{0.2078}}@{}} \toprule\noalign{} \begin{minipage}[b]{\linewidth}\centering-Name\footnotemark{}+Name\pandocfootnotemark{1} \end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright Capital \end{minipage} & \begin{minipage}[b]{\linewidth}\raggedright@@ -113,7 +113,7 @@ \midrule\noalign{} Denmark & Copenhagen & 5,809,502 & 43,094 \\ Finland & Helsinki & 5,537,364 & 338,145 \\-Iceland\footnotemark{} & Reykjavik & 343,518 & 103,000 \\+Iceland\pandocfootnotemark{2} & Reykjavik & 343,518 & 103,000 \\ Norway & Oslo & 5,372,191 & 323,802 \\ Sweden & Stockholm & 10,313,447 & 450,295 \\ \midrule\noalign{}@@ -121,9 +121,7 @@ \bottomrule\noalign{} \end{tabular} \end{table}-\addtocounter{footnote}{-1}-\footnotetext{In English.}-\addtocounter{footnote}{1}-\footnotetext{Brrr.}+\pandocfootnotetext{1}{In English.}+\pandocfootnotetext{2}{Brrr.}  ```
test/command/11113.docx view

binary file changed (14206 → 3166 bytes)

test/command/11689.docx view

binary file changed (15080 → 4154 bytes)

test/command/11833.docx view

binary file changed (10947 → 5711 bytes)

+ test/command/11852.md view
@@ -0,0 +1,33 @@+```+% pandoc -f html -t native+<p>a<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a></p>+<section id="footnotes" class="footnotes" role="doc-endnotes"><hr /><ol>+<li id="fn1"><p>note</p><ol><li>one</li><li>two</li></ol>+<a href="#fnref1" class="footnote-back" role="doc-backlink">x</a></li></ol></section>+^D+[ Para+    [ Str "a"+    , Note+        [ Para [ Str "note" ]+        , OrderedList+            ( 1 , DefaultStyle , DefaultDelim )+            [ [ Plain [ Str "one" ] ] , [ Plain [ Str "two" ] ] ]+        ]+    ]+]+```++```+% pandoc -f html -t native+<p>a<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a></p>+<section id="footnotes" class="footnotes" role="doc-endnotes"><hr /><ol>+<li id="fn1"><p>one</p><hr /><p>two</p>+<a href="#fnref1" class="footnote-back" role="doc-backlink">x</a></li></ol></section>+^D+[ Para+    [ Str "a"+    , Note+        [ Para [ Str "one" ] , HorizontalRule , Para [ Str "two" ] ]+    ]+]+```
+ test/command/11936.md view
@@ -0,0 +1,139 @@+Footnotes in tables are deferred. Each `\pandocfootnotetext` must+be matched to its `\pandocfootnotemark`, and the text of a nested+note must come after the text containing its mark.++```+% pandoc -f native -t latex+[ Table+    ( "" , [] , [] )+    (Caption Nothing [])+    [ ( AlignDefault , ColWidthDefault )+    , ( AlignDefault , ColWidthDefault )+    ]+    (TableHead+       ( "" , [] , [] )+       [ Row+           ( "" , [] , [] )+           [ Cell+               ( "" , [] , [] )+               AlignDefault+               (RowSpan 1)+               (ColSpan 1)+               [ Plain [ Str "A" ] ]+           , Cell+               ( "" , [] , [] )+               AlignDefault+               (RowSpan 1)+               (ColSpan 1)+               [ Plain [ Str "B" ] ]+           ]+       ])+    [ TableBody+        ( "" , [] , [] )+        (RowHeadColumns 0)+        []+        [ Row+            ( "" , [] , [] )+            [ Cell+                ( "" , [] , [] )+                AlignDefault+                (RowSpan 1)+                (ColSpan 1)+                [ Plain+                    [ Str "x"+                    , Note+                        [ Para+                            [ Str "outer"+                            , Space+                            , Note [ Para [ Str "inner note" ] ]+                            ]+                        ]+                    ]+                ]+            , Cell+                ( "" , [] , [] )+                AlignDefault+                (RowSpan 1)+                (ColSpan 1)+                [ Plain [ Str "y" , Note [ Para [ Str "second" ] ] ] ]+            ]+        ]+    ]+    (TableFoot ( "" , [] , [] ) [])+]+^D+{\def\LTcaptype{none} % do not increment counter+\begin{longtable}[]{@{}ll@{}}+\toprule\noalign{}+A & B \\+\midrule\noalign{}+\endhead+\bottomrule\noalign{}+\endlastfoot+x\pandocfootnotemark{2} & y\pandocfootnotemark{3} \\+\end{longtable}+}+\pandocfootnotetext{2}{outer \pandocfootnotemark{1}}+\pandocfootnotetext{1}{inner note}+\pandocfootnotetext{3}{second}+```++The LaTeX reader handles this output, ignoring the definitions of+the macros from the template:++```+% pandoc -f latex -t markdown+\makeatletter+\protected\def\pandocfootnotemark#1{\footnotemark{}%+  \expandafter\xdef\csname pandoc@fn@#1\endcsname{%+    \noexpand\pandoc@fnrestore+    {\unexpanded\expandafter{\@thefnmark}}%+    {\ifdefined\Hy@footnote@currentHref+       \unexpanded\expandafter{\Hy@footnote@currentHref}\fi}}}+\def\pandoc@fnrestore#1#2{%+  \gdef\@thefnmark{#1}%+  \ifdefined\Hy@footnote@currentHref\gdef\Hy@footnote@currentHref{#2}\fi}+\newcommand\pandocfootnotetext[1]{%+  \csname pandoc@fn@#1\endcsname\@footnotetext}+\makeatother++\begin{tabular}{ll}+x\pandocfootnotemark{2} & y\pandocfootnotemark{3} \\+\end{tabular}+\pandocfootnotetext{2}{outer \pandocfootnotemark{1}}+\pandocfootnotetext{1}{inner note}+\pandocfootnotetext{3}{second}+^D+  ------- -------+  x[^1]   y[^2]+  ------- -------++[^1]: outer [^3]++[^2]: second++[^3]: inner note+```++It also handles adjustments to the footnote counter with+`\footnotemark` and `\footnotetext`, as in output from+earlier versions of pandoc:++```+% pandoc -f latex -t markdown+\begin{tabular}{ll}+x\footnotemark{} & y\footnotemark{} \\+\end{tabular}+\addtocounter{footnote}{-1}+\footnotetext{first}+\addtocounter{footnote}{1}+\footnotetext{second}+^D+  ------- -------+  x[^1]   y[^2]+  ------- -------++[^1]: first++[^2]: second+```
test/command/11944.docx view

binary file changed (11719 → 4132 bytes)

+ test/command/11961.md view
@@ -0,0 +1,8 @@+```+% pandoc -f html -t docx -o - | pandoc -f docx -t html+<p><em>Emph <em>in emph <em>flip</em> flops</em></em> and more</p>+^D+<p><em>Emph</em> in emph <em>flip</em> flops and more</p>++```+
+ test/command/11988.docx view

binary file changed (absent → 1000 bytes)

+ test/command/11988.md view
@@ -0,0 +1,25 @@+Every underline type except `none` is read as underlined text, not+only `single`.++```+% pandoc command/11988.docx -t native+^D+[ Para+    [ Underline+        [ Str "single"+        , Space+        , Str "double"+        , Space+        , Str "thick"+        , Space+        , Str "dotted"+        , Space+        , Str "wave"+        , Space+        , Str "words"+        ]+    , Space+    , Str "none"+    ]+]+```
+ test/command/12001.md view
@@ -0,0 +1,68 @@+```+% pandoc -f markdown+rebase_relative_paths -t native command/12001/note/doc.md command/12001/next/doc.md command/12001/grid/doc.md command/12001/next/doc.md+^D+[ Para+    [ Str "Text"+    , Note+        [ Para+            [ Image+                ( "" , [] , [] )+                [ Str "C" ]+                ( "command/12001/note/img/note.png" , "" )+            , Space+            , Link+                ( "" , [] , [] )+                [ Str "doc" ]+                ( "command/12001/note/files/doc.pdf" , "" )+            ]+        ]+    ]+, Para+    [ Str "Next"+    , Space+    , Image+        ( "" , [] , [] )+        [ Str "N" ]+        ( "command/12001/next/img/next.png" , "" )+    ]+, Table+    ( "" , [] , [] )+    (Caption Nothing [])+    [ ( AlignDefault , ColWidth 0.3194444444444444 ) ]+    (TableHead ( "" , [] , [] ) [])+    [ TableBody+        ( "" , [] , [] )+        (RowHeadColumns 0)+        []+        [ Row+            ( "" , [] , [] )+            [ Cell+                ( "" , [] , [] )+                AlignDefault+                (RowSpan 1)+                (ColSpan 1)+                [ Figure+                    ( "" , [] , [] )+                    (Caption Nothing [ Plain [ Str "C" ] ])+                    [ Plain+                        [ Image+                            ( "" , [] , [] )+                            [ Str "C" ]+                            ( "command/12001/grid/img/grid.png" , "" )+                        ]+                    ]+                ]+            ]+        ]+    ]+    (TableFoot ( "" , [] , [] ) [])+, Para+    [ Str "Next"+    , Space+    , Image+        ( "" , [] , [] )+        [ Str "N" ]+        ( "command/12001/next/img/next.png" , "" )+    ]+]+```
+ test/command/12001/grid/doc.md view
@@ -0,0 +1,3 @@++----------------------++| ![C](img/grid.png)   |++----------------------+
+ test/command/12001/next/doc.md view
@@ -0,0 +1,1 @@+Next ![N](img/next.png)
+ test/command/12001/note/doc.md view
@@ -0,0 +1,3 @@+Text[^1]++[^1]: ![C](img/note.png) [doc](files/doc.pdf)
+ test/command/12051.md view
@@ -0,0 +1,10 @@+```+% pandoc -f markdown -t native+\itshape+hello\+world+^D+[ Para+    [ RawInline (Format "tex") "\\itshape\nhello\\\nworld" ]+]+```
+ test/command/12060.md view
@@ -0,0 +1,12 @@+Custom style names are encoded as LibreOffice does:++```+% pandoc -t opendocument+::: {custom-style="My Style"}+[Text here]{custom-style="Char (1)"} and [more]{custom-style="Quote_20_Text"}+:::+^D+<text:p text:style-name="My_20_Style"><text:span text:style-name="Char_20__28_1_29_">Text+here</text:span> and+<text:span text:style-name="Quote_20_Text">more</text:span></text:p>+```
+ test/command/12063.md view
@@ -0,0 +1,14 @@+```+% pandoc -f rst -t gfm+======== ====================+Suffix   Meaning+======== ====================+-set     Set a list of items+-append  Append single item+======== ====================+^D+| Suffix  | Meaning             |+|---------|---------------------|+| -set    | Set a list of items |+| -append | Append single item  |+```
+ test/command/12071.md view
@@ -0,0 +1,11 @@+```+% pandoc -f rst -t native+::++        foo+    bar++        baz+^D+[ CodeBlock ( "" , [] , [] ) "    foo\nbar\n\n    baz" ]+```
test/command/2378.md view
@@ -14,7 +14,7 @@ \begin{longtable}[]{@{}ll@{}} \caption{a table}\tabularnewline \toprule\noalign{}-x & y\footnotemark{} \\+x & y\pandocfootnotemark{1} \\ \midrule\noalign{} \endfirsthead \toprule\noalign{}@@ -25,5 +25,5 @@ \endlastfoot 1 & 2 \\ \end{longtable}-\footnotetext{a footnote}+\pandocfootnotetext{1}{a footnote} ```
test/command/2623.odt view

binary file changed (9375 → 3846 bytes)

+ test/command/4558.md view
@@ -0,0 +1,69 @@+```+% pandoc -f html -t latex+<table class="repeat-caption">+  <caption>Repeated caption.</caption>+  <thead><tr><th>one</th><th>two</th></tr></thead>+  <tbody><tr><td>1</td><td>2</td></tr></tbody>+</table>+^D+\begin{longtable}[]{@{}ll@{}}+\caption{Repeated caption.}\tabularnewline+\toprule\noalign{}+one & two \\+\midrule\noalign{}+\endfirsthead+\caption[]{Repeated caption.}\tabularnewline+\toprule\noalign{}+one & two \\+\midrule\noalign{}+\endhead+\bottomrule\noalign{}+\endlastfoot+1 & 2 \\+\end{longtable}+```++```+% pandoc -f html -t latex+<table class="repeat-caption">+  <caption>Repeated caption.</caption>+  <tbody><tr><td>one</td><td>two</td></tr></tbody>+</table>+^D+\begin{longtable}[]{@{}ll@{}}+\caption{Repeated caption.}\tabularnewline+\toprule\noalign{}+\endfirsthead+\caption[]{Repeated caption.}\tabularnewline+\toprule\noalign{}+\endhead+\bottomrule\noalign{}+\endlastfoot+one & two \\+\end{longtable}+```++```+% pandoc -f html -t latex --table-caption-position=below+<table class="repeat-caption">+  <caption>Repeated caption.</caption>+  <thead><tr><th>one</th><th>two</th></tr></thead>+  <tbody><tr><td>1</td><td>2</td></tr></tbody>+</table>+^D+\begin{longtable}[]{@{}ll@{}}+\toprule\noalign{}+one & two \\+\midrule\noalign{}+\endhead+\bottomrule\noalign{}+\tabularnewline+\caption[]{Repeated caption.}+\endfoot+\bottomrule\noalign{}+\tabularnewline+\caption{Repeated caption.}+\endlastfoot+1 & 2 \\+\end{longtable}+```
test/command/5367.md view
@@ -22,10 +22,11 @@  \begin{longtable}[]{@{}   >{\centering\arraybackslash}p{(\linewidth - 0\tabcolsep) * \real{0.1667}}@{}}-\caption[Sample table.]{Sample table.\footnotemark{}}\tabularnewline+\caption[Sample table.]{Sample+table.\pandocfootnotemark{1}}\tabularnewline \toprule\noalign{} \begin{minipage}[b]{\linewidth}\centering-Fruit\footnotemark{}+Fruit\pandocfootnotemark{2} \end{minipage} \\ \midrule\noalign{} \endfirsthead@@ -37,14 +38,11 @@ \endhead \bottomrule\noalign{} \endlastfoot-Bans\footnotemark{} \\+Bans\pandocfootnotemark{3} \\ \end{longtable}-\addtocounter{footnote}{-2}-\footnotetext{caption footnote}-\addtocounter{footnote}{1}-\footnotetext{header footnote}-\addtocounter{footnote}{1}-\footnotetext{table cell footnote}+\pandocfootnotetext{1}{caption footnote}+\pandocfootnotetext{2}{header footnote}+\pandocfootnotetext{3}{table cell footnote}  dolly\footnote{doc footnote} ```
test/command/5476.md view
@@ -5,8 +5,8 @@ \begin{figure} \centering \pandocbounded{\includegraphics[keepaspectratio,alt={moon}]{test/lalune.jpg}}-\caption[moon]{moon\footnotemark{}}+\caption[moon]{moon\pandocfootnotemark{1}} \end{figure}-\footnotetext{the moon}+\pandocfootnotetext{1}{the moon} ``` 
test/command/5753.md view
@@ -11,8 +11,8 @@ end ^D [ Para-    [ Math DisplayMath "q_3\n+ 4"-    , Math DisplayMath "- 5 +\nq_5"+    [ Math DisplayMath "q_3\n + 4"+    , Math DisplayMath "- 5 +\n  q_5"     ] , Para [ Str "end" ] ]
test/command/7691.docx view

binary file changed (11923 → 2565 bytes)

+ test/command/7744.md view
@@ -0,0 +1,62 @@+ANSI escape sequences are stripped from code in output cells+unless the output is ipynb (#5633).  This used to take time+quadratic in the length of the output (#7744).++```+% pandoc -f ipynb -t native+{+ "cells": [+  {+   "cell_type": "code",+   "execution_count": 1,+   "metadata": {},+   "outputs": [+    {+     "name": "stdout",+     "output_type": "stream",+     "text": [+      "\u001b[1;31mError\u001b[0m: café 日本\n",+      "[0m and \u001b(B are kept\n",+      "\u001b[32m\u001b[1mok\u001b[0m\u001b[0m\n",+      "\u001b[2K\n",+      "loading model\n",+      "\u001b[31 \u001b[0m tail\n",+      "\u001b[1\u001b(Bm end\n"+     ]+    },+    {+     "name": "stderr",+     "output_type": "stream",+     "text": [+      "cut \u001b[31 at the end"+     ]+    }+   ],+   "source": [+    "print(x)"+   ]+  }+ ],+ "metadata": {},+ "nbformat": 4,+ "nbformat_minor": 5+}+^D+[ Div+    ( ""+    , [ "cell" , "code" ]+    , [ ( "execution_count" , "1" ) ]+    )+    [ CodeBlock ( "" , [ "python" ] , [] ) "print(x)"+    , Div+        ( "" , [ "output" , "stream" , "stdout" ] , [] )+        [ CodeBlock+            ( "" , [] , [] )+            "Error: caf\233 \26085\26412\n[0m and \ESC(B are kept\nok\nodel\n tail\n end\n"+        ]+    , Div+        ( "" , [ "output" , "stream" , "stderr" ] , [] )+        [ CodeBlock ( "" , [] , [] ) "cut " ]+    ]+]+```
test/command/8240.md view
@@ -7,8 +7,8 @@ ^D \begin{description} \tightlist-\item[Header\footnotemark{}:]+\item[Header\pandocfootnotemark{1}:] Some other text. \end{description}-\footnotetext{A footnote.}+\pandocfootnotetext{1}{A footnote.} ```
test/command/9002.docx view

binary file changed (12631 → 3247 bytes)

+ test/command/9183.docx view

binary file changed (absent → 3761 bytes)

+ test/command/9183.md view
@@ -0,0 +1,10 @@+Adjacent "Source Code" paragraphs become one code block with `+styles`,+as they do without it:+```+% pandoc -f docx+styles -t native command/9183.docx+^D+[ Div+    ( "" , [] , [ ( "custom-style" , "Source Code" ) ] )+    [ CodeBlock ( "" , [] , [] ) "x = 1\ny = x + 1" ]+]+```
+ test/command/9222.docx view

binary file changed (absent → 2856 bytes)

+ test/command/9222.md view
@@ -0,0 +1,10 @@+A list whose abstract numbering only has a `w:numStyleLink` takes its+levels from the abstract numbering with the matching `w:styleLink`:+```+% pandoc -f docx -t native command/9222.docx+^D+[ OrderedList+    ( 1 , Decimal , Period )+    [ [ Plain [ Str "one" ] ] , [ Plain [ Str "two" ] ] ]+]+```
test/command/9358.docx view

binary file changed (13439 → 3687 bytes)

test/command/9391.docx view

binary file changed (13701 → 4078 bytes)

test/command/9603.docx view

binary file changed (13799 → 3254 bytes)

+ test/command/epub-invalid-date.md view
@@ -0,0 +1,15 @@+Dates that cannot be normalized are skipped, so that `dc:date`+is not empty.++```+% pandoc -t epub3 -o - | pandoc -f epub -t plain -s+---+title: T+date: [10 septembre 2026, 2026-09-10]+---+^D+T+2026-09-10++T+```
+ test/command/epub-title-header-id.md view
@@ -0,0 +1,22 @@+The identifier of the title header added before initial content+must not clash with an existing one, even inside a div.++```+% pandoc -t epub3 --metadata title=Intro -o - | pandoc -f epub -t html+See [Intro](#intro).++::: box+# Intro+:::+^D+<p><span id="title_page.xhtml"></span></p>+<p><span id="ch001.xhtml"></span></p>+<section id="ch001.xhtml_intro-1" class="unnumbered level1">+<h1 class="unnumbered">Intro</h1>+<p>See <a href="#ch002.xhtml_intro">Intro</a>.</p>+</section>+<p><span id="ch002.xhtml"></span></p>+<section id="ch002.xhtml_intro" class="level1 box">+<h1>Intro</h1>+</section>+```
test/command/pandoc-citeproc-119.md view
@@ -24,7 +24,7 @@  ::: {#ref-averroes/hannes .csl-entry} Averroes. (1892). *Des Averroës Abhandlung: "Über die Möglichkeit der-Conjunktion" oder "Über den materiellen Intellekt"*. (L. Hannes, Ed. &+Conjunktion" oder "Über den materiellen Intellekt."* (L. Hannes, Ed. & Trans.). Halle an der Saale: C. A. Kaemmerer. ::: 
test/docx/0_level_headers.docx view

binary file changed (35146 → 13802 bytes)

test/docx/adjacent_codeblocks.docx view

binary file changed (22264 → 6782 bytes)

test/docx/already_auto_ident.docx view

binary file changed (8463 → 3413 bytes)

test/docx/alternate_document_path.docx view

binary file changed (11582 → 2924 bytes)

test/docx/anchor_header_after_anchor.docx view

binary file changed (27098 → 7785 bytes)

test/docx/block_quotes.docx view

binary file changed (12153 → 3449 bytes)

test/docx/char_styles.docx view

binary file changed (18059 → 2649 bytes)

test/docx/codeblock.docx view

binary file changed (8465 → 3363 bytes)

test/docx/comments.docx view

binary file changed (17109 → 4929 bytes)

test/docx/comments_warning.docx view

binary file changed (17078 → 5058 bytes)

test/docx/compact-style-removal.docx view

binary file changed (9951 → 3822 bytes)

test/docx/cross_reference.docx view

binary file changed (14978 → 3061 bytes)

+ test/docx/custom_xml.docx view

binary file changed (absent → 995 bytes)

+ test/docx/custom_xml.native view
@@ -0,0 +1,36 @@+[ Para+    [ Str "This"+    , Space+    , Str "is"+    , Space+    , Str "some"+    , Space+    , Str "sample"+    , Space+    , Str "content"+    , Space+    , Str "that"+    , Space+    , Str "will"+    , Space+    , Str "be"+    , Space+    , Str "wrapped"+    , Space+    , Str "inside"+    , Space+    , Str "a"+    , Space+    , Str "customXml"+    , Space+    , Str "element"+    , Space+    , Str "in"+    , Space+    , Str "the"+    , Space+    , Str "OpenXML"+    , Space+    , Str "structure."+    ]+]
test/docx/deep_normalize.docx view

binary file changed (18131 → 3446 bytes)

test/docx/definition_list.docx view

binary file changed (9174 → 3357 bytes)

test/docx/diagram.docx view

binary file changed (22716 → 10159 bytes)

test/docx/double_strikeout.docx view

binary file changed (13825 → 3032 bytes)

test/docx/drop_cap.docx view

binary file changed (15671 → 2531 bytes)

test/docx/dummy_item_after_list_item.docx view

binary file changed (70197 → 5664 bytes)

test/docx/dummy_item_after_paragraph.docx view

binary file changed (70234 → 5695 bytes)

test/docx/empty_field.docx view

binary file changed (14312 → 4004 bytes)

test/docx/enumerated_headings.docx view

binary file changed (12539 → 3796 bytes)

test/docx/german_styled_lists.docx view

binary file changed (43957 → 6699 bytes)

test/docx/hanging_indent.docx view

binary file changed (14754 → 2240 bytes)

test/docx/headers.docx view

binary file changed (18926 → 2701 bytes)

test/docx/i18n_blocks.docx view

binary file changed (13680 → 4188 bytes)

test/docx/image.docx view

binary file changed (35407 → 25821 bytes)

test/docx/image_vml.docx view

binary file changed (23559 → 18373 bytes)

test/docx/image_vml_as_object.docx view

binary file changed (74199 → 16180 bytes)

test/docx/image_with_textbox_caption.docx view

binary file changed (19882 → 9793 bytes)

test/docx/inline_code.docx view

binary file changed (8379 → 3226 bytes)

test/docx/inline_formatting.docx view

binary file changed (20047 → 2739 bytes)

test/docx/instrText_hyperlink.docx view

binary file changed (13628 → 3485 bytes)

binary file changed (16269 → 4146 bytes)

binary file changed (15874 → 4982 bytes)

test/docx/links.docx view

binary file changed (32356 → 3164 bytes)

test/docx/lists-compact.docx view

binary file changed (9952 → 3822 bytes)

test/docx/lists.docx view

binary file changed (9473 → 3800 bytes)

test/docx/lists_continuing.docx view

binary file changed (17717 → 8217 bytes)

test/docx/lists_level_override.docx view

binary file changed (16735 → 5781 bytes)

test/docx/lists_restart_8367.docx view

binary file changed (22718 → 5760 bytes)

test/docx/lists_restarting.docx view

binary file changed (18189 → 8431 bytes)

test/docx/lists_sublist_reset.docx view

binary file changed (12757 → 3515 bytes)

test/docx/mendeley_citations.docx view

binary file changed (17716 → 5554 bytes)

test/docx/metadata.docx view

binary file changed (26077 → 3901 bytes)

test/docx/metadata_after_normal.docx view

binary file changed (38654 → 4025 bytes)

test/docx/nested_anchors_in_header.docx view

binary file changed (17535 → 4112 bytes)

test/docx/nested_instrText.docx view

binary file changed (14112 → 3767 bytes)

test/docx/nested_sdt.docx view

binary file changed (11694 → 2425 bytes)

test/docx/nested_smart_tags.docx view

binary file changed (13641 → 4302 bytes)

test/docx/normalize.docx view

binary file changed (14672 → 3009 bytes)

test/docx/notes.docx view

binary file changed (19056 → 4219 bytes)

test/docx/numbered_header.docx view

binary file changed (15467 → 3415 bytes)

test/docx/overlapping_targets.docx view

binary file changed (13747 → 3039 bytes)

test/docx/pageref.docx view

binary file changed (14431 → 3386 bytes)

test/docx/paragraph_insertion_deletion.docx view

binary file changed (12066 → 2470 bytes)

test/docx/relative_indentation_blockquotes.docx view

binary file changed (12655 → 3141 bytes)

test/docx/sdt_elements.docx view

binary file changed (29409 → 9269 bytes)

test/docx/sdt_in_footnote.docx view

binary file changed (26716 → 8922 bytes)

test/docx/special_punctuation.docx view

binary file changed (8408 → 3081 bytes)

test/docx/table_captions_no_field.docx view

binary file changed (40487 → 7873 bytes)

test/docx/table_captions_with_field.docx view

binary file changed (41091 → 8515 bytes)

test/docx/table_gridbefore.docx view

binary file changed (13143 → 6136 bytes)

test/docx/table_header_rowspan.docx view

binary file changed (15826 → 5519 bytes)

test/docx/table_one_header_row.docx view

binary file changed (12185 → 2815 bytes)

test/docx/table_one_row.docx view

binary file changed (12148 → 2801 bytes)

test/docx/table_variable_width.docx view

binary file changed (10006 → 3202 bytes)

test/docx/table_with_list_cell.docx view

binary file changed (19028 → 3798 bytes)

test/docx/tables.docx view

binary file changed (31751 → 3892 bytes)

test/docx/tabs.docx view

binary file changed (12919 → 2389 bytes)

test/docx/task_list.docx view

binary file changed (10749 → 4025 bytes)

test/docx/text_in_shape_format.docx view

binary file changed (42504 → 14717 bytes)

test/docx/textbox_image.docx view

binary file changed (40379 → 10886 bytes)

test/docx/textbox_image_duplicate_encoding.docx view

binary file changed (36750 → 10999 bytes)

test/docx/track_changes_deletion.docx view

binary file changed (13350 → 2451 bytes)

test/docx/track_changes_edge_spaces.docx view

binary file changed (1093 → 1082 bytes)

test/docx/track_changes_insertion.docx view

binary file changed (12956 → 2425 bytes)

test/docx/track_changes_move.docx view

binary file changed (26151 → 2590 bytes)

test/docx/track_changes_paragraph_mark_in_note.docx view

binary file changed (1863 → 1862 bytes)

test/docx/track_changes_scrubbed_metadata.docx view

binary file changed (13325 → 3395 bytes)

test/docx/trailing_spaces_in_formatting.docx view

binary file changed (12916 → 2385 bytes)

test/docx/trim_last_inline.docx view

binary file changed (4179 → 2096 bytes)

test/docx/unicode.docx view

binary file changed (11506 → 2605 bytes)

test/docx/unused_anchors.docx view

binary file changed (13276 → 2754 bytes)

test/docx/verbatim_subsuper.docx view

binary file changed (10353 → 4028 bytes)

test/docx/zotero_citations.docx view

binary file changed (14943 → 4656 bytes)

+ test/odt/fodt-native/styles-off.native view
@@ -0,0 +1,88 @@+[ Header+    1+    ( "plain-heading" , [] , [] )+    [ Str "Plain" , Space , Str "Heading" ]+, Header+    1+    ( "custom-title" , [] , [] )+    [ Str "Custom" , Space , Str "Title" ]+, Para [ Str "Standard" , Space , Str "paragraph." ]+, Para [ Str "Body" , Space , Str "text." ]+, Para+    [ Strong+        [ Str "Bold" , Space , Str "body" , Space , Str "text." ]+    ]+, Para+    [ Emph+        [ Str "A"+        , Space+        , Str "Bird"+        , Space+        , Str "came"+        , Space+        , Str "down"+        , Space+        , Str "the"+        , Space+        , Str "Walk"+        ]+    ]+, Para+    [ Str "Some"+    , Space+    , Str "keyword,"+    , Space+    , Emph [ Str "emphatic" ]+    , Str ","+    , Space+    , Strong [ Str "bold" ]+    , Space+    , Str "and"+    , Space+    , Code ( "" , [] , [] ) "code"+    , Space+    , Str "text."+    ]+, Para+    [ Str "An"+    , Space+    , Emph [ Str "emphasized" ]+    , Space+    , Str "word."+    ]+, CodeBlock ( "" , [] , [] ) "code line 1\ncode line 2"+, OrderedList+    ( 1 , DefaultStyle , DefaultDelim )+    [ [ Plain [ Str "item" , Space , Str "one" ] ]+    , [ Plain [ Str "item" , Space , Str "two" ] ]+    ]+, Table+    ( "" , [] , [] )+    (Caption Nothing [])+    [ ( AlignDefault , ColWidthDefault )+    , ( AlignDefault , ColWidthDefault )+    ]+    (TableHead ( "" , [] , [] ) [])+    [ TableBody+        ( "" , [] , [] )+        (RowHeadColumns 0)+        []+        [ Row+            ( "" , [] , [] )+            [ Cell+                ( "" , [] , [] )+                AlignDefault+                (RowSpan 1)+                (ColSpan 1)+                [ Plain [ Str "cell" ] ]+            , Cell+                ( "" , [] , [] )+                AlignDefault+                (RowSpan 1)+                (ColSpan 1)+                [ Plain [ Emph [ Str "poem" ] ] ]+            ]+        ]+    ]+    (TableFoot ( "" , [] , [] ) [])+]
+ test/odt/fodt-native/styles.native view
@@ -0,0 +1,117 @@+[ Header+    1+    ( "plain-heading" , [] , [] )+    [ Str "Plain" , Space , Str "Heading" ]+, Header+    1+    ( "custom-title"+    , []+    , [ ( "custom-style" , "Chapter Title" ) ]+    )+    [ Str "Custom" , Space , Str "Title" ]+, Para [ Str "Standard" , Space , Str "paragraph." ]+, Div+    ( "" , [] , [ ( "custom-style" , "Text body" ) ] )+    [ Para [ Str "Body" , Space , Str "text." ] ]+, Div+    ( "" , [] , [ ( "custom-style" , "Text body" ) ] )+    [ Para+        [ Strong+            [ Str "Bold" , Space , Str "body" , Space , Str "text." ]+        ]+    ]+, Div+    ( "" , [] , [ ( "custom-style" , "Poetry" ) ] )+    [ Para+        [ Str "A"+        , Space+        , Str "Bird"+        , Space+        , Str "came"+        , Space+        , Str "down"+        , Space+        , Str "the"+        , Space+        , Str "Walk"+        ]+    ]+, Para+    [ Str "Some"+    , Space+    , Span+        ( "" , [] , [ ( "custom-style" , "Keyword" ) ] )+        [ Str "keyword" ]+    , Str ","+    , Space+    , Span+        ( "" , [] , [ ( "custom-style" , "Emphatic(x)" ) ] )+        [ Str "emphatic" ]+    , Str ","+    , Space+    , Strong [ Str "bold" ]+    , Space+    , Str "and"+    , Space+    , Code ( "" , [] , [] ) "code"+    , Space+    , Str "text."+    ]+, Div+    ( "" , [] , [ ( "custom-style" , "Undefined Style" ) ] )+    [ Para+        [ Str "An"+        , Space+        , Emph [ Str "emphasized" ]+        , Space+        , Str "word."+        ]+    ]+, Div+    ( "" , [] , [ ( "custom-style" , "Preformatted Text" ) ] )+    [ CodeBlock ( "" , [] , [] ) "code line 1\ncode line 2" ]+, OrderedList+    ( 1 , DefaultStyle , DefaultDelim )+    [ [ Plain [ Str "item" , Space , Str "one" ] ]+    , [ Plain+          [ Str "item"+          , Space+          , Span+              ( "" , [] , [ ( "custom-style" , "Keyword" ) ] )+              [ Str "two" ]+          ]+      ]+    ]+, Table+    ( "" , [] , [] )+    (Caption Nothing [])+    [ ( AlignDefault , ColWidthDefault )+    , ( AlignDefault , ColWidthDefault )+    ]+    (TableHead ( "" , [] , [] ) [])+    [ TableBody+        ( "" , [] , [] )+        (RowHeadColumns 0)+        []+        [ Row+            ( "" , [] , [] )+            [ Cell+                ( "" , [] , [] )+                AlignDefault+                (RowSpan 1)+                (ColSpan 1)+                [ Plain [ Str "cell" ] ]+            , Cell+                ( "" , [] , [] )+                AlignDefault+                (RowSpan 1)+                (ColSpan 1)+                [ Div+                    ( "" , [] , [ ( "custom-style" , "Poetry" ) ] )+                    [ Para [ Str "poem" ] ]+                ]+            ]+        ]+    ]+    (TableFoot ( "" , [] , [] ) [])+]
+ test/odt/fodt/styles.fodt view
@@ -0,0 +1,64 @@+<?xml version="1.0" encoding="UTF-8"?>+<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">+ <office:styles>+  <style:style style:name="Standard" style:family="paragraph" style:class="text"/>+  <style:style style:name="Heading" style:family="paragraph" style:parent-style-name="Standard"/>+  <style:style style:name="Heading_20_1" style:display-name="Heading 1" style:family="paragraph" style:parent-style-name="Heading"/>+  <style:style style:name="Chapter_20_Title" style:display-name="Chapter Title" style:family="paragraph" style:parent-style-name="Heading"/>+  <style:style style:name="Text_20_body" style:display-name="Text body" style:family="paragraph" style:parent-style-name="Standard"/>+  <style:style style:name="Poetry" style:family="paragraph" style:parent-style-name="Standard">+   <style:text-properties fo:font-style="italic"/>+  </style:style>+  <style:style style:name="Preformatted_20_Text" style:display-name="Preformatted Text" style:family="paragraph" style:parent-style-name="Standard"/>+  <style:style style:name="Table_20_Contents" style:display-name="Table Contents" style:family="paragraph" style:parent-style-name="Standard"/>+  <style:style style:name="Keyword" style:family="text"/>+  <style:style style:name="Emphatic_28_x_29_" style:family="text">+   <style:text-properties fo:font-style="italic"/>+  </style:style>+  <style:style style:name="Source_20_Text" style:display-name="Source Text" style:family="text"/>+  <style:style style:name="Emphasis" style:family="text">+   <style:text-properties fo:font-style="italic"/>+  </style:style>+ </office:styles>+ <office:automatic-styles>+  <style:style style:name="P1" style:family="paragraph" style:parent-style-name="Text_20_body">+   <style:text-properties fo:font-weight="bold"/>+  </style:style>+  <style:style style:name="T1" style:family="text" style:parent-style-name="Keyword"/>+  <style:style style:name="T2" style:family="text">+   <style:text-properties fo:font-weight="bold"/>+  </style:style>+ </office:automatic-styles>+ <office:body>+  <office:text>+   <text:h text:style-name="Heading_20_1" text:outline-level="1">Plain Heading</text:h>+   <text:h text:style-name="Chapter_20_Title" text:outline-level="1">Custom Title</text:h>+   <text:p text:style-name="Standard">Standard paragraph.</text:p>+   <text:p text:style-name="Text_20_body">Body text.</text:p>+   <text:p text:style-name="P1">Bold body text.</text:p>+   <text:p text:style-name="Poetry">A Bird came down the Walk</text:p>+   <text:p text:style-name="Standard">Some <text:span text:style-name="T1">keyword</text:span>, <text:span text:style-name="Emphatic_28_x_29_">emphatic</text:span>, <text:span text:style-name="T2">bold</text:span> and <text:span text:style-name="Source_20_Text">code</text:span> text.</text:p>+   <text:p text:style-name="Undefined_20_Style">An <text:span text:style-name="Emphasis">emphasized</text:span> word.</text:p>+   <text:p text:style-name="Preformatted_20_Text">code line 1</text:p>+   <text:p text:style-name="Preformatted_20_Text">code line 2</text:p>+   <text:list>+    <text:list-item>+     <text:p text:style-name="Text_20_body">item one</text:p>+    </text:list-item>+    <text:list-item>+     <text:p text:style-name="Text_20_body">item <text:span text:style-name="Keyword">two</text:span></text:p>+    </text:list-item>+   </text:list>+   <table:table>+    <table:table-row>+     <table:table-cell>+      <text:p text:style-name="Table_20_Contents">cell</text:p>+     </table:table-cell>+     <table:table-cell>+      <text:p text:style-name="Poetry">poem</text:p>+     </table:table-cell>+    </table:table-row>+   </table:table>+  </office:text>+ </office:body>+</office:document>
test/odt/odt/blockquote.odt view

binary file changed (8594 → 3448 bytes)

test/odt/odt/blockquote2.odt view

binary file changed (9024 → 5916 bytes)

test/odt/odt/bold.odt view

binary file changed (10377 → 5135 bytes)

test/odt/odt/citation.odt view

binary file changed (10842 → 5568 bytes)

test/odt/odt/endnote.odt view

binary file changed (10950 → 5634 bytes)

test/odt/odt/expression.odt view

binary file changed (10916 → 5718 bytes)

test/odt/odt/expressionUnevaluated.odt view

binary file changed (10829 → 5715 bytes)

test/odt/odt/externalLink.odt view

binary file changed (10735 → 5419 bytes)

test/odt/odt/footnote.odt view

binary file changed (10843 → 5530 bytes)

test/odt/odt/formula.odt view

binary file changed (14252 → 6625 bytes)

test/odt/odt/headers.odt view

binary file changed (10515 → 5025 bytes)

test/odt/odt/hiddenTextByStyle.odt view

binary file changed (10798 → 5627 bytes)

test/odt/odt/hiddenTextByVariable.odt view

binary file changed (10788 → 5617 bytes)

test/odt/odt/horizontalRule.odt view

binary file changed (10130 → 4573 bytes)

test/odt/odt/image.odt view

binary file changed (33360 → 22364 bytes)

test/odt/odt/imageIndex.odt view

binary file changed (34417 → 23063 bytes)

test/odt/odt/imageRelative.odt view

binary file changed (16213 → 4245 bytes)

test/odt/odt/imageWithCaption.odt view

binary file changed (33811 → 22727 bytes)

test/odt/odt/inlinedCode.odt view

binary file changed (10141 → 5145 bytes)

test/odt/odt/italic.odt view

binary file changed (10426 → 5192 bytes)

test/odt/odt/listBlocks.odt view

binary file changed (10565 → 5117 bytes)

test/odt/odt/listContinueNumbering.odt view

binary file changed (12263 → 4366 bytes)

test/odt/odt/listContinueNumbering2.odt view

binary file changed (9300 → 4651 bytes)

test/odt/odt/listContinueNumbering3.odt view

binary file changed (10880 → 7301 bytes)

test/odt/odt/orderedListHeader.odt view

binary file changed (11565 → 5993 bytes)

test/odt/odt/orderedListMixed.odt view

binary file changed (11970 → 5557 bytes)

test/odt/odt/orderedListRoman.odt view

binary file changed (11730 → 5572 bytes)

test/odt/odt/orderedListSimple.odt view

binary file changed (11817 → 5598 bytes)

test/odt/odt/paragraph.odt view

binary file changed (8538 → 3352 bytes)

test/odt/odt/preformattedText.odt view

binary file changed (7342 → 3976 bytes)

test/odt/odt/preformattedTextParentStyle.odt view

binary file changed (7150 → 6213 bytes)

test/odt/odt/referenceAllInOne.odt view

binary file changed (10878 → 5077 bytes)

test/odt/odt/referenceToChapter.odt view

binary file changed (10487 → 4629 bytes)

test/odt/odt/referenceToListItem.odt view

binary file changed (10855 → 5137 bytes)

test/odt/odt/referenceToText.odt view

binary file changed (10208 → 4602 bytes)

test/odt/odt/simpleTable.odt view

binary file changed (10705 → 5409 bytes)

test/odt/odt/simpleTableWithCaption.odt view

binary file changed (10396 → 5264 bytes)

test/odt/odt/simpleTableWithHeader.odt view

binary file changed (10377 → 4397 bytes)

test/odt/odt/simpleTableWithMultipleHeaderRows.odt view

binary file changed (10173 → 4275 bytes)

test/odt/odt/sourceText.odt view

binary file changed (6805 → 5887 bytes)

test/odt/odt/strikeout.odt view

binary file changed (10582 → 5310 bytes)

test/odt/odt/tab.odt view

binary file changed (10170 → 4948 bytes)

test/odt/odt/table.odt view

binary file changed (10763 → 5381 bytes)

test/odt/odt/tableWithCaption.odt view

binary file changed (10623 → 4810 bytes)

test/odt/odt/tableWithContents.odt view

binary file changed (8817 → 4254 bytes)

test/odt/odt/tableWithSpans.odt view

binary file changed (10695 → 4401 bytes)

test/odt/odt/textMixedStyles.odt view

binary file changed (10571 → 4621 bytes)

test/odt/odt/trackedChanges.odt view

binary file changed (11135 → 5808 bytes)

test/odt/odt/underlined.odt view

binary file changed (10513 → 5253 bytes)

test/odt/odt/unicode.odt view

binary file changed (11787 → 6124 bytes)

test/odt/odt/unorderedList.odt view

binary file changed (9505 → 4097 bytes)

test/odt/odt/unorderedListHeader.odt view

binary file changed (9920 → 4651 bytes)

test/odt/odt/variable.odt view

binary file changed (10851 → 5680 bytes)

test/pptx-reader/basic.native view
@@ -1,149 +1,260 @@-[ Header 2 ( "slide-1" , [] , [] ) [ Str "LLMs" ]-, BulletList-    [ [ Plain-          [ Str-              "Provider  \61664 Available LLMs \8211 who manages? How?"+Pandoc+  Meta { unMeta = fromList [] }+  [ Header 2 ( "slide-1" , [] , [] ) [ Str "LLMs" ]+  , BulletList+      [ [ Plain+            [ Str "Provider"+            , Space+            , Str "\61664"+            , Space+            , Str "Available"+            , Space+            , Str "LLMs"+            , Space+            , Str "\8211"+            , Space+            , Str "who"+            , Space+            , Str "manages?"+            , Space+            , Str "How?"+            ]+        ]+      , [ Plain+            [ Str "EW"+            , Space+            , Str "maintained"+            , Space+            , Str "list"+            , Space+            , Str "of"+            , Space+            , Str "\8220approved\8221"+            , Space+            , Str "LLMs"+            , Space+            , Str "for"+            , Space+            , Str "Universal"+            , Space+            , Str "workers"+            ]+        ]+      , [ Plain+            [ Str "Rebuilding"+            , Space+            , Str "of"+            , Space+            , Str "UWs"+            , Space+            , Str "to"+            , Space+            , Str "the"+            , Space+            , Str "\8220Newgen\8221"+            , Space+            , Str "thing"+            , Space+            , Str "completely"+            ]+        ]+      , [ Plain [ Str "Streaming" , Space , Str "support" ] ]+      , [ Plain+            [ Str "Multimodal"+            , Space+            , Str "(voice"+            , Space+            , Str "streaming)"+            , Space+            , Str "models?"+            ]+        ]+      ]+  , Header+      2+      ( "slide-2" , [] , [] )+      [ Str "Everworker"+      , Space+      , Str "venn"+      , Space+      , Str "diagram"+      ]+  , Para [ Strong [ Str "SKILLS" ] ]+  , Para []+  , Para+      [ Strong+          [ Str "Specialized"+          , Space+          , Str "Workers"+          , Space+          , Str "/"+          , Space+          , Str "Workflows:"           ]       ]-    , [ Plain-          [ Str-              "EW maintained list of \8220approved\8221 LLMs for Universal workers"+  , Para []+  , Para+      [ Str "n8n,"+      , Space+      , Str "UI"+      , Space+      , Str "Path,"+      , Space+      ]+  , Para [ Str "other" , Space , Str "RPA" ]+  , Para [ Strong [ Str "BRAINS" ] ]+  , Para []+  , Para+      [ Strong+          [ Str "Universal"+          , Space+          , Str "Workers"+          , Space+          , Str "/"+          , Space+          , Str "AI"+          , Space+          , Str "Agents:"           ]       ]-    , [ Plain-          [ Str-              "Rebuilding of UWs to the \8220Newgen\8221 thing completely"+  , Para []+  , Para [ Str "openai," , Space , Str "anthropic," ]+  , Para+      [ Str "Crew"+      , Space+      , Str "AI,"+      , Space+      , Str "other"+      , Space+      ]+  , Para [ Str "\8220AI" , Space , Str "natives\8221" ]+  , Para [ Strong [ Str "KNOWLEDGE" ] , Space ]+  , Para []+  , Para [ Strong [ Str "Data" , Space , Str "/" ] , Space ]+  , Para [ Strong [ Str "RAG" , Space , Str "Pipelines" ] ]+  , Para []+  , Para+      [ Str "Vector"+      , Space+      , Str "DBs,"+      , Space+      , Str "specialized"+      , Space+      , Str "data"+      , Space+      , Str "prep"+      , Space+      , Str "vendors,"+      , Space+      , Str "\8230"+      ]+  , Para [ Str "glean" ]+  , Para [ Strong [ Str "EW" ] ]+  , Header 2 ( "slide-3" , [] , [] ) [ Str "Table" ]+  , Table+      ( "" , [] , [] )+      (Caption Nothing [])+      [ ( AlignDefault , ColWidthDefault )+      , ( AlignDefault , ColWidthDefault )+      , ( AlignDefault , ColWidthDefault )+      ]+      (TableHead+         ( "" , [] , [] )+         [ Row+             ( "" , [] , [] )+             [ Cell+                 ( "" , [] , [] )+                 AlignDefault+                 (RowSpan 1)+                 (ColSpan 1)+                 [ Plain [ Str "Col1" ] ]+             , Cell+                 ( "" , [] , [] )+                 AlignDefault+                 (RowSpan 1)+                 (ColSpan 1)+                 [ Plain [ Str "Col2" ] ]+             , Cell+                 ( "" , [] , [] )+                 AlignDefault+                 (RowSpan 1)+                 (ColSpan 1)+                 [ Plain [ Str "Col3" ] ]+             ]+         ])+      [ TableBody+          ( "" , [] , [] )+          (RowHeadColumns 0)+          []+          [ Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "Name" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "Anton" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "Antich" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "Age" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "23" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "years" ] ]+              ]           ]       ]-    , [ Plain [ Str "Streaming support" ] ]-    , [ Plain [ Str "Multimodal (voice streaming) models?" ] ]-    ]-, Header-    2-    ( "slide-2" , [] , [] )-    [ Str "Everworker   venn   diagram" ]-, Para [ Str "SKILLS" ]-, Para [ Str "" ]-, Para [ Str "Specialized Workers / Workflows:" ]-, Para [ Str "" ]-, Para [ Str "n8n, UI Path, " ]-, Para [ Str "other RPA" ]-, Para [ Str "BRAINS" ]-, Para [ Str "" ]-, Para [ Str "Universal Workers / AI Agents:" ]-, Para [ Str "" ]-, Para [ Str "openai , anthropic," ]-, Para [ Str "Crew AI, other " ]-, Para [ Str "\8220AI natives\8221" ]-, Para [ Str "KNOWLEDGE " ]-, Para [ Str "" ]-, Para [ Str "Data / " ]-, Para [ Str "RAG Pipelines" ]-, Para [ Str "" ]-, Para-    [ Str "Vector DBs, specialized data prep vendors, \8230" ]-, Para [ Str "glean" ]-, Para [ Str "EW" ]-, Header 2 ( "slide-3" , [] , [] ) [ Str "Table" ]-, Table-    ( "" , [] , [] )-    (Caption Nothing [])-    [ ( AlignDefault , ColWidthDefault )-    , ( AlignDefault , ColWidthDefault )-    , ( AlignDefault , ColWidthDefault )-    ]-    (TableHead-       ( "" , [] , [] )-       [ Row-           ( "" , [] , [] )-           [ Cell-               ( "" , [] , [] )-               AlignDefault-               (RowSpan 1)-               (ColSpan 1)-               [ Plain [ Str "Col1" ] ]-           , Cell-               ( "" , [] , [] )-               AlignDefault-               (RowSpan 1)-               (ColSpan 1)-               [ Plain [ Str "Col2" ] ]-           , Cell-               ( "" , [] , [] )-               AlignDefault-               (RowSpan 1)-               (ColSpan 1)-               [ Plain [ Str "Col3" ] ]-           ]-       ])-    [ TableBody-        ( "" , [] , [] )-        (RowHeadColumns 0)-        []-        [ Row-            ( "" , [] , [] )-            [ Cell-                ( "" , [] , [] )-                AlignDefault-                (RowSpan 1)-                (ColSpan 1)-                [ Plain [ Str "Name" ] ]-            , Cell-                ( "" , [] , [] )-                AlignDefault-                (RowSpan 1)-                (ColSpan 1)-                [ Plain [ Str "Anton" ] ]-            , Cell-                ( "" , [] , [] )-                AlignDefault-                (RowSpan 1)-                (ColSpan 1)-                [ Plain [ Str "Antich" ] ]-            ]-        , Row-            ( "" , [] , [] )-            [ Cell-                ( "" , [] , [] )-                AlignDefault-                (RowSpan 1)-                (ColSpan 1)-                [ Plain [ Str "Age" ] ]-            , Cell-                ( "" , [] , [] )-                AlignDefault-                (RowSpan 1)-                (ColSpan 1)-                [ Plain [ Str "23" ] ]-            , Cell-                ( "" , [] , [] )-                AlignDefault-                (RowSpan 1)-                (ColSpan 1)-                [ Plain [ Str "years" ] ]+      (TableFoot ( "" , [] , [] ) [])+  , Para+      [ Image+          ( "" , [] , [] ) [] ( "ppt/media/image1.png" , "Picture 6" )+      ]+  , Header+      2+      ( "slide-4" , [] , [] )+      [ Str "Smart" , Space , Str "Art" ]+  , Div+      ( ""+      , [ "smartart" , "chevron2" ]+      , [ ( "layout" , "chevron2" ) ]+      )+      [ Para [ Strong [ Str "First" ] ]+      , BulletList+          [ [ Plain [ Str "another" ] ]+          , [ Plain [ Str "subtitle" ] ]+          ]+      , Para [ Strong [ Str "Second" ] ]+      , BulletList+          [ [ Plain+                [ Str "and" , Space , Str "yet" , Space , Str "again" ]             ]-        ]-    ]-    (TableFoot ( "" , [] , [] ) [])-, Para-    [ Image-        ( "" , [] , [] ) [] ( "ppt/media/image1.png" , "Picture 6" )-    ]-, Header 2 ( "slide-4" , [] , [] ) [ Str "Smart Art" ]-, Div-    ( ""-    , [ "smartart" , "chevron2" ]-    , [ ( "layout" , "chevron2" ) ]-    )-    [ Para [ Strong [ Str "First" ] ]-    , BulletList-        [ [ Plain [ Str "another" ] ]-        , [ Plain [ Str "subtitle" ] ]-        ]-    , Para [ Strong [ Str "Second" ] ]-    , BulletList-        [ [ Plain [ Str "and yet again" ] ]-        , [ Plain [ Str "yet more" ] ]-        ]-    ]-]+          , [ Plain [ Str "yet" , Space , Str "more" ] ]+          ]+      ]+  ]
test/pptx-reader/basic.pptx view

binary file changed (111674 → 81677 bytes)

+ test/pptx-reader/charts.native view
@@ -0,0 +1,336 @@+Pandoc+  Meta { unMeta = fromList [] }+  [ Header+      2+      ( "slide-1" , [] , [] )+      [ Str "Bar" , Space , Str "chart" ]+  , Div+      ( "" , [ "chart" ] , [ ( "type" , "bar" ) ] )+      [ Table+          ( "" , [] , [] )+          (Caption+             Nothing+             [ Plain+                 [ Str "Growth"+                 , Space+                 , Str "by"+                 , Space+                 , Str "debt"+                 ]+             ])+          [ ( AlignDefault , ColWidthDefault )+          , ( AlignDefault , ColWidthDefault )+          , ( AlignDefault , ColWidthDefault )+          ]+          (TableHead+             ( "" , [] , [] )+             [ Row+                 ( "" , [] , [] )+                 [ Cell+                     ( "" , [] , [] )+                     AlignDefault+                     (RowSpan 1)+                     (ColSpan 1)+                     [ Plain [] ]+                 , Cell+                     ( "" , [] , [] )+                     AlignDefault+                     (RowSpan 1)+                     (ColSpan 1)+                     [ Plain [ Str "2019" ] ]+                 , Cell+                     ( "" , [] , [] )+                     AlignDefault+                     (RowSpan 1)+                     (ColSpan 1)+                     [ Plain [ Str "2020" ] ]+                 ]+             ])+          [ TableBody+              ( "" , [] , [] )+              (RowHeadColumns 0)+              []+              [ Row+                  ( "" , [] , [] )+                  [ Cell+                      ( "" , [] , [] )+                      AlignDefault+                      (RowSpan 1)+                      (ColSpan 1)+                      [ Plain [ Str "Below" , Space , Str "30%" ] ]+                  , Cell+                      ( "" , [] , [] )+                      AlignDefault+                      (RowSpan 1)+                      (ColSpan 1)+                      [ Plain [ Str "4.1" ] ]+                  , Cell+                      ( "" , [] , [] )+                      AlignDefault+                      (RowSpan 1)+                      (ColSpan 1)+                      [ Plain [ Str "3.9" ] ]+                  ]+              , Row+                  ( "" , [] , [] )+                  [ Cell+                      ( "" , [] , [] )+                      AlignDefault+                      (RowSpan 1)+                      (ColSpan 1)+                      [ Plain+                          [ Str "30"+                          , Space+                          , Str "to"+                          , Space+                          , Str "60%"+                          ]+                      ]+                  , Cell+                      ( "" , [] , [] )+                      AlignDefault+                      (RowSpan 1)+                      (ColSpan 1)+                      [ Plain [ Str "2.8" ] ]+                  , Cell+                      ( "" , [] , [] )+                      AlignDefault+                      (RowSpan 1)+                      (ColSpan 1)+                      [ Plain [ Str "2.5" ] ]+                  ]+              , Row+                  ( "" , [] , [] )+                  [ Cell+                      ( "" , [] , [] )+                      AlignDefault+                      (RowSpan 1)+                      (ColSpan 1)+                      [ Plain+                          [ Str "60"+                          , Space+                          , Str "to"+                          , Space+                          , Str "90%"+                          ]+                      ]+                  , Cell+                      ( "" , [] , [] )+                      AlignDefault+                      (RowSpan 1)+                      (ColSpan 1)+                      [ Plain [ Str "2.8" ] ]+                  , Cell+                      ( "" , [] , [] )+                      AlignDefault+                      (RowSpan 1)+                      (ColSpan 1)+                      [ Plain [] ]+                  ]+              , Row+                  ( "" , [] , [] )+                  [ Cell+                      ( "" , [] , [] )+                      AlignDefault+                      (RowSpan 1)+                      (ColSpan 1)+                      [ Plain+                          [ Str "90%"+                          , Space+                          , Str "and"+                          , Space+                          , Str "above"+                          ]+                      ]+                  , Cell+                      ( "" , [] , [] )+                      AlignDefault+                      (RowSpan 1)+                      (ColSpan 1)+                      [ Plain [ Str "-0.1" ] ]+                  , Cell+                      ( "" , [] , [] )+                      AlignDefault+                      (RowSpan 1)+                      (ColSpan 1)+                      [ Plain [ Str "0.4" ] ]+                  ]+              ]+          ]+          (TableFoot ( "" , [] , [] ) [])+      ]+  , Header+      2+      ( "slide-2" , [] , [] )+      [ Str "Line" , Space , Str "chart" ]+  , Div+      ( "" , [ "chart" ] , [ ( "type" , "line" ) ] )+      [ Table+          ( "" , [] , [] )+          (Caption Nothing [])+          [ ( AlignDefault , ColWidthDefault )+          , ( AlignDefault , ColWidthDefault )+          ]+          (TableHead+             ( "" , [] , [] )+             [ Row+                 ( "" , [] , [] )+                 [ Cell+                     ( "" , [] , [] )+                     AlignDefault+                     (RowSpan 1)+                     (ColSpan 1)+                     [ Plain [] ]+                 , Cell+                     ( "" , [] , [] )+                     AlignDefault+                     (RowSpan 1)+                     (ColSpan 1)+                     [ Plain [ Str "Revenue" ] ]+                 ]+             ])+          [ TableBody+              ( "" , [] , [] )+              (RowHeadColumns 0)+              []+              [ Row+                  ( "" , [] , [] )+                  [ Cell+                      ( "" , [] , [] )+                      AlignDefault+                      (RowSpan 1)+                      (ColSpan 1)+                      [ Plain [ Str "Q1" ] ]+                  , Cell+                      ( "" , [] , [] )+                      AlignDefault+                      (RowSpan 1)+                      (ColSpan 1)+                      [ Plain [ Str "10" ] ]+                  ]+              , Row+                  ( "" , [] , [] )+                  [ Cell+                      ( "" , [] , [] )+                      AlignDefault+                      (RowSpan 1)+                      (ColSpan 1)+                      [ Plain [ Str "Q2" ] ]+                  , Cell+                      ( "" , [] , [] )+                      AlignDefault+                      (RowSpan 1)+                      (ColSpan 1)+                      [ Plain [ Str "12" ] ]+                  ]+              , Row+                  ( "" , [] , [] )+                  [ Cell+                      ( "" , [] , [] )+                      AlignDefault+                      (RowSpan 1)+                      (ColSpan 1)+                      [ Plain [ Str "Q3" ] ]+                  , Cell+                      ( "" , [] , [] )+                      AlignDefault+                      (RowSpan 1)+                      (ColSpan 1)+                      [ Plain [ Str "15" ] ]+                  ]+              ]+          ]+          (TableFoot ( "" , [] , [] ) [])+      ]+  , Header+      2+      ( "slide-3" , [] , [] )+      [ Str "Scatter" , Space , Str "chart" ]+  , Div+      ( "" , [ "chart" ] , [ ( "type" , "scatter" ) ] )+      [ Table+          ( "" , [] , [] )+          (Caption Nothing [])+          [ ( AlignDefault , ColWidthDefault )+          , ( AlignDefault , ColWidthDefault )+          ]+          (TableHead+             ( "" , [] , [] )+             [ Row+                 ( "" , [] , [] )+                 [ Cell+                     ( "" , [] , [] )+                     AlignDefault+                     (RowSpan 1)+                     (ColSpan 1)+                     [ Plain [] ]+                 , Cell+                     ( "" , [] , [] )+                     AlignDefault+                     (RowSpan 1)+                     (ColSpan 1)+                     [ Plain [ Str "Trial" ] ]+                 ]+             ])+          [ TableBody+              ( "" , [] , [] )+              (RowHeadColumns 0)+              []+              [ Row+                  ( "" , [] , [] )+                  [ Cell+                      ( "" , [] , [] )+                      AlignDefault+                      (RowSpan 1)+                      (ColSpan 1)+                      [ Plain [ Str "1" ] ]+                  , Cell+                      ( "" , [] , [] )+                      AlignDefault+                      (RowSpan 1)+                      (ColSpan 1)+                      [ Plain [ Str "2.5" ] ]+                  ]+              , Row+                  ( "" , [] , [] )+                  [ Cell+                      ( "" , [] , [] )+                      AlignDefault+                      (RowSpan 1)+                      (ColSpan 1)+                      [ Plain [ Str "2" ] ]+                  , Cell+                      ( "" , [] , [] )+                      AlignDefault+                      (RowSpan 1)+                      (ColSpan 1)+                      [ Plain [ Str "4.0" ] ]+                  ]+              , Row+                  ( "" , [] , [] )+                  [ Cell+                      ( "" , [] , [] )+                      AlignDefault+                      (RowSpan 1)+                      (ColSpan 1)+                      [ Plain [ Str "3" ] ]+                  , Cell+                      ( "" , [] , [] )+                      AlignDefault+                      (RowSpan 1)+                      (ColSpan 1)+                      [ Plain [ Str "5.5" ] ]+                  ]+              ]+          ]+          (TableFoot ( "" , [] , [] ) [])+      ]+  , Header+      2+      ( "slide-4" , [] , [] )+      [ Str "Unreadable" , Space , Str "chart" ]+  , Para+      [ Str+          "[Graphic: other: http://schemas.openxmlformats.org/drawingml/2006/chart]"+      ]+  ]
+ test/pptx-reader/charts.pptx view

binary file changed (absent → 14692 bytes)

+ test/pptx-reader/equations.native view
@@ -0,0 +1,26 @@+Pandoc+  Meta { unMeta = fromList [] }+  [ Header 2 ( "slide-1" , [] , [] ) [ Str "Equations" ]+  , Para+      [ Str "The"+      , Space+      , Str "mean"+      , Space+      , Str "is"+      , Space+      , Math InlineMath "\\bar{x} = \\frac{1}{n}\\sum x_{i}"+      , Space+      , Str "here."+      ]+  , Para [ Math DisplayMath "E = mc^{2}" ]+  , Para+      [ Str "After"+      , Space+      , Str "the"+      , Space+      , Str "equations."+      ]+  , Header 2 ( "slide-2" , [] , [] ) [ Str "Fallback" ]+  , Para+      [ Str "From" , Space , Str "the" , Space , Str "fallback" ]+  ]
+ test/pptx-reader/equations.pptx view

binary file changed (absent → 9712 bytes)

+ test/pptx-reader/formatting.native view
@@ -0,0 +1,65 @@+Pandoc+  Meta { unMeta = fromList [] }+  [ Header 2 ( "slide-1" , [] , [] ) [ Str "Formatting" ]+  , Para+      [ Str "A"+      , Space+      , Strong [ Str "bold" , Space , Str "word" ]+      , Space+      , Str "and"+      , Space+      , Str "an"+      , Space+      , Emph [ Str "italic" ]+      , Space+      , Str "one,"+      , Space+      , Str "not"+      , Space+      , Str "bold"+      , Space+      , Str "and"+      , Space+      , Str "mid"+      , Strong [ Str "word" ]+      ]+  , Para+      [ Str "Area"+      , Space+      , Str "in"+      , Space+      , Str "m"+      , Superscript [ Str "2" ]+      , Str ","+      , Space+      , Str "water"+      , Space+      , Str "is"+      , Space+      , Str "H"+      , Subscript [ Str "2" ]+      , Str "O."+      ]+  , Para+      [ Str "See"+      , Space+      , Link+          ( "" , [] , [] )+          [ Str "the"+          , Space+          , Str "pandoc"+          , Space+          , Strong [ Str "site" ]+          ]+          ( "https://pandoc.org" , "" )+      , Space+      , Str "or"+      , Space+      , Link+          ( "" , [] , [] )+          [ Str "the" , Space , Str "report" ]+          ( "Report%20draft.pdf" , "" )+      , Str "."+      ]+  , Para [ Str "Next" , Space , Str "slide" ]+  ]
+ test/pptx-reader/formatting.pptx view

binary file changed (absent → 28605 bytes)

+ test/pptx-reader/groups.native view
@@ -0,0 +1,14 @@+Pandoc+  Meta { unMeta = fromList [] }+  [ Header 2 ( "slide-1" , [] , [] ) [ Str "Groups" ]+  , Para [ Str "Before" ]+  , Para [ Str "Grouped" ]+  , Para+      [ Image+          ( "" , [] , [] )+          [ Str "image.png" ]+          ( "ppt/media/image1.png" , "Picture 5" )+      ]+  , Para [ Str "Nested" ]+  , Para [ Str "After" ]+  ]
+ test/pptx-reader/groups.pptx view

binary file changed (absent → 8767 bytes)

+ test/pptx-reader/hidden.native view
@@ -0,0 +1,21 @@+Pandoc+  Meta { unMeta = fromList [] }+  [ Header 2 ( "slide-1" , [] , [] ) [ Str "Shown" ]+  , Header+      2 ( "slide-2" , [ "hidden" ] , [] ) [ Str "Hidden" ]+  , Para+      [ Str "Body"+      , Space+      , Str "of"+      , Space+      , Str "the"+      , Space+      , Str "hidden"+      , Space+      , Str "slide"+      ]+  , Header+      2+      ( "slide-3" , [] , [] )+      [ Str "Shown" , Space , Str "explicitly" ]+  ]
+ test/pptx-reader/hidden.pptx view

binary file changed (absent → 9910 bytes)

+ test/pptx-reader/lists-writer.native view
@@ -0,0 +1,26 @@+Pandoc+  Meta { unMeta = fromList [] }+  [ Header 2 ( "slide-1" , [] , [] ) [ Str "Bullets" ]+  , BulletList+      [ [ Plain [ Str "one" ] ]+      , [ Plain [ Str "two" ]+        , BulletList+            [ [ Plain [ Str "nested" , Space , Str "a" ] ]+            , [ Plain [ Str "nested" , Space , Str "b" ] ]+            ]+        ]+      , [ Plain [ Str "three" ] ]+      ]+  , Header 2 ( "slide-2" , [] , [] ) [ Str "Numbers" ]+  , OrderedList+      ( 1 , Decimal , Period )+      [ [ Plain [ Str "first" ] ]+      , [ Plain [ Str "second" ]+        , OrderedList+            ( 1 , LowerAlpha , Period ) [ [ Plain [ Str "inner" ] ] ]+        ]+      , [ Plain [ Str "third" ] ]+      ]+  , Para+      [ Str "After" , Space , Str "the" , Space , Str "list." ]+  ]
+ test/pptx-reader/lists-writer.pptx view

binary file changed (absent → 28558 bytes)

+ test/pptx-reader/lists.native view
@@ -0,0 +1,21 @@+Pandoc+  Meta { unMeta = fromList [] }+  [ Header+      2+      ( "slide-1" , [] , [] )+      [ Str "Inherited" , Space , Str "bullets" ]+  , BulletList+      [ [ Plain [ Str "one" ] ]+      , [ Plain [ Str "two" ]+        , BulletList [ [ Plain [ Str "nested" ] ] ]+        ]+      , [ Plain [ Str "three" ] ]+      ]+  , Para [ Str "no" , Space , Str "bullet" ]+  , Header+      2 ( "slide-2" , [] , [] ) [ Str "Text" , Space , Str "box" ]+  , Para [ Str "plain" ]+  , OrderedList+      ( 3 , LowerAlpha , OneParen )+      [ [ Plain [ Str "third" ] ] , [ Plain [ Str "fourth" ] ] ]+  ]
+ test/pptx-reader/lists.pptx view

binary file changed (absent → 29285 bytes)

+ test/pptx-reader/notes.native view
@@ -0,0 +1,60 @@+Pandoc+  Meta { unMeta = fromList [] }+  [ Header+      2+      ( "slide-1" , [] , [] )+      [ Str "Notes"+      , Space+      , Str "on"+      , Space+      , Str "two"+      , Space+      , Str "paragraphs"+      ]+  , Div+      ( "" , [ "notes" ] , [] )+      [ Para+          [ Str "First"+          , Space+          , Str "paragraph"+          , Space+          , Str "of"+          , Space+          , Str "the"+          , Space+          , Str "notes."+          ]+      , Para [ Str "Second" , Space , Str "paragraph." ]+      ]+  , Header+      2 ( "slide-2" , [] , [] ) [ Str "No" , Space , Str "notes" ]+  , Header+      2+      ( "slide-3" , [] , [] )+      [ Str "Empty" , Space , Str "notes" ]+  , Header+      2+      ( "slide-4" , [ "hidden" ] , [] )+      [ Str "Hidden"+      , Space+      , Str "slide"+      , Space+      , Str "with"+      , Space+      , Str "notes"+      ]+  , Div+      ( "" , [ "notes" ] , [] )+      [ Para+          [ Str "Notes"+          , Space+          , Str "on"+          , Space+          , Str "the"+          , Space+          , Str "hidden"+          , Space+          , Str "slide."+          ]+      ]+  ]
+ test/pptx-reader/notes.pptx view

binary file changed (absent → 17225 bytes)

+ test/pptx-reader/runs.native view
@@ -0,0 +1,51 @@+Pandoc+  Meta { unMeta = fromList [] }+  [ Header+      2+      ( "slide-1" , [] , [] )+      [ Str "Everworker" , Space , Str "diagram" ]+  , Para+      [ Str "Hel" , Strong [ Str "lo" ] , Space , Str "world" ]+  , Para [ Str "openai," , Space , Str "anthropic" ]+  , Para+      [ Str "line"+      , Space+      , Str "one"+      , LineBreak+      , Str "line"+      , Space+      , Str "two"+      ]+  , Table+      ( "" , [] , [] )+      (Caption Nothing [])+      [ ( AlignDefault , ColWidthDefault ) ]+      (TableHead+         ( "" , [] , [] )+         [ Row+             ( "" , [] , [] )+             [ Cell+                 ( "" , [] , [] )+                 AlignDefault+                 (RowSpan 1)+                 (ColSpan 1)+                 [ Plain [ Str "Header" ] ]+             ]+         ])+      [ TableBody+          ( "" , [] , [] )+          (RowHeadColumns 0)+          []+          [ Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "cell" ] ]+              ]+          ]+      ]+      (TableFoot ( "" , [] , [] ) [])+  ]
+ test/pptx-reader/runs.pptx view

binary file changed (absent → 8571 bytes)

+ test/pptx-reader/slide-numbers.native view
@@ -0,0 +1,7 @@+Pandoc+  Meta { unMeta = fromList [] }+  [ Header 2 ( "slide-1" , [] , [] ) [ Str "Numbered" ]+  , Para [ Str "Body" , Space , Str "text" ]+  , Para+      [ Str "A" , Space , Str "footer" , Space , Str "note" ]+  ]
+ test/pptx-reader/slide-numbers.pptx view

binary file changed (absent → 28478 bytes)

+ test/pptx-reader/table-spans.native view
@@ -0,0 +1,90 @@+Pandoc+  Meta { unMeta = fromList [] }+  [ Header+      2+      ( "slide-1" , [] , [] )+      [ Str "Merged" , Space , Str "cells" ]+  , Table+      ( "" , [] , [] )+      (Caption Nothing [])+      [ ( AlignDefault , ColWidthDefault )+      , ( AlignDefault , ColWidthDefault )+      , ( AlignDefault , ColWidthDefault )+      ]+      (TableHead+         ( "" , [] , [] )+         [ Row+             ( "" , [] , [] )+             [ Cell+                 ( "" , [] , [] )+                 AlignDefault+                 (RowSpan 1)+                 (ColSpan 2)+                 [ Plain [ Str "Group" ] ]+             , Cell+                 ( "" , [] , [] )+                 AlignDefault+                 (RowSpan 1)+                 (ColSpan 1)+                 [ Plain [ Str "Total" ] ]+             ]+         ])+      [ TableBody+          ( "" , [] , [] )+          (RowHeadColumns 0)+          []+          [ Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 2)+                  (ColSpan 1)+                  [ Plain [ Str "a" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "1" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 2)+                  [ Plain [ Str "wide" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "b" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "2" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "3" ] ]+              ]+          ]+      ]+      (TableFoot ( "" , [] , [] ) [])+  ]
+ test/pptx-reader/table-spans.pptx view

binary file changed (absent → 8486 bytes)

test/s5-basic.html view
@@ -15,6 +15,12 @@     ** See https://pandoc.org/MANUAL.html#variables-for-html for config info.     */     span.smallcaps{font-variant: small-caps;}+    em, em em em, em em em em em {+      font-style: italic;+    }+    em em, em em em em {+      font-style: normal;+    }     div.columns{display: flex; gap: 1.5em;}     div.column{flex: auto;}     @media screen {@@ -31,7 +37,27 @@       margin: 0 0.8em 0.2em -1.6em;       vertical-align: middle;     }-  </style>+    @media print {+      html {+        color-scheme: light only;+      }+      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;+      }+      h2, h3, h4 {+        page-break-after: avoid;+      }+    }  </style>   <!-- configuration parameters -->   <meta name="defaultView" content="slideshow" />   <meta name="controlVis" content="hidden" />
test/s5-fancy.html view
@@ -15,6 +15,12 @@     ** See https://pandoc.org/MANUAL.html#variables-for-html for config info.     */     span.smallcaps{font-variant: small-caps;}+    em, em em em, em em em em em {+      font-style: italic;+    }+    em em, em em em em {+      font-style: normal;+    }     div.columns{display: flex; gap: 1.5em;}     div.column{flex: auto;}     @media screen {@@ -31,7 +37,27 @@       margin: 0 0.8em 0.2em -1.6em;       vertical-align: middle;     }-  </style>+    @media print {+      html {+        color-scheme: light only;+      }+      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;+      }+      h2, h3, h4 {+        page-break-after: avoid;+      }+    }  </style>   <!-- configuration parameters -->   <meta name="defaultView" content="slideshow" />   <meta name="controlVis" content="hidden" />
test/s5-inserts.html view
@@ -13,6 +13,12 @@     ** See https://pandoc.org/MANUAL.html#variables-for-html for config info.     */     span.smallcaps{font-variant: small-caps;}+    em, em em em, em em em em em {+      font-style: italic;+    }+    em em, em em em em {+      font-style: normal;+    }     div.columns{display: flex; gap: 1.5em;}     div.column{flex: auto;}     @media screen {@@ -29,7 +35,27 @@       margin: 0 0.8em 0.2em -1.6em;       vertical-align: middle;     }-  </style>+    @media print {+      html {+        color-scheme: light only;+      }+      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;+      }+      h2, h3, h4 {+        page-break-after: avoid;+      }+    }  </style>   <link rel="stylesheet" href="main.css" type="text/css" />   STUFF INSERTED </head>
test/test-pandoc.hs view
@@ -1,6 +1,6 @@ module Main (main) where -import System.Environment (getArgs, getExecutablePath)+import System.Environment (getArgs, getExecutablePath, setEnv) import qualified Control.Exception as E import Text.Pandoc.App (convertWithOpts, handleOptInfo, defaultOpts, options,                         parseOptionsFromArgs)@@ -58,7 +58,10 @@ import qualified Tests.Writers.Txt2Tags import qualified Tests.XML import qualified Tests.MediaBag+import qualified Tests.Data import Text.Pandoc.Shared (inDirectory)+import System.FilePath (takeDirectory)+import System.Directory (getCurrentDirectory)  tests :: FilePath -> TestTree tests pandocPath = testGroup "pandoc tests"@@ -66,6 +69,7 @@         , testGroup "Old" (Tests.Old.tests pandocPath)         , testGroup "Shared" Tests.Shared.tests         , testGroup "MediaBag" Tests.MediaBag.tests+        , testGroup "Data" Tests.Data.tests         , testGroup "XML" Tests.XML.tests         , testGroup "ImageSize" Tests.ImageSize.tests         , testGroup "Writers"@@ -134,4 +138,7 @@     _ -> inDirectory "test" $ do            fp <- getExecutablePath            -- putStrLn $ "Using pandoc executable at " ++ fp+           -- look for data in data/ directory...+           cwd <- getCurrentDirectory+           setEnv "pandoc_datadir" (takeDirectory cwd) -- drop the /test            defaultMain $ tests fp
test/writer.html4 view
@@ -40,27 +40,6 @@         font-size: 1.8em;       }     }-    @media print {-      html {-        color-scheme: light only;-      }-      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;-      }-      h2, h3, h4 {-        page-break-after: avoid;-      }-    }     p {       margin: 1em 0;     }@@ -166,6 +145,12 @@       text-decoration: none;     }     span.smallcaps{font-variant: small-caps;}+    em, em em em, em em em em em {+      font-style: italic;+    }+    em em, em em em em {+      font-style: normal;+    }     div.columns{display: flex; gap: 1.5em;}     div.column{flex: auto;}     @media screen {@@ -182,7 +167,27 @@       margin: 0 0.8em 0.2em -1.6em;       vertical-align: middle;     }-  </style>+    @media print {+      html {+        color-scheme: light only;+      }+      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;+      }+      h2, h3, h4 {+        page-break-after: avoid;+      }+    }  </style> </head> <body> <div id="header">
test/writer.html5 view
@@ -40,27 +40,6 @@         font-size: 1.8em;       }     }-    @media print {-      html {-        color-scheme: light only;-      }-      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;-      }-      h2, h3, h4 {-        page-break-after: avoid;-      }-    }     p {       margin: 1em 0;     }@@ -166,6 +145,12 @@       text-decoration: none;     }     span.smallcaps{font-variant: small-caps;}+    em, em em em, em em em em em {+      font-style: italic;+    }+    em em, em em em em {+      font-style: normal;+    }     div.columns{display: flex; gap: 1.5em;}     div.column{flex: auto;}     @media screen {@@ -182,7 +167,27 @@       margin: 0 0.8em 0.2em -1.6em;       vertical-align: middle;     }-  </style>+    @media print {+      html {+        color-scheme: light only;+      }+      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;+      }+      h2, h3, h4 {+        page-break-after: avoid;+      }+    }  </style> </head> <body> <header id="title-block-header">
+ test/xlsx-reader/absolute-targets.native view
@@ -0,0 +1,50 @@+Pandoc+  Meta { unMeta = fromList [] }+  [ Header 2 ( "sheet-1" , [] , [] ) [ Str "Data" ]+  , Table+      ( "" , [] , [] )+      (Caption Nothing [])+      [ ( AlignDefault , ColWidthDefault )+      , ( AlignDefault , ColWidthDefault )+      ]+      (TableHead+         ( "" , [] , [] )+         [ Row+             ( "" , [] , [] )+             [ Cell+                 ( "" , [] , [] )+                 AlignDefault+                 (RowSpan 1)+                 (ColSpan 1)+                 [ Plain [ Str "Item" ] ]+             , Cell+                 ( "" , [] , [] )+                 AlignDefault+                 (RowSpan 1)+                 (ColSpan 1)+                 [ Plain [ Str "Count" ] ]+             ]+         ])+      [ TableBody+          ( "" , [] , [] )+          (RowHeadColumns 0)+          []+          [ Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "Apples" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "3" ] ]+              ]+          ]+      ]+      (TableFoot ( "" , [] , [] ) [])+  ]
+ test/xlsx-reader/absolute-targets.xlsx view

binary file changed (absent → 2381 bytes)

test/xlsx-reader/basic.native view
@@ -49,7 +49,7 @@                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Plain [ Str "23.0" ] ]+                  [ Plain [ Str "23" ] ]               , Cell                   ( "" , [] , [] )                   AlignDefault@@ -70,7 +70,7 @@                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Plain [ Str "35.0" ] ]+                  [ Plain [ Str "35" ] ]               , Cell                   ( "" , [] , [] )                   AlignDefault@@ -262,7 +262,7 @@                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Plain [ Str "23.0" ] ]+                  [ Plain [ Str "23" ] ]               , Cell                   ( "" , [] , [] )                   AlignDefault@@ -274,7 +274,7 @@                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Plain [ Str "23.0" ] ]+                  [ Plain [ Str "23" ] ]               ]           , Row               ( "" , [] , [] )@@ -289,7 +289,7 @@                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Plain [ Str "35.0" ] ]+                  [ Plain [ Str "35" ] ]               , Cell                   ( "" , [] , [] )                   AlignDefault@@ -307,7 +307,7 @@                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Plain [ Str "35.0" ] ]+                  [ Plain [ Str "35" ] ]               ]           , Row               ( "" , [] , [] )@@ -355,13 +355,13 @@                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Plain [ Str "35.0" ] ]+                  [ Plain [ Str "35" ] ]               , Cell                   ( "" , [] , [] )                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Plain [ Str "23.0" ] ]+                  [ Plain [ Str "23" ] ]               , Cell                   ( "" , [] , [] )                   AlignDefault@@ -373,7 +373,7 @@                   AlignDefault                   (RowSpan 1)                   (ColSpan 1)-                  [ Plain [ Str "58.0" ] ]+                  [ Plain [ Str "58" ] ]               ]           ]       ]
+ test/xlsx-reader/cell-fonts.native view
@@ -0,0 +1,54 @@+Pandoc+  Meta { unMeta = fromList [] }+  [ Header 2 ( "sheet-1" , [] , [] ) [ Str "Fonts" ]+  , Table+      ( "" , [] , [] )+      (Caption Nothing [])+      [ ( AlignDefault , ColWidthDefault ) ]+      (TableHead+         ( "" , [] , [] )+         [ Row+             ( "" , [] , [] )+             [ Cell+                 ( "" , [] , [] )+                 AlignDefault+                 (RowSpan 1)+                 (ColSpan 1)+                 [ Plain [ Str "Format" ] ]+             ]+         ])+      [ TableBody+          ( "" , [] , [] )+          (RowHeadColumns 0)+          []+          [ Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Emph [ Str "italic" ] ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Strong [ Str "bold" ] ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "regular" ] ]+              ]+          ]+      ]+      (TableFoot ( "" , [] , [] ) [])+  ]
+ test/xlsx-reader/cell-fonts.xlsx view

binary file changed (absent → 2113 bytes)

+ test/xlsx-reader/charts.native view
@@ -0,0 +1,273 @@+Pandoc+  Meta { unMeta = fromList [] }+  [ Header 2 ( "sheet-1" , [] , [] ) [ Str "Data" ]+  , Table+      ( "" , [] , [] )+      (Caption Nothing [])+      [ ( AlignDefault , ColWidthDefault )+      , ( AlignDefault , ColWidthDefault )+      ]+      (TableHead+         ( "" , [] , [] )+         [ Row+             ( "" , [] , [] )+             [ Cell+                 ( "" , [] , [] )+                 AlignDefault+                 (RowSpan 1)+                 (ColSpan 1)+                 [ Plain [ Str "Group" ] ]+             , Cell+                 ( "" , [] , [] )+                 AlignDefault+                 (RowSpan 1)+                 (ColSpan 1)+                 [ Plain [ Str "Mean" ] ]+             ]+         ])+      [ TableBody+          ( "" , [] , [] )+          (RowHeadColumns 0)+          []+          [ Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "Low" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "4.1" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "High" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "-0.1" ] ]+              ]+          ]+      ]+      (TableFoot ( "" , [] , [] ) [])+  , Div+      ( "" , [ "chart" ] , [ ( "type" , "bar" ) ] )+      [ Table+          ( "" , [] , [] )+          (Caption+             Nothing [ Plain [ Str "Mean" , Space , Str "growth" ] ])+          [ ( AlignDefault , ColWidthDefault )+          , ( AlignDefault , ColWidthDefault )+          ]+          (TableHead+             ( "" , [] , [] )+             [ Row+                 ( "" , [] , [] )+                 [ Cell+                     ( "" , [] , [] )+                     AlignDefault+                     (RowSpan 1)+                     (ColSpan 1)+                     [ Plain [] ]+                 , Cell+                     ( "" , [] , [] )+                     AlignDefault+                     (RowSpan 1)+                     (ColSpan 1)+                     [ Plain [ Str "Mean" ] ]+                 ]+             ])+          [ TableBody+              ( "" , [] , [] )+              (RowHeadColumns 0)+              []+              [ Row+                  ( "" , [] , [] )+                  [ Cell+                      ( "" , [] , [] )+                      AlignDefault+                      (RowSpan 1)+                      (ColSpan 1)+                      [ Plain [ Str "Low" ] ]+                  , Cell+                      ( "" , [] , [] )+                      AlignDefault+                      (RowSpan 1)+                      (ColSpan 1)+                      [ Plain [ Str "4.1" ] ]+                  ]+              , Row+                  ( "" , [] , [] )+                  [ Cell+                      ( "" , [] , [] )+                      AlignDefault+                      (RowSpan 1)+                      (ColSpan 1)+                      [ Plain [ Str "High" ] ]+                  , Cell+                      ( "" , [] , [] )+                      AlignDefault+                      (RowSpan 1)+                      (ColSpan 1)+                      [ Plain [ Str "-0.1" ] ]+                  ]+              ]+          ]+          (TableFoot ( "" , [] , [] ) [])+      ]+  , Div+      ( "" , [ "chart" ] , [ ( "type" , "line" ) ] )+      [ Table+          ( "" , [] , [] )+          (Caption Nothing [])+          [ ( AlignDefault , ColWidthDefault )+          , ( AlignDefault , ColWidthDefault )+          ]+          (TableHead+             ( "" , [] , [] )+             [ Row+                 ( "" , [] , [] )+                 [ Cell+                     ( "" , [] , [] )+                     AlignDefault+                     (RowSpan 1)+                     (ColSpan 1)+                     [ Plain [] ]+                 , Cell+                     ( "" , [] , [] )+                     AlignDefault+                     (RowSpan 1)+                     (ColSpan 1)+                     [ Plain [ Str "Mean" ] ]+                 ]+             ])+          [ TableBody+              ( "" , [] , [] )+              (RowHeadColumns 0)+              []+              [ Row+                  ( "" , [] , [] )+                  [ Cell+                      ( "" , [] , [] )+                      AlignDefault+                      (RowSpan 1)+                      (ColSpan 1)+                      [ Plain [ Str "Low" ] ]+                  , Cell+                      ( "" , [] , [] )+                      AlignDefault+                      (RowSpan 1)+                      (ColSpan 1)+                      [ Plain [ Str "4.1" ] ]+                  ]+              , Row+                  ( "" , [] , [] )+                  [ Cell+                      ( "" , [] , [] )+                      AlignDefault+                      (RowSpan 1)+                      (ColSpan 1)+                      [ Plain [ Str "High" ] ]+                  , Cell+                      ( "" , [] , [] )+                      AlignDefault+                      (RowSpan 1)+                      (ColSpan 1)+                      [ Plain [ Str "-0.1" ] ]+                  ]+              ]+          ]+          (TableFoot ( "" , [] , [] ) [])+      ]+  , Header+      2+      ( "sheet-2" , [] , [] )+      [ Str "Chart" , Space , Str "sheet" ]+  , Div+      ( "" , [ "chart" ] , [ ( "type" , "pie" ) ] )+      [ Table+          ( "" , [] , [] )+          (Caption+             Nothing+             [ Plain+                 [ Str "On"+                 , Space+                 , Str "its"+                 , Space+                 , Str "own"+                 , Space+                 , Str "sheet"+                 ]+             ])+          [ ( AlignDefault , ColWidthDefault )+          , ( AlignDefault , ColWidthDefault )+          ]+          (TableHead+             ( "" , [] , [] )+             [ Row+                 ( "" , [] , [] )+                 [ Cell+                     ( "" , [] , [] )+                     AlignDefault+                     (RowSpan 1)+                     (ColSpan 1)+                     [ Plain [] ]+                 , Cell+                     ( "" , [] , [] )+                     AlignDefault+                     (RowSpan 1)+                     (ColSpan 1)+                     [ Plain [ Str "Share" ] ]+                 ]+             ])+          [ TableBody+              ( "" , [] , [] )+              (RowHeadColumns 0)+              []+              [ Row+                  ( "" , [] , [] )+                  [ Cell+                      ( "" , [] , [] )+                      AlignDefault+                      (RowSpan 1)+                      (ColSpan 1)+                      [ Plain [ Str "Low" ] ]+                  , Cell+                      ( "" , [] , [] )+                      AlignDefault+                      (RowSpan 1)+                      (ColSpan 1)+                      [ Plain [ Str "0.6" ] ]+                  ]+              , Row+                  ( "" , [] , [] )+                  [ Cell+                      ( "" , [] , [] )+                      AlignDefault+                      (RowSpan 1)+                      (ColSpan 1)+                      [ Plain [ Str "High" ] ]+                  , Cell+                      ( "" , [] , [] )+                      AlignDefault+                      (RowSpan 1)+                      (ColSpan 1)+                      [ Plain [ Str "0.4" ] ]+                  ]+              ]+          ]+          (TableFoot ( "" , [] , [] ) [])+      ]+  ]
+ test/xlsx-reader/charts.xlsx view

binary file changed (absent → 6696 bytes)

+ test/xlsx-reader/dates-1904.native view
@@ -0,0 +1,65 @@+Pandoc+  Meta { unMeta = fromList [] }+  [ Header 2 ( "sheet-1" , [] , [] ) [ Str "Dates" ]+  , Table+      ( "" , [] , [] )+      (Caption Nothing [])+      [ ( AlignDefault , ColWidthDefault )+      , ( AlignDefault , ColWidthDefault )+      ]+      (TableHead+         ( "" , [] , [] )+         [ Row+             ( "" , [] , [] )+             [ Cell+                 ( "" , [] , [] )+                 AlignDefault+                 (RowSpan 1)+                 (ColSpan 1)+                 [ Plain [ Str "Case" ] ]+             , Cell+                 ( "" , [] , [] )+                 AlignDefault+                 (RowSpan 1)+                 (ColSpan 1)+                 [ Plain [ Str "Value" ] ]+             ]+         ])+      [ TableBody+          ( "" , [] , [] )+          (RowHeadColumns 0)+          []+          [ Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "day" , Space , Str "zero" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "1904-01-01" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "built-in" , Space , Str "14" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "2023-11-01" ] ]+              ]+          ]+      ]+      (TableFoot ( "" , [] , [] ) [])+  ]
+ test/xlsx-reader/dates-1904.xlsx view

binary file changed (absent → 2305 bytes)

+ test/xlsx-reader/dates.native view
@@ -0,0 +1,432 @@+Pandoc+  Meta { unMeta = fromList [] }+  [ Header 2 ( "sheet-1" , [] , [] ) [ Str "Dates" ]+  , Table+      ( "" , [] , [] )+      (Caption Nothing [])+      [ ( AlignDefault , ColWidthDefault )+      , ( AlignDefault , ColWidthDefault )+      ]+      (TableHead+         ( "" , [] , [] )+         [ Row+             ( "" , [] , [] )+             [ Cell+                 ( "" , [] , [] )+                 AlignDefault+                 (RowSpan 1)+                 (ColSpan 1)+                 [ Plain [ Str "Case" ] ]+             , Cell+                 ( "" , [] , [] )+                 AlignDefault+                 (RowSpan 1)+                 (ColSpan 1)+                 [ Plain [ Str "Value" ] ]+             ]+         ])+      [ TableBody+          ( "" , [] , [] )+          (RowHeadColumns 0)+          []+          [ Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "built-in" , Space , Str "14" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "2023-11-01" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "built-in" , Space , Str "22" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "2023-11-01" , Space , Str "14:30" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "built-in" , Space , Str "20" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "14:30" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "built-in" , Space , Str "21" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "14:30:05" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "built-in" , Space , Str "45" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "00:05:30" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "built-in"+                      , Space+                      , Str "46,"+                      , Space+                      , Str "a"+                      , Space+                      , Str "duration"+                      ]+                  ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "1.5" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "custom" , Space , Str "date" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "2023-11-01" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "custom"+                      , Space+                      , Str "date"+                      , Space+                      , Str "and"+                      , Space+                      , Str "time"+                      ]+                  ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "2023-11-01" , Space , Str "14:30:05" ]+                  ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "custom"+                      , Space+                      , Str "elapsed"+                      , Space+                      , Str "hours"+                      ]+                  ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "1.5" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "custom" , Space , Str "month" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "2023-11-01" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "fixed" , Space , Str "decimals" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "45231" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "quoted" , Space , Str "d" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "7" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "locale" , Space , Str "prefix" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "2023-11-01" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "first" , Space , Str "day" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "1900-01-01" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "28"+                      , Space+                      , Str "February"+                      , Space+                      , Str "1900"+                      ]+                  ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "1900-02-28" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "nonexistent"+                      , Space+                      , Str "29"+                      , Space+                      , Str "February"+                      , Space+                      , Str "1900"+                      ]+                  ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "60" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "1"+                      , Space+                      , Str "March"+                      , Space+                      , Str "1900"+                      ]+                  ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "1900-03-01" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "zero"+                      , Space+                      , Str "under"+                      , Space+                      , Str "a"+                      , Space+                      , Str "date"+                      , Space+                      , Str "format"+                      ]+                  ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "0" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "negative"+                      , Space+                      , Str "under"+                      , Space+                      , Str "a"+                      , Space+                      , Str "date"+                      , Space+                      , Str "format"+                      ]+                  ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "-1" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "rounds"+                      , Space+                      , Str "to"+                      , Space+                      , Str "the"+                      , Space+                      , Str "next"+                      , Space+                      , Str "day"+                      ]+                  ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "2023-11-02" , Space , Str "00:00" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "General" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "45231" ] ]+              ]+          ]+      ]+      (TableFoot ( "" , [] , [] ) [])+  ]
+ test/xlsx-reader/dates.xlsx view

binary file changed (absent → 2711 bytes)

+ test/xlsx-reader/merged.native view
@@ -0,0 +1,161 @@+Pandoc+  Meta { unMeta = fromList [] }+  [ Header 2 ( "sheet-1" , [] , [] ) [ Str "Merged" ]+  , Table+      ( "" , [] , [] )+      (Caption Nothing [])+      [ ( AlignDefault , ColWidthDefault )+      , ( AlignDefault , ColWidthDefault )+      , ( AlignDefault , ColWidthDefault )+      , ( AlignDefault , ColWidthDefault )+      ]+      (TableHead+         ( "" , [] , [] )+         [ Row+             ( "" , [] , [] )+             [ Cell+                 ( "" , [] , [] )+                 AlignDefault+                 (RowSpan 1)+                 (ColSpan 2)+                 [ Plain [ Str "Group" ] ]+             , Cell+                 ( "" , [] , [] )+                 AlignDefault+                 (RowSpan 1)+                 (ColSpan 1)+                 [ Plain [ Str "Total" ] ]+             , Cell+                 ( "" , [] , [] )+                 AlignDefault+                 (RowSpan 1)+                 (ColSpan 1)+                 [ Plain [] ]+             ]+         ])+      [ TableBody+          ( "" , [] , [] )+          (RowHeadColumns 0)+          []+          [ Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 2)+                  (ColSpan 1)+                  [ Plain [ Str "x" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "1" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "note" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "2" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 4)+                  [ Plain+                      [ Str "A"+                      , Space+                      , Str "row"+                      , Space+                      , Str "across"+                      ]+                  ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "last" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "3" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "wide" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "tail" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [] ]+              ]+          ]+      ]+      (TableFoot ( "" , [] , [] ) [])+  ]
+ test/xlsx-reader/merged.xlsx view

binary file changed (absent → 2208 bytes)

+ test/xlsx-reader/numbers.native view
@@ -0,0 +1,260 @@+Pandoc+  Meta { unMeta = fromList [] }+  [ Header 2 ( "sheet-1" , [] , [] ) [ Str "Numbers" ]+  , Table+      ( "" , [] , [] )+      (Caption Nothing [])+      [ ( AlignDefault , ColWidthDefault )+      , ( AlignDefault , ColWidthDefault )+      ]+      (TableHead+         ( "" , [] , [] )+         [ Row+             ( "" , [] , [] )+             [ Cell+                 ( "" , [] , [] )+                 AlignDefault+                 (RowSpan 1)+                 (ColSpan 1)+                 [ Plain [ Str "Case" ] ]+             , Cell+                 ( "" , [] , [] )+                 AlignDefault+                 (RowSpan 1)+                 (ColSpan 1)+                 [ Plain [ Str "Value" ] ]+             ]+         ])+      [ TableBody+          ( "" , [] , [] )+          (RowHeadColumns 0)+          []+          [ Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "small" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "0.05" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "integer" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "21" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "large" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "12345678" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "negative" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "-0.1" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "float" , Space , Str "noise" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "0.3" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "carry" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "1" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "eleven" , Space , Str "digits" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "12345678901" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "twelve" , Space , Str "digits" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "1.23456789012E+11" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "fifteen" , Space , Str "digits" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "1.23456789012345E+14" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "huge" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "1.23456789012346E+17" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "tiny" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "1.5E-10" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "zero" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "0" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "true" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "TRUE" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "false" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "FALSE" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "error" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "#DIV/0!" ] ]+              ]+          ]+      ]+      (TableFoot ( "" , [] , [] ) [])+  ]
+ test/xlsx-reader/numbers.xlsx view

binary file changed (absent → 2369 bytes)

+ test/xlsx-reader/strings.native view
@@ -0,0 +1,67 @@+Pandoc+  Meta { unMeta = fromList [] }+  [ Header 2 ( "sheet-1" , [] , [] ) [ Str "Strings" ]+  , Table+      ( "" , [] , [] )+      (Caption Nothing [])+      [ ( AlignDefault , ColWidthDefault )+      , ( AlignDefault , ColWidthDefault )+      ]+      (TableHead+         ( "" , [] , [] )+         [ Row+             ( "" , [] , [] )+             [ Cell+                 ( "" , [] , [] )+                 AlignDefault+                 (RowSpan 1)+                 (ColSpan 1)+                 [ Plain [ Str "Inline" ] ]+             , Cell+                 ( "" , [] , [] )+                 AlignDefault+                 (RowSpan 1)+                 (ColSpan 1)+                 [ Plain [ Str "Shared" ] ]+             ]+         ])+      [ TableBody+          ( "" , [] , [] )+          (RowHeadColumns 0)+          []+          [ Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "plain" , Space , Str "inline" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "Hello" , Space , Str "world" ] ]+              ]+          , Row+              ( "" , [] , [] )+              [ Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain [ Str "Hello" , Space , Str "world" ] ]+              , Cell+                  ( "" , [] , [] )+                  AlignDefault+                  (RowSpan 1)+                  (ColSpan 1)+                  [ Plain+                      [ Str "x" , Space , Str "=" , Space , Str "1" ]+                  ]+              ]+          ]+      ]+      (TableFoot ( "" , [] , [] ) [])+  ]
+ test/xlsx-reader/strings.xlsx view

binary file changed (absent → 2544 bytes)