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